Hocus focus

This commit is contained in:
Sasha Koshka 2023-01-30 17:01:47 -05:00
parent 35870951a2
commit 801c3ef6f5
15 changed files with 367 additions and 355 deletions

View File

@ -124,14 +124,14 @@ func (window *Window) handleKeyPress (
modifiers.NumberPad = numberPad modifiers.NumberPad = numberPad
if key == tomo.KeyTab && modifiers.Alt { if key == tomo.KeyTab && modifiers.Alt {
if child, ok := window.child.(tomo.Selectable); ok { if child, ok := window.child.(tomo.Focusable); ok {
direction := tomo.SelectionDirectionForward direction := tomo.KeynavDirectionForward
if modifiers.Shift { if modifiers.Shift {
direction = tomo.SelectionDirectionBackward direction = tomo.KeynavDirectionBackward
} }
if !child.HandleSelection(direction) { if !child.HandleFocus(direction) {
child.HandleDeselection() child.HandleUnfocus()
} }
} }
} else if child, ok := window.child.(tomo.KeyboardTarget); ok { } else if child, ok := window.child.(tomo.KeyboardTarget); ok {

View File

@ -87,11 +87,11 @@ func (window *Window) Adopt (child tomo.Element) {
if previousChild, ok := window.child.(tomo.Flexible); ok { if previousChild, ok := window.child.(tomo.Flexible); ok {
previousChild.OnFlexibleHeightChange(nil) previousChild.OnFlexibleHeightChange(nil)
} }
if previousChild, ok := window.child.(tomo.Selectable); ok { if previousChild, ok := window.child.(tomo.Focusable); ok {
previousChild.OnSelectionRequest(nil) previousChild.OnFocusRequest(nil)
previousChild.OnSelectionMotionRequest(nil) previousChild.OnFocusMotionRequest(nil)
if previousChild.Selected() { if previousChild.Focused() {
previousChild.HandleDeselection() previousChild.HandleUnfocus()
} }
} }
@ -100,8 +100,8 @@ func (window *Window) Adopt (child tomo.Element) {
if newChild, ok := child.(tomo.Flexible); ok { if newChild, ok := child.(tomo.Flexible); ok {
newChild.OnFlexibleHeightChange(window.resizeChildToFit) newChild.OnFlexibleHeightChange(window.resizeChildToFit)
} }
if newChild, ok := child.(tomo.Selectable); ok { if newChild, ok := child.(tomo.Focusable); ok {
newChild.OnSelectionRequest(window.childSelectionRequestCallback) newChild.OnFocusRequest(window.childSelectionRequestCallback)
} }
if child != nil { if child != nil {
child.OnDamage(window.childDrawCallback) child.OnDamage(window.childDrawCallback)
@ -282,20 +282,20 @@ func (window *Window) childMinimumSizeChangeCallback (width, height int) {
} }
func (window *Window) childSelectionRequestCallback () (granted bool) { func (window *Window) childSelectionRequestCallback () (granted bool) {
if child, ok := window.child.(tomo.Selectable); ok { if child, ok := window.child.(tomo.Focusable); ok {
child.HandleSelection(tomo.SelectionDirectionNeutral) child.HandleFocus(tomo.KeynavDirectionNeutral)
} }
return true return true
} }
func (window *Window) childSelectionMotionRequestCallback ( func (window *Window) childSelectionMotionRequestCallback (
direction tomo.SelectionDirection, direction tomo.KeynavDirection,
) ( ) (
granted bool, granted bool,
) { ) {
if child, ok := window.child.(tomo.Selectable); ok { if child, ok := window.child.(tomo.Focusable); ok {
if !child.HandleSelection(direction) { if !child.HandleFocus(direction) {
child.HandleDeselection() child.HandleUnfocus()
} }
return true return true
} }

View File

@ -28,58 +28,61 @@ type Element interface {
OnMinimumSizeChange (callback func ()) OnMinimumSizeChange (callback func ())
} }
// SelectionDirection represents a keyboard navigation direction. // KeynavDirection represents a keyboard navigation direction.
type SelectionDirection int type KeynavDirection int
const ( const (
SelectionDirectionNeutral SelectionDirection = 0 KeynavDirectionNeutral KeynavDirection = 0
SelectionDirectionBackward SelectionDirection = -1 KeynavDirectionBackward KeynavDirection = -1
SelectionDirectionForward SelectionDirection = 1 KeynavDirectionForward KeynavDirection = 1
) )
// Canon returns a well-formed direction. // Canon returns a well-formed direction.
func (direction SelectionDirection) Canon () (canon SelectionDirection) { func (direction KeynavDirection) Canon () (canon KeynavDirection) {
if direction > 0 { if direction > 0 {
return SelectionDirectionForward return KeynavDirectionForward
} else if direction == 0 { } else if direction == 0 {
return SelectionDirectionNeutral return KeynavDirectionNeutral
} else { } else {
return SelectionDirectionBackward return KeynavDirectionBackward
} }
} }
// Selectable represents an element that has keyboard navigation support. This // Focusable represents an element that has keyboard navigation support. This
// includes inputs, buttons, sliders, etc. as well as any elements that have // includes inputs, buttons, sliders, etc. as well as any elements that have
// children (so keyboard navigation events can be propagated downward). // children (so keyboard navigation events can be propagated downward).
type Selectable interface { type Focusable interface {
Element Element
// Selected returns whether or not this element is currently selected. // Focused returns whether or not this element is currently focused.
Selected () (selected bool) Focused () (selected bool)
// Select selects this element, if its parent element grants the // Focus focuses this element, if its parent element grants the
// request. // request.
Select () Focus ()
// HandleSelection causes this element to mark itself as selected, if it // HandleFocus causes this element to mark itself as focused. If the
// can currently be. Otherwise, it will return false and do nothing. // element does not have children, it is disabled, or there are no more
HandleSelection (direction SelectionDirection) (accepted bool) // selectable children in the given direction, it should return false
// and do nothing. Otherwise, it should select itself and any children
// (if applicable) and return true.
HandleFocus (direction KeynavDirection) (accepted bool)
// HandleDeselection causes this element to mark itself and all of its // HandleDeselection causes this element to mark itself and all of its
// children as deselected. // children as unfocused.
HandleDeselection () HandleUnfocus ()
// OnSelectionRequest sets a function to be called when this element // OnFocusRequest sets a function to be called when this element wants
// wants its parent element to select it. Parent elements should return // its parent element to focus it. Parent elements should return true if
// true if the request was granted, and false if it was not. // the request was granted, and false if it was not.
OnSelectionRequest (func () (granted bool)) OnFocusRequest (func () (granted bool))
// OnSelectionMotionRequest sets a function to be called when this // OnFocusMotionRequest sets a function to be called when this
// element wants its parent element to select the element behind or in // element wants its parent element to focus the element behind or in
// front of it, depending on the specified direction. Parent elements // front of it, depending on the specified direction. Parent elements
// should return true if the request was granted, and false if it was // should return true if the request was granted, and false if it was
// not. // not.
OnSelectionMotionRequest (func (SelectionDirection) (granted bool)) OnFocusMotionRequest (func (direction KeynavDirection) (granted bool))
} }
// KeyboardTarget represents an element that can receive keyboard input. // KeyboardTarget represents an element that can receive keyboard input.

View File

@ -11,9 +11,9 @@ var buttonCase = theme.C("basic", "button")
// Button is a clickable button. // Button is a clickable button.
type Button struct { type Button struct {
*core.Core *core.Core
*core.SelectableCore *core.FocusableCore
core core.CoreControl core core.CoreControl
selectableControl core.SelectableCoreControl focusableControl core.FocusableCoreControl
drawer artist.TextDrawer drawer artist.TextDrawer
pressed bool pressed bool
@ -26,8 +26,8 @@ type Button struct {
func NewButton (text string) (element *Button) { func NewButton (text string) (element *Button) {
element = &Button { } element = &Button { }
element.Core, element.core = core.NewCore(element) element.Core, element.core = core.NewCore(element)
element.SelectableCore, element.FocusableCore,
element.selectableControl = core.NewSelectableCore (func () { element.focusableControl = core.NewFocusableCore (func () {
if element.core.HasImage () { if element.core.HasImage () {
element.draw() element.draw()
element.core.DamageAll() element.core.DamageAll()
@ -45,7 +45,7 @@ func (element *Button) Resize (width, height int) {
func (element *Button) HandleMouseDown (x, y int, button tomo.Button) { func (element *Button) HandleMouseDown (x, y int, button tomo.Button) {
if !element.Enabled() { return } if !element.Enabled() { return }
if !element.Selected() { element.Select() } if !element.Focused() { element.Focus() }
if button != tomo.ButtonLeft { return } if button != tomo.ButtonLeft { return }
element.pressed = true element.pressed = true
if element.core.HasImage() { if element.core.HasImage() {
@ -106,7 +106,7 @@ func (element *Button) OnClick (callback func ()) {
// SetEnabled sets whether this button can be clicked or not. // SetEnabled sets whether this button can be clicked or not.
func (element *Button) SetEnabled (enabled bool) { func (element *Button) SetEnabled (enabled bool) {
element.selectableControl.SetEnabled(enabled) element.focusableControl.SetEnabled(enabled)
} }
// SetText sets the button's label text. // SetText sets the button's label text.
@ -131,7 +131,7 @@ func (element *Button) draw () {
pattern, inset := theme.ButtonPattern(theme.PatternState { pattern, inset := theme.ButtonPattern(theme.PatternState {
Case: buttonCase, Case: buttonCase,
Disabled: !element.Enabled(), Disabled: !element.Enabled(),
Selected: element.Selected(), Focused: element.Focused(),
Pressed: element.pressed, Pressed: element.pressed,
}) })

View File

@ -11,9 +11,9 @@ var checkboxCase = theme.C("basic", "checkbox")
// Checkbox is a toggle-able checkbox with a label. // Checkbox is a toggle-able checkbox with a label.
type Checkbox struct { type Checkbox struct {
*core.Core *core.Core
*core.SelectableCore *core.FocusableCore
core core.CoreControl core core.CoreControl
selectableControl core.SelectableCoreControl focusableControl core.FocusableCoreControl
drawer artist.TextDrawer drawer artist.TextDrawer
pressed bool pressed bool
@ -27,8 +27,8 @@ type Checkbox struct {
func NewCheckbox (text string, checked bool) (element *Checkbox) { func NewCheckbox (text string, checked bool) (element *Checkbox) {
element = &Checkbox { checked: checked } element = &Checkbox { checked: checked }
element.Core, element.core = core.NewCore(element) element.Core, element.core = core.NewCore(element)
element.SelectableCore, element.FocusableCore,
element.selectableControl = core.NewSelectableCore (func () { element.focusableControl = core.NewFocusableCore (func () {
if element.core.HasImage () { if element.core.HasImage () {
element.draw() element.draw()
element.core.DamageAll() element.core.DamageAll()
@ -47,7 +47,7 @@ func (element *Checkbox) Resize (width, height int) {
func (element *Checkbox) HandleMouseDown (x, y int, button tomo.Button) { func (element *Checkbox) HandleMouseDown (x, y int, button tomo.Button) {
if !element.Enabled() { return } if !element.Enabled() { return }
element.Select() element.Focus()
element.pressed = true element.pressed = true
if element.core.HasImage() { if element.core.HasImage() {
element.draw() element.draw()
@ -113,7 +113,7 @@ func (element *Checkbox) Value () (checked bool) {
// SetEnabled sets whether this checkbox can be toggled or not. // SetEnabled sets whether this checkbox can be toggled or not.
func (element *Checkbox) SetEnabled (enabled bool) { func (element *Checkbox) SetEnabled (enabled bool) {
element.selectableControl.SetEnabled(enabled) element.focusableControl.SetEnabled(enabled)
} }
// SetText sets the checkbox's label text. // SetText sets the checkbox's label text.
@ -150,7 +150,7 @@ func (element *Checkbox) draw () {
pattern, inset := theme.ButtonPattern(theme.PatternState { pattern, inset := theme.ButtonPattern(theme.PatternState {
Case: checkboxCase, Case: checkboxCase,
Disabled: !element.Enabled(), Disabled: !element.Enabled(),
Selected: element.Selected(), Focused: element.Focused(),
Pressed: element.pressed, Pressed: element.pressed,
}) })
artist.FillRectangle(element.core, pattern, boxBounds) artist.FillRectangle(element.core, pattern, boxBounds)

View File

@ -14,16 +14,16 @@ type Container struct {
*core.Core *core.Core
core core.CoreControl core core.CoreControl
layout tomo.Layout layout tomo.Layout
children []tomo.LayoutEntry children []tomo.LayoutEntry
drags [10]tomo.MouseTarget drags [10]tomo.MouseTarget
warping bool warping bool
selected bool focused bool
selectable bool focusable bool
flexible bool flexible bool
onSelectionRequest func () (granted bool) onFocusRequest func () (granted bool)
onSelectionMotionRequest func (tomo.SelectionDirection) (granted bool) onFocusMotionRequest func (tomo.KeynavDirection) (granted bool)
onFlexibleHeightChange func () onFlexibleHeightChange func ()
} }
@ -57,14 +57,14 @@ func (element *Container) Adopt (child tomo.Element, expand bool) {
if child0, ok := child.(tomo.Flexible); ok { if child0, ok := child.(tomo.Flexible); ok {
child0.OnFlexibleHeightChange(element.updateMinimumSize) child0.OnFlexibleHeightChange(element.updateMinimumSize)
} }
if child0, ok := child.(tomo.Selectable); ok { if child0, ok := child.(tomo.Focusable); ok {
child0.OnSelectionRequest (func () (granted bool) { child0.OnFocusRequest (func () (granted bool) {
return element.childSelectionRequestCallback(child0) return element.childFocusRequestCallback(child0)
}) })
child0.OnSelectionMotionRequest ( child0.OnFocusMotionRequest (
func (direction tomo.SelectionDirection) (granted bool) { func (direction tomo.KeynavDirection) (granted bool) {
if element.onSelectionMotionRequest == nil { return } if element.onFocusMotionRequest == nil { return }
return element.onSelectionMotionRequest(direction) return element.onFocusMotionRequest(direction)
}) })
} }
@ -132,11 +132,11 @@ func (element *Container) Disown (child tomo.Element) {
func (element *Container) clearChildEventHandlers (child tomo.Element) { func (element *Container) clearChildEventHandlers (child tomo.Element) {
child.OnDamage(nil) child.OnDamage(nil)
child.OnMinimumSizeChange(nil) child.OnMinimumSizeChange(nil)
if child0, ok := child.(tomo.Selectable); ok { if child0, ok := child.(tomo.Focusable); ok {
child0.OnSelectionRequest(nil) child0.OnFocusRequest(nil)
child0.OnSelectionMotionRequest(nil) child0.OnFocusMotionRequest(nil)
if child0.Selected() { if child0.Focused() {
child0.HandleDeselection() child0.HandleUnfocus()
} }
} }
if child0, ok := child.(tomo.Flexible); ok { if child0, ok := child.(tomo.Flexible); ok {
@ -238,7 +238,7 @@ func (element *Container) HandleMouseScroll (x, y int, deltaX, deltaY float64) {
} }
func (element *Container) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) { func (element *Container) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) {
element.forSelected (func (child tomo.Selectable) bool { element.forFocused (func (child tomo.Focusable) bool {
child0, handlesKeyboard := child.(tomo.KeyboardTarget) child0, handlesKeyboard := child.(tomo.KeyboardTarget)
if handlesKeyboard { if handlesKeyboard {
child0.HandleKeyDown(key, modifiers) child0.HandleKeyDown(key, modifiers)
@ -248,7 +248,7 @@ func (element *Container) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers)
} }
func (element *Container) HandleKeyUp (key tomo.Key, modifiers tomo.Modifiers) { func (element *Container) HandleKeyUp (key tomo.Key, modifiers tomo.Modifiers) {
element.forSelected (func (child tomo.Selectable) bool { element.forFocused (func (child tomo.Focusable) bool {
child0, handlesKeyboard := child.(tomo.KeyboardTarget) child0, handlesKeyboard := child.(tomo.KeyboardTarget)
if handlesKeyboard { if handlesKeyboard {
child0.HandleKeyUp(key, modifiers) child0.HandleKeyUp(key, modifiers)
@ -257,103 +257,6 @@ func (element *Container) HandleKeyUp (key tomo.Key, modifiers tomo.Modifiers) {
}) })
} }
func (element *Container) Selected () (selected bool) {
return element.selected
}
func (element *Container) Select () {
if element.onSelectionRequest != nil {
element.onSelectionRequest()
}
}
func (element *Container) HandleSelection (direction tomo.SelectionDirection) (ok bool) {
if !element.selectable { return false }
direction = direction.Canon()
firstSelected := element.firstSelected()
if firstSelected < 0 {
// no element is currently selected, so we need to select either
// the first or last selectable element depending on the
// direction.
switch direction {
case tomo.SelectionDirectionNeutral, tomo.SelectionDirectionForward:
// if we recieve a neutral or forward direction, select
// the first selectable element.
return element.selectFirstSelectableElement(direction)
case tomo.SelectionDirectionBackward:
// if we recieve a backward direction, select the last
// selectable element.
return element.selectLastSelectableElement(direction)
}
} else {
// an element is currently selected, so we need to move the
// selection in the specified direction
firstSelectedChild :=
element.children[firstSelected].Element.(tomo.Selectable)
// before we move the selection, the currently selected child
// may also be able to move its selection. if the child is able
// to do that, we will let it and not move ours.
if firstSelectedChild.HandleSelection(direction) {
return true
}
// find the previous/next selectable element relative to the
// currently selected element, if it exists.
for index := firstSelected + int(direction);
index < len(element.children) && index >= 0;
index += int(direction) {
child, selectable :=
element.children[index].
Element.(tomo.Selectable)
if selectable && child.HandleSelection(direction) {
// we have found one, so we now actually move
// the selection.
firstSelectedChild.HandleDeselection()
element.selected = true
return true
}
}
}
return false
}
func (element *Container) selectFirstSelectableElement (
direction tomo.SelectionDirection,
) (
ok bool,
) {
element.forSelectable (func (child tomo.Selectable) bool {
if child.HandleSelection(direction) {
element.selected = true
ok = true
return false
}
return true
})
return
}
func (element *Container) selectLastSelectableElement (
direction tomo.SelectionDirection,
) (
ok bool,
) {
element.forSelectableBackward (func (child tomo.Selectable) bool {
if child.HandleSelection(direction) {
element.selected = true
ok = true
return false
}
return true
})
return
}
func (element *Container) FlexibleHeightFor (width int) (height int) { func (element *Container) FlexibleHeightFor (width int) (height int) {
return element.layout.FlexibleHeightFor(element.children, width) return element.layout.FlexibleHeightFor(element.children, width)
} }
@ -362,37 +265,134 @@ func (element *Container) OnFlexibleHeightChange (callback func ()) {
element.onFlexibleHeightChange = callback element.onFlexibleHeightChange = callback
} }
func (element *Container) HandleDeselection () { func (element *Container) Focused () (focused bool) {
element.selected = false return element.focused
element.forSelected (func (child tomo.Selectable) bool { }
child.HandleDeselection()
func (element *Container) Focus () {
if element.onFocusRequest != nil {
element.onFocusRequest()
}
}
func (element *Container) HandleFocus (direction tomo.KeynavDirection) (ok bool) {
if !element.focusable { return false }
direction = direction.Canon()
firstFocused := element.firstFocused()
if firstFocused < 0 {
// no element is currently focused, so we need to focus either
// the first or last focusable element depending on the
// direction.
switch direction {
case tomo.KeynavDirectionNeutral, tomo.KeynavDirectionForward:
// if we recieve a neutral or forward direction, focus
// the first focusable element.
return element.focusFirstFocusableElement(direction)
case tomo.KeynavDirectionBackward:
// if we recieve a backward direction, focus the last
// focusable element.
return element.focusLastFocusableElement(direction)
}
} else {
// an element is currently focused, so we need to move the
// focus in the specified direction
firstFocusedChild :=
element.children[firstFocused].Element.(tomo.Focusable)
// before we move the focus, the currently focused child
// may also be able to move its focus. if the child is able
// to do that, we will let it and not move ours.
if firstFocusedChild.HandleFocus(direction) {
return true
}
// find the previous/next focusable element relative to the
// currently focused element, if it exists.
for index := firstFocused + int(direction);
index < len(element.children) && index >= 0;
index += int(direction) {
child, focusable :=
element.children[index].
Element.(tomo.Focusable)
if focusable && child.HandleFocus(direction) {
// we have found one, so we now actually move
// the focus.
firstFocusedChild.HandleUnfocus()
element.focused = true
return true
}
}
}
return false
}
func (element *Container) focusFirstFocusableElement (
direction tomo.KeynavDirection,
) (
ok bool,
) {
element.forFocusable (func (child tomo.Focusable) bool {
if child.HandleFocus(direction) {
element.focused = true
ok = true
return false
}
return true
})
return
}
func (element *Container) focusLastFocusableElement (
direction tomo.KeynavDirection,
) (
ok bool,
) {
element.forFocusableBackward (func (child tomo.Focusable) bool {
if child.HandleFocus(direction) {
element.focused = true
ok = true
return false
}
return true
})
return
}
func (element *Container) HandleUnfocus () {
element.focused = false
element.forFocused (func (child tomo.Focusable) bool {
child.HandleUnfocus()
return true return true
}) })
} }
func (element *Container) OnSelectionRequest (callback func () (granted bool)) { func (element *Container) OnFocusRequest (callback func () (granted bool)) {
element.onSelectionRequest = callback element.onFocusRequest = callback
} }
func (element *Container) OnSelectionMotionRequest ( func (element *Container) OnFocusMotionRequest (
callback func (direction tomo.SelectionDirection) (granted bool), callback func (direction tomo.KeynavDirection) (granted bool),
) { ) {
element.onSelectionMotionRequest = callback element.onFocusMotionRequest = callback
} }
func (element *Container) forSelected (callback func (child tomo.Selectable) bool) { func (element *Container) forFocused (callback func (child tomo.Focusable) bool) {
for _, entry := range element.children { for _, entry := range element.children {
child, selectable := entry.Element.(tomo.Selectable) child, focusable := entry.Element.(tomo.Focusable)
if selectable && child.Selected() { if focusable && child.Focused() {
if !callback(child) { break } if !callback(child) { break }
} }
} }
} }
func (element *Container) forSelectable (callback func (child tomo.Selectable) bool) { func (element *Container) forFocusable (callback func (child tomo.Focusable) bool) {
for _, entry := range element.children { for _, entry := range element.children {
child, selectable := entry.Element.(tomo.Selectable) child, focusable := entry.Element.(tomo.Focusable)
if selectable { if focusable {
if !callback(child) { break } if !callback(child) { break }
} }
} }
@ -400,26 +400,26 @@ func (element *Container) forSelectable (callback func (child tomo.Selectable) b
func (element *Container) forFlexible (callback func (child tomo.Flexible) bool) { func (element *Container) forFlexible (callback func (child tomo.Flexible) bool) {
for _, entry := range element.children { for _, entry := range element.children {
child, selectable := entry.Element.(tomo.Flexible) child, flexible := entry.Element.(tomo.Flexible)
if selectable { if flexible {
if !callback(child) { break } if !callback(child) { break }
} }
} }
} }
func (element *Container) forSelectableBackward (callback func (child tomo.Selectable) bool) { func (element *Container) forFocusableBackward (callback func (child tomo.Focusable) bool) {
for index := len(element.children) - 1; index >= 0; index -- { for index := len(element.children) - 1; index >= 0; index -- {
child, selectable := element.children[index].Element.(tomo.Selectable) child, focusable := element.children[index].Element.(tomo.Focusable)
if selectable { if focusable {
if !callback(child) { break } if !callback(child) { break }
} }
} }
} }
func (element *Container) firstSelected () (index int) { func (element *Container) firstFocused () (index int) {
for currentIndex, entry := range element.children { for currentIndex, entry := range element.children {
child, selectable := entry.Element.(tomo.Selectable) child, focusable := entry.Element.(tomo.Focusable)
if selectable && child.Selected() { if focusable && child.Focused() {
return currentIndex return currentIndex
} }
} }
@ -427,9 +427,9 @@ func (element *Container) firstSelected () (index int) {
} }
func (element *Container) reflectChildProperties () { func (element *Container) reflectChildProperties () {
element.selectable = false element.focusable = false
element.forSelectable (func (tomo.Selectable) bool { element.forFocusable (func (tomo.Focusable) bool {
element.selectable = true element.focusable = true
return false return false
}) })
element.flexible = false element.flexible = false
@ -437,22 +437,22 @@ func (element *Container) reflectChildProperties () {
element.flexible = true element.flexible = true
return false return false
}) })
if !element.selectable { if !element.focusable {
element.selected = false element.focused = false
} }
} }
func (element *Container) childSelectionRequestCallback ( func (element *Container) childFocusRequestCallback (
child tomo.Selectable, child tomo.Focusable,
) ( ) (
granted bool, granted bool,
) { ) {
if element.onSelectionRequest != nil && element.onSelectionRequest() { if element.onFocusRequest != nil && element.onFocusRequest() {
element.forSelected (func (child tomo.Selectable) bool { element.forFocused (func (child tomo.Focusable) bool {
child.HandleDeselection() child.HandleUnfocus()
return true return true
}) })
child.HandleSelection(tomo.SelectionDirectionNeutral) child.HandleFocus(tomo.KeynavDirectionNeutral)
return true return true
} else { } else {
return false return false

View File

@ -12,9 +12,9 @@ var listCase = theme.C("basic", "list")
// List is an element that contains several objects that a user can select. // List is an element that contains several objects that a user can select.
type List struct { type List struct {
*core.Core *core.Core
*core.SelectableCore *core.FocusableCore
core core.CoreControl core core.CoreControl
selectableControl core.SelectableCoreControl focusableControl core.FocusableCoreControl
pressed bool pressed bool
@ -34,8 +34,8 @@ type List struct {
func NewList (entries ...ListEntry) (element *List) { func NewList (entries ...ListEntry) (element *List) {
element = &List { selectedEntry: -1 } element = &List { selectedEntry: -1 }
element.Core, element.core = core.NewCore(element) element.Core, element.core = core.NewCore(element)
element.SelectableCore, element.FocusableCore,
element.selectableControl = core.NewSelectableCore (func () { element.focusableControl = core.NewFocusableCore (func () {
if element.core.HasImage () { if element.core.HasImage () {
element.draw() element.draw()
element.core.DamageAll() element.core.DamageAll()
@ -78,7 +78,7 @@ func (element *List) Collapse (width, height int) {
func (element *List) HandleMouseDown (x, y int, button tomo.Button) { func (element *List) HandleMouseDown (x, y int, button tomo.Button) {
if !element.Enabled() { return } if !element.Enabled() { return }
if !element.Selected() { element.Select() } if !element.Focused() { element.Focus() }
if button != tomo.ButtonLeft { return } if button != tomo.ButtonLeft { return }
element.pressed = true element.pressed = true
if element.selectUnderMouse(x, y) && element.core.HasImage() { if element.selectUnderMouse(x, y) && element.core.HasImage() {
@ -377,7 +377,7 @@ func (element *List) draw () {
pattern, inset := theme.ListPattern(theme.PatternState { pattern, inset := theme.ListPattern(theme.PatternState {
Case: listCase, Case: listCase,
Disabled: !element.Enabled(), Disabled: !element.Enabled(),
Selected: element.Selected(), Focused: element.Focused(),
}) })
artist.FillRectangle(element.core, pattern, bounds) artist.FillRectangle(element.core, pattern, bounds)
@ -394,6 +394,6 @@ func (element *List) draw () {
if entryPosition.Y > bounds.Max.Y { break } if entryPosition.Y > bounds.Max.Y { break }
entry.Draw ( entry.Draw (
innerCanvas, entryPosition, innerCanvas, entryPosition,
element.Selected(), element.selectedEntry == index) element.Focused(), element.selectedEntry == index)
} }
} }

View File

@ -55,14 +55,14 @@ func (entry *ListEntry) updateBounds () {
func (entry *ListEntry) Draw ( func (entry *ListEntry) Draw (
destination tomo.Canvas, destination tomo.Canvas,
offset image.Point, offset image.Point,
selected bool, focused bool,
on bool, on bool,
) ( ) (
updatedRegion image.Rectangle, updatedRegion image.Rectangle,
) { ) {
pattern, _ := theme.ItemPattern(theme.PatternState { pattern, _ := theme.ItemPattern(theme.PatternState {
Case: listEntryCase, Case: listEntryCase,
Selected: selected, Focused: focused,
On: on, On: on,
}) })
artist.FillRectangle ( artist.FillRectangle (
@ -71,7 +71,7 @@ func (entry *ListEntry) Draw (
entry.Bounds().Add(offset)) entry.Bounds().Add(offset))
foreground, _ := theme.ForegroundPattern (theme.PatternState { foreground, _ := theme.ForegroundPattern (theme.PatternState {
Case: listEntryCase, Case: listEntryCase,
Selected: selected, Focused: focused,
On: on, On: on,
}) })
return entry.drawer.Draw ( return entry.drawer.Draw (

View File

@ -15,7 +15,7 @@ var scrollBarVerticalCase = theme.C("basic", "scrollBarVertical")
type ScrollContainer struct { type ScrollContainer struct {
*core.Core *core.Core
core core.CoreControl core core.CoreControl
selected bool focused bool
child tomo.Scrollable child tomo.Scrollable
childWidth, childHeight int childWidth, childHeight int
@ -40,8 +40,8 @@ type ScrollContainer struct {
bar image.Rectangle bar image.Rectangle
} }
onSelectionRequest func () (granted bool) onFocusRequest func () (granted bool)
onSelectionMotionRequest func (tomo.SelectionDirection) (granted bool) onFocusMotionRequest func (tomo.KeynavDirection) (granted bool)
} }
// NewScrollContainer creates a new scroll container with the specified scroll // NewScrollContainer creates a new scroll container with the specified scroll
@ -80,11 +80,11 @@ func (element *ScrollContainer) Adopt (child tomo.Scrollable) {
child.OnDamage(element.childDamageCallback) child.OnDamage(element.childDamageCallback)
child.OnMinimumSizeChange(element.updateMinimumSize) child.OnMinimumSizeChange(element.updateMinimumSize)
child.OnScrollBoundsChange(element.childScrollBoundsChangeCallback) child.OnScrollBoundsChange(element.childScrollBoundsChangeCallback)
if newChild, ok := child.(tomo.Selectable); ok { if newChild, ok := child.(tomo.Focusable); ok {
newChild.OnSelectionRequest ( newChild.OnFocusRequest (
element.childSelectionRequestCallback) element.childFocusRequestCallback)
newChild.OnSelectionMotionRequest ( newChild.OnFocusMotionRequest (
element.childSelectionMotionRequestCallback) element.childFocusMotionRequestCallback)
} }
// TODO: somehow inform the core that we do not in fact want to // TODO: somehow inform the core that we do not in fact want to
@ -193,82 +193,80 @@ func (element *ScrollContainer) scrollChildBy (x, y int) {
element.child.ScrollTo(scrollPoint) element.child.ScrollTo(scrollPoint)
} }
func (element *ScrollContainer) Selected () (selected bool) { func (element *ScrollContainer) Focused () (focused bool) {
return element.selected return element.focused
} }
func (element *ScrollContainer) Select () { func (element *ScrollContainer) Focus () {
if element.onSelectionRequest != nil { if element.onFocusRequest != nil {
element.onSelectionRequest() element.onFocusRequest()
} }
} }
func (element *ScrollContainer) HandleSelection ( func (element *ScrollContainer) HandleFocus (
direction tomo.SelectionDirection, direction tomo.KeynavDirection,
) ( ) (
accepted bool, accepted bool,
) { ) {
if child, ok := element.child.(tomo.Selectable); ok { if child, ok := element.child.(tomo.Focusable); ok {
element.selected = true element.focused = true
return child.HandleSelection(direction) return child.HandleFocus(direction)
} else { } else {
element.selected = false element.focused = false
return false return false
} }
} }
func (element *ScrollContainer) HandleDeselection () { func (element *ScrollContainer) HandleUnfocus () {
if child, ok := element.child.(tomo.Selectable); ok { if child, ok := element.child.(tomo.Focusable); ok {
child.HandleDeselection() child.HandleUnfocus()
} }
element.selected = false element.focused = false
} }
func (element *ScrollContainer) OnSelectionRequest (callback func () (granted bool)) { func (element *ScrollContainer) OnFocusRequest (callback func () (granted bool)) {
element.onSelectionRequest = callback element.onFocusRequest = callback
} }
func (element *ScrollContainer) OnSelectionMotionRequest ( func (element *ScrollContainer) OnFocusMotionRequest (
callback func (direction tomo.SelectionDirection) (granted bool), callback func (direction tomo.KeynavDirection) (granted bool),
) { ) {
element.onSelectionMotionRequest = callback element.onFocusMotionRequest = callback
} }
func (element *ScrollContainer) childDamageCallback (region tomo.Canvas) { func (element *ScrollContainer) childDamageCallback (region tomo.Canvas) {
element.core.DamageRegion(artist.Paste(element, region, image.Point { })) element.core.DamageRegion(artist.Paste(element, region, image.Point { }))
} }
func (element *ScrollContainer) childSelectionRequestCallback () (granted bool) { func (element *ScrollContainer) childFocusRequestCallback () (granted bool) {
child, ok := element.child.(tomo.Selectable) child, ok := element.child.(tomo.Focusable)
if !ok { return false } if !ok { return false }
if element.onSelectionRequest != nil && element.onSelectionRequest() { if element.onFocusRequest != nil && element.onFocusRequest() {
child.HandleSelection(tomo.SelectionDirectionNeutral) child.HandleFocus(tomo.KeynavDirectionNeutral)
return true return true
} else { } else {
return false return false
} }
} }
func (element *ScrollContainer) childSelectionMotionRequestCallback ( func (element *ScrollContainer) childFocusMotionRequestCallback (
direction tomo.SelectionDirection, direction tomo.KeynavDirection,
) ( ) (
granted bool, granted bool,
) { ) {
if element.onSelectionMotionRequest == nil { if element.onFocusMotionRequest == nil { return }
return return element.onFocusMotionRequest(direction)
}
return element.onSelectionMotionRequest(direction)
} }
func (element *ScrollContainer) clearChildEventHandlers (child tomo.Scrollable) { func (element *ScrollContainer) clearChildEventHandlers (child tomo.Scrollable) {
child.OnDamage(nil) child.OnDamage(nil)
child.OnMinimumSizeChange(nil) child.OnMinimumSizeChange(nil)
child.OnScrollBoundsChange(nil) child.OnScrollBoundsChange(nil)
if child0, ok := child.(tomo.Selectable); ok { if child0, ok := child.(tomo.Focusable); ok {
child0.OnSelectionRequest(nil) child0.OnFocusRequest(nil)
child0.OnSelectionMotionRequest(nil) child0.OnFocusMotionRequest(nil)
if child0.Selected() { if child0.Focused() {
child0.HandleDeselection() child0.HandleUnfocus()
} }
} }
if child0, ok := child.(tomo.Flexible); ok { if child0, ok := child.(tomo.Flexible); ok {

View File

@ -12,9 +12,9 @@ var switchCase = theme.C("basic", "switch")
// functionally identical to Checkbox, but plays a different semantic role. // functionally identical to Checkbox, but plays a different semantic role.
type Switch struct { type Switch struct {
*core.Core *core.Core
*core.SelectableCore *core.FocusableCore
core core.CoreControl core core.CoreControl
selectableControl core.SelectableCoreControl focusableControl core.FocusableCoreControl
drawer artist.TextDrawer drawer artist.TextDrawer
pressed bool pressed bool
@ -28,8 +28,8 @@ type Switch struct {
func NewSwitch (text string, on bool) (element *Switch) { func NewSwitch (text string, on bool) (element *Switch) {
element = &Switch { checked: on, text: text } element = &Switch { checked: on, text: text }
element.Core, element.core = core.NewCore(element) element.Core, element.core = core.NewCore(element)
element.SelectableCore, element.FocusableCore,
element.selectableControl = core.NewSelectableCore (func () { element.focusableControl = core.NewFocusableCore (func () {
if element.core.HasImage () { if element.core.HasImage () {
element.draw() element.draw()
element.core.DamageAll() element.core.DamageAll()
@ -49,7 +49,7 @@ func (element *Switch) Resize (width, height int) {
func (element *Switch) HandleMouseDown (x, y int, button tomo.Button) { func (element *Switch) HandleMouseDown (x, y int, button tomo.Button) {
if !element.Enabled() { return } if !element.Enabled() { return }
element.Select() element.Focus()
element.pressed = true element.pressed = true
if element.core.HasImage() { if element.core.HasImage() {
element.draw() element.draw()
@ -115,7 +115,7 @@ func (element *Switch) Value () (on bool) {
// SetEnabled sets whether this switch can be flipped or not. // SetEnabled sets whether this switch can be flipped or not.
func (element *Switch) SetEnabled (enabled bool) { func (element *Switch) SetEnabled (enabled bool) {
element.selectableControl.SetEnabled(enabled) element.focusableControl.SetEnabled(enabled)
} }
// SetText sets the checkbox's label text. // SetText sets the checkbox's label text.
@ -171,7 +171,7 @@ func (element *Switch) draw () {
gutterPattern, _ := theme.GutterPattern(theme.PatternState { gutterPattern, _ := theme.GutterPattern(theme.PatternState {
Case: switchCase, Case: switchCase,
Disabled: !element.Enabled(), Disabled: !element.Enabled(),
Selected: element.Selected(), Focused: element.Focused(),
Pressed: element.pressed, Pressed: element.pressed,
}) })
artist.FillRectangle(element.core, gutterPattern, gutterBounds) artist.FillRectangle(element.core, gutterPattern, gutterBounds)
@ -179,7 +179,7 @@ func (element *Switch) draw () {
handlePattern, _ := theme.HandlePattern(theme.PatternState { handlePattern, _ := theme.HandlePattern(theme.PatternState {
Case: switchCase, Case: switchCase,
Disabled: !element.Enabled(), Disabled: !element.Enabled(),
Selected: element.Selected(), Focused: element.Focused(),
Pressed: element.pressed, Pressed: element.pressed,
}) })
artist.FillRectangle(element.core, handlePattern, handleBounds) artist.FillRectangle(element.core, handlePattern, handleBounds)

View File

@ -12,9 +12,9 @@ var textBoxCase = theme.C("basic", "textBox")
// TextBox is a single-line text input. // TextBox is a single-line text input.
type TextBox struct { type TextBox struct {
*core.Core *core.Core
*core.SelectableCore *core.FocusableCore
core core.CoreControl core core.CoreControl
selectableControl core.SelectableCoreControl focusableControl core.FocusableCoreControl
cursor int cursor int
scroll int scroll int
@ -35,8 +35,8 @@ type TextBox struct {
func NewTextBox (placeholder, value string) (element *TextBox) { func NewTextBox (placeholder, value string) (element *TextBox) {
element = &TextBox { } element = &TextBox { }
element.Core, element.core = core.NewCore(element) element.Core, element.core = core.NewCore(element)
element.SelectableCore, element.FocusableCore,
element.selectableControl = core.NewSelectableCore (func () { element.focusableControl = core.NewFocusableCore (func () {
if element.core.HasImage () { if element.core.HasImage () {
element.draw() element.draw()
element.core.DamageAll() element.core.DamageAll()
@ -61,8 +61,8 @@ func (element *TextBox) Resize (width, height int) {
} }
func (element *TextBox) HandleMouseDown (x, y int, button tomo.Button) { func (element *TextBox) HandleMouseDown (x, y int, button tomo.Button) {
if !element.Enabled() { return } if !element.Enabled() { return }
if !element.Selected() { element.Select() } if !element.Focused() { element.Focus() }
} }
func (element *TextBox) HandleMouseUp (x, y int, button tomo.Button) { } func (element *TextBox) HandleMouseUp (x, y int, button tomo.Button) { }
@ -277,13 +277,13 @@ func (element *TextBox) draw () {
// FIXME: take index into account // FIXME: take index into account
pattern, inset := theme.InputPattern(theme.PatternState { pattern, inset := theme.InputPattern(theme.PatternState {
Case: textBoxCase, Case: textBoxCase,
Disabled: !element.Enabled(), Disabled: !element.Enabled(),
Selected: element.Selected(), Focused: element.Focused(),
}) })
artist.FillRectangle(element.core, pattern, bounds) artist.FillRectangle(element.core, pattern, bounds)
if len(element.text) == 0 && !element.Selected() { if len(element.text) == 0 && !element.Focused() {
// draw placeholder // draw placeholder
textBounds := element.placeholderDrawer.LayoutBounds() textBounds := element.placeholderDrawer.LayoutBounds()
offset := image.Point { offset := image.Point {
@ -314,7 +314,7 @@ func (element *TextBox) draw () {
foreground, foreground,
offset.Sub(textBounds.Min)) offset.Sub(textBounds.Min))
if element.Selected() { if element.Focused() {
// cursor // cursor
cursorPosition := element.valueDrawer.PositionOf ( cursorPosition := element.valueDrawer.PositionOf (
element.cursor) element.cursor)

View File

@ -15,9 +15,6 @@ type Core struct {
minimumHeight int minimumHeight int
} }
selectable bool
selected bool
onMinimumSizeChange func () onMinimumSizeChange func ()
onDamage func (region tomo.Canvas) onDamage func (region tomo.Canvas)
} }

View File

@ -2,110 +2,110 @@ package core
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
// SelectableCore is a struct that can be embedded into objects to make them // FocusableCore is a struct that can be embedded into objects to make them
// selectable, giving them the default selectability behavior. // focusable, giving them the default keynav behavior.
type SelectableCore struct { type FocusableCore struct {
selected bool focused bool
enabled bool enabled bool
drawSelectionChange func () drawFocusChange func ()
onSelectionRequest func () (granted bool) onFocusRequest func () (granted bool)
onSelectionMotionRequest func(tomo.SelectionDirection) (granted bool) onFocusMotionRequest func(tomo.KeynavDirection) (granted bool)
} }
// NewSelectableCore creates a new selectability core and its corresponding // NewFocusableCore creates a new focusability core and its corresponding
// control. If your element needs to visually update itself when it's selection // control. If your element needs to visually update itself when it's focus
// state changes (which it should), a callback to draw and push the update can // state changes (which it should), a callback to draw and push the update can
// be specified. // be specified.
func NewSelectableCore ( func NewFocusableCore (
drawSelectionChange func (), drawFocusChange func (),
) ( ) (
core *SelectableCore, core *FocusableCore,
control SelectableCoreControl, control FocusableCoreControl,
) { ) {
core = &SelectableCore { core = &FocusableCore {
drawSelectionChange: drawSelectionChange, drawFocusChange: drawFocusChange,
enabled: true, enabled: true,
} }
control = SelectableCoreControl { core: core } control = FocusableCoreControl { core: core }
return return
} }
// Selected returns whether or not this element is currently selected. // Focused returns whether or not this element is currently focused.
func (core *SelectableCore) Selected () (selected bool) { func (core *FocusableCore) Focused () (focused bool) {
return core.selected return core.focused
} }
// Select selects this element, if its parent element grants the request. // Focus focuses this element, if its parent element grants the request.
func (core *SelectableCore) Select () { func (core *FocusableCore) Focus () {
if !core.enabled { return } if !core.enabled { return }
if core.onSelectionRequest != nil { if core.onFocusRequest != nil {
core.onSelectionRequest() core.onFocusRequest()
} }
} }
// HandleSelection causes this element to mark itself as selected, if it can // HandleFocus causes this element to mark itself as focused, if it can
// currently be. Otherwise, it will return false and do nothing. // currently be. Otherwise, it will return false and do nothing.
func (core *SelectableCore) HandleSelection ( func (core *FocusableCore) HandleFocus (
direction tomo.SelectionDirection, direction tomo.KeynavDirection,
) ( ) (
accepted bool, accepted bool,
) { ) {
direction = direction.Canon() direction = direction.Canon()
if !core.enabled { return false } if !core.enabled { return false }
if core.selected && direction != tomo.SelectionDirectionNeutral { if core.focused && direction != tomo.KeynavDirectionNeutral {
return false return false
} }
core.selected = true core.focused = true
if core.drawSelectionChange != nil { core.drawSelectionChange() } if core.drawFocusChange != nil { core.drawFocusChange() }
return true return true
} }
// HandleDeselection causes this element to mark itself as deselected. // HandleUnfocus causes this element to mark itself as unfocused.
func (core *SelectableCore) HandleDeselection () { func (core *FocusableCore) HandleUnfocus () {
core.selected = false core.focused = false
if core.drawSelectionChange != nil { core.drawSelectionChange() } if core.drawFocusChange != nil { core.drawFocusChange() }
} }
// OnSelectionRequest sets a function to be called when this element // OnFocusRequest sets a function to be called when this element
// wants its parent element to select it. Parent elements should return // wants its parent element to focus it. Parent elements should return
// true if the request was granted, and false if it was not. // true if the request was granted, and false if it was not.
func (core *SelectableCore) OnSelectionRequest (callback func () (granted bool)) { func (core *FocusableCore) OnFocusRequest (callback func () (granted bool)) {
core.onSelectionRequest = callback core.onFocusRequest = callback
} }
// OnSelectionMotionRequest sets a function to be called when this // OnFocusMotionRequest sets a function to be called when this
// element wants its parent element to select the element behind or in // element wants its parent element to focus the element behind or in
// front of it, depending on the specified direction. Parent elements // front of it, depending on the specified direction. Parent elements
// should return true if the request was granted, and false if it was // should return true if the request was granted, and false if it was
// not. // not.
func (core *SelectableCore) OnSelectionMotionRequest ( func (core *FocusableCore) OnFocusMotionRequest (
callback func (direction tomo.SelectionDirection) (granted bool), callback func (direction tomo.KeynavDirection) (granted bool),
) { ) {
core.onSelectionMotionRequest = callback core.onFocusMotionRequest = callback
} }
// Enabled returns whether or not the element is enabled. // Enabled returns whether or not the element is enabled.
func (core *SelectableCore) Enabled () (enabled bool) { func (core *FocusableCore) Enabled () (enabled bool) {
return core.enabled return core.enabled
} }
// SelectableCoreControl is a struct that can be used to exert control over a // FocusableCoreControl is a struct that can be used to exert control over a
// selectability core. It must not be directly embedded into an element, but // focusability core. It must not be directly embedded into an element, but
// instead kept as a private member. When a SelectableCore struct is created, a // instead kept as a private member. When a FocusableCore struct is created, a
// corresponding SelectableCoreControl struct is linked to it and returned // corresponding FocusableCoreControl struct is linked to it and returned
// alongside it. // alongside it.
type SelectableCoreControl struct { type FocusableCoreControl struct {
core *SelectableCore core *FocusableCore
} }
// SetEnabled sets whether the selectability core is enabled. If the state // SetEnabled sets whether the focusability core is enabled. If the state
// changes, this will call drawSelectionChange. // changes, this will call drawFocusChange.
func (control SelectableCoreControl) SetEnabled (enabled bool) { func (control FocusableCoreControl) SetEnabled (enabled bool) {
if control.core.enabled == enabled { return } if control.core.enabled == enabled { return }
control.core.enabled = enabled control.core.enabled = enabled
if !enabled { control.core.selected = false } if !enabled { control.core.focused = false }
if control.core.drawSelectionChange != nil { if control.core.drawFocusChange != nil {
control.core.drawSelectionChange() control.core.drawFocusChange()
} }
} }

View File

@ -26,3 +26,9 @@ var listEntryPattern = artist.NewMultiBordered (
var onListEntryPattern = artist.NewMultiBordered ( var onListEntryPattern = artist.NewMultiBordered (
artist.Stroke { Pattern: artist.NewUniform(hex(0x6e8079FF)) }) artist.Stroke { Pattern: artist.NewUniform(hex(0x6e8079FF)) })
var selectedListEntryPattern = artist.NewMultiBordered (
artist.Stroke { Pattern: artist.NewUniform(hex(0x999C99FF)) })
var selectedOnListEntryPattern = artist.NewMultiBordered (
artist.Stroke { Pattern: artist.NewUniform(hex(0x6e8079FF)) })

View File

@ -31,9 +31,9 @@ type PatternState struct {
// question is capable of being toggled. // question is capable of being toggled.
On bool On bool
// Selected should be set to true if the element that is using this // Focused should be set to true if the element that is using this
// pattern is currently selected. // pattern is currently focused.
Selected bool Focused bool
// Pressed should be set to true if the element that is using this // Pressed should be set to true if the element that is using this
// pattern is being pressed down by the mouse. This is only necessary if // pattern is being pressed down by the mouse. This is only necessary if
@ -123,7 +123,7 @@ func InputPattern (state PatternState) (pattern artist.Pattern, inset Inset) {
if state.Disabled { if state.Disabled {
return disabledInputPattern, Inset { 1, 1, 1, 1 } return disabledInputPattern, Inset { 1, 1, 1, 1 }
} else { } else {
if state.Selected { if state.Focused {
return selectedInputPattern, Inset { 1, 1, 1, 1 } return selectedInputPattern, Inset { 1, 1, 1, 1 }
} else { } else {
return inputPattern, Inset { 1, 1, 1, 1 } return inputPattern, Inset { 1, 1, 1, 1 }
@ -133,7 +133,7 @@ func InputPattern (state PatternState) (pattern artist.Pattern, inset Inset) {
// ListPattern returns a background pattern for a list of things. // ListPattern returns a background pattern for a list of things.
func ListPattern (state PatternState) (pattern artist.Pattern, inset Inset) { func ListPattern (state PatternState) (pattern artist.Pattern, inset Inset) {
if state.Selected { if state.Focused {
return selectedListPattern, Inset { 4, 4, 4, 4 } return selectedListPattern, Inset { 4, 4, 4, 4 }
} else { } else {
return listPattern, Inset { 4, 4, 4, 4 } return listPattern, Inset { 4, 4, 4, 4 }
@ -142,10 +142,18 @@ func ListPattern (state PatternState) (pattern artist.Pattern, inset Inset) {
// ItemPattern returns a background pattern for a list item. // ItemPattern returns a background pattern for a list item.
func ItemPattern (state PatternState) (pattern artist.Pattern, inset Inset) { func ItemPattern (state PatternState) (pattern artist.Pattern, inset Inset) {
if state.On { if state.Focused {
return onListEntryPattern, Inset { 4, 4, 4, 4 } if state.On {
return selectedOnListEntryPattern, Inset { 4, 4, 4, 4 }
} else {
return selectedListEntryPattern, Inset { 4, 4, 4, 4 }
}
} else { } else {
return listEntryPattern, Inset { 4, 4, 4, 4 } if state.On {
return onListEntryPattern, Inset { 4, 4, 4, 4 }
} else {
return listEntryPattern, Inset { 4, 4, 4, 4 }
}
} }
} }
@ -155,14 +163,14 @@ func ButtonPattern (state PatternState) (pattern artist.Pattern, inset Inset) {
return disabledButtonPattern, Inset { 1, 1, 1, 1 } return disabledButtonPattern, Inset { 1, 1, 1, 1 }
} else { } else {
if state.Pressed { if state.Pressed {
if state.Selected { if state.Focused {
return pressedSelectedButtonPattern, Inset { return pressedSelectedButtonPattern, Inset {
2, 0, 0, 2 } 2, 0, 0, 2 }
} else { } else {
return pressedButtonPattern, Inset { 2, 0, 0, 2 } return pressedButtonPattern, Inset { 2, 0, 0, 2 }
} }
} else { } else {
if state.Selected { if state.Focused {
return selectedButtonPattern, Inset { 1, 1, 1, 1 } return selectedButtonPattern, Inset { 1, 1, 1, 1 }
} else { } else {
return buttonPattern, Inset { 1, 1, 1, 1 } return buttonPattern, Inset { 1, 1, 1, 1 }
@ -187,7 +195,7 @@ func HandlePattern (state PatternState) (pattern artist.Pattern, inset Inset) {
if state.Disabled { if state.Disabled {
return disabledScrollBarPattern, Inset { 1, 1, 1, 1 } return disabledScrollBarPattern, Inset { 1, 1, 1, 1 }
} else { } else {
if state.Selected { if state.Focused {
if state.Pressed { if state.Pressed {
return pressedSelectedScrollBarPattern, Inset { 1, 1, 1, 1 } return pressedSelectedScrollBarPattern, Inset { 1, 1, 1, 1 }
} else { } else {
@ -212,7 +220,7 @@ func SunkenPattern (state PatternState) (pattern artist.Pattern, inset Inset) {
// RaisedPattern returns a general purpose pattern that is raised up out of the // RaisedPattern returns a general purpose pattern that is raised up out of the
// background. // background.
func RaisedPattern (state PatternState) (pattern artist.Pattern, inset Inset) { func RaisedPattern (state PatternState) (pattern artist.Pattern, inset Inset) {
if state.Selected { if state.Focused {
return selectedRaisedPattern, Inset { 1, 1, 1, 1 } return selectedRaisedPattern, Inset { 1, 1, 1, 1 }
} else { } else {
return raisedPattern, Inset { 1, 1, 1, 1 } return raisedPattern, Inset { 1, 1, 1, 1 }