checkbox.go (9695B)
1 package tview 2 3 import ( 4 "github.com/gdamore/tcell/v2" 5 ) 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 14 // Whether or not this checkbox is disabled/read-only. 15 disabled bool 16 17 // Whether or not this box is checked. 18 checked bool 19 20 // The text to be displayed before the input area. 21 label string 22 23 // The screen width of the label area. A value of 0 means use the width of 24 // the label text. 25 labelWidth int 26 27 // The label style. 28 labelStyle tcell.Style 29 30 // The style of the unchecked checkbox. 31 uncheckedStyle tcell.Style 32 33 // The style of the checked checkbox. 34 checkedStyle tcell.Style 35 36 // Teh style of the checkbox when it is currently focused. 37 focusStyle tcell.Style 38 39 // The string used to display an unchecked box. 40 uncheckedString string 41 42 // The string used to display a checked box. 43 checkedString string 44 45 // An optional function which is called when the user changes the checked 46 // state of this checkbox. 47 changed func(checked bool) 48 49 // An optional function which is called when the user indicated that they 50 // are done entering text. The key which was pressed is provided (tab, 51 // shift-tab, or escape). 52 done func(tcell.Key) 53 54 // A callback function set by the Form class and called when the user leaves 55 // this form item. 56 finished func(tcell.Key) 57 } 58 59 // NewCheckbox returns a new input field. 60 func NewCheckbox() *Checkbox { 61 return &Checkbox{ 62 Box: NewBox(), 63 labelStyle: tcell.StyleDefault.Foreground(Styles.SecondaryTextColor), 64 uncheckedStyle: tcell.StyleDefault.Background(Styles.ContrastBackgroundColor).Foreground(Styles.PrimaryTextColor), 65 checkedStyle: tcell.StyleDefault.Background(Styles.ContrastBackgroundColor).Foreground(Styles.PrimaryTextColor), 66 focusStyle: tcell.StyleDefault.Background(Styles.PrimaryTextColor).Foreground(Styles.ContrastBackgroundColor), 67 uncheckedString: " ", 68 checkedString: "X", 69 } 70 } 71 72 // SetChecked sets the state of the checkbox. This also triggers the "changed" 73 // callback if the state changes with this call. 74 func (c *Checkbox) SetChecked(checked bool) *Checkbox { 75 if c.checked != checked { 76 if c.changed != nil { 77 c.changed(checked) 78 } 79 c.checked = checked 80 } 81 return c 82 } 83 84 // IsChecked returns whether or not the box is checked. 85 func (c *Checkbox) IsChecked() bool { 86 return c.checked 87 } 88 89 // SetLabel sets the text to be displayed before the input area. 90 func (c *Checkbox) SetLabel(label string) *Checkbox { 91 c.label = label 92 return c 93 } 94 95 // GetLabel returns the text to be displayed before the input area. 96 func (c *Checkbox) GetLabel() string { 97 return c.label 98 } 99 100 // SetLabelWidth sets the screen width of the label. A value of 0 will cause the 101 // primitive to use the width of the label string. 102 func (c *Checkbox) SetLabelWidth(width int) *Checkbox { 103 c.labelWidth = width 104 return c 105 } 106 107 // SetLabelColor sets the color of the label. 108 func (c *Checkbox) SetLabelColor(color tcell.Color) *Checkbox { 109 c.labelStyle = c.labelStyle.Foreground(color) 110 return c 111 } 112 113 // SetLabelStyle sets the style of the label. 114 func (c *Checkbox) SetLabelStyle(style tcell.Style) *Checkbox { 115 c.labelStyle = style 116 return c 117 } 118 119 // SetFieldBackgroundColor sets the background color of the input area. 120 func (c *Checkbox) SetFieldBackgroundColor(color tcell.Color) *Checkbox { 121 c.uncheckedStyle = c.uncheckedStyle.Background(color) 122 c.checkedStyle = c.checkedStyle.Background(color) 123 c.focusStyle = c.focusStyle.Foreground(color) 124 return c 125 } 126 127 // SetFieldTextColor sets the text color of the input area. 128 func (c *Checkbox) SetFieldTextColor(color tcell.Color) *Checkbox { 129 c.uncheckedStyle = c.uncheckedStyle.Foreground(color) 130 c.checkedStyle = c.checkedStyle.Foreground(color) 131 c.focusStyle = c.focusStyle.Background(color) 132 return c 133 } 134 135 // SetUncheckedStyle sets the style of the unchecked checkbox. 136 func (c *Checkbox) SetUncheckedStyle(style tcell.Style) *Checkbox { 137 c.uncheckedStyle = style 138 return c 139 } 140 141 // SetCheckedStyle sets the style of the checked checkbox. 142 func (c *Checkbox) SetCheckedStyle(style tcell.Style) *Checkbox { 143 c.checkedStyle = style 144 return c 145 } 146 147 // SetActivatedStyle sets the style of the checkbox when it is currently 148 // focused. 149 func (c *Checkbox) SetActivatedStyle(style tcell.Style) *Checkbox { 150 c.focusStyle = style 151 return c 152 } 153 154 // SetCheckedString sets the string to be displayed when the checkbox is 155 // checked (defaults to "X"). The string may contain color tags (consider 156 // adapting the checkbox's various styles accordingly). See [Escape] in 157 // case you want to display square brackets. 158 func (c *Checkbox) SetCheckedString(checked string) *Checkbox { 159 c.checkedString = checked 160 return c 161 } 162 163 // SetUncheckedString sets the string to be displayed when the checkbox is 164 // not checked (defaults to the empty space " "). The string may contain color 165 // tags (consider adapting the checkbox's various styles accordingly). See 166 // [Escape] in case you want to display square brackets. 167 func (c *Checkbox) SetUncheckedString(unchecked string) *Checkbox { 168 c.uncheckedString = unchecked 169 return c 170 } 171 172 // SetFormAttributes sets attributes shared by all form items. 173 func (c *Checkbox) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem { 174 c.labelWidth = labelWidth 175 c.SetLabelColor(labelColor) 176 c.backgroundColor = bgColor 177 c.SetFieldTextColor(fieldTextColor) 178 c.SetFieldBackgroundColor(fieldBgColor) 179 return c 180 } 181 182 // GetFieldWidth returns this primitive's field width. 183 func (c *Checkbox) GetFieldWidth() int { 184 return 1 185 } 186 187 // GetFieldHeight returns this primitive's field height. 188 func (c *Checkbox) GetFieldHeight() int { 189 return 1 190 } 191 192 // SetDisabled sets whether or not the item is disabled / read-only. 193 func (c *Checkbox) SetDisabled(disabled bool) FormItem { 194 c.disabled = disabled 195 if c.finished != nil { 196 c.finished(-1) 197 } 198 return c 199 } 200 201 // SetChangedFunc sets a handler which is called when the checked state of this 202 // checkbox was changed. The handler function receives the new state. 203 func (c *Checkbox) SetChangedFunc(handler func(checked bool)) *Checkbox { 204 c.changed = handler 205 return c 206 } 207 208 // SetDoneFunc sets a handler which is called when the user is done using the 209 // checkbox. The callback function is provided with the key that was pressed, 210 // which is one of the following: 211 // 212 // - KeyEscape: Abort text input. 213 // - KeyTab: Move to the next field. 214 // - KeyBacktab: Move to the previous field. 215 func (c *Checkbox) SetDoneFunc(handler func(key tcell.Key)) *Checkbox { 216 c.done = handler 217 return c 218 } 219 220 // SetFinishedFunc sets a callback invoked when the user leaves this form item. 221 func (c *Checkbox) SetFinishedFunc(handler func(key tcell.Key)) FormItem { 222 c.finished = handler 223 return c 224 } 225 226 // Focus is called when this primitive receives focus. 227 func (c *Checkbox) Focus(delegate func(p Primitive)) { 228 // If we're part of a form and this item is disabled, there's nothing the 229 // user can do here so we're finished. 230 if c.finished != nil && c.disabled { 231 c.finished(-1) 232 return 233 } 234 235 c.Box.Focus(delegate) 236 } 237 238 // Draw draws this primitive onto the screen. 239 func (c *Checkbox) Draw(screen tcell.Screen) { 240 c.Box.DrawForSubclass(screen, c) 241 242 // Prepare 243 x, y, width, height := c.GetInnerRect() 244 rightLimit := x + width 245 if height < 1 || rightLimit <= x { 246 return 247 } 248 249 // Draw label. 250 _, labelBg, _ := c.labelStyle.Decompose() 251 if c.labelWidth > 0 { 252 labelWidth := c.labelWidth 253 if labelWidth > width { 254 labelWidth = width 255 } 256 printWithStyle(screen, c.label, x, y, 0, labelWidth, AlignLeft, c.labelStyle, labelBg == tcell.ColorDefault) 257 x += labelWidth 258 width -= labelWidth 259 } else { 260 _, _, drawnWidth := printWithStyle(screen, c.label, x, y, 0, width, AlignLeft, c.labelStyle, labelBg == tcell.ColorDefault) 261 x += drawnWidth 262 width -= drawnWidth 263 } 264 265 // Draw checkbox. 266 str := c.uncheckedString 267 style := c.uncheckedStyle 268 if c.checked { 269 str = c.checkedString 270 style = c.checkedStyle 271 } 272 if c.disabled { 273 style = style.Background(c.backgroundColor) 274 } 275 if c.HasFocus() { 276 style = c.focusStyle 277 } 278 printWithStyle(screen, str, x, y, 0, width, AlignLeft, style, c.disabled) 279 } 280 281 // InputHandler returns the handler for this primitive. 282 func (c *Checkbox) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { 283 return c.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { 284 if c.disabled { 285 return 286 } 287 288 // Process key event. 289 switch key := event.Key(); key { 290 case tcell.KeyRune, tcell.KeyEnter: // Check. 291 if key == tcell.KeyRune && event.Rune() != ' ' { 292 break 293 } 294 c.checked = !c.checked 295 if c.changed != nil { 296 c.changed(c.checked) 297 } 298 case tcell.KeyTab, tcell.KeyBacktab, tcell.KeyEscape: // We're done. 299 if c.done != nil { 300 c.done(key) 301 } 302 if c.finished != nil { 303 c.finished(key) 304 } 305 } 306 }) 307 } 308 309 // MouseHandler returns the mouse handler for this primitive. 310 func (c *Checkbox) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { 311 return c.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { 312 if c.disabled { 313 return false, nil 314 } 315 316 x, y := event.Position() 317 _, rectY, _, _ := c.GetInnerRect() 318 if !c.InRect(x, y) { 319 return false, nil 320 } 321 322 // Process mouse event. 323 if y == rectY { 324 if action == MouseLeftDown { 325 setFocus(c) 326 consumed = true 327 } else if action == MouseLeftClick { 328 c.checked = !c.checked 329 if c.changed != nil { 330 c.changed(c.checked) 331 } 332 consumed = true 333 } 334 } 335 336 return 337 }) 338 }