Added a selectability core to reduce complexity of selectables

This commit is contained in:
Sasha Koshka 2023-01-27 17:55:49 -05:00
parent b2b2a80a06
commit 9422ff6198
7 changed files with 203 additions and 295 deletions

View File

@ -9,24 +9,28 @@ import "git.tebibyte.media/sashakoshka/tomo/elements/core"
// Button is a clickable button. // Button is a clickable button.
type Button struct { type Button struct {
*core.Core *core.Core
*core.SelectableCore
core core.CoreControl core core.CoreControl
selectableControl core.SelectableCoreControl
pressed bool
enabled bool
selected bool
text string
drawer artist.TextDrawer drawer artist.TextDrawer
pressed bool
text string
onClick func () onClick func ()
onSelectionRequest func () (granted bool)
onSelectionMotionRequest func (tomo.SelectionDirection) (granted bool)
} }
// NewButton creates a new button with the specified label text. // NewButton creates a new button with the specified label text.
func NewButton (text string) (element *Button) { func NewButton (text string) (element *Button) {
element = &Button { enabled: true } element = &Button { }
element.Core, element.core = core.NewCore(element) element.Core, element.core = core.NewCore(element)
element.SelectableCore,
element.selectableControl = core.NewSelectableCore (func () {
if element.core.HasImage () {
element.draw()
element.core.DamageAll()
}
})
element.drawer.SetFace(theme.FontFaceRegular()) element.drawer.SetFace(theme.FontFaceRegular())
element.SetText(text) element.SetText(text)
return return
@ -38,8 +42,8 @@ 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.Selected() { element.Select() }
if button != tomo.ButtonLeft { return } if button != tomo.ButtonLeft { return }
element.pressed = true element.pressed = true
if element.core.HasImage() { if element.core.HasImage() {
@ -59,7 +63,7 @@ func (element *Button) HandleMouseUp (x, y int, button tomo.Button) {
within := image.Point { x, y }. within := image.Point { x, y }.
In(element.Bounds()) In(element.Bounds())
if !element.enabled { return } if !element.Enabled() { return }
if within && element.onClick != nil { if within && element.onClick != nil {
element.onClick() element.onClick()
} }
@ -69,7 +73,7 @@ func (element *Button) HandleMouseMove (x, y int) { }
func (element *Button) HandleMouseScroll (x, y int, deltaX, deltaY float64) { } func (element *Button) HandleMouseScroll (x, y int, deltaX, deltaY float64) { }
func (element *Button) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) { func (element *Button) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) {
if !element.enabled { return } if !element.Enabled() { return }
if key == tomo.KeyEnter { if key == tomo.KeyEnter {
element.pressed = true element.pressed = true
if element.core.HasImage() { if element.core.HasImage() {
@ -86,61 +90,13 @@ func (element *Button) HandleKeyUp(key tomo.Key, modifiers tomo.Modifiers) {
element.draw() element.draw()
element.core.DamageAll() element.core.DamageAll()
} }
if !element.enabled { return } if !element.Enabled() { return }
if element.onClick != nil { if element.onClick != nil {
element.onClick() element.onClick()
} }
} }
} }
func (element *Button) Selected () (selected bool) {
return element.selected
}
func (element *Button) Select () {
if !element.enabled { return }
if element.onSelectionRequest != nil {
element.onSelectionRequest()
}
}
func (element *Button) HandleSelection (
direction tomo.SelectionDirection,
) (
accepted bool,
) {
direction = direction.Canon()
if !element.enabled { return false }
if element.selected && direction != tomo.SelectionDirectionNeutral {
return false
}
element.selected = true
if element.core.HasImage() {
element.draw()
element.core.DamageAll()
}
return true
}
func (element *Button) HandleDeselection () {
element.selected = false
if element.core.HasImage() {
element.draw()
element.core.DamageAll()
}
}
func (element *Button) OnSelectionRequest (callback func () (granted bool)) {
element.onSelectionRequest = callback
}
func (element *Button) OnSelectionMotionRequest (
callback func (direction tomo.SelectionDirection) (granted bool),
) {
element.onSelectionMotionRequest = callback
}
// OnClick sets the function to be called when the button is clicked. // OnClick sets the function to be called when the button is clicked.
func (element *Button) OnClick (callback func ()) { func (element *Button) OnClick (callback func ()) {
element.onClick = callback element.onClick = callback
@ -148,12 +104,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) {
if element.enabled == enabled { return } element.selectableControl.SetEnabled(enabled)
element.enabled = enabled
if element.core.HasImage () {
element.draw()
element.core.DamageAll()
}
} }
// SetText sets the button's label text. // SetText sets the button's label text.
@ -178,7 +129,7 @@ func (element *Button) draw () {
artist.FillRectangle ( artist.FillRectangle (
element.core, element.core,
theme.ButtonPattern ( theme.ButtonPattern (
element.enabled, element.Enabled(),
element.Selected(), element.Selected(),
element.pressed), element.pressed),
bounds) bounds)
@ -204,6 +155,6 @@ func (element *Button) draw () {
offset = offset.Add(theme.SinkOffsetVector()) offset = offset.Add(theme.SinkOffsetVector())
} }
foreground := theme.ForegroundPattern(element.enabled) foreground := theme.ForegroundPattern(element.Enabled())
element.drawer.Draw(element.core, foreground, offset) element.drawer.Draw(element.core, foreground, offset)
} }

View File

@ -9,25 +9,29 @@ import "git.tebibyte.media/sashakoshka/tomo/elements/core"
// 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 core.CoreControl core core.CoreControl
selectableControl core.SelectableCoreControl
pressed bool
checked bool
enabled bool
selected bool
text string
drawer artist.TextDrawer drawer artist.TextDrawer
onClick func () pressed bool
onSelectionRequest func () (granted bool) checked bool
onSelectionMotionRequest func (tomo.SelectionDirection) (granted bool) text string
onToggle func ()
} }
// NewCheckbox creates a new cbeckbox with the specified label text. // NewCheckbox creates a new cbeckbox with the specified label text.
func NewCheckbox (text string, checked bool) (element *Checkbox) { func NewCheckbox (text string, checked bool) (element *Checkbox) {
element = &Checkbox { enabled: true, checked: checked } element = &Checkbox { checked: checked }
element.Core, element.core = core.NewCore(element) element.Core, element.core = core.NewCore(element)
element.SelectableCore,
element.selectableControl = core.NewSelectableCore (func () {
if element.core.HasImage () {
element.draw()
element.core.DamageAll()
}
})
element.drawer.SetFace(theme.FontFaceRegular()) element.drawer.SetFace(theme.FontFaceRegular())
element.SetText(text) element.SetText(text)
return return
@ -40,6 +44,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 }
element.Select() element.Select()
element.pressed = true element.pressed = true
if element.core.HasImage() { if element.core.HasImage() {
@ -49,7 +54,7 @@ func (element *Checkbox) HandleMouseDown (x, y int, button tomo.Button) {
} }
func (element *Checkbox) HandleMouseUp (x, y int, button tomo.Button) { func (element *Checkbox) HandleMouseUp (x, y int, button tomo.Button) {
if button != tomo.ButtonLeft { return } if button != tomo.ButtonLeft || !element.pressed { return }
element.pressed = false element.pressed = false
within := image.Point { x, y }. within := image.Point { x, y }.
@ -62,8 +67,8 @@ func (element *Checkbox) HandleMouseUp (x, y int, button tomo.Button) {
element.draw() element.draw()
element.core.DamageAll() element.core.DamageAll()
} }
if within && element.onClick != nil { if within && element.onToggle != nil {
element.onClick() element.onToggle()
} }
} }
@ -88,65 +93,15 @@ func (element *Checkbox) HandleKeyUp (key tomo.Key, modifiers tomo.Modifiers) {
element.draw() element.draw()
element.core.DamageAll() element.core.DamageAll()
} }
if element.onClick != nil { if element.onToggle != nil {
element.onClick() element.onToggle()
} }
} }
} }
// Selected returns whether or not this element is selected. // OnToggle sets the function to be called when the checkbox is toggled.
func (element *Checkbox) Selected () (selected bool) { func (element *Checkbox) OnToggle (callback func ()) {
return element.selected element.onToggle = callback
}
// Select requests that this element be selected.
func (element *Checkbox) Select () {
if !element.enabled { return }
if element.onSelectionRequest != nil {
element.onSelectionRequest()
}
}
func (element *Checkbox) HandleSelection (
direction tomo.SelectionDirection,
) (
accepted bool,
) {
direction = direction.Canon()
if !element.enabled { return false }
if element.selected && direction != tomo.SelectionDirectionNeutral {
return false
}
element.selected = true
if element.core.HasImage() {
element.draw()
element.core.DamageAll()
}
return true
}
func (element *Checkbox) HandleDeselection () {
element.selected = false
if element.core.HasImage() {
element.draw()
element.core.DamageAll()
}
}
func (element *Checkbox) OnSelectionRequest (callback func () (granted bool)) {
element.onSelectionRequest = callback
}
func (element *Checkbox) OnSelectionMotionRequest (
callback func (direction tomo.SelectionDirection) (granted bool),
) {
element.onSelectionMotionRequest = callback
}
// OnClick sets the function to be called when the checkbox is toggled.
func (element *Checkbox) OnClick (callback func ()) {
element.onClick = callback
} }
// Value reports whether or not the checkbox is currently checked. // Value reports whether or not the checkbox is currently checked.
@ -156,12 +111,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) {
if element.enabled == enabled { return } element.selectableControl.SetEnabled(enabled)
element.enabled = enabled
if element.core.HasImage () {
element.draw()
element.core.DamageAll()
}
} }
// SetText sets the checkbox's label text. // SetText sets the checkbox's label text.
@ -171,9 +121,15 @@ func (element *Checkbox) SetText (text string) {
element.text = text element.text = text
element.drawer.SetText([]rune(text)) element.drawer.SetText([]rune(text))
textBounds := element.drawer.LayoutBounds() textBounds := element.drawer.LayoutBounds()
element.core.SetMinimumSize (
textBounds.Dy() + theme.Padding() + textBounds.Dx(), if text == "" {
textBounds.Dy()) element.core.SetMinimumSize(textBounds.Dy(), textBounds.Dy())
} else {
element.core.SetMinimumSize (
textBounds.Dy() + theme.Padding() + textBounds.Dx(),
textBounds.Dy())
}
if element.core.HasImage () { if element.core.HasImage () {
element.draw() element.draw()
element.core.DamageAll() element.core.DamageAll()
@ -188,17 +144,11 @@ func (element *Checkbox) draw () {
artist.FillRectangle ( artist.FillRectangle (
element.core, element.core,
theme.ButtonPattern ( theme.ButtonPattern (
element.enabled, element.Enabled(),
element.Selected(), element.Selected(),
element.pressed), element.pressed),
boxBounds) boxBounds)
innerBounds := bounds
innerBounds.Min.X += theme.Padding()
innerBounds.Min.Y += theme.Padding()
innerBounds.Max.X -= theme.Padding()
innerBounds.Max.Y -= theme.Padding()
textBounds := element.drawer.LayoutBounds() textBounds := element.drawer.LayoutBounds()
offset := image.Point { offset := image.Point {
X: bounds.Dy() + theme.Padding(), X: bounds.Dy() + theme.Padding(),
@ -207,7 +157,7 @@ func (element *Checkbox) draw () {
offset.Y -= textBounds.Min.Y offset.Y -= textBounds.Min.Y
offset.X -= textBounds.Min.X offset.X -= textBounds.Min.X
foreground := theme.ForegroundPattern(element.enabled) foreground := theme.ForegroundPattern(element.Enabled())
element.drawer.Draw(element.core, foreground, offset) element.drawer.Draw(element.core, foreground, offset)
if element.checked { if element.checked {
@ -217,7 +167,7 @@ func (element *Checkbox) draw () {
} }
artist.FillRectangle ( artist.FillRectangle (
element.core, element.core,
theme.ForegroundPattern(element.enabled), theme.ForegroundPattern(element.Enabled()),
checkBounds) checkBounds)
} }
} }

View File

@ -10,10 +10,10 @@ import "git.tebibyte.media/sashakoshka/tomo/elements/core"
// 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 core.CoreControl core core.CoreControl
selectableControl core.SelectableCoreControl
enabled bool
selected bool
pressed bool pressed bool
contentHeight int contentHeight int
@ -24,16 +24,21 @@ type List struct {
scroll int scroll int
entries []ListEntry entries []ListEntry
onSelectionRequest func () (granted bool)
onSelectionMotionRequest func (tomo.SelectionDirection) (granted bool)
onScrollBoundsChange func () onScrollBoundsChange func ()
onNoEntrySelected func () onNoEntrySelected func ()
} }
// NewList creates a new list element with the specified entries. // NewList creates a new list element with the specified entries.
func NewList (entries ...ListEntry) (element *List) { func NewList (entries ...ListEntry) (element *List) {
element = &List { enabled: true, selectedEntry: -1 } element = &List { selectedEntry: -1 }
element.Core, element.core = core.NewCore(element) element.Core, element.core = core.NewCore(element)
element.SelectableCore,
element.selectableControl = core.NewSelectableCore (func () {
if element.core.HasImage () {
element.draw()
element.core.DamageAll()
}
})
element.entries = make([]ListEntry, len(entries)) element.entries = make([]ListEntry, len(entries))
for index, entry := range entries { for index, entry := range entries {
@ -70,8 +75,8 @@ 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.Selected() { element.Select() }
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() {
@ -97,7 +102,7 @@ func (element *List) HandleMouseMove (x, y int) {
func (element *List) HandleMouseScroll (x, y int, deltaX, deltaY float64) { } func (element *List) HandleMouseScroll (x, y int, deltaX, deltaY float64) { }
func (element *List) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) { func (element *List) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) {
if !element.enabled { return } if !element.Enabled() { return }
altered := false altered := false
switch key { switch key {
@ -119,54 +124,6 @@ func (element *List) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) {
func (element *List) HandleKeyUp(key tomo.Key, modifiers tomo.Modifiers) { } func (element *List) HandleKeyUp(key tomo.Key, modifiers tomo.Modifiers) { }
func (element *List) Selected () (selected bool) {
return element.selected
}
func (element *List) Select () {
if !element.enabled { return }
if element.onSelectionRequest != nil {
element.onSelectionRequest()
}
}
func (element *List) HandleSelection (
direction tomo.SelectionDirection,
) (
accepted bool,
) {
direction = direction.Canon()
if !element.enabled { return false }
if element.selected && direction != tomo.SelectionDirectionNeutral {
return false
}
element.selected = true
if element.core.HasImage() {
element.draw()
element.core.DamageAll()
}
return true
}
func (element *List) HandleDeselection () {
element.selected = false
if element.core.HasImage() {
element.draw()
element.core.DamageAll()
}
}
func (element *List) OnSelectionRequest (callback func () (granted bool)) {
element.onSelectionRequest = callback
}
func (element *List) OnSelectionMotionRequest (
callback func (direction tomo.SelectionDirection) (granted bool),
) {
element.onSelectionMotionRequest = callback
}
// ScrollContentBounds returns the full content size of the element. // ScrollContentBounds returns the full content size of the element.
func (element *List) ScrollContentBounds () (bounds image.Rectangle) { func (element *List) ScrollContentBounds () (bounds image.Rectangle) {
return image.Rect ( return image.Rect (
@ -222,16 +179,6 @@ func (element *List) OnScrollBoundsChange (callback func ()) {
element.onScrollBoundsChange = callback element.onScrollBoundsChange = callback
} }
// SetEnabled sets whether this list can be interacted with or not.
func (element *List) SetEnabled (enabled bool) {
if element.enabled == enabled { return }
element.enabled = enabled
if element.core.HasImage () {
element.draw()
element.core.DamageAll()
}
}
// OnNoEntrySelected sets a function to be called when the user chooses to // OnNoEntrySelected sets a function to be called when the user chooses to
// deselect the current selected entry by clicking on empty space within the // deselect the current selected entry by clicking on empty space within the
// list or by pressing the escape key. // list or by pressing the escape key.
@ -416,7 +363,7 @@ func (element *List) draw () {
artist.FillRectangle ( artist.FillRectangle (
element, element,
theme.ListPattern(element.selected), theme.ListPattern(element.Selected()),
bounds) bounds)
dot := image.Point { dot := image.Point {
@ -437,6 +384,6 @@ func (element *List) draw () {
} }
entry.Draw ( entry.Draw (
element, entryPosition, element, entryPosition,
element.selectedEntry == index && element.selected) element.selectedEntry == index && element.Selected())
} }
} }

1
elements/basic/switch.go Normal file
View File

@ -0,0 +1 @@
package basic

View File

@ -10,10 +10,9 @@ import "git.tebibyte.media/sashakoshka/tomo/elements/core"
// 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 core.CoreControl core core.CoreControl
selectableControl core.SelectableCoreControl
enabled bool
selected bool
cursor int cursor int
scroll int scroll int
@ -25,8 +24,6 @@ type TextBox struct {
onKeyDown func (key tomo.Key, modifiers tomo.Modifiers) (handled bool) onKeyDown func (key tomo.Key, modifiers tomo.Modifiers) (handled bool)
onChange func () onChange func ()
onSelectionRequest func () (granted bool)
onSelectionMotionRequest func (tomo.SelectionDirection) (granted bool)
onScrollBoundsChange func () onScrollBoundsChange func ()
} }
@ -34,8 +31,15 @@ type TextBox struct {
// a value. When the value is empty, the placeholder will be displayed in gray // a value. When the value is empty, the placeholder will be displayed in gray
// text. // text.
func NewTextBox (placeholder, value string) (element *TextBox) { func NewTextBox (placeholder, value string) (element *TextBox) {
element = &TextBox { enabled: true } element = &TextBox { }
element.Core, element.core = core.NewCore(element) element.Core, element.core = core.NewCore(element)
element.SelectableCore,
element.selectableControl = core.NewSelectableCore (func () {
if element.core.HasImage () {
element.draw()
element.core.DamageAll()
}
})
element.placeholderDrawer.SetFace(theme.FontFaceRegular()) element.placeholderDrawer.SetFace(theme.FontFaceRegular())
element.valueDrawer.SetFace(theme.FontFaceRegular()) element.valueDrawer.SetFace(theme.FontFaceRegular())
element.placeholder = placeholder element.placeholder = placeholder
@ -55,8 +59,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.Selected() { element.Select() }
} }
func (element *TextBox) HandleMouseUp (x, y int, button tomo.Button) { } func (element *TextBox) HandleMouseUp (x, y int, button tomo.Button) { }
@ -133,62 +137,6 @@ func (element *TextBox) HandleKeyDown(key tomo.Key, modifiers tomo.Modifiers) {
func (element *TextBox) HandleKeyUp(key tomo.Key, modifiers tomo.Modifiers) { } func (element *TextBox) HandleKeyUp(key tomo.Key, modifiers tomo.Modifiers) { }
func (element *TextBox) Selected () (selected bool) {
return element.selected
}
func (element *TextBox) Select () {
if element.onSelectionRequest != nil {
element.onSelectionRequest()
}
}
func (element *TextBox) HandleSelection (
direction tomo.SelectionDirection,
) (
accepted bool,
) {
direction = direction.Canon()
if !element.enabled { return false }
if element.selected && direction != tomo.SelectionDirectionNeutral {
return false
}
element.selected = true
if element.core.HasImage() {
element.draw()
element.core.DamageAll()
}
return true
}
func (element *TextBox) HandleDeselection () {
element.selected = false
if element.core.HasImage() {
element.draw()
element.core.DamageAll()
}
}
func (element *TextBox) OnSelectionRequest (callback func () (granted bool)) {
element.onSelectionRequest = callback
}
func (element *TextBox) OnSelectionMotionRequest (
callback func (direction tomo.SelectionDirection) (granted bool),
) {
element.onSelectionMotionRequest = callback
}
func (element *TextBox) SetEnabled (enabled bool) {
if element.enabled == enabled { return }
element.enabled = enabled
if element.core.HasImage () {
element.draw()
element.core.DamageAll()
}
}
func (element *TextBox) SetPlaceholder (placeholder string) { func (element *TextBox) SetPlaceholder (placeholder string) {
if element.placeholder == placeholder { return } if element.placeholder == placeholder { return }
@ -325,11 +273,11 @@ func (element *TextBox) draw () {
artist.FillRectangle ( artist.FillRectangle (
element.core, element.core,
theme.InputPattern ( theme.InputPattern (
element.enabled, element.Enabled(),
element.Selected()), element.Selected()),
bounds) bounds)
if len(element.text) == 0 && !element.selected { if len(element.text) == 0 && !element.Selected() {
// draw placeholder // draw placeholder
textBounds := element.placeholderDrawer.LayoutBounds() textBounds := element.placeholderDrawer.LayoutBounds()
offset := image.Point { offset := image.Point {
@ -348,13 +296,13 @@ func (element *TextBox) draw () {
X: theme.Padding() - element.scroll, X: theme.Padding() - element.scroll,
Y: theme.Padding(), Y: theme.Padding(),
} }
foreground := theme.ForegroundPattern(element.enabled) foreground := theme.ForegroundPattern(element.Enabled())
element.valueDrawer.Draw ( element.valueDrawer.Draw (
element.core, element.core,
foreground, foreground,
offset.Sub(textBounds.Min)) offset.Sub(textBounds.Min))
if element.selected { if element.Selected() {
// cursor // cursor
cursorPosition := element.valueDrawer.PositionOf ( cursorPosition := element.valueDrawer.PositionOf (
element.cursor) element.cursor)

111
elements/core/selectable.go Normal file
View File

@ -0,0 +1,111 @@
package core
import "git.tebibyte.media/sashakoshka/tomo"
// SelectableCore is a struct that can be embedded into objects to make them
// selectable, giving them the default selectability behavior.
type SelectableCore struct {
selected bool
enabled bool
drawSelectionChange func ()
onSelectionRequest func () (granted bool)
onSelectionMotionRequest func(tomo.SelectionDirection) (granted bool)
}
// NewSelectableCore creates a new selectability core and its corresponding
// control. If your element needs to visually update itself when it's selection
// state changes (which it should), a callback to draw and push the update can
// be specified.
func NewSelectableCore (
drawSelectionChange func (),
) (
core *SelectableCore,
control SelectableCoreControl,
) {
core = &SelectableCore {
drawSelectionChange: drawSelectionChange,
enabled: true,
}
control = SelectableCoreControl { core: core }
return
}
// Selected returns whether or not this element is currently selected.
func (core *SelectableCore) Selected () (selected bool) {
return core.selected
}
// Select selects this element, if its parent element grants the request.
func (core *SelectableCore) Select () {
if !core.enabled { return }
if core.onSelectionRequest != nil {
core.onSelectionRequest()
}
}
// HandleSelection causes this element to mark itself as selected, if it can
// currently be. Otherwise, it will return false and do nothing.
func (core *SelectableCore) HandleSelection (
direction tomo.SelectionDirection,
) (
accepted bool,
) {
direction = direction.Canon()
if !core.enabled { return false }
if core.selected && direction != tomo.SelectionDirectionNeutral {
return false
}
core.selected = true
if core.drawSelectionChange != nil { core.drawSelectionChange() }
return true
}
// HandleDeselection causes this element to mark itself as deselected.
func (core *SelectableCore) HandleDeselection () {
core.selected = false
if core.drawSelectionChange != nil { core.drawSelectionChange() }
}
// OnSelectionRequest sets a function to be called when this element
// wants its parent element to select it. Parent elements should return
// true if the request was granted, and false if it was not.
func (core *SelectableCore) OnSelectionRequest (callback func () (granted bool)) {
core.onSelectionRequest = callback
}
// OnSelectionMotionRequest sets a function to be called when this
// element wants its parent element to select 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 *SelectableCore) OnSelectionMotionRequest (
callback func (direction tomo.SelectionDirection) (granted bool),
) {
core.onSelectionMotionRequest = callback
}
// Enabled returns whether or not the element is enabled.
func (core *SelectableCore) Enabled () (enabled bool) {
return core.enabled
}
// SelectableCoreControl is a struct that can be used to exert control over a
// selectability core. It must not be directly embedded into an element, but
// instead kept as a private member. When a SelectableCore struct is created, a
// corresponding SelectableCoreControl struct is linked to it and returned
// alongside it.
type SelectableCoreControl struct {
core *SelectableCore
}
// SetEnabled sets whether the selectability core is enabled. If the state
// changes, this will call drawSelectionChange.
func (control SelectableCoreControl) SetEnabled (enabled bool) {
if control.core.enabled == enabled { return }
control.core.enabled = enabled
if !enabled { control.core.selected = false }
if control.core.drawSelectionChange != nil {
control.core.drawSelectionChange()
}
}

View File

@ -30,7 +30,7 @@ func run () {
disabledCheckbox.SetEnabled(false) disabledCheckbox.SetEnabled(false)
container.Adopt(disabledCheckbox, false) container.Adopt(disabledCheckbox, false)
vsync := basic.NewCheckbox("Enable vsync", false) vsync := basic.NewCheckbox("Enable vsync", false)
vsync.OnClick (func () { vsync.OnToggle (func () {
if vsync.Value() { if vsync.Value() {
popups.NewDialog ( popups.NewDialog (
popups.DialogKindInfo, popups.DialogKindInfo,