This commit is contained in:
Sasha Koshka 2023-05-03 01:07:44 -04:00
parent 2ac23185e6
commit 37df313544
27 changed files with 293 additions and 411 deletions

View File

@ -4,7 +4,6 @@ import "image"
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/shatter" import "git.tebibyte.media/sashakoshka/tomo/shatter"
import "git.tebibyte.media/sashakoshka/tomo/ability"
var boxCase = tomo.C("tomo", "box") var boxCase = tomo.C("tomo", "box")

View File

@ -4,9 +4,10 @@ import "image"
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/ability"
import "git.tebibyte.media/sashakoshka/tomo/textdraw" import "git.tebibyte.media/sashakoshka/tomo/textdraw"
var buttonCase = tomo.C("tomo", "button")
// Button is a clickable button. // Button is a clickable button.
type Button struct { type Button struct {
entity tomo.Entity entity tomo.Entity
@ -26,11 +27,11 @@ 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 { showText: true, enabled: true } element = &Button { showText: true, enabled: true }
element.entity = tomo.NewEntity(element).(buttonEntity) element.entity = tomo.GetBackend().NewEntity(element)
element.theme.Case = tomo.C("tomo", "button") element.drawer.SetFace (element.entity.Theme().FontFace (
element.drawer.SetFace (element.theme.FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal)) tomo.FontSizeNormal,
buttonCase))
element.SetText(text) element.SetText(text)
return return
} }
@ -44,13 +45,13 @@ func (element *Button) Entity () tomo.Entity {
func (element *Button) Draw (destination artist.Canvas) { func (element *Button) Draw (destination artist.Canvas) {
state := element.state() state := element.state()
bounds := element.entity.Bounds() bounds := element.entity.Bounds()
pattern := element.theme.Pattern(tomo.PatternButton, state) pattern := element.entity.Theme().Pattern(tomo.PatternButton, state, buttonCase)
pattern.Draw(destination, bounds) pattern.Draw(destination, bounds)
foreground := element.theme.Color(tomo.ColorForeground, state) foreground := element.entity.Theme().Color(tomo.ColorForeground, state, buttonCase)
sink := element.theme.Sink(tomo.PatternButton) sink := element.entity.Theme().Sink(tomo.PatternButton, buttonCase)
margin := element.theme.Margin(tomo.PatternButton) margin := element.entity.Theme().Margin(tomo.PatternButton, buttonCase)
offset := image.Pt ( offset := image.Pt (
bounds.Dx() / 2, bounds.Dx() / 2,
@ -65,7 +66,7 @@ func (element *Button) Draw (destination artist.Canvas) {
} }
if element.hasIcon { if element.hasIcon {
icon := element.theme.Icon(element.iconId, tomo.IconSizeSmall) icon := element.entity.Theme().Icon(element.iconId, tomo.IconSizeSmall, buttonCase)
if icon != nil { if icon != nil {
iconBounds := icon.Bounds() iconBounds := icon.Bounds()
addedWidth := iconBounds.Dx() addedWidth := iconBounds.Dx()
@ -150,21 +151,11 @@ func (element *Button) ShowText (showText bool) {
element.entity.Invalidate() element.entity.Invalidate()
} }
// SetTheme sets the element's theme. func (element *Button) HandleThemeChange () {
func (element *Button) SetTheme (new tomo.Theme) { element.drawer.SetFace (element.entity.Theme().FontFace (
if new == element.theme.Theme { return }
element.theme.Theme = new
element.drawer.SetFace (element.theme.FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal)) tomo.FontSizeNormal,
element.updateMinimumSize() buttonCase))
element.entity.Invalidate()
}
// SetConfig sets the element's configuration.
func (element *Button) SetConfig (new tomo.Config) {
if new == element.config.Config { return }
element.config.Config = new
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
} }
@ -219,14 +210,14 @@ func (element *Button) HandleKeyUp(key input.Key, modifiers input.Modifiers) {
} }
func (element *Button) updateMinimumSize () { func (element *Button) updateMinimumSize () {
padding := element.theme.Padding(tomo.PatternButton) padding := element.entity.Theme().Padding(tomo.PatternButton, buttonCase)
margin := element.theme.Margin(tomo.PatternButton) margin := element.entity.Theme().Margin(tomo.PatternButton, buttonCase)
textBounds := element.drawer.LayoutBounds() textBounds := element.drawer.LayoutBounds()
minimumSize := textBounds.Sub(textBounds.Min) minimumSize := textBounds.Sub(textBounds.Min)
if element.hasIcon { if element.hasIcon {
icon := element.theme.Icon(element.iconId, tomo.IconSizeSmall) icon := element.entity.Theme().Icon(element.iconId, tomo.IconSizeSmall, buttonCase)
if icon != nil { if icon != nil {
bounds := icon.Bounds() bounds := icon.Bounds()
if element.showText { if element.showText {

View File

@ -2,8 +2,9 @@ package elements
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/ability" import "git.tebibyte.media/sashakoshka/tomo/artist/artutil"
var cellCase = tomo.C("tomo", "cell")
// Cell is a single-element container that satisfies tomo.Selectable. It // Cell is a single-element container that satisfies tomo.Selectable. It
// provides styling based on whether or not it is selected. // provides styling based on whether or not it is selected.
@ -20,8 +21,7 @@ type Cell struct {
// method. // method.
func NewCell (child tomo.Element) (element *Cell) { func NewCell (child tomo.Element) (element *Cell) {
element = &Cell { enabled: true } element = &Cell { enabled: true }
element.theme.Case = tomo.C("tomo", "cell") element.entity = tomo.GetBackend().NewEntity(element)
element.entity = tomo.NewEntity(element).(cellEntity)
element.Adopt(child) element.Adopt(child)
return return
} }
@ -34,11 +34,11 @@ func (element *Cell) Entity () tomo.Entity {
// Draw causes the element to draw to the specified destination canvas. // Draw causes the element to draw to the specified destination canvas.
func (element *Cell) Draw (destination artist.Canvas) { func (element *Cell) Draw (destination artist.Canvas) {
bounds := element.entity.Bounds() bounds := element.entity.Bounds()
pattern := element.theme.Pattern(tomo.PatternTableCell, element.state()) pattern := element.entity.Theme().Pattern(tomo.PatternTableCell, element.state(), cellCase)
if element.child == nil { if element.child == nil {
pattern.Draw(destination, bounds) pattern.Draw(destination, bounds)
} else { } else {
artist.DrawShatter ( artutil.DrawShatter (
destination, pattern, bounds, destination, pattern, bounds,
element.child.Entity().Bounds()) element.child.Entity().Bounds())
} }
@ -49,7 +49,7 @@ func (element *Cell) Layout () {
if element.child == nil { return } if element.child == nil { return }
bounds := element.entity.Bounds() bounds := element.entity.Bounds()
bounds = element.theme.Padding(tomo.PatternTableCell).Apply(bounds) bounds = element.entity.Theme().Padding(tomo.PatternTableCell, cellCase).Apply(bounds)
element.entity.PlaceChild(0, bounds) element.entity.PlaceChild(0, bounds)
} }
@ -57,7 +57,7 @@ func (element *Cell) Layout () {
// DrawBackground draws this element's background pattern to the specified // DrawBackground draws this element's background pattern to the specified
// destination canvas. // destination canvas.
func (element *Cell) DrawBackground (destination artist.Canvas) { func (element *Cell) DrawBackground (destination artist.Canvas) {
element.theme.Pattern(tomo.PatternTableCell, element.state()). element.entity.Theme().Pattern(tomo.PatternTableCell, element.state(), cellCase).
Draw(destination, element.entity.Bounds()) Draw(destination, element.entity.Bounds())
} }
@ -96,16 +96,6 @@ func (element *Cell) SetEnabled (enabled bool) {
element.invalidateChild() element.invalidateChild()
} }
// SetTheme sets this element's theme.
func (element *Cell) SetTheme (theme tomo.Theme) {
if theme == element.theme.Theme { return }
element.theme.Theme = theme
element.updateMinimumSize()
element.entity.Invalidate()
element.invalidateChild()
element.entity.InvalidateLayout()
}
// OnSelectionChange sets a function to be called when this element is selected // OnSelectionChange sets a function to be called when this element is selected
// or unselected. // or unselected.
func (element *Cell) OnSelectionChange (callback func ()) { func (element *Cell) OnSelectionChange (callback func ()) {
@ -116,6 +106,13 @@ func (element *Cell) Selected () bool {
return element.entity.Selected() return element.entity.Selected()
} }
func (element *Cell) HandleThemeChange () {
element.updateMinimumSize()
element.entity.Invalidate()
element.invalidateChild()
element.entity.InvalidateLayout()
}
func (element *Cell) HandleSelectionChange () { func (element *Cell) HandleSelectionChange () {
element.entity.Invalidate() element.entity.Invalidate()
element.invalidateChild() element.invalidateChild()
@ -145,7 +142,7 @@ func (element *Cell) updateMinimumSize () {
width += childWidth width += childWidth
height += childHeight height += childHeight
} }
padding := element.theme.Padding(tomo.PatternTableCell) padding := element.entity.Theme().Padding(tomo.PatternTableCell, cellCase)
width += padding.Horizontal() width += padding.Horizontal()
height += padding.Vertical() height += padding.Vertical()

View File

@ -4,9 +4,10 @@ import "image"
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/ability"
import "git.tebibyte.media/sashakoshka/tomo/textdraw" import "git.tebibyte.media/sashakoshka/tomo/textdraw"
var checkboxCase = tomo.C("tomo", "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 {
entity tomo.Entity entity tomo.Entity
@ -23,11 +24,11 @@ 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, enabled: true } element = &Checkbox { checked: checked, enabled: true }
element.entity = tomo.NewEntity(element).(checkboxEntity) element.entity = tomo.GetBackend().NewEntity(element)
element.theme.Case = tomo.C("tomo", "checkbox") element.drawer.SetFace (element.entity.Theme().FontFace (
element.drawer.SetFace (element.theme.FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal)) tomo.FontSizeNormal,
checkboxCase))
element.SetText(text) element.SetText(text)
return return
} }
@ -51,11 +52,11 @@ func (element *Checkbox) Draw (destination artist.Canvas) {
element.entity.DrawBackground(destination) element.entity.DrawBackground(destination)
pattern := element.theme.Pattern(tomo.PatternButton, state) pattern := element.entity.Theme().Pattern(tomo.PatternButton, state, checkboxCase)
pattern.Draw(destination, boxBounds) pattern.Draw(destination, boxBounds)
textBounds := element.drawer.LayoutBounds() textBounds := element.drawer.LayoutBounds()
margin := element.theme.Margin(tomo.PatternBackground) margin := element.entity.Theme().Margin(tomo.PatternBackground, checkboxCase)
offset := bounds.Min.Add(image.Point { offset := bounds.Min.Add(image.Point {
X: bounds.Dy() + margin.X, X: bounds.Dy() + margin.X,
}) })
@ -63,7 +64,7 @@ func (element *Checkbox) Draw (destination artist.Canvas) {
offset.Y -= textBounds.Min.Y offset.Y -= textBounds.Min.Y
offset.X -= textBounds.Min.X offset.X -= textBounds.Min.X
foreground := element.theme.Color(tomo.ColorForeground, state) foreground := element.entity.Theme().Color(tomo.ColorForeground, state, checkboxCase)
element.drawer.Draw(destination, foreground, offset) element.drawer.Draw(destination, foreground, offset)
} }
@ -103,21 +104,11 @@ func (element *Checkbox) SetText (text string) {
element.entity.Invalidate() element.entity.Invalidate()
} }
// SetTheme sets the element's theme. func (element *Checkbox) HandleThemeChange () {
func (element *Checkbox) SetTheme (new tomo.Theme) { element.drawer.SetFace (element.entity.Theme().FontFace (
if new == element.theme.Theme { return }
element.theme.Theme = new
element.drawer.SetFace (element.theme.FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal)) tomo.FontSizeNormal,
element.updateMinimumSize() checkboxCase))
element.entity.Invalidate()
}
// SetConfig sets the element's configuration.
func (element *Checkbox) SetConfig (new tomo.Config) {
if new == element.config.Config { return }
element.config.Config = new
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
} }
@ -179,7 +170,7 @@ func (element *Checkbox) updateMinimumSize () {
if element.text == "" { if element.text == "" {
element.entity.SetMinimumSize(textBounds.Dy(), textBounds.Dy()) element.entity.SetMinimumSize(textBounds.Dy(), textBounds.Dy())
} else { } else {
margin := element.theme.Margin(tomo.PatternBackground) margin := element.entity.Theme().Margin(tomo.PatternBackground, checkboxCase)
element.entity.SetMinimumSize ( element.entity.SetMinimumSize (
textBounds.Dy() + margin.X + textBounds.Dx(), textBounds.Dy() + margin.X + textBounds.Dx(),
textBounds.Dy()) textBounds.Dy())

View File

@ -7,6 +7,8 @@ import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/ability" import "git.tebibyte.media/sashakoshka/tomo/ability"
import "git.tebibyte.media/sashakoshka/tomo/textdraw" import "git.tebibyte.media/sashakoshka/tomo/textdraw"
var comboBoxCase = tomo.C("tomo", "comboBox")
// Option specifies a ComboBox option. A blank option will display as "(None)". // Option specifies a ComboBox option. A blank option will display as "(None)".
type Option string type Option string
@ -36,11 +38,11 @@ type ComboBox struct {
func NewComboBox (options ...Option) (element *ComboBox) { func NewComboBox (options ...Option) (element *ComboBox) {
if len(options) == 0 { options = []Option { "" } } if len(options) == 0 { options = []Option { "" } }
element = &ComboBox { enabled: true, options: options } element = &ComboBox { enabled: true, options: options }
element.entity = tomo.NewEntity(element).(tomo.FocusableEntity) element.entity = tomo.GetBackend().NewEntity(element)
element.theme.Case = tomo.C("tomo", "comboBox") element.drawer.SetFace (element.entity.Theme().FontFace (
element.drawer.SetFace (element.theme.FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal)) tomo.FontSizeNormal,
comboBoxCase))
element.Select(options[0]) element.Select(options[0])
return return
} }
@ -54,14 +56,14 @@ func (element *ComboBox) Entity () tomo.Entity {
func (element *ComboBox) Draw (destination artist.Canvas) { func (element *ComboBox) Draw (destination artist.Canvas) {
state := element.state() state := element.state()
bounds := element.entity.Bounds() bounds := element.entity.Bounds()
pattern := element.theme.Pattern(tomo.PatternButton, state) pattern := element.entity.Theme().Pattern(tomo.PatternButton, state, comboBoxCase)
pattern.Draw(destination, bounds) pattern.Draw(destination, bounds)
foreground := element.theme.Color(tomo.ColorForeground, state) foreground := element.entity.Theme().Color(tomo.ColorForeground, state, comboBoxCase)
sink := element.theme.Sink(tomo.PatternButton) sink := element.entity.Theme().Sink(tomo.PatternButton, comboBoxCase)
margin := element.theme.Margin(tomo.PatternButton) margin := element.entity.Theme().Margin(tomo.PatternButton, comboBoxCase)
padding := element.theme.Padding(tomo.PatternButton) padding := element.entity.Theme().Padding(tomo.PatternButton, comboBoxCase)
offset := image.Pt(0, bounds.Dy() / 2).Add(bounds.Min) offset := image.Pt(0, bounds.Dy() / 2).Add(bounds.Min)
@ -70,7 +72,7 @@ func (element *ComboBox) Draw (destination artist.Canvas) {
offset.Y -= textBounds.Min.Y offset.Y -= textBounds.Min.Y
offset.X -= textBounds.Min.X offset.X -= textBounds.Min.X
icon := element.theme.Icon(tomo.IconExpand, tomo.IconSizeSmall) icon := element.entity.Theme().Icon(tomo.IconExpand, tomo.IconSizeSmall, comboBoxCase)
if icon != nil { if icon != nil {
iconBounds := icon.Bounds() iconBounds := icon.Bounds()
addedWidth := iconBounds.Dx() + margin.X addedWidth := iconBounds.Dx() + margin.X
@ -138,21 +140,11 @@ func (element *ComboBox) SetEnabled (enabled bool) {
element.entity.Invalidate() element.entity.Invalidate()
} }
// SetTheme sets the element's theme. func (element *ComboBox) HandleThemeChange () {
func (element *ComboBox) SetTheme (new tomo.Theme) { element.drawer.SetFace (element.entity.Theme().FontFace (
if new == element.theme.Theme { return }
element.theme.Theme = new
element.drawer.SetFace (element.theme.FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal)) tomo.FontSizeNormal,
element.updateMinimumSize() comboBoxCase))
element.entity.Invalidate()
}
// SetConfig sets the element's configuration.
func (element *ComboBox) SetConfig (new tomo.Config) {
if new == element.config.Config { return }
element.config.Config = new
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
} }
@ -224,7 +216,7 @@ func (element *ComboBox) dropDown () {
menu, err := window.NewMenu(element.entity.Bounds()) menu, err := window.NewMenu(element.entity.Bounds())
if err != nil { return } if err != nil { return }
cellToOption := make(map[tomo.Selectable] Option) cellToOption := make(map[ability.Selectable] Option)
list := NewList() list := NewList()
for _, option := range element.options { for _, option := range element.options {
@ -250,13 +242,13 @@ func (element *ComboBox) dropDown () {
} }
func (element *ComboBox) updateMinimumSize () { func (element *ComboBox) updateMinimumSize () {
padding := element.theme.Padding(tomo.PatternButton) padding := element.entity.Theme().Padding(tomo.PatternButton, comboBoxCase)
margin := element.theme.Margin(tomo.PatternButton) margin := element.entity.Theme().Margin(tomo.PatternButton, comboBoxCase)
textBounds := element.drawer.LayoutBounds() textBounds := element.drawer.LayoutBounds()
minimumSize := textBounds.Sub(textBounds.Min) minimumSize := textBounds.Sub(textBounds.Min)
icon := element.theme.Icon(tomo.IconExpand, tomo.IconSizeSmall) icon := element.entity.Theme().Icon(tomo.IconExpand, tomo.IconSizeSmall, comboBoxCase)
if icon != nil { if icon != nil {
bounds := icon.Bounds() bounds := icon.Bounds()
minimumSize.Max.X += bounds.Dx() minimumSize.Max.X += bounds.Dx()

View File

@ -1,7 +1,6 @@
package elements package elements
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/ability"
type scratchEntry struct { type scratchEntry struct {
expand bool expand bool

View File

@ -11,6 +11,8 @@ import "git.tebibyte.media/sashakoshka/tomo/shatter"
// TODO: base on flow implementation of list. also be able to switch to a table // TODO: base on flow implementation of list. also be able to switch to a table
// variant for a more information dense view. // variant for a more information dense view.
var directoryCase = tomo.C("tomo", "list")
type historyEntry struct { type historyEntry struct {
location string location string
filesystem ReadDirStatFS filesystem ReadDirStatFS
@ -42,8 +44,7 @@ func NewDirectory (
err error, err error,
) { ) {
element = &Directory { } element = &Directory { }
element.theme.Case = tomo.C("tomo", "list") element.entity = tomo.GetBackend().NewEntity(element)
element.entity = tomo.NewEntity(element).(directoryEntity)
element.container.entity = element.entity element.container.entity = element.entity
element.minimumSize = element.updateMinimumSize element.minimumSize = element.updateMinimumSize
element.init() element.init()
@ -59,7 +60,7 @@ func (element *Directory) Draw (destination artist.Canvas) {
tiles := shatter.Shatter(element.entity.Bounds(), rocks...) tiles := shatter.Shatter(element.entity.Bounds(), rocks...)
for _, tile := range tiles { for _, tile := range tiles {
element.DrawBackground(canvas.Cut(destination, tile)) element.DrawBackground(artist.Cut(destination, tile))
} }
} }
@ -68,8 +69,8 @@ func (element *Directory) Layout () {
element.scroll.Y = element.maxScrollHeight() element.scroll.Y = element.maxScrollHeight()
} }
margin := element.theme.Margin(tomo.PatternPinboard) margin := element.entity.Theme().Margin(tomo.PatternPinboard, directoryCase)
padding := element.theme.Padding(tomo.PatternPinboard) padding := element.entity.Theme().Padding(tomo.PatternPinboard, directoryCase)
bounds := padding.Apply(element.entity.Bounds()) bounds := padding.Apply(element.entity.Bounds())
element.contentBounds = image.Rectangle { } element.contentBounds = image.Rectangle { }
@ -93,7 +94,7 @@ func (element *Directory) Layout () {
if width + dot.X > bounds.Max.X { if width + dot.X > bounds.Max.X {
nextLine() nextLine()
} }
if typedChild, ok := child.(tomo.Flexible); ok { if typedChild, ok := child.(ability.Flexible); ok {
height = typedChild.FlexibleHeightFor(width) height = typedChild.FlexibleHeightFor(width)
} }
if rowHeight < height { if rowHeight < height {
@ -139,7 +140,7 @@ func (element *Directory) HandleChildMouseDown (
child tomo.Element, child tomo.Element,
) { ) {
element.selectNone() element.selectNone()
if child, ok := child.(tomo.Selectable); ok { if child, ok := child.(ability.Selectable); ok {
index := element.entity.IndexOf(child) index := element.entity.IndexOf(child)
element.entity.SelectChild(index, true) element.entity.SelectChild(index, true)
} }
@ -166,7 +167,7 @@ func (element *Directory) ScrollContentBounds () image.Rectangle {
// ScrollViewportBounds returns the size and position of the element's // ScrollViewportBounds returns the size and position of the element's
// viewport relative to ScrollBounds. // viewport relative to ScrollBounds.
func (element *Directory) ScrollViewportBounds () image.Rectangle { func (element *Directory) ScrollViewportBounds () image.Rectangle {
padding := element.theme.Padding(tomo.PatternPinboard) padding := element.entity.Theme().Padding(tomo.PatternPinboard, directoryCase)
bounds := padding.Apply(element.entity.Bounds()) bounds := padding.Apply(element.entity.Bounds())
bounds = bounds.Sub(bounds.Min).Add(element.scroll) bounds = bounds.Sub(bounds.Min).Add(element.scroll)
return bounds return bounds
@ -199,14 +200,11 @@ func (element *Directory) ScrollAxes () (horizontal, vertical bool) {
} }
func (element *Directory) DrawBackground (destination artist.Canvas) { func (element *Directory) DrawBackground (destination artist.Canvas) {
element.theme.Pattern(tomo.PatternPinboard, tomo.State { }). element.entity.Theme().Pattern(tomo.PatternPinboard, tomo.State { }, directoryCase).
Draw(destination, element.entity.Bounds()) Draw(destination, element.entity.Bounds())
} }
// SetTheme sets the element's theme. func (element *Directory) HandleThemeChange () {
func (element *Directory) SetTheme (theme tomo.Theme) {
if theme == element.theme.Theme { return }
element.theme.Theme = theme
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
element.entity.InvalidateLayout() element.entity.InvalidateLayout()
@ -295,7 +293,7 @@ func (element *Directory) selectNone () {
} }
func (element *Directory) maxScrollHeight () (height int) { func (element *Directory) maxScrollHeight () (height int) {
padding := element.theme.Padding(tomo.PatternSunken) padding := element.entity.Theme().Padding(tomo.PatternSunken, directoryCase)
viewportHeight := element.entity.Bounds().Dy() - padding.Vertical() viewportHeight := element.entity.Bounds().Dy() - padding.Vertical()
height = element.contentBounds.Dy() - viewportHeight height = element.contentBounds.Dy() - viewportHeight
if height < 0 { height = 0 } if height < 0 { height = 0 }
@ -304,7 +302,7 @@ func (element *Directory) maxScrollHeight () (height int) {
func (element *Directory) updateMinimumSize () { func (element *Directory) updateMinimumSize () {
padding := element.theme.Padding(tomo.PatternPinboard) padding := element.entity.Theme().Padding(tomo.PatternPinboard, directoryCase)
minimumWidth := 0 minimumWidth := 0
for index := 0; index < element.entity.CountChildren(); index ++ { for index := 0; index < element.entity.CountChildren(); index ++ {
width, height := element.entity.ChildMinimumSize(index) width, height := element.entity.ChildMinimumSize(index)

View File

@ -6,6 +6,8 @@ import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/ability" import "git.tebibyte.media/sashakoshka/tomo/ability"
import "git.tebibyte.media/sashakoshka/tomo/shatter" import "git.tebibyte.media/sashakoshka/tomo/shatter"
var documentCase = tomo.C("tomo", "document")
// Document is a scrollable container capcable of laying out flexible child // Document is a scrollable container capcable of laying out flexible child
// elements. Children can be added either inline (similar to an HTML/CSS inline // elements. Children can be added either inline (similar to an HTML/CSS inline
// element), or expanding (similar to an HTML/CSS block element). // element), or expanding (similar to an HTML/CSS block element).
@ -22,8 +24,7 @@ type Document struct {
// NewDocument creates a new document container. // NewDocument creates a new document container.
func NewDocument (children ...tomo.Element) (element *Document) { func NewDocument (children ...tomo.Element) (element *Document) {
element = &Document { } element = &Document { }
element.theme.Case = tomo.C("tomo", "document") element.entity = tomo.GetBackend().NewEntity(element)
element.entity = tomo.NewEntity(element)
element.container.entity = element.entity element.container.entity = element.entity
element.minimumSize = element.updateMinimumSize element.minimumSize = element.updateMinimumSize
element.init() element.init()
@ -40,7 +41,7 @@ func (element *Document) Draw (destination artist.Canvas) {
tiles := shatter.Shatter(element.entity.Bounds(), rocks...) tiles := shatter.Shatter(element.entity.Bounds(), rocks...)
for _, tile := range tiles { for _, tile := range tiles {
element.entity.DrawBackground(canvas.Cut(destination, tile)) element.entity.DrawBackground(artist.Cut(destination, tile))
} }
} }
@ -50,8 +51,8 @@ func (element *Document) Layout () {
element.scroll.Y = element.maxScrollHeight() element.scroll.Y = element.maxScrollHeight()
} }
margin := element.theme.Margin(tomo.PatternBackground) margin := element.entity.Theme().Margin(tomo.PatternBackground, documentCase)
padding := element.theme.Padding(tomo.PatternBackground) padding := element.entity.Theme().Padding(tomo.PatternBackground, documentCase)
bounds := padding.Apply(element.entity.Bounds()) bounds := padding.Apply(element.entity.Bounds())
element.contentBounds = image.Rectangle { } element.contentBounds = image.Rectangle { }
@ -82,7 +83,7 @@ func (element *Document) Layout () {
if width < bounds.Dx() && entry.expand { if width < bounds.Dx() && entry.expand {
width = bounds.Dx() width = bounds.Dx()
} }
if typedChild, ok := child.(tomo.Flexible); ok { if typedChild, ok := child.(ability.Flexible); ok {
height = typedChild.FlexibleHeightFor(width) height = typedChild.FlexibleHeightFor(width)
} }
if rowHeight < height { if rowHeight < height {
@ -135,10 +136,7 @@ func (element *Document) DrawBackground (destination artist.Canvas) {
element.entity.DrawBackground(destination) element.entity.DrawBackground(destination)
} }
// SetTheme sets the element's theme. func (element *Document) HandleThemeChange () {
func (element *Document) SetTheme (theme tomo.Theme) {
if theme == element.theme.Theme { return }
element.theme.Theme = theme
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
element.entity.InvalidateLayout() element.entity.InvalidateLayout()
@ -152,7 +150,7 @@ func (element *Document) ScrollContentBounds () image.Rectangle {
// ScrollViewportBounds returns the size and position of the element's // ScrollViewportBounds returns the size and position of the element's
// viewport relative to ScrollBounds. // viewport relative to ScrollBounds.
func (element *Document) ScrollViewportBounds () image.Rectangle { func (element *Document) ScrollViewportBounds () image.Rectangle {
padding := element.theme.Padding(tomo.PatternBackground) padding := element.entity.Theme().Padding(tomo.PatternBackground, documentCase)
bounds := padding.Apply(element.entity.Bounds()) bounds := padding.Apply(element.entity.Bounds())
bounds = bounds.Sub(bounds.Min).Add(element.scroll) bounds = bounds.Sub(bounds.Min).Add(element.scroll)
return bounds return bounds
@ -185,7 +183,7 @@ func (element *Document) ScrollAxes () (horizontal, vertical bool) {
} }
func (element *Document) maxScrollHeight () (height int) { func (element *Document) maxScrollHeight () (height int) {
padding := element.theme.Padding(tomo.PatternSunken) padding := element.entity.Theme().Padding(tomo.PatternBackground, documentCase)
viewportHeight := element.entity.Bounds().Dy() - padding.Vertical() viewportHeight := element.entity.Bounds().Dy() - padding.Vertical()
height = element.contentBounds.Dy() - viewportHeight height = element.contentBounds.Dy() - viewportHeight
if height < 0 { height = 0 } if height < 0 { height = 0 }
@ -193,7 +191,7 @@ func (element *Document) maxScrollHeight () (height int) {
} }
func (element *Document) updateMinimumSize () { func (element *Document) updateMinimumSize () {
padding := element.theme.Padding(tomo.PatternBackground) padding := element.entity.Theme().Padding(tomo.PatternBackground, documentCase)
minimumWidth := 0 minimumWidth := 0
for index := 0; index < element.entity.CountChildren(); index ++ { for index := 0; index < element.entity.CountChildren(); index ++ {
width, height := element.entity.ChildMinimumSize(index) width, height := element.entity.ChildMinimumSize(index)

View File

@ -7,6 +7,8 @@ import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
var fileCase = tomo.C("files", "file")
// File displays an interactive visual representation of a file within any // File displays an interactive visual representation of a file within any
// file system. // file system.
type File struct { type File struct {
@ -32,8 +34,7 @@ func NewFile (
err error, err error,
) { ) {
element = &File { enabled: true } element = &File { enabled: true }
element.theme.Case = tomo.C("files", "file") element.entity = tomo.GetBackend().NewEntity(element)
element.entity = tomo.NewEntity(element).(fileEntity)
err = element.SetLocation(location, within) err = element.SetLocation(location, within)
return return
} }
@ -48,9 +49,9 @@ func (element *File) Draw (destination artist.Canvas) {
// background // background
state := element.state() state := element.state()
bounds := element.entity.Bounds() bounds := element.entity.Bounds()
sink := element.theme.Sink(tomo.PatternButton) sink := element.entity.Theme().Sink(tomo.PatternButton, fileCase)
element.theme. element.entity.Theme().
Pattern(tomo.PatternButton, state). Pattern(tomo.PatternButton, state, fileCase).
Draw(destination, bounds) Draw(destination, bounds)
// icon // icon
@ -65,7 +66,7 @@ func (element *File) Draw (destination artist.Canvas) {
} }
icon.Draw ( icon.Draw (
destination, destination,
element.theme.Color(tomo.ColorForeground, state), element.entity.Theme().Color(tomo.ColorForeground, state, fileCase),
bounds.Min.Add(offset)) bounds.Min.Add(offset))
} }
} }
@ -174,7 +175,7 @@ func (element *File) HandleMouseUp (
if button != input.ButtonLeft { return } if button != input.ButtonLeft { return }
element.pressed = false element.pressed = false
within := position.In(element.entity.Bounds()) within := position.In(element.entity.Bounds())
if time.Since(element.lastClick) < element.config.DoubleClickDelay() { if time.Since(element.lastClick) < element.entity.Config().DoubleClickDelay() {
if element.Enabled() && within && element.onChoose != nil { if element.Enabled() && within && element.onChoose != nil {
element.onChoose() element.onChoose()
} }
@ -184,17 +185,8 @@ func (element *File) HandleMouseUp (
element.entity.Invalidate() element.entity.Invalidate()
} }
// SetTheme sets the element's theme. func (element *File) HandleThemeChange () {
func (element *File) SetTheme (theme tomo.Theme) { element.updateMinimumSize()
if theme == element.theme.Theme { return }
element.theme.Theme = theme
element.entity.Invalidate()
}
// SetConfig sets the element's configuration.
func (element *File) SetConfig (config tomo.Config) {
if config == element.config.Config { return }
element.config.Config = config
element.entity.Invalidate() element.entity.Invalidate()
} }
@ -208,11 +200,11 @@ func (element *File) state () tomo.State {
} }
func (element *File) icon () artist.Icon { func (element *File) icon () artist.Icon {
return element.theme.Icon(element.iconID, tomo.IconSizeLarge) return element.entity.Theme().Icon(element.iconID, tomo.IconSizeLarge, fileCase)
} }
func (element *File) updateMinimumSize () { func (element *File) updateMinimumSize () {
padding := element.theme.Padding(tomo.PatternButton) padding := element.entity.Theme().Padding(tomo.PatternButton, fileCase)
icon := element.icon() icon := element.icon()
if icon == nil { if icon == nil {
element.entity.SetMinimumSize ( element.entity.SetMinimumSize (

View File

@ -4,6 +4,8 @@ import "image"
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
var iconCase = tomo.C("tomo", "icon")
// Icon is an element capable of displaying a singular icon. // Icon is an element capable of displaying a singular icon.
type Icon struct { type Icon struct {
entity tomo.Entity entity tomo.Entity
@ -17,8 +19,7 @@ func NewIcon (id tomo.Icon, size tomo.IconSize) (element *Icon) {
id: id, id: id,
size: size, size: size,
} }
element.entity = tomo.NewEntity(element).(ability.ThemeableEntity) element.entity = tomo.GetBackend().NewEntity(element)
element.theme.Case = tomo.C("tomo", "icon")
element.updateMinimumSize() element.updateMinimumSize()
return return
} }
@ -37,11 +38,7 @@ func (element *Icon) SetIcon (id tomo.Icon, size tomo.IconSize) {
element.entity.Invalidate() element.entity.Invalidate()
} }
// SetTheme sets the element's theme. func (element *Icon) HandleThemeChange () {
func (element *Icon) SetTheme (new tomo.Theme) {
if new == element.theme.Theme { return }
element.theme.Theme = new
if element.entity == nil { return }
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
} }
@ -52,8 +49,8 @@ func (element *Icon) Draw (destination artist.Canvas) {
bounds := element.entity.Bounds() bounds := element.entity.Bounds()
state := tomo.State { } state := tomo.State { }
element.theme. element.entity.Theme().
Pattern(tomo.PatternBackground, state). Pattern(tomo.PatternBackground, state, iconCase).
Draw(destination, bounds) Draw(destination, bounds)
icon := element.icon() icon := element.icon()
if icon != nil { if icon != nil {
@ -63,13 +60,13 @@ func (element *Icon) Draw (destination artist.Canvas) {
(bounds.Dy() - iconBounds.Dy()) / 2) (bounds.Dy() - iconBounds.Dy()) / 2)
icon.Draw ( icon.Draw (
destination, destination,
element.theme.Color(tomo.ColorForeground, state), element.entity.Theme().Color(tomo.ColorForeground, state, iconCase),
bounds.Min.Add(offset)) bounds.Min.Add(offset))
} }
} }
func (element *Icon) icon () artist.Icon { func (element *Icon) icon () artist.Icon {
return element.theme.Icon(element.id, element.size) return element.entity.Theme().Icon(element.id, element.size, iconCase)
} }
func (element *Icon) updateMinimumSize () { func (element *Icon) updateMinimumSize () {

View File

@ -15,8 +15,8 @@ type Image struct {
// NewImage creates a new image element. // NewImage creates a new image element.
func NewImage (image image.Image) (element *Image) { func NewImage (image image.Image) (element *Image) {
element = &Image { buffer: canvas.FromImage(image) } element = &Image { buffer: artist.FromImage(image) }
element.entity = tomo.NewEntity(element) element.entity = tomo.GetBackend().NewEntity(element)
bounds := element.buffer.Bounds() bounds := element.buffer.Bounds()
element.entity.SetMinimumSize(bounds.Dx(), bounds.Dy()) element.entity.SetMinimumSize(bounds.Dx(), bounds.Dy())
return return

View File

@ -8,6 +8,8 @@ import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/textdraw" import "git.tebibyte.media/sashakoshka/tomo/textdraw"
var labelCase = tomo.C("tomo", "label")
// Label is a simple text box. // Label is a simple text box.
type Label struct { type Label struct {
entity tomo.Entity entity tomo.Entity
@ -25,11 +27,10 @@ type Label struct {
// NewLabel creates a new label. // NewLabel creates a new label.
func NewLabel (text string) (element *Label) { func NewLabel (text string) (element *Label) {
element = &Label { } element = &Label { }
element.theme.Case = tomo.C("tomo", "label") element.entity = tomo.GetBackend().NewEntity(element)
element.entity = tomo.NewEntity(element) element.drawer.SetFace (element.entity.Theme().FontFace (
element.drawer.SetFace (element.theme.FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal)) tomo.FontSizeNormal, labelCase))
element.SetText(text) element.SetText(text)
return return
} }
@ -58,9 +59,9 @@ func (element *Label) Draw (destination artist.Canvas) {
element.entity.DrawBackground(destination) element.entity.DrawBackground(destination)
textBounds := element.drawer.LayoutBounds() textBounds := element.drawer.LayoutBounds()
foreground := element.theme.Color ( foreground := element.entity.Theme().Color (
tomo.ColorForeground, tomo.ColorForeground,
tomo.State { }) tomo.State { }, labelCase)
element.drawer.Draw(destination, foreground, bounds.Min.Sub(textBounds.Min)) element.drawer.Draw(destination, foreground, bounds.Min.Sub(textBounds.Min))
} }
@ -127,21 +128,10 @@ func (element *Label) SetAlign (align textdraw.Align) {
element.entity.Invalidate() element.entity.Invalidate()
} }
// SetTheme sets the element's theme. func (element *Label) HandleThemeChange () {
func (element *Label) SetTheme (new tomo.Theme) { element.drawer.SetFace (element.entity.Theme().FontFace (
if new == element.theme.Theme { return }
element.theme.Theme = new
element.drawer.SetFace (element.theme.FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal)) tomo.FontSizeNormal, labelCase))
element.updateMinimumSize()
element.entity.Invalidate()
}
// SetConfig sets the element's configuration.
func (element *Label) SetConfig (new tomo.Config) {
if new == element.config.Config { return }
element.config.Config = new
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
} }
@ -190,7 +180,7 @@ func (element *Label) updateMinimumSize () {
if element.wrap { if element.wrap {
em := element.drawer.Em().Round() em := element.drawer.Em().Round()
if em < 1 { if em < 1 {
em = element.theme.Padding(tomo.PatternBackground)[0] em = element.entity.Theme().Padding(tomo.PatternBackground, labelCase)[0]
} }
width, height = em, element.drawer.LineHeight().Round() width, height = em, element.drawer.LineHeight().Round()
element.entity.NotifyFlexibleHeightChange() element.entity.NotifyFlexibleHeightChange()

View File

@ -33,7 +33,7 @@ func NewHLerpSlider[T Numeric] (min, max T, value T) (element *LerpSlider[T]) {
min: min, min: min,
max: max, max: max,
} }
element.entity = tomo.NewEntity(element).(tomo.FocusableEntity) element.entity = tomo.GetBackend().NewEntity(element)
element.construct() element.construct()
element.SetValue(value) element.SetValue(value)
return return

View File

@ -4,11 +4,15 @@ import "image"
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/ability"
import "git.tebibyte.media/sashakoshka/tomo/artist/artutil"
type list struct { type list struct {
container container
entity tomo.Entity entity tomo.Entity
c tomo.Case
enabled bool enabled bool
scroll image.Point scroll image.Point
contentBounds image.Rectangle contentBounds image.Rectangle
@ -32,8 +36,8 @@ type FlowList struct {
func NewList (children ...tomo.Element) (element *List) { func NewList (children ...tomo.Element) (element *List) {
element = &List { } element = &List { }
element.theme.Case = tomo.C("tomo", "list") element.c = tomo.C("tomo", "list")
element.entity = tomo.NewEntity(element) element.entity = tomo.GetBackend().NewEntity(element)
element.container.entity = element.entity element.container.entity = element.entity
element.minimumSize = element.updateMinimumSize element.minimumSize = element.updateMinimumSize
element.init(children...) element.init(children...)
@ -42,8 +46,8 @@ func NewList (children ...tomo.Element) (element *List) {
func NewFlowList (children ...tomo.Element) (element *FlowList) { func NewFlowList (children ...tomo.Element) (element *FlowList) {
element = &FlowList { } element = &FlowList { }
element.theme.Case = tomo.C("tomo", "flowList") element.c = tomo.C("tomo", "flowList")
element.entity = tomo.NewEntity(element).(listEntity) element.entity = tomo.GetBackend().NewEntity(element)
element.container.entity = element.entity element.container.entity = element.entity
element.minimumSize = element.updateMinimumSize element.minimumSize = element.updateMinimumSize
element.init(children...) element.init(children...)
@ -63,8 +67,8 @@ func (element *list) Draw (destination artist.Canvas) {
rocks[index] = element.entity.Child(index).Entity().Bounds() rocks[index] = element.entity.Child(index).Entity().Bounds()
} }
pattern := element.theme.Pattern(tomo.PatternSunken, element.state()) pattern := element.entity.Theme().Pattern(tomo.PatternSunken, element.state(), element.c)
artist.DrawShatter(destination, pattern, element.entity.Bounds(), rocks...) artutil.DrawShatter(destination, pattern, element.entity.Bounds(), rocks...)
} }
func (element *List) Layout () { func (element *List) Layout () {
@ -72,8 +76,8 @@ func (element *List) Layout () {
element.scroll.Y = element.maxScrollHeight() element.scroll.Y = element.maxScrollHeight()
} }
margin := element.theme.Margin(tomo.PatternSunken) margin := element.entity.Theme().Margin(tomo.PatternSunken, element.c)
padding := element.theme.Padding(tomo.PatternSunken) padding := element.entity.Theme().Padding(tomo.PatternSunken, element.c)
bounds := padding.Apply(element.entity.Bounds()) bounds := padding.Apply(element.entity.Bounds())
element.contentBounds = image.Rectangle { } element.contentBounds = image.Rectangle { }
@ -110,8 +114,8 @@ func (element *FlowList) Layout () {
element.scroll.Y = element.maxScrollHeight() element.scroll.Y = element.maxScrollHeight()
} }
margin := element.theme.Margin(tomo.PatternSunken) margin := element.entity.Theme().Margin(tomo.PatternSunken, element.c)
padding := element.theme.Padding(tomo.PatternSunken) padding := element.entity.Theme().Padding(tomo.PatternSunken, element.c)
bounds := padding.Apply(element.entity.Bounds()) bounds := padding.Apply(element.entity.Bounds())
element.contentBounds = image.Rectangle { } element.contentBounds = image.Rectangle { }
@ -135,7 +139,7 @@ func (element *FlowList) Layout () {
if width + dot.X > bounds.Max.X { if width + dot.X > bounds.Max.X {
nextLine() nextLine()
} }
if typedChild, ok := child.(tomo.Flexible); ok { if typedChild, ok := child.(ability.Flexible); ok {
height = typedChild.FlexibleHeightFor(width) height = typedChild.FlexibleHeightFor(width)
} }
if rowHeight < height { if rowHeight < height {
@ -162,7 +166,7 @@ func (element *FlowList) Layout () {
func (element *list) Selected () ability.Selectable { func (element *list) Selected () ability.Selectable {
if element.selected == -1 { return nil } if element.selected == -1 { return nil }
child, ok := element.entity.Child(element.selected).(tomo.Selectable) child, ok := element.entity.Child(element.selected).(ability.Selectable)
if !ok { return nil } if !ok { return nil }
return child return child
} }
@ -221,7 +225,7 @@ func (element *list) HandleChildMouseDown (
) { ) {
if !element.enabled { return } if !element.enabled { return }
element.Focus() element.Focus()
if child, ok := child.(tomo.Selectable); ok { if child, ok := child.(ability.Selectable); ok {
element.Select(child) element.Select(child)
} }
} }
@ -274,10 +278,7 @@ func (element *list) DrawBackground (destination artist.Canvas) {
element.entity.DrawBackground(destination) element.entity.DrawBackground(destination)
} }
// SetTheme sets the element's theme. func (element *list) HandleThemeChange () {
func (element *list) SetTheme (theme tomo.Theme) {
if theme == element.theme.Theme { return }
element.theme.Theme = theme
element.minimumSize() element.minimumSize()
element.entity.Invalidate() element.entity.Invalidate()
element.entity.InvalidateLayout() element.entity.InvalidateLayout()
@ -312,7 +313,7 @@ func (element *list) ScrollContentBounds () image.Rectangle {
// ScrollViewportBounds returns the size and position of the element's // ScrollViewportBounds returns the size and position of the element's
// viewport relative to ScrollBounds. // viewport relative to ScrollBounds.
func (element *list) ScrollViewportBounds () image.Rectangle { func (element *list) ScrollViewportBounds () image.Rectangle {
padding := element.theme.Padding(tomo.PatternSunken) padding := element.entity.Theme().Padding(tomo.PatternSunken, element.c)
bounds := padding.Apply(element.entity.Bounds()) bounds := padding.Apply(element.entity.Bounds())
bounds = bounds.Sub(bounds.Min).Add(element.scroll) bounds = bounds.Sub(bounds.Min).Add(element.scroll)
return bounds return bounds
@ -364,7 +365,7 @@ func (element *list) selectNone () {
func (element *list) scrollToSelected () { func (element *list) scrollToSelected () {
if element.selected < 0 { return } if element.selected < 0 { return }
target := element.entity.Child(element.selected).Entity().Bounds() target := element.entity.Child(element.selected).Entity().Bounds()
padding := element.theme.Padding(tomo.PatternSunken) padding := element.entity.Theme().Padding(tomo.PatternSunken, element.c)
bounds := padding.Apply(element.entity.Bounds()) bounds := padding.Apply(element.entity.Bounds())
if target.Min.Y < bounds.Min.Y { if target.Min.Y < bounds.Min.Y {
element.scroll.Y -= bounds.Min.Y - target.Min.Y element.scroll.Y -= bounds.Min.Y - target.Min.Y
@ -385,7 +386,7 @@ func (element *list) state () tomo.State {
} }
func (element *list) maxScrollHeight () (height int) { func (element *list) maxScrollHeight () (height int) {
padding := element.theme.Padding(tomo.PatternSunken) padding := element.entity.Theme().Padding(tomo.PatternSunken, element.c)
viewportHeight := element.entity.Bounds().Dy() - padding.Vertical() viewportHeight := element.entity.Bounds().Dy() - padding.Vertical()
height = element.contentBounds.Dy() - viewportHeight height = element.contentBounds.Dy() - viewportHeight
if height < 0 { height = 0 } if height < 0 { height = 0 }
@ -393,8 +394,8 @@ func (element *list) maxScrollHeight () (height int) {
} }
func (element *List) updateMinimumSize () { func (element *List) updateMinimumSize () {
margin := element.theme.Margin(tomo.PatternSunken) margin := element.entity.Theme().Margin(tomo.PatternSunken, element.c)
padding := element.theme.Padding(tomo.PatternSunken) padding := element.entity.Theme().Padding(tomo.PatternSunken, element.c)
width := 0 width := 0
height := 0 height := 0
@ -427,7 +428,7 @@ func (element *List) updateMinimumSize () {
} }
func (element *FlowList) updateMinimumSize () { func (element *FlowList) updateMinimumSize () {
padding := element.theme.Padding(tomo.PatternSunken) padding := element.entity.Theme().Padding(tomo.PatternSunken, element.c)
minimumWidth := 0 minimumWidth := 0
for index := 0; index < element.entity.CountChildren(); index ++ { for index := 0; index < element.entity.CountChildren(); index ++ {
width, height := element.entity.ChildMinimumSize(index) width, height := element.entity.ChildMinimumSize(index)

View File

@ -4,6 +4,8 @@ import "image"
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
var progressBarCase = tomo.C("tomo", "progressBar")
// ProgressBar displays a visual indication of how far along a task is. // ProgressBar displays a visual indication of how far along a task is.
type ProgressBar struct { type ProgressBar struct {
entity tomo.Entity entity tomo.Entity
@ -16,8 +18,7 @@ func NewProgressBar (progress float64) (element *ProgressBar) {
if progress < 0 { progress = 0 } if progress < 0 { progress = 0 }
if progress > 1 { progress = 1 } if progress > 1 { progress = 1 }
element = &ProgressBar { progress: progress } element = &ProgressBar { progress: progress }
element.entity = tomo.NewEntity(element) element.entity = tomo.GetBackend().NewEntity(element)
element.theme.Case = tomo.C("tomo", "progressBar")
element.updateMinimumSize() element.updateMinimumSize()
return return
} }
@ -31,15 +32,15 @@ func (element *ProgressBar) Entity () tomo.Entity {
func (element *ProgressBar) Draw (destination artist.Canvas) { func (element *ProgressBar) Draw (destination artist.Canvas) {
bounds := element.entity.Bounds() bounds := element.entity.Bounds()
pattern := element.theme.Pattern(tomo.PatternSunken, tomo.State { }) pattern := element.entity.Theme().Pattern(tomo.PatternSunken, tomo.State { }, progressBarCase)
padding := element.theme.Padding(tomo.PatternSunken) padding := element.entity.Theme().Padding(tomo.PatternSunken, progressBarCase)
pattern.Draw(destination, bounds) pattern.Draw(destination, bounds)
bounds = padding.Apply(bounds) bounds = padding.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)
mercury := element.theme.Pattern(tomo.PatternMercury, tomo.State { }) mercury := element.entity.Theme().Pattern(tomo.PatternMercury, tomo.State { }, progressBarCase)
mercury.Draw(destination, meterBounds) mercury.Draw(destination, meterBounds)
} }
@ -52,17 +53,14 @@ func (element *ProgressBar) SetProgress (progress float64) {
element.entity.Invalidate() element.entity.Invalidate()
} }
// SetTheme sets the element's theme. func (element *ProgressBar) HandleThemeChange () {
func (element *ProgressBar) SetTheme (new tomo.Theme) {
if new == element.theme.Theme { return }
element.theme.Theme = new
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
} }
func (element *ProgressBar) updateMinimumSize() { func (element *ProgressBar) updateMinimumSize() {
padding := element.theme.Padding(tomo.PatternSunken) padding := element.entity.Theme().Padding(tomo.PatternSunken, progressBarCase)
innerPadding := element.theme.Padding(tomo.PatternMercury) innerPadding := element.entity.Theme().Padding(tomo.PatternMercury, progressBarCase)
element.entity.SetMinimumSize ( element.entity.SetMinimumSize (
padding.Horizontal() + innerPadding.Horizontal(), padding.Horizontal() + innerPadding.Horizontal(),
padding.Vertical() + innerPadding.Vertical()) padding.Vertical() + innerPadding.Vertical())

View File

@ -6,6 +6,8 @@ import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/ability" import "git.tebibyte.media/sashakoshka/tomo/ability"
var scrollCase = tomo.C("tomo", "scroll")
// ScrollMode specifies which sides of a Scroll have scroll bars. // ScrollMode specifies which sides of a Scroll have scroll bars.
type ScrollMode int; const ( type ScrollMode int; const (
ScrollNeither ScrollMode = 0 ScrollNeither ScrollMode = 0
@ -33,8 +35,7 @@ type Scroll struct {
// NewScroll creates a new scroll element. // NewScroll creates a new scroll element.
func NewScroll (mode ScrollMode, child ability.Scrollable) (element *Scroll) { func NewScroll (mode ScrollMode, child ability.Scrollable) (element *Scroll) {
element = &Scroll { } element = &Scroll { }
element.theme.Case = tomo.C("tomo", "scroll") element.entity = tomo.GetBackend().NewEntity(element)
element.entity = tomo.NewEntity(element).(scrollEntity)
if mode.Includes(ScrollHorizontal) { if mode.Includes(ScrollHorizontal) {
element.horizontal = NewHScrollBar() element.horizontal = NewHScrollBar()
@ -82,8 +83,8 @@ func (element *Scroll) Draw (destination artist.Canvas) {
bounds.Max.X - element.vertical.Entity().Bounds().Dx(), bounds.Max.X - element.vertical.Entity().Bounds().Dx(),
bounds.Max.Y - element.horizontal.Entity().Bounds().Dy()) bounds.Max.Y - element.horizontal.Entity().Bounds().Dy())
state := tomo.State { } state := tomo.State { }
deadArea := element.theme.Pattern(tomo.PatternDead, state) deadArea := element.entity.Theme().Pattern(tomo.PatternDead, state, scrollCase)
deadArea.Draw(canvas.Cut(destination, bounds), bounds) deadArea.Draw(artist.Cut(destination, bounds), bounds)
} }
} }
@ -185,20 +186,12 @@ func (element *Scroll) HandleScroll (
element.scrollChildBy(int(deltaX), int(deltaY)) element.scrollChildBy(int(deltaX), int(deltaY))
} }
// SetTheme sets the element's theme. func (element *Scroll) HandleThemeChange () {
func (element *Scroll) SetTheme (theme tomo.Theme) {
if theme == element.theme.Theme { return }
element.theme.Theme = theme
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
element.entity.InvalidateLayout() element.entity.InvalidateLayout()
} }
// SetConfig sets the element's configuration.
func (element *Scroll) SetConfig (config tomo.Config) {
element.config.Config = config
}
func (element *Scroll) updateMinimumSize () { func (element *Scroll) updateMinimumSize () {
var width, height int var width, height int

View File

@ -4,7 +4,6 @@ import "image"
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/ability"
// ScrollBar is an element similar to Slider, but it has special behavior that // ScrollBar is an element similar to Slider, but it has special behavior that
// makes it well suited for controlling the viewport position on one axis of a // makes it well suited for controlling the viewport position on one axis of a
@ -20,6 +19,8 @@ import "git.tebibyte.media/sashakoshka/tomo/ability"
type ScrollBar struct { type ScrollBar struct {
entity tomo.Entity entity tomo.Entity
c tomo.Case
vertical bool vertical bool
enabled bool enabled bool
dragging bool dragging bool
@ -39,8 +40,8 @@ func NewVScrollBar () (element *ScrollBar) {
vertical: true, vertical: true,
enabled: true, enabled: true,
} }
element.theme.Case = tomo.C("tomo", "scrollBarVertical") element.c = tomo.C("tomo", "scrollBarVertical")
element.entity = tomo.NewEntity(element).(scrollBarEntity) element.entity = tomo.GetBackend().NewEntity(element)
element.updateMinimumSize() element.updateMinimumSize()
return return
} }
@ -50,8 +51,8 @@ func NewHScrollBar () (element *ScrollBar) {
element = &ScrollBar { element = &ScrollBar {
enabled: true, enabled: true,
} }
element.theme.Case = tomo.C("tomo", "scrollBarHorizontal") element.c = tomo.C("tomo", "scrollBarHorizontal")
element.entity = tomo.NewEntity(element).(tomo.Entity) element.entity = tomo.GetBackend().NewEntity(element)
element.updateMinimumSize() element.updateMinimumSize()
return return
} }
@ -70,10 +71,10 @@ func (element *ScrollBar) Draw (destination artist.Canvas) {
Disabled: !element.Enabled(), Disabled: !element.Enabled(),
Pressed: element.dragging, Pressed: element.dragging,
} }
element.theme.Pattern(tomo.PatternGutter, state).Draw ( element.entity.Theme().Pattern(tomo.PatternGutter, state, element.c).Draw (
destination, destination,
bounds) bounds)
element.theme.Pattern(tomo.PatternHandle, state).Draw ( element.entity.Theme().Pattern(tomo.PatternHandle, state, element.c).Draw (
destination, destination,
element.bar) element.bar)
} }
@ -83,7 +84,7 @@ func (element *ScrollBar) HandleMouseDown (
button input.Button, button input.Button,
modifiers input.Modifiers, modifiers input.Modifiers,
) { ) {
velocity := element.config.ScrollVelocity() velocity := element.entity.Config().ScrollVelocity()
if position.In(element.bar) { if position.In(element.bar) {
// the mouse is pressed down within the bar's handle // the mouse is pressed down within the bar's handle
@ -185,17 +186,7 @@ func (element *ScrollBar) OnScroll (callback func (viewport image.Point)) {
element.onScroll = callback element.onScroll = callback
} }
// SetTheme sets the element's theme. func (element *ScrollBar) HandleThemeChange () {
func (element *ScrollBar) SetTheme (new tomo.Theme) {
if new == element.theme.Theme { return }
element.theme.Theme = new
element.entity.Invalidate()
}
// SetConfig sets the element's configuration.
func (element *ScrollBar) SetConfig (new tomo.Config) {
if new == element.config.Config { return }
element.config.Config = new
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
} }
@ -263,7 +254,7 @@ func (element *ScrollBar) recalculate () {
func (element *ScrollBar) recalculateVertical () { func (element *ScrollBar) recalculateVertical () {
bounds := element.entity.Bounds() bounds := element.entity.Bounds()
padding := element.theme.Padding(tomo.PatternGutter) padding := element.entity.Theme().Padding(tomo.PatternGutter, element.c)
element.track = padding.Apply(bounds) element.track = padding.Apply(bounds)
contentBounds := element.contentBounds contentBounds := element.contentBounds
@ -290,7 +281,7 @@ func (element *ScrollBar) recalculateVertical () {
func (element *ScrollBar) recalculateHorizontal () { func (element *ScrollBar) recalculateHorizontal () {
bounds := element.entity.Bounds() bounds := element.entity.Bounds()
padding := element.theme.Padding(tomo.PatternGutter) padding := element.entity.Theme().Padding(tomo.PatternGutter, element.c)
element.track = padding.Apply(bounds) element.track = padding.Apply(bounds)
contentBounds := element.contentBounds contentBounds := element.contentBounds
@ -316,8 +307,8 @@ func (element *ScrollBar) recalculateHorizontal () {
} }
func (element *ScrollBar) updateMinimumSize () { func (element *ScrollBar) updateMinimumSize () {
gutterPadding := element.theme.Padding(tomo.PatternGutter) gutterPadding := element.entity.Theme().Padding(tomo.PatternGutter, element.c)
handlePadding := element.theme.Padding(tomo.PatternHandle) handlePadding := element.entity.Theme().Padding(tomo.PatternHandle, element.c)
if element.vertical { if element.vertical {
element.entity.SetMinimumSize ( element.entity.SetMinimumSize (
gutterPadding.Horizontal() + handlePadding.Horizontal(), gutterPadding.Horizontal() + handlePadding.Horizontal(),

View File

@ -4,7 +4,6 @@ import "image"
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/ability"
// Slider is a slider control with a floating point value between zero and one. // Slider is a slider control with a floating point value between zero and one.
type Slider struct { type Slider struct {
@ -22,12 +21,16 @@ func NewVSlider (value float64) (element *Slider) {
func NewHSlider (value float64) (element *Slider) { func NewHSlider (value float64) (element *Slider) {
element = &Slider { } element = &Slider { }
element.value = value element.value = value
element.entity = tomo.NewEntity(element) element.entity = tomo.GetBackend().NewEntity(element)
element.construct() element.construct()
return return
} }
type slider struct { type slider struct {
entity tomo.Entity
c tomo.Case
value float64 value float64
vertical bool vertical bool
dragging bool dragging bool
@ -43,9 +46,9 @@ type slider struct {
func (element *slider) construct () { func (element *slider) construct () {
element.enabled = true element.enabled = true
if element.vertical { if element.vertical {
element.theme.Case = tomo.C("tomo", "sliderVertical") element.c = tomo.C("tomo", "sliderVertical")
} else { } else {
element.theme.Case = tomo.C("tomo", "sliderHorizontal") element.c = tomo.C("tomo", "sliderHorizontal")
} }
element.updateMinimumSize() element.updateMinimumSize()
} }
@ -58,7 +61,7 @@ func (element *slider) Entity () tomo.Entity {
// Draw causes the element to draw to the specified destination canvas. // Draw causes the element to draw to the specified destination canvas.
func (element *slider) Draw (destination artist.Canvas) { func (element *slider) Draw (destination artist.Canvas) {
bounds := element.entity.Bounds() bounds := element.entity.Bounds()
element.track = element.theme.Padding(tomo.PatternGutter).Apply(bounds) element.track = element.entity.Theme().Padding(tomo.PatternGutter, element.c).Apply(bounds)
if element.vertical { if element.vertical {
barSize := element.track.Dx() barSize := element.track.Dx()
element.bar = image.Rect(0, 0, barSize, barSize).Add(element.track.Min) element.bar = image.Rect(0, 0, barSize, barSize).Add(element.track.Min)
@ -80,8 +83,8 @@ func (element *slider) Draw (destination artist.Canvas) {
Focused: element.entity.Focused(), Focused: element.entity.Focused(),
Pressed: element.dragging, Pressed: element.dragging,
} }
element.theme.Pattern(tomo.PatternGutter, state).Draw(destination, bounds) element.entity.Theme().Pattern(tomo.PatternGutter, state, element.c).Draw(destination, bounds)
element.theme.Pattern(tomo.PatternHandle, state).Draw(destination, element.bar) element.entity.Theme().Pattern(tomo.PatternHandle, state, element.c).Draw(destination, element.bar)
} }
// Focus gives this element input focus. // Focus gives this element input focus.
@ -205,21 +208,12 @@ func (element *slider) OnRelease (callback func ()) {
element.onRelease = callback element.onRelease = callback
} }
// SetTheme sets the element's theme. func (element *slider) HandleThemeChange () {
func (element *slider) SetTheme (new tomo.Theme) {
if new == element.theme.Theme { return }
element.theme.Theme = new
element.entity.Invalidate()
}
// SetConfig sets the element's configuration.
func (element *slider) SetConfig (new tomo.Config) {
if new == element.config.Config { return }
element.config.Config = new
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
} }
func (element *slider) changeValue (delta float64) { func (element *slider) changeValue (delta float64) {
element.value += delta element.value += delta
if element.value < 0 { if element.value < 0 {
@ -252,8 +246,8 @@ func (element *slider) valueFor (x, y int) (value float64) {
} }
func (element *slider) updateMinimumSize () { func (element *slider) updateMinimumSize () {
gutterPadding := element.theme.Padding(tomo.PatternGutter) gutterPadding := element.entity.Theme().Padding(tomo.PatternGutter, element.c)
handlePadding := element.theme.Padding(tomo.PatternHandle) handlePadding := element.entity.Theme().Padding(tomo.PatternHandle, element.c)
if element.vertical { if element.vertical {
element.entity.SetMinimumSize ( element.entity.SetMinimumSize (
gutterPadding.Horizontal() + handlePadding.Horizontal(), gutterPadding.Horizontal() + handlePadding.Horizontal(),

View File

@ -3,6 +3,8 @@ package elements
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
var spacerCase = tomo.C("tomo", "spacer")
// Spacer can be used to put space between two elements.. // Spacer can be used to put space between two elements..
type Spacer struct { type Spacer struct {
entity tomo.Entity entity tomo.Entity
@ -12,8 +14,7 @@ type Spacer struct {
// NewSpacer creates a new spacer. // NewSpacer creates a new spacer.
func NewSpacer () (element *Spacer) { func NewSpacer () (element *Spacer) {
element = &Spacer { } element = &Spacer { }
element.entity = tomo.NewEntity(element).(spacerEntity) element.entity = tomo.GetBackend().NewEntity(element)
element.theme.Case = tomo.C("tomo", "spacer")
element.updateMinimumSize() element.updateMinimumSize()
return return
} }
@ -35,14 +36,14 @@ func (element *Spacer) Draw (destination artist.Canvas) {
bounds := element.entity.Bounds() bounds := element.entity.Bounds()
if element.line { if element.line {
pattern := element.theme.Pattern ( pattern := element.entity.Theme().Pattern (
tomo.PatternLine, tomo.PatternLine,
tomo.State { }) tomo.State { }, spacerCase)
pattern.Draw(destination, bounds) pattern.Draw(destination, bounds)
} else { } else {
pattern := element.theme.Pattern ( pattern := element.entity.Theme().Pattern (
tomo.PatternBackground, tomo.PatternBackground,
tomo.State { }) tomo.State { }, spacerCase)
pattern.Draw(destination, bounds) pattern.Draw(destination, bounds)
} }
} }
@ -55,23 +56,13 @@ func (element *Spacer) SetLine (line bool) {
element.entity.Invalidate() element.entity.Invalidate()
} }
// SetTheme sets the element's theme. func (element *Spacer) HandleThemeChange () {
func (element *Spacer) SetTheme (new tomo.Theme) {
if new == element.theme.Theme { return }
element.theme.Theme = new
element.entity.Invalidate()
}
// SetConfig sets the element's configuration.
func (element *Spacer) SetConfig (new tomo.Config) {
if new == element.config.Config { return }
element.config.Config = new
element.entity.Invalidate() element.entity.Invalidate()
} }
func (element *Spacer) updateMinimumSize () { func (element *Spacer) updateMinimumSize () {
if element.line { if element.line {
padding := element.theme.Padding(tomo.PatternLine) padding := element.entity.Theme().Padding(tomo.PatternLine, spacerCase)
element.entity.SetMinimumSize ( element.entity.SetMinimumSize (
padding.Horizontal(), padding.Horizontal(),
padding.Vertical()) padding.Vertical())

View File

@ -6,6 +6,8 @@ import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/textdraw" import "git.tebibyte.media/sashakoshka/tomo/textdraw"
var switchCase = tomo.C("tomo", "switch")
// Switch is a toggle-able on/off switch with an optional label. It is // Switch is a toggle-able on/off switch with an optional label. It is
// functionally identical to Checkbox, but plays a different semantic role. // functionally identical to Checkbox, but plays a different semantic role.
type Switch struct { type Switch struct {
@ -27,11 +29,10 @@ func NewSwitch (text string, on bool) (element *Switch) {
text: text, text: text,
enabled: true, enabled: true,
} }
element.entity = tomo.NewEntity(element).(checkboxEntity) element.entity = tomo.GetBackend().NewEntity(element)
element.theme.Case = tomo.C("tomo", "switch") element.drawer.SetFace (element.entity.Theme().FontFace (
element.drawer.SetFace (element.theme.FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal)) tomo.FontSizeNormal, switchCase))
element.drawer.SetText([]rune(text)) element.drawer.SetText([]rune(text))
element.updateMinimumSize() element.updateMinimumSize()
return return
@ -71,24 +72,24 @@ func (element *Switch) Draw (destination artist.Canvas) {
} }
} }
gutterPattern := element.theme.Pattern ( gutterPattern := element.entity.Theme().Pattern (
tomo.PatternGutter, state) tomo.PatternGutter, state, switchCase)
gutterPattern.Draw(destination, gutterBounds) gutterPattern.Draw(destination, gutterBounds)
handlePattern := element.theme.Pattern ( handlePattern := element.entity.Theme().Pattern (
tomo.PatternHandle, state) tomo.PatternHandle, state, switchCase)
handlePattern.Draw(destination, handleBounds) handlePattern.Draw(destination, handleBounds)
textBounds := element.drawer.LayoutBounds() textBounds := element.drawer.LayoutBounds()
offset := bounds.Min.Add(image.Point { offset := bounds.Min.Add(image.Point {
X: bounds.Dy() * 2 + X: bounds.Dy() * 2 +
element.theme.Margin(tomo.PatternBackground).X, element.entity.Theme().Margin(tomo.PatternBackground, switchCase).X,
}) })
offset.Y -= textBounds.Min.Y offset.Y -= textBounds.Min.Y
offset.X -= textBounds.Min.X offset.X -= textBounds.Min.X
foreground := element.theme.Color(tomo.ColorForeground, state) foreground := element.entity.Theme().Color(tomo.ColorForeground, state, switchCase)
element.drawer.Draw(destination, foreground, offset) element.drawer.Draw(destination, foreground, offset)
} }
@ -181,21 +182,10 @@ func (element *Switch) SetText (text string) {
element.entity.Invalidate() element.entity.Invalidate()
} }
// SetTheme sets the element's theme. func (element *Switch) HandleThemeChange () {
func (element *Switch) SetTheme (new tomo.Theme) { element.drawer.SetFace (element.entity.Theme().FontFace (
if new == element.theme.Theme { return }
element.theme.Theme = new
element.drawer.SetFace (element.theme.FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal)) tomo.FontSizeNormal, switchCase))
element.updateMinimumSize()
element.entity.Invalidate()
}
// SetConfig sets the element's configuration.
func (element *Switch) SetConfig (new tomo.Config) {
if new == element.config.Config { return }
element.config.Config = new
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
} }
@ -209,7 +199,7 @@ func (element *Switch) updateMinimumSize () {
} else { } else {
element.entity.SetMinimumSize ( element.entity.SetMinimumSize (
lineHeight * 2 + lineHeight * 2 +
element.theme.Margin(tomo.PatternBackground).X + element.entity.Theme().Margin(tomo.PatternBackground, switchCase).X +
textBounds.Dx(), textBounds.Dx(),
lineHeight) lineHeight)
} }

View File

@ -12,6 +12,8 @@ import "git.tebibyte.media/sashakoshka/tomo/textmanip"
import "git.tebibyte.media/sashakoshka/tomo/fixedutil" import "git.tebibyte.media/sashakoshka/tomo/fixedutil"
import "git.tebibyte.media/sashakoshka/tomo/artist/shapes" import "git.tebibyte.media/sashakoshka/tomo/artist/shapes"
var textBoxCase = tomo.C("tomo", "textBox")
// TextBox is a single-line text input. // TextBox is a single-line text input.
type TextBox struct { type TextBox struct {
entity tomo.Entity entity tomo.Entity
@ -38,15 +40,14 @@ type TextBox struct {
// text. // text.
func NewTextBox (placeholder, value string) (element *TextBox) { func NewTextBox (placeholder, value string) (element *TextBox) {
element = &TextBox { enabled: true } element = &TextBox { enabled: true }
element.theme.Case = tomo.C("tomo", "textBox") element.entity = tomo.GetBackend().NewEntity(element)
element.entity = tomo.NewEntity(element).(textBoxEntity)
element.placeholder = placeholder element.placeholder = placeholder
element.placeholderDrawer.SetFace (element.theme.FontFace ( element.placeholderDrawer.SetFace (element.entity.Theme().FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal)) tomo.FontSizeNormal, textBoxCase))
element.valueDrawer.SetFace (element.theme.FontFace ( element.valueDrawer.SetFace (element.entity.Theme().FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal)) tomo.FontSizeNormal, textBoxCase))
element.placeholderDrawer.SetText([]rune(placeholder)) element.placeholderDrawer.SetText([]rune(placeholder))
element.updateMinimumSize() element.updateMinimumSize()
element.SetValue(value) element.SetValue(value)
@ -63,15 +64,15 @@ func (element *TextBox) Draw (destination artist.Canvas) {
bounds := element.entity.Bounds() bounds := element.entity.Bounds()
state := element.state() state := element.state()
pattern := element.theme.Pattern(tomo.PatternInput, state) pattern := element.entity.Theme().Pattern(tomo.PatternInput, state, textBoxCase)
padding := element.theme.Padding(tomo.PatternInput) padding := element.entity.Theme().Padding(tomo.PatternInput, textBoxCase)
innerCanvas := canvas.Cut(destination, padding.Apply(bounds)) innerCanvas := artist.Cut(destination, padding.Apply(bounds))
pattern.Draw(destination, bounds) pattern.Draw(destination, bounds)
offset := element.textOffset() offset := element.textOffset()
if element.entity.Focused() && !element.dot.Empty() { if element.entity.Focused() && !element.dot.Empty() {
// draw selection bounds // draw selection bounds
accent := element.theme.Color(tomo.ColorAccent, state) accent := element.entity.Theme().Color(tomo.ColorAccent, state, textBoxCase)
canon := element.dot.Canon() canon := element.dot.Canon()
foff := fixedutil.Pt(offset) foff := fixedutil.Pt(offset)
start := element.valueDrawer.PositionAt(canon.Start).Add(foff) start := element.valueDrawer.PositionAt(canon.Start).Add(foff)
@ -89,9 +90,9 @@ func (element *TextBox) Draw (destination artist.Canvas) {
if len(element.text) == 0 { if len(element.text) == 0 {
// draw placeholder // draw placeholder
textBounds := element.placeholderDrawer.LayoutBounds() textBounds := element.placeholderDrawer.LayoutBounds()
foreground := element.theme.Color ( foreground := element.entity.Theme().Color (
tomo.ColorForeground, tomo.ColorForeground,
tomo.State { Disabled: true }) tomo.State { Disabled: true }, textBoxCase)
element.placeholderDrawer.Draw ( element.placeholderDrawer.Draw (
innerCanvas, innerCanvas,
foreground, foreground,
@ -99,7 +100,7 @@ func (element *TextBox) Draw (destination artist.Canvas) {
} else { } else {
// draw input value // draw input value
textBounds := element.valueDrawer.LayoutBounds() textBounds := element.valueDrawer.LayoutBounds()
foreground := element.theme.Color(tomo.ColorForeground, state) foreground := element.entity.Theme().Color(tomo.ColorForeground, state, textBoxCase)
element.valueDrawer.Draw ( element.valueDrawer.Draw (
innerCanvas, innerCanvas,
foreground, foreground,
@ -108,7 +109,7 @@ func (element *TextBox) Draw (destination artist.Canvas) {
if element.entity.Focused() && element.dot.Empty() { if element.entity.Focused() && element.dot.Empty() {
// draw cursor // draw cursor
foreground := element.theme.Color(tomo.ColorForeground, state) foreground := element.entity.Theme().Color(tomo.ColorForeground, state, textBoxCase)
cursorPosition := fixedutil.RoundPt ( cursorPosition := fixedutil.RoundPt (
element.valueDrawer.PositionAt(element.dot.End)) element.valueDrawer.PositionAt(element.dot.End))
shapes.ColorLine ( shapes.ColorLine (
@ -144,7 +145,7 @@ func (element *TextBox) HandleMouseDown (
runeIndex := element.atPosition(position) runeIndex := element.atPosition(position)
if runeIndex == -1 { return } if runeIndex == -1 { return }
if time.Since(element.lastClick) < element.config.DoubleClickDelay() { if time.Since(element.lastClick) < element.entity.Config().DoubleClickDelay() {
element.dragging = 2 element.dragging = 2
element.dot = textmanip.WordAround(element.text, runeIndex) element.dot = textmanip.WordAround(element.text, runeIndex)
} else { } else {
@ -202,7 +203,7 @@ func (element *TextBox) HandleMotion (position image.Point) {
} }
func (element *TextBox) textOffset () image.Point { func (element *TextBox) textOffset () image.Point {
padding := element.theme.Padding(tomo.PatternInput) padding := element.entity.Theme().Padding(tomo.PatternInput, textBoxCase)
bounds := element.entity.Bounds() bounds := element.entity.Bounds()
innerBounds := padding.Apply(bounds) innerBounds := padding.Apply(bounds)
textHeight := element.valueDrawer.LineHeight().Round() textHeight := element.valueDrawer.LineHeight().Round()
@ -464,27 +465,17 @@ func (element *TextBox) ScrollAxes () (horizontal, vertical bool) {
return true, false return true, false
} }
// SetTheme sets the element's theme. func (element *TextBox) HandleThemeChange () {
func (element *TextBox) SetTheme (new tomo.Theme) { face := element.entity.Theme().FontFace (
if new == element.theme.Theme { return }
element.theme.Theme = new
face := element.theme.FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal) tomo.FontSizeNormal,
textBoxCase)
element.placeholderDrawer.SetFace(face) element.placeholderDrawer.SetFace(face)
element.valueDrawer.SetFace(face) element.valueDrawer.SetFace(face)
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
} }
// SetConfig sets the element's configuration.
func (element *TextBox) SetConfig (new tomo.Config) {
if new == element.config.Config { return }
element.config.Config = new
element.updateMinimumSize()
element.entity.Invalidate()
}
func (element *TextBox) contextMenu (position image.Point) { func (element *TextBox) contextMenu (position image.Point) {
window := element.entity.Window() window := element.entity.Window()
menu, err := window.NewMenu(image.Rectangle { position, position }) menu, err := window.NewMenu(image.Rectangle { position, position })
@ -528,12 +519,12 @@ func (element *TextBox) runOnChange () {
} }
func (element *TextBox) scrollViewportWidth () (width int) { func (element *TextBox) scrollViewportWidth () (width int) {
padding := element.theme.Padding(tomo.PatternInput) padding := element.entity.Theme().Padding(tomo.PatternInput, textBoxCase)
return padding.Apply(element.entity.Bounds()).Dx() return padding.Apply(element.entity.Bounds()).Dx()
} }
func (element *TextBox) scrollToCursor () { func (element *TextBox) scrollToCursor () {
padding := element.theme.Padding(tomo.PatternInput) padding := element.entity.Theme().Padding(tomo.PatternInput, textBoxCase)
bounds := padding.Apply(element.entity.Bounds()) bounds := padding.Apply(element.entity.Bounds())
bounds = bounds.Sub(bounds.Min) bounds = bounds.Sub(bounds.Min)
bounds.Max.X -= element.valueDrawer.Em().Round() bounds.Max.X -= element.valueDrawer.Em().Round()
@ -556,7 +547,7 @@ func (element *TextBox) scrollToCursor () {
func (element *TextBox) updateMinimumSize () { func (element *TextBox) updateMinimumSize () {
textBounds := element.placeholderDrawer.LayoutBounds() textBounds := element.placeholderDrawer.LayoutBounds()
padding := element.theme.Padding(tomo.PatternInput) padding := element.entity.Theme().Padding(tomo.PatternInput, textBoxCase)
element.entity.SetMinimumSize ( element.entity.SetMinimumSize (
padding.Horizontal() + textBounds.Dx(), padding.Horizontal() + textBounds.Dx(),
padding.Vertical() + padding.Vertical() +

View File

@ -6,6 +6,8 @@ import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/textdraw" import "git.tebibyte.media/sashakoshka/tomo/textdraw"
var toggleButtonCase = tomo.C("tomo", "toggleButton")
// ToggleButton is a togglable button. // ToggleButton is a togglable button.
type ToggleButton struct { type ToggleButton struct {
entity tomo.Entity entity tomo.Entity
@ -30,11 +32,11 @@ func NewToggleButton (text string, on bool) (element *ToggleButton) {
enabled: true, enabled: true,
on: on, on: on,
} }
element.entity = tomo.NewEntity(element) element.entity = tomo.GetBackend().NewEntity(element)
element.theme.Case = tomo.C("tomo", "toggleButton") element.drawer.SetFace (element.entity.Theme().FontFace (
element.drawer.SetFace (element.theme.FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal)) tomo.FontSizeNormal,
toggleButtonCase))
element.SetText(text) element.SetText(text)
return return
} }
@ -48,10 +50,10 @@ func (element *ToggleButton) Entity () tomo.Entity {
func (element *ToggleButton) Draw (destination artist.Canvas) { func (element *ToggleButton) Draw (destination artist.Canvas) {
state := element.state() state := element.state()
bounds := element.entity.Bounds() bounds := element.entity.Bounds()
pattern := element.theme.Pattern(tomo.PatternButton, state) pattern := element.entity.Theme().Pattern(tomo.PatternButton, state, toggleButtonCase)
lampPattern := element.theme.Pattern(tomo.PatternLamp, state) lampPattern := element.entity.Theme().Pattern(tomo.PatternLamp, state, toggleButtonCase)
lampPadding := element.theme.Padding(tomo.PatternLamp).Horizontal() lampPadding := element.entity.Theme().Padding(tomo.PatternLamp, toggleButtonCase).Horizontal()
lampBounds := bounds lampBounds := bounds
lampBounds.Max.X = lampBounds.Min.X + lampPadding lampBounds.Max.X = lampBounds.Min.X + lampPadding
bounds.Min.X += lampPadding bounds.Min.X += lampPadding
@ -59,9 +61,9 @@ func (element *ToggleButton) Draw (destination artist.Canvas) {
pattern.Draw(destination, bounds) pattern.Draw(destination, bounds)
lampPattern.Draw(destination, lampBounds) lampPattern.Draw(destination, lampBounds)
foreground := element.theme.Color(tomo.ColorForeground, state) foreground := element.entity.Theme().Color(tomo.ColorForeground, state, toggleButtonCase)
sink := element.theme.Sink(tomo.PatternButton) sink := element.entity.Theme().Sink(tomo.PatternButton, toggleButtonCase)
margin := element.theme.Margin(tomo.PatternButton) margin := element.entity.Theme().Margin(tomo.PatternButton, toggleButtonCase)
offset := image.Pt ( offset := image.Pt (
bounds.Dx() / 2, bounds.Dx() / 2,
@ -76,7 +78,7 @@ func (element *ToggleButton) Draw (destination artist.Canvas) {
} }
if element.hasIcon { if element.hasIcon {
icon := element.theme.Icon(element.iconId, tomo.IconSizeSmall) icon := element.entity.Theme().Icon(element.iconId, tomo.IconSizeSmall, toggleButtonCase)
if icon != nil { if icon != nil {
iconBounds := icon.Bounds() iconBounds := icon.Bounds()
addedWidth := iconBounds.Dx() addedWidth := iconBounds.Dx()
@ -166,21 +168,10 @@ func (element *ToggleButton) ShowText (showText bool) {
element.entity.Invalidate() element.entity.Invalidate()
} }
// SetTheme sets the element's theme. func (element *ToggleButton) HandleThemeChange () {
func (element *ToggleButton) SetTheme (new tomo.Theme) { element.drawer.SetFace (element.entity.Theme().FontFace (
if new == element.theme.Theme { return }
element.theme.Theme = new
element.drawer.SetFace (element.theme.FontFace (
tomo.FontStyleRegular, tomo.FontStyleRegular,
tomo.FontSizeNormal)) tomo.FontSizeNormal, toggleButtonCase))
element.updateMinimumSize()
element.entity.Invalidate()
}
// SetConfig sets the element's configuration.
func (element *ToggleButton) SetConfig (new tomo.Config) {
if new == element.config.Config { return }
element.config.Config = new
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
} }
@ -239,15 +230,15 @@ func (element *ToggleButton) HandleKeyUp(key input.Key, modifiers input.Modifier
} }
func (element *ToggleButton) updateMinimumSize () { func (element *ToggleButton) updateMinimumSize () {
padding := element.theme.Padding(tomo.PatternButton) padding := element.entity.Theme().Padding(tomo.PatternButton, toggleButtonCase)
margin := element.theme.Margin(tomo.PatternButton) margin := element.entity.Theme().Margin(tomo.PatternButton, toggleButtonCase)
lampPadding := element.theme.Padding(tomo.PatternLamp) lampPadding := element.entity.Theme().Padding(tomo.PatternLamp, toggleButtonCase)
textBounds := element.drawer.LayoutBounds() textBounds := element.drawer.LayoutBounds()
minimumSize := textBounds.Sub(textBounds.Min) minimumSize := textBounds.Sub(textBounds.Min)
if element.hasIcon { if element.hasIcon {
icon := element.theme.Icon(element.iconId, tomo.IconSizeSmall) icon := element.entity.Theme().Icon(element.iconId, tomo.IconSizeSmall, toggleButtonCase)
if icon != nil { if icon != nil {
bounds := icon.Bounds() bounds := icon.Bounds()
if element.showText { if element.showText {

View File

@ -1,16 +1,19 @@
package main package main
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/nasin"
import "git.tebibyte.media/sashakoshka/tomo/elements" import "git.tebibyte.media/sashakoshka/tomo/elements"
import "git.tebibyte.media/sashakoshka/tomo/elements/testing" import "git.tebibyte.media/sashakoshka/tomo/elements/testing"
import _ "git.tebibyte.media/sashakoshka/tomo/backends/all"
func main () { func main () {
tomo.Run(run) nasin.Run(Application { })
} }
func run () { type Application struct { }
window, _ := tomo.NewWindow(tomo.Bounds(0, 0, 128, 128))
func (Application) Init () error {
window, err := nasin.NewWindow(tomo.Bounds(0, 0, 128, 128))
if err != nil { return err }
window.SetTitle("vertical stack") window.SetTitle("vertical stack")
container := elements.NewVBox(elements.SpaceBoth) container := elements.NewVBox(elements.SpaceBoth)
@ -25,13 +28,15 @@ func run () {
container.Adopt(okButton) container.Adopt(okButton)
okButton.Focus() okButton.Focus()
}) })
okButton.OnClick(tomo.Stop) okButton.OnClick(nasin.Stop)
container.AdoptExpand(label) container.AdoptExpand(label)
container.Adopt(button, okButton) container.Adopt(button, okButton)
window.Adopt(container) window.Adopt(container)
okButton.Focus() okButton.Focus()
window.OnClose(tomo.Stop) window.OnClose(nasin.Stop)
window.Show() window.Show()
return nil
} }

View File

@ -6,6 +6,7 @@ import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/ability" import "git.tebibyte.media/sashakoshka/tomo/ability"
type entity struct { type entity struct {
backend *backend
window *window window *window
parent *entity parent *entity
children []*entity children []*entity
@ -21,7 +22,7 @@ type entity struct {
} }
func (backend *backend) NewEntity (owner tomo.Element) tomo.Entity { func (backend *backend) NewEntity (owner tomo.Element) tomo.Entity {
entity := &entity { element: owner } entity := &entity { element: owner, backend: backend }
entity.InvalidateLayout() entity.InvalidateLayout()
return entity return entity
} }
@ -162,7 +163,7 @@ func (entity *entity) DrawBackground (destination artist.Canvas) {
if entity.parent != nil { if entity.parent != nil {
entity.parent.element.(ability.Container).DrawBackground(destination) entity.parent.element.(ability.Container).DrawBackground(destination)
} else if entity.window != nil { } else if entity.window != nil {
entity.window.system.theme.Pattern ( entity.backend.theme.Pattern (
tomo.PatternBackground, tomo.PatternBackground,
tomo.State { }, tomo.State { },
tomo.C("tomo", "window")).Draw ( tomo.C("tomo", "window")).Draw (
@ -292,11 +293,11 @@ func (entity *entity) NotifyScrollBoundsChange () {
// ----------- ThemeableEntity ----------- // // ----------- ThemeableEntity ----------- //
func (entity *entity) Theme () tomo.Theme { func (entity *entity) Theme () tomo.Theme {
return entity.window.theme return entity.backend.theme
} }
// ----------- ConfigurableEntity ----------- // // ----------- ConfigurableEntity ----------- //
func (entity *entity) Config () tomo.Config { func (entity *entity) Config () tomo.Config {
return entity.window.config return entity.backend.config
} }

View File

@ -1,11 +1,8 @@
package x package x
import "image" import "image"
import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/ability" import "git.tebibyte.media/sashakoshka/tomo/ability"
import defaultTheme "git.tebibyte.media/sashakoshka/tomo/default/theme"
import defaultConfig "git.tebibyte.media/sashakoshka/tomo/default/config"
type entitySet map[*entity] struct { } type entitySet map[*entity] struct { }
@ -26,10 +23,7 @@ type system struct {
child *entity child *entity
focused *entity focused *entity
canvas artist.BasicCanvas canvas artist.BasicCanvas
theme tomo.Theme
config tomo.Config
invalidateIgnore bool invalidateIgnore bool
drawingInvalid entitySet drawingInvalid entitySet
anyLayoutInvalid bool anyLayoutInvalid bool
@ -43,12 +37,7 @@ func (system *system) initialize () {
system.drawingInvalid = make(entitySet) system.drawingInvalid = make(entitySet)
} }
func (system *system) setTheme (theme tomo.Theme) { func (system *system) handleThemeChange () {
if theme == nil {
system.theme = defaultTheme.Default { }
} else {
system.theme = theme
}
system.propagate (func (entity *entity) bool { system.propagate (func (entity *entity) bool {
if child, ok := system.child.element.(ability.Themeable); ok { if child, ok := system.child.element.(ability.Themeable); ok {
child.HandleThemeChange() child.HandleThemeChange()
@ -57,12 +46,7 @@ func (system *system) setTheme (theme tomo.Theme) {
}) })
} }
func (system *system) setConfig (config tomo.Config) { func (system *system) handleConfigChange () {
if config == nil {
system.config = defaultConfig.Default { }
} else {
system.config = config
}
system.propagate (func (entity *entity) bool { system.propagate (func (entity *entity) bool {
if child, ok := system.child.element.(ability.Configurable); ok { if child, ok := system.child.element.(ability.Configurable); ok {
child.HandleConfigChange() child.HandleConfigChange()

View File

@ -120,9 +120,6 @@ func (backend *backend) newWindow (
Connect(backend.connection, window.xWindow.Id) Connect(backend.connection, window.xWindow.Id)
xevent.SelectionRequestFun(window.handleSelectionRequest). xevent.SelectionRequestFun(window.handleSelectionRequest).
Connect(backend.connection, window.xWindow.Id) Connect(backend.connection, window.xWindow.Id)
window.setTheme(backend.theme)
window.setConfig(backend.config)
window.metrics.bounds = bounds window.metrics.bounds = bounds
window.setMinimumSize(8, 8) window.setMinimumSize(8, 8)

View File

@ -1,6 +1,8 @@
package x package x
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import defaultTheme "git.tebibyte.media/sashakoshka/tomo/default/theme"
import defaultConfig "git.tebibyte.media/sashakoshka/tomo/default/config"
import "github.com/jezek/xgbutil" import "github.com/jezek/xgbutil"
import "github.com/jezek/xgb/xproto" import "github.com/jezek/xgb/xproto"
@ -96,17 +98,26 @@ func (backend *backend) Do (callback func ()) {
func (backend *backend) SetTheme (theme tomo.Theme) { func (backend *backend) SetTheme (theme tomo.Theme) {
backend.assert() backend.assert()
backend.theme = theme if theme == nil {
backend.theme = defaultTheme.Default { }
} else {
backend.theme = theme
}
for _, window := range backend.windows { for _, window := range backend.windows {
window.setTheme(theme) window.handleThemeChange()
} }
} }
func (backend *backend) SetConfig (config tomo.Config) { func (backend *backend) SetConfig (config tomo.Config) {
backend.assert() backend.assert()
if config == nil {
backend.config = defaultConfig.Default { }
} else {
backend.config = config
}
backend.config = config backend.config = config
for _, window := range backend.windows { for _, window := range backend.windows {
window.setConfig(config) window.handleConfigChange()
} }
} }