[GH-ISSUE #249] Change textview's text on key press. #195

Closed
opened 2026-03-04 01:02:52 +03:00 by kerem · 7 comments
Owner

Originally created by @emmggi on GitHub (Mar 14, 2019).
Original GitHub issue: https://github.com/rivo/tview/issues/249

Sorry to be asking a question like this but i'm running in circles on how to do this.

I need to trigger the redraw of a textview on a keypress and have the "mode" change from one to another on each F1 keypress and have the text in textview update accordingly but i don't know what's the issue i'm facing.

Also I'm wondering can I set a minimum width for a table cell?

Here's my code

package main

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

// Switch modes on keypress
var mode string = "Mode_1"
func switchmode() {
	if mode == "Mode_1" {
		mode = "Mode_2"
	} else if mode == "Mode_2" {
		mode = "Mode_1"
	}
}

func main(){
	app := tview.NewApplication()
	// Layout the widgets
	grid := tview.NewGrid().
			SetBorders(true)

	// New textview
	textview := tview.NewTextView().
		SetDynamicColors(true).
		SetRegions(true).
		SetWordWrap(true).
		SetTextAlign(tview.AlignCenter).
		SetText(mode)
	textview.SetBackgroundColor(tcell.ColorDefault)

	app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
		k := event.Key()
		if k == tcell.KeyF1 {
			switchmode()
		}
		app.QueueUpdateDraw(func(){
			textview.SetText(mode)
			})
		return event
	})

	// Position
	grid.AddItem(textview, 0, 0, 1, 17, 1, 12, false)

	// Error handling
	if err := tview.NewApplication().SetRoot(grid, true).SetFocus(grid).Run(); err != nil {
		panic(err)
	}

}
Originally created by @emmggi on GitHub (Mar 14, 2019). Original GitHub issue: https://github.com/rivo/tview/issues/249 Sorry to be asking a question like this but i'm running in circles on how to do this. I need to trigger the redraw of a textview on a keypress and have the "mode" change from one to another on each F1 keypress and have the text in textview update accordingly but i don't know what's the issue i'm facing. Also I'm wondering can I set a minimum width for a table cell? Here's my code ``` package main import ( "github.com/gdamore/tcell" "github.com/rivo/tview" ) // Switch modes on keypress var mode string = "Mode_1" func switchmode() { if mode == "Mode_1" { mode = "Mode_2" } else if mode == "Mode_2" { mode = "Mode_1" } } func main(){ app := tview.NewApplication() // Layout the widgets grid := tview.NewGrid(). SetBorders(true) // New textview textview := tview.NewTextView(). SetDynamicColors(true). SetRegions(true). SetWordWrap(true). SetTextAlign(tview.AlignCenter). SetText(mode) textview.SetBackgroundColor(tcell.ColorDefault) app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { k := event.Key() if k == tcell.KeyF1 { switchmode() } app.QueueUpdateDraw(func(){ textview.SetText(mode) }) return event }) // Position grid.AddItem(textview, 0, 0, 1, 17, 1, 12, false) // Error handling if err := tview.NewApplication().SetRoot(grid, true).SetFocus(grid).Run(); err != nil { panic(err) } } ```
kerem closed this issue 2026-03-04 01:02:52 +03:00
Author
Owner

@rivo commented on GitHub (Mar 16, 2019):

Your key capture function doesn't fire because your app object is not used to run the application. At the end of your program, you instantiate a new Application object. It should look like this, however:

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

You also don't need to call QueueUpdateDraw() from within the SetInputCapture() function. It is called implicitly. (See the Wiki for more information.)

So it could like something like this:

package main

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

// Switch modes on keypress
var mode = "Mode_1"

func switchmode(textview *tview.TextView) {
	if mode == "Mode_1" {
		mode = "Mode_2"
	} else if mode == "Mode_2" {
		mode = "Mode_1"
	}
	textview.SetText(mode)
}

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

	// Layout the widgets
	grid := tview.NewGrid().
		SetBorders(true)

	// New textview
	textview := tview.NewTextView().
		SetDynamicColors(true).
		SetRegions(true).
		SetWordWrap(true).
		SetTextAlign(tview.AlignCenter).
		SetText(mode)
	textview.SetBackgroundColor(tcell.ColorDefault)

	app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
		k := event.Key()
		if k == tcell.KeyF1 {
			switchmode(textview)
		}
		return event
	})

	// Position
	grid.AddItem(textview, 0, 0, 1, 17, 1, 12, false)

	// Error handling
	if err := app.SetRoot(grid, true).SetFocus(grid).Run(); err != nil {
		panic(err)
	}

}

