Added a selectability core to reduce complexity of selectables
This commit is contained in:
parent
b2b2a80a06
commit
9422ff6198
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
1
elements/basic/switch.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package basic
|
@ -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
111
elements/core/selectable.go
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
|
Reference in New Issue
Block a user