checkbox.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. package tview
  2. import (
  3. "strings"
  4. "github.com/gdamore/tcell/v2"
  5. "github.com/rivo/uniseg"
  6. )
  7. // Checkbox implements a simple box for boolean values which can be checked and
  8. // unchecked.
  9. //
  10. // See https://github.com/rivo/tview/wiki/Checkbox for an example.
  11. type Checkbox struct {
  12. *Box
  13. // Whether or not this checkbox is disabled/read-only.
  14. disabled bool
  15. // Whether or not this box is checked.
  16. checked bool
  17. // The text to be displayed before the input area.
  18. label string
  19. // The screen width of the label area. A value of 0 means use the width of
  20. // the label text.
  21. labelWidth int
  22. // The label color.
  23. labelColor tcell.Color
  24. // The background color of the input area.
  25. fieldBackgroundColor tcell.Color
  26. // The text color of the input area.
  27. fieldTextColor tcell.Color
  28. // The string use to display a checked box.
  29. checkedString string
  30. // An optional function which is called when the user changes the checked
  31. // state of this checkbox.
  32. changed func(checked bool)
  33. // An optional function which is called when the user indicated that they
  34. // are done entering text. The key which was pressed is provided (tab,
  35. // shift-tab, or escape).
  36. done func(tcell.Key)
  37. // A callback function set by the Form class and called when the user leaves
  38. // this form item.
  39. finished func(tcell.Key)
  40. }
  41. // NewCheckbox returns a new input field.
  42. func NewCheckbox() *Checkbox {
  43. return &Checkbox{
  44. Box: NewBox(),
  45. labelColor: Styles.SecondaryTextColor,
  46. fieldBackgroundColor: Styles.ContrastBackgroundColor,
  47. fieldTextColor: Styles.PrimaryTextColor,
  48. checkedString: "X",
  49. }
  50. }
  51. // SetChecked sets the state of the checkbox. This also triggers the "changed"
  52. // callback if the state changes with this call.
  53. func (c *Checkbox) SetChecked(checked bool) *Checkbox {
  54. if c.checked != checked {
  55. if c.changed != nil {
  56. c.changed(checked)
  57. }
  58. c.checked = checked
  59. }
  60. return c
  61. }
  62. // IsChecked returns whether or not the box is checked.
  63. func (c *Checkbox) IsChecked() bool {
  64. return c.checked
  65. }
  66. // SetLabel sets the text to be displayed before the input area.
  67. func (c *Checkbox) SetLabel(label string) *Checkbox {
  68. c.label = label
  69. return c
  70. }
  71. // GetLabel returns the text to be displayed before the input area.
  72. func (c *Checkbox) GetLabel() string {
  73. return c.label
  74. }
  75. // SetLabelWidth sets the screen width of the label. A value of 0 will cause the
  76. // primitive to use the width of the label string.
  77. func (c *Checkbox) SetLabelWidth(width int) *Checkbox {
  78. c.labelWidth = width
  79. return c
  80. }
  81. // SetLabelColor sets the color of the label.
  82. func (c *Checkbox) SetLabelColor(color tcell.Color) *Checkbox {
  83. c.labelColor = color
  84. return c
  85. }
  86. // SetFieldBackgroundColor sets the background color of the input area.
  87. func (c *Checkbox) SetFieldBackgroundColor(color tcell.Color) *Checkbox {
  88. c.fieldBackgroundColor = color
  89. return c
  90. }
  91. // SetFieldTextColor sets the text color of the input area.
  92. func (c *Checkbox) SetFieldTextColor(color tcell.Color) *Checkbox {
  93. c.fieldTextColor = color
  94. return c
  95. }
  96. // SetCheckedString sets the string to be displayed when the checkbox is
  97. // checked (defaults to "X").
  98. func (c *Checkbox) SetCheckedString(checked string) *Checkbox {
  99. c.checkedString = checked
  100. return c
  101. }
  102. // SetFormAttributes sets attributes shared by all form items.
  103. func (c *Checkbox) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
  104. c.labelWidth = labelWidth
  105. c.labelColor = labelColor
  106. c.backgroundColor = bgColor
  107. c.fieldTextColor = fieldTextColor
  108. c.fieldBackgroundColor = fieldBgColor
  109. return c
  110. }
  111. // GetFieldWidth returns this primitive's field width.
  112. func (c *Checkbox) GetFieldWidth() int {
  113. return 1
  114. }
  115. // GetFieldHeight returns this primitive's field height.
  116. func (c *Checkbox) GetFieldHeight() int {
  117. return 1
  118. }
  119. // SetDisabled sets whether or not the item is disabled / read-only.
  120. func (c *Checkbox) SetDisabled(disabled bool) FormItem {
  121. c.disabled = disabled
  122. if c.finished != nil {
  123. c.finished(-1)
  124. }
  125. return c
  126. }
  127. // SetChangedFunc sets a handler which is called when the checked state of this
  128. // checkbox was changed. The handler function receives the new state.
  129. func (c *Checkbox) SetChangedFunc(handler func(checked bool)) *Checkbox {
  130. c.changed = handler
  131. return c
  132. }
  133. // SetDoneFunc sets a handler which is called when the user is done using the
  134. // checkbox. The callback function is provided with the key that was pressed,
  135. // which is one of the following:
  136. //
  137. // - KeyEscape: Abort text input.
  138. // - KeyTab: Move to the next field.
  139. // - KeyBacktab: Move to the previous field.
  140. func (c *Checkbox) SetDoneFunc(handler func(key tcell.Key)) *Checkbox {
  141. c.done = handler
  142. return c
  143. }
  144. // SetFinishedFunc sets a callback invoked when the user leaves this form item.
  145. func (c *Checkbox) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
  146. c.finished = handler
  147. return c
  148. }
  149. // Focus is called when this primitive receives focus.
  150. func (c *Checkbox) Focus(delegate func(p Primitive)) {
  151. // If we're part of a form and this item is disabled, there's nothing the
  152. // user can do here so we're finished.
  153. if c.finished != nil && c.disabled {
  154. c.finished(-1)
  155. return
  156. }
  157. c.Box.Focus(delegate)
  158. }
  159. // Draw draws this primitive onto the screen.
  160. func (c *Checkbox) Draw(screen tcell.Screen) {
  161. c.Box.DrawForSubclass(screen, c)
  162. // Prepare
  163. x, y, width, height := c.GetInnerRect()
  164. rightLimit := x + width
  165. if height < 1 || rightLimit <= x {
  166. return
  167. }
  168. // Draw label.
  169. if c.labelWidth > 0 {
  170. labelWidth := c.labelWidth
  171. if labelWidth > width {
  172. labelWidth = width
  173. }
  174. Print(screen, c.label, x, y, labelWidth, AlignLeft, c.labelColor)
  175. x += labelWidth
  176. } else {
  177. _, drawnWidth := Print(screen, c.label, x, y, width, AlignLeft, c.labelColor)
  178. x += drawnWidth
  179. }
  180. // Draw checkbox.
  181. fieldBackgroundColor := c.fieldBackgroundColor
  182. if c.disabled {
  183. fieldBackgroundColor = c.backgroundColor
  184. }
  185. fieldStyle := tcell.StyleDefault.Background(fieldBackgroundColor).Foreground(c.fieldTextColor)
  186. if c.HasFocus() {
  187. fieldStyle = fieldStyle.Background(c.fieldTextColor).Foreground(fieldBackgroundColor)
  188. }
  189. checkboxWidth := uniseg.StringWidth(c.checkedString)
  190. checkedString := c.checkedString
  191. if !c.checked {
  192. checkedString = strings.Repeat(" ", checkboxWidth)
  193. }
  194. printWithStyle(screen, checkedString, x, y, 0, checkboxWidth, AlignLeft, fieldStyle, false)
  195. }
  196. // InputHandler returns the handler for this primitive.
  197. func (c *Checkbox) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
  198. return c.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
  199. if c.disabled {
  200. return
  201. }
  202. // Process key event.
  203. switch key := event.Key(); key {
  204. case tcell.KeyRune, tcell.KeyEnter: // Check.
  205. if key == tcell.KeyRune && event.Rune() != ' ' {
  206. break
  207. }
  208. c.checked = !c.checked
  209. if c.changed != nil {
  210. c.changed(c.checked)
  211. }
  212. case tcell.KeyTab, tcell.KeyBacktab, tcell.KeyEscape: // We're done.
  213. if c.done != nil {
  214. c.done(key)
  215. }
  216. if c.finished != nil {
  217. c.finished(key)
  218. }
  219. }
  220. })
  221. }
  222. // MouseHandler returns the mouse handler for this primitive.
  223. func (c *Checkbox) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
  224. return c.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
  225. if c.disabled {
  226. return false, nil
  227. }
  228. x, y := event.Position()
  229. _, rectY, _, _ := c.GetInnerRect()
  230. if !c.InRect(x, y) {
  231. return false, nil
  232. }
  233. // Process mouse event.
  234. if y == rectY {
  235. if action == MouseLeftDown {
  236. setFocus(c)
  237. consumed = true
  238. } else if action == MouseLeftClick {
  239. c.checked = !c.checked
  240. if c.changed != nil {
  241. c.changed(c.checked)
  242. }
  243. consumed = true
  244. }
  245. }
  246. return
  247. })
  248. }