Let me know if this answers your question.

<!-- gh-comment-id:473525333 --> @rivo commented on GitHub (Mar 16, 2019): Your key capture function doesn't fire because your `app` object is not used to run the application. At the end of your program, you instantiate a new `Application` object. It should look like this, however: ```go if err := app.SetRoot(grid, true).SetFocus(grid).Run(); err != nil { panic(err) } ``` You also don't need to call `QueueUpdateDraw()` from within the `SetInputCapture()` function. It is called implicitly. (See the [Wiki](https://github.com/rivo/tview/wiki/Concurrency#event-handlers) for more information.) So it could like something like this: ```go package main import ( "github.com/gdamore/tcell" "github.com/rivo/tview" ) // Switch modes on keypress var mode = "Mode_1" func switchmode(textview *tview.TextView) { if mode == "Mode_1" { mode = "Mode_2" } else if mode == "Mode_2" { mode = "Mode_1" } textview.SetText(mode) } func main() { app := tview.NewApplication() // Layout the widgets grid := tview.NewGrid(). SetBorders(true) // New textview textview := tview.NewTextView(). SetDynamicColors(true). SetRegions(true). SetWordWrap(true). SetTextAlign(tview.AlignCenter). SetText(mode) textview.SetBackgroundColor(tcell.ColorDefault) app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { k := event.Key() if k == tcell.KeyF1 { switchmode(textview) } return event }) // Position grid.AddItem(textview, 0, 0, 1, 17, 1, 12, false) // Error handling if err := app.SetRoot(grid, true).SetFocus(grid).Run(); err != nil { panic(err) } } ``` Let me know if this answers your question.
Author
Owner

@emmggi commented on GitHub (Mar 16, 2019):

I still don't understand how i can refresh the screen, sorry. This is from another app, the table is being overwritten by another table without the previous one clearing.

screenshot-2019-03-16_20:50:25

Also is there a way to set a minimum width for a table cell?

I tried to do something like app.Draw() on each enter keypress because that's when i want it to happen. Or table.Clear() but nothing happens.

<!-- gh-comment-id:473578793 --> @emmggi commented on GitHub (Mar 16, 2019): I still don't understand how i can refresh the screen, sorry. This is from another app, the table is being overwritten by another table without the previous one clearing. ![screenshot-2019-03-16_20:50:25](https://user-images.githubusercontent.com/45419292/54480793-2a2f9380-482d-11e9-87d6-9f8e4dc40928.png) Also is there a way to set a minimum width for a table cell? I tried to do something like `app.Draw()` on each enter keypress because that's when i want it to happen. Or `table.Clear()` but nothing happens.
Author
Owner

@rivo commented on GitHub (Mar 16, 2019):

I can't help you with the table without seeing the code for this. Please paste some code that will reproduce this issue.

Again, you don't need to call app.Draw() on each key event as it's called automatically by the main application loop. Unless you're making use of goroutines, you probably don't need to call app.Draw() anywhere at all. You can see in my code above that I'm not calling app.Draw().

There's no minimum width for a table cell. The column width of a table is chosen based on the widest cell in that column. You could pad your cell's text with spaces if you want it to be wider than its contained text.

To answer your other questions, I need more specific information, preferably code.

<!-- gh-comment-id:473591435 --> @rivo commented on GitHub (Mar 16, 2019): I can't help you with the table without seeing the code for this. Please paste some code that will reproduce this issue. Again, you don't need to call `app.Draw()` on each key event as it's called automatically by the main application loop. Unless you're making use of goroutines, you probably don't need to call `app.Draw()` anywhere at all. You can see in my code above that I'm not calling `app.Draw()`. There's no minimum width for a table cell. The column width of a table is chosen based on the widest cell in that column. You could pad your cell's text with spaces if you want it to be wider than its contained text. To answer your other questions, I need more specific information, preferably code.
Author
Owner

@emmggi commented on GitHub (Mar 16, 2019):

@rivo I'd say this is the most important part. The startsearch() function returns a []string. Each two values need to be one next to another in two columns, which they are at the moment. But as mentioned, it's not clearing the screen from the previous table when new one comes.

g, _ := gocui.NewGui(gocui.OutputNormal)
maxW, maxH := g.Size()

// Start a new app
app := tview.NewApplication()

// Layout the widgets
grid := tview.NewGrid().
		SetBorders(true)

// Add an input field for the user input
inputField := tview.NewInputField().
			SetLabel(" Search for: ").
			SetFieldBackgroundColor(tcell.ColorDefault).
			SetFieldTextColor(tcell.ColorWhite).
			SetLabelColor(tcell.ColorWhite)	
inputField.SetBackgroundColor(tcell.ColorDefault)

// Table for the results
table := tview.NewTable().SetBorders(true)
table.SetBackgroundColor(tcell.ColorDefault)


// Remove the entered text once the search has been done
inputField.SetDoneFunc(func(key tcell.Key){
	// Iterating over the results
	rows := len(startSearch(inputField.GetText()))
	results := startSearch(inputField.GetText())

	for row := 0; row < rows -1; row+=2 {
		table.SetCell(row, 0, tview.NewTableCell(results[row]).
									SetMaxWidth(maxW / 2))
		table.SetCell(row, 1, tview.NewTableCell(results[row + 1]).
									SetMaxWidth(maxW / 2))
	}
	inputField.SetText("")
	})

// Textview that changes on changed mode
textview := tview.NewTextView().
	SetDynamicColors(true).
	SetRegions(true).
	SetWordWrap(true).
	SetTextAlign(tview.AlignCenter).
	SetText(mode)
textview.SetBackgroundColor(tcell.ColorDefault)

// Handle keypress
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
	switch event.Key() {
		case tcell.KeyF1:
			switchmode(textview)
	}

	return event
})

