Hocus focus
This commit is contained in:
@@ -11,9 +11,9 @@ var buttonCase = theme.C("basic", "button")
|
||||
// Button is a clickable button.
|
||||
type Button struct {
|
||||
*core.Core
|
||||
*core.SelectableCore
|
||||
*core.FocusableCore
|
||||
core core.CoreControl
|
||||
selectableControl core.SelectableCoreControl
|
||||
focusableControl core.FocusableCoreControl
|
||||
drawer artist.TextDrawer
|
||||
|
||||
pressed bool
|
||||
@@ -26,8 +26,8 @@ type Button struct {
|
||||
func NewButton (text string) (element *Button) {
|
||||
element = &Button { }
|
||||
element.Core, element.core = core.NewCore(element)
|
||||
element.SelectableCore,
|
||||
element.selectableControl = core.NewSelectableCore (func () {
|
||||
element.FocusableCore,
|
||||
element.focusableControl = core.NewFocusableCore (func () {
|
||||
if element.core.HasImage () {
|
||||
element.draw()
|
||||
element.core.DamageAll()
|
||||
@@ -45,7 +45,7 @@ func (element *Button) Resize (width, height int) {
|
||||
|
||||
func (element *Button) HandleMouseDown (x, y int, button tomo.Button) {
|
||||
if !element.Enabled() { return }
|
||||
if !element.Selected() { element.Select() }
|
||||
if !element.Focused() { element.Focus() }
|
||||
if button != tomo.ButtonLeft { return }
|
||||
element.pressed = true
|
||||
if element.core.HasImage() {
|
||||
@@ -106,7 +106,7 @@ func (element *Button) OnClick (callback func ()) {
|
||||
|
||||
// SetEnabled sets whether this button can be clicked or not.
|
||||
func (element *Button) SetEnabled (enabled bool) {
|
||||
element.selectableControl.SetEnabled(enabled)
|
||||
element.focusableControl.SetEnabled(enabled)
|
||||
}
|
||||
|
||||
// SetText sets the button's label text.
|
||||
@@ -131,7 +131,7 @@ func (element *Button) draw () {
|
||||
pattern, inset := theme.ButtonPattern(theme.PatternState {
|
||||
Case: buttonCase,
|
||||
Disabled: !element.Enabled(),
|
||||
Selected: element.Selected(),
|
||||
Focused: element.Focused(),
|
||||
Pressed: element.pressed,
|
||||
})
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ var checkboxCase = theme.C("basic", "checkbox")
|
||||
// Checkbox is a toggle-able checkbox with a label.
|
||||
type Checkbox struct {
|
||||
*core.Core
|
||||
*core.SelectableCore
|
||||
*core.FocusableCore
|
||||
core core.CoreControl
|
||||
selectableControl core.SelectableCoreControl
|
||||
focusableControl core.FocusableCoreControl
|
||||
drawer artist.TextDrawer
|
||||
|
||||
pressed bool
|
||||
@@ -27,8 +27,8 @@ type Checkbox struct {
|
||||
func NewCheckbox (text string, checked bool) (element *Checkbox) {
|
||||
element = &Checkbox { checked: checked }
|
||||
element.Core, element.core = core.NewCore(element)
|
||||
element.SelectableCore,
|
||||
element.selectableControl = core.NewSelectableCore (func () {
|
||||
element.FocusableCore,
|
||||
element.focusableControl = core.NewFocusableCore (func () {
|
||||
if element.core.HasImage () {
|
||||
element.draw()
|
||||
element.core.DamageAll()
|
||||
@@ -47,7 +47,7 @@ func (element *Checkbox) Resize (width, height int) {
|
||||
|
||||
func (element *Checkbox) HandleMouseDown (x, y int, button tomo.Button) {
|
||||
if !element.Enabled() { return }
|
||||
element.Select()
|
||||
element.Focus()
|
||||
element.pressed = true
|
||||
if element.core.HasImage() {
|
||||
element.draw()
|
||||
@@ -113,7 +113,7 @@ func (element *Checkbox) Value () (checked bool) {
|
||||
|
||||
// SetEnabled sets whether this checkbox can be toggled or not.
|
||||
func (element *Checkbox) SetEnabled (enabled bool) {
|
||||
element.selectableControl.SetEnabled(enabled)
|
||||
element.focusableControl.SetEnabled(enabled)
|
||||
}
|
||||
|
||||
// SetText sets the checkbox's label text.
|
||||
@@ -150,7 +150,7 @@ func (element *Checkbox) draw () {
|
||||
pattern, inset := theme.ButtonPattern(theme.PatternState {
|
||||
Case: checkboxCase,
|
||||
Disabled: !element.Enabled(),
|
||||
Selected: element.Selected(),
|
||||
Focused: element.Focused(),
|
||||
Pressed: element.pressed,
|
||||
})
|
||||
artist.FillRectangle(element.core, pattern, boxBounds)
|
||||
|
||||
@@ -14,16 +14,16 @@ type Container struct {
|
||||
*core.Core
|
||||
core core.CoreControl
|
||||
|
||||
layout tomo.Layout
|
||||
children []tomo.LayoutEntry
|
||||
drags [10]tomo.MouseTarget
|
||||
warping bool
|
||||
selected bool
|
||||
selectable bool
|
||||
flexible bool
|
||||
layout tomo.Layout
|
||||
children []tomo.LayoutEntry
|
||||
drags [10]tomo.MouseTarget
|
||||
warping bool
|
||||
focused bool
|
||||
focusable bool
|
||||
flexible bool
|
||||
|
||||
onSelectionRequest func () (granted bool)
|
||||
onSelectionMotionRequest func (tomo.SelectionDirection) (granted bool)
|
||||
onFocusRequest func () (granted bool)
|
||||
onFocusMotionRequest func (tomo.KeynavDirection) (granted bool)
|
||||
onFlexibleHeightChange func ()
|
||||
}
|
||||
|
||||
@@ -57,14 +57,14 @@ func (element *Container) Adopt (child tomo.Element, expand bool) {
|
||||
if child0, ok := child.(tomo.Flexible); ok {
|
||||
child0.OnFlexibleHeightChange(element.updateMinimumSize)
|
||||
}
|
||||
if child0, ok := child.(tomo.Selectable); ok {
|
||||
child0.OnSelectionRequest (func () (granted bool) {
|
||||
return element.childSelectionRequestCallback(child0)
|
||||
if child0, ok := child.(tomo.Focusable); ok {
|
||||
child0.OnFocusRequest (func () (granted bool) {
|
||||
return element.childFocusRequestCallback(child0)
|
||||
})
|
||||
child0.OnSelectionMotionRequest (
|
||||
func (direction tomo.SelectionDirection) (granted bool) {
|
||||
if element.onSelectionMotionRequest == nil { return }
|
||||
return element.onSelectionMotionRequest(direction)
|
||||
child0.OnFocusMotionRequest (
|
||||
func (direction tomo.KeynavDirection) (granted bool) {
|
||||
if element.onFocusMotionRequest == nil { return }
|
||||
return element.onFocusMotionRequest(direction)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -132,11 +132,11 @@ func (element *Container) Disown (child tomo.Element) {
|
||||
func (element *Container) clearChildEventHandlers (child tomo.Element) {
|
||||
child.OnDamage(nil)
|
||||
child.OnMinimumSizeChange(nil)
|
||||
if child0, ok := child.(tomo.Selectable); ok {
|
||||
child0.OnSelectionRequest(nil)
|
||||
child0.OnSelectionMotionRequest(nil)
|
||||
if child0.Selected() {
|
||||
child0.HandleDeselection()
|
||||
if child0, ok := child.(tomo.Focusable); ok {
|
||||
child0.OnFocusRequest(nil)
|
||||
child0.OnFocusMotionRequest(nil)
|
||||
if child0.Focused() {
|
||||
child0.HandleUnfocus()
|
||||
}
|
||||
}
|
||||
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) {
|
||||
element.forSelected (func (child tomo.Selectable) bool {
|
||||
element.forFocused (func (child tomo.Focusable) bool {
|
||||
child0, handlesKeyboard := child.(tomo.KeyboardTarget)
|
||||
if handlesKeyboard {
|
||||
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) {
|
||||
element.forSelected (func (child tomo.Selectable) bool {
|
||||
element.forFocused (func (child tomo.Focusable) bool {
|
||||
child0, handlesKeyboard := child.(tomo.KeyboardTarget)
|
||||
if handlesKeyboard {
|
||||
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) {
|
||||
return element.layout.FlexibleHeightFor(element.children, width)
|
||||
}
|
||||
@@ -362,37 +265,134 @@ func (element *Container) OnFlexibleHeightChange (callback func ()) {
|
||||
element.onFlexibleHeightChange = callback
|
||||
}
|
||||
|
||||
func (element *Container) HandleDeselection () {
|
||||
element.selected = false
|
||||
element.forSelected (func (child tomo.Selectable) bool {
|
||||
child.HandleDeselection()
|
||||
func (element *Container) Focused () (focused bool) {
|
||||
return element.focused
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
func (element *Container) OnSelectionRequest (callback func () (granted bool)) {
|
||||
element.onSelectionRequest = callback
|
||||
func (element *Container) OnFocusRequest (callback func () (granted bool)) {
|
||||
element.onFocusRequest = callback
|
||||
}
|
||||
|
||||
func (element *Container) OnSelectionMotionRequest (
|
||||
callback func (direction tomo.SelectionDirection) (granted bool),
|
||||
func (element *Container) OnFocusMotionRequest (
|
||||
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 {
|
||||
child, selectable := entry.Element.(tomo.Selectable)
|
||||
if selectable && child.Selected() {
|
||||
child, focusable := entry.Element.(tomo.Focusable)
|
||||
if focusable && child.Focused() {
|
||||
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 {
|
||||
child, selectable := entry.Element.(tomo.Selectable)
|
||||
if selectable {
|
||||
child, focusable := entry.Element.(tomo.Focusable)
|
||||
if focusable {
|
||||
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) {
|
||||
for _, entry := range element.children {
|
||||
child, selectable := entry.Element.(tomo.Flexible)
|
||||
if selectable {
|
||||
child, flexible := entry.Element.(tomo.Flexible)
|
||||
if flexible {
|
||||
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 -- {
|
||||
child, selectable := element.children[index].Element.(tomo.Selectable)
|
||||
if selectable {
|
||||
child, focusable := element.children[index].Element.(tomo.Focusable)
|
||||
if focusable {
|
||||
if !callback(child) { break }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (element *Container) firstSelected () (index int) {
|
||||
func (element *Container) firstFocused () (index int) {
|
||||
for currentIndex, entry := range element.children {
|
||||
child, selectable := entry.Element.(tomo.Selectable)
|
||||
if selectable && child.Selected() {
|
||||
child, focusable := entry.Element.(tomo.Focusable)
|
||||
if focusable && child.Focused() {
|
||||
return currentIndex
|
||||
}
|
||||
}
|
||||
@@ -427,9 +427,9 @@ func (element *Container) firstSelected () (index int) {
|
||||
}
|
||||
|
||||
func (element *Container) reflectChildProperties () {
|
||||
element.selectable = false
|
||||
element.forSelectable (func (tomo.Selectable) bool {
|
||||
element.selectable = true
|
||||
element.focusable = false
|
||||
element.forFocusable (func (tomo.Focusable) bool {
|
||||
element.focusable = true
|
||||
return false
|
||||
})
|
||||
element.flexible = false
|
||||
@@ -437,22 +437,22 @@ func (element *Container) reflectChildProperties () {
|
||||
element.flexible = true
|
||||
return false
|
||||
})
|
||||
if !element.selectable {
|
||||
element.selected = false
|
||||
if !element.focusable {
|
||||
element.focused = false
|
||||
}
|
||||
}
|
||||
|
||||
func (element *Container) childSelectionRequestCallback (
|
||||
child tomo.Selectable,
|
||||
func (element *Container) childFocusRequestCallback (
|
||||
child tomo.Focusable,
|
||||
) (
|
||||
granted bool,
|
||||
) {
|
||||
if element.onSelectionRequest != nil && element.onSelectionRequest() {
|
||||
element.forSelected (func (child tomo.Selectable) bool {
|
||||
child.HandleDeselection()
|
||||
if element.onFocusRequest != nil && element.onFocusRequest() {
|
||||
element.forFocused (func (child tomo.Focusable) bool {
|
||||
child.HandleUnfocus()
|
||||
return true
|
||||
})
|
||||
child.HandleSelection(tomo.SelectionDirectionNeutral)
|
||||
child.HandleFocus(tomo.KeynavDirectionNeutral)
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
|
||||
@@ -12,9 +12,9 @@ var listCase = theme.C("basic", "list")
|
||||
// List is an element that contains several objects that a user can select.
|
||||
type List struct {
|
||||
*core.Core
|
||||
*core.SelectableCore
|
||||
*core.FocusableCore
|
||||
core core.CoreControl
|
||||
selectableControl core.SelectableCoreControl
|
||||
focusableControl core.FocusableCoreControl
|
||||
|
||||
pressed bool
|
||||
|
||||
@@ -34,8 +34,8 @@ type List struct {
|
||||
func NewList (entries ...ListEntry) (element *List) {
|
||||
element = &List { selectedEntry: -1 }
|
||||
element.Core, element.core = core.NewCore(element)
|
||||
element.SelectableCore,
|
||||
element.selectableControl = core.NewSelectableCore (func () {
|
||||
element.FocusableCore,
|
||||
element.focusableControl = core.NewFocusableCore (func () {
|
||||
if element.core.HasImage () {
|
||||
element.draw()
|
||||
element.core.DamageAll()
|
||||
@@ -78,7 +78,7 @@ func (element *List) Collapse (width, height int) {
|
||||
|
||||
func (element *List) HandleMouseDown (x, y int, button tomo.Button) {
|
||||
if !element.Enabled() { return }
|
||||
if !element.Selected() { element.Select() }
|
||||
if !element.Focused() { element.Focus() }
|
||||
if button != tomo.ButtonLeft { return }
|
||||
element.pressed = true
|
||||
if element.selectUnderMouse(x, y) && element.core.HasImage() {
|
||||
@@ -377,7 +377,7 @@ func (element *List) draw () {
|
||||
pattern, inset := theme.ListPattern(theme.PatternState {
|
||||
Case: listCase,
|
||||
Disabled: !element.Enabled(),
|
||||
Selected: element.Selected(),
|
||||
Focused: element.Focused(),
|
||||
})
|
||||
artist.FillRectangle(element.core, pattern, bounds)
|
||||
|
||||
@@ -394,6 +394,6 @@ func (element *List) draw () {
|
||||
if entryPosition.Y > bounds.Max.Y { break }
|
||||
entry.Draw (
|
||||
innerCanvas, entryPosition,
|
||||
element.Selected(), element.selectedEntry == index)
|
||||
element.Focused(), element.selectedEntry == index)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,14 +55,14 @@ func (entry *ListEntry) updateBounds () {
|
||||
func (entry *ListEntry) Draw (
|
||||
destination tomo.Canvas,
|
||||
offset image.Point,
|
||||
selected bool,
|
||||
focused bool,
|
||||
on bool,
|
||||
) (
|
||||
updatedRegion image.Rectangle,
|
||||
) {
|
||||
pattern, _ := theme.ItemPattern(theme.PatternState {
|
||||
Case: listEntryCase,
|
||||
Selected: selected,
|
||||
Focused: focused,
|
||||
On: on,
|
||||
})
|
||||
artist.FillRectangle (
|
||||
@@ -71,7 +71,7 @@ func (entry *ListEntry) Draw (
|
||||
entry.Bounds().Add(offset))
|
||||
foreground, _ := theme.ForegroundPattern (theme.PatternState {
|
||||
Case: listEntryCase,
|
||||
Selected: selected,
|
||||
Focused: focused,
|
||||
On: on,
|
||||
})
|
||||
return entry.drawer.Draw (
|
||||
|
||||
@@ -15,7 +15,7 @@ var scrollBarVerticalCase = theme.C("basic", "scrollBarVertical")
|
||||
type ScrollContainer struct {
|
||||
*core.Core
|
||||
core core.CoreControl
|
||||
selected bool
|
||||
focused bool
|
||||
|
||||
child tomo.Scrollable
|
||||
childWidth, childHeight int
|
||||
@@ -40,8 +40,8 @@ type ScrollContainer struct {
|
||||
bar image.Rectangle
|
||||
}
|
||||
|
||||
onSelectionRequest func () (granted bool)
|
||||
onSelectionMotionRequest func (tomo.SelectionDirection) (granted bool)
|
||||
onFocusRequest func () (granted bool)
|
||||
onFocusMotionRequest func (tomo.KeynavDirection) (granted bool)
|
||||
}
|
||||
|
||||
// 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.OnMinimumSizeChange(element.updateMinimumSize)
|
||||
child.OnScrollBoundsChange(element.childScrollBoundsChangeCallback)
|
||||
if newChild, ok := child.(tomo.Selectable); ok {
|
||||
newChild.OnSelectionRequest (
|
||||
element.childSelectionRequestCallback)
|
||||
newChild.OnSelectionMotionRequest (
|
||||
element.childSelectionMotionRequestCallback)
|
||||
if newChild, ok := child.(tomo.Focusable); ok {
|
||||
newChild.OnFocusRequest (
|
||||
element.childFocusRequestCallback)
|
||||
newChild.OnFocusMotionRequest (
|
||||
element.childFocusMotionRequestCallback)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
func (element *ScrollContainer) Selected () (selected bool) {
|
||||
return element.selected
|
||||
func (element *ScrollContainer) Focused () (focused bool) {
|
||||
return element.focused
|
||||
}
|
||||
|
||||
func (element *ScrollContainer) Select () {
|
||||
if element.onSelectionRequest != nil {
|
||||
element.onSelectionRequest()
|
||||
func (element *ScrollContainer) Focus () {
|
||||
if element.onFocusRequest != nil {
|
||||
element.onFocusRequest()
|
||||
}
|
||||
}
|
||||
|
||||
func (element *ScrollContainer) HandleSelection (
|
||||
direction tomo.SelectionDirection,
|
||||
func (element *ScrollContainer) HandleFocus (
|
||||
direction tomo.KeynavDirection,
|
||||
) (
|
||||
accepted bool,
|
||||
) {
|
||||
if child, ok := element.child.(tomo.Selectable); ok {
|
||||
element.selected = true
|
||||
return child.HandleSelection(direction)
|
||||
if child, ok := element.child.(tomo.Focusable); ok {
|
||||
element.focused = true
|
||||
return child.HandleFocus(direction)
|
||||
} else {
|
||||
element.selected = false
|
||||
element.focused = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (element *ScrollContainer) HandleDeselection () {
|
||||
if child, ok := element.child.(tomo.Selectable); ok {
|
||||
child.HandleDeselection()
|
||||
func (element *ScrollContainer) HandleUnfocus () {
|
||||
if child, ok := element.child.(tomo.Focusable); ok {
|
||||
child.HandleUnfocus()
|
||||
}
|
||||
element.selected = false
|
||||
element.focused = false
|
||||
}
|
||||
|
||||
func (element *ScrollContainer) OnSelectionRequest (callback func () (granted bool)) {
|
||||
element.onSelectionRequest = callback
|
||||
func (element *ScrollContainer) OnFocusRequest (callback func () (granted bool)) {
|
||||
element.onFocusRequest = callback
|
||||
}
|
||||
|
||||
func (element *ScrollContainer) OnSelectionMotionRequest (
|
||||
callback func (direction tomo.SelectionDirection) (granted bool),
|
||||
func (element *ScrollContainer) OnFocusMotionRequest (
|
||||
callback func (direction tomo.KeynavDirection) (granted bool),
|
||||
) {
|
||||
element.onSelectionMotionRequest = callback
|
||||
element.onFocusMotionRequest = callback
|
||||
}
|
||||
|
||||
func (element *ScrollContainer) childDamageCallback (region tomo.Canvas) {
|
||||
element.core.DamageRegion(artist.Paste(element, region, image.Point { }))
|
||||
}
|
||||
|
||||
func (element *ScrollContainer) childSelectionRequestCallback () (granted bool) {
|
||||
child, ok := element.child.(tomo.Selectable)
|
||||
func (element *ScrollContainer) childFocusRequestCallback () (granted bool) {
|
||||
child, ok := element.child.(tomo.Focusable)
|
||||
if !ok { return false }
|
||||
if element.onSelectionRequest != nil && element.onSelectionRequest() {
|
||||
child.HandleSelection(tomo.SelectionDirectionNeutral)
|
||||
if element.onFocusRequest != nil && element.onFocusRequest() {
|
||||
child.HandleFocus(tomo.KeynavDirectionNeutral)
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (element *ScrollContainer) childSelectionMotionRequestCallback (
|
||||
direction tomo.SelectionDirection,
|
||||
func (element *ScrollContainer) childFocusMotionRequestCallback (
|
||||
direction tomo.KeynavDirection,
|
||||
) (
|
||||
granted bool,
|
||||
) {
|
||||
if element.onSelectionMotionRequest == nil {
|
||||
return
|
||||
}
|
||||
return element.onSelectionMotionRequest(direction)
|
||||
if element.onFocusMotionRequest == nil { return }
|
||||
return element.onFocusMotionRequest(direction)
|
||||
}
|
||||
|
||||
func (element *ScrollContainer) clearChildEventHandlers (child tomo.Scrollable) {
|
||||
child.OnDamage(nil)
|
||||
child.OnMinimumSizeChange(nil)
|
||||
child.OnScrollBoundsChange(nil)
|
||||
if child0, ok := child.(tomo.Selectable); ok {
|
||||
child0.OnSelectionRequest(nil)
|
||||
child0.OnSelectionMotionRequest(nil)
|
||||
if child0.Selected() {
|
||||
child0.HandleDeselection()
|
||||
if child0, ok := child.(tomo.Focusable); ok {
|
||||
child0.OnFocusRequest(nil)
|
||||
child0.OnFocusMotionRequest(nil)
|
||||
if child0.Focused() {
|
||||
child0.HandleUnfocus()
|
||||
}
|
||||
}
|
||||
if child0, ok := child.(tomo.Flexible); ok {
|
||||
|
||||
@@ -12,9 +12,9 @@ var switchCase = theme.C("basic", "switch")
|
||||
// functionally identical to Checkbox, but plays a different semantic role.
|
||||
type Switch struct {
|
||||
*core.Core
|
||||
*core.SelectableCore
|
||||
*core.FocusableCore
|
||||
core core.CoreControl
|
||||
selectableControl core.SelectableCoreControl
|
||||
focusableControl core.FocusableCoreControl
|
||||
drawer artist.TextDrawer
|
||||
|
||||
pressed bool
|
||||
@@ -28,8 +28,8 @@ type Switch struct {
|
||||
func NewSwitch (text string, on bool) (element *Switch) {
|
||||
element = &Switch { checked: on, text: text }
|
||||
element.Core, element.core = core.NewCore(element)
|
||||
element.SelectableCore,
|
||||
element.selectableControl = core.NewSelectableCore (func () {
|
||||
element.FocusableCore,
|
||||
element.focusableControl = core.NewFocusableCore (func () {
|
||||
if element.core.HasImage () {
|
||||
element.draw()
|
||||
element.core.DamageAll()
|
||||
@@ -49,7 +49,7 @@ func (element *Switch) Resize (width, height int) {
|
||||
|
||||
func (element *Switch) HandleMouseDown (x, y int, button tomo.Button) {
|
||||
if !element.Enabled() { return }
|
||||
element.Select()
|
||||
element.Focus()
|
||||
element.pressed = true
|
||||
if element.core.HasImage() {
|
||||
element.draw()
|
||||
@@ -115,7 +115,7 @@ func (element *Switch) Value () (on bool) {
|
||||
|
||||
// SetEnabled sets whether this switch can be flipped or not.
|
||||
func (element *Switch) SetEnabled (enabled bool) {
|
||||
element.selectableControl.SetEnabled(enabled)
|
||||
element.focusableControl.SetEnabled(enabled)
|
||||
}
|
||||
|
||||
// SetText sets the checkbox's label text.
|
||||
@@ -171,7 +171,7 @@ func (element *Switch) draw () {
|
||||
gutterPattern, _ := theme.GutterPattern(theme.PatternState {
|
||||
Case: switchCase,
|
||||
Disabled: !element.Enabled(),
|
||||
Selected: element.Selected(),
|
||||
Focused: element.Focused(),
|
||||
Pressed: element.pressed,
|
||||
})
|
||||
artist.FillRectangle(element.core, gutterPattern, gutterBounds)
|
||||
@@ -179,7 +179,7 @@ func (element *Switch) draw () {
|
||||
handlePattern, _ := theme.HandlePattern(theme.PatternState {
|
||||
Case: switchCase,
|
||||
Disabled: !element.Enabled(),
|
||||
Selected: element.Selected(),
|
||||
Focused: element.Focused(),
|
||||
Pressed: element.pressed,
|
||||
})
|
||||
artist.FillRectangle(element.core, handlePattern, handleBounds)
|
||||
|
||||
@@ -12,9 +12,9 @@ var textBoxCase = theme.C("basic", "textBox")
|
||||
// TextBox is a single-line text input.
|
||||
type TextBox struct {
|
||||
*core.Core
|
||||
*core.SelectableCore
|
||||
*core.FocusableCore
|
||||
core core.CoreControl
|
||||
selectableControl core.SelectableCoreControl
|
||||
focusableControl core.FocusableCoreControl
|
||||
|
||||
cursor int
|
||||
scroll int
|
||||
@@ -35,8 +35,8 @@ type TextBox struct {
|
||||
func NewTextBox (placeholder, value string) (element *TextBox) {
|
||||
element = &TextBox { }
|
||||
element.Core, element.core = core.NewCore(element)
|
||||
element.SelectableCore,
|
||||
element.selectableControl = core.NewSelectableCore (func () {
|
||||
element.FocusableCore,
|
||||
element.focusableControl = core.NewFocusableCore (func () {
|
||||
if element.core.HasImage () {
|
||||
element.draw()
|
||||
element.core.DamageAll()
|
||||
@@ -61,8 +61,8 @@ func (element *TextBox) Resize (width, height int) {
|
||||
}
|
||||
|
||||
func (element *TextBox) HandleMouseDown (x, y int, button tomo.Button) {
|
||||
if !element.Enabled() { return }
|
||||
if !element.Selected() { element.Select() }
|
||||
if !element.Enabled() { return }
|
||||
if !element.Focused() { element.Focus() }
|
||||
}
|
||||
|
||||
func (element *TextBox) HandleMouseUp (x, y int, button tomo.Button) { }
|
||||
@@ -277,13 +277,13 @@ func (element *TextBox) draw () {
|
||||
|
||||
// FIXME: take index into account
|
||||
pattern, inset := theme.InputPattern(theme.PatternState {
|
||||
Case: textBoxCase,
|
||||
Case: textBoxCase,
|
||||
Disabled: !element.Enabled(),
|
||||
Selected: element.Selected(),
|
||||
Focused: element.Focused(),
|
||||
})
|
||||
artist.FillRectangle(element.core, pattern, bounds)
|
||||
|
||||
if len(element.text) == 0 && !element.Selected() {
|
||||
if len(element.text) == 0 && !element.Focused() {
|
||||
// draw placeholder
|
||||
textBounds := element.placeholderDrawer.LayoutBounds()
|
||||
offset := image.Point {
|
||||
@@ -314,7 +314,7 @@ func (element *TextBox) draw () {
|
||||
foreground,
|
||||
offset.Sub(textBounds.Min))
|
||||
|
||||
if element.Selected() {
|
||||
if element.Focused() {
|
||||
// cursor
|
||||
cursorPosition := element.valueDrawer.PositionOf (
|
||||
element.cursor)
|
||||
|
||||
Reference in New Issue
Block a user