Browse Source

Remove return values from methods which return their primitive (breaks chaining)

tablepad
Trevor Slocum 1 year ago
parent
commit
ed5e6d94dd
  1. 1
      CHANGELOG
  2. 18
      FORK.md
  3. 73
      application.go
  4. 43
      box.go
  5. 21
      button.go
  6. 42
      checkbox.go
  7. 43
      contextmenu.go
  8. 14
      demos/box/main.go
  9. 12
      demos/button/main.go
  10. 9
      demos/checkbox/main.go
  11. 22
      demos/dropdown/main.go
  12. 30
      demos/flex/main.go
  13. 39
      demos/form/main.go
  14. 25
      demos/frame/main.go
  15. 37
      demos/grid/main.go
  16. 20
      demos/inputfield/autocomplete/main.go
  17. 15
      demos/inputfield/autocompleteasync/main.go
  18. 22
      demos/inputfield/simple/main.go
  19. 30
      demos/list/main.go
  20. 22
      demos/modal/main.go
  21. 31
      demos/pages/main.go
  22. 20
      demos/presentation/center.go
  23. 13
      demos/presentation/code.go
  24. 20
      demos/presentation/colors.go
  25. 40
      demos/presentation/cover.go
  26. 3
      demos/presentation/end.go
  27. 63
      demos/presentation/flex.go
  28. 19
      demos/presentation/form.go
  29. 62
      demos/presentation/grid.go
  30. 7
      demos/presentation/inputfield.go
  31. 26
      demos/presentation/introduction.go
  32. 33
      demos/presentation/main.go
  33. 106
      demos/presentation/table.go
  34. 98
      demos/presentation/textview.go
  35. 68
      demos/presentation/treeview.go
  36. 39
      demos/presentation/window.go
  37. 12
      demos/primitive/main.go
  38. 7
      demos/progressbar/main.go
  39. 25
      demos/table/main.go
  40. 26
      demos/textview/main.go
  41. 22
      demos/treeview/main.go
  42. 55
      demos/unicode/main.go
  43. 23
      doc_test.go
  44. 103
      dropdown.go
  45. 21
      flex.go
  46. 157
      form.go
  47. 9
      frame.go
  48. 36
      grid.go
  49. 108
      inputfield.go
  50. 108
      list.go
  51. 10
      list_test.go
  52. 50
      modal.go
  53. 27
      pages.go
  54. 6
      progressbar.go
  55. 122
      table.go
  56. 84
      textview.go
  57. 22
      textview_test.go
  58. 95
      treeview.go
  59. 7
      util_test.go
  60. 9
      window.go

1
CHANGELOG

@ -4,6 +4,7 @@ v1.5.1 (WIP)
- Add TableCell.SetBytes, TableCell.GetBytes and TableCell.GetText
- Allow modification of scroll bar render text
- Optimize TextView (writing is 90% faster, drawing is 50% faster)
- Remove return values from methods which return their primitive (breaks chaining)
v1.5.0 (2020-10-03)
- Add scroll bar to TextView

18
FORK.md

