From a34e8768aba87cc026e2b78416041512a5251644 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Tue, 14 Mar 2023 18:30:32 -0400 Subject: [PATCH] Redid cores to conform to the new API changes --- elements/container.go | 2 +- elements/core/core.go | 66 +++++++++++++++++---------- elements/core/propagator.go | 90 +++++++++++++++---------------------- elements/core/selectable.go | 42 +++++------------ 4 files changed, 93 insertions(+), 107 deletions(-) diff --git a/elements/container.go b/elements/container.go index 036c87b..1001e7c 100644 --- a/elements/container.go +++ b/elements/container.go @@ -1,4 +1,4 @@ -package element +package elements import "git.tebibyte.media/sashakoshka/tomo/input" diff --git a/elements/core/core.go b/elements/core/core.go index b5c6259..2de8ff2 100644 --- a/elements/core/core.go +++ b/elements/core/core.go @@ -3,31 +3,38 @@ package core import "image" import "image/color" import "git.tebibyte.media/sashakoshka/tomo/canvas" +import "git.tebibyte.media/sashakoshka/tomo/elements" // Core is a struct that implements some core functionality common to most // widgets. It is meant to be embedded directly into a struct. type Core struct { canvas canvas.Canvas bounds image.Rectangle + parent elements.Parent + outer elements.Element metrics struct { minimumWidth int minimumHeight int } - drawSizeChange func () - onMinimumSizeChange func () - onDamage func (region canvas.Canvas) + drawSizeChange func () + onDamage func (region image.Rectangle) } -// NewCore creates a new element core and its corresponding control. +// NewCore creates a new element core and its corresponding control given the +// element that it will be a part of. If outer is nil, this function will return +// nil. func NewCore ( + outer elements.Element, drawSizeChange func (), ) ( core *Core, control CoreControl, ) { + if outer == nil { return } core = &Core { + outer: outer, drawSizeChange: drawSizeChange, } control = CoreControl { core: core } @@ -47,28 +54,32 @@ func (core *Core) MinimumSize () (width, height int) { return core.metrics.minimumWidth, core.metrics.minimumHeight } +// MinimumSize fulfils the tomo.Element interface. This should not need to be +// overridden, unless you want to detect when the element is parented or +// unparented. +func (core *Core) SetParent (parent elements.Parent) { + if core.parent != nil { + panic("core.SetParent: element already has a parent") + } + + core.parent = parent +} + // DrawTo fulfills the tomo.Element interface. This should not need to be // overridden. -func (core *Core) DrawTo (canvas canvas.Canvas, bounds image.Rectangle) { - core.canvas = canvas - core.bounds = bounds +func (core *Core) DrawTo ( + canvas canvas.Canvas, + bounds image.Rectangle, + onDamage func (region image.Rectangle), +) { + core.canvas = canvas + core.bounds = bounds + core.onDamage = onDamage if core.drawSizeChange != nil && core.canvas != nil { core.drawSizeChange() } } -// OnDamage fulfils the tomo.Element interface. This should not need to be -// overridden. -func (core *Core) OnDamage (callback func (region canvas.Canvas)) { - core.onDamage = callback -} - -// OnMinimumSizeChange fulfils the tomo.Element interface. This should not need -// to be overridden. -func (core *Core) OnMinimumSizeChange (callback func ()) { - core.onMinimumSizeChange = callback -} - // CoreControl is a struct that can exert control over a Core struct. It can be // used as a canvas. It must not be directly embedded into an element, but // instead kept as a private member. When a Core struct is created, a @@ -106,6 +117,16 @@ func (control CoreControl) Buffer () (data []color.RGBA, stride int) { return control.core.canvas.Buffer() } +// Parent returns the element's parent. +func (control CoreControl) Parent () elements.Parent { + return control.core.parent +} + +// Outer returns the outer element given when the control was constructed. +func (control CoreControl) Outer () elements.Element { + return control.core.outer +} + // HasImage returns true if the core has an allocated image buffer, and false if // it doesn't. func (control CoreControl) HasImage () (has bool) { @@ -118,8 +139,7 @@ func (control CoreControl) DamageRegion (regions ...image.Rectangle) { if control.core.canvas == nil { return } if control.core.onDamage != nil { for _, region := range regions { - control.core.onDamage ( - canvas.Cut(control.core.canvas, region)) + control.core.onDamage(region) } } } @@ -141,8 +161,8 @@ func (control CoreControl) SetMinimumSize (width, height int) { core.metrics.minimumWidth = width core.metrics.minimumHeight = height - if control.core.onMinimumSizeChange != nil { - control.core.onMinimumSizeChange() + if control.core.parent != nil { + control.core.parent.NotifyMinimumSizeChange(control.core.outer) } } diff --git a/elements/core/propagator.go b/elements/core/propagator.go index 2375f5b..33ba869 100644 --- a/elements/core/propagator.go +++ b/elements/core/propagator.go @@ -6,9 +6,9 @@ import "git.tebibyte.media/sashakoshka/tomo/theme" import "git.tebibyte.media/sashakoshka/tomo/config" import "git.tebibyte.media/sashakoshka/tomo/elements" -// Parent represents an object that can provide access to a list of child +// Container represents an object that can provide access to a list of child // elements. -type Parent interface { +type Container interface { Child (index int) elements.Element CountChildren () int } @@ -18,20 +18,20 @@ type Parent interface { // all of the event handlers. It also implements standard behavior for focus // propagation and keyboard navigation. type Propagator struct { - parent Parent - drags [10]elements.MouseTarget - focused bool - - onFocusRequest func () (granted bool) + core CoreControl + container Container + drags [10]elements.MouseTarget + focused bool } -// NewPropagator creates a new event propagator that uses the specified parent -// to access a list of child elements that will have events propagated to them. -// If parent is nil, the function will return nil. -func NewPropagator (parent Parent) (propagator *Propagator) { - if parent == nil { return nil } +// NewPropagator creates a new event propagator that uses the specified +// container to access a list of child elements that will have events propagated +// to them. If container is nil, the function will return nil. +func NewPropagator (container Container, core CoreControl) (propagator *Propagator) { + if container == nil { return nil } propagator = &Propagator { - parent: parent, + core: core, + container: container, } return } @@ -47,8 +47,12 @@ func (propagator *Propagator) Focused () (focused bool) { // Focus focuses this element, if its parent element grants the // request. func (propagator *Propagator) Focus () { - if propagator.onFocusRequest != nil { - propagator.onFocusRequest() + if propagator.focused == true { return } + parent := propagator.core.Parent() + if parent, ok := parent.(elements.FocusableParent); ok && parent != nil { + propagator.focused = parent.RequestFocus ( + propagator.core.Outer().(elements.Focusable), + input.KeynavDirectionNeutral) } } @@ -86,7 +90,7 @@ func (propagator *Propagator) HandleFocus (direction input.KeynavDirection) (acc // an element is currently focused, so we need to move the // focus in the specified direction firstFocusedChild := - propagator.parent.Child(firstFocused). + propagator.container.Child(firstFocused). (elements.Focusable) // before we move the focus, the currently focused child @@ -99,11 +103,11 @@ func (propagator *Propagator) HandleFocus (direction input.KeynavDirection) (acc // find the previous/next focusable element relative to the // currently focused element, if it exists. for index := firstFocused + int(direction); - index < propagator.parent.CountChildren() && index >= 0; + index < propagator.container.CountChildren() && index >= 0; index += int(direction) { child, focusable := - propagator.parent.Child(index). + propagator.container.Child(index). (elements.Focusable) if focusable && child.HandleFocus(direction) { // we have found one, so we now actually move @@ -128,22 +132,6 @@ func (propagator *Propagator) HandleUnfocus () { propagator.focused = false } -// OnFocusRequest sets a function to be called when this element wants its -// parent element to focus it. Parent elements should return true if the request -// was granted, and false if it was not. If the parent element returns true, the -// element acts as if a HandleFocus call was made with KeynavDirectionNeutral. -func (propagator *Propagator) OnFocusRequest (callback func () (granted bool)) { - propagator.onFocusRequest = callback -} - -// OnFocusMotionRequest sets a function to be called when this element wants its -// parent element to focus the element behind or in front of it, depending on -// the specified direction. Parent elements should return true if the request -// was granted, and false if it was not. -func (propagator *Propagator) OnFocusMotionRequest ( - callback func (direction input.KeynavDirection) (granted bool), -) { } - // HandleKeyDown propogates the keyboard event to the currently selected child. func (propagator *Propagator) HandleKeyDown (key input.Key, modifiers input.Modifiers) { propagator.forFocused (func (child elements.Focusable) bool { @@ -194,18 +182,16 @@ func (propagator *Propagator) HandleMouseUp (x, y int, button input.Button) { func (propagator *Propagator) HandleMouseMove (x, y int) { handled := false for _, child := range propagator.drags { - if child != nil { - child.HandleMouseMove(x, y) + if child, ok := child.(elements.MotionTarget); ok { + child.HandleMotion(x, y) handled = true } } - if handled { - child, handlesMouse := - propagator.childAt(image.Pt(x, y)). - (elements.MouseTarget) - if handlesMouse { - child.HandleMouseMove(x, y) + if !handled { + child := propagator.childAt(image.Pt(x, y)) + if child, ok := child.(elements.MotionTarget); ok { + child.HandleMotion(x, y) } } } @@ -213,11 +199,9 @@ func (propagator *Propagator) HandleMouseMove (x, y int) { // HandleScroll propagates the mouse event to the element under the mouse // pointer. func (propagator *Propagator) HandleMouseScroll (x, y int, deltaX, deltaY float64) { - child, handlesMouse := - propagator.childAt(image.Pt(x, y)). - (elements.MouseTarget) - if handlesMouse { - child.HandleMouseScroll(x, y, deltaX, deltaY) + child := propagator.childAt(image.Pt(x, y)) + if child, ok := child.(elements.ScrollTarget); ok { + child.HandleScroll(x, y, deltaX, deltaY) } } @@ -281,16 +265,16 @@ func (propagator *Propagator) focusLastFocusableElement ( // ----------- Iterator utilities ----------- // func (propagator *Propagator) forChildren (callback func (child elements.Element) bool) { - for index := 0; index < propagator.parent.CountChildren(); index ++ { - child := propagator.parent.Child(index) + for index := 0; index < propagator.container.CountChildren(); index ++ { + child := propagator.container.Child(index) if child == nil { continue } if !callback(child) { break } } } func (propagator *Propagator) forChildrenReverse (callback func (child elements.Element) bool) { - for index := propagator.parent.CountChildren() - 1; index > 0; index -- { - child := propagator.parent.Child(index) + for index := propagator.container.CountChildren() - 1; index > 0; index -- { + child := propagator.container.Child(index) if child == nil { continue } if !callback(child) { break } } @@ -327,8 +311,8 @@ func (propagator *Propagator) forFocusable (callback func (child elements.Focusa } func (propagator *Propagator) firstFocused () int { - for index := 0; index < propagator.parent.CountChildren(); index ++ { - child, focusable := propagator.parent.Child(index).(elements.Focusable) + for index := 0; index < propagator.container.CountChildren(); index ++ { + child, focusable := propagator.container.Child(index).(elements.Focusable) if focusable && child.Focused() { return index } diff --git a/elements/core/selectable.go b/elements/core/selectable.go index 824dcae..c8fc30e 100644 --- a/elements/core/selectable.go +++ b/elements/core/selectable.go @@ -2,15 +2,15 @@ package core // import "runtime/debug" import "git.tebibyte.media/sashakoshka/tomo/input" +import "git.tebibyte.media/sashakoshka/tomo/elements" // FocusableCore is a struct that can be embedded into objects to make them // focusable, giving them the default keynav behavior. type FocusableCore struct { + core CoreControl focused bool enabled bool drawFocusChange func () - onFocusRequest func () (granted bool) - onFocusMotionRequest func(input.KeynavDirection) (granted bool) } // NewFocusableCore creates a new focusability core and its corresponding @@ -18,16 +18,18 @@ type FocusableCore struct { // state changes (which it should), a callback to draw and push the update can // be specified. func NewFocusableCore ( + core CoreControl, drawFocusChange func (), ) ( - core *FocusableCore, + focusable *FocusableCore, control FocusableCoreControl, ) { - core = &FocusableCore { + focusable = &FocusableCore { + core: core, drawFocusChange: drawFocusChange, enabled: true, } - control = FocusableCoreControl { core: core } + control = FocusableCoreControl { core: focusable } return } @@ -39,13 +41,11 @@ func (core *FocusableCore) Focused () (focused bool) { // Focus focuses this element, if its parent element grants the request. func (core *FocusableCore) Focus () { if !core.enabled || core.focused { return } - if core.onFocusRequest != nil { - if core.onFocusRequest() { - core.focused = true - if core.drawFocusChange != nil { - core.drawFocusChange() - } - } + parent := core.core.Parent() + if parent, ok := parent.(elements.FocusableParent); ok && parent != nil { + core.focused = parent.RequestFocus ( + core.core.Outer().(elements.Focusable), + input.KeynavDirectionNeutral) } } @@ -76,24 +76,6 @@ func (core *FocusableCore) HandleUnfocus () { if core.drawFocusChange != nil { core.drawFocusChange() } } -// OnFocusRequest sets a function to be called when this element -// wants its parent element to focus it. Parent elements should return -// true if the request was granted, and false if it was not. -func (core *FocusableCore) OnFocusRequest (callback func () (granted bool)) { - core.onFocusRequest = callback -} - -// OnFocusMotionRequest sets a function to be called when this -// element wants its parent element to focus the element behind or in -// front of it, depending on the specified direction. Parent elements -// should return true if the request was granted, and false if it was -// not. -func (core *FocusableCore) OnFocusMotionRequest ( - callback func (direction input.KeynavDirection) (granted bool), -) { - core.onFocusMotionRequest = callback -} - // Enabled returns whether or not the element is enabled. func (core *FocusableCore) Enabled () (enabled bool) { return core.enabled