...

Source file src/github.com/rivo/tview/checkbox.go

Documentation: github.com/rivo/tview

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

View as plain text