cview/checkbox.go

296 lines
7.0 KiB
Go
Raw Normal View History

package cview
2017-12-20 19:54:49 +00:00
import (
2020-03-25 14:32:57 +00:00
"sync"
2017-12-20 19:54:49 +00:00
"github.com/gdamore/tcell"
)
2020-04-26 23:55:45 +00:00
// CheckBox implements a simple box for boolean values which can be checked and
// unchecked.
2020-04-26 23:55:45 +00:00
type CheckBox struct {
2017-12-20 19:54:49 +00:00
*Box
// Whether or not this box is checked.
checked bool
// The text to be displayed before the checkbox.
2017-12-20 19:54:49 +00:00
label string
// The text to be displayed after the checkbox.
Add support for displaying text next to a checkbox When building forms the label field is typically quite short, just one or two words. For checkboxes it is often desirable to have a longer descriptive piece of text. This is not practical to use as a label and in many applications would more commonly be placed to the right of the checkbox. This adds support for a "SetMessage()" method which provides support for text adjacent to the checkbox As an example, this form shows one usage pattern, where the checkbox is used to require case sensitive matching of the author name query. In this case the checkbox label is an empty string, and the message text is used instead: ╔════════════ User filtering ════════════════════╗ ║ ║ ║ Age: ________ ║ ║ ║ ║ Author: ______________ ║ ║ ║ ║ X Case sensitive ║ ║ ║ ║ Apply Cancel ║ ╚════════════════════════════════════════════════╝ Another pattern is where there are a series of checkboxes, all related to the same attribute. Thus they have a common form label but different message text ╔════════════ Request filtering ═════════════════╗ ║ ║ ║ State: X Opened ║ ║ ║ ║ _ Closed ║ ║ ║ ║ _ Merged ║ ║ ║ ║ X Locked ║ ║ ║ ║ Apply Cancel ║ ╚════════════════════════════════════════════════╝ Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-11-14 10:48:40 +00:00
message string
// The screen width of the label area. A value of 0 means use the width of
// the label text.
labelWidth int
2017-12-20 19:54:49 +00:00
// The label color.
labelColor tcell.Color
// The background color of the input area.
fieldBackgroundColor tcell.Color
// The text color of the input area.
fieldTextColor tcell.Color
// An optional function which is called when the user changes the checked
// state of this checkbox.
changed func(checked bool)
// An optional function which is called when the user indicated that they
// are done entering text. The key which was pressed is provided (tab,
// shift-tab, or escape).
done func(tcell.Key)
// A callback function set by the Form class and called when the user leaves
// this form item.
finished func(tcell.Key)
2020-03-25 14:32:57 +00:00
sync.Mutex
2017-12-20 19:54:49 +00:00
}
2020-04-26 23:55:45 +00:00
// NewCheckBox returns a new input field.
func NewCheckBox() *CheckBox {
return &CheckBox{
2017-12-20 19:54:49 +00:00
Box: NewBox(),
labelColor: Styles.SecondaryTextColor,
fieldBackgroundColor: Styles.ContrastBackgroundColor,
fieldTextColor: Styles.PrimaryTextColor,
2017-12-20 19:54:49 +00:00
}
}
// SetChecked sets the state of the checkbox.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) SetChecked(checked bool) *CheckBox {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
2017-12-20 19:54:49 +00:00
c.checked = checked
return c
}
// IsChecked returns whether or not the box is checked.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) IsChecked() bool {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
return c.checked
}
2017-12-20 19:54:49 +00:00
// SetLabel sets the text to be displayed before the input area.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) SetLabel(label string) *CheckBox {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
2017-12-20 19:54:49 +00:00
c.label = label
return c
}
// GetLabel returns the text to be displayed before the input area.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) GetLabel() string {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
2017-12-20 19:54:49 +00:00
return c.label
}
Add support for displaying text next to a checkbox When building forms the label field is typically quite short, just one or two words. For checkboxes it is often desirable to have a longer descriptive piece of text. This is not practical to use as a label and in many applications would more commonly be placed to the right of the checkbox. This adds support for a "SetMessage()" method which provides support for text adjacent to the checkbox As an example, this form shows one usage pattern, where the checkbox is used to require case sensitive matching of the author name query. In this case the checkbox label is an empty string, and the message text is used instead: ╔════════════ User filtering ════════════════════╗ ║ ║ ║ Age: ________ ║ ║ ║ ║ Author: ______________ ║ ║ ║ ║ X Case sensitive ║ ║ ║ ║ Apply Cancel ║ ╚════════════════════════════════════════════════╝ Another pattern is where there are a series of checkboxes, all related to the same attribute. Thus they have a common form label but different message text ╔════════════ Request filtering ═════════════════╗ ║ ║ ║ State: X Opened ║ ║ ║ ║ _ Closed ║ ║ ║ ║ _ Merged ║ ║ ║ ║ X Locked ║ ║ ║ ║ Apply Cancel ║ ╚════════════════════════════════════════════════╝ Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-11-14 10:48:40 +00:00
// SetMessage sets the text to be displayed after the checkbox
2020-04-26 23:55:45 +00:00
func (c *CheckBox) SetMessage(message string) *CheckBox {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
Add support for displaying text next to a checkbox When building forms the label field is typically quite short, just one or two words. For checkboxes it is often desirable to have a longer descriptive piece of text. This is not practical to use as a label and in many applications would more commonly be placed to the right of the checkbox. This adds support for a "SetMessage()" method which provides support for text adjacent to the checkbox As an example, this form shows one usage pattern, where the checkbox is used to require case sensitive matching of the author name query. In this case the checkbox label is an empty string, and the message text is used instead: ╔════════════ User filtering ════════════════════╗ ║ ║ ║ Age: ________ ║ ║ ║ ║ Author: ______________ ║ ║ ║ ║ X Case sensitive ║ ║ ║ ║ Apply Cancel ║ ╚════════════════════════════════════════════════╝ Another pattern is where there are a series of checkboxes, all related to the same attribute. Thus they have a common form label but different message text ╔════════════ Request filtering ═════════════════╗ ║ ║ ║ State: X Opened ║ ║ ║ ║ _ Closed ║ ║ ║ ║ _ Merged ║ ║ ║ ║ X Locked ║ ║ ║ ║ Apply Cancel ║ ╚════════════════════════════════════════════════╝ Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-11-14 10:48:40 +00:00
c.message = message
return c
}
// GetMessage returns the text to be displayed after the checkbox
2020-04-26 23:55:45 +00:00
func (c *CheckBox) GetMessage() string {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
Add support for displaying text next to a checkbox When building forms the label field is typically quite short, just one or two words. For checkboxes it is often desirable to have a longer descriptive piece of text. This is not practical to use as a label and in many applications would more commonly be placed to the right of the checkbox. This adds support for a "SetMessage()" method which provides support for text adjacent to the checkbox As an example, this form shows one usage pattern, where the checkbox is used to require case sensitive matching of the author name query. In this case the checkbox label is an empty string, and the message text is used instead: ╔════════════ User filtering ════════════════════╗ ║ ║ ║ Age: ________ ║ ║ ║ ║ Author: ______________ ║ ║ ║ ║ X Case sensitive ║ ║ ║ ║ Apply Cancel ║ ╚════════════════════════════════════════════════╝ Another pattern is where there are a series of checkboxes, all related to the same attribute. Thus they have a common form label but different message text ╔════════════ Request filtering ═════════════════╗ ║ ║ ║ State: X Opened ║ ║ ║ ║ _ Closed ║ ║ ║ ║ _ Merged ║ ║ ║ ║ X Locked ║ ║ ║ ║ Apply Cancel ║ ╚════════════════════════════════════════════════╝ Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-11-14 10:48:40 +00:00
return c.message
}
// SetLabelWidth sets the screen width of the label. A value of 0 will cause the
// primitive to use the width of the label string.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) SetLabelWidth(width int) *CheckBox {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
c.labelWidth = width
return c
}
2017-12-20 19:54:49 +00:00
// SetLabelColor sets the color of the label.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) SetLabelColor(color tcell.Color) *CheckBox {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
2017-12-20 19:54:49 +00:00
c.labelColor = color
return c
}
// SetFieldBackgroundColor sets the background color of the input area.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) SetFieldBackgroundColor(color tcell.Color) *CheckBox {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
2017-12-20 19:54:49 +00:00
c.fieldBackgroundColor = color
return c
}
// SetFieldTextColor sets the text color of the input area.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) SetFieldTextColor(color tcell.Color) *CheckBox {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
2017-12-20 19:54:49 +00:00
c.fieldTextColor = color
return c
}
// SetFormAttributes sets attributes shared by all form items.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
c.labelWidth = labelWidth
2017-12-20 19:54:49 +00:00
c.labelColor = labelColor
c.backgroundColor = bgColor
c.fieldTextColor = fieldTextColor
c.fieldBackgroundColor = fieldBgColor
return c
}
// GetFieldWidth returns this primitive's field width.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) GetFieldWidth() int {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
Add support for displaying text next to a checkbox When building forms the label field is typically quite short, just one or two words. For checkboxes it is often desirable to have a longer descriptive piece of text. This is not practical to use as a label and in many applications would more commonly be placed to the right of the checkbox. This adds support for a "SetMessage()" method which provides support for text adjacent to the checkbox As an example, this form shows one usage pattern, where the checkbox is used to require case sensitive matching of the author name query. In this case the checkbox label is an empty string, and the message text is used instead: ╔════════════ User filtering ════════════════════╗ ║ ║ ║ Age: ________ ║ ║ ║ ║ Author: ______________ ║ ║ ║ ║ X Case sensitive ║ ║ ║ ║ Apply Cancel ║ ╚════════════════════════════════════════════════╝ Another pattern is where there are a series of checkboxes, all related to the same attribute. Thus they have a common form label but different message text ╔════════════ Request filtering ═════════════════╗ ║ ║ ║ State: X Opened ║ ║ ║ ║ _ Closed ║ ║ ║ ║ _ Merged ║ ║ ║ ║ X Locked ║ ║ ║ ║ Apply Cancel ║ ╚════════════════════════════════════════════════╝ Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-11-14 10:48:40 +00:00
if c.message == "" {
return 1
}
return 2 + len(c.message)
}
2017-12-20 19:54:49 +00:00
// 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.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) SetChangedFunc(handler func(checked bool)) *CheckBox {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
2017-12-20 19:54:49 +00:00
c.changed = handler
return c
}
// SetDoneFunc sets a handler which is called when the user is done using the
// checkbox. The callback function is provided with the key that was pressed,
// which is one of the following:
2017-12-20 19:54:49 +00:00
//
// - KeyEscape: Abort text input.
// - KeyTab: Move to the next field.
// - KeyBacktab: Move to the previous field.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) SetDoneFunc(handler func(key tcell.Key)) *CheckBox {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
2017-12-20 19:54:49 +00:00
c.done = handler
return c
}
// SetFinishedFunc sets a callback invoked when the user leaves this form item.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
c.finished = handler
return c
2017-12-20 19:54:49 +00:00
}
// Draw draws this primitive onto the screen.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) Draw(screen tcell.Screen) {
2017-12-20 19:54:49 +00:00
c.Box.Draw(screen)
2020-03-25 14:32:57 +00:00
c.Lock()
defer c.Unlock()
2017-12-20 19:54:49 +00:00
// Prepare
x, y, width, height := c.GetInnerRect()
rightLimit := x + width
2017-12-20 19:54:49 +00:00
if height < 1 || rightLimit <= x {
return
}
// Draw label.
if c.labelWidth > 0 {
labelWidth := c.labelWidth
if labelWidth > rightLimit-x {
labelWidth = rightLimit - x
}
Print(screen, c.label, x, y, labelWidth, AlignLeft, c.labelColor)
x += labelWidth
} else {
_, drawnWidth := Print(screen, c.label, x, y, rightLimit-x, AlignLeft, c.labelColor)
x += drawnWidth
}
2017-12-20 19:54:49 +00:00
// Draw checkbox.
fieldStyle := tcell.StyleDefault.Background(c.fieldBackgroundColor).Foreground(c.fieldTextColor)
if c.focus.HasFocus() {
fieldStyle = fieldStyle.Background(c.fieldTextColor).Foreground(c.fieldBackgroundColor)
}
checkedRune := 'X'
if !c.checked {
checkedRune = ' '
}
screen.SetContent(x, y, checkedRune, nil, fieldStyle)
Add support for displaying text next to a checkbox When building forms the label field is typically quite short, just one or two words. For checkboxes it is often desirable to have a longer descriptive piece of text. This is not practical to use as a label and in many applications would more commonly be placed to the right of the checkbox. This adds support for a "SetMessage()" method which provides support for text adjacent to the checkbox As an example, this form shows one usage pattern, where the checkbox is used to require case sensitive matching of the author name query. In this case the checkbox label is an empty string, and the message text is used instead: ╔════════════ User filtering ════════════════════╗ ║ ║ ║ Age: ________ ║ ║ ║ ║ Author: ______________ ║ ║ ║ ║ X Case sensitive ║ ║ ║ ║ Apply Cancel ║ ╚════════════════════════════════════════════════╝ Another pattern is where there are a series of checkboxes, all related to the same attribute. Thus they have a common form label but different message text ╔════════════ Request filtering ═════════════════╗ ║ ║ ║ State: X Opened ║ ║ ║ ║ _ Closed ║ ║ ║ ║ _ Merged ║ ║ ║ ║ X Locked ║ ║ ║ ║ Apply Cancel ║ ╚════════════════════════════════════════════════╝ Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-11-14 10:48:40 +00:00
if c.message != "" {
Print(screen, c.message, x+2, y, len(c.message), AlignLeft, c.labelColor)
}
2017-12-20 19:54:49 +00:00
}
// InputHandler returns the handler for this primitive.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return c.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
2020-06-10 16:52:50 +00:00
if HitShortcut(event, Keys.Select, Keys.Select2) {
2020-03-25 14:32:57 +00:00
c.Lock()
2017-12-20 19:54:49 +00:00
c.checked = !c.checked
2020-03-25 14:32:57 +00:00
c.Unlock()
2017-12-20 19:54:49 +00:00
if c.changed != nil {
c.changed(c.checked)
}
} else if HitShortcut(event, Keys.Cancel, Keys.MovePreviousField, Keys.MoveNextField) {
2017-12-20 19:54:49 +00:00
if c.done != nil {
c.done(event.Key())
2017-12-20 19:54:49 +00:00
}
if c.finished != nil {
c.finished(event.Key())
}
2017-12-20 19:54:49 +00:00
}
})
2017-12-20 19:54:49 +00:00
}
2019-11-04 06:30:25 +00:00
// MouseHandler returns the mouse handler for this primitive.
2020-04-26 23:55:45 +00:00
func (c *CheckBox) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
return c.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
x, y := event.Position()
_, rectY, _, _ := c.GetInnerRect()
if !c.InRect(x, y) {
return false, nil
}
2019-11-04 06:30:25 +00:00
// Process mouse event.
if action == MouseLeftClick && y == rectY {
setFocus(c)
2019-11-04 06:30:25 +00:00
c.checked = !c.checked
if c.changed != nil {
c.changed(c.checked)
}
consumed = true
2019-11-04 06:30:25 +00:00
}
return
2019-11-04 06:30:25 +00:00
})
}