[GH-ISSUE #704] Make selected button text underlined #514

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

Originally created by @riscie on GitHub (Mar 1, 2022).
Original GitHub issue: https://github.com/rivo/tview/issues/704

Hi there

Great library. We are building a small TUI for managing a local kubernetes cluster and our team really enjoys working with the library. Great work 👏

I wanted to ask, wether there is any way to make all buttons use an underlined text when they are in focus. I browsed the sourcecode a bit and to me it seems this is not yet possible at the moment. In modals for example, it seems it is not possible to get a hold of the buttons after adding them with AddButtons(...). The styles.go is also not exporting something like this for the user to set it globally atm.

Is there a way to achieve this?

Kindly, Riscie

Originally created by @riscie on GitHub (Mar 1, 2022). Original GitHub issue: https://github.com/rivo/tview/issues/704 Hi there Great library. We are building a small TUI for managing a local kubernetes cluster and our team really enjoys working with the library. Great work :clap: I wanted to ask, wether there is any way to make all buttons use an underlined text when they are in focus. I browsed the sourcecode a bit and to me it seems this is not yet possible at the moment. In modals for example, it seems it is not possible to get a hold of the buttons after adding them with `AddButtons(...)`. The [styles.go](https://github.com/rivo/tview/blob/master/styles.go) is also not exporting something like this for the user to set it globally atm. Is there a way to achieve this? Kindly, Riscie
kerem closed this issue 2026-03-04 01:05:39 +03:00
Author
Owner

@darkhz commented on GitHub (Mar 2, 2022):

Possibly you could do the following:

  • Create a custom modal/dialog box. For example you could draw a flex, and specifically add a textview and button primitives to it. For each of the buttons, attach a SetFocusFunc, and within the SetFocusFunc you could modify the button's label to include an underline.

  • Or, you could draw a Form primitive, add buttons to it, get each button using GetButton or GetButtonIndex, and attach a SetFocusFunc to the Button primitive returned by GetButton/GetButtonIndex.

This is a code example to illustrate the first point that was mentioned above.

package main

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

const (
	okButtonString     = "OK"
	cancelButtonString = "Cancel"
)

func main() {
	App := tview.NewApplication()
	Page := tview.NewPages()

	ModalTextView := tview.NewTextView().
		SetText("Quit?").
		SetTextAlign(tview.AlignCenter)

	ModalButtonOK := tview.NewButton("OK")
	ModalButtonCancel := tview.NewButton("Cancel")

	ModalButtonOK.SetExitFunc(func(key tcell.Key) {
		if key == tcell.KeyTab {
			App.SetFocus(ModalButtonCancel)

			// Reset the underlined OK button's label
			// when not focused.
			ModalButtonOK.SetLabel(okButtonString)
		}
	})
	ModalButtonOK.SetSelectedFunc(func() {
		App.Stop()
	})
	ModalButtonOK.SetFocusFunc(func() {
		// Underline the OK button's label when focused.
		ModalButtonOK.SetLabel("[::u]" + okButtonString)
	})

	ModalButtonCancel.SetExitFunc(func(key tcell.Key) {
		if key == tcell.KeyTab {
			App.SetFocus(ModalButtonOK)

			// Reset the underlined Cancel button's label
			// when not focused.
			ModalButtonCancel.SetLabel(cancelButtonString)
		}
	})
	ModalButtonCancel.SetSelectedFunc(func() {
		Page.SwitchToPage("table")
	})
	ModalButtonCancel.SetFocusFunc(func() {
		// Underline the Cancel button's label when focused.
		ModalButtonCancel.SetLabel("[::u]" + cancelButtonString)
	})

	Modal := modal(ModalTextView, ModalButtonOK, ModalButtonCancel, 60, 10)

	Table := tview.NewTable()
	Table.SetCellSimple(0, 1, "Press 'q' to quit")
	Table.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
		if event.Rune() == 'q' {
			Page.SwitchToPage("modal").ShowPage("table")
			App.SetFocus(ModalButtonOK)
		}

		return event
	})

	Page.AddPage("table", Table, true, true)
	Page.AddPage("modal", Modal, true, false)

	if err := App.SetRoot(Page, true).SetFocus(Table).Run(); err != nil {
		panic(err)
	}
}

