Add relevant info to pages of the presentation demo

Also, only draw DropDown symbols when focused by default.

Resolves #61.
This commit is contained in:
Trevor Slocum 2021-06-04 20:35:34 -07:00
parent d11681497c
commit 71e3cc57f7
21 changed files with 118 additions and 78 deletions

View File

@ -1,6 +1,8 @@
v1.5.6 (WIP)
- Add TrueColorTags option and do not use TrueColor tag values by default
- Add TextView.SetHighlightForegroundColor and TextView.SetHighlightBackgroundColor
- Add DropDown.SetDropDownOpenSymbolRune
- Add DropDown.SetAlwaysDrawDropDownSymbol (DropDown symbols are now only drawn when focused by default)
- Draw additional accents when rendering a list divider
- Update List, Table and TreeView to not handle Tab or Backtab
- Allow specifying TabbedPanels switcher height

View File

@ -4,7 +4,6 @@ import (
"sync"
"github.com/gdamore/tcell/v2"
"github.com/lucasb-eyer/go-colorful"
)
// Button is labeled box that triggers an action when selected.
@ -37,24 +36,13 @@ type Button struct {
func NewButton(label string) *Button {
box := NewBox()
box.SetRect(0, 0, TaggedStringWidth(label)+4, 1)
bg := Styles.PrimaryTextColor
if bg == tcell.ColorDefault {
r, g, b := Styles.InverseTextColor.RGB()
c := colorful.Color{R: float64(r) / 255, G: float64(g) / 255, B: float64(b) / 255}
_, _, li := c.Hcl()
if li < .5 {
bg = tcell.ColorWhite.TrueColor()
} else {
bg = tcell.ColorBlack.TrueColor()
}
}
box.SetBackgroundColor(bg)
box.SetBackgroundColor(Styles.ContrastBackgroundColor)
return &Button{
Box: box,
label: []byte(label),
labelColor: Styles.InverseTextColor,
labelColor: Styles.PrimaryTextColor,
labelColorFocused: Styles.PrimaryTextColor,
backgroundColorFocused: Styles.ContrastBackgroundColor,
backgroundColorFocused: Styles.MoreContrastBackgroundColor,
}
}

View File

@ -18,7 +18,7 @@ The [black:red]tags [black:green]look [black:yellow]like [::u]this:
[#00ff00[]`
// Colors demonstrates how to use colors.
func Colors(nextSlide func()) (title string, content cview.Primitive) {
func Colors(nextSlide func()) (title string, info string, content cview.Primitive) {
tv := cview.NewTextView()
tv.SetBorder(true)
tv.SetTitle("A [red]c[yellow]o[green]l[darkcyan]o[blue]r[darkmagenta]f[red]u[yellow]l[white] [black:red]c[:yellow]o[:green]l[:darkcyan]o[:blue]r[:darkmagenta]f[:red]u[:yellow]l[white:] [::bu]title")
@ -29,5 +29,5 @@ func Colors(nextSlide func()) (title string, content cview.Primitive) {
nextSlide()
})
return "Colors", Center(44, 16, tv)
return "Colors", "", Center(44, 16, tv)
}

View File

@ -16,14 +16,10 @@ const logo = `
======= == === ======== ==== ====
`
const (
subtitle = `Terminal-based user interface toolkit`
mouse = `Navigate with your keyboard or mouse.`
navigation = `Next slide: Ctrl-N Previous: Ctrl-P Exit: Ctrl-C`
)
const subtitle = "Terminal-based user interface toolkit"
// Cover returns the cover page.
func Cover(nextSlide func()) (title string, content cview.Primitive) {
func Cover(nextSlide func()) (title string, info string, content cview.Primitive) {
// What's the size of the logo?
lines := strings.Split(logo, "\n")
logoWidth := 0
@ -43,10 +39,7 @@ func Cover(nextSlide func()) (title string, content cview.Primitive) {
// Create a frame for the subtitle and navigation infos.
frame := cview.NewFrame(cview.NewBox())
frame.SetBorders(0, 0, 0, 0, 0, 0)
frame.AddText(subtitle, true, cview.AlignCenter, tcell.ColorWhite.TrueColor())
frame.AddText("", true, cview.AlignCenter, tcell.ColorWhite.TrueColor())
frame.AddText(mouse, true, cview.AlignCenter, tcell.ColorDarkMagenta.TrueColor())
frame.AddText(navigation, true, cview.AlignCenter, tcell.ColorDarkMagenta.TrueColor())
frame.AddText(subtitle, true, cview.AlignCenter, tcell.ColorDarkMagenta.TrueColor())
// Create a Flex layout that centers the logo and subtitle.
subFlex := cview.NewFlex()
@ -60,5 +53,5 @@ func Cover(nextSlide func()) (title string, content cview.Primitive) {
flex.AddItem(subFlex, logoHeight, 1, true)
flex.AddItem(frame, 0, 10, false)
return "Start", flex
return "Start", appInfo, flex
}

View File

@ -8,12 +8,12 @@ import (
)
// End shows the final slide.
func End(nextSlide func()) (title string, content cview.Primitive) {
func End(nextSlide func()) (title string, info string, content cview.Primitive) {
textView := cview.NewTextView()
textView.SetDoneFunc(func(key tcell.Key) {
nextSlide()
})
url := "https://code.rocketnine.space/tslocum/cview"
fmt.Fprint(textView, url)
return "End", Center(len(url), 1, textView)
return "End", "", Center(len(url), 1, textView)
}

View File

@ -13,7 +13,7 @@ func demoBox(title string) *cview.Box {
}
// Flex demonstrates flexbox layout.
func Flex(nextSlide func()) (title string, content cview.Primitive) {
func Flex(nextSlide func()) (title string, info string, content cview.Primitive) {
modalShown := false
panels := cview.NewPanels()
@ -50,5 +50,5 @@ func Flex(nextSlide func()) (title string, content cview.Primitive) {
panels.AddPanel("flex", flex, true, true)
panels.AddPanel("modal", modal, false, false)
return "Flex", panels
return "Flex", "", panels
}

View File

@ -29,7 +29,7 @@ const form = `[green]package[white] main
}`
// Form demonstrates forms.
func Form(nextSlide func()) (title string, content cview.Primitive) {
func Form(nextSlide func()) (title string, info string, content cview.Primitive) {
f := cview.NewForm()
f.AddInputField("First name:", "", 20, nil, nil)
f.AddInputField("Last name:", "", 20, nil, nil)
@ -40,5 +40,5 @@ func Form(nextSlide func()) (title string, content cview.Primitive) {
f.AddButton("Cancel", nextSlide)
f.SetBorder(true)
f.SetTitle("Employee Information")
return "Form", Code(f, 36, 15, form)
return "Form", formInfo, Code(f, 36, 15, form)
}

View File

@ -6,7 +6,7 @@ import (
)
// Grid demonstrates the grid layout.
func Grid(nextSlide func()) (title string, content cview.Primitive) {
func Grid(nextSlide func()) (title string, info string, content cview.Primitive) {
modalShown := false
panels := cview.NewPanels()
@ -57,5 +57,5 @@ func Grid(nextSlide func()) (title string, content cview.Primitive) {
panels.AddPanel("grid", grid, true, true)
panels.AddPanel("modal", modal, false, false)
return "Grid", panels
return "Grid", "", panels
}

View File

@ -30,12 +30,12 @@ const inputField = `[green]package[white] main
}`
// InputField demonstrates the InputField.
func InputField(nextSlide func()) (title string, content cview.Primitive) {
func InputField(nextSlide func()) (title string, info string, content cview.Primitive) {
input := cview.NewInputField()
input.SetLabel("Enter a number: ")
input.SetAcceptanceFunc(cview.InputFieldInteger)
input.SetDoneFunc(func(key tcell.Key) {
nextSlide()
})
return "InputField", Code(input, 30, 1, inputField)
return "InputField", "", Code(input, 30, 1, inputField)
}

View File

@ -3,7 +3,7 @@ package main
import "code.rocketnine.space/tslocum/cview"
// Introduction returns a cview.List with the highlights of the cview package.
func Introduction(nextSlide func()) (title string, content cview.Primitive) {
func Introduction(nextSlide func()) (title string, info string, content cview.Primitive) {
list := cview.NewList()
listText := [][]string{
@ -58,5 +58,5 @@ func Introduction(nextSlide func()) (title string, content cview.Primitive) {
})
reset()
return "Introduction", Center(80, 12, list)
return "Introduction", listInfo, Center(80, 12, list)
}

View File

@ -24,10 +24,19 @@ import (
"github.com/gdamore/tcell/v2"
)
// 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 cview.Primitive)
const (
appInfo = "Next slide: Ctrl-N Previous: Ctrl-P Exit: Ctrl-C (Navigate with your keyboard or mouse)"
listInfo = "Next item: J, Down Previous item: K, Up Open context menu: Alt+Enter"
textViewInfo = "Scroll down: J, Down, PageDown Scroll up: K, Up, PageUp"
sliderInfo = "Decrease: H, J, Left, Down Increase: K, L, Right, Up"
formInfo = "Next field: Tab Previous field: Shift+Tab Select: Enter"
windowInfo = "Windows may be dragged an resized using the mouse."
)
// Slide is a function which returns the slide's title, any applicable
// information and its main primitive, its. It receives a "nextSlide" function
// which can be called to advance the presentation to the next slide.
type Slide func(nextSlide func()) (title string, info string, content cview.Primitive)
// The application.
var app = cview.NewApplication()
@ -83,8 +92,21 @@ func main() {
for index, slide := range slides {
slideRegions = append(slideRegions, cursor)
title, primitive := slide(nextSlide)
panels.AddTab(strconv.Itoa(index), title, primitive)
title, info, primitive := slide(nextSlide)
h := cview.NewTextView()
if info != "" {
h.SetDynamicColors(true)
h.SetText(" [" + cview.ColorHex(cview.Styles.SecondaryTextColor) + "]Info:[-] " + info)
}
// Create a Flex layout that centers the logo and subtitle.
f := cview.NewFlex()
f.SetDirection(cview.FlexRow)
f.AddItem(h, 1, 1, false)
f.AddItem(primitive, 0, 1, true)
panels.AddTab(strconv.Itoa(index), title, f)
cursor += len(title) + 4
}

View File

@ -32,7 +32,7 @@ const sliderCode = `[green]package[white] main
}`
// Slider demonstrates the Slider.
func Slider(nextSlide func()) (title string, content cview.Primitive) {
func Slider(nextSlide func()) (title string, info string, content cview.Primitive) {
slider := cview.NewSlider()
slider.SetLabel("Volume: 0%")
slider.SetChangedFunc(func(value int) {
@ -41,5 +41,5 @@ func Slider(nextSlide func()) (title string, content cview.Primitive) {
slider.SetDoneFunc(func(key tcell.Key) {
nextSlide()
})
return "Slider", Code(slider, 30, 1, sliderCode)
return "Slider", sliderInfo, Code(slider, 30, 1, sliderCode)
}

View File

@ -248,7 +248,7 @@ const tableSelectCell = `[green]func[white] [yellow]main[white]() {
}`
// Table demonstrates the Table.
func Table(nextSlide func()) (title string, content cview.Primitive) {
func Table(nextSlide func()) (title string, info string, content cview.Primitive) {
table := cview.NewTable()
table.SetFixed(1, 1)
for row, line := range strings.Split(tableData, "\n") {
@ -378,5 +378,5 @@ func Table(nextSlide func()) (title string, content cview.Primitive) {
flex.AddItem(subFlex, 0, 1, true)
flex.AddItem(code, codeWidth, 1, false)
return "Table", flex
return "Table", "", flex
}

View File

@ -30,7 +30,7 @@ const textView1 = `[green]func[white] [yellow]main[white]() {
}`
// TextView1 demonstrates the basic text view.
func TextView1(nextSlide func()) (title string, content cview.Primitive) {
func TextView1(nextSlide func()) (title string, info string, content cview.Primitive) {
textView := cview.NewTextView()
textView.SetTextColor(tcell.ColorYellow.TrueColor())
textView.SetDoneFunc(func(key tcell.Key) {
@ -57,7 +57,7 @@ func TextView1(nextSlide func()) (title string, content cview.Primitive) {
textView.SetBorder(true)
textView.SetTitle("TextView implements io.Writer")
textView.ScrollToEnd()
return "TextView 1", Code(textView, 36, 13, textView1)
return "TextView 1", textViewInfo, Code(textView, 36, 13, textView1)
}
const textView2 = `[green]package[white] main
@ -108,7 +108,7 @@ const textView2 = `[green]package[white] main
}`
// TextView2 demonstrates the extended text view.
func TextView2(nextSlide func()) (title string, content cview.Primitive) {
func TextView2(nextSlide func()) (title string, info string, content cview.Primitive) {
codeView := cview.NewTextView()
codeView.SetWrap(false)
fmt.Fprint(codeView, textView2)
@ -159,5 +159,5 @@ func TextView2(nextSlide func()) (title string, content cview.Primitive) {
flex.AddItem(textView, 0, 1, true)
flex.AddItem(codeView, 0, 1, false)
return "TextView 2", flex
return "TextView 2", textViewInfo, flex
}

View File

@ -118,7 +118,7 @@ var rootNode = &node{
}}
// TreeView demonstrates the tree view.
func TreeView(nextSlide func()) (title string, content cview.Primitive) {
func TreeView(nextSlide func()) (title string, info string, content cview.Primitive) {
treeNextSlide = nextSlide
tree.SetBorder(true)
tree.SetTitle("TreeView")
@ -161,5 +161,5 @@ func TreeView(nextSlide func()) (title string, content cview.Primitive) {
flex.AddItem(tree, 0, 1, true)
flex.AddItem(treeCode, codeWidth, 1, false)
return "TreeView", flex
return "TreeView", "", flex
}

View File

@ -7,7 +7,7 @@ import (
const loremIpsumText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
// Window returns the window page.
func Window(nextSlide func()) (title string, content cview.Primitive) {
func Window(nextSlide func()) (title string, info string, content cview.Primitive) {
wm := cview.NewWindowManager()
list := cview.NewList()
@ -34,5 +34,5 @@ func Window(nextSlide func()) (title string, content cview.Primitive) {
wm.Add(w1, w2)
return "Window", wm
return "Window", windowInfo, wm
}

View File

@ -134,9 +134,15 @@ type DropDown struct {
// The chars to show when the option's text gets shortened.
abbreviationChars string
// The symbol to draw at the end of the field.
// The symbol to draw at the end of the field when closed.
dropDownSymbol rune
// The symbol to draw at the end of the field when opened.
dropDownOpenSymbol rune
// A flag that determines whether the drop down symbol is always drawn.
alwaysDrawDropDownSymbol bool
sync.RWMutex
}
@ -144,7 +150,7 @@ type DropDown struct {
func NewDropDown() *DropDown {
list := NewList()
list.ShowSecondaryText(false)
list.SetMainTextColor(Styles.PrimitiveBackgroundColor)
list.SetMainTextColor(Styles.SecondaryTextColor)
list.SetSelectedTextColor(Styles.PrimitiveBackgroundColor)
list.SetSelectedBackgroundColor(Styles.PrimaryTextColor)
list.SetHighlightFullLine(true)
@ -159,6 +165,7 @@ func NewDropDown() *DropDown {
fieldTextColor: Styles.PrimaryTextColor,
prefixTextColor: Styles.ContrastSecondaryTextColor,
dropDownSymbol: Styles.DropDownSymbol,
dropDownOpenSymbol: Styles.DropDownOpenSymbol,
abbreviationChars: Styles.DropDownAbbreviationChars,
labelColorFocused: ColorUnset,
fieldBackgroundColorFocused: ColorUnset,
@ -178,6 +185,22 @@ func (d *DropDown) SetDropDownSymbolRune(symbol rune) {
d.dropDownSymbol = symbol
}
// SetDropDownOpenSymbolRune sets the rune to be drawn at the end of the
// dropdown field to indicate that the a dropdown is open.
func (d *DropDown) SetDropDownOpenSymbolRune(symbol rune) {
d.Lock()
defer d.Unlock()
d.dropDownOpenSymbol = symbol
}
// SetAlwaysDrawDropDownSymbol sets a flad that determines whether the drop
// down symbol is always drawn. The symbol is normally only drawn when focused.
func (d *DropDown) SetAlwaysDrawDropDownSymbol(alwaysDraw bool) {
d.Lock()
defer d.Unlock()
d.alwaysDrawDropDownSymbol = alwaysDraw
}
// SetCurrentOption sets the index of the currently selected option. This may
// be a negative value to indicate that no option is currently selected. Calling
// this function will also trigger the "selected" callback (if there is one).
@ -594,7 +617,13 @@ func (d *DropDown) Draw(screen tcell.Screen) {
}
// Draw drop-down symbol
screen.SetContent(x+fieldWidth-2, y, d.dropDownSymbol, nil, new(tcell.Style).Foreground(fieldTextColor).Background(fieldBackgroundColor))
if d.alwaysDrawDropDownSymbol || d._hasFocus() {
symbol := d.dropDownSymbol
if d.open {
symbol = d.dropDownOpenSymbol
}
screen.SetContent(x+fieldWidth-2, y, symbol, nil, new(tcell.Style).Foreground(fieldTextColor).Background(fieldBackgroundColor))
}
// Draw options list.
if hasFocus && d.open {
@ -742,6 +771,10 @@ func (d *DropDown) HasFocus() bool {
d.RLock()
defer d.RUnlock()
return d._hasFocus()
}
func (d *DropDown) _hasFocus() bool {
if d.open {
return d.list.HasFocus()
}

16
form.go
View File

@ -151,14 +151,14 @@ func NewForm() *Form {
Box: box,
itemPadding: 1,
labelColor: Styles.SecondaryTextColor,
fieldBackgroundColor: Styles.MoreContrastBackgroundColor,
fieldBackgroundColorFocused: Styles.ContrastBackgroundColor,
fieldTextColor: Styles.InverseTextColor,
fieldTextColorFocused: Styles.InverseTextColor,
buttonBackgroundColor: Styles.MoreContrastBackgroundColor,
buttonBackgroundColorFocused: Styles.ContrastBackgroundColor,
buttonTextColor: Styles.InverseTextColor,
buttonTextColorFocused: Styles.InverseTextColor,
fieldBackgroundColor: Styles.ContrastBackgroundColor,
fieldBackgroundColorFocused: Styles.MoreContrastBackgroundColor,
fieldTextColor: Styles.PrimaryTextColor,
fieldTextColorFocused: Styles.PrimaryTextColor,
buttonBackgroundColor: Styles.ContrastBackgroundColor,
buttonBackgroundColorFocused: Styles.MoreContrastBackgroundColor,
buttonTextColor: Styles.PrimaryTextColor,
buttonTextColorFocused: Styles.PrimaryTextColor,
labelColorFocused: ColorUnset,
}

View File

@ -144,10 +144,10 @@ func NewInputField() *InputField {
return &InputField{
Box: NewBox(),
labelColor: Styles.SecondaryTextColor,
fieldBackgroundColor: Styles.PrimaryTextColor,
fieldBackgroundColorFocused: Styles.ContrastBackgroundColor,
fieldTextColor: Styles.ContrastPrimaryTextColor,
fieldTextColorFocused: Styles.ContrastPrimaryTextColor,
fieldBackgroundColor: Styles.ContrastBackgroundColor,
fieldBackgroundColorFocused: Styles.MoreContrastBackgroundColor,
fieldTextColor: Styles.PrimaryTextColor,
fieldTextColorFocused: Styles.PrimaryTextColor,
placeholderTextColor: Styles.ContrastSecondaryTextColor,
autocompleteListTextColor: Styles.PrimitiveBackgroundColor,
autocompleteListBackgroundColor: Styles.MoreContrastBackgroundColor,

View File

@ -65,9 +65,9 @@ func NewSlider() *Slider {
increment: 10,
labelColor: Styles.SecondaryTextColor,
fieldBackgroundColor: Styles.ContrastBackgroundColor,
fieldBackgroundColorFocused: Styles.MoreContrastBackgroundColor,
fieldTextColor: Styles.PrimaryTextColor,
labelColorFocused: ColorUnset,
fieldBackgroundColorFocused: ColorUnset,
fieldTextColorFocused: ColorUnset,
}
return s

View File

@ -33,7 +33,8 @@ type Theme struct {
// Drop down
DropDownAbbreviationChars string // The chars to show when the option's text gets shortened.
DropDownSymbol rune // The symbol to draw at the end of the field.
DropDownSymbol rune // The symbol to draw at the end of the field when closed.
DropDownOpenSymbol rune // The symbol to draw at the end of the field when opened.
// Scroll bar
ScrollBarColor tcell.Color
@ -59,8 +60,8 @@ var Styles = Theme{
ContrastSecondaryTextColor: tcell.ColorLightSlateGray.TrueColor(),
PrimitiveBackgroundColor: tcell.ColorBlack.TrueColor(),
ContrastBackgroundColor: tcell.ColorLimeGreen.TrueColor(),
MoreContrastBackgroundColor: tcell.ColorGreen.TrueColor(),
ContrastBackgroundColor: tcell.ColorGreen.TrueColor(),
MoreContrastBackgroundColor: tcell.ColorDarkGreen.TrueColor(),
CheckBoxCheckedRune: 'X',
@ -70,7 +71,8 @@ var Styles = Theme{
ContextMenuPaddingRight: 1,
DropDownAbbreviationChars: "...",
DropDownSymbol: '▼',
DropDownSymbol: '◀',
DropDownOpenSymbol: '▼',
ScrollBarColor: tcell.ColorWhite.TrueColor(),