[GH-ISSUE #1071] Instability/Inconsistency of grid, Newbutton Output #775

Closed
opened 2026-03-04 01:07:39 +03:00 by kerem · 3 comments
Owner

Originally created by @WhipMeHarder on GitHub (Jan 13, 2025).
Original GitHub issue: https://github.com/rivo/tview/issues/1071

The following experimental code is being reported - as seen below, which seems to produce component output instability and inconsistency. When used, there is no reliability that the movement or appearance of the selected button's blue background will follow the up and down input of the arrow keys from the keyboard. The top and first button is supposed to be selected. This happens intermittently, as does the selection of buttons when using the keyboard.

package main

import (
	"github.com/gdamore/tcell/v2"
	"github.com/rivo/tview"
	"fmt"
)

func main() {
    app := tview.NewApplication()

	// Create the buttons
	buttons := []string {"test 1", "test 2", "test 3", "test 4", "test 5"}
	grid := tview.NewGrid().SetColumns(45, 65, 45).SetRows(3, 3, 3, 3, 3, 3, 3, 3, 3, 3)

	// Add buttons to the grid
	for i, label := range buttons {
		grid.AddItem(tview.NewButton(label).SetBorder(true).SetTitle("[ "+label+" ]"), i, 0, 1, 1, 0, 0, false)
		
	}
	
	f := tview.NewInputField()
	f.SetBorder(true)
	f.SetTitle("Enter")

	g := tview.NewButton("Some text")	//.SetBorder(true)

	grid.AddItem(g, 5, 0, 1, 1, 0, 0, true)
	grid.AddItem(f, 6, 0, 1, 1, 0, 0, true)
	grid.AddItem(tview.NewTextView().SetText("Some small text").SetTextAlign(tview.AlignCenter), 7, 0, 1, 1, 0, 0, true)
	
	
	// Blue area button
	supr := tview.NewButton("test 1")
	supr.SetBackgroundColor(tcell.ColorBlue)
	supr.SetBorder(false)
	grid.AddItem(supr, 0, 0, 1, 1, 0, 0, true)

	currentRow := 0 // Track the current row of the blue area

	// Function to update the blue area position
	moveBlueArea := func(delta int) {
		newRow := currentRow + delta
		if newRow < 0 { newRow = 0 }
		if newRow >= len(buttons) { newRow = len(buttons) - 1}

		//if newRow >= 0 && newRow < len(buttons) {
			
			grid.RemoveItem(supr) // Remove from current position
			supr := tview.NewButton(buttons[newRow])
			grid.AddItem(supr, newRow, 0, 1, 1, 0, 0, true) // Add to new position
			
			currentRow = newRow
		//}
	}

	// Add the blue area initially
	grid.AddItem(supr, currentRow, 0, 1, 1, 0, 0, true)

	// Set up key handlers
	app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
		switch event.Key() {
			case tcell.KeyUp:
				moveBlueArea(-1) // Move up
			case tcell.KeyDown:
				moveBlueArea(1) // Move down
			case tcell.KeyTAB:
				f.SetInputCapture(func(e *tcell.EventKey) *tcell.EventKey { fmt.Println("Key pressed:", e); return e })
			case tcell.KeyEnter:
				app.Stop() // Exit on Enter key
		}
		return event
	})

	// Set up the layout and run the application
	if err := app.SetRoot(grid, true).SetFocus(grid).Run(); err != nil {
		panic(err)
	}
}

Various experiments have been done to:

  1. Rewrite or rebuild the entire display - even with or without the exception of the selected button.
  2. To add or change the background colour properties, of each selected button, including those which are not; and then back to point number one.

However, regardless of what methods are used, the output results always remain the same and haphazard. Is there a way to make the interactive output stable?

