[GH-ISSUE #65] Project is missing examples/demos #48

Closed
opened 2026-03-04 01:01:25 +03:00 by kerem · 11 comments
Owner

Originally created by @KingRikkie on GitHub (Mar 5, 2018).
Original GitHub issue: https://github.com/rivo/tview/issues/65

First I'd like to tell you that your project certainly is awesome and the most complete TUI package. I looked into gocui first but as development has been on hold since over half a year now I went with tview.

Unfortunately your repo is lacking examples and demo applications, for example how do I colorize Text per line inside a TextView without enabling dynamic color? What about #52 ? How do I use SetAfterDrawFunc() and alike in a real world project? How to alter SetBorder() to only show a single border line instead of the default double-line on focus? I have a hard time getting through the needed setup/adjustments for my solution (but maybe it's just me).

gocui has a nice list of projects using it where I can look at different implementions for a broad spectrum of various elements and methods. tview is young but if we don't have anything to link yet then please provide more (pseudo) real world examples, Postgres is not enough.

Do you have any complex (WIP) demos? Demo

Originally created by @KingRikkie on GitHub (Mar 5, 2018). Original GitHub issue: https://github.com/rivo/tview/issues/65 First I'd like to tell you that your project certainly is awesome and the most complete TUI package. I looked into [gocui](https://github.com/jroimartin/gocui) first but as development has been on hold since over half a year now I went with tview. Unfortunately your repo is lacking examples and demo applications, for example how do I colorize Text per line inside a `TextView` without enabling dynamic color? What about #52 ? How do I use `SetAfterDrawFunc()` and alike in a real world project? How to alter `SetBorder()` to only show a single border line instead of the default double-line on focus? I have a hard time getting through the needed setup/adjustments for my solution (but maybe it's just me). gocui has [a nice list](https://github.com/jroimartin/gocui#projects-using-gocui) of projects using it where I can look at different implementions for a broad spectrum of various elements and methods. tview is young but if we don't have anything to link yet then please provide more (pseudo) real world examples, [Postgres](https://github.com/rivo/tview/wiki/Postgres) is not enough. Do you have any complex (WIP) demos? ![Demo](https://cloud.githubusercontent.com/assets/1223476/19418932/63645052-93ce-11e6-867c-da5e97e37237.png)
kerem closed this issue 2026-03-04 01:01:25 +03:00
Author
Owner

@rivo commented on GitHub (Mar 5, 2018):

Hi, I'm glad you like tview. Let me address your questions:

Unfortunately your repo is lacking examples and demo applications

There are a bunch of demos in the repo and in the Wiki. They're meant to get you started with the different primitives so I kept them fairly simple. If you want to go more in-depth, you will have to refer to the documentation.

how do I colorize Text per line inside a TextView without enabling dynamic color?

This is not possible. If you want to colorize text partially, you will need to use dynamic colors.

What about #52 ?

Not sure what your question is here.

How do I use SetAfterDrawFunc() and alike in a real world project?

This really depends on what you need to do. If you want to know the background to this function, take a look at #58.

How to alter SetBorder() to only show a single border line instead of the default double-line on focus? I have a hard time getting through the needed setup/adjustments for my solution (but maybe it's just me).

If you want to change the default look of the Box borders, you'll need to use SetDrawFunc(). Basically, you'll have to draw your own borders. In #57, there was a longer discussion about this.

tview is young but if we don't have anything to link yet then please provide more (pseudo) real world examples

If you have an interesting public project that uses tview, I'm happy to link to it. So far, I haven't had any requests to add links. Probably because, you're right, tviewis still young.

Do you have any complex (WIP) demos?

The most complex demo I'm providing is the one in the demos/presentation directory. It shows a lot of functionality (but not all, of course).

<!-- gh-comment-id:370420985 --> @rivo commented on GitHub (Mar 5, 2018): Hi, I'm glad you like `tview`. Let me address your questions: > Unfortunately your repo is lacking examples and demo applications There are a bunch of demos in the repo and in the Wiki. They're meant to get you started with the different primitives so I kept them fairly simple. If you want to go more in-depth, you will have to refer to the documentation. > how do I colorize Text per line inside a `TextView` without enabling dynamic color? This is not possible. If you want to colorize text partially, you will need to use dynamic colors. > What about #52 ? Not sure what your question is here. > How do I use `SetAfterDrawFunc()` and alike in a real world project? This really depends on what you need to do. If you want to know the background to this function, take a look at #58. > How to alter `SetBorder()` to only show a single border line instead of the default double-line on focus? I have a hard time getting through the needed setup/adjustments for my solution (but maybe it's just me). If you want to change the default look of the `Box` borders, you'll need to use [`SetDrawFunc()`](https://godoc.org/github.com/rivo/tview#Box.SetDrawFunc). Basically, you'll have to draw your own borders. In #57, there was a longer discussion about this. > tview is young but if we don't have anything to link yet then please provide more (pseudo) real world examples If you have an interesting public project that uses `tview`, I'm happy to link to it. So far, I haven't had any requests to add links. Probably because, you're right, `tview`is still young. > Do you have any complex (WIP) demos? The most complex demo I'm providing is the one in the `demos/presentation` directory. It shows a lot of functionality (but not all, of course).
Author
Owner

@KingRikkie commented on GitHub (Mar 5, 2018):

Thank you for your reply.

If you want to go more in-depth, you will have to refer to the documentation.

The documentation is great for reference but not so much for snippets or use cases.

If you want to colorize text partially, you will need to use dynamic colors.

One could create a custom "CrappyConsoleLikeTextView" element by adding a Frame around a Box and call AddText() onto it as it takes a tcell.Color as argument like you did in cover.go.

I was thinking about (ab)using a Table to show a different color per line, row by row.
My problem with [color] is that the text I am going to show will can contain square brackets on its own and this could potentially lead to breaking the style. But I guess I'll fall back to inserting an opening square bracket in front of every closing bracket, ugly but works.

What about #52 ?

Not sure what your question is here.

Access to Print() and the needed tcell.screen at least for Box would be really helpful making it more dynamic. I saw that you rejected a PR about this already, but maybe Box is just the right barebone for it (or a wiki entry about how to create a basic custom primitive).

<!-- gh-comment-id:370514614 --> @KingRikkie commented on GitHub (Mar 5, 2018): Thank you for your reply. > If you want to go more in-depth, you will have to refer to the documentation. The documentation is great for reference but not so much for snippets or use cases. > If you want to colorize text partially, you will need to use dynamic colors. One could create a custom "CrappyConsoleLikeTextView" element by adding a `Frame` around a `Box` and call `AddText()` onto it as it takes a `tcell.Color` as argument like you did in [cover.go](https://github.com/rivo/tview/blob/master/demos/presentation/cover.go). I was thinking about (ab)using a `Table` to show a different color per line, row by row. My problem with `[color]` is that the text I am going to show will can contain square brackets on its own and this could potentially lead to breaking the style. But I guess I'll fall back to inserting an opening square bracket in front of every closing bracket, ugly but works. >>What about #52 ? >Not sure what your question is here. Access to `Print()` and the needed `tcell.screen` at least for `Box` would be really helpful making it more dynamic. I saw that you rejected a PR about this already, but maybe Box is just the right barebone for it (or a wiki entry about how to create a basic custom primitive).
Author
Owner

@rivo commented on GitHub (Mar 6, 2018):

Frame could be a solution but note that it also uses color tags. Pretty much everything does now. (People wanted to be able to individually color almost all text.)

Depending on what else you need (e.g. scrolling, selections), I would probably still use TextView:

for _, line := range lines {
	fmt.Fprintf(textView, "[%s]%s\n", color, line)
}

If your line content potentially contains strings that look like a color tag, I could offer you to add an Escape() function to tview which makes them appear as original text again. Let me know if you need that.

Access to Print() and the needed tcell.screen at least for Box would be really helpful making it more dynamic.

As mentioned, this exists.

<!-- gh-comment-id:370769304 --> @rivo commented on GitHub (Mar 6, 2018): `Frame` could be a solution but note that it also uses color tags. Pretty much everything does now. (People wanted to be able to individually color almost all text.) Depending on what else you need (e.g. scrolling, selections), I would probably still use `TextView`: ```go for _, line := range lines { fmt.Fprintf(textView, "[%s]%s\n", color, line) } ``` If your line content potentially contains strings that look like a color tag, I could offer you to add an `Escape()` function to `tview` which makes them appear as original text again. Let me know if you need that. > Access to `Print()` and the needed `tcell.screen` at least for `Box` would be really helpful making it more dynamic. As mentioned, [this exists](https://godoc.org/github.com/rivo/tview#Box.SetDrawFunc).
Author
Owner

@KingRikkie commented on GitHub (Mar 9, 2018):

I could offer you to add an Escape() function to tview which makes them appear as original text again.

Simple enough, can be defined globally:

var bracketsEscaper = strings.NewReplacer("]", "[]")
fmt.Fprintln(textView, "[red]" + bracketsEscaper.Replace(stringWithBrackets))

As mentioned, this exists.

Figured that out already, but is there a way to add a callback, executing it and then setting back the previously installed one (if any)? Modal unfortunately does not support a "pop-over" so I am creating my own ShowModal(title string, text string, yesText string, noText string, yesFunc func(), noFunc func()) that will install a callback onto app.SetAfterDrawFunc, draws a simple modal on top of screen, waits for user decision, then removes the handler and redraws screen. Doing app.SetAfterDrawFunc(nil) will however remove any possible other callback so my solution can't be used as a simple blackbox.

<!-- gh-comment-id:371768456 --> @KingRikkie commented on GitHub (Mar 9, 2018): > I could offer you to add an `Escape()` function to `tview` which makes them appear as original text again. Simple enough, can be defined globally: ```go var bracketsEscaper = strings.NewReplacer("]", "[]") fmt.Fprintln(textView, "[red]" + bracketsEscaper.Replace(stringWithBrackets)) ``` > As mentioned, this exists. Figured that out already, but is there a way to add a callback, executing it and then setting back the previously installed one (if any)? Modal unfortunately does not support a "pop-over" so I am creating my own `ShowModal(title string, text string, yesText string, noText string, yesFunc func(), noFunc func())` that will install a callback onto app.SetAfterDrawFunc, draws a simple modal on top of screen, waits for user decision, then removes the handler and redraws screen. Doing `app.SetAfterDrawFunc(nil)` will however remove any possible other callback so my solution can't be used as a simple blackbox.
Author
Owner

@rivo commented on GitHub (Mar 10, 2018):

Do you mean it would help you to have getters, too? I.e. app.GetBeforeDrawFunc() and app.GetAfterDrawFunc()? Or would that still cause problems in your example?

<!-- gh-comment-id:372026384 --> @rivo commented on GitHub (Mar 10, 2018): Do you mean it would help you to have getters, too? I.e. `app.GetBeforeDrawFunc()` and `app.GetAfterDrawFunc()`? Or would that still cause problems in your example?
Author
Owner

@KingRikkie commented on GitHub (Mar 10, 2018):

If one could do:

prevHandler := app.GetAfterDrawFunc()
app.SetAfterDrawFunc(someModalHandler)
...
app.SetAfterDrawFunc(nil)
...
app.SetAfterDrawFunc(prevHandler)
<!-- gh-comment-id:372049425 --> @KingRikkie commented on GitHub (Mar 10, 2018): If one could do: ```go prevHandler := app.GetAfterDrawFunc() app.SetAfterDrawFunc(someModalHandler) ... app.SetAfterDrawFunc(nil) ... app.SetAfterDrawFunc(prevHandler) ```
Author
Owner

@rivo commented on GitHub (Mar 10, 2018):

I added these getters. Let me know if this helps you.

If not, I can always reopen this issue.

<!-- gh-comment-id:372072460 --> @rivo commented on GitHub (Mar 10, 2018): I added these getters. Let me know if this helps you. If not, I can always reopen this issue.
Author
Owner

@KingRikkie commented on GitHub (Apr 5, 2018):

@rivo
Yes these getters are fitting nicely.
In case anyone wants a basic modal pop-up:

modal.go

package main

import (
    "strings"

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

// ShowModal prompts user a basic modal on top of current view
func ShowModal(title string, text string, width int, height int, yesFunc func(), noFunc func(), yesKey tcell.Key, noKey tcell.Key) {
    afterDrawHandler = app.GetAfterDrawFunc()
    inputCaptureHandler = app.GetInputCapture()

    app.SetAfterDrawFunc(func(screen tcell.Screen) {
        // draw modal as simple box
        app.Lock()
        screenWidth, screenHeight := screen.Size()
        centerY := screenHeight / 2
        centerX := screenWidth / 2

        // draw top and bottom line
        for cx := centerX - (width / 2); cx < centerX+(width/2); cx++ {
            screen.SetContent(cx, centerY+(height/2), tview.GraphicsDbVertBar, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
            screen.SetContent(cx, centerY-(height/2)-1, tview.GraphicsDbVertBar, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
        }

        // draw vertical lines
        for cy := centerY - (height / 2); cy < centerY+(height/2); cy++ {
            screen.SetContent(centerX+(width/2), cy, tview.GraphicsDbHorBar, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
            screen.SetContent(centerX-(width/2)-1, cy, tview.GraphicsDbHorBar, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
        }

        // set corners
        screen.SetContent(centerX-(width/2)-1, centerY+(height/2), tview.GraphicsDbBottomLeftCorner, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
        screen.SetContent(centerX+(width/2), centerY+(height/2), tview.GraphicsDbBottomRightCorner, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
        screen.SetContent(centerX-(width/2)-1, centerY-(height/2)-1, tview.GraphicsDbTopLeftCorner, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
        screen.SetContent(centerX+(width/2), centerY-(height/2)-1, tview.GraphicsDbTopRightCorner, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))

        // clear box area
        for cy := centerY - (height / 2); cy < centerY+(height/2); cy++ {
            for cx := centerX - (width / 2); cx < centerX+(width/2); cx++ {
                screen.SetContent(cx, cy, '\u2588', nil, tcell.StyleDefault.Foreground(tcell.ColorBlack))
            }
        }

        // write title if any
        if title != "" {
            tview.Print(screen, title, centerX-((width/2)-2), centerY-((height/2)+1), width-4, tview.AlignLeft, tcell.ColorWhite)
        }

        // write text
        if text != "" {
            textLine := strings.Split(text, "\n")
            for i, line := range textLine {
                if i > ((height / 2) + 1) {
                    break
                }
                tview.Print(screen, line, centerX-((width/2)-1), centerY-(height/2)+1+i, width, tview.AlignLeft, tcell.ColorWhite)
            }
        }

        app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
            if event.Key() == yesKey || event.Key() == noKey {
                app.SetAfterDrawFunc(nil)
                if afterDrawHandler != nil {
                    app.SetAfterDrawFunc(afterDrawHandler)
                }
                app.SetInputCapture(nil)
                if inputCaptureHandler != nil {
                    app.SetInputCapture(inputCaptureHandler)
                }
                app.Draw()

                if event.Key() == noKey {
                    noFunc()
                } else if event.Key() == yesKey {
                    yesFunc()
                }
            }
            return event
        })

        app.Unlock()
    })
}

main.go

...
var app *tview.Application // the tview base applicaion
var afterDrawHandler func(screen tcell.Screen)                        // afterDrawHandler in case we overwrite it
var inputCaptureHandler (func(event *tcell.EventKey) *tcell.EventKey) // inputCaptureHandler in case we overwrite it
...
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
    if event.Key() == tcell.KeyEsc {
        ShowModal("Exit Application?", "Press [red][ENTER[][white] to exit\nPress [red][ESC[][white] to return", 40, 8, func() { app.Stop() }, func() {}, tcell.KeyEnter, tcell.KeyEsc)
    }
    return event
})
<!-- gh-comment-id:378901144 --> @KingRikkie commented on GitHub (Apr 5, 2018): @rivo Yes these getters are fitting nicely. In case anyone wants a basic modal pop-up: **modal.go** ```golang package main import ( "strings" "github.com/gdamore/tcell" "github.com/rivo/tview" ) // ShowModal prompts user a basic modal on top of current view func ShowModal(title string, text string, width int, height int, yesFunc func(), noFunc func(), yesKey tcell.Key, noKey tcell.Key) { afterDrawHandler = app.GetAfterDrawFunc() inputCaptureHandler = app.GetInputCapture() app.SetAfterDrawFunc(func(screen tcell.Screen) { // draw modal as simple box app.Lock() screenWidth, screenHeight := screen.Size() centerY := screenHeight / 2 centerX := screenWidth / 2 // draw top and bottom line for cx := centerX - (width / 2); cx < centerX+(width/2); cx++ { screen.SetContent(cx, centerY+(height/2), tview.GraphicsDbVertBar, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(cx, centerY-(height/2)-1, tview.GraphicsDbVertBar, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) } // draw vertical lines for cy := centerY - (height / 2); cy < centerY+(height/2); cy++ { screen.SetContent(centerX+(width/2), cy, tview.GraphicsDbHorBar, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(centerX-(width/2)-1, cy, tview.GraphicsDbHorBar, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) } // set corners screen.SetContent(centerX-(width/2)-1, centerY+(height/2), tview.GraphicsDbBottomLeftCorner, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(centerX+(width/2), centerY+(height/2), tview.GraphicsDbBottomRightCorner, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(centerX-(width/2)-1, centerY-(height/2)-1, tview.GraphicsDbTopLeftCorner, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(centerX+(width/2), centerY-(height/2)-1, tview.GraphicsDbTopRightCorner, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) // clear box area for cy := centerY - (height / 2); cy < centerY+(height/2); cy++ { for cx := centerX - (width / 2); cx < centerX+(width/2); cx++ { screen.SetContent(cx, cy, '\u2588', nil, tcell.StyleDefault.Foreground(tcell.ColorBlack)) } } // write title if any if title != "" { tview.Print(screen, title, centerX-((width/2)-2), centerY-((height/2)+1), width-4, tview.AlignLeft, tcell.ColorWhite) } // write text if text != "" { textLine := strings.Split(text, "\n") for i, line := range textLine { if i > ((height / 2) + 1) { break } tview.Print(screen, line, centerX-((width/2)-1), centerY-(height/2)+1+i, width, tview.AlignLeft, tcell.ColorWhite) } } app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if event.Key() == yesKey || event.Key() == noKey { app.SetAfterDrawFunc(nil) if afterDrawHandler != nil { app.SetAfterDrawFunc(afterDrawHandler) } app.SetInputCapture(nil) if inputCaptureHandler != nil { app.SetInputCapture(inputCaptureHandler) } app.Draw() if event.Key() == noKey { noFunc() } else if event.Key() == yesKey { yesFunc() } } return event }) app.Unlock() }) } ``` **main.go** ```golang ... var app *tview.Application // the tview base applicaion var afterDrawHandler func(screen tcell.Screen) // afterDrawHandler in case we overwrite it var inputCaptureHandler (func(event *tcell.EventKey) *tcell.EventKey) // inputCaptureHandler in case we overwrite it ... app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if event.Key() == tcell.KeyEsc { ShowModal("Exit Application?", "Press [red][ENTER[][white] to exit\nPress [red][ESC[][white] to return", 40, 8, func() { app.Stop() }, func() {}, tcell.KeyEnter, tcell.KeyEsc) } return event }) ```
Author
Owner

@lnxbil commented on GitHub (Nov 29, 2018):

The stated example is not working anymore, any hints on what has changed besides the styling stuff?

package main

import (
	"strings"

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

var app *tview.Application                                            // the tview base applicaion
var afterDrawHandler func(screen tcell.Screen)                        // afterDrawHandler in case we overwrite it
var inputCaptureHandler (func(event *tcell.EventKey) *tcell.EventKey) // inputCaptureHandler in case we overwrite it

// ShowModal prompts user a basic modal on top of current view
func ShowModal(title string, text string, width int, height int, yesFunc func(), noFunc func(), yesKey tcell.Key, noKey tcell.Key) {
	afterDrawHandler = app.GetAfterDrawFunc()
	inputCaptureHandler = app.GetInputCapture()

	app.SetAfterDrawFunc(func(screen tcell.Screen) {
		// draw modal as simple box
		app.Lock()
		screenWidth, screenHeight := screen.Size()
		centerY := screenHeight / 2
		centerX := screenWidth / 2
		// draw top and bottom line
		for cx := centerX - (width / 2); cx < centerX+(width/2); cx++ {
			screen.SetContent(cx, centerY+(height/2), tview.BoxDrawingsDoubleVertical, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
			screen.SetContent(cx, centerY-(height/2)-1, tview.BoxDrawingsDoubleVertical, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
		}

		// draw vertical lines
		for cy := centerY - (height / 2); cy < centerY+(height/2); cy++ {
			screen.SetContent(centerX+(width/2), cy, tview.BoxDrawingsDoubleHorizontal, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
			screen.SetContent(centerX-(width/2)-1, cy, tview.BoxDrawingsDoubleHorizontal, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
		}

		// set corners
		screen.SetContent(centerX-(width/2)-1, centerY+(height/2), tview.BoxDrawingsDoubleDownAndLeft, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
		screen.SetContent(centerX+(width/2), centerY+(height/2), tview.BoxDrawingsDoubleDownAndRight, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
		screen.SetContent(centerX-(width/2)-1, centerY-(height/2)-1, tview.BoxDrawingsDoubleUpAndLeft, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
		screen.SetContent(centerX+(width/2), centerY-(height/2)-1, tview.BoxDrawingsDoubleUpAndRight, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))

		// clear box area
		for cy := centerY - (height / 2); cy < centerY+(height/2); cy++ {
			for cx := centerX - (width / 2); cx < centerX+(width/2); cx++ {
				screen.SetContent(cx, cy, '\u2588', nil, tcell.StyleDefault.Foreground(tcell.ColorBlack))
			}
		}

		// write title if any
		if title != "" {
			tview.Print(screen, title, centerX-((width/2)-2), centerY-((height/2)+1), width-4, tview.AlignLeft, tcell.ColorWhite)
		}

		// write text
		if text != "" {
			textLine := strings.Split(text, "\n")
			for i, line := range textLine {
				if i > ((height / 2) + 1) {
					break
				}
				tview.Print(screen, line, centerX-((width/2)-1), centerY-(height/2)+1+i, width, tview.AlignLeft, tcell.ColorWhite)
			}
		}

		app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
			if event.Key() == yesKey || event.Key() == noKey {
				app.SetAfterDrawFunc(nil)
				if afterDrawHandler != nil {
					app.SetAfterDrawFunc(afterDrawHandler)
				}
				app.SetInputCapture(nil)
				if inputCaptureHandler != nil {
					app.SetInputCapture(inputCaptureHandler)
				}
				app.Draw()

				if event.Key() == noKey {
					noFunc()
				} else if event.Key() == yesKey {
					yesFunc()
				}
			}
			return event
		})

		app.Unlock()
	})
}

func main() {
	app = tview.NewApplication()
	button := tview.NewButton("Hit Enter to close").SetSelectedFunc(func() {
		ShowModal("Title", "Text", 31, 10, func() { app.Stop() }, func() {}, 0, 0)
	})
	button.SetBorder(true).SetRect(0, 0, 22, 3)
	if err := app.SetRoot(button, false).Run(); err != nil {
		panic(err)
	}
}

<!-- gh-comment-id:442850893 --> @lnxbil commented on GitHub (Nov 29, 2018): The stated example is not working anymore, any hints on what has changed besides the styling stuff? ```go package main import ( "strings" "github.com/gdamore/tcell" "github.com/rivo/tview" ) var app *tview.Application // the tview base applicaion var afterDrawHandler func(screen tcell.Screen) // afterDrawHandler in case we overwrite it var inputCaptureHandler (func(event *tcell.EventKey) *tcell.EventKey) // inputCaptureHandler in case we overwrite it // ShowModal prompts user a basic modal on top of current view func ShowModal(title string, text string, width int, height int, yesFunc func(), noFunc func(), yesKey tcell.Key, noKey tcell.Key) { afterDrawHandler = app.GetAfterDrawFunc() inputCaptureHandler = app.GetInputCapture() app.SetAfterDrawFunc(func(screen tcell.Screen) { // draw modal as simple box app.Lock() screenWidth, screenHeight := screen.Size() centerY := screenHeight / 2 centerX := screenWidth / 2 // draw top and bottom line for cx := centerX - (width / 2); cx < centerX+(width/2); cx++ { screen.SetContent(cx, centerY+(height/2), tview.BoxDrawingsDoubleVertical, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(cx, centerY-(height/2)-1, tview.BoxDrawingsDoubleVertical, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) } // draw vertical lines for cy := centerY - (height / 2); cy < centerY+(height/2); cy++ { screen.SetContent(centerX+(width/2), cy, tview.BoxDrawingsDoubleHorizontal, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(centerX-(width/2)-1, cy, tview.BoxDrawingsDoubleHorizontal, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) } // set corners screen.SetContent(centerX-(width/2)-1, centerY+(height/2), tview.BoxDrawingsDoubleDownAndLeft, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(centerX+(width/2), centerY+(height/2), tview.BoxDrawingsDoubleDownAndRight, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(centerX-(width/2)-1, centerY-(height/2)-1, tview.BoxDrawingsDoubleUpAndLeft, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(centerX+(width/2), centerY-(height/2)-1, tview.BoxDrawingsDoubleUpAndRight, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) // clear box area for cy := centerY - (height / 2); cy < centerY+(height/2); cy++ { for cx := centerX - (width / 2); cx < centerX+(width/2); cx++ { screen.SetContent(cx, cy, '\u2588', nil, tcell.StyleDefault.Foreground(tcell.ColorBlack)) } } // write title if any if title != "" { tview.Print(screen, title, centerX-((width/2)-2), centerY-((height/2)+1), width-4, tview.AlignLeft, tcell.ColorWhite) } // write text if text != "" { textLine := strings.Split(text, "\n") for i, line := range textLine { if i > ((height / 2) + 1) { break } tview.Print(screen, line, centerX-((width/2)-1), centerY-(height/2)+1+i, width, tview.AlignLeft, tcell.ColorWhite) } } app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if event.Key() == yesKey || event.Key() == noKey { app.SetAfterDrawFunc(nil) if afterDrawHandler != nil { app.SetAfterDrawFunc(afterDrawHandler) } app.SetInputCapture(nil) if inputCaptureHandler != nil { app.SetInputCapture(inputCaptureHandler) } app.Draw() if event.Key() == noKey { noFunc() } else if event.Key() == yesKey { yesFunc() } } return event }) app.Unlock() }) } func main() { app = tview.NewApplication() button := tview.NewButton("Hit Enter to close").SetSelectedFunc(func() { ShowModal("Title", "Text", 31, 10, func() { app.Stop() }, func() {}, 0, 0) }) button.SetBorder(true).SetRect(0, 0, 22, 3) if err := app.SetRoot(button, false).Run(); err != nil { panic(err) } } ```
Author
Owner

@rivo commented on GitHub (Dec 3, 2018):

You'll need to make two modifications:

  • Remove the app.Lock() and app.Unlock() statements. The before/after draw handlers are already executed in a locked state. (I didn't realize they were there in the old example but they weren't needed back then, either.) If you leave them in, you'll get a deadlock, which is why the example gets stuck.
  • The BoxDrawings... constants were named wrong. #126 fixed this and also refactored it. It's better to use tview.Borders.Horizontal etc.

Let me know if there's anything else.

<!-- gh-comment-id:443661813 --> @rivo commented on GitHub (Dec 3, 2018): You'll need to make two modifications: - Remove the `app.Lock()` and `app.Unlock()` statements. The before/after draw handlers are already executed in a locked state. (I didn't realize they were there in the old example but they weren't needed back then, either.) If you leave them in, you'll get a deadlock, which is why the example gets stuck. - The `BoxDrawings...` constants were named wrong. #126 fixed this and also refactored it. It's better to use [`tview.Borders.Horizontal`](https://godoc.org/github.com/rivo/tview#pkg-variables) etc. Let me know if there's anything else.
Author
Owner

@lnxbil commented on GitHub (Dec 10, 2018):

Okay, that seems to work.

So, just for completeness, the corrected example (only the named remarks) for anyone stumbling on this long closed issue as I did.

package main

import (
	"strings"

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

var app *tview.Application                                            // the tview base applicaion
var afterDrawHandler func(screen tcell.Screen)                        // afterDrawHandler in case we overwrite it
var inputCaptureHandler (func(event *tcell.EventKey) *tcell.EventKey) // inputCaptureHandler in case we overwrite it

// ShowModal prompts user a basic modal on top of current view
func ShowModal(title string, text string, width int, height int, yesFunc func(), noFunc func(), yesKey tcell.Key, noKey tcell.Key) {
	afterDrawHandler = app.GetAfterDrawFunc()
	inputCaptureHandler = app.GetInputCapture()

	app.SetAfterDrawFunc(func(screen tcell.Screen) {
		// draw modal as simple box
		screenWidth, screenHeight := screen.Size()
		centerY := screenHeight / 2
		centerX := screenWidth / 2
		// draw top and bottom line
		for cx := centerX - (width / 2); cx < centerX+(width/2); cx++ {
			screen.SetContent(cx, centerY+(height/2), tview.Borders.Horizontal, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
			screen.SetContent(cx, centerY-(height/2)-1, tview.Borders.Horizontal, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
		}

		// draw vertical lines
		for cy := centerY - (height / 2); cy < centerY+(height/2); cy++ {
			screen.SetContent(centerX+(width/2), cy, tview.Borders.Vertical, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
			screen.SetContent(centerX-(width/2)-1, cy, tview.Borders.Vertical, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
		}

		// set corners
		screen.SetContent(centerX-(width/2)-1, centerY+(height/2), tview.Borders.BottomLeft, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
		screen.SetContent(centerX+(width/2), centerY+(height/2), tview.Borders.BottomRight, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
		screen.SetContent(centerX-(width/2)-1, centerY-(height/2)-1, tview.Borders.TopLeft, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))
		screen.SetContent(centerX+(width/2), centerY-(height/2)-1, tview.Borders.TopRight, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite))

		// clear box area
		for cy := centerY - (height / 2); cy < centerY+(height/2); cy++ {
			for cx := centerX - (width / 2); cx < centerX+(width/2); cx++ {
				screen.SetContent(cx, cy, '\u2588', nil, tcell.StyleDefault.Foreground(tcell.ColorBlack))
			}
		}

		// write title if any
		if title != "" {
			tview.Print(screen, title, centerX-((width/2)-2), centerY-((height/2)+1), width-4, tview.AlignLeft, tcell.ColorWhite)
		}

		// write text
		if text != "" {
			textLine := strings.Split(text, "\n")
			for i, line := range textLine {
				if i > ((height / 2) + 1) {
					break
				}
				tview.Print(screen, line, centerX-((width/2)-1), centerY-(height/2)+1+i, width, tview.AlignLeft, tcell.ColorWhite)
			}
		}

		app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
			if event.Key() == yesKey || event.Key() == noKey {
				app.SetAfterDrawFunc(nil)
				if afterDrawHandler != nil {
					app.SetAfterDrawFunc(afterDrawHandler)
				}
				app.SetInputCapture(nil)
				if inputCaptureHandler != nil {
					app.SetInputCapture(inputCaptureHandler)
				}
				app.Draw()

				if event.Key() == noKey {
					noFunc()
				} else if event.Key() == yesKey {
					yesFunc()
				}
			}
			return event
		})

	})
}

func main() {
	app = tview.NewApplication()
	button := tview.NewButton("Hit Enter to close").SetSelectedFunc(func() {
		ShowModal("Title", "Text", 31, 10, func() { app.Stop() }, func() {}, 0, 0)
	})
	button.SetBorder(true).SetRect(0, 0, 22, 3)
	if err := app.SetRoot(button, false).Run(); err != nil {
		panic(err)
	}
}
<!-- gh-comment-id:445872498 --> @lnxbil commented on GitHub (Dec 10, 2018): Okay, that seems to work. So, just for completeness, the corrected example (only the named remarks) for anyone stumbling on this long closed issue as I did. ```go package main import ( "strings" "github.com/gdamore/tcell" "github.com/rivo/tview" ) var app *tview.Application // the tview base applicaion var afterDrawHandler func(screen tcell.Screen) // afterDrawHandler in case we overwrite it var inputCaptureHandler (func(event *tcell.EventKey) *tcell.EventKey) // inputCaptureHandler in case we overwrite it // ShowModal prompts user a basic modal on top of current view func ShowModal(title string, text string, width int, height int, yesFunc func(), noFunc func(), yesKey tcell.Key, noKey tcell.Key) { afterDrawHandler = app.GetAfterDrawFunc() inputCaptureHandler = app.GetInputCapture() app.SetAfterDrawFunc(func(screen tcell.Screen) { // draw modal as simple box screenWidth, screenHeight := screen.Size() centerY := screenHeight / 2 centerX := screenWidth / 2 // draw top and bottom line for cx := centerX - (width / 2); cx < centerX+(width/2); cx++ { screen.SetContent(cx, centerY+(height/2), tview.Borders.Horizontal, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(cx, centerY-(height/2)-1, tview.Borders.Horizontal, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) } // draw vertical lines for cy := centerY - (height / 2); cy < centerY+(height/2); cy++ { screen.SetContent(centerX+(width/2), cy, tview.Borders.Vertical, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(centerX-(width/2)-1, cy, tview.Borders.Vertical, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) } // set corners screen.SetContent(centerX-(width/2)-1, centerY+(height/2), tview.Borders.BottomLeft, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(centerX+(width/2), centerY+(height/2), tview.Borders.BottomRight, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(centerX-(width/2)-1, centerY-(height/2)-1, tview.Borders.TopLeft, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) screen.SetContent(centerX+(width/2), centerY-(height/2)-1, tview.Borders.TopRight, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) // clear box area for cy := centerY - (height / 2); cy < centerY+(height/2); cy++ { for cx := centerX - (width / 2); cx < centerX+(width/2); cx++ { screen.SetContent(cx, cy, '\u2588', nil, tcell.StyleDefault.Foreground(tcell.ColorBlack)) } } // write title if any if title != "" { tview.Print(screen, title, centerX-((width/2)-2), centerY-((height/2)+1), width-4, tview.AlignLeft, tcell.ColorWhite) } // write text if text != "" { textLine := strings.Split(text, "\n") for i, line := range textLine { if i > ((height / 2) + 1) { break } tview.Print(screen, line, centerX-((width/2)-1), centerY-(height/2)+1+i, width, tview.AlignLeft, tcell.ColorWhite) } } app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if event.Key() == yesKey || event.Key() == noKey { app.SetAfterDrawFunc(nil) if afterDrawHandler != nil { app.SetAfterDrawFunc(afterDrawHandler) } app.SetInputCapture(nil) if inputCaptureHandler != nil { app.SetInputCapture(inputCaptureHandler) } app.Draw() if event.Key() == noKey { noFunc() } else if event.Key() == yesKey { yesFunc() } } return event }) }) } func main() { app = tview.NewApplication() button := tview.NewButton("Hit Enter to close").SetSelectedFunc(func() { ShowModal("Title", "Text", 31, 10, func() { app.Stop() }, func() {}, 0, 0) }) button.SetBorder(true).SetRect(0, 0, 22, 3) if err := app.SetRoot(button, false).Run(); err != nil { panic(err) } } ```
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#48
No description provided.