From 53527f5f80de52c02f0250b8f48d0b14fe5eccec Mon Sep 17 00:00:00 2001 From: Oliver <480930+rivo@users.noreply.github.com> Date: Sat, 30 Dec 2017 22:10:13 +0100 Subject: [PATCH] Added a presentation demonstrating tview's capabilities. --- demos/presentation/center.go | 16 ++ demos/presentation/code.go | 25 ++ demos/presentation/cover.go | 56 +++++ demos/presentation/end.go | 18 ++ demos/presentation/flex.go | 39 +++ demos/presentation/form.go | 42 ++++ demos/presentation/helloworld.go | 31 +++ demos/presentation/inputfield.go | 41 ++++ demos/presentation/introduction.go | 14 ++ demos/presentation/main.go | 86 +++++++ demos/presentation/table.go | 366 +++++++++++++++++++++++++++++ demos/presentation/textview.go | 150 ++++++++++++ 12 files changed, 884 insertions(+) create mode 100644 demos/presentation/center.go create mode 100644 demos/presentation/code.go create mode 100644 demos/presentation/cover.go create mode 100644 demos/presentation/end.go create mode 100644 demos/presentation/flex.go create mode 100644 demos/presentation/form.go create mode 100644 demos/presentation/helloworld.go create mode 100644 demos/presentation/inputfield.go create mode 100644 demos/presentation/introduction.go create mode 100644 demos/presentation/main.go create mode 100644 demos/presentation/table.go create mode 100644 demos/presentation/textview.go diff --git a/demos/presentation/center.go b/demos/presentation/center.go new file mode 100644 index 0000000..b597b1c --- /dev/null +++ b/demos/presentation/center.go @@ -0,0 +1,16 @@ +package main + +import "github.com/rivo/tview" + +// Center returns a new primitive which shows the provided primitive in its +// center, given the provided primitive's size. +func Center(width, height int, p tview.Primitive) tview.Primitive { + return tview.NewFlex(). + AddItem(tview.NewBox(), 0, 1, false). + AddItem(tview.NewFlex(). + SetDirection(tview.FlexRow). + AddItem(tview.NewBox(), 0, 1, false). + AddItem(p, height, 1, true). + AddItem(tview.NewBox(), 0, 1, false), width, 1, true). + AddItem(tview.NewBox(), 0, 1, false) +} diff --git a/demos/presentation/code.go b/demos/presentation/code.go new file mode 100644 index 0000000..59fd048 --- /dev/null +++ b/demos/presentation/code.go @@ -0,0 +1,25 @@ +package main + +import ( + "fmt" + + "github.com/rivo/tview" +) + +// The width of the code window. +const codeWidth = 56 + +// Code returns a primitive which displays the given primitive (with the given +// size) on the left side and its source code on the right side. +func Code(p tview.Primitive, width, height int, code string) tview.Primitive { + // Set up code view. + codeView := tview.NewTextView(). + SetWrap(false). + SetDynamicColors(true) + codeView.SetBorderPadding(1, 1, 2, 0) + fmt.Fprint(codeView, code) + + return tview.NewFlex(). + AddItem(Center(width, height, p), 0, 1, true). + AddItem(codeView, codeWidth, 1, false) +} diff --git a/demos/presentation/cover.go b/demos/presentation/cover.go new file mode 100644 index 0000000..11bf681 --- /dev/null +++ b/demos/presentation/cover.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/gdamore/tcell" + "github.com/rivo/tview" +) + +const logo = ` + __ _ + / /__ __(_)__ _ __ + / __/ | / / / _ \ | /| / / +/ /_ | |/ / / __/ |/ |/ / +\__/ |___/_/\___/|__/|__/ + +` + +const subtitle = `tview - Rich Widgets for Terminal UIs` + +// Cover returns the cover page. +func Cover(nextSlide func()) (title string, content tview.Primitive) { + // What's the size of the logo? + lines := strings.Split(logo, "\n") + logoWidth := 0 + logoHeight := len(lines) + for _, line := range lines { + if len(line) > logoWidth { + logoWidth = len(line) + } + } + logoBox := tview.NewTextView(). + SetTextColor(tcell.ColorGreen). + SetDoneFunc(func(key tcell.Key) { + nextSlide() + }) + fmt.Fprint(logoBox, logo) + + // Create a frame for the subtitle. + frame := tview.NewFrame(tview.NewBox()). + SetBorders(0, 0, 0, 0, 0, 0). + AddText(subtitle, true, tview.AlignCenter, tcell.ColorWhite) + + // Create a Flex layout that centers the logo and subtitle. + flex := tview.NewFlex(). + SetDirection(tview.FlexRow). + AddItem(tview.NewBox(), 0, 7, false). + AddItem(tview.NewFlex(). + AddItem(tview.NewBox(), 0, 1, false). + AddItem(logoBox, logoWidth, 1, true). + AddItem(tview.NewBox(), 0, 1, false), logoHeight, 1, true). + AddItem(frame, 0, 10, false) + + return "Start", flex +} diff --git a/demos/presentation/end.go b/demos/presentation/end.go new file mode 100644 index 0000000..119b676 --- /dev/null +++ b/demos/presentation/end.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + + "github.com/gdamore/tcell" + "github.com/rivo/tview" +) + +// End shows the final slide. +func End(nextSlide func()) (title string, content tview.Primitive) { + textView := tview.NewTextView().SetDoneFunc(func(key tcell.Key) { + nextSlide() + }) + url := "https://github.com/rivo/tview" + fmt.Fprint(textView, url) + return "End", Center(len(url), 1, textView) +} diff --git a/demos/presentation/flex.go b/demos/presentation/flex.go new file mode 100644 index 0000000..22638c7 --- /dev/null +++ b/demos/presentation/flex.go @@ -0,0 +1,39 @@ +package main + +import ( + "github.com/gdamore/tcell" + "github.com/rivo/tview" +) + +// Flex demonstrates flexbox layout. +func Flex(nextSlide func()) (title string, content tview.Primitive) { + modalShown := false + pages := tview.NewPages() + textView := tview.NewTextView(). + SetDoneFunc(func(key tcell.Key) { + if modalShown { + nextSlide() + modalShown = false + } else { + pages.ShowPage("modal") + modalShown = true + } + }) + textView.SetBorder(true).SetTitle("Flexible width, twice of middle column") + flex := tview.NewFlex(). + AddItem(textView, 0, 2, true). + AddItem(tview.NewFlex(). + SetDirection(tview.FlexRow). + AddItem(tview.NewBox().SetBorder(true).SetTitle("Flexible width"), 0, 1, false). + AddItem(tview.NewBox().SetBorder(true).SetTitle("Fixed height"), 15, 1, false). + AddItem(tview.NewBox().SetBorder(true).SetTitle("Flexible height"), 0, 1, false), 0, 1, false). + AddItem(tview.NewBox().SetBorder(true).SetTitle("Fixed width"), 30, 1, false) + modal := tview.NewModal(). + SetText("Resize the window to see the effect of the flexbox parameters"). + AddButtons([]string{"Ok"}).SetDoneFunc(func(buttonIndex int, buttonLabel string) { + pages.HidePage("modal") + }) + pages.AddPage("flex", flex, true, true). + AddPage("modal", modal, false, false) + return "Flex", pages +} diff --git a/demos/presentation/form.go b/demos/presentation/form.go new file mode 100644 index 0000000..59d5abb --- /dev/null +++ b/demos/presentation/form.go @@ -0,0 +1,42 @@ +package main + +import ( + "github.com/rivo/tview" +) + +const form = `[green]package[white] main + +[green]import[white] ( + [red]"github.com/rivo/tview"[white] +) + +[green]func[white] [yellow]main[white]() { + form := tview.[yellow]NewForm[white](). + [yellow]AddInputField[white]([red]"First name:"[white], [red]""[white], [red]20[white], nil). + [yellow]AddInputField[white]([red]"Last name:"[white], [red]""[white], [red]20[white], nil). + [yellow]AddDropDown[white]([red]"Role:"[white], [][green]string[white]{ + [red]"Engineer"[white], + [red]"Manager"[white], + [red]"Administration"[white], + }, [red]0[white], nil). + [yellow]AddCheckbox[white]([red]"On vacation:"[white], false, nil). + [yellow]AddButton[white]([red]"Save"[white], [yellow]func[white]() { [blue]/* Save data */[white] }). + [yellow]AddButton[white]([red]"Cancel"[white], [yellow]func[white]() { [blue]/* Cancel */[white] }) + tview.[yellow]NewApplication[white](). + [yellow]SetRoot[white](form, true). + [yellow]SetFocus[white](form). + [yellow]Run[white]() +}` + +// Form demonstrates forms. +func Form(nextSlide func()) (title string, content tview.Primitive) { + f := tview.NewForm(). + AddInputField("First name:", "", 20, nil). + AddInputField("Last name:", "", 20, nil). + AddDropDown("Role:", []string{"Engineer", "Manager", "Administration"}, 0, nil). + AddCheckbox("On vacation:", false, nil). + AddButton("Save", nextSlide). + AddButton("Cancel", nextSlide) + f.SetBorder(true).SetTitle("Employee Information") + return "Forms", Code(f, 36, 13, form) +} diff --git a/demos/presentation/helloworld.go b/demos/presentation/helloworld.go new file mode 100644 index 0000000..f312a79 --- /dev/null +++ b/demos/presentation/helloworld.go @@ -0,0 +1,31 @@ +package main + +import ( + "github.com/gdamore/tcell" + "github.com/rivo/tview" +) + +const helloWorld = `[green]package[white] main + +[green]import[white] ( + [red]"github.com/rivo/tview"[white] +) + +[green]func[white] [yellow]main[white]() { + box := tview.[yellow]NewBox[white](). + [yellow]SetBorder[white](true). + [yellow]SetTitle[white]([red]"Hello, world!"[white]) + tview.[yellow]NewApplication[white](). + [yellow]SetRoot[white](box, true). + [yellow]Run[white]() +}` + +// HelloWorld shows a simple "Hello world" example. +func HelloWorld(nextSlide func()) (title string, content tview.Primitive) { + // We use a text view because we want to capture keyboard input. + textView := tview.NewTextView().SetDoneFunc(func(key tcell.Key) { + nextSlide() + }) + textView.SetBorder(true).SetTitle("Hello, world!") + return "Hello, world", Code(textView, 30, 10, helloWorld) +} diff --git a/demos/presentation/inputfield.go b/demos/presentation/inputfield.go new file mode 100644 index 0000000..b5e19c5 --- /dev/null +++ b/demos/presentation/inputfield.go @@ -0,0 +1,41 @@ +package main + +import ( + "github.com/gdamore/tcell" + "github.com/rivo/tview" +) + +const inputField = `[green]package[white] main + +[green]import[white] ( + [red]"strconv"[white] + + [red]"github.com/gdamore/tcell"[white] + [red]"github.com/rivo/tview"[white] +) + +[green]func[white] [yellow]main[white]() { + input := tview.[yellow]NewInputField[white](). + [yellow]SetLabel[white]([red]"Enter a number: "[white]). + [yellow]SetAcceptanceFunc[white]( + tview.InputFieldInteger, + ).[yellow]SetDoneFunc[white]([yellow]func[white](key tcell.Key) { + text := input.[yellow]GetText[white]() + n, _ := strconv.[yellow]Atoi[white](text) + [blue]// We have a number.[white] + }) + tview.[yellow]NewApplication[white](). + [yellow]SetRoot[white](input, true). + [yellow]SetFocus[white](input). + [yellow]Run[white]() +}` + +// InputField demonstrates the InputField. +func InputField(nextSlide func()) (title string, content tview.Primitive) { + input := tview.NewInputField(). + SetLabel("Enter a number: "). + SetAcceptanceFunc(tview.InputFieldInteger).SetDoneFunc(func(key tcell.Key) { + nextSlide() + }) + return "Input", Code(input, 30, 1, inputField) +} diff --git a/demos/presentation/introduction.go b/demos/presentation/introduction.go new file mode 100644 index 0000000..8dc0f0c --- /dev/null +++ b/demos/presentation/introduction.go @@ -0,0 +1,14 @@ +package main + +import "github.com/rivo/tview" + +// Introduction returns a tview.List with the highlights of the tview package. +func Introduction(nextSlide func()) (title string, content tview.Primitive) { + list := tview.NewList(). + AddItem("A Go package for terminal based UIs", "with a special focus on rich interactive widgets", '1', nextSlide). + AddItem("Based on github.com/gdamore/tcell", "Like termbox but better (see tcell docs)", '2', nextSlide). + AddItem("Designed to be simple", `"Hello world" is 5 lines of code`, '3', nextSlide). + AddItem("Good for data entry", `For charts, use "termui" - for low-level views, use "gocui" - ...`, '4', nextSlide). + AddItem("Extensive documentation", "Everything is documented, examples in GitHub wiki, demo code for each widget", '5', nextSlide) + return "Introduction", Center(80, 10, list) +} diff --git a/demos/presentation/main.go b/demos/presentation/main.go new file mode 100644 index 0000000..d96af03 --- /dev/null +++ b/demos/presentation/main.go @@ -0,0 +1,86 @@ +/* +Navigation + + - Ctrl-N: Jump to next slide + - Ctrl-P: Jump to previous slide +*/ +package main + +import ( + "fmt" + "strconv" + + "github.com/gdamore/tcell" + "github.com/rivo/tview" +) + +// Slide is a function which returns the slide's main primitive and its title. +// It receives a "nextSlide" function which can be called to advance the +// presentation to the next slide. +type Slide func(nextSlide func()) (title string, content tview.Primitive) + +// The application. +var app = tview.NewApplication() + +// Starting point for the presentation. +func main() { + // The presentation slides. + slides := []Slide{ + Cover, + Introduction, + HelloWorld, + InputField, + Form, + TextView1, + TextView2, + Table, + Flex, + End, + } + + // The bottom row has some info on where we are. + info := tview.NewTextView(). + SetDynamicColors(true). + SetRegions(true). + SetWrap(false) + + // Create the pages for all slides. + currentSlide := 0 + info.Highlight(strconv.Itoa(currentSlide)) + pages := tview.NewPages() + previousSlide := func() { + currentSlide = (currentSlide - 1 + len(slides)) % len(slides) + info.Highlight(strconv.Itoa(currentSlide)) + pages.SwitchToPage(strconv.Itoa(currentSlide)) + } + nextSlide := func() { + currentSlide = (currentSlide + 1) % len(slides) + info.Highlight(strconv.Itoa(currentSlide)) + pages.SwitchToPage(strconv.Itoa(currentSlide)) + } + for index, slide := range slides { + title, primitive := slide(nextSlide) + pages.AddPage(strconv.Itoa(index), primitive, true, index == currentSlide) + fmt.Fprintf(info, `%d ["%d"][cyan]%s[white][""] `, index+1, index, title) + } + + // Create the main layout. + layout := tview.NewFlex(). + SetDirection(tview.FlexRow). + AddItem(pages, 0, 1, true). + AddItem(info, 1, 1, false) + + // Shortcuts to navigate the slides. + app.SetKeyCapture(tcell.KeyCtrlN, 0, func(p tview.Primitive) bool { + nextSlide() + return false + }).SetKeyCapture(tcell.KeyCtrlP, 0, func(p tview.Primitive) bool { + previousSlide() + return false + }) + + // Start the application. + if err := app.SetRoot(layout, true).SetFocus(layout).Run(); err != nil { + panic(err) + } +} diff --git a/demos/presentation/table.go b/demos/presentation/table.go new file mode 100644 index 0000000..0081cee --- /dev/null +++ b/demos/presentation/table.go @@ -0,0 +1,366 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/gdamore/tcell" + "github.com/rivo/tview" +) + +const tableData = `OrderDate|Region|Rep|Item|Units|UnitCost|Total +1/6/2017|East|Jones|Pencil|95|1.99|189.05 +1/23/2017|Central|Kivell|Binder|50|19.99|999.50 +2/9/2017|Central|Jardine|Pencil|36|4.99|179.64 +2/26/2017|Central|Gill|Pen|27|19.99|539.73 +3/15/2017|West|Sorvino|Pencil|56|2.99|167.44 +4/1/2017|East|Jones|Binder|60|4.99|299.40 +4/18/2017|Central|Andrews|Pencil|75|1.99|149.25 +5/5/2017|Central|Jardine|Pencil|90|4.99|449.10 +5/22/2017|West|Thompson|Pencil|32|1.99|63.68 +6/8/2017|East|Jones|Binder|60|8.99|539.40 +6/25/2017|Central|Morgan|Pencil|90|4.99|449.10 +7/12/2017|East|Howard|Binder|29|1.99|57.71 +7/29/2017|East|Parent|Binder|81|19.99|1,619.19 +8/15/2017|East|Jones|Pencil|35|4.99|174.65 +9/1/2017|Central|Smith|Desk|2|125.00|250.00 +9/18/2017|East|Jones|Pen Set|16|15.99|255.84 +10/5/2017|Central|Morgan|Binder|28|8.99|251.72 +10/22/2017|East|Jones|Pen|64|8.99|575.36 +11/8/2017|East|Parent|Pen|15|19.99|299.85 +11/25/2017|Central|Kivell|Pen Set|96|4.99|479.04 +12/12/2017|Central|Smith|Pencil|67|1.29|86.43 +12/29/2017|East|Parent|Pen Set|74|15.99|1,183.26 +1/15/2018|Central|Gill|Binder|46|8.99|413.54 +2/1/2018|Central|Smith|Binder|87|15.00|1,305.00 +2/18/2018|East|Jones|Binder|4|4.99|19.96 +3/7/2018|West|Sorvino|Binder|7|19.99|139.93 +3/24/2018|Central|Jardine|Pen Set|50|4.99|249.50 +4/10/2018|Central|Andrews|Pencil|66|1.99|131.34 +4/27/2018|East|Howard|Pen|96|4.99|479.04 +5/14/2018|Central|Gill|Pencil|53|1.29|68.37 +5/31/2018|Central|Gill|Binder|80|8.99|719.20 +6/17/2018|Central|Kivell|Desk|5|125.00|625.00 +7/4/2018|East|Jones|Pen Set|62|4.99|309.38 +7/21/2018|Central|Morgan|Pen Set|55|12.49|686.95 +8/7/2018|Central|Kivell|Pen Set|42|23.95|1,005.90 +8/24/2018|West|Sorvino|Desk|3|275.00|825.00 +9/10/2018|Central|Gill|Pencil|7|1.29|9.03 +9/27/2018|West|Sorvino|Pen|76|1.99|151.24 +10/14/2018|West|Thompson|Binder|57|19.99|1,139.43 +10/31/2018|Central|Andrews|Pencil|14|1.29|18.06 +11/17/2018|Central|Jardine|Binder|11|4.99|54.89 +12/4/2018|Central|Jardine|Binder|94|19.99|1,879.06 +12/21/2018|Central|Andrews|Binder|28|4.99|139.72` + +const tableBasic = `[green]func[white] [yellow]main[white]() { + table := tview.[yellow]NewTable[white](). + [yellow]SetFixed[white]([red]1[white], [red]1[white]) + [yellow]for[white] row := [red]0[white]; row < [red]40[white]; row++ { + [yellow]for[white] column := [red]0[white]; column < [red]7[white]; column++ { + color := tcell.ColorWhite + [yellow]if[white] row == [red]0[white] { + color = tcell.ColorYellow + } [yellow]else[white] [yellow]if[white] column == [red]0[white] { + color = tcell.ColorDarkCyan + } + align := tview.AlignLeft + [yellow]if[white] row == [red]0[white] { + align = tview.AlignCenter + } [yellow]else[white] [yellow]if[white] column == [red]0[white] || column >= [red]4[white] { + align = tview.AlignRight + } + table.[yellow]SetCell[white](row, + column, + &tview.TableCell{ + Text: [red]"..."[white], + Color: color, + Align: align, + }) + } + } + tview.[yellow]NewApplication[white](). + [yellow]SetRoot[white](table, true). + [yellow]SetFocus[white](table). + [yellow]Run[white]() +}` + +const tableSeparator = `[green]func[white] [yellow]main[white]() { + table := tview.[yellow]NewTable[white](). + [yellow]SetFixed[white]([red]1[white], [red]1[white]). + [yellow]SetSeparator[white](tview.GraphicsVertBar) + [yellow]for[white] row := [red]0[white]; row < [red]40[white]; row++ { + [yellow]for[white] column := [red]0[white]; column < [red]7[white]; column++ { + color := tcell.ColorWhite + [yellow]if[white] row == [red]0[white] { + color = tcell.ColorYellow + } [yellow]else[white] [yellow]if[white] column == [red]0[white] { + color = tcell.ColorDarkCyan + } + align := tview.AlignLeft + [yellow]if[white] row == [red]0[white] { + align = tview.AlignCenter + } [yellow]else[white] [yellow]if[white] column == [red]0[white] || column >= [red]4[white] { + align = tview.AlignRight + } + table.[yellow]SetCell[white](row, + column, + &tview.TableCell{ + Text: [red]"..."[white], + Color: color, + Align: align, + }) + } + } + tview.[yellow]NewApplication[white](). + [yellow]SetRoot[white](table, true). + [yellow]SetFocus[white](table). + [yellow]Run[white]() +}` + +const tableBorders = `[green]func[white] [yellow]main[white]() { + table := tview.[yellow]NewTable[white](). + [yellow]SetFixed[white]([red]1[white], [red]1[white]). + [yellow]SetBorders[white](true) + [yellow]for[white] row := [red]0[white]; row < [red]40[white]; row++ { + [yellow]for[white] column := [red]0[white]; column < [red]7[white]; column++ { + color := tcell.ColorWhite + [yellow]if[white] row == [red]0[white] { + color = tcell.ColorYellow + } [yellow]else[white] [yellow]if[white] column == [red]0[white] { + color = tcell.ColorDarkCyan + } + align := tview.AlignLeft + [yellow]if[white] row == [red]0[white] { + align = tview.AlignCenter + } [yellow]else[white] [yellow]if[white] column == [red]0[white] || column >= [red]4[white] { + align = tview.AlignRight + } + table.[yellow]SetCell[white](row, + column, + &tview.TableCell{ + Text: [red]"..."[white], + Color: color, + Align: align, + }) + } + } + tview.[yellow]NewApplication[white](). + [yellow]SetRoot[white](table, true). + [yellow]SetFocus[white](table). + [yellow]Run[white]() +}` + +const tableSelectRow = `[green]func[white] [yellow]main[white]() { + table := tview.[yellow]NewTable[white](). + [yellow]SetFixed[white]([red]1[white], [red]1[white]). + [yellow]SetSelectable[white](true, false) + [yellow]for[white] row := [red]0[white]; row < [red]40[white]; row++ { + [yellow]for[white] column := [red]0[white]; column < [red]7[white]; column++ { + color := tcell.ColorWhite + [yellow]if[white] row == [red]0[white] { + color = tcell.ColorYellow + } [yellow]else[white] [yellow]if[white] column == [red]0[white] { + color = tcell.ColorDarkCyan + } + align := tview.AlignLeft + [yellow]if[white] row == [red]0[white] { + align = tview.AlignCenter + } [yellow]else[white] [yellow]if[white] column == [red]0[white] || column >= [red]4[white] { + align = tview.AlignRight + } + table.[yellow]SetCell[white](row, + column, + &tview.TableCell{ + Text: [red]"..."[white], + Color: color, + Align: align, + NotSelectable: row == [red]0[white] || column == [red]0[white], + }) + } + } + tview.[yellow]NewApplication[white](). + [yellow]SetRoot[white](table, true). + [yellow]SetFocus[white](table). + [yellow]Run[white]() +}` + +const tableSelectColumn = `[green]func[white] [yellow]main[white]() { + table := tview.[yellow]NewTable[white](). + [yellow]SetFixed[white]([red]1[white], [red]1[white]). + [yellow]SetSelectable[white](false, true) + [yellow]for[white] row := [red]0[white]; row < [red]40[white]; row++ { + [yellow]for[white] column := [red]0[white]; column < [red]7[white]; column++ { + color := tcell.ColorWhite + [yellow]if[white] row == [red]0[white] { + color = tcell.ColorYellow + } [yellow]else[white] [yellow]if[white] column == [red]0[white] { + color = tcell.ColorDarkCyan + } + align := tview.AlignLeft + [yellow]if[white] row == [red]0[white] { + align = tview.AlignCenter + } [yellow]else[white] [yellow]if[white] column == [red]0[white] || column >= [red]4[white] { + align = tview.AlignRight + } + table.[yellow]SetCell[white](row, + column, + &tview.TableCell{ + Text: [red]"..."[white], + Color: color, + Align: align, + NotSelectable: row == [red]0[white] || column == [red]0[white], + }) + } + } + tview.[yellow]NewApplication[white](). + [yellow]SetRoot[white](table, true). + [yellow]SetFocus[white](table). + [yellow]Run[white]() +}` + +const tableSelectCell = `[green]func[white] [yellow]main[white]() { + table := tview.[yellow]NewTable[white](). + [yellow]SetFixed[white]([red]1[white], [red]1[white]). + [yellow]SetSelectable[white](true, true) + [yellow]for[white] row := [red]0[white]; row < [red]40[white]; row++ { + [yellow]for[white] column := [red]0[white]; column < [red]7[white]; column++ { + color := tcell.ColorWhite + [yellow]if[white] row == [red]0[white] { + color = tcell.ColorYellow + } [yellow]else[white] [yellow]if[white] column == [red]0[white] { + color = tcell.ColorDarkCyan + } + align := tview.AlignLeft + [yellow]if[white] row == [red]0[white] { + align = tview.AlignCenter + } [yellow]else[white] [yellow]if[white] column == [red]0[white] || column >= [red]4[white] { + align = tview.AlignRight + } + table.[yellow]SetCell[white](row, + column, + &tview.TableCell{ + Text: [red]"..."[white], + Color: color, + Align: align, + NotSelectable: row == [red]0[white] || column == [red]0[white], + }) + } + } + tview.[yellow]NewApplication[white](). + [yellow]SetRoot[white](table, true). + [yellow]SetFocus[white](table). + [yellow]Run[white]() +}` + +// Table demonstrates the Table. +func Table(nextSlide func()) (title string, content tview.Primitive) { + table := tview.NewTable(). + SetFixed(1, 1) + for row, line := range strings.Split(tableData, "\n") { + for column, cell := range strings.Split(line, "|") { + color := tcell.ColorWhite + if row == 0 { + color = tcell.ColorYellow + } else if column == 0 { + color = tcell.ColorDarkCyan + } + align := tview.AlignLeft + if row == 0 { + align = tview.AlignCenter + } else if column == 0 || column >= 4 { + align = tview.AlignRight + } + table.SetCell(row, column, &tview.TableCell{ + Text: cell, + Color: color, + Align: align, + NotSelectable: row == 0 || column == 0, + }) + } + } + table.SetBorder(true).SetTitle("Table") + + code := tview.NewTextView(). + SetWrap(false). + SetDynamicColors(true) + code.SetBorderPadding(1, 1, 2, 0) + + list := tview.NewList() + + basic := func() { + table.SetBorders(false). + SetSelectable(false, false). + SetSeparator(' ') + code.Clear() + fmt.Fprint(code, tableBasic) + } + + separator := func() { + table.SetBorders(false). + SetSelectable(false, false). + SetSeparator(tview.GraphicsVertBar) + code.Clear() + fmt.Fprint(code, tableSeparator) + } + + borders := func() { + table.SetBorders(true). + SetSelectable(false, false) + code.Clear() + fmt.Fprint(code, tableBorders) + } + + selectRow := func() { + table.SetBorders(false). + SetSelectable(true, false). + SetSeparator(' ') + code.Clear() + fmt.Fprint(code, tableSelectRow) + } + + selectColumn := func() { + table.SetBorders(false). + SetSelectable(false, true). + SetSeparator(' ') + code.Clear() + fmt.Fprint(code, tableSelectColumn) + } + + selectCell := func() { + table.SetBorders(false). + SetSelectable(true, true). + SetSeparator(' ') + code.Clear() + fmt.Fprint(code, tableSelectCell) + } + + navigate := func() { + app.SetFocus(table) + table.SetDoneFunc(func(key tcell.Key) { + app.SetFocus(list) + }).SetSelectedFunc(func(row int, column int) { + app.SetFocus(list) + }) + } + + list.ShowSecondaryText(false). + AddItem("Basic table", "", 'b', basic). + AddItem("Table with separator", "", 's', separator). + AddItem("Table with borders", "", 'o', borders). + AddItem("Selectable rows", "", 'r', selectRow). + AddItem("Selectable columns", "", 'c', selectColumn). + AddItem("Selectable cells", "", 'l', selectCell). + AddItem("Navigate", "", 'n', navigate). + AddItem("Next slide", "", 'x', nextSlide) + list.SetBorderPadding(1, 1, 2, 2) + + basic() + + return "Table", tview.NewFlex(). + AddItem(tview.NewFlex(). + SetDirection(tview.FlexRow). + AddItem(list, 10, 1, true). + AddItem(table, 0, 1, false), 0, 1, true). + AddItem(code, codeWidth, 1, false) +} diff --git a/demos/presentation/textview.go b/demos/presentation/textview.go new file mode 100644 index 0000000..5f554e9 --- /dev/null +++ b/demos/presentation/textview.go @@ -0,0 +1,150 @@ +package main + +import ( + "fmt" + "strconv" + "time" + + "github.com/gdamore/tcell" + "github.com/rivo/tview" +) + +const textView1 = `[green]func[white] [yellow]main[white]() { + textView := tview.[yellow]NewTextView[white](). + [yellow]SetTextColor[white](tcell.ColorYellow). + [yellow]SetScrollable[white](false). + [yellow]SetChangedFunc[white]([yellow]func[white]() { + app.[yellow]Draw[white]() + }) + [green]go[white] [yellow]func[white]() { + [green]var[white] n [green]int +[white] [yellow]for[white] { + n++ + fmt.[yellow]Fprintf[white](textView, [red]"%d "[white], n) + time.[yellow]Sleep[white]([red]200[white] * time.Millisecond) + } + }() + tview.[yellow]NewApplication[white](). + [yellow]SetRoot[white](textView, true). + [yellow]SetFocus[white](textView). + [yellow]Run[white]() +}` + +// TextView1 demonstrates the basic text view. +func TextView1(nextSlide func()) (title string, content tview.Primitive) { + textView := tview.NewTextView(). + SetTextColor(tcell.ColorYellow). + SetScrollable(false). + SetChangedFunc(func() { + app.Draw() + }).SetDoneFunc(func(key tcell.Key) { + nextSlide() + }) + go func() { + var n int + for { + n++ + fmt.Fprintf(textView, "%d ", n) + time.Sleep(200 * time.Millisecond) + } + }() + textView.SetBorder(true).SetTitle("TextView implements io.Writer") + return "Text 1", Code(textView, 36, 13, textView1) +} + +const textView2 = `[green]package[white] main + +[green]import[white] ( + [red]"strconv"[white] + + [red]"github.com/gdamore/tcell"[white] + [red]"github.com/rivo/tview"[white] +) + +[green]func[white] [yellow]main[white]() { + ["0"]textView[""] := tview.[yellow]NewTextView[white]() + ["1"]textView[""].[yellow]SetDynamicColors[white](true). + [yellow]SetWrap[white](false). + [yellow]SetRegions[white](true). + [yellow]SetDoneFunc[white]([yellow]func[white](key tcell.Key) { + highlights := ["2"]textView[""].[yellow]GetHighlights[white]() + hasHighlights := [yellow]len[white](highlights) > [red]0 +[white] [yellow]switch[white] key { + [yellow]case[white] tcell.KeyEnter: + [yellow]if[white] hasHighlights { + ["3"]textView[""].[yellow]Highlight[white]() + } [yellow]else[white] { + ["4"]textView[""].[yellow]Highlight[white]([red]"0"[white]). + [yellow]ScrollToHighlight[white]() + } + [yellow]case[white] tcell.KeyTab: + [yellow]if[white] hasHighlights { + current, _ := strconv.[yellow]Atoi[white](highlights[[red]0[white]]) + next := (current + [red]1[white]) % [red]9 +[white] ["5"]textView[""].[yellow]Highlight[white](strconv.[yellow]Itoa[white](next)). + [yellow]ScrollToHighlight[white]() + } + [yellow]case[white] tcell.KeyBacktab: + [yellow]if[white] hasHighlights { + current, _ := strconv.[yellow]Atoi[white](highlights[[red]0[white]]) + next := (current - [red]1[white] + [red]9[white]) % [red]9 +[white] ["6"]textView[""].[yellow]Highlight[white](strconv.[yellow]Itoa[white](next)). + [yellow]ScrollToHighlight[white]() + } + } + }) + fmt.[yellow]Fprint[white](["7"]textView[""], content) + tview.[yellow]NewApplication[white](). + [yellow]SetRoot[white](["8"]textView[""], true). + [yellow]SetFocus[white](["9"]textView[""]). + [yellow]Run[white]() +}` + +// TextView2 demonstrates the extended text view. +func TextView2(nextSlide func()) (title string, content tview.Primitive) { + codeView := tview.NewTextView(). + SetWrap(false) + fmt.Fprint(codeView, textView2) + codeView.SetBorder(true).SetTitle("TextView content") + + textView := tview.NewTextView() + textView.SetDynamicColors(true). + SetWrap(false). + SetRegions(true). + SetDoneFunc(func(key tcell.Key) { + if key == tcell.KeyEscape { + nextSlide() + return + } + highlights := textView.GetHighlights() + hasHighlights := len(highlights) > 0 + switch key { + case tcell.KeyEnter: + if hasHighlights { + textView.Highlight() + } else { + textView.Highlight("0"). + ScrollToHighlight() + } + case tcell.KeyTab: + if hasHighlights { + current, _ := strconv.Atoi(highlights[0]) + next := (current + 1) % 10 + textView.Highlight(strconv.Itoa(next)). + ScrollToHighlight() + } + case tcell.KeyBacktab: + if hasHighlights { + current, _ := strconv.Atoi(highlights[0]) + next := (current - 1 + 10) % 10 + textView.Highlight(strconv.Itoa(next)). + ScrollToHighlight() + } + } + }) + fmt.Fprint(textView, textView2) + textView.SetBorder(true).SetTitle("TextView code") + return "Text 2", tview.NewFlex(). + AddItem(textView, 0, 1, true). + AddItem(codeView, 0, 1, false) +}