[GH-ISSUE #100] Can't switch focus when using Flex #76

Closed
opened 2026-03-04 01:01:43 +03:00 by kerem · 6 comments
Owner

Originally created by @arguablykomodo on GitHub (Apr 14, 2018).
Original GitHub issue: https://github.com/rivo/tview/issues/100

Let's say you have this example code:

app := tview.NewApplication()

flex := tview.NewFlex().
	AddItem(tview.NewButton("First button"), 0, 1, true).
	AddItem(tview.NewButton("Second Button"), 0, 1, true)

if err := app.SetRoot(flex, true).SetFocus(flex).Run(); err != nil {
	panic(err)
}

if you were to compile and run this code, you would get the following output:

image

But for some reason, you cannot press TAB to switch over to the second button, there's just no way to switch.

Am i not doing this correctly? Or is it a bug? Are you allowed to switch focus when using Flex?

(I am using windows 10 and go 1.10.1)

Originally created by @arguablykomodo on GitHub (Apr 14, 2018). Original GitHub issue: https://github.com/rivo/tview/issues/100 Let's say you have this example code: ```go app := tview.NewApplication() flex := tview.NewFlex(). AddItem(tview.NewButton("First button"), 0, 1, true). AddItem(tview.NewButton("Second Button"), 0, 1, true) if err := app.SetRoot(flex, true).SetFocus(flex).Run(); err != nil { panic(err) } ``` if you were to compile and run this code, you would get the following output: ![image](https://user-images.githubusercontent.com/22225222/38761944-723e9506-3f5d-11e8-9c10-e40476bcba91.png) But for some reason, you cannot press TAB to switch over to the second button, there's just no way to switch. Am i not doing this correctly? Or is it a bug? Are you allowed to switch focus when using Flex? (I am using windows 10 and go 1.10.1)
kerem closed this issue 2026-03-04 01:01:43 +03:00
Author
Owner

@rivo commented on GitHub (Apr 14, 2018):

Primitives such as the button are independent of each other. So they wouldn't know what to do when Tab is pressed. And Flex is just a layout primitive. It doesn't process any keys.

You could use Form. It allows you to add buttons. And it handles the Tab key, advancing the focus to the next button when it is pressed.

Alternatively, you can install your own input handler on a button using SetInputCapture(). You listen for the Tab key and call app.SetFocus() to move the focus to a different button.

Let me know if this helps.

<!-- gh-comment-id:381320382 --> @rivo commented on GitHub (Apr 14, 2018): Primitives such as the button are independent of each other. So they wouldn't know what to do when Tab is pressed. And `Flex` is just a layout primitive. It doesn't process any keys. You could use [`Form`](https://godoc.org/github.com/rivo/tview#Form). It allows you to add buttons. And it handles the Tab key, advancing the focus to the next button when it is pressed. Alternatively, you can install your own input handler on a button using [`SetInputCapture()`](https://godoc.org/github.com/rivo/tview#Box.SetInputCapture). You listen for the Tab key and call `app.SetFocus()` to move the focus to a different button. Let me know if this helps.
Author
Owner

@arguablykomodo commented on GitHub (Apr 14, 2018):

Thanks for the response! It really helps me a lot

<!-- gh-comment-id:381338370 --> @arguablykomodo commented on GitHub (Apr 14, 2018): Thanks for the response! It really helps me a lot
Author
Owner

@ethantrithon commented on GitHub (Feb 14, 2020):

Primitives such as the button are independent of each other. So they wouldn't know what to do when Tab is pressed. And Flex is just a layout primitive. It doesn't process any keys.

You could use Form. It allows you to add buttons. And it handles the Tab key, advancing the focus to the next button when it is pressed.

Alternatively, you can install your own input handler on a button using SetInputCapture(). You listen for the Tab key and call app.SetFocus() to move the focus to a different button.

Let me know if this helps.

is "app.SetFocus" the ONLY way to give focus to some other primitive? this seems rather hack-y to me, especially given the fact you might not have access to the actual application object at all times.

can we do this internally somehow? or if not, how is it done ideally?

<!-- gh-comment-id:586461357 --> @ethantrithon commented on GitHub (Feb 14, 2020): > Primitives such as the button are independent of each other. So they wouldn't know what to do when Tab is pressed. And `Flex` is just a layout primitive. It doesn't process any keys. > > You could use [`Form`](https://godoc.org/github.com/rivo/tview#Form). It allows you to add buttons. And it handles the Tab key, advancing the focus to the next button when it is pressed. > > Alternatively, you can install your own input handler on a button using [`SetInputCapture()`](https://godoc.org/github.com/rivo/tview#Box.SetInputCapture). You listen for the Tab key and call `app.SetFocus()` to move the focus to a different button. > > Let me know if this helps. is "app.SetFocus" the ONLY way to give focus to some other primitive? this seems rather hack-y to me, especially given the fact you might not have access to the actual application object at all times. can we do this internally somehow? or if not, how is it done ideally?
Author
Owner

@arguablykomodo commented on GitHub (Feb 14, 2020):

This is how i implemented it, and I must admit that it looks very hacky and it would be quite a hassle to work with this code if i were to add new UI elements. Maybe some system in which the app cycles through the different inputs in the order they were added would be more ideal.

<!-- gh-comment-id:586488694 --> @arguablykomodo commented on GitHub (Feb 14, 2020): This is [how i implemented it](https://github.com/SrKomodo/shadowfox-updater/blob/b171cb8464c0a10d8e4507c98ad69d51bb95832e/ui.go#L61), and I must admit that it looks very hacky and it would be quite a hassle to work with this code if i were to add new UI elements. Maybe some system in which the app cycles through the different inputs in the order they were added would be more ideal.
Author
Owner

@ghost commented on GitHub (Jan 19, 2021):

Just leaving this here for anyone else who wants to implement it. A small tweak on @SrKomodo's implementation to make it more generic. Putting all of your inputs into a slice and passing them to this function will cycle through each one in order.

func cycleFocus(app *tview.Application, elements []tview.Primitive, reverse bool) {
	for i, el := range elements {
		if !el.HasFocus() {
			continue
		}

		if reverse {
			i = i - 1
			if i < 0 {
				i = len(elements) - 1
			}
		} else {
			i = i + 1
			i = i % len(elements)
		}

		app.SetFocus(elements[i])
		return
	}
}

Can be used in this context:

inputs := []tview.Primitive{
	input1,
	input2,
	input3,
}

frame.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
	if event.Key() == tcell.KeyTab {
		cycleFocus(app, inputs, false)
	} else if event.Key() == tcell.KeyBacktab {
		cycleFocus(app, inputs, true)
	}
	return event
})
<!-- gh-comment-id:763131391 --> @ghost commented on GitHub (Jan 19, 2021): Just leaving this here for anyone else who wants to implement it. A small tweak on @SrKomodo's implementation to make it more generic. Putting all of your inputs into a slice and passing them to this function will cycle through each one in order. ``` func cycleFocus(app *tview.Application, elements []tview.Primitive, reverse bool) { for i, el := range elements { if !el.HasFocus() { continue } if reverse { i = i - 1 if i < 0 { i = len(elements) - 1 } } else { i = i + 1 i = i % len(elements) } app.SetFocus(elements[i]) return } } ``` Can be used in this context: ``` inputs := []tview.Primitive{ input1, input2, input3, } frame.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if event.Key() == tcell.KeyTab { cycleFocus(app, inputs, false) } else if event.Key() == tcell.KeyBacktab { cycleFocus(app, inputs, true) } return event }) ```
Author
Owner

@rivo commented on GitHub (Jan 21, 2021):

Btw, if it helps, you can now intercept key events on the Flex and Grid level, too.

<!-- gh-comment-id:764474052 --> @rivo commented on GitHub (Jan 21, 2021): Btw, if it helps, you can now intercept key events on the `Flex` and `Grid` level, too.
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#76
No description provided.