Half-done migration of basic elements
This commit is contained in:
parent
0bdbaa39ca
commit
3998d842b1
@ -6,8 +6,6 @@ import "git.tebibyte.media/sashakoshka/tomo/theme"
|
|||||||
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
|
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
|
||||||
|
|
||||||
var buttonCase = theme.C("basic", "button")
|
|
||||||
|
|
||||||
// Button is a clickable button.
|
// Button is a clickable button.
|
||||||
type Button struct {
|
type Button struct {
|
||||||
*core.Core
|
*core.Core
|
||||||
@ -25,19 +23,27 @@ type Button struct {
|
|||||||
// 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 { }
|
element = &Button { }
|
||||||
element.Core, element.core = core.NewCore(element.draw)
|
element.Core, element.core = core.NewCore (
|
||||||
|
element.draw,
|
||||||
|
element.redo,
|
||||||
|
element.redo,
|
||||||
|
theme.C("basic", "button"))
|
||||||
element.FocusableCore,
|
element.FocusableCore,
|
||||||
element.focusableControl = core.NewFocusableCore (func () {
|
element.focusableControl = core.NewFocusableCore(element.redo)
|
||||||
if element.core.HasImage () {
|
|
||||||
element.draw()
|
|
||||||
element.core.DamageAll()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
element.drawer.SetFace(theme.FontFaceRegular())
|
|
||||||
element.SetText(text)
|
element.SetText(text)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (element *Button) redo () {
|
||||||
|
element.drawer.SetFace (
|
||||||
|
element.core.FontFace(theme.FontStyleRegular,
|
||||||
|
theme.FontSizeNormal))
|
||||||
|
if element.core.HasImage () {
|
||||||
|
element.draw()
|
||||||
|
element.core.DamageAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (element *Button) HandleMouseDown (x, y int, button input.Button) {
|
func (element *Button) HandleMouseDown (x, y int, button input.Button) {
|
||||||
if !element.Enabled() { return }
|
if !element.Enabled() { return }
|
||||||
if !element.Focused() { element.Focus() }
|
if !element.Focused() { element.Focus() }
|
||||||
@ -83,10 +89,7 @@ func (element *Button) HandleKeyDown (key input.Key, modifiers input.Modifiers)
|
|||||||
func (element *Button) HandleKeyUp(key input.Key, modifiers input.Modifiers) {
|
func (element *Button) HandleKeyUp(key input.Key, modifiers input.Modifiers) {
|
||||||
if key == input.KeyEnter && element.pressed {
|
if key == input.KeyEnter && element.pressed {
|
||||||
element.pressed = false
|
element.pressed = false
|
||||||
if element.core.HasImage() {
|
element.redo()
|
||||||
element.draw()
|
|
||||||
element.core.DamageAll()
|
|
||||||
}
|
|
||||||
if !element.Enabled() { return }
|
if !element.Enabled() { return }
|
||||||
if element.onClick != nil {
|
if element.onClick != nil {
|
||||||
element.onClick()
|
element.onClick()
|
||||||
@ -111,33 +114,28 @@ func (element *Button) 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()
|
||||||
_, inset := theme.ButtonPattern(theme.PatternState { Case: buttonCase })
|
minimumSize := textBounds.Inset(-element.core.Config().Padding())
|
||||||
minimumSize := inset.Inverse().Apply(textBounds).Inset(-theme.Padding())
|
|
||||||
element.core.SetMinimumSize(minimumSize.Dx(), minimumSize.Dy())
|
element.core.SetMinimumSize(minimumSize.Dx(), minimumSize.Dy())
|
||||||
if element.core.HasImage () {
|
element.redo()
|
||||||
element.draw()
|
|
||||||
element.core.DamageAll()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (element *Button) draw () {
|
func (element *Button) draw () {
|
||||||
bounds := element.Bounds()
|
bounds := element.Bounds()
|
||||||
|
|
||||||
pattern, inset := theme.ButtonPattern(theme.PatternState {
|
state := theme.PatternState {
|
||||||
Case: buttonCase,
|
|
||||||
Disabled: !element.Enabled(),
|
Disabled: !element.Enabled(),
|
||||||
Focused: element.Focused(),
|
Focused: element.Focused(),
|
||||||
Pressed: element.pressed,
|
Pressed: element.pressed,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
pattern := element.core.Pattern(theme.PatternButton, state)
|
||||||
|
|
||||||
artist.FillRectangle(element, pattern, bounds)
|
artist.FillRectangle(element, pattern, bounds)
|
||||||
|
|
||||||
innerBounds := inset.Apply(bounds)
|
|
||||||
|
|
||||||
textBounds := element.drawer.LayoutBounds()
|
textBounds := element.drawer.LayoutBounds()
|
||||||
offset := image.Point {
|
offset := image.Point {
|
||||||
X: innerBounds.Min.X + (innerBounds.Dx() - textBounds.Dx()) / 2,
|
X: bounds.Min.X + (bounds.Dx() - textBounds.Dx()) / 2,
|
||||||
Y: innerBounds.Min.Y + (innerBounds.Dy() - textBounds.Dy()) / 2,
|
Y: bounds.Min.Y + (bounds.Dy() - textBounds.Dy()) / 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
// account for the fact that the bounding rectangle will be shifted over
|
// account for the fact that the bounding rectangle will be shifted over
|
||||||
@ -145,9 +143,6 @@ func (element *Button) draw () {
|
|||||||
offset.Y -= textBounds.Min.Y
|
offset.Y -= textBounds.Min.Y
|
||||||
offset.X -= textBounds.Min.X
|
offset.X -= textBounds.Min.X
|
||||||
|
|
||||||
foreground, _ := theme.ForegroundPattern (theme.PatternState {
|
foreground := element.core.Pattern(theme.PatternForeground, state)
|
||||||
Case: buttonCase,
|
|
||||||
Disabled: !element.Enabled(),
|
|
||||||
})
|
|
||||||
element.drawer.Draw(element, foreground, offset)
|
element.drawer.Draw(element, foreground, offset)
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,6 @@ import "git.tebibyte.media/sashakoshka/tomo/theme"
|
|||||||
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
|
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
|
||||||
|
|
||||||
var checkboxCase = theme.C("basic", "checkbox")
|
|
||||||
|
|
||||||
// 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
|
||||||
@ -26,19 +24,27 @@ type Checkbox struct {
|
|||||||
// 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 { checked: checked }
|
element = &Checkbox { checked: checked }
|
||||||
element.Core, element.core = core.NewCore(element.draw)
|
element.Core, element.core = core.NewCore (
|
||||||
|
element.draw,
|
||||||
|
element.redo,
|
||||||
|
element.redo,
|
||||||
|
theme.C("basic", "checkbox"))
|
||||||
element.FocusableCore,
|
element.FocusableCore,
|
||||||
element.focusableControl = core.NewFocusableCore (func () {
|
element.focusableControl = core.NewFocusableCore(element.redo)
|
||||||
if element.core.HasImage () {
|
|
||||||
element.draw()
|
|
||||||
element.core.DamageAll()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
element.drawer.SetFace(theme.FontFaceRegular())
|
|
||||||
element.SetText(text)
|
element.SetText(text)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (element *Checkbox) redo () {
|
||||||
|
element.drawer.SetFace (
|
||||||
|
element.core.FontFace(theme.FontStyleRegular,
|
||||||
|
theme.FontSizeNormal))
|
||||||
|
if element.core.HasImage () {
|
||||||
|
element.draw()
|
||||||
|
element.core.DamageAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (element *Checkbox) HandleMouseDown (x, y int, button input.Button) {
|
func (element *Checkbox) HandleMouseDown (x, y int, button input.Button) {
|
||||||
if !element.Enabled() { return }
|
if !element.Enabled() { return }
|
||||||
element.Focus()
|
element.Focus()
|
||||||
@ -122,7 +128,7 @@ func (element *Checkbox) SetText (text string) {
|
|||||||
element.core.SetMinimumSize(textBounds.Dy(), textBounds.Dy())
|
element.core.SetMinimumSize(textBounds.Dy(), textBounds.Dy())
|
||||||
} else {
|
} else {
|
||||||
element.core.SetMinimumSize (
|
element.core.SetMinimumSize (
|
||||||
textBounds.Dy() + theme.Padding() + textBounds.Dx(),
|
textBounds.Dy() + element.core.Config().Padding() + textBounds.Dx(),
|
||||||
textBounds.Dy())
|
textBounds.Dy())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,35 +142,27 @@ func (element *Checkbox) draw () {
|
|||||||
bounds := element.Bounds()
|
bounds := element.Bounds()
|
||||||
boxBounds := image.Rect(0, 0, bounds.Dy(), bounds.Dy()).Add(bounds.Min)
|
boxBounds := image.Rect(0, 0, bounds.Dy(), bounds.Dy()).Add(bounds.Min)
|
||||||
|
|
||||||
backgroundPattern, _ := theme.BackgroundPattern(theme.PatternState {
|
state := theme.PatternState {
|
||||||
Case: checkboxCase,
|
|
||||||
})
|
|
||||||
artist.FillRectangle(element, backgroundPattern, bounds)
|
|
||||||
|
|
||||||
pattern, inset := theme.ButtonPattern(theme.PatternState {
|
|
||||||
Case: checkboxCase,
|
|
||||||
Disabled: !element.Enabled(),
|
Disabled: !element.Enabled(),
|
||||||
Focused: element.Focused(),
|
Focused: element.Focused(),
|
||||||
Pressed: element.pressed,
|
Pressed: element.pressed,
|
||||||
})
|
On: element.checked,
|
||||||
|
}
|
||||||
|
|
||||||
|
backgroundPattern := element.core.Pattern(theme.PatternBackground, state)
|
||||||
|
artist.FillRectangle(element, backgroundPattern, bounds)
|
||||||
|
|
||||||
|
pattern := element.core.Pattern (theme.PatternButton, state)
|
||||||
artist.FillRectangle(element, pattern, boxBounds)
|
artist.FillRectangle(element, pattern, boxBounds)
|
||||||
|
|
||||||
textBounds := element.drawer.LayoutBounds()
|
textBounds := element.drawer.LayoutBounds()
|
||||||
offset := bounds.Min.Add(image.Point {
|
offset := bounds.Min.Add(image.Point {
|
||||||
X: bounds.Dy() + theme.Padding(),
|
X: bounds.Dy() + element.core.Config().Padding(),
|
||||||
})
|
})
|
||||||
|
|
||||||
offset.Y -= textBounds.Min.Y
|
offset.Y -= textBounds.Min.Y
|
||||||
offset.X -= textBounds.Min.X
|
offset.X -= textBounds.Min.X
|
||||||
|
|
||||||
foreground, _ := theme.ForegroundPattern (theme.PatternState {
|
foreground := element.core.Pattern(theme.PatternForeground, state)
|
||||||
Case: checkboxCase,
|
|
||||||
Disabled: !element.Enabled(),
|
|
||||||
})
|
|
||||||
element.drawer.Draw(element, foreground, offset)
|
element.drawer.Draw(element, foreground, offset)
|
||||||
|
|
||||||
if element.checked {
|
|
||||||
checkBounds := inset.Apply(boxBounds).Inset(2)
|
|
||||||
artist.FillRectangle(element, foreground, checkBounds)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,6 @@ import "git.tebibyte.media/sashakoshka/tomo/layouts"
|
|||||||
import "git.tebibyte.media/sashakoshka/tomo/elements"
|
import "git.tebibyte.media/sashakoshka/tomo/elements"
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
|
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
|
||||||
|
|
||||||
var containerCase = theme.C("basic", "container")
|
|
||||||
|
|
||||||
// Container is an element capable of containg other elements, and arranging
|
// Container is an element capable of containg other elements, and arranging
|
||||||
// them in a layout.
|
// them in a layout.
|
||||||
type Container struct {
|
type Container struct {
|
||||||
@ -33,7 +31,11 @@ type Container struct {
|
|||||||
// NewContainer creates a new container.
|
// NewContainer creates a new container.
|
||||||
func NewContainer (layout layouts.Layout) (element *Container) {
|
func NewContainer (layout layouts.Layout) (element *Container) {
|
||||||
element = &Container { }
|
element = &Container { }
|
||||||
element.Core, element.core = core.NewCore(element.redoAll)
|
element.Core, element.core = core.NewCore (
|
||||||
|
element.redoAll,
|
||||||
|
element.handleConfigChange,
|
||||||
|
element.handleThemeChange,
|
||||||
|
theme.C("basic", "container"))
|
||||||
element.SetLayout(layout)
|
element.SetLayout(layout)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -205,9 +207,7 @@ func (element *Container) redoAll () {
|
|||||||
|
|
||||||
// draw a background
|
// draw a background
|
||||||
bounds := element.Bounds()
|
bounds := element.Bounds()
|
||||||
pattern, _ := theme.BackgroundPattern (theme.PatternState {
|
pattern := element.core.Pattern (theme.PatternBackground, theme.PatternState { })
|
||||||
Case: containerCase,
|
|
||||||
})
|
|
||||||
artist.FillRectangle(element, pattern, bounds)
|
artist.FillRectangle(element, pattern, bounds)
|
||||||
|
|
||||||
// cut our canvas up and give peices to child elements
|
// cut our canvas up and give peices to child elements
|
||||||
@ -216,6 +216,24 @@ func (element *Container) redoAll () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (element *Container) handleConfigChange () {
|
||||||
|
for _, child := range element.children {
|
||||||
|
if child0, ok := child.Element.(elements.Configurable); ok {
|
||||||
|
child0.SetConfig(element.core.Config())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
element.redoAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (element *Container) handleThemeChange () {
|
||||||
|
for _, child := range element.children {
|
||||||
|
if child0, ok := child.Element.(elements.Themeable); ok {
|
||||||
|
child0.SetTheme(element.core.Theme())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
element.redoAll()
|
||||||
|
}
|
||||||
|
|
||||||
func (element *Container) HandleMouseDown (x, y int, button input.Button) {
|
func (element *Container) HandleMouseDown (x, y int, button input.Button) {
|
||||||
child, handlesMouse := element.ChildAt(image.Pt(x, y)).(elements.MouseTarget)
|
child, handlesMouse := element.ChildAt(image.Pt(x, y)).(elements.MouseTarget)
|
||||||
if !handlesMouse { return }
|
if !handlesMouse { return }
|
||||||
@ -266,7 +284,7 @@ func (element *Container) HandleKeyUp (key input.Key, modifiers input.Modifiers)
|
|||||||
func (element *Container) FlexibleHeightFor (width int) (height int) {
|
func (element *Container) FlexibleHeightFor (width int) (height int) {
|
||||||
return element.layout.FlexibleHeightFor (
|
return element.layout.FlexibleHeightFor (
|
||||||
element.children,
|
element.children,
|
||||||
theme.Margin(), width)
|
element.core.Config().Margin(), width)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (element *Container) OnFlexibleHeightChange (callback func ()) {
|
func (element *Container) OnFlexibleHeightChange (callback func ()) {
|
||||||
@ -469,15 +487,15 @@ func (element *Container) childFocusRequestCallback (
|
|||||||
|
|
||||||
func (element *Container) updateMinimumSize () {
|
func (element *Container) updateMinimumSize () {
|
||||||
width, height := element.layout.MinimumSize (
|
width, height := element.layout.MinimumSize (
|
||||||
element.children, theme.Margin())
|
element.children, element.core.Config().Margin())
|
||||||
if element.flexible {
|
if element.flexible {
|
||||||
height = element.layout.FlexibleHeightFor (
|
height = element.layout.FlexibleHeightFor (
|
||||||
element.children, theme.Margin(), width)
|
element.children, element.core.Config().Margin(), width)
|
||||||
}
|
}
|
||||||
element.core.SetMinimumSize(width, height)
|
element.core.SetMinimumSize(width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (element *Container) recalculate () {
|
func (element *Container) recalculate () {
|
||||||
element.layout.Arrange (
|
element.layout.Arrange (
|
||||||
element.children, theme.Margin(), element.Bounds())
|
element.children, element.core.Config().Margin(), element.Bounds())
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,6 @@ import "git.tebibyte.media/sashakoshka/tomo/theme"
|
|||||||
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
|
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
|
||||||
|
|
||||||
var labelCase = theme.C("basic", "label")
|
|
||||||
|
|
||||||
// Label is a simple text box.
|
// Label is a simple text box.
|
||||||
type Label struct {
|
type Label struct {
|
||||||
*core.Core
|
*core.Core
|
||||||
@ -22,14 +20,35 @@ type Label struct {
|
|||||||
// wrapped.
|
// wrapped.
|
||||||
func NewLabel (text string, wrap bool) (element *Label) {
|
func NewLabel (text string, wrap bool) (element *Label) {
|
||||||
element = &Label { }
|
element = &Label { }
|
||||||
element.Core, element.core = core.NewCore(element.handleResize)
|
element.Core, element.core = core.NewCore (
|
||||||
face := theme.FontFaceRegular()
|
element.handleResize,
|
||||||
|
element.redo,
|
||||||
|
element.redo,
|
||||||
|
theme.C("basic", "label"))
|
||||||
|
face := element.core.FontFace (
|
||||||
|
theme.FontStyleRegular,
|
||||||
|
theme.FontSizeNormal)
|
||||||
element.drawer.SetFace(face)
|
element.drawer.SetFace(face)
|
||||||
element.SetWrap(wrap)
|
element.SetWrap(wrap)
|
||||||
element.SetText(text)
|
element.SetText(text)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (element *Label) redo () {
|
||||||
|
face := element.core.FontFace (
|
||||||
|
theme.FontStyleRegular,
|
||||||
|
theme.FontSizeNormal)
|
||||||
|
element.drawer.SetFace(face)
|
||||||
|
element.updateMinimumSize()
|
||||||
|
bounds := element.Bounds()
|
||||||
|
if element.wrap {
|
||||||
|
element.drawer.SetMaxWidth(bounds.Dx())
|
||||||
|
element.drawer.SetMaxHeight(bounds.Dy())
|
||||||
|
}
|
||||||
|
element.draw()
|
||||||
|
element.core.DamageAll()
|
||||||
|
}
|
||||||
|
|
||||||
func (element *Label) handleResize () {
|
func (element *Label) handleResize () {
|
||||||
bounds := element.Bounds()
|
bounds := element.Bounds()
|
||||||
if element.wrap {
|
if element.wrap {
|
||||||
@ -93,7 +112,7 @@ func (element *Label) SetWrap (wrap bool) {
|
|||||||
func (element *Label) updateMinimumSize () {
|
func (element *Label) updateMinimumSize () {
|
||||||
if element.wrap {
|
if element.wrap {
|
||||||
em := element.drawer.Em().Round()
|
em := element.drawer.Em().Round()
|
||||||
if em < 1 { em = theme.Padding() }
|
if em < 1 { em = element.core.Config().Padding() }
|
||||||
element.core.SetMinimumSize (
|
element.core.SetMinimumSize (
|
||||||
em, element.drawer.LineHeight().Round())
|
em, element.drawer.LineHeight().Round())
|
||||||
if element.onFlexibleHeightChange != nil {
|
if element.onFlexibleHeightChange != nil {
|
||||||
@ -108,15 +127,15 @@ func (element *Label) updateMinimumSize () {
|
|||||||
func (element *Label) draw () {
|
func (element *Label) draw () {
|
||||||
bounds := element.Bounds()
|
bounds := element.Bounds()
|
||||||
|
|
||||||
pattern, _ := theme.BackgroundPattern(theme.PatternState {
|
pattern := element.core.Pattern (
|
||||||
Case: labelCase,
|
theme.PatternBackground,
|
||||||
})
|
theme.PatternState { })
|
||||||
artist.FillRectangle(element, pattern, bounds)
|
artist.FillRectangle(element, pattern, bounds)
|
||||||
|
|
||||||
textBounds := element.drawer.LayoutBounds()
|
textBounds := element.drawer.LayoutBounds()
|
||||||
|
|
||||||
foreground, _ := theme.ForegroundPattern (theme.PatternState {
|
foreground := element.core.Pattern (
|
||||||
Case: labelCase,
|
theme.PatternForeground,
|
||||||
})
|
theme.PatternState { })
|
||||||
element.drawer.Draw (element, foreground, bounds.Min.Sub(textBounds.Min))
|
element.drawer.Draw (element, foreground, bounds.Min.Sub(textBounds.Min))
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,6 @@ import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
|||||||
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
|
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
|
||||||
|
|
||||||
var listCase = theme.C("basic", "list")
|
|
||||||
|
|
||||||
// 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
|
||||||
@ -34,7 +32,11 @@ type List struct {
|
|||||||
// 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 { selectedEntry: -1 }
|
element = &List { selectedEntry: -1 }
|
||||||
element.Core, element.core = core.NewCore(element.handleResize)
|
element.Core, element.core = core.NewCore (
|
||||||
|
element.handleResize,
|
||||||
|
element.redo,
|
||||||
|
element.redo,
|
||||||
|
theme.C("basic", "list"))
|
||||||
element.FocusableCore,
|
element.FocusableCore,
|
||||||
element.focusableControl = core.NewFocusableCore (func () {
|
element.focusableControl = core.NewFocusableCore (func () {
|
||||||
if element.core.HasImage () {
|
if element.core.HasImage () {
|
||||||
@ -63,6 +65,36 @@ func (element *List) handleResize () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (element *List) handleConfigChange () {
|
||||||
|
for index, entry := range element.entries {
|
||||||
|
entry.SetConfig(element.core.Config())
|
||||||
|
element.entries[index] = entry
|
||||||
|
}
|
||||||
|
element.redo()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (element *List) handleThemeChange () {
|
||||||
|
for index, entry := range element.entries {
|
||||||
|
entry.SetConfig(element.core.Config())
|
||||||
|
element.entries[index] = entry
|
||||||
|
}
|
||||||
|
element.redo()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (element *List) redo () {
|
||||||
|
for index, entry := range element.entries {
|
||||||
|
element.entries[index] = element.resizeEntryToFit(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if element.core.HasImage() {
|
||||||
|
element.draw()
|
||||||
|
element.core.DamageAll()
|
||||||
|
}
|
||||||
|
if element.onScrollBoundsChange != nil {
|
||||||
|
element.onScrollBoundsChange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Collapse forces a minimum width and height upon the list. If a zero value is
|
// Collapse forces a minimum width and height upon the list. If a zero value is
|
||||||
// given for a dimension, its minimum will be determined by the list's content.
|
// given for a dimension, its minimum will be determined by the list's content.
|
||||||
// If the list's height goes beyond the forced size, it will need to be accessed
|
// If the list's height goes beyond the forced size, it will need to be accessed
|
||||||
@ -164,9 +196,7 @@ func (element *List) ScrollAxes () (horizontal, vertical bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (element *List) scrollViewportHeight () (height int) {
|
func (element *List) scrollViewportHeight () (height int) {
|
||||||
_, inset := theme.ListPattern(theme.PatternState {
|
inset := element.core.Inset(theme.PatternSunken)
|
||||||
Case: listCase,
|
|
||||||
})
|
|
||||||
return element.Bounds().Dy() - inset[0] - inset[2]
|
return element.Bounds().Dy() - inset[0] - inset[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,6 +228,8 @@ func (element *List) CountEntries () (count int) {
|
|||||||
func (element *List) Append (entry ListEntry) {
|
func (element *List) Append (entry ListEntry) {
|
||||||
// append
|
// append
|
||||||
entry.Collapse(element.forcedMinimumWidth)
|
entry.Collapse(element.forcedMinimumWidth)
|
||||||
|
entry.SetTheme(element.core.Theme())
|
||||||
|
entry.SetConfig(element.core.Config())
|
||||||
element.entries = append(element.entries, entry)
|
element.entries = append(element.entries, entry)
|
||||||
|
|
||||||
// recalculate, redraw, notify
|
// recalculate, redraw, notify
|
||||||
@ -290,7 +322,7 @@ func (element *List) Replace (index int, entry ListEntry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (element *List) selectUnderMouse (x, y int) (updated bool) {
|
func (element *List) selectUnderMouse (x, y int) (updated bool) {
|
||||||
_, inset := theme.ListPattern(theme.PatternState { })
|
inset := element.core.Inset(theme.PatternSunken)
|
||||||
bounds := inset.Apply(element.Bounds())
|
bounds := inset.Apply(element.Bounds())
|
||||||
mousePoint := image.Pt(x, y)
|
mousePoint := image.Pt(x, y)
|
||||||
dot := image.Pt (
|
dot := image.Pt (
|
||||||
@ -332,9 +364,7 @@ func (element *List) changeSelectionBy (delta int) (updated bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (element *List) resizeEntryToFit (entry ListEntry) (resized ListEntry) {
|
func (element *List) resizeEntryToFit (entry ListEntry) (resized ListEntry) {
|
||||||
_, inset := theme.ListPattern(theme.PatternState {
|
inset := element.core.Inset(theme.PatternSunken)
|
||||||
Case: listCase,
|
|
||||||
})
|
|
||||||
entry.Collapse(element.forcedMinimumWidth - inset[3] - inset[1])
|
entry.Collapse(element.forcedMinimumWidth - inset[3] - inset[1])
|
||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
@ -361,9 +391,7 @@ func (element *List) updateMinimumSize () {
|
|||||||
minimumHeight = element.contentHeight
|
minimumHeight = element.contentHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
_, inset := theme.ListPattern(theme.PatternState {
|
inset := element.core.Inset(theme.PatternSunken)
|
||||||
Case: listCase,
|
|
||||||
})
|
|
||||||
minimumHeight += inset[0] + inset[2]
|
minimumHeight += inset[0] + inset[2]
|
||||||
|
|
||||||
element.core.SetMinimumSize(minimumWidth, minimumHeight)
|
element.core.SetMinimumSize(minimumWidth, minimumHeight)
|
||||||
@ -372,8 +400,8 @@ func (element *List) updateMinimumSize () {
|
|||||||
func (element *List) draw () {
|
func (element *List) draw () {
|
||||||
bounds := element.Bounds()
|
bounds := element.Bounds()
|
||||||
|
|
||||||
pattern, inset := theme.ListPattern(theme.PatternState {
|
inset := element.core.Inset(theme.PatternSunken)
|
||||||
Case: listCase,
|
pattern := element.core.Pattern (theme.PatternSunken, theme.PatternState {
|
||||||
Disabled: !element.Enabled(),
|
Disabled: !element.Enabled(),
|
||||||
Focused: element.Focused(),
|
Focused: element.Focused(),
|
||||||
})
|
})
|
||||||
|
@ -2,6 +2,7 @@ package basicElements
|
|||||||
|
|
||||||
import "image"
|
import "image"
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/theme"
|
import "git.tebibyte.media/sashakoshka/tomo/theme"
|
||||||
|
import "git.tebibyte.media/sashakoshka/tomo/config"
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
||||||
|
|
||||||
@ -15,6 +16,8 @@ type ListEntry struct {
|
|||||||
text string
|
text string
|
||||||
forcedMinimumWidth int
|
forcedMinimumWidth int
|
||||||
onSelect func ()
|
onSelect func ()
|
||||||
|
theme theme.Theme
|
||||||
|
config config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewListEntry (text string, onSelect func ()) (entry ListEntry) {
|
func NewListEntry (text string, onSelect func ()) (entry ListEntry) {
|
||||||
@ -23,7 +26,6 @@ func NewListEntry (text string, onSelect func ()) (entry ListEntry) {
|
|||||||
onSelect: onSelect,
|
onSelect: onSelect,
|
||||||
}
|
}
|
||||||
entry.drawer.SetText([]rune(text))
|
entry.drawer.SetText([]rune(text))
|
||||||
entry.drawer.SetFace(theme.FontFaceRegular())
|
|
||||||
entry.updateBounds()
|
entry.updateBounds()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -34,6 +36,19 @@ func (entry *ListEntry) Collapse (width int) {
|
|||||||
entry.updateBounds()
|
entry.updateBounds()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (entry *ListEntry) SetTheme (new theme.Theme) {
|
||||||
|
entry.theme = new
|
||||||
|
entry.drawer.SetFace (entry.theme.FontFace (
|
||||||
|
theme.FontStyleRegular,
|
||||||
|
theme.FontSizeNormal,
|
||||||
|
listEntryCase))
|
||||||
|
entry.updateBounds()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *ListEntry) SetConfig (config config.Config) {
|
||||||
|
entry.config = config
|
||||||
|
}
|
||||||
|
|
||||||
func (entry *ListEntry) updateBounds () {
|
func (entry *ListEntry) updateBounds () {
|
||||||
entry.bounds = image.Rectangle { }
|
entry.bounds = image.Rectangle { }
|
||||||
entry.bounds.Max.Y = entry.drawer.LineHeight().Round()
|
entry.bounds.Max.Y = entry.drawer.LineHeight().Round()
|
||||||
@ -43,8 +58,7 @@ func (entry *ListEntry) updateBounds () {
|
|||||||
entry.bounds.Max.X = entry.drawer.LayoutBounds().Dx()
|
entry.bounds.Max.X = entry.drawer.LayoutBounds().Dx()
|
||||||
}
|
}
|
||||||
|
|
||||||
_, inset := theme.ItemPattern(theme.PatternState {
|
inset := entry.theme.Inset(theme.PatternRaised, listEntryCase)
|
||||||
})
|
|
||||||
entry.bounds.Max.Y += inset[0] + inset[2]
|
entry.bounds.Max.Y += inset[0] + inset[2]
|
||||||
|
|
||||||
entry.textPoint =
|
entry.textPoint =
|
||||||
@ -60,20 +74,16 @@ func (entry *ListEntry) Draw (
|
|||||||
) (
|
) (
|
||||||
updatedRegion image.Rectangle,
|
updatedRegion image.Rectangle,
|
||||||
) {
|
) {
|
||||||
pattern, _ := theme.ItemPattern(theme.PatternState {
|
state := theme.PatternState {
|
||||||
Case: listEntryCase,
|
|
||||||
Focused: focused,
|
Focused: focused,
|
||||||
On: on,
|
On: on,
|
||||||
})
|
}
|
||||||
|
pattern := entry.theme.Pattern (theme.PatternRaised, listEntryCase, state)
|
||||||
artist.FillRectangle (
|
artist.FillRectangle (
|
||||||
destination,
|
destination,
|
||||||
pattern,
|
pattern,
|
||||||
entry.Bounds().Add(offset))
|
entry.Bounds().Add(offset))
|
||||||
foreground, _ := theme.ForegroundPattern (theme.PatternState {
|
foreground := entry.theme.Pattern (theme.PatternForeground, listEntryCase, state)
|
||||||
Case: listEntryCase,
|
|
||||||
Focused: focused,
|
|
||||||
On: on,
|
|
||||||
})
|
|
||||||
return entry.drawer.Draw (
|
return entry.drawer.Draw (
|
||||||
destination,
|
destination,
|
||||||
foreground,
|
foreground,
|
||||||
|
@ -16,11 +16,24 @@ type ProgressBar struct {
|
|||||||
// level.
|
// level.
|
||||||
func NewProgressBar (progress float64) (element *ProgressBar) {
|
func NewProgressBar (progress float64) (element *ProgressBar) {
|
||||||
element = &ProgressBar { progress: progress }
|
element = &ProgressBar { progress: progress }
|
||||||
element.Core, element.core = core.NewCore(element.draw)
|
element.Core, element.core = core.NewCore (
|
||||||
element.core.SetMinimumSize(theme.Padding() * 2, theme.Padding() * 2)
|
element.draw,
|
||||||
|
element.redo,
|
||||||
|
element.redo,
|
||||||
|
theme.C("basic", "progressBar"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (element *ProgressBar) redo () {
|
||||||
|
element.core.SetMinimumSize (
|
||||||
|
element.core.Config().Padding() * 2,
|
||||||
|
element.core.Config().Padding() * 2)
|
||||||
|
if element.core.HasImage() {
|
||||||
|
element.draw()
|
||||||
|
element.core.DamageAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SetProgress sets the progress level of the bar.
|
// SetProgress sets the progress level of the bar.
|
||||||
func (element *ProgressBar) SetProgress (progress float64) {
|
func (element *ProgressBar) SetProgress (progress float64) {
|
||||||
if progress == element.progress { return }
|
if progress == element.progress { return }
|
||||||
@ -34,13 +47,18 @@ func (element *ProgressBar) SetProgress (progress float64) {
|
|||||||
func (element *ProgressBar) draw () {
|
func (element *ProgressBar) draw () {
|
||||||
bounds := element.Bounds()
|
bounds := element.Bounds()
|
||||||
|
|
||||||
pattern, inset := theme.SunkenPattern(theme.PatternState { })
|
pattern := element.core.Pattern (
|
||||||
|
theme.PatternSunken,
|
||||||
|
theme.PatternState { })
|
||||||
|
inset := element.core.Inset(theme.PatternSunken)
|
||||||
artist.FillRectangle(element, pattern, bounds)
|
artist.FillRectangle(element, pattern, bounds)
|
||||||
bounds = inset.Apply(bounds)
|
bounds = inset.Apply(bounds)
|
||||||
meterBounds := image.Rect (
|
meterBounds := image.Rect (
|
||||||
bounds.Min.X, bounds.Min.Y,
|
bounds.Min.X, bounds.Min.Y,
|
||||||
bounds.Min.X + int(float64(bounds.Dx()) * element.progress),
|
bounds.Min.X + int(float64(bounds.Dx()) * element.progress),
|
||||||
bounds.Max.Y)
|
bounds.Max.Y)
|
||||||
accent, _ := theme.AccentPattern(theme.PatternState { })
|
accent := element.core.Pattern (
|
||||||
|
theme.PatternSunken,
|
||||||
|
theme.PatternState { })
|
||||||
artist.FillRectangle(element, accent, meterBounds)
|
artist.FillRectangle(element, accent, meterBounds)
|
||||||
}
|
}
|
||||||
|
@ -22,27 +22,27 @@ type Core struct {
|
|||||||
theme theme.Theme
|
theme theme.Theme
|
||||||
c theme.Case
|
c theme.Case
|
||||||
|
|
||||||
drawSizeChange func ()
|
handleSizeChange func ()
|
||||||
onConfigChange func ()
|
handleConfigChange func ()
|
||||||
onThemeChange func ()
|
handleThemeChange func ()
|
||||||
onMinimumSizeChange func ()
|
onMinimumSizeChange func ()
|
||||||
onDamage func (region canvas.Canvas)
|
onDamage func (region canvas.Canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCore creates a new element core and its corresponding control.
|
// NewCore creates a new element core and its corresponding control.
|
||||||
func NewCore (
|
func NewCore (
|
||||||
drawSizeChange func (),
|
handleSizeChange func (),
|
||||||
onConfigChange func (),
|
handleConfigChange func (),
|
||||||
onThemeChange func (),
|
handleThemeChange func (),
|
||||||
c theme.Case,
|
c theme.Case,
|
||||||
) (
|
) (
|
||||||
core *Core,
|
core *Core,
|
||||||
control CoreControl,
|
control CoreControl,
|
||||||
) {
|
) {
|
||||||
core = &Core {
|
core = &Core {
|
||||||
drawSizeChange: drawSizeChange,
|
handleSizeChange: handleSizeChange,
|
||||||
onConfigChange: onConfigChange,
|
handleConfigChange: handleConfigChange,
|
||||||
onThemeChange: onThemeChange,
|
handleThemeChange: handleThemeChange,
|
||||||
c: c,
|
c: c,
|
||||||
}
|
}
|
||||||
control = CoreControl { core: core }
|
control = CoreControl { core: core }
|
||||||
@ -88,8 +88,8 @@ func (core *Core) MinimumSize () (width, height int) {
|
|||||||
// overridden.
|
// overridden.
|
||||||
func (core *Core) DrawTo (canvas canvas.Canvas) {
|
func (core *Core) DrawTo (canvas canvas.Canvas) {
|
||||||
core.canvas = canvas
|
core.canvas = canvas
|
||||||
if core.drawSizeChange != nil {
|
if core.handleSizeChange != nil {
|
||||||
core.drawSizeChange()
|
core.handleSizeChange()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,8 +109,8 @@ func (core *Core) OnMinimumSizeChange (callback func ()) {
|
|||||||
// to be overridden.
|
// to be overridden.
|
||||||
func (core *Core) SetConfig (config config.Config) {
|
func (core *Core) SetConfig (config config.Config) {
|
||||||
core.config = config
|
core.config = config
|
||||||
if core.onConfigChange != nil {
|
if core.handleConfigChange != nil {
|
||||||
core.onConfigChange()
|
core.handleConfigChange()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,8 +118,8 @@ func (core *Core) SetConfig (config config.Config) {
|
|||||||
// to be overridden.
|
// to be overridden.
|
||||||
func (core *Core) SetTheme (theme theme.Theme) {
|
func (core *Core) SetTheme (theme theme.Theme) {
|
||||||
core.theme = theme
|
core.theme = theme
|
||||||
if core.onThemeChange != nil {
|
if core.handleThemeChange != nil {
|
||||||
core.onThemeChange()
|
core.handleThemeChange()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user