@ -26,6 +26,14 @@ maintainers and allowing code changes which may be outside of tview's scope.
# Differences
## Primitive methods do not return the primitive they belong to
When chaining multiple method calls on a primitive together, application
developers might accidentally end the chain with a different return type than
the first method call. This could result in unexpected return types. For
example, ending a chain with `SetTitle` would result in a `Box` rather than the
original primitive.
## cview is [thread-safe](https://docs.rocketnine.space/gitlab.com/tslocum/cview/#hdr-Concurrency)
tview [is not thread-safe](https://godoc.org/github.com/rivo/tview#hdr-Concurrency).
@ -38,11 +46,6 @@ tview [blocks until the queued function returns](https://github.com/rivo/tview/b
All clicks are handled as single clicks until an interval is set with [Application.SetDoubleClickInterval](https://docs.rocketnine.space/gitlab.com/tslocum/cview/#Application.SetDoubleClickInterval).
## Setting a primitive's background color to `tcell.ColorDefault` does not result in transparency
Call [Box.SetBackgroundTransparent](https://docs.rocketnine.space/gitlab.com/tslocum/cview/#Box.SetBackgroundTransparent)
to enable background transparency.
## Tables are sorted when a fixed row is clicked by default
Call [Table.SetSortClicked](https://docs.rocketnine.space/gitlab.com/tslocum/cview/#Table.SetSortClicked)
@ -53,6 +56,11 @@ to disable this behavior.
Call [List.SetWrapAround](https://docs.rocketnine.space/gitlab.com/tslocum/cview/#List.SetWrapAround)
to wrap around when navigating.
## Setting a primitive's background color to `tcell.ColorDefault` does not result in transparency
Call [Box.SetBackgroundTransparent](https://docs.rocketnine.space/gitlab.com/tslocum/cview/#Box.SetBackgroundTransparent)
to enable background transparency.
## TextViews store their text as []byte instead of string
This greatly improves buffer efficiency. [TextView.Write](https://docs.rocketnine.space/gitlab.com/tslocum/cview/#TextView.Write)

73
application.go

@ -127,12 +127,12 @@ func NewApplication() *Application {
// Note that this also affects the default event handling of the application
// itself: Such a handler can intercept the Ctrl-C event which closes the
// application.
func (a *Application) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Application {
func (a *Application) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) {
a.Lock()
defer a.Unlock()
a.inputCapture = capture
return a
}
// GetInputCapture returns the function installed with SetInputCapture() or nil
@ -149,9 +149,9 @@ func (a *Application) GetInputCapture() func(event *tcell.EventKey) *tcell.Event
// forwarded to the appropriate mouse event handler. This function can then
// choose to forward that event (or a different one) by returning it or stop
// the event processing by returning a nil mouse event.
func (a *Application) SetMouseCapture(capture func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction)) *Application {
func (a *Application) SetMouseCapture(capture func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction)) {
a.mouseCapture = capture
return a
}
// GetMouseCapture returns the function installed with SetMouseCapture() or nil
@ -173,9 +173,9 @@ func (a *Application) SetDoubleClickInterval(interval time.Duration) {
//
// This function is typically called before the first call to Run(). Init() need
// not be called on the screen.
func (a *Application) SetScreen(screen tcell.Screen) *Application {
func (a *Application) SetScreen(screen tcell.Screen) {
if screen == nil {
return a // Invalid input. Do nothing.
return // Invalid input. Do nothing.
}
a.Lock()
@ -183,7 +183,7 @@ func (a *Application) SetScreen(screen tcell.Screen) *Application {
// Run() has not been called yet.
a.screen = screen
a.Unlock()
return a
return
}
// Run() is already in progress. Exchange screen.
@ -191,12 +191,10 @@ func (a *Application) SetScreen(screen tcell.Screen) *Application {
a.Unlock()
oldScreen.Fini()
a.screenReplacement <- screen
return a
}
// EnableMouse enables mouse events.
func (a *Application) EnableMouse(enable bool) *Application {
func (a *Application) EnableMouse(enable bool) {
a.Lock()
defer a.Unlock()
if enable != a.enableMouse && a.screen != nil {
@ -207,7 +205,7 @@ func (a *Application) EnableMouse(enable bool) *Application {
}
}
a.enableMouse = enable
return a
}
// Run starts the application and thus the event loop. This function returns
@ -545,11 +543,11 @@ func (a *Application) Suspend(f func()) bool {
// Draw refreshes the screen (during the next update cycle). It calls the Draw()
// function of the application's root primitive and then syncs the screen
// buffer.
func (a *Application) Draw() *Application {
func (a *Application) Draw() {
a.QueueUpdate(func() {
a.draw()
})
return a
}
// ForceDraw refreshes the screen immediately. Use this function with caution as
@ -559,12 +557,12 @@ func (a *Application) Draw() *Application {
//
// It is safe to call this function during queued updates and direct event
// handling.
func (a *Application) ForceDraw() *Application {
return a.draw()
func (a *Application) ForceDraw() {
a.draw()
}
// draw actually does what Draw() promises to do.
func (a *Application) draw() *Application {
func (a *Application) draw() {
a.Lock()
screen := a.screen
@ -576,7 +574,7 @@ func (a *Application) draw() *Application {
// Maybe we're not ready yet or not anymore.
if screen == nil || root == nil {
a.Unlock()
return a
return
}
// Resize if requested.
@ -590,7 +588,7 @@ func (a *Application) draw() *Application {
a.Unlock()
if before(screen) {
screen.Show()
return a
return
}
} else {
a.Unlock()
@ -606,8 +604,6 @@ func (a *Application) draw() *Application {
// Sync screen.
screen.Show()
return a
}
// SetBeforeDrawFunc installs a callback function which is invoked just before
@ -619,12 +615,11 @@ func (a *Application) draw() *Application {
// you may call screen.Clear().
//
// Provide nil to uninstall the callback function.
func (a *Application) SetBeforeDrawFunc(handler func(screen tcell.Screen) bool) *Application {
func (a *Application) SetBeforeDrawFunc(handler func(screen tcell.Screen) bool) {
a.Lock()
defer a.Unlock()
a.beforeDraw = handler
return a
}
// GetBeforeDrawFunc returns the callback function installed with
@ -640,12 +635,11 @@ func (a *Application) GetBeforeDrawFunc() func(screen tcell.Screen) bool {
// primitive was drawn during screen updates.
//
// Provide nil to uninstall the callback function.
func (a *Application) SetAfterDrawFunc(handler func(screen tcell.Screen)) *Application {
func (a *Application) SetAfterDrawFunc(handler func(screen tcell.Screen)) {
a.Lock()
defer a.Unlock()
a.afterDraw = handler
return a
}
// GetAfterDrawFunc returns the callback function installed with
@ -664,7 +658,7 @@ func (a *Application) GetAfterDrawFunc() func(screen tcell.Screen) {
// the application starts.
//
// It also calls SetFocus() on the primitive.
func (a *Application) SetRoot(root Primitive, fullscreen bool) *Application {
func (a *Application) SetRoot(root Primitive, fullscreen bool) {
a.Lock()
a.root = root
a.rootFullscreen = fullscreen
@ -674,18 +668,15 @@ func (a *Application) SetRoot(root Primitive, fullscreen bool) *Application {
a.Unlock()
a.SetFocus(root)
return a
}
// ResizeToFullScreen resizes the given primitive such that it fills the entire
// screen.
func (a *Application) ResizeToFullScreen(p Primitive) *Application {
func (a *Application) ResizeToFullScreen(p Primitive) {
a.RLock()
width, height := a.screen.Size()
a.RUnlock()
p.SetRect(0, 0, width, height)
return a
}
// SetAfterResizeFunc installs a callback function which is invoked when the
@ -694,12 +685,11 @@ func (a *Application) ResizeToFullScreen(p Primitive) *Application {
// application is drawn.
//
// Provide nil to uninstall the callback function.
func (a *Application) SetAfterResizeFunc(handler func(width int, height int)) *Application {
func (a *Application) SetAfterResizeFunc(handler func(width int, height int)) {
a.Lock()
defer a.Unlock()
a.afterResize = handler
return a
}
// GetAfterResizeFunc returns the callback function installed with
@ -717,14 +707,14 @@ func (a *Application) GetAfterResizeFunc() func(width int, height int) {
//
// Blur() will be called on the previously focused primitive. Focus() will be
// called on the new primitive.
func (a *Application) SetFocus(p Primitive) *Application {
func (a *Application) SetFocus(p Primitive) {
a.Lock()
if a.beforeFocus != nil {
a.Unlock()
ok := a.beforeFocus(p)
if !ok {
return a
return
}
a.Lock()
}
@ -752,8 +742,6 @@ func (a *Application) SetFocus(p Primitive) *Application {
a.SetFocus(p)
})
}
return a
}
// GetFocus returns the primitive which has the current focus. If none has it,
@ -769,24 +757,22 @@ func (a *Application) GetFocus() Primitive {
// application's focus changes. Return false to maintain the current focus.
//
// Provide nil to uninstall the callback function.
func (a *Application) SetBeforeFocusFunc(handler func(p Primitive) bool) *Application {
func (a *Application) SetBeforeFocusFunc(handler func(p Primitive) bool) {
a.Lock()
defer a.Unlock()
a.beforeFocus = handler
return a
}
// SetAfterFocusFunc installs a callback function which is invoked after the
// application's focus changes.
//
// Provide nil to uninstall the callback function.
func (a *Application) SetAfterFocusFunc(handler func(p Primitive)) *Application {
func (a *Application) SetAfterFocusFunc(handler func(p Primitive)) {
a.Lock()
defer a.Unlock()
a.afterFocus = handler
return a
}
// QueueUpdate queues a function to be executed as part of the event loop.
@ -795,27 +781,24 @@ func (a *Application) SetAfterFocusFunc(handler func(p Primitive)) *Application
// may not be desirable. You can call Draw() from f if the screen should be
// refreshed after each update. Alternatively, use QueueUpdateDraw() to follow
// up with an immediate refresh of the screen.
func (a *Application) QueueUpdate(f func()) *Application {
func (a *Application) QueueUpdate(f func()) {
a.updates <- f
return a
}
// QueueUpdateDraw works like QueueUpdate() except it refreshes the screen
// immediately after executing f.
func (a *Application) QueueUpdateDraw(f func()) *Application {
func (a *Application) QueueUpdateDraw(f func()) {
a.QueueUpdate(func() {
f()
a.draw()
})
return a
}
// QueueEvent sends an event to the Application event loop.
//
// It is not recommended for event to be nil.
func (a *Application) QueueEvent(event tcell.Event) *Application {
func (a *Application) QueueEvent(event tcell.Event) {
a.events <- event
return a
}
// RingBell sends a bell code to the terminal.

43
box.go

@ -98,12 +98,11 @@ func (b *Box) GetBorderPadding() (top, bottom, left, right int) {
}
// SetBorderPadding sets the size of the borders around the box content.
func (b *Box) SetBorderPadding(top, bottom, left, right int) *Box {
func (b *Box) SetBorderPadding(top, bottom, left, right int) {
b.l.Lock()
defer b.l.Unlock()
b.paddingTop, b.paddingBottom, b.paddingLeft, b.paddingRight = top, bottom, left, right
return b
}
// GetRect returns the current position of the rectangle, x, y, width, and
@ -172,12 +171,11 @@ func (b *Box) SetRect(x, y, width, height int) {
// must return the box's inner dimensions (x, y, width, height) which will be
// returned by GetInnerRect(), used by descendent primitives to draw their own
// content.
func (b *Box) SetDrawFunc(handler func(screen tcell.Screen, x, y, width, height int) (int, int, int, int)) *Box {
func (b *Box) SetDrawFunc(handler func(screen tcell.Screen, x, y, width, height int) (int, int, int, int)) {
b.l.Lock()
defer b.l.Unlock()
b.draw = handler
return b
}
// GetDrawFunc returns the callback function which was installed with
@ -227,12 +225,11 @@ func (b *Box) InputHandler() func(event *tcell.EventKey, setFocus func(p Primiti
// can have focus at a time. Composing primitives such as Form pass the focus on
// to their contained primitives and thus never receive any key events
// themselves. Therefore, they cannot intercept key events.
func (b *Box) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Box {
func (b *Box) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) {
b.l.Lock()
defer b.l.Unlock()
b.inputCapture = capture
return b
}
// GetInputCapture returns the function installed with SetInputCapture() or nil
@ -280,9 +277,8 @@ func (b *Box) MouseHandler() func(action MouseAction, event *tcell.EventMouse, s
// called.
//
// Providing a nil handler will remove a previously existing handler.
func (b *Box) SetMouseCapture(capture func(action MouseAction, event *tcell.EventMouse) (MouseAction, *tcell.EventMouse)) *Box {
func (b *Box) SetMouseCapture(capture func(action MouseAction, event *tcell.EventMouse) (MouseAction, *tcell.EventMouse)) {
b.mouseCapture = capture
return b
}
// InRect returns true if the given coordinate is within the bounds of the box's
@ -299,12 +295,11 @@ func (b *Box) GetMouseCapture() func(action MouseAction, event *tcell.EventMouse
}
// SetBackgroundColor sets the box's background color.
func (b *Box) SetBackgroundColor(color tcell.Color) *Box {
func (b *Box) SetBackgroundColor(color tcell.Color) {
b.l.Lock()
defer b.l.Unlock()
b.backgroundColor = color
return b
}
// GetBackgroundColor returns the box's background color.
@ -316,12 +311,11 @@ func (b *Box) GetBackgroundColor() tcell.Color {
// SetBackgroundTransparent sets the flag indicating whether or not the box's
// background is transparent.
func (b *Box) SetBackgroundTransparent(transparent bool) *Box {
func (b *Box) SetBackgroundTransparent(transparent bool) {
b.l.Lock()
defer b.l.Unlock()
b.backgroundTransparent = transparent
return b
}
// GetBorder returns a value indicating whether the box have a border
@ -334,50 +328,45 @@ func (b *Box) GetBorder() bool {
// SetBorder sets the flag indicating whether or not the box should have a
// border.
func (b *Box) SetBorder(show bool) *Box {
func (b *Box) SetBorder(show bool) {
b.l.Lock()
defer b.l.Unlock()
b.border = show
return b
}
// SetBorderColor sets the box's border color.
func (b *Box) SetBorderColor(color tcell.Color) *Box {
func (b *Box) SetBorderColor(color tcell.Color) {
b.l.Lock()
defer b.l.Unlock()
b.borderColor = color
return b
}
// SetBorderColorFocused sets the box's border color when the box is focused.
func (b *Box) SetBorderColorFocused(color tcell.Color) *Box {
func (b *Box) SetBorderColorFocused(color tcell.Color) {
b.l.Lock()
defer b.l.Unlock()
b.borderColorFocused = color
return b
}
// SetBorderAttributes sets the border's style attributes. You can combine
// different attributes using bitmask operations:
//
// box.SetBorderAttributes(tcell.AttrUnderline | tcell.AttrBold)
func (b *Box) SetBorderAttributes(attr tcell.AttrMask) *Box {
func (b *Box) SetBorderAttributes(attr tcell.AttrMask) {
b.l.Lock()
defer b.l.Unlock()
b.borderAttributes = attr
return b
}
// SetTitle sets the box's title.
func (b *Box) SetTitle(title string) *Box {
func (b *Box) SetTitle(title string) {
b.l.Lock()
defer b.l.Unlock()
b.title = []byte(title)
return b
}
// GetTitle returns the box's current title.
@ -389,22 +378,21 @@ func (b *Box) GetTitle() string {
}
// SetTitleColor sets the box's title color.
func (b *Box) SetTitleColor(color tcell.Color) *Box {
func (b *Box) SetTitleColor(color tcell.Color) {
b.l.Lock()
defer b.l.Unlock()
b.titleColor = color
return b
}
// SetTitleAlign sets the alignment of the title, one of AlignLeft, AlignCenter,
// or AlignRight.
func (b *Box) SetTitleAlign(align int) *Box {
func (b *Box) SetTitleAlign(align int) {
b.l.Lock()
defer b.l.Unlock()
b.titleAlign = align
return b
}
// Draw draws this primitive onto the screen.
@ -527,12 +515,11 @@ func (b *Box) Draw(screen tcell.Screen) {
// ShowFocus sets the flag indicating whether or not the borders of this
// primitive should change thickness when focused.
func (b *Box) ShowFocus(showFocus bool) *Box {
func (b *Box) ShowFocus(showFocus bool) {
b.l.Lock()
defer b.l.Unlock()
b.showFocus = showFocus
return b
}
// Focus is called when this primitive receives focus.

21
button.go

@ -34,7 +34,8 @@ type Button struct {
// NewButton returns a new input field.
func NewButton(label string) *Button {
box := NewBox().SetBackgroundColor(Styles.ContrastBackgroundColor)
box := NewBox()
box.SetBackgroundColor(Styles.ContrastBackgroundColor)
box.SetRect(0, 0, TaggedStringWidth(label)+4, 1)
return &Button{
Box: box,
@ -46,12 +47,11 @@ func NewButton(label string) *Button {
}
// SetLabel sets the button text.
func (b *Button) SetLabel(label string) *Button {
func (b *Button) SetLabel(label string) {
b.Lock()
defer b.Unlock()
b.label = []byte(label)
return b
}
// GetLabel returns the button text.
@ -63,41 +63,37 @@ func (b *Button) GetLabel() string {
}
// SetLabelColor sets the color of the button text.
func (b *Button) SetLabelColor(color tcell.Color) *Button {
func (b *Button) SetLabelColor(color tcell.Color) {
b.Lock()
defer b.Unlock()
b.labelColor = color
return b
}
// SetLabelColorFocused sets the color of the button text when the button is
// in focus.
func (b *Button) SetLabelColorFocused(color tcell.Color) *Button {
func (b *Button) SetLabelColorFocused(color tcell.Color) {
b.Lock()
defer b.Unlock()
b.labelColorFocused = color
return b
}
// SetBackgroundColorFocused sets the background color of the button text when
// the button is in focus.
func (b *Button) SetBackgroundColorFocused(color tcell.Color) *Button {
func (b *Button) SetBackgroundColorFocused(color tcell.Color) {
b.Lock()
defer b.Unlock()
b.backgroundColorFocused = color
return b
}
// SetSelectedFunc sets a handler which is called when the button was selected.
func (b *Button) SetSelectedFunc(handler func()) *Button {
func (b *Button) SetSelectedFunc(handler func()) {
b.Lock()
defer b.Unlock()
b.selected = handler
return b
}
// SetBlurFunc sets a handler which is called when the user leaves the button.
@ -107,12 +103,11 @@ func (b *Button) SetSelectedFunc(handler func()) *Button {
// - KeyEscape: Leaving the button with no specific direction.
// - KeyTab: Move to the next field.
// - KeyBacktab: Move to the previous field.
func (b *Button) SetBlurFunc(handler func(key tcell.Key)) *Button {
func (b *Button) SetBlurFunc(handler func(key tcell.Key)) {
b.Lock()
defer b.Unlock()
b.blur = handler
return b
}
// Draw draws this primitive onto the screen.

42
checkbox.go

@ -76,21 +76,19 @@ func NewCheckBox() *CheckBox {
}
// SetChecked sets the state of the checkbox.
func (c *CheckBox) SetChecked(checked bool) *CheckBox {
func (c *CheckBox) SetChecked(checked bool) {
c.Lock()
defer c.Unlock()
c.checked = checked
return c
}
// SetCheckedRune sets the rune to show when the checkbox is checked.
func (c *CheckBox) SetCheckedRune(rune rune) *CheckBox {
func (c *CheckBox) SetCheckedRune(rune rune) {
c.Lock()
defer c.Unlock()
c.checkedRune = rune
return c
}
// IsChecked returns whether or not the box is checked.
@ -102,12 +100,11 @@ func (c *CheckBox) IsChecked() bool {
}
// SetLabel sets the text to be displayed before the input area.
func (c *CheckBox) SetLabel(label string) *CheckBox {
func (c *CheckBox) SetLabel(label string) {
c.Lock()
defer c.Unlock()
c.label = []byte(label)
return c
}
// GetLabel returns the text to be displayed before the input area.
@ -119,12 +116,11 @@ func (c *CheckBox) GetLabel() string {
}
// SetMessage sets the text to be displayed after the checkbox
func (c *CheckBox) SetMessage(message string) *CheckBox {
func (c *CheckBox) SetMessage(message string) {
c.Lock()
defer c.Unlock()
c.message = []byte(message)
return c
}
// GetMessage returns the text to be displayed after the checkbox
@ -137,66 +133,59 @@ func (c *CheckBox) GetMessage() string {
// SetLabelWidth sets the screen width of the label. A value of 0 will cause the
// primitive to use the width of the label string.
func (c *CheckBox) SetLabelWidth(width int) *CheckBox {
func (c *CheckBox) SetLabelWidth(width int) {
c.Lock()
defer c.Unlock()
c.labelWidth = width
return c
}
// SetLabelColor sets the color of the label.
func (c *CheckBox) SetLabelColor(color tcell.Color) *CheckBox {
func (c *CheckBox) SetLabelColor(color tcell.Color) {
c.Lock()
defer c.Unlock()
c.labelColor = color
return c
}
// SetLabelColorFocused sets the color of the label when focused.
func (c *CheckBox) SetLabelColorFocused(color tcell.Color) *CheckBox {
func (c *CheckBox) SetLabelColorFocused(color tcell.Color) {
c.Lock()
defer c.Unlock()
c.labelColorFocused = color
return c
}
// SetFieldBackgroundColor sets the background color of the input area.
func (c *CheckBox) SetFieldBackgroundColor(color tcell.Color) *CheckBox {
func (c *CheckBox) SetFieldBackgroundColor(color tcell.Color) {
c.Lock()
defer c.Unlock()
c.fieldBackgroundColor = color
return c
}
// SetFieldBackgroundColorFocused sets the background color of the input area when focused.
func (c *CheckBox) SetFieldBackgroundColorFocused(color tcell.Color) *CheckBox {
func (c *CheckBox) SetFieldBackgroundColorFocused(color tcell.Color) {
c.Lock()
defer c.Unlock()
c.fieldBackgroundColorFocused = color
return c
}
// SetFieldTextColor sets the text color of the input area.
func (c *CheckBox) SetFieldTextColor(color tcell.Color) *CheckBox {
func (c *CheckBox) SetFieldTextColor(color tcell.Color) {
c.Lock()
defer c.Unlock()
c.fieldTextColor = color
return c
}
// SetFieldTextColorFocused sets the text color of the input area when focused.
func (c *CheckBox) SetFieldTextColorFocused(color tcell.Color) *CheckBox {
func (c *CheckBox) SetFieldTextColorFocused(color tcell.Color) {
c.Lock()
defer c.Unlock()
c.fieldTextColorFocused = color
return c
}
// GetFieldHeight returns the height of the field.
@ -219,12 +208,11 @@ func (c *CheckBox) GetFieldWidth() int {
// SetChangedFunc sets a handler which is called when the checked state of this
// checkbox was changed by the user. The handler function receives the new
// state.
func (c *CheckBox) SetChangedFunc(handler func(checked bool)) *CheckBox {
func (c *CheckBox) SetChangedFunc(handler func(checked bool)) {
c.Lock()
defer c.Unlock()
c.changed = handler
return c
}
// SetDoneFunc sets a handler which is called when the user is done using the
@ -234,21 +222,19 @@ func (c *CheckBox) SetChangedFunc(handler func(checked bool)) *CheckBox {
// - KeyEscape: Abort text input.
// - KeyTab: Move to the next field.
// - KeyBacktab: Move to the previous field.
func (c *CheckBox) SetDoneFunc(handler func(key tcell.Key)) *CheckBox {
func (c *CheckBox) SetDoneFunc(handler func(key tcell.Key)) {
c.Lock()
defer c.Unlock()
c.done = handler
return c
}
// SetFinishedFunc sets a callback invoked when the user leaves this form item.
func (c *CheckBox) SetFinishedFunc(handler func(key tcell.Key)) *CheckBox {
func (c *CheckBox) SetFinishedFunc(handler func(key tcell.Key)) {
c.Lock()
defer c.Unlock()
c.finished = handler
return c
}
// SetAttributes applies attribute settings to a form item.

43
contextmenu.go

@ -28,18 +28,17 @@ func (c *ContextMenu) initializeList() {
return
}
c.list = NewList().
ShowSecondaryText(false).
SetHover(true).
SetWrapAround(true)
c.list.
ShowFocus(false).
SetBorder(true).
SetBorderPadding(
Styles.ContextMenuPaddingTop,
Styles.ContextMenuPaddingBottom,
Styles.ContextMenuPaddingLeft,
Styles.ContextMenuPaddingRight)
c.list = NewList()
c.list.ShowSecondaryText(false)
c.list.SetHover(true)
c.list.SetWrapAround(true)
c.list.ShowFocus(false)
c.list.SetBorder(true)
c.list.SetBorderPadding(
Styles.ContextMenuPaddingTop,
Styles.ContextMenuPaddingBottom,
Styles.ContextMenuPaddingLeft,
Styles.ContextMenuPaddingRight)
}
// ContextMenuList returns the underlying List of the context menu.
@ -54,21 +53,23 @@ func (c *ContextMenu) ContextMenuList() *List {
// AddContextItem adds an item to the context menu. Adding an item with no text
// or shortcut will add a divider.
func (c *ContextMenu) AddContextItem(text string, shortcut rune, selected func(index int)) *ContextMenu {
func (c *ContextMenu) AddContextItem(text string, shortcut rune, selected func(index int)) {
c.l.Lock()
defer c.l.Unlock()
c.initializeList()
c.list.AddItem(NewListItem(text).SetShortcut(shortcut).SetSelectedFunc(c.wrap(selected)))
item := NewListItem(text)
item.SetShortcut(shortcut)
item.SetSelectedFunc(c.wrap(selected))
c.list.AddItem(item)
if text == "" && shortcut == 0 {
c.list.Lock()
index := len(c.list.items) - 1
c.list.items[index].enabled = false
c.list.Unlock()
}
return c
}
func (c *ContextMenu) wrap(f func(index int)) func() {
@ -78,27 +79,24 @@ func (c *ContextMenu) wrap(f func(index int)) func() {
}
// ClearContextMenu removes all items from the context menu.
func (c *ContextMenu) ClearContextMenu() *ContextMenu {
func (c *ContextMenu) ClearContextMenu() {
c.l.Lock()
defer c.l.Unlock()
c.initializeList()
c.list.Clear()
return c
}
// SetContextSelectedFunc sets the function which is called when the user
// selects a context menu item. The function receives the item's index in the
// menu (starting with 0), its text and its shortcut rune. SetSelectedFunc must
// be called before the context menu is shown.
func (c *ContextMenu) SetContextSelectedFunc(handler func(index int, text string, shortcut rune)) *ContextMenu {
func (c *ContextMenu) SetContextSelectedFunc(handler func(index int, text string, shortcut rune)) {
c.l.Lock()
defer c.l.Unlock()
c.selected = handler
return c
}
// ShowContextMenu shows the context menu. Provide -1 for both to position on
@ -158,7 +156,8 @@ func (c *ContextMenu) show(item int, x int, y int, setFocus func(Primitive)) {
} else {
c.l.Unlock()
}
}).SetDoneFunc(func() {
})
c.list.SetDoneFunc(func() {
c.l.Lock()
defer c.l.Unlock()

14
demos/box/main.go

@ -7,11 +7,15 @@ import (
)
func main() {
box := cview.NewBox().
SetBorder(true).
SetBorderAttributes(tcell.AttrBold).
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")
if err := cview.NewApplication().SetRoot(box, true).Run(); err != nil {
app := cview.NewApplication()
box := cview.NewBox()
box.SetBorder(true)
box.SetBorderAttributes(tcell.AttrBold)
box.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")
app.SetRoot(box, true)
if err := app.Run(); err != nil {
panic(err)
}
}

12
demos/button/main.go

@ -5,11 +5,17 @@ import "gitlab.com/tslocum/cview"
func main() {
app := cview.NewApplication()
button := cview.NewButton("Hit Enter to close").SetSelectedFunc(func() {
app.EnableMouse(true)
button := cview.NewButton("Hit Enter to close")
button.SetBorder(true)
button.SetRect(0, 0, 22, 3)
button.SetSelectedFunc(func() {
app.Stop()
})
button.SetBorder(true).SetRect(0, 0, 22, 3)
if err := app.SetRoot(button, false).EnableMouse(true).Run(); err != nil {
app.SetRoot(button, false)
if err := app.Run(); err != nil {
panic(err)
}
}

9
demos/checkbox/main.go

@ -7,8 +7,13 @@ import (
func main() {
app := cview.NewApplication()
checkbox := cview.NewCheckBox().SetLabel("Hit Enter to check box: ")
if err := app.SetRoot(checkbox, true).EnableMouse(true).Run(); err != nil {
app.EnableMouse(true)
checkbox := cview.NewCheckBox()
checkbox.SetLabel("Hit Enter to check box: ")
app.SetRoot(checkbox, true)
if err := app.Run(); err != nil {
panic(err)
}
}

22
demos/dropdown/main.go

@ -5,15 +5,19 @@ import "gitlab.com/tslocum/cview"
func main() {
app := cview.NewApplication()
dropdown := cview.NewDropDown().
SetLabel("Select an option (hit Enter): ").
SetOptions(nil,
cview.NewDropDownOption("First"),
cview.NewDropDownOption("Second"),
cview.NewDropDownOption("Third"),
cview.NewDropDownOption("Fourth"),
cview.NewDropDownOption("Fifth"))
if err := app.SetRoot(dropdown, true).EnableMouse(true).Run(); err != nil {
app.EnableMouse(true)
dropdown := cview.NewDropDown()
dropdown.SetLabel("Select an option (hit Enter): ")
dropdown.SetOptions(nil,
cview.NewDropDownOption("First"),
cview.NewDropDownOption("Second"),
cview.NewDropDownOption("Third"),
cview.NewDropDownOption("Fourth"),
cview.NewDropDownOption("Fifth"))
app.SetRoot(dropdown, true)
if err := app.Run(); err != nil {
panic(err)
}
}

30
demos/flex/main.go

@ -5,16 +5,30 @@ import (
"gitlab.com/tslocum/cview"
)
func demoBox(title string) *cview.Box {
b := cview.NewBox()
b.SetBorder(true)
b.SetTitle(title)
return b
}
func main() {
app := cview.NewApplication()
flex := cview.NewFlex().
AddItem(cview.NewBox().SetBorder(true).SetTitle("Left (1/2 x width of Top)"), 0, 1, false).
AddItem(cview.NewFlex().SetDirection(cview.FlexRow).
AddItem(cview.NewBox().SetBorder(true).SetTitle("Top"), 0, 1, false).
AddItem(cview.NewBox().SetBorder(true).SetTitle("Middle (3 x height of Top)"), 0, 3, false).
AddItem(cview.NewBox().SetBorder(true).SetTitle("Bottom (5 rows)"), 5, 1, false), 0, 2, false).
AddItem(cview.NewBox().SetBorder(true).SetTitle("Right (20 cols)"), 20, 1, false)
if err := app.SetRoot(flex, true).EnableMouse(true).Run(); err != nil {
app.EnableMouse(true)
subFlex := cview.NewFlex()
subFlex.SetDirection(cview.FlexRow)
subFlex.AddItem(demoBox("Top"), 0, 1, false)
subFlex.AddItem(demoBox("Middle (3 x height of Top)"), 0, 3, false)
subFlex.AddItem(demoBox("Bottom (5 rows)"), 5, 1, false)
flex := cview.NewFlex()
flex.AddItem(demoBox("Left (1/2 x width of Top)"), 0, 1, false)
flex.AddItem(subFlex, 0, 2, false)
flex.AddItem(demoBox("Right (20 cols)"), 20, 1, false)
app.SetRoot(flex, true)
if err := app.Run(); err != nil {
panic(err)
}
}

39
demos/form/main.go

@ -7,22 +7,29 @@ import (
func main() {
app := cview.NewApplication()
form := cview.NewForm().
AddDropDownSimple("Title", 0, nil, "Mr.", "Ms.", "Mrs.", "Dr.", "Prof.").
AddInputField("First name", "", 20, nil, nil).
AddInputField("Last name", "", 20, nil, nil).
AddFormItem(cview.NewInputField().
SetLabel("Address").
SetFieldWidth(30).
SetFieldNote("Your complete address")).
AddPasswordField("Password", "", 10, '*', nil).
AddCheckBox("", "Age 18+", false, nil).
AddButton("Save", nil).
AddButton("Quit", func() {
app.Stop()
})
form.SetBorder(true).SetTitle("Enter some data").SetTitleAlign(cview.AlignLeft)
if err := app.SetRoot(form, true).EnableMouse(true).Run(); err != nil {
app.EnableMouse(true)
form := cview.NewForm()
form.AddDropDownSimple("Title", 0, nil, "Mr.", "Ms.", "Mrs.", "Dr.", "Prof.")
form.AddInputField("First name", "", 20, nil, nil)
form.AddInputField("Last name", "", 20, nil, nil)
addressField := cview.NewInputField()
addressField.SetLabel("Address")
addressField.SetFieldWidth(30)
addressField.SetFieldNote("Your complete address")
form.AddFormItem(addressField)
form.AddPasswordField("Password", "", 10, '*', nil)
form.AddCheckBox("", "Age 18+", false, nil)
form.AddButton("Save", nil)
form.AddButton("Quit", func() {
app.Stop()
})
form.SetBorder(true)
form.SetTitle("Enter some data")
form.SetTitleAlign(cview.AlignLeft)
app.SetRoot(form, true)
if err := app.Run(); err != nil {
panic(err)
}
}

25
demos/frame/main.go

@ -8,15 +8,22 @@ import (
func main() {
app := cview.NewApplication()
frame := cview.NewFrame(cview.NewBox().SetBackgroundColor(tcell.ColorBlue.TrueColor())).
SetBorders(2, 2, 2, 2, 4, 4).
AddText("Header left", true, cview.AlignLeft, tcell.ColorWhite.TrueColor()).
AddText("Header middle", true, cview.AlignCenter, tcell.ColorWhite.TrueColor()).
AddText("Header right", true, cview.AlignRight, tcell.ColorWhite.TrueColor()).
AddText("Header second middle", true, cview.AlignCenter, tcell.ColorRed.TrueColor()).
AddText("Footer middle", false, cview.AlignCenter, tcell.ColorGreen.TrueColor()).
AddText("Footer second middle", false, cview.AlignCenter, tcell.ColorGreen.TrueColor())
if err := app.SetRoot(frame, true).EnableMouse(true).Run(); err != nil {
app.EnableMouse(true)
box := cview.NewBox()
box.SetBackgroundColor(tcell.ColorBlue.TrueColor())
frame := cview.NewFrame(box)
frame.SetBorders(2, 2, 2, 2, 4, 4)
frame.AddText("Header left", true, cview.AlignLeft, tcell.ColorWhite.TrueColor())
frame.AddText("Header middle", true, cview.AlignCenter, tcell.ColorWhite.TrueColor())
frame.AddText("Header right", true, cview.AlignRight, tcell.ColorWhite.TrueColor())
frame.AddText("Header second middle", true, cview.AlignCenter, tcell.ColorRed.TrueColor())
frame.AddText("Footer middle", false, cview.AlignCenter, tcell.ColorGreen.TrueColor())
frame.AddText("Footer second middle", false, cview.AlignCenter, tcell.ColorGreen.TrueColor())
app.SetRoot(frame, true)
if err := app.Run(); err != nil {
panic(err)
}
}

37
demos/grid/main.go

@ -6,33 +6,38 @@ import (
)
func main() {
app := cview.NewApplication()
app.EnableMouse(true)
newPrimitive := func(text string) cview.Primitive {
return cview.NewTextView().
SetTextAlign(cview.AlignCenter).
SetText(text)
tv := cview.NewTextView()
tv.SetTextAlign(cview.AlignCenter)
tv.SetText(text)
return tv
}
menu := newPrimitive("Menu")
main := newPrimitive("Main content")
sideBar := newPrimitive("Side Bar")
grid := cview.NewGrid().
SetRows(3, 0, 3).
SetColumns(30, 0, 30).
SetBorders(true).
AddItem(newPrimitive("Header"), 0, 0, 1, 3, 0, 0, false).
AddItem(newPrimitive("Footer"), 2, 0, 1, 3, 0, 0, false)
grid := cview.NewGrid()
grid.SetRows(3, 0, 3)
grid.SetColumns(30, 0, 30)
grid.SetBorders(true)
grid.AddItem(newPrimitive("Header"), 0, 0, 1, 3, 0, 0, false)
grid.AddItem(newPrimitive("Footer"), 2, 0, 1, 3, 0, 0, false)
// Layout for screens narrower than 100 cells (menu and side bar are hidden).
grid.AddItem(menu, 0, 0, 0, 0, 0, 0, false).
AddItem(main, 1, 0, 1, 3, 0, 0, false).
AddItem(sideBar, 0, 0, 0, 0, 0, 0, false)
grid.AddItem(menu, 0, 0, 0, 0, 0, 0, false)
grid.AddItem(main, 1, 0, 1, 3, 0, 0, false)
grid.AddItem(sideBar, 0, 0, 0, 0, 0, 0, false)
// Layout for screens wider than 100 cells.
grid.AddItem(menu, 1, 0, 1, 1, 0, 100, false).
AddItem(main, 1, 1, 1, 1, 0, 100, false).
AddItem(sideBar, 1, 2, 1, 1, 0, 100, false)
grid.AddItem(menu, 1, 0, 1, 1, 0, 100, false)
grid.AddItem(main, 1, 1, 1, 1, 0, 100, false)
grid.AddItem(sideBar, 1, 2, 1, 1, 0, 100, false)
if err := cview.NewApplication().SetRoot(grid, true).EnableMouse(true).Run(); err != nil {
app.SetRoot(grid, true)
if err := app.Run(); err != nil {
panic(err)
}
}

20
demos/inputfield/autocomplete/main.go

File diff suppressed because one or more lines are too long

15
demos/inputfield/autocompleteasync/main.go

@ -17,12 +17,12 @@ type company struct {
func main() {
app := cview.NewApplication()
inputField := cview.NewInputField().
SetLabel("Enter a company name: ").
SetFieldWidth(30).
SetDoneFunc(func(key tcell.Key) {
app.Stop()
})
inputField := cview.NewInputField()
inputField.SetLabel("Enter a company name: ")
inputField.SetFieldWidth(30)
inputField.SetDoneFunc(func(key tcell.Key) {
app.Stop()
})
// Set up autocomplete function.
var mutex sync.RWMutex
@ -75,7 +75,8 @@ func main() {
return nil
})
if err := app.SetRoot(inputField, true).Run(); err != nil {
app.SetRoot(inputField, true)
if err := app.Run(); err != nil {
panic(err)
}
}

22
demos/inputfield/simple/main.go

@ -8,15 +8,19 @@ import (
func main() {
app := cview.NewApplication()
inputField := cview.NewInputField().
SetLabel("Enter a number: ").
SetPlaceholder("E.g. 1234").
SetFieldWidth(10).
SetAcceptanceFunc(cview.InputFieldInteger).
SetDoneFunc(func(key tcell.Key) {
app.Stop()
})
if err := app.SetRoot(inputField, true).EnableMouse(true).Run(); err != nil {
app.EnableMouse(true)
inputField := cview.NewInputField()
inputField.SetLabel("Enter a number: ")
inputField.SetPlaceholder("E.g. 1234")
inputField.SetFieldWidth(10)
inputField.SetAcceptanceFunc(cview.InputFieldInteger)
inputField.SetDoneFunc(func(key tcell.Key) {
app.Stop()
})
app.SetRoot(inputField, true)
if err := app.Run(); err != nil {
panic(err)
}
}

30
demos/list/main.go

@ -2,23 +2,32 @@
package main
import (
"fmt"
"gitlab.com/tslocum/cview"
)
func main() {
app := cview.NewApplication()
app.EnableMouse(true)
list := cview.NewList()
reset := func() {
list.
Clear().
AddItem(cview.NewListItem("List item 1").SetSecondaryText("Some explanatory text").SetShortcut('a')).
AddItem(cview.NewListItem("List item 2").SetSecondaryText("Some explanatory text").SetShortcut('b')).
AddItem(cview.NewListItem("List item 3").SetSecondaryText("Some explanatory text").SetShortcut('c')).
AddItem(cview.NewListItem("List item 4").SetSecondaryText("Some explanatory text").SetShortcut('d')).
AddItem(cview.NewListItem("Quit").SetSecondaryText("Press to exit").SetShortcut('q').SetSelectedFunc(func() {
app.Stop()
}))
list.Clear()
for i := 0; i < 4; i++ {
item := cview.NewListItem(fmt.Sprintf("List item %d", i+1))
item.SetSecondaryText("Some explanatory text")
item.SetShortcut(rune('a' + i))
list.AddItem(item)
}
quitItem := cview.NewListItem("Quit")
quitItem.SetSecondaryText("Press to exit")
quitItem.SetShortcut('q')
quitItem.SetSelectedFunc(func() {
app.Stop()
})
list.AddItem(quitItem)
list.ContextMenuList().SetItemEnabled(3, false)
}
@ -52,7 +61,8 @@ func main() {
})
reset()
if err := app.SetRoot(list, true).EnableMouse(true).Run(); err != nil {
app.SetRoot(list, true)
if err := app.Run(); err != nil {
panic(err)
}
}

22
demos/modal/main.go

@ -7,15 +7,19 @@ import (
func main() {
app := cview.NewApplication()
modal := cview.NewModal().
SetText("Do you want to quit the application?").
AddButtons([]string{"Quit", "Cancel"}).
SetDoneFunc(func(buttonIndex int, buttonLabel string) {
if buttonLabel == "Quit" {
app.Stop()
}
})
if err := app.SetRoot(modal, false).EnableMouse(true).Run(); err != nil {
app.EnableMouse(true)
modal := cview.NewModal()
modal.SetText("Do you want to quit the application?")
modal.AddButtons([]string{"Quit", "Cancel"})
modal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
if buttonLabel == "Quit" {
app.Stop()
}
})
app.SetRoot(modal, false)
if err := app.Run(); err != nil {
panic(err)
}
}

31
demos/pages/main.go

@ -11,25 +11,28 @@ const pageCount = 5
func main() {
app := cview.NewApplication()
app.EnableMouse(true)
pages := cview.NewPages()
for page := 0; page < pageCount; page++ {
func(page int) {
pages.AddPage(fmt.Sprintf("page-%d", page),
cview.NewModal().
SetText(fmt.Sprintf("This is page %d. Choose where to go next.", page+1)).
AddButtons([]string{"Next", "Quit"}).
SetDoneFunc(func(buttonIndex int, buttonLabel string) {
if buttonIndex == 0 {
pages.SwitchToPage(fmt.Sprintf("page-%d", (page+1)%pageCount))
} else {
app.Stop()
}
}),
false,
page == 0)
modal := cview.NewModal()
modal.SetText(fmt.Sprintf("This is page %d. Choose where to go next.", page+1))
modal.AddButtons([]string{"Next", "Quit"})
modal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
if buttonIndex == 0 {
pages.SwitchToPage(fmt.Sprintf("page-%d", (page+1)%pageCount))
} else {
app.Stop()
}
})
pages.AddPage(fmt.Sprintf("page-%d", page), modal, false, page == 0)
}(page)
}
if err := app.SetRoot(pages, true).EnableMouse(true).Run(); err != nil {
app.SetRoot(pages, true)
if err := app.Run(); err != nil {
panic(err)
}
}

20
demos/presentation/center.go

@ -5,12 +5,16 @@ import "gitlab.com/tslocum/cview"
// 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 cview.Primitive) cview.Primitive {
return cview.NewFlex().
AddItem(cview.NewBox(), 0, 1, false).
AddItem(cview.NewFlex().
SetDirection(cview.FlexRow).
AddItem(cview.NewBox(), 0, 1, false).
AddItem(p, height, 1, true).
AddItem(cview.NewBox(), 0, 1, false), width, 1, true).
AddItem(cview.NewBox(), 0, 1, false)
subFlex := cview.NewFlex()
subFlex.SetDirection(cview.FlexRow)
subFlex.AddItem(cview.NewBox(), 0, 1, false)
subFlex.AddItem(p, height, 1, true)
subFlex.AddItem(cview.NewBox(), 0, 1, false)
flex := cview.NewFlex()
flex.AddItem(cview.NewBox(), 0, 1, false)
flex.AddItem(subFlex, width, 1, true)
flex.AddItem(cview.NewBox(), 0, 1, false)
return flex
}

13
demos/presentation/code.go

@ -13,13 +13,14 @@ const codeWidth = 56
// size) on the left side and its source code on the right side.
func Code(p cview.Primitive, width, height int, code string) cview.Primitive {