Originally created by @WhipMeHarder on GitHub (Jan 13, 2025). Original GitHub issue: https://github.com/rivo/tview/issues/1071 The following experimental code is being reported - as seen below, which seems to produce component output instability and inconsistency. When used, there is no reliability that the movement or appearance of the selected button's blue background will follow the up and down input of the arrow keys from the keyboard. The top and first button is supposed to be selected. This happens intermittently, as does the selection of buttons when using the keyboard. ``` package main import ( "github.com/gdamore/tcell/v2" "github.com/rivo/tview" "fmt" ) func main() { app := tview.NewApplication() // Create the buttons buttons := []string {"test 1", "test 2", "test 3", "test 4", "test 5"} grid := tview.NewGrid().SetColumns(45, 65, 45).SetRows(3, 3, 3, 3, 3, 3, 3, 3, 3, 3) // Add buttons to the grid for i, label := range buttons { grid.AddItem(tview.NewButton(label).SetBorder(true).SetTitle("[ "+label+" ]"), i, 0, 1, 1, 0, 0, false) } f := tview.NewInputField() f.SetBorder(true) f.SetTitle("Enter") g := tview.NewButton("Some text") //.SetBorder(true) grid.AddItem(g, 5, 0, 1, 1, 0, 0, true) grid.AddItem(f, 6, 0, 1, 1, 0, 0, true) grid.AddItem(tview.NewTextView().SetText("Some small text").SetTextAlign(tview.AlignCenter), 7, 0, 1, 1, 0, 0, true) // Blue area button supr := tview.NewButton("test 1") supr.SetBackgroundColor(tcell.ColorBlue) supr.SetBorder(false) grid.AddItem(supr, 0, 0, 1, 1, 0, 0, true) currentRow := 0 // Track the current row of the blue area // Function to update the blue area position moveBlueArea := func(delta int) { newRow := currentRow + delta if newRow < 0 { newRow = 0 } if newRow >= len(buttons) { newRow = len(buttons) - 1} //if newRow >= 0 && newRow < len(buttons) { grid.RemoveItem(supr) // Remove from current position supr := tview.NewButton(buttons[newRow]) grid.AddItem(supr, newRow, 0, 1, 1, 0, 0, true) // Add to new position currentRow = newRow //} } // Add the blue area initially grid.AddItem(supr, currentRow, 0, 1, 1, 0, 0, true) // Set up key handlers app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { switch event.Key() { case tcell.KeyUp: moveBlueArea(-1) // Move up case tcell.KeyDown: moveBlueArea(1) // Move down case tcell.KeyTAB: f.SetInputCapture(func(e *tcell.EventKey) *tcell.EventKey { fmt.Println("Key pressed:", e); return e }) case tcell.KeyEnter: app.Stop() // Exit on Enter key } return event }) // Set up the layout and run the application if err := app.SetRoot(grid, true).SetFocus(grid).Run(); err != nil { panic(err) } } ``` Various experiments have been done to: 1. Rewrite or rebuild the entire display - even with or without the exception of the selected button. 2. To add or change the background colour properties, of each selected button, including those which are not; and then back to point number one. However, regardless of what methods are used, the output results always remain the same and haphazard. Is there a way to make the interactive output stable?
kerem closed this issue 2026-03-04 01:07:40 +03:00
Author
Owner

@rivo commented on GitHub (Mar 29, 2025):

The following line in your code

supr := tview.NewButton(buttons[newRow])

creates a new variable with the new button. But the scope of this variable extends only until the end of the function. So you're actually not updating your global supr variable with this. Thus, you end up always removing the same button that does not exist anymore after the first deletion. It has no effect.

There are Golang linters who can catch this mistake. You might want to look into them.

Other than that, I would also say, maybe try to avoid deleting and creating elements on the fly like that. Maybe what you really want is to call SetDisabled(). Or maybe just switch the focus using SetFocus(). Have a look at the Form implementation. It switches between different elements, similarly to what you're doing. Or maybe just use Form directly, then you don't have to worry about it.

<!-- gh-comment-id:2763456832 --> @rivo commented on GitHub (Mar 29, 2025): The following line in your code ```golang supr := tview.NewButton(buttons[newRow]) ``` creates a new variable with the new button. But the scope of this variable extends only until the end of the function. So you're actually not updating your global `supr` variable with this. Thus, you end up always removing the same button that does not exist anymore after the first deletion. It has no effect. There are Golang linters who can catch this mistake. You might want to look into them. Other than that, I would also say, maybe try to avoid deleting and creating elements on the fly like that. Maybe what you really want is to call [`SetDisabled()`](https://pkg.go.dev/github.com/rivo/tview#Button.SetDisabled). Or maybe just switch the focus using [`SetFocus()`](https://pkg.go.dev/github.com/rivo/tview#Application.SetFocus). Have a look at the `Form` implementation. It switches between different elements, similarly to what you're doing. Or maybe just use `Form` directly, then you don't have to worry about it.
Author
Owner

@WhipMeHarder commented on GitHub (May 8, 2025):

Ok. Thank you for giving such an insightful understanding. I will definitely look into this.

<!-- gh-comment-id:2863398806 --> @WhipMeHarder commented on GitHub (May 8, 2025): Ok. Thank you for giving such an insightful understanding. I will definitely look into this.
Author
Owner

@rivo commented on GitHub (Aug 27, 2025):

Will close this for now. Feel to open a new issue if needed.

<!-- gh-comment-id:3229443668 --> @rivo commented on GitHub (Aug 27, 2025): Will close this for now. Feel to open a new issue if needed.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/tview#775
No description provided.