Hocus focus

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

View File

@@ -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