From c2a76fcaf6cb176ca3725048a80a410a111bc5fe Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Wed, 11 Jan 2023 15:46:48 -0500 Subject: [PATCH] Added extended selection capabilities to the API --- backends/x/window.go | 9 +---- elements/basic/button.go | 17 ++------ elements/basic/container.go | 12 ++++++ elements/basic/label.go | 4 -- elements/basic/test.go | 10 +---- elements/core/core.go | 78 ++++++++++++++++++++++--------------- theme/theme.go | 4 +- tomo.go | 6 ++- 8 files changed, 72 insertions(+), 68 deletions(-) diff --git a/backends/x/window.go b/backends/x/window.go index 0a436e2..86ecf07 100644 --- a/backends/x/window.go +++ b/backends/x/window.go @@ -78,7 +78,7 @@ func (backend *Backend) NewWindow ( func (window *Window) Adopt (child tomo.Element) { if window.child != nil { child.SetParentHooks (tomo.ParentHooks { }) - if child.Selectable() { child.Handle(tomo.EventDeselect { }) } + if child.Selected() { child.Handle(tomo.EventDeselect { }) } } window.child = child if child != nil { @@ -87,12 +87,7 @@ func (window *Window) Adopt (child tomo.Element) { MinimumSizeChange: window.childMinimumSizeChangeCallback, SelectionRequest: window.childSelectionRequestCallback, }) - // TODO: it is possible for an element to start out as - // selectable, and then become unselectable. it should be - // standard behavior for containers (including windows) to, if - // no children are selected, select the first child on ctrl-tab - // press. we should therefore be able to ask any element if it - // is selected. + if child.Selectable() { child.Handle(tomo.EventSelect { }) } window.resizeChildToFit() } diff --git a/elements/basic/button.go b/elements/basic/button.go index 75364b1..ec6e57f 100644 --- a/elements/basic/button.go +++ b/elements/basic/button.go @@ -12,7 +12,6 @@ type Button struct { pressed bool enabled bool - selected bool onClick func () text string @@ -91,10 +90,10 @@ func (element *Button) Handle (event tomo.Event) { } case tomo.EventSelect: - element.selected = true + element.core.SetSelected(true) case tomo.EventDeselect: - element.selected = false + element.core.SetSelected(false) } return } @@ -103,16 +102,6 @@ func (element *Button) OnClick (callback func ()) { element.onClick = callback } -func (element *Button) AdvanceSelection (direction int) (ok bool) { - wasSelected := element.selected - element.selected = false - if element.core.HasImage() && wasSelected { - element.draw() - element.core.PushAll() - } - return -} - func (element *Button) Select () { element.core.Select() } @@ -150,7 +139,7 @@ func (element *Button) draw () { theme.RaisedProfile ( element.pressed, element.enabled, - element.selected), + element.Selected()), bounds) innerBounds := bounds diff --git a/elements/basic/container.go b/elements/basic/container.go index ed66a22..100bcf0 100644 --- a/elements/basic/container.go +++ b/elements/basic/container.go @@ -13,6 +13,7 @@ type Container struct { layout tomo.Layout children []tomo.LayoutEntry selectable bool + selected bool drags [10]tomo.Element } @@ -183,6 +184,16 @@ func (element *Container) Handle (event tomo.Event) { Y: mouseMoveEvent.Y - childPosition.Y, }) } + + case tomo.EventSelect: + if !element.Selectable() { break } + element.core.SetSelected(true) + + case tomo.EventDeselect: + element.core.SetSelected(false) + // TODO: propogate deselect event to all children who report + // themselves as selected. + } return } @@ -198,6 +209,7 @@ func (element *Container) updateSelectable () { if entry.Selectable() { selectable = true } } element.core.SetSelectable(selectable) + if !selectable { element.selected = false } } func (element *Container) updateMinimumSize () { diff --git a/elements/basic/label.go b/elements/basic/label.go index 4a6bb04..adf7508 100644 --- a/elements/basic/label.go +++ b/elements/basic/label.go @@ -84,10 +84,6 @@ func (element *Label) updateMinimumSize () { } } -func (element *Label) AdvanceSelection (direction int) (ok bool) { - return -} - func (element *Label) draw () { bounds := element.core.Bounds() diff --git a/elements/basic/test.go b/elements/basic/test.go index d5ab164..ea38453 100644 --- a/elements/basic/test.go +++ b/elements/basic/test.go @@ -3,6 +3,7 @@ package basic import "image" import "image/color" import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/theme" import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/elements/core" @@ -33,9 +34,7 @@ func (element *Test) Handle (event tomo.Event) { resizeEvent.Height) artist.Rectangle ( element.core, - artist.NewUniform (color.RGBA { - R: 0x40, G: 0x80, B: 0x90, A: 0xFF, - }), + theme.AccentImage(), artist.NewUniform(color.Black), 1, element.Bounds()) artist.Line ( @@ -46,7 +45,6 @@ func (element *Test) Handle (event tomo.Event) { element.core, artist.NewUniform(color.White), 1, image.Pt(1, resizeEvent.Height - 2), image.Pt(resizeEvent.Width - 2, 1)) - // println(resizeEvent.Width, resizeEvent.Height) case tomo.EventMouseDown: element.drawing = true @@ -78,7 +76,3 @@ func (element *Test) Handle (event tomo.Event) { } return } - -func (element *Test) AdvanceSelection (direction int) (ok bool) { - return -} diff --git a/elements/core/core.go b/elements/core/core.go index 028935a..42362e2 100644 --- a/elements/core/core.go +++ b/elements/core/core.go @@ -16,6 +16,7 @@ type Core struct { } selectable bool + selected bool hooks tomo.ParentHooks } @@ -47,14 +48,22 @@ func (core Core) Bounds () (bounds image.Rectangle) { return } -func (core *Core) SetParentHooks (hooks tomo.ParentHooks) { - core.hooks = hooks -} - func (core Core) Selectable () (selectable bool) { return core.selectable } +func (core Core) Selected () (selected bool) { + return core.selected +} + +func (core Core) AdvanceSelection (direction int) (ok bool) { + return +} + +func (core *Core) SetParentHooks (hooks tomo.ParentHooks) { + core.hooks = hooks +} + func (core Core) MinimumSize () (width, height int) { return core.metrics.minimumWidth, core.metrics.minimumHeight } @@ -76,6 +85,18 @@ func (control CoreControl) Select () { control.core.hooks.RunSelectionRequest() } +func (control CoreControl) SetSelected (selected bool) { + if !control.core.selectable { return } + control.core.selected = selected +} + +func (control CoreControl) SetSelectable (selectable bool) { + if control.core.selectable == selectable { return } + control.core.selectable = selectable + if !selectable { control.core.selected = false } + control.core.hooks.RunSelectabilityChange(selectable) +} + func (control CoreControl) PushRegion (bounds image.Rectangle) { control.core.hooks.RunDraw(control.SubImage(bounds).(*image.RGBA)) } @@ -91,38 +112,31 @@ func (control *CoreControl) AllocateCanvas (width, height int) { control.RGBA = core.canvas } -func (control CoreControl) SetSelectable (selectable bool) { - changed := control.core.selectable != selectable - control.core.selectable = selectable - if changed { - control.core.hooks.RunSelectabilityChange(selectable) - } -} - func (control CoreControl) SetMinimumSize (width, height int) { core := control.core - if width != core.metrics.minimumWidth || - height != core.metrics.minimumHeight { + if width == core.metrics.minimumWidth && + height == core.metrics.minimumHeight { + return + } - core.metrics.minimumWidth = width - core.metrics.minimumHeight = height - core.hooks.RunMinimumSizeChange(width, height) + core.metrics.minimumWidth = width + core.metrics.minimumHeight = height + core.hooks.RunMinimumSizeChange(width, height) - // if there is an image buffer, and the current size is less - // than this new minimum size, send core.parent a resize event. - if control.HasImage() { - bounds := control.Bounds() - imageWidth, - imageHeight, - constrained := control.ConstrainSize ( - bounds.Dx(), - bounds.Dy()) - if constrained { - core.parent.Handle (tomo.EventResize { - Width: imageWidth, - Height: imageHeight, - }) - } + // if there is an image buffer, and the current size is less + // than this new minimum size, send core.parent a resize event. + if control.HasImage() { + bounds := control.Bounds() + imageWidth, + imageHeight, + constrained := control.ConstrainSize ( + bounds.Dx(), + bounds.Dy()) + if constrained { + core.parent.Handle (tomo.EventResize { + Width: imageWidth, + Height: imageHeight, + }) } } } diff --git a/theme/theme.go b/theme/theme.go index b7f5f19..b638a4e 100644 --- a/theme/theme.go +++ b/theme/theme.go @@ -12,7 +12,7 @@ import "git.tebibyte.media/sashakoshka/tomo/defaultfont" var foregroundImage = artist.NewUniform(color.Gray16 { 0x0000}) var disabledForegroundImage = artist.NewUniform(color.Gray16 { 0x5555}) -var accentImage = artist.NewUniform(color.RGBA { 0x3E, 0x81, 0x69, 0xFF}) +var accentImage = artist.NewUniform(color.RGBA { 0x40, 0x80, 0x90, 0xFF}) var highlightImage = artist.NewUniform(color.Gray16 { 0xEEEE }) var shadowImage = artist.NewUniform(color.Gray16 { 0x3333 }) var weakShadeImage = artist.NewUniform(color.Gray16 { 0x7777 }) @@ -24,7 +24,7 @@ var backgroundImage = artist.NewUniform(color.Gray16 { 0xAAAA}) var backgroundProfile = artist.ShadingProfile { Highlight: highlightImage, Shadow: shadowImage, - Stroke: artist.NewUniform(color.Gray16 { 0x0000 }), + Stroke: strokeImage, Fill: backgroundImage, StrokeWeight: 1, ShadingWeight: 1, diff --git a/tomo.go b/tomo.go index 14ac3c6..e880f17 100644 --- a/tomo.go +++ b/tomo.go @@ -90,7 +90,11 @@ type Element interface { // Selectable returns whether this element can be selected. If this // element contains other selectable elements, it must return true. - Selectable () (bool) + Selectable () (selectable bool) + + // Selected returns wehther this element is currently selected. This + // will always return false if it is not selectable. + Selected () (selected bool) // If this element contains other elements, and one is selected, this // method will advance the selection in the specified direction. If no