...

Source file src/code.rocketnine.space/tslocum/cview/checkbox.go

Documentation: code.rocketnine.space/tslocum/cview

     1  package cview
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/gdamore/tcell/v2"
     7  )
     8  
     9  // CheckBox implements a simple box for boolean values which can be checked and
    10  // unchecked.
    11  type CheckBox struct {
    12  	*Box
    13  
    14  	// Whether or not this box is checked.
    15  	checked bool
    16  
    17  	// The text to be displayed before the checkbox.
    18  	label []byte
    19  
    20  	// The text to be displayed after the checkbox.
    21  	message []byte
    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 color.
    28  	labelColor tcell.Color
    29  
    30  	// The label color when focused.
    31  	labelColorFocused tcell.Color
    32  
    33  	// The background color of the input area.
    34  	fieldBackgroundColor tcell.Color
    35  
    36  	// The background color of the input area when focused.
    37  	fieldBackgroundColorFocused tcell.Color
    38  
    39  	// The text color of the input area.
    40  	fieldTextColor tcell.Color
    41  
    42  	// The text color of the input area when focused.
    43  	fieldTextColorFocused tcell.Color
    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  	// The rune to show when the checkbox is checked
    59  	checkedRune rune
    60  
    61  	// An optional rune to show within the checkbox when it is focused
    62  	cursorRune rune
    63  
    64  	sync.RWMutex
    65  }
    66  
    67  // NewCheckBox returns a new input field.
    68  func NewCheckBox() *CheckBox {
    69  	return &CheckBox{
    70  		Box:                         NewBox(),
    71  		labelColor:                  Styles.SecondaryTextColor,
    72  		fieldBackgroundColor:        Styles.MoreContrastBackgroundColor,
    73  		fieldBackgroundColorFocused: Styles.ContrastBackgroundColor,
    74  		fieldTextColor:              Styles.PrimaryTextColor,
    75  		checkedRune:                 Styles.CheckBoxCheckedRune,
    76  		cursorRune:                  Styles.CheckBoxCursorRune,
    77  		labelColorFocused:           ColorUnset,
    78  		fieldTextColorFocused:       ColorUnset,
    79  	}
    80  }
    81  
    82  // SetChecked sets the state of the checkbox.
    83  func (c *CheckBox) SetChecked(checked bool) {
    84  	c.Lock()
    85  	defer c.Unlock()
    86  
    87  	c.checked = checked
    88  }
    89  
    90  // SetCheckedRune sets the rune to show when the checkbox is checked.
    91  func (c *CheckBox) SetCheckedRune(rune rune) {
    92  	c.Lock()
    93  	defer c.Unlock()
    94  
    95  	c.checkedRune = rune
    96  }
    97  
    98  // SetCursorRune sets the rune to show within the checkbox when it is focused.
    99  func (c *CheckBox) SetCursorRune(rune rune) {
   100  	c.Lock()
   101  	defer c.Unlock()
   102  
   103  	c.cursorRune = rune
   104  }
   105  
   106  // IsChecked returns whether or not the box is checked.
   107  func (c *CheckBox) IsChecked() bool {
   108  	c.RLock()
   109  	defer c.RUnlock()
   110  
   111  	return c.checked
   112  }
   113  
   114  // SetLabel sets the text to be displayed before the input area.
   115  func (c *CheckBox) SetLabel(label string) {
   116  	c.Lock()
   117  	defer c.Unlock()
   118  
   119  	c.label = []byte(label)
   120  }
   121  
   122  // GetLabel returns the text to be displayed before the input area.
   123  func (c *CheckBox) GetLabel() string {
   124  	c.RLock()
   125  	defer c.RUnlock()
   126  
   127  	return string(c.label)
   128  }
   129  
   130  // SetMessage sets the text to be displayed after the checkbox
   131  func (c *CheckBox) SetMessage(message string) {
   132  	c.Lock()
   133  	defer c.Unlock()
   134  
   135  	c.message = []byte(message)
   136  }
   137  
   138  // GetMessage returns the text to be displayed after the checkbox
   139  func (c *CheckBox) GetMessage() string {
   140  	c.RLock()
   141  	defer c.RUnlock()
   142  
   143  	return string(c.message)
   144  }
   145  
   146  // SetLabelWidth sets the screen width of the label. A value of 0 will cause the
   147  // primitive to use the width of the label string.
   148  func (c *CheckBox) SetLabelWidth(width int) {
   149  	c.Lock()
   150  	defer c.Unlock()
   151  
   152  	c.labelWidth = width
   153  }
   154  
   155  // SetLabelColor sets the color of the label.
   156  func (c *CheckBox) SetLabelColor(color tcell.Color) {
   157  	c.Lock()
   158  	defer c.Unlock()
   159  
   160  	c.labelColor = color
   161  }
   162  
   163  // SetLabelColorFocused sets the color of the label when focused.
   164  func (c *CheckBox) SetLabelColorFocused(color tcell.Color) {
   165  	c.Lock()
   166  	defer c.Unlock()
   167  
   168  	c.labelColorFocused = color
   169  }
   170  
   171  // SetFieldBackgroundColor sets the background color of the input area.
   172  func (c *CheckBox) SetFieldBackgroundColor(color tcell.Color) {
   173  	c.Lock()
   174  	defer c.Unlock()
   175  
   176  	c.fieldBackgroundColor = color
   177  }
   178  
   179  // SetFieldBackgroundColorFocused sets the background color of the input area when focused.
   180  func (c *CheckBox) SetFieldBackgroundColorFocused(color tcell.Color) {
   181  	c.Lock()
   182  	defer c.Unlock()
   183  
   184  	c.fieldBackgroundColorFocused = color
   185  }
   186  
   187  // SetFieldTextColor sets the text color of the input area.
   188  func (c *CheckBox) SetFieldTextColor(color tcell.Color) {
   189  	c.Lock()
   190  	defer c.Unlock()
   191  
   192  	c.fieldTextColor = color
   193  }
   194  
   195  // SetFieldTextColorFocused sets the text color of the input area when focused.
   196  func (c *CheckBox) SetFieldTextColorFocused(color tcell.Color) {
   197  	c.Lock()
   198  	defer c.Unlock()
   199  
   200  	c.fieldTextColorFocused = color
   201  }
   202  
   203  // GetFieldHeight returns the height of the field.
   204  func (c *CheckBox) GetFieldHeight() int {
   205  	return 1
   206  }
   207  
   208  // GetFieldWidth returns this primitive's field width.
   209  func (c *CheckBox) GetFieldWidth() int {
   210  	c.RLock()
   211  	defer c.RUnlock()
   212  
   213  	if len(c.message) == 0 {
   214  		return 1
   215  	}
   216  
   217  	return 2 + len(c.message)
   218  }
   219  
   220  // SetChangedFunc sets a handler which is called when the checked state of this
   221  // checkbox was changed by the user. The handler function receives the new
   222  // state.
   223  func (c *CheckBox) SetChangedFunc(handler func(checked bool)) {
   224  	c.Lock()
   225  	defer c.Unlock()
   226  
   227  	c.changed = handler
   228  }
   229  
   230  // SetDoneFunc sets a handler which is called when the user is done using the
   231  // checkbox. The callback function is provided with the key that was pressed,
   232  // which is one of the following:
   233  //
   234  //   - KeyEscape: Abort text input.
   235  //   - KeyTab: Move to the next field.
   236  //   - KeyBacktab: Move to the previous field.
   237  func (c *CheckBox) SetDoneFunc(handler func(key tcell.Key)) {
   238  	c.Lock()
   239  	defer c.Unlock()
   240  
   241  	c.done = handler
   242  }
   243  
   244  // SetFinishedFunc sets a callback invoked when the user leaves this form item.
   245  func (c *CheckBox) SetFinishedFunc(handler func(key tcell.Key)) {
   246  	c.Lock()
   247  	defer c.Unlock()
   248  
   249  	c.finished = handler
   250  }
   251  
   252  // Draw draws this primitive onto the screen.
   253  func (c *CheckBox) Draw(screen tcell.Screen) {
   254  	if !c.GetVisible() {
   255  		return
   256  	}
   257  
   258  	c.Box.Draw(screen)
   259  
   260  	c.Lock()
   261  	defer c.Unlock()
   262  
   263  	hasFocus := c.GetFocusable().HasFocus()
   264  
   265  	// Select colors
   266  	labelColor := c.labelColor
   267  	fieldBackgroundColor := c.fieldBackgroundColor
   268  	fieldTextColor := c.fieldTextColor
   269  	if hasFocus {
   270  		if c.labelColorFocused != ColorUnset {
   271  			labelColor = c.labelColorFocused
   272  		}
   273  		if c.fieldBackgroundColorFocused != ColorUnset {
   274  			fieldBackgroundColor = c.fieldBackgroundColorFocused
   275  		}
   276  		if c.fieldTextColorFocused != ColorUnset {
   277  			fieldTextColor = c.fieldTextColorFocused
   278  		}
   279  	}
   280  
   281  	// Prepare
   282  	x, y, width, height := c.GetInnerRect()
   283  	rightLimit := x + width
   284  	if height < 1 || rightLimit <= x {
   285  		return
   286  	}
   287  
   288  	// Draw label.
   289  	if c.labelWidth > 0 {
   290  		labelWidth := c.labelWidth
   291  		if labelWidth > rightLimit-x {
   292  			labelWidth = rightLimit - x
   293  		}
   294  		Print(screen, c.label, x, y, labelWidth, AlignLeft, labelColor)
   295  		x += labelWidth
   296  	} else {
   297  		_, drawnWidth := Print(screen, c.label, x, y, rightLimit-x, AlignLeft, labelColor)
   298  		x += drawnWidth
   299  	}
   300  
   301  	// Draw checkbox.
   302  	fieldStyle := tcell.StyleDefault.Background(fieldBackgroundColor).Foreground(fieldTextColor)
   303  
   304  	checkedRune := c.checkedRune
   305  	if !c.checked {
   306  		checkedRune = ' '
   307  	}
   308  	rightRune := ' '
   309  	if c.cursorRune != 0 && hasFocus {
   310  		rightRune = c.cursorRune
   311  	}
   312  	screen.SetContent(x, y, ' ', nil, fieldStyle)
   313  	screen.SetContent(x+1, y, checkedRune, nil, fieldStyle)
   314  	screen.SetContent(x+2, y, rightRune, nil, fieldStyle)
   315  
   316  	if len(c.message) > 0 {
   317  		Print(screen, c.message, x+4, y, len(c.message), AlignLeft, labelColor)
   318  	}
   319  }
   320  
   321  // InputHandler returns the handler for this primitive.
   322  func (c *CheckBox) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
   323  	return c.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
   324  		if HitShortcut(event, Keys.Select, Keys.Select2) {
   325  			c.Lock()
   326  			c.checked = !c.checked
   327  			c.Unlock()
   328  			if c.changed != nil {
   329  				c.changed(c.checked)
   330  			}
   331  		} else if HitShortcut(event, Keys.Cancel, Keys.MovePreviousField, Keys.MoveNextField) {
   332  			if c.done != nil {
   333  				c.done(event.Key())
   334  			}
   335  			if c.finished != nil {
   336  				c.finished(event.Key())
   337  			}
   338  		}
   339  	})
   340  }
   341  
   342  // MouseHandler returns the mouse handler for this primitive.
   343  func (c *CheckBox) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
   344  	return c.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
   345  		x, y := event.Position()
   346  		_, rectY, _, _ := c.GetInnerRect()
   347  		if !c.InRect(x, y) {
   348  			return false, nil
   349  		}
   350  
   351  		// Process mouse event.
   352  		if action == MouseLeftClick && y == rectY {
   353  			setFocus(c)
   354  			c.checked = !c.checked
   355  			if c.changed != nil {
   356  				c.changed(c.checked)
   357  			}
   358  			consumed = true
   359  		}
   360  
   361  		return
   362  	})
   363  }
   364  

View as plain text