func modal(textview, okbtn, cancelbtn tview.Primitive, width, height int) tview.Primitive {
	buttonlayout := tview.NewFlex().
		AddItem(nil, width/8, 0, false).
		AddItem(okbtn, width/4, 0, true).
		AddItem(nil, width/4, 0, false).
		AddItem(cancelbtn, width/4, 0, false).
		SetDirection(tview.FlexColumn)

	items := tview.NewFlex().
		AddItem(nil, 0, 1, false).
		AddItem(textview, height-5, 1, false).
		AddItem(buttonlayout, 1, 0, true).
		AddItem(nil, 0, 1, false).
		SetDirection(tview.FlexRow)

	items.SetBorder(true)

	modal := tview.NewFlex().
		AddItem(nil, 0, 1, false).
		AddItem(items, height, 1, false).
		AddItem(nil, 0, 1, false).
		SetDirection(tview.FlexRow)

	return tview.NewFlex().
		AddItem(nil, 0, 1, false).
		AddItem(modal, width+2, 1, false).
		AddItem(nil, 0, 1, false)
}
<!-- gh-comment-id:1057211824 --> @darkhz commented on GitHub (Mar 2, 2022): Possibly you could do the following: - Create a custom modal/dialog box. For example you could draw a flex, and specifically add a textview and button primitives to it. For each of the buttons, attach a SetFocusFunc, and within the SetFocusFunc you could modify the button's label to include an underline. - Or, you could draw a Form primitive, add buttons to it, get each button using GetButton or GetButtonIndex, and attach a SetFocusFunc to the Button primitive returned by GetButton/GetButtonIndex. This is a code example to illustrate the first point that was mentioned above. ```go package main import ( "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) const ( okButtonString = "OK" cancelButtonString = "Cancel" ) func main() { App := tview.NewApplication() Page := tview.NewPages() ModalTextView := tview.NewTextView(). SetText("Quit?"). SetTextAlign(tview.AlignCenter) ModalButtonOK := tview.NewButton("OK") ModalButtonCancel := tview.NewButton("Cancel") ModalButtonOK.SetExitFunc(func(key tcell.Key) { if key == tcell.KeyTab { App.SetFocus(ModalButtonCancel) // Reset the underlined OK button's label // when not focused. ModalButtonOK.SetLabel(okButtonString) } }) ModalButtonOK.SetSelectedFunc(func() { App.Stop() }) ModalButtonOK.SetFocusFunc(func() { // Underline the OK button's label when focused. ModalButtonOK.SetLabel("[::u]" + okButtonString) }) ModalButtonCancel.SetExitFunc(func(key tcell.Key) { if key == tcell.KeyTab { App.SetFocus(ModalButtonOK) // Reset the underlined Cancel button's label // when not focused. ModalButtonCancel.SetLabel(cancelButtonString) } }) ModalButtonCancel.SetSelectedFunc(func() { Page.SwitchToPage("table") }) ModalButtonCancel.SetFocusFunc(func() { // Underline the Cancel button's label when focused. ModalButtonCancel.SetLabel("[::u]" + cancelButtonString) }) Modal := modal(ModalTextView, ModalButtonOK, ModalButtonCancel, 60, 10) Table := tview.NewTable() Table.SetCellSimple(0, 1, "Press 'q' to quit") Table.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if event.Rune() == 'q' { Page.SwitchToPage("modal").ShowPage("table") App.SetFocus(ModalButtonOK) } return event }) Page.AddPage("table", Table, true, true) Page.AddPage("modal", Modal, true, false) if err := App.SetRoot(Page, true).SetFocus(Table).Run(); err != nil { panic(err) } } func modal(textview, okbtn, cancelbtn tview.Primitive, width, height int) tview.Primitive { buttonlayout := tview.NewFlex(). AddItem(nil, width/8, 0, false). AddItem(okbtn, width/4, 0, true). AddItem(nil, width/4, 0, false). AddItem(cancelbtn, width/4, 0, false). SetDirection(tview.FlexColumn) items := tview.NewFlex(). AddItem(nil, 0, 1, false). AddItem(textview, height-5, 1, false). AddItem(buttonlayout, 1, 0, true). AddItem(nil, 0, 1, false). SetDirection(tview.FlexRow) items.SetBorder(true) modal := tview.NewFlex(). AddItem(nil, 0, 1, false). AddItem(items, height, 1, false). AddItem(nil, 0, 1, false). SetDirection(tview.FlexRow) return tview.NewFlex(). AddItem(nil, 0, 1, false). AddItem(modal, width+2, 1, false). AddItem(nil, 0, 1, false) } ```
Author
Owner

@rivo commented on GitHub (Dec 17, 2022):

There is a Button.SetActivatedStyle(style) function now which will allow you to underline the text for activated buttons. Note that if you're using a button as part of a Form, you should use Form.SetButtonActivatedStyle(style).

<!-- gh-comment-id:1356366170 --> @rivo commented on GitHub (Dec 17, 2022): There is a `Button.SetActivatedStyle(style)` function now which will allow you to underline the text for activated buttons. Note that if you're using a button as part of a `Form`, you should use `Form.SetButtonActivatedStyle(style)`.
Author
Owner

@robgonnella commented on GitHub (Jun 20, 2023):

@rivo first off, thank you for your wonderful contribution with tview! I recently started building a terminal app and have really been enjoying how fun and easy it is to build with the help of this project.

With regard to the modal component, it would be great if we could add access to the SetActivatedStyle method of underlying form in your modal implementation. I'm happy to attempt a PR if you're open to the idea.

<!-- gh-comment-id:1598895037 --> @robgonnella commented on GitHub (Jun 20, 2023): @rivo first off, thank you for your wonderful contribution with tview! I recently started building a terminal app and have really been enjoying how fun and easy it is to build with the help of this project. With regard to the modal component, it would be great if we could add access to the `SetActivatedStyle` method of underlying form in your modal implementation. I'm happy to attempt a PR if you're open to the idea.
Author
Owner

@rivo commented on GitHub (Jun 21, 2023):

@robgonnella Done with the latest commit.

<!-- gh-comment-id:1601213034 --> @rivo commented on GitHub (Jun 21, 2023): @robgonnella Done with the latest commit.
Author
Owner

@robgonnella commented on GitHub (Jun 23, 2023):

Thanks @rivo !

<!-- gh-comment-id:1603596893 --> @robgonnella commented on GitHub (Jun 23, 2023): Thanks @rivo !
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#514
No description provided.