// Position the widgets
// p primitive, row, column, rowSpan, colSpan, minGridHeight, minGridWidth, focus bool
grid.AddItem(inputField,   0,         1,          1, maxW - 21,  1,       30, true)
grid.AddItem(textview,     0, maxW - 18,          1,        17,  1,       12, false)
grid.AddItem(table,        2,          1, maxH - 30,  maxW - 2, 30, maxW - 1, false)	

// Error handling
if err := app.SetRoot(grid, true).SetFocus(grid).Run(); err != nil {
	panic(err)
}
<!-- gh-comment-id:473592802 --> @emmggi commented on GitHub (Mar 16, 2019): @rivo I'd say this is the most important part. The `startsearch()` function returns a `[]string`. Each two values need to be one next to another in two columns, which they are at the moment. But as mentioned, it's not clearing the screen from the previous table when new one comes. ```go g, _ := gocui.NewGui(gocui.OutputNormal) maxW, maxH := g.Size() // Start a new app app := tview.NewApplication() // Layout the widgets grid := tview.NewGrid(). SetBorders(true) // Add an input field for the user input inputField := tview.NewInputField(). SetLabel(" Search for: "). SetFieldBackgroundColor(tcell.ColorDefault). SetFieldTextColor(tcell.ColorWhite). SetLabelColor(tcell.ColorWhite) inputField.SetBackgroundColor(tcell.ColorDefault) // Table for the results table := tview.NewTable().SetBorders(true) table.SetBackgroundColor(tcell.ColorDefault) // Remove the entered text once the search has been done inputField.SetDoneFunc(func(key tcell.Key){ // Iterating over the results rows := len(startSearch(inputField.GetText())) results := startSearch(inputField.GetText()) for row := 0; row < rows -1; row+=2 { table.SetCell(row, 0, tview.NewTableCell(results[row]). SetMaxWidth(maxW / 2)) table.SetCell(row, 1, tview.NewTableCell(results[row + 1]). SetMaxWidth(maxW / 2)) } inputField.SetText("") }) // Textview that changes on changed mode textview := tview.NewTextView(). SetDynamicColors(true). SetRegions(true). SetWordWrap(true). SetTextAlign(tview.AlignCenter). SetText(mode) textview.SetBackgroundColor(tcell.ColorDefault) // Handle keypress app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { switch event.Key() { case tcell.KeyF1: switchmode(textview) } return event }) // Position the widgets // p primitive, row, column, rowSpan, colSpan, minGridHeight, minGridWidth, focus bool grid.AddItem(inputField, 0, 1, 1, maxW - 21, 1, 30, true) grid.AddItem(textview, 0, maxW - 18, 1, 17, 1, 12, false) grid.AddItem(table, 2, 1, maxH - 30, maxW - 2, 30, maxW - 1, false) // Error handling if err := app.SetRoot(grid, true).SetFocus(grid).Run(); err != nil { panic(err) } ```
Author
Owner

@emmggi commented on GitHub (Mar 20, 2019):

Can this one be reopened? I'm still having issues with this.

<!-- gh-comment-id:474936116 --> @emmggi commented on GitHub (Mar 20, 2019): Can this one be reopened? I'm still having issues with this.
Author
Owner

@rivo commented on GitHub (Aug 29, 2019):

This issue kind of got lost. Apologies for that.

It seems that there's a misunderstanding regarding how Grid.AddItem() works. Here's the documentation for it. And you might also want to check out the documentation for Grid.SetColumns().

Grid rows and columns don't refer to the individual cells in a terminal window. A column can be multiple cells wide and a row can be multiple cells tall. In your example, let's assume that maxW = 120. Now you're telling tview that your table spans maxW - 2 = 118 columns. Does your Grid layout really have that many columns? (Because you include borders, you must have a huge screen for that.)

I wouldn't be surprised that given these numbers, you are getting some weird artefacts.

First of all, I think you don't need maxW or maxH at all in your application. The idea of tview is to let you create layouts independently of the actual screen resolution. Without having tested it, this is probably what you want:

// Position the widgets
// p primitive, row, column, rowSpan, colSpan, minGridHeight, minGridWidth, focus bool
grid.SetRows(1)
grid.AddItem(inputField, 0, 0, 1, 1, 0, 0, true)
grid.AddItem(textview, 0, 1, 1, 1, 0, 0, false)
grid.AddItem(table, 1, 0, 1, 2, 0, 0, false)

I'll reopen this again for a while in case you still have questions.

<!-- gh-comment-id:526231162 --> @rivo commented on GitHub (Aug 29, 2019): This issue kind of got lost. Apologies for that. It seems that there's a misunderstanding regarding how `Grid.AddItem()` works. [Here's the documentation for it](https://godoc.org/github.com/rivo/tview#Grid.AddItem). And you might also want to check out the documentation for [`Grid.SetColumns()`](https://godoc.org/github.com/rivo/tview#Grid.SetColumns). Grid rows and columns don't refer to the individual cells in a terminal window. A column can be multiple cells wide and a row can be multiple cells tall. In your example, let's assume that `maxW = 120`. Now you're telling `tview` that your table spans `maxW - 2 = 118` columns. Does your `Grid` layout really have that many columns? (Because you include borders, you must have a huge screen for that.) I wouldn't be surprised that given these numbers, you are getting some weird artefacts. First of all, I think you don't need `maxW` or `maxH` at all in your application. The idea of `tview` is to let you create layouts independently of the actual screen resolution. Without having tested it, this is probably what you want: ```go // Position the widgets // p primitive, row, column, rowSpan, colSpan, minGridHeight, minGridWidth, focus bool grid.SetRows(1) grid.AddItem(inputField, 0, 0, 1, 1, 0, 0, true) grid.AddItem(textview, 0, 1, 1, 1, 0, 0, false) grid.AddItem(table, 1, 0, 1, 2, 0, 0, false) ``` I'll reopen this again for a while in case you still have questions.
Author
Owner

@emmggi commented on GitHub (Aug 29, 2019):

I've given up on using tview for this project. Thanks still for letting me know on the fix.

<!-- gh-comment-id:526332581 --> @emmggi commented on GitHub (Aug 29, 2019): I've given up on using tview for this project. Thanks still for letting me know on the fix.
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#195
No description provided.