ui/check_button.go
Noah Petherbridge 7d9ba79cd2 Window Manager Basics, Work in Progress
* Adds Window Manager support to the Supervisor, so that Window widgets
  can be dragged by their title bar, clicked to focus, etc.
* Create a ui.Window as normal, but instead of Packing or Placing it
  into a parent container as before, you call .Supervise() and give it
  your Supervisor. The window registers itself to be managed and drawn
  by the Supervisor itself.
* Supervisor manages the focused window order using a doubly linked
  list. When a window takes focus it moves to the top of the list.
  Widgets in the active window take event priority.
* Extended DragDrop API to support holding a widget pointer in the drag
  operation.
* Changed widget event Handle functions to return an error: so that they
  could return ErrStopPropagation to prevent events going to more
  widgets once handled (for important events).

Some bugs remain around overlapping windows and event propagation.
2020-04-06 22:57:28 -07:00

124 lines
2.7 KiB
Go

package ui
import (
"fmt"
"strconv"
"git.kirsle.net/go/render"
"git.kirsle.net/go/ui/theme"
)
// CheckButton implements a checkbox and radiobox widget. It's based on a
// Button and holds a boolean or string pointer (boolean for checkbox,
// string for radio).
type CheckButton struct {
Button
BoolVar *bool
StringVar *string
Value string
}
// NewCheckButton creates a new CheckButton.
func NewCheckButton(name string, boolVar *bool, child Widget) *CheckButton {
w := &CheckButton{
BoolVar: boolVar,
}
w.Button.child = child
w.IDFunc(func() string {
return fmt.Sprintf("CheckButton<%s %+v>", name, w.BoolVar)
})
w.setup()
return w
}
// NewRadioButton creates a CheckButton bound to a string variable.
func NewRadioButton(name string, stringVar *string, value string, child Widget) *CheckButton {
w := &CheckButton{
StringVar: stringVar,
Value: value,
}
w.Button.child = child
w.IDFunc(func() string {
return fmt.Sprintf(`RadioButton<%s "%s" %s>`, name, w.Value, strconv.FormatBool(*w.StringVar == w.Value))
})
w.setup()
return w
}
// Compute to re-evaluate the button state (in the case of radio buttons where
// a different button will affect the state of this one when clicked).
func (w *CheckButton) Compute(e render.Engine) {
if w.StringVar != nil {
// Radio button, always re-assign the border style in case a sister
// radio button has changed the value.
if *w.StringVar == w.Value {
w.SetBorderStyle(BorderSunken)
} else {
w.SetBorderStyle(BorderRaised)
}
}
w.Button.Compute(e)
}
// setup the common things between checkboxes and radioboxes.
func (w *CheckButton) setup() {
var borderStyle BorderStyle = BorderRaised
if w.BoolVar != nil {
if *w.BoolVar == true {
borderStyle = BorderSunken
}
}
w.Configure(Config{
BorderSize: 2,
BorderStyle: borderStyle,
OutlineSize: 1,
OutlineColor: theme.ButtonOutlineColor,
Background: theme.ButtonBackgroundColor,
})
w.Handle(MouseOver, func(ed EventData) error {
w.hovering = true
w.SetBackground(theme.ButtonHoverColor)
return nil
})
w.Handle(MouseOut, func(ed EventData) error {
w.hovering = false
w.SetBackground(theme.ButtonBackgroundColor)
return nil
})
w.Handle(MouseDown, func(ed EventData) error {
w.clicked = true
w.SetBorderStyle(BorderSunken)
return nil
})
w.Handle(MouseUp, func(ed EventData) error {
w.clicked = false
return nil
})
w.Handle(Click, func(ed EventData) error {
var sunken bool
if w.BoolVar != nil {
if *w.BoolVar {
*w.BoolVar = false
} else {
*w.BoolVar = true
sunken = true
}
} else if w.StringVar != nil {
*w.StringVar = w.Value
sunken = true
}
if sunken {
w.SetBorderStyle(BorderSunken)
} else {
w.SetBorderStyle(BorderRaised)
}
return nil
})
}