diff --git a/elements/box.go b/elements/box.go index d6d3288..63677b2 100644 --- a/elements/box.go +++ b/elements/box.go @@ -4,7 +4,6 @@ import "image" import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/shatter" -import "git.tebibyte.media/sashakoshka/tomo/ability" var boxCase = tomo.C("tomo", "box") diff --git a/elements/button.go b/elements/button.go index 7738712..2aaa1c9 100644 --- a/elements/button.go +++ b/elements/button.go @@ -4,9 +4,10 @@ import "image" import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/artist" -import "git.tebibyte.media/sashakoshka/tomo/ability" import "git.tebibyte.media/sashakoshka/tomo/textdraw" +var buttonCase = tomo.C("tomo", "button") + // Button is a clickable button. type Button struct { entity tomo.Entity @@ -26,11 +27,11 @@ type Button struct { // NewButton creates a new button with the specified label text. func NewButton (text string) (element *Button) { element = &Button { showText: true, enabled: true } - element.entity = tomo.NewEntity(element).(buttonEntity) - element.theme.Case = tomo.C("tomo", "button") - element.drawer.SetFace (element.theme.FontFace ( + element.entity = tomo.GetBackend().NewEntity(element) + element.drawer.SetFace (element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal)) + tomo.FontSizeNormal, + buttonCase)) element.SetText(text) return } @@ -44,13 +45,13 @@ func (element *Button) Entity () tomo.Entity { func (element *Button) Draw (destination artist.Canvas) { state := element.state() bounds := element.entity.Bounds() - pattern := element.theme.Pattern(tomo.PatternButton, state) + pattern := element.entity.Theme().Pattern(tomo.PatternButton, state, buttonCase) pattern.Draw(destination, bounds) - foreground := element.theme.Color(tomo.ColorForeground, state) - sink := element.theme.Sink(tomo.PatternButton) - margin := element.theme.Margin(tomo.PatternButton) + foreground := element.entity.Theme().Color(tomo.ColorForeground, state, buttonCase) + sink := element.entity.Theme().Sink(tomo.PatternButton, buttonCase) + margin := element.entity.Theme().Margin(tomo.PatternButton, buttonCase) offset := image.Pt ( bounds.Dx() / 2, @@ -65,7 +66,7 @@ func (element *Button) Draw (destination artist.Canvas) { } if element.hasIcon { - icon := element.theme.Icon(element.iconId, tomo.IconSizeSmall) + icon := element.entity.Theme().Icon(element.iconId, tomo.IconSizeSmall, buttonCase) if icon != nil { iconBounds := icon.Bounds() addedWidth := iconBounds.Dx() @@ -150,21 +151,11 @@ func (element *Button) ShowText (showText bool) { element.entity.Invalidate() } -// SetTheme sets the element's theme. -func (element *Button) SetTheme (new tomo.Theme) { - if new == element.theme.Theme { return } - element.theme.Theme = new - element.drawer.SetFace (element.theme.FontFace ( +func (element *Button) HandleThemeChange () { + element.drawer.SetFace (element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal)) - element.updateMinimumSize() - 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 + tomo.FontSizeNormal, + buttonCase)) element.updateMinimumSize() element.entity.Invalidate() } @@ -219,14 +210,14 @@ func (element *Button) HandleKeyUp(key input.Key, modifiers input.Modifiers) { } func (element *Button) updateMinimumSize () { - padding := element.theme.Padding(tomo.PatternButton) - margin := element.theme.Margin(tomo.PatternButton) + padding := element.entity.Theme().Padding(tomo.PatternButton, buttonCase) + margin := element.entity.Theme().Margin(tomo.PatternButton, buttonCase) textBounds := element.drawer.LayoutBounds() minimumSize := textBounds.Sub(textBounds.Min) if element.hasIcon { - icon := element.theme.Icon(element.iconId, tomo.IconSizeSmall) + icon := element.entity.Theme().Icon(element.iconId, tomo.IconSizeSmall, buttonCase) if icon != nil { bounds := icon.Bounds() if element.showText { diff --git a/elements/cell.go b/elements/cell.go index 7032706..2337d65 100644 --- a/elements/cell.go +++ b/elements/cell.go @@ -2,8 +2,9 @@ package elements import "git.tebibyte.media/sashakoshka/tomo" 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 // provides styling based on whether or not it is selected. @@ -20,8 +21,7 @@ type Cell struct { // method. func NewCell (child tomo.Element) (element *Cell) { element = &Cell { enabled: true } - element.theme.Case = tomo.C("tomo", "cell") - element.entity = tomo.NewEntity(element).(cellEntity) + element.entity = tomo.GetBackend().NewEntity(element) element.Adopt(child) return } @@ -34,11 +34,11 @@ func (element *Cell) Entity () tomo.Entity { // Draw causes the element to draw to the specified destination canvas. func (element *Cell) Draw (destination artist.Canvas) { 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 { pattern.Draw(destination, bounds) } else { - artist.DrawShatter ( + artutil.DrawShatter ( destination, pattern, bounds, element.child.Entity().Bounds()) } @@ -49,7 +49,7 @@ func (element *Cell) Layout () { if element.child == nil { return } 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) } @@ -57,7 +57,7 @@ func (element *Cell) Layout () { // DrawBackground draws this element's background pattern to the specified // destination 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()) } @@ -96,16 +96,6 @@ func (element *Cell) SetEnabled (enabled bool) { 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 // or unselected. func (element *Cell) OnSelectionChange (callback func ()) { @@ -116,6 +106,13 @@ func (element *Cell) Selected () bool { return element.entity.Selected() } +func (element *Cell) HandleThemeChange () { + element.updateMinimumSize() + element.entity.Invalidate() + element.invalidateChild() + element.entity.InvalidateLayout() +} + func (element *Cell) HandleSelectionChange () { element.entity.Invalidate() element.invalidateChild() @@ -145,7 +142,7 @@ func (element *Cell) updateMinimumSize () { width += childWidth height += childHeight } - padding := element.theme.Padding(tomo.PatternTableCell) + padding := element.entity.Theme().Padding(tomo.PatternTableCell, cellCase) width += padding.Horizontal() height += padding.Vertical() diff --git a/elements/checkbox.go b/elements/checkbox.go index ed12272..53eb38d 100644 --- a/elements/checkbox.go +++ b/elements/checkbox.go @@ -4,9 +4,10 @@ import "image" import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/artist" -import "git.tebibyte.media/sashakoshka/tomo/ability" import "git.tebibyte.media/sashakoshka/tomo/textdraw" +var checkboxCase = tomo.C("tomo", "checkbox") + // Checkbox is a toggle-able checkbox with a label. type Checkbox struct { entity tomo.Entity @@ -23,11 +24,11 @@ type Checkbox struct { // NewCheckbox creates a new cbeckbox with the specified label text. func NewCheckbox (text string, checked bool) (element *Checkbox) { element = &Checkbox { checked: checked, enabled: true } - element.entity = tomo.NewEntity(element).(checkboxEntity) - element.theme.Case = tomo.C("tomo", "checkbox") - element.drawer.SetFace (element.theme.FontFace ( + element.entity = tomo.GetBackend().NewEntity(element) + element.drawer.SetFace (element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal)) + tomo.FontSizeNormal, + checkboxCase)) element.SetText(text) return } @@ -51,11 +52,11 @@ func (element *Checkbox) Draw (destination artist.Canvas) { element.entity.DrawBackground(destination) - pattern := element.theme.Pattern(tomo.PatternButton, state) + pattern := element.entity.Theme().Pattern(tomo.PatternButton, state, checkboxCase) pattern.Draw(destination, boxBounds) textBounds := element.drawer.LayoutBounds() - margin := element.theme.Margin(tomo.PatternBackground) + margin := element.entity.Theme().Margin(tomo.PatternBackground, checkboxCase) offset := bounds.Min.Add(image.Point { X: bounds.Dy() + margin.X, }) @@ -63,7 +64,7 @@ func (element *Checkbox) Draw (destination artist.Canvas) { offset.Y -= textBounds.Min.Y 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) } @@ -103,21 +104,11 @@ func (element *Checkbox) SetText (text string) { element.entity.Invalidate() } -// SetTheme sets the element's theme. -func (element *Checkbox) SetTheme (new tomo.Theme) { - if new == element.theme.Theme { return } - element.theme.Theme = new - element.drawer.SetFace (element.theme.FontFace ( +func (element *Checkbox) HandleThemeChange () { + element.drawer.SetFace (element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal)) - element.updateMinimumSize() - 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 + tomo.FontSizeNormal, + checkboxCase)) element.updateMinimumSize() element.entity.Invalidate() } @@ -179,7 +170,7 @@ func (element *Checkbox) updateMinimumSize () { if element.text == "" { element.entity.SetMinimumSize(textBounds.Dy(), textBounds.Dy()) } else { - margin := element.theme.Margin(tomo.PatternBackground) + margin := element.entity.Theme().Margin(tomo.PatternBackground, checkboxCase) element.entity.SetMinimumSize ( textBounds.Dy() + margin.X + textBounds.Dx(), textBounds.Dy()) diff --git a/elements/combobox.go b/elements/combobox.go index 549b827..dbd66ca 100644 --- a/elements/combobox.go +++ b/elements/combobox.go @@ -7,6 +7,8 @@ import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/ability" 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)". type Option string @@ -36,11 +38,11 @@ type ComboBox struct { func NewComboBox (options ...Option) (element *ComboBox) { if len(options) == 0 { options = []Option { "" } } element = &ComboBox { enabled: true, options: options } - element.entity = tomo.NewEntity(element).(tomo.FocusableEntity) - element.theme.Case = tomo.C("tomo", "comboBox") - element.drawer.SetFace (element.theme.FontFace ( + element.entity = tomo.GetBackend().NewEntity(element) + element.drawer.SetFace (element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal)) + tomo.FontSizeNormal, + comboBoxCase)) element.Select(options[0]) return } @@ -54,14 +56,14 @@ func (element *ComboBox) Entity () tomo.Entity { func (element *ComboBox) Draw (destination artist.Canvas) { state := element.state() bounds := element.entity.Bounds() - pattern := element.theme.Pattern(tomo.PatternButton, state) + pattern := element.entity.Theme().Pattern(tomo.PatternButton, state, comboBoxCase) pattern.Draw(destination, bounds) - foreground := element.theme.Color(tomo.ColorForeground, state) - sink := element.theme.Sink(tomo.PatternButton) - margin := element.theme.Margin(tomo.PatternButton) - padding := element.theme.Padding(tomo.PatternButton) + foreground := element.entity.Theme().Color(tomo.ColorForeground, state, comboBoxCase) + sink := element.entity.Theme().Sink(tomo.PatternButton, comboBoxCase) + margin := element.entity.Theme().Margin(tomo.PatternButton, comboBoxCase) + padding := element.entity.Theme().Padding(tomo.PatternButton, comboBoxCase) 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.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 { iconBounds := icon.Bounds() addedWidth := iconBounds.Dx() + margin.X @@ -138,21 +140,11 @@ func (element *ComboBox) SetEnabled (enabled bool) { element.entity.Invalidate() } -// SetTheme sets the element's theme. -func (element *ComboBox) SetTheme (new tomo.Theme) { - if new == element.theme.Theme { return } - element.theme.Theme = new - element.drawer.SetFace (element.theme.FontFace ( +func (element *ComboBox) HandleThemeChange () { + element.drawer.SetFace (element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal)) - element.updateMinimumSize() - 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 + tomo.FontSizeNormal, + comboBoxCase)) element.updateMinimumSize() element.entity.Invalidate() } @@ -224,7 +216,7 @@ func (element *ComboBox) dropDown () { menu, err := window.NewMenu(element.entity.Bounds()) if err != nil { return } - cellToOption := make(map[tomo.Selectable] Option) + cellToOption := make(map[ability.Selectable] Option) list := NewList() for _, option := range element.options { @@ -250,13 +242,13 @@ func (element *ComboBox) dropDown () { } func (element *ComboBox) updateMinimumSize () { - padding := element.theme.Padding(tomo.PatternButton) - margin := element.theme.Margin(tomo.PatternButton) + padding := element.entity.Theme().Padding(tomo.PatternButton, comboBoxCase) + margin := element.entity.Theme().Margin(tomo.PatternButton, comboBoxCase) textBounds := element.drawer.LayoutBounds() 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 { bounds := icon.Bounds() minimumSize.Max.X += bounds.Dx() diff --git a/elements/container.go b/elements/container.go index c483d16..df61e45 100644 --- a/elements/container.go +++ b/elements/container.go @@ -1,7 +1,6 @@ package elements import "git.tebibyte.media/sashakoshka/tomo" -import "git.tebibyte.media/sashakoshka/tomo/ability" type scratchEntry struct { expand bool diff --git a/elements/directory.go b/elements/directory.go index 7c5a882..9234a2d 100644 --- a/elements/directory.go +++ b/elements/directory.go @@ -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 // variant for a more information dense view. +var directoryCase = tomo.C("tomo", "list") + type historyEntry struct { location string filesystem ReadDirStatFS @@ -42,8 +44,7 @@ func NewDirectory ( err error, ) { element = &Directory { } - element.theme.Case = tomo.C("tomo", "list") - element.entity = tomo.NewEntity(element).(directoryEntity) + element.entity = tomo.GetBackend().NewEntity(element) element.container.entity = element.entity element.minimumSize = element.updateMinimumSize element.init() @@ -59,7 +60,7 @@ func (element *Directory) Draw (destination artist.Canvas) { tiles := shatter.Shatter(element.entity.Bounds(), rocks...) 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() } - margin := element.theme.Margin(tomo.PatternPinboard) - padding := element.theme.Padding(tomo.PatternPinboard) + margin := element.entity.Theme().Margin(tomo.PatternPinboard, directoryCase) + padding := element.entity.Theme().Padding(tomo.PatternPinboard, directoryCase) bounds := padding.Apply(element.entity.Bounds()) element.contentBounds = image.Rectangle { } @@ -93,7 +94,7 @@ func (element *Directory) Layout () { if width + dot.X > bounds.Max.X { nextLine() } - if typedChild, ok := child.(tomo.Flexible); ok { + if typedChild, ok := child.(ability.Flexible); ok { height = typedChild.FlexibleHeightFor(width) } if rowHeight < height { @@ -139,7 +140,7 @@ func (element *Directory) HandleChildMouseDown ( child tomo.Element, ) { element.selectNone() - if child, ok := child.(tomo.Selectable); ok { + if child, ok := child.(ability.Selectable); ok { index := element.entity.IndexOf(child) 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 // viewport relative to ScrollBounds. 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 = bounds.Sub(bounds.Min).Add(element.scroll) return bounds @@ -199,14 +200,11 @@ func (element *Directory) ScrollAxes () (horizontal, vertical bool) { } 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()) } -// SetTheme sets the element's theme. -func (element *Directory) SetTheme (theme tomo.Theme) { - if theme == element.theme.Theme { return } - element.theme.Theme = theme +func (element *Directory) HandleThemeChange () { element.updateMinimumSize() element.entity.Invalidate() element.entity.InvalidateLayout() @@ -295,7 +293,7 @@ func (element *Directory) selectNone () { } 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() height = element.contentBounds.Dy() - viewportHeight if height < 0 { height = 0 } @@ -304,7 +302,7 @@ func (element *Directory) maxScrollHeight () (height int) { func (element *Directory) updateMinimumSize () { - padding := element.theme.Padding(tomo.PatternPinboard) + padding := element.entity.Theme().Padding(tomo.PatternPinboard, directoryCase) minimumWidth := 0 for index := 0; index < element.entity.CountChildren(); index ++ { width, height := element.entity.ChildMinimumSize(index) diff --git a/elements/document.go b/elements/document.go index 7a23d50..5f92c8c 100644 --- a/elements/document.go +++ b/elements/document.go @@ -6,6 +6,8 @@ import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/ability" import "git.tebibyte.media/sashakoshka/tomo/shatter" +var documentCase = tomo.C("tomo", "document") + // Document is a scrollable container capcable of laying out flexible child // elements. Children can be added either inline (similar to an HTML/CSS inline // element), or expanding (similar to an HTML/CSS block element). @@ -22,8 +24,7 @@ type Document struct { // NewDocument creates a new document container. func NewDocument (children ...tomo.Element) (element *Document) { element = &Document { } - element.theme.Case = tomo.C("tomo", "document") - element.entity = tomo.NewEntity(element) + element.entity = tomo.GetBackend().NewEntity(element) element.container.entity = element.entity element.minimumSize = element.updateMinimumSize element.init() @@ -40,7 +41,7 @@ func (element *Document) Draw (destination artist.Canvas) { tiles := shatter.Shatter(element.entity.Bounds(), rocks...) 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() } - margin := element.theme.Margin(tomo.PatternBackground) - padding := element.theme.Padding(tomo.PatternBackground) + margin := element.entity.Theme().Margin(tomo.PatternBackground, documentCase) + padding := element.entity.Theme().Padding(tomo.PatternBackground, documentCase) bounds := padding.Apply(element.entity.Bounds()) element.contentBounds = image.Rectangle { } @@ -82,7 +83,7 @@ func (element *Document) Layout () { if width < bounds.Dx() && entry.expand { width = bounds.Dx() } - if typedChild, ok := child.(tomo.Flexible); ok { + if typedChild, ok := child.(ability.Flexible); ok { height = typedChild.FlexibleHeightFor(width) } if rowHeight < height { @@ -135,10 +136,7 @@ func (element *Document) DrawBackground (destination artist.Canvas) { element.entity.DrawBackground(destination) } -// SetTheme sets the element's theme. -func (element *Document) SetTheme (theme tomo.Theme) { - if theme == element.theme.Theme { return } - element.theme.Theme = theme +func (element *Document) HandleThemeChange () { element.updateMinimumSize() element.entity.Invalidate() element.entity.InvalidateLayout() @@ -152,7 +150,7 @@ func (element *Document) ScrollContentBounds () image.Rectangle { // ScrollViewportBounds returns the size and position of the element's // viewport relative to ScrollBounds. 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 = bounds.Sub(bounds.Min).Add(element.scroll) return bounds @@ -185,7 +183,7 @@ func (element *Document) ScrollAxes () (horizontal, vertical bool) { } 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() height = element.contentBounds.Dy() - viewportHeight if height < 0 { height = 0 } @@ -193,7 +191,7 @@ func (element *Document) maxScrollHeight () (height int) { } func (element *Document) updateMinimumSize () { - padding := element.theme.Padding(tomo.PatternBackground) + padding := element.entity.Theme().Padding(tomo.PatternBackground, documentCase) minimumWidth := 0 for index := 0; index < element.entity.CountChildren(); index ++ { width, height := element.entity.ChildMinimumSize(index) diff --git a/elements/file.go b/elements/file.go index 5b701a9..0c3e105 100644 --- a/elements/file.go +++ b/elements/file.go @@ -7,6 +7,8 @@ import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/input" 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 system. type File struct { @@ -32,8 +34,7 @@ func NewFile ( err error, ) { element = &File { enabled: true } - element.theme.Case = tomo.C("files", "file") - element.entity = tomo.NewEntity(element).(fileEntity) + element.entity = tomo.GetBackend().NewEntity(element) err = element.SetLocation(location, within) return } @@ -48,9 +49,9 @@ func (element *File) Draw (destination artist.Canvas) { // background state := element.state() bounds := element.entity.Bounds() - sink := element.theme.Sink(tomo.PatternButton) - element.theme. - Pattern(tomo.PatternButton, state). + sink := element.entity.Theme().Sink(tomo.PatternButton, fileCase) + element.entity.Theme(). + Pattern(tomo.PatternButton, state, fileCase). Draw(destination, bounds) // icon @@ -65,7 +66,7 @@ func (element *File) Draw (destination artist.Canvas) { } icon.Draw ( destination, - element.theme.Color(tomo.ColorForeground, state), + element.entity.Theme().Color(tomo.ColorForeground, state, fileCase), bounds.Min.Add(offset)) } } @@ -174,7 +175,7 @@ func (element *File) HandleMouseUp ( if button != input.ButtonLeft { return } element.pressed = false 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 { element.onChoose() } @@ -184,17 +185,8 @@ func (element *File) HandleMouseUp ( element.entity.Invalidate() } -// SetTheme sets the element's theme. -func (element *File) SetTheme (theme tomo.Theme) { - 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 +func (element *File) HandleThemeChange () { + element.updateMinimumSize() element.entity.Invalidate() } @@ -208,11 +200,11 @@ func (element *File) state () tomo.State { } 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 () { - padding := element.theme.Padding(tomo.PatternButton) + padding := element.entity.Theme().Padding(tomo.PatternButton, fileCase) icon := element.icon() if icon == nil { element.entity.SetMinimumSize ( diff --git a/elements/icon.go b/elements/icon.go index dbb177a..db02085 100644 --- a/elements/icon.go +++ b/elements/icon.go @@ -4,6 +4,8 @@ import "image" import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/artist" +var iconCase = tomo.C("tomo", "icon") + // Icon is an element capable of displaying a singular icon. type Icon struct { entity tomo.Entity @@ -17,8 +19,7 @@ func NewIcon (id tomo.Icon, size tomo.IconSize) (element *Icon) { id: id, size: size, } - element.entity = tomo.NewEntity(element).(ability.ThemeableEntity) - element.theme.Case = tomo.C("tomo", "icon") + element.entity = tomo.GetBackend().NewEntity(element) element.updateMinimumSize() return } @@ -37,11 +38,7 @@ func (element *Icon) SetIcon (id tomo.Icon, size tomo.IconSize) { element.entity.Invalidate() } -// SetTheme sets the element's theme. -func (element *Icon) SetTheme (new tomo.Theme) { - if new == element.theme.Theme { return } - element.theme.Theme = new - if element.entity == nil { return } +func (element *Icon) HandleThemeChange () { element.updateMinimumSize() element.entity.Invalidate() } @@ -52,8 +49,8 @@ func (element *Icon) Draw (destination artist.Canvas) { bounds := element.entity.Bounds() state := tomo.State { } - element.theme. - Pattern(tomo.PatternBackground, state). + element.entity.Theme(). + Pattern(tomo.PatternBackground, state, iconCase). Draw(destination, bounds) icon := element.icon() if icon != nil { @@ -63,13 +60,13 @@ func (element *Icon) Draw (destination artist.Canvas) { (bounds.Dy() - iconBounds.Dy()) / 2) icon.Draw ( destination, - element.theme.Color(tomo.ColorForeground, state), + element.entity.Theme().Color(tomo.ColorForeground, state, iconCase), bounds.Min.Add(offset)) } } 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 () { diff --git a/elements/image.go b/elements/image.go index 254e876..271a654 100644 --- a/elements/image.go +++ b/elements/image.go @@ -15,8 +15,8 @@ type Image struct { // NewImage creates a new image element. func NewImage (image image.Image) (element *Image) { - element = &Image { buffer: canvas.FromImage(image) } - element.entity = tomo.NewEntity(element) + element = &Image { buffer: artist.FromImage(image) } + element.entity = tomo.GetBackend().NewEntity(element) bounds := element.buffer.Bounds() element.entity.SetMinimumSize(bounds.Dx(), bounds.Dy()) return diff --git a/elements/label.go b/elements/label.go index 6853c33..0d28b1b 100644 --- a/elements/label.go +++ b/elements/label.go @@ -8,6 +8,8 @@ import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/textdraw" +var labelCase = tomo.C("tomo", "label") + // Label is a simple text box. type Label struct { entity tomo.Entity @@ -25,11 +27,10 @@ type Label struct { // NewLabel creates a new label. func NewLabel (text string) (element *Label) { element = &Label { } - element.theme.Case = tomo.C("tomo", "label") - element.entity = tomo.NewEntity(element) - element.drawer.SetFace (element.theme.FontFace ( + element.entity = tomo.GetBackend().NewEntity(element) + element.drawer.SetFace (element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal)) + tomo.FontSizeNormal, labelCase)) element.SetText(text) return } @@ -58,9 +59,9 @@ func (element *Label) Draw (destination artist.Canvas) { element.entity.DrawBackground(destination) textBounds := element.drawer.LayoutBounds() - foreground := element.theme.Color ( + foreground := element.entity.Theme().Color ( tomo.ColorForeground, - tomo.State { }) + tomo.State { }, labelCase) element.drawer.Draw(destination, foreground, bounds.Min.Sub(textBounds.Min)) } @@ -127,21 +128,10 @@ func (element *Label) SetAlign (align textdraw.Align) { element.entity.Invalidate() } -// SetTheme sets the element's theme. -func (element *Label) SetTheme (new tomo.Theme) { - if new == element.theme.Theme { return } - element.theme.Theme = new - element.drawer.SetFace (element.theme.FontFace ( +func (element *Label) HandleThemeChange () { + element.drawer.SetFace (element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal)) - 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 + tomo.FontSizeNormal, labelCase)) element.updateMinimumSize() element.entity.Invalidate() } @@ -190,7 +180,7 @@ func (element *Label) updateMinimumSize () { if element.wrap { em := element.drawer.Em().Round() 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() element.entity.NotifyFlexibleHeightChange() diff --git a/elements/lerpslider.go b/elements/lerpslider.go index 5e9eb59..9d62618 100644 --- a/elements/lerpslider.go +++ b/elements/lerpslider.go @@ -33,7 +33,7 @@ func NewHLerpSlider[T Numeric] (min, max T, value T) (element *LerpSlider[T]) { min: min, max: max, } - element.entity = tomo.NewEntity(element).(tomo.FocusableEntity) + element.entity = tomo.GetBackend().NewEntity(element) element.construct() element.SetValue(value) return diff --git a/elements/list.go b/elements/list.go index dbae972..db1c006 100644 --- a/elements/list.go +++ b/elements/list.go @@ -4,11 +4,15 @@ import "image" import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/input" 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 { container entity tomo.Entity + c tomo.Case + enabled bool scroll image.Point contentBounds image.Rectangle @@ -32,8 +36,8 @@ type FlowList struct { func NewList (children ...tomo.Element) (element *List) { element = &List { } - element.theme.Case = tomo.C("tomo", "list") - element.entity = tomo.NewEntity(element) + element.c = tomo.C("tomo", "list") + element.entity = tomo.GetBackend().NewEntity(element) element.container.entity = element.entity element.minimumSize = element.updateMinimumSize element.init(children...) @@ -42,8 +46,8 @@ func NewList (children ...tomo.Element) (element *List) { func NewFlowList (children ...tomo.Element) (element *FlowList) { element = &FlowList { } - element.theme.Case = tomo.C("tomo", "flowList") - element.entity = tomo.NewEntity(element).(listEntity) + element.c = tomo.C("tomo", "flowList") + element.entity = tomo.GetBackend().NewEntity(element) element.container.entity = element.entity element.minimumSize = element.updateMinimumSize element.init(children...) @@ -63,8 +67,8 @@ func (element *list) Draw (destination artist.Canvas) { rocks[index] = element.entity.Child(index).Entity().Bounds() } - pattern := element.theme.Pattern(tomo.PatternSunken, element.state()) - artist.DrawShatter(destination, pattern, element.entity.Bounds(), rocks...) + pattern := element.entity.Theme().Pattern(tomo.PatternSunken, element.state(), element.c) + artutil.DrawShatter(destination, pattern, element.entity.Bounds(), rocks...) } func (element *List) Layout () { @@ -72,8 +76,8 @@ func (element *List) Layout () { element.scroll.Y = element.maxScrollHeight() } - margin := element.theme.Margin(tomo.PatternSunken) - padding := element.theme.Padding(tomo.PatternSunken) + margin := element.entity.Theme().Margin(tomo.PatternSunken, element.c) + padding := element.entity.Theme().Padding(tomo.PatternSunken, element.c) bounds := padding.Apply(element.entity.Bounds()) element.contentBounds = image.Rectangle { } @@ -110,8 +114,8 @@ func (element *FlowList) Layout () { element.scroll.Y = element.maxScrollHeight() } - margin := element.theme.Margin(tomo.PatternSunken) - padding := element.theme.Padding(tomo.PatternSunken) + margin := element.entity.Theme().Margin(tomo.PatternSunken, element.c) + padding := element.entity.Theme().Padding(tomo.PatternSunken, element.c) bounds := padding.Apply(element.entity.Bounds()) element.contentBounds = image.Rectangle { } @@ -135,7 +139,7 @@ func (element *FlowList) Layout () { if width + dot.X > bounds.Max.X { nextLine() } - if typedChild, ok := child.(tomo.Flexible); ok { + if typedChild, ok := child.(ability.Flexible); ok { height = typedChild.FlexibleHeightFor(width) } if rowHeight < height { @@ -162,7 +166,7 @@ func (element *FlowList) Layout () { func (element *list) Selected () ability.Selectable { 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 } return child } @@ -221,7 +225,7 @@ func (element *list) HandleChildMouseDown ( ) { if !element.enabled { return } element.Focus() - if child, ok := child.(tomo.Selectable); ok { + if child, ok := child.(ability.Selectable); ok { element.Select(child) } } @@ -274,10 +278,7 @@ func (element *list) DrawBackground (destination artist.Canvas) { element.entity.DrawBackground(destination) } -// SetTheme sets the element's theme. -func (element *list) SetTheme (theme tomo.Theme) { - if theme == element.theme.Theme { return } - element.theme.Theme = theme +func (element *list) HandleThemeChange () { element.minimumSize() element.entity.Invalidate() element.entity.InvalidateLayout() @@ -312,7 +313,7 @@ func (element *list) ScrollContentBounds () image.Rectangle { // ScrollViewportBounds returns the size and position of the element's // viewport relative to ScrollBounds. 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 = bounds.Sub(bounds.Min).Add(element.scroll) return bounds @@ -364,7 +365,7 @@ func (element *list) selectNone () { func (element *list) scrollToSelected () { if element.selected < 0 { return } 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()) if target.Min.Y < bounds.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) { - padding := element.theme.Padding(tomo.PatternSunken) + padding := element.entity.Theme().Padding(tomo.PatternSunken, element.c) viewportHeight := element.entity.Bounds().Dy() - padding.Vertical() height = element.contentBounds.Dy() - viewportHeight if height < 0 { height = 0 } @@ -393,8 +394,8 @@ func (element *list) maxScrollHeight () (height int) { } func (element *List) updateMinimumSize () { - margin := element.theme.Margin(tomo.PatternSunken) - padding := element.theme.Padding(tomo.PatternSunken) + margin := element.entity.Theme().Margin(tomo.PatternSunken, element.c) + padding := element.entity.Theme().Padding(tomo.PatternSunken, element.c) width := 0 height := 0 @@ -427,7 +428,7 @@ func (element *List) updateMinimumSize () { } func (element *FlowList) updateMinimumSize () { - padding := element.theme.Padding(tomo.PatternSunken) + padding := element.entity.Theme().Padding(tomo.PatternSunken, element.c) minimumWidth := 0 for index := 0; index < element.entity.CountChildren(); index ++ { width, height := element.entity.ChildMinimumSize(index) diff --git a/elements/progressbar.go b/elements/progressbar.go index 7179153..c4c5308 100644 --- a/elements/progressbar.go +++ b/elements/progressbar.go @@ -4,6 +4,8 @@ import "image" import "git.tebibyte.media/sashakoshka/tomo" 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. type ProgressBar struct { entity tomo.Entity @@ -16,8 +18,7 @@ func NewProgressBar (progress float64) (element *ProgressBar) { if progress < 0 { progress = 0 } if progress > 1 { progress = 1 } element = &ProgressBar { progress: progress } - element.entity = tomo.NewEntity(element) - element.theme.Case = tomo.C("tomo", "progressBar") + element.entity = tomo.GetBackend().NewEntity(element) element.updateMinimumSize() return } @@ -31,15 +32,15 @@ func (element *ProgressBar) Entity () tomo.Entity { func (element *ProgressBar) Draw (destination artist.Canvas) { bounds := element.entity.Bounds() - pattern := element.theme.Pattern(tomo.PatternSunken, tomo.State { }) - padding := element.theme.Padding(tomo.PatternSunken) + pattern := element.entity.Theme().Pattern(tomo.PatternSunken, tomo.State { }, progressBarCase) + padding := element.entity.Theme().Padding(tomo.PatternSunken, progressBarCase) pattern.Draw(destination, bounds) bounds = padding.Apply(bounds) meterBounds := image.Rect ( bounds.Min.X, bounds.Min.Y, bounds.Min.X + int(float64(bounds.Dx()) * element.progress), 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) } @@ -52,17 +53,14 @@ func (element *ProgressBar) SetProgress (progress float64) { element.entity.Invalidate() } -// SetTheme sets the element's theme. -func (element *ProgressBar) SetTheme (new tomo.Theme) { - if new == element.theme.Theme { return } - element.theme.Theme = new +func (element *ProgressBar) HandleThemeChange () { element.updateMinimumSize() element.entity.Invalidate() } func (element *ProgressBar) updateMinimumSize() { - padding := element.theme.Padding(tomo.PatternSunken) - innerPadding := element.theme.Padding(tomo.PatternMercury) + padding := element.entity.Theme().Padding(tomo.PatternSunken, progressBarCase) + innerPadding := element.entity.Theme().Padding(tomo.PatternMercury, progressBarCase) element.entity.SetMinimumSize ( padding.Horizontal() + innerPadding.Horizontal(), padding.Vertical() + innerPadding.Vertical()) diff --git a/elements/scroll.go b/elements/scroll.go index a32bc34..cd39609 100644 --- a/elements/scroll.go +++ b/elements/scroll.go @@ -6,6 +6,8 @@ import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/ability" +var scrollCase = tomo.C("tomo", "scroll") + // ScrollMode specifies which sides of a Scroll have scroll bars. type ScrollMode int; const ( ScrollNeither ScrollMode = 0 @@ -33,8 +35,7 @@ type Scroll struct { // NewScroll creates a new scroll element. func NewScroll (mode ScrollMode, child ability.Scrollable) (element *Scroll) { element = &Scroll { } - element.theme.Case = tomo.C("tomo", "scroll") - element.entity = tomo.NewEntity(element).(scrollEntity) + element.entity = tomo.GetBackend().NewEntity(element) if mode.Includes(ScrollHorizontal) { element.horizontal = NewHScrollBar() @@ -82,8 +83,8 @@ func (element *Scroll) Draw (destination artist.Canvas) { bounds.Max.X - element.vertical.Entity().Bounds().Dx(), bounds.Max.Y - element.horizontal.Entity().Bounds().Dy()) state := tomo.State { } - deadArea := element.theme.Pattern(tomo.PatternDead, state) - deadArea.Draw(canvas.Cut(destination, bounds), bounds) + deadArea := element.entity.Theme().Pattern(tomo.PatternDead, state, scrollCase) + deadArea.Draw(artist.Cut(destination, bounds), bounds) } } @@ -185,20 +186,12 @@ func (element *Scroll) HandleScroll ( element.scrollChildBy(int(deltaX), int(deltaY)) } -// SetTheme sets the element's theme. -func (element *Scroll) SetTheme (theme tomo.Theme) { - if theme == element.theme.Theme { return } - element.theme.Theme = theme +func (element *Scroll) HandleThemeChange () { element.updateMinimumSize() element.entity.Invalidate() element.entity.InvalidateLayout() } -// SetConfig sets the element's configuration. -func (element *Scroll) SetConfig (config tomo.Config) { - element.config.Config = config -} - func (element *Scroll) updateMinimumSize () { var width, height int diff --git a/elements/scrollbar.go b/elements/scrollbar.go index 634a5b9..e3424e2 100644 --- a/elements/scrollbar.go +++ b/elements/scrollbar.go @@ -4,7 +4,6 @@ import "image" import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/input" 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 // 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 { entity tomo.Entity + c tomo.Case + vertical bool enabled bool dragging bool @@ -39,8 +40,8 @@ func NewVScrollBar () (element *ScrollBar) { vertical: true, enabled: true, } - element.theme.Case = tomo.C("tomo", "scrollBarVertical") - element.entity = tomo.NewEntity(element).(scrollBarEntity) + element.c = tomo.C("tomo", "scrollBarVertical") + element.entity = tomo.GetBackend().NewEntity(element) element.updateMinimumSize() return } @@ -50,8 +51,8 @@ func NewHScrollBar () (element *ScrollBar) { element = &ScrollBar { enabled: true, } - element.theme.Case = tomo.C("tomo", "scrollBarHorizontal") - element.entity = tomo.NewEntity(element).(tomo.Entity) + element.c = tomo.C("tomo", "scrollBarHorizontal") + element.entity = tomo.GetBackend().NewEntity(element) element.updateMinimumSize() return } @@ -70,10 +71,10 @@ func (element *ScrollBar) Draw (destination artist.Canvas) { Disabled: !element.Enabled(), Pressed: element.dragging, } - element.theme.Pattern(tomo.PatternGutter, state).Draw ( + element.entity.Theme().Pattern(tomo.PatternGutter, state, element.c).Draw ( destination, bounds) - element.theme.Pattern(tomo.PatternHandle, state).Draw ( + element.entity.Theme().Pattern(tomo.PatternHandle, state, element.c).Draw ( destination, element.bar) } @@ -83,7 +84,7 @@ func (element *ScrollBar) HandleMouseDown ( button input.Button, modifiers input.Modifiers, ) { - velocity := element.config.ScrollVelocity() + velocity := element.entity.Config().ScrollVelocity() if position.In(element.bar) { // 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 } -// SetTheme sets the element's theme. -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 +func (element *ScrollBar) HandleThemeChange () { element.updateMinimumSize() element.entity.Invalidate() } @@ -263,7 +254,7 @@ func (element *ScrollBar) recalculate () { func (element *ScrollBar) recalculateVertical () { bounds := element.entity.Bounds() - padding := element.theme.Padding(tomo.PatternGutter) + padding := element.entity.Theme().Padding(tomo.PatternGutter, element.c) element.track = padding.Apply(bounds) contentBounds := element.contentBounds @@ -290,7 +281,7 @@ func (element *ScrollBar) recalculateVertical () { func (element *ScrollBar) recalculateHorizontal () { bounds := element.entity.Bounds() - padding := element.theme.Padding(tomo.PatternGutter) + padding := element.entity.Theme().Padding(tomo.PatternGutter, element.c) element.track = padding.Apply(bounds) contentBounds := element.contentBounds @@ -316,8 +307,8 @@ func (element *ScrollBar) recalculateHorizontal () { } func (element *ScrollBar) updateMinimumSize () { - gutterPadding := element.theme.Padding(tomo.PatternGutter) - handlePadding := element.theme.Padding(tomo.PatternHandle) + gutterPadding := element.entity.Theme().Padding(tomo.PatternGutter, element.c) + handlePadding := element.entity.Theme().Padding(tomo.PatternHandle, element.c) if element.vertical { element.entity.SetMinimumSize ( gutterPadding.Horizontal() + handlePadding.Horizontal(), diff --git a/elements/slider.go b/elements/slider.go index f2b7ea1..5e67425 100644 --- a/elements/slider.go +++ b/elements/slider.go @@ -4,7 +4,6 @@ import "image" import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/input" 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. type Slider struct { @@ -22,12 +21,16 @@ func NewVSlider (value float64) (element *Slider) { func NewHSlider (value float64) (element *Slider) { element = &Slider { } element.value = value - element.entity = tomo.NewEntity(element) + element.entity = tomo.GetBackend().NewEntity(element) element.construct() return } type slider struct { + entity tomo.Entity + + c tomo.Case + value float64 vertical bool dragging bool @@ -43,9 +46,9 @@ type slider struct { func (element *slider) construct () { element.enabled = true if element.vertical { - element.theme.Case = tomo.C("tomo", "sliderVertical") + element.c = tomo.C("tomo", "sliderVertical") } else { - element.theme.Case = tomo.C("tomo", "sliderHorizontal") + element.c = tomo.C("tomo", "sliderHorizontal") } element.updateMinimumSize() } @@ -58,7 +61,7 @@ func (element *slider) Entity () tomo.Entity { // Draw causes the element to draw to the specified destination canvas. func (element *slider) Draw (destination artist.Canvas) { 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 { barSize := element.track.Dx() 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(), Pressed: element.dragging, } - element.theme.Pattern(tomo.PatternGutter, state).Draw(destination, bounds) - element.theme.Pattern(tomo.PatternHandle, state).Draw(destination, element.bar) + element.entity.Theme().Pattern(tomo.PatternGutter, state, element.c).Draw(destination, bounds) + element.entity.Theme().Pattern(tomo.PatternHandle, state, element.c).Draw(destination, element.bar) } // Focus gives this element input focus. @@ -205,21 +208,12 @@ func (element *slider) OnRelease (callback func ()) { element.onRelease = callback } -// SetTheme sets the element's theme. -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 +func (element *slider) HandleThemeChange () { element.updateMinimumSize() element.entity.Invalidate() } + func (element *slider) changeValue (delta float64) { element.value += delta if element.value < 0 { @@ -252,8 +246,8 @@ func (element *slider) valueFor (x, y int) (value float64) { } func (element *slider) updateMinimumSize () { - gutterPadding := element.theme.Padding(tomo.PatternGutter) - handlePadding := element.theme.Padding(tomo.PatternHandle) + gutterPadding := element.entity.Theme().Padding(tomo.PatternGutter, element.c) + handlePadding := element.entity.Theme().Padding(tomo.PatternHandle, element.c) if element.vertical { element.entity.SetMinimumSize ( gutterPadding.Horizontal() + handlePadding.Horizontal(), diff --git a/elements/spacer.go b/elements/spacer.go index 0f6d73d..1f072df 100644 --- a/elements/spacer.go +++ b/elements/spacer.go @@ -3,6 +3,8 @@ package elements import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/artist" +var spacerCase = tomo.C("tomo", "spacer") + // Spacer can be used to put space between two elements.. type Spacer struct { entity tomo.Entity @@ -12,8 +14,7 @@ type Spacer struct { // NewSpacer creates a new spacer. func NewSpacer () (element *Spacer) { element = &Spacer { } - element.entity = tomo.NewEntity(element).(spacerEntity) - element.theme.Case = tomo.C("tomo", "spacer") + element.entity = tomo.GetBackend().NewEntity(element) element.updateMinimumSize() return } @@ -35,14 +36,14 @@ func (element *Spacer) Draw (destination artist.Canvas) { bounds := element.entity.Bounds() if element.line { - pattern := element.theme.Pattern ( + pattern := element.entity.Theme().Pattern ( tomo.PatternLine, - tomo.State { }) + tomo.State { }, spacerCase) pattern.Draw(destination, bounds) } else { - pattern := element.theme.Pattern ( + pattern := element.entity.Theme().Pattern ( tomo.PatternBackground, - tomo.State { }) + tomo.State { }, spacerCase) pattern.Draw(destination, bounds) } } @@ -55,23 +56,13 @@ func (element *Spacer) SetLine (line bool) { element.entity.Invalidate() } -// SetTheme sets the element's theme. -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 +func (element *Spacer) HandleThemeChange () { element.entity.Invalidate() } func (element *Spacer) updateMinimumSize () { if element.line { - padding := element.theme.Padding(tomo.PatternLine) + padding := element.entity.Theme().Padding(tomo.PatternLine, spacerCase) element.entity.SetMinimumSize ( padding.Horizontal(), padding.Vertical()) diff --git a/elements/switch.go b/elements/switch.go index 11d139f..dd6ea85 100644 --- a/elements/switch.go +++ b/elements/switch.go @@ -6,6 +6,8 @@ import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/artist" 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 // functionally identical to Checkbox, but plays a different semantic role. type Switch struct { @@ -27,11 +29,10 @@ func NewSwitch (text string, on bool) (element *Switch) { text: text, enabled: true, } - element.entity = tomo.NewEntity(element).(checkboxEntity) - element.theme.Case = tomo.C("tomo", "switch") - element.drawer.SetFace (element.theme.FontFace ( + element.entity = tomo.GetBackend().NewEntity(element) + element.drawer.SetFace (element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal)) + tomo.FontSizeNormal, switchCase)) element.drawer.SetText([]rune(text)) element.updateMinimumSize() return @@ -71,24 +72,24 @@ func (element *Switch) Draw (destination artist.Canvas) { } } - gutterPattern := element.theme.Pattern ( - tomo.PatternGutter, state) + gutterPattern := element.entity.Theme().Pattern ( + tomo.PatternGutter, state, switchCase) gutterPattern.Draw(destination, gutterBounds) - handlePattern := element.theme.Pattern ( - tomo.PatternHandle, state) + handlePattern := element.entity.Theme().Pattern ( + tomo.PatternHandle, state, switchCase) handlePattern.Draw(destination, handleBounds) textBounds := element.drawer.LayoutBounds() offset := bounds.Min.Add(image.Point { X: bounds.Dy() * 2 + - element.theme.Margin(tomo.PatternBackground).X, + element.entity.Theme().Margin(tomo.PatternBackground, switchCase).X, }) offset.Y -= textBounds.Min.Y 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) } @@ -181,21 +182,10 @@ func (element *Switch) SetText (text string) { element.entity.Invalidate() } -// SetTheme sets the element's theme. -func (element *Switch) SetTheme (new tomo.Theme) { - if new == element.theme.Theme { return } - element.theme.Theme = new - element.drawer.SetFace (element.theme.FontFace ( +func (element *Switch) HandleThemeChange () { + element.drawer.SetFace (element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal)) - 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 + tomo.FontSizeNormal, switchCase)) element.updateMinimumSize() element.entity.Invalidate() } @@ -209,7 +199,7 @@ func (element *Switch) updateMinimumSize () { } else { element.entity.SetMinimumSize ( lineHeight * 2 + - element.theme.Margin(tomo.PatternBackground).X + + element.entity.Theme().Margin(tomo.PatternBackground, switchCase).X + textBounds.Dx(), lineHeight) } diff --git a/elements/textbox.go b/elements/textbox.go index fe1dfa3..81cc5e0 100644 --- a/elements/textbox.go +++ b/elements/textbox.go @@ -12,6 +12,8 @@ import "git.tebibyte.media/sashakoshka/tomo/textmanip" import "git.tebibyte.media/sashakoshka/tomo/fixedutil" import "git.tebibyte.media/sashakoshka/tomo/artist/shapes" +var textBoxCase = tomo.C("tomo", "textBox") + // TextBox is a single-line text input. type TextBox struct { entity tomo.Entity @@ -38,15 +40,14 @@ type TextBox struct { // text. func NewTextBox (placeholder, value string) (element *TextBox) { element = &TextBox { enabled: true } - element.theme.Case = tomo.C("tomo", "textBox") - element.entity = tomo.NewEntity(element).(textBoxEntity) + element.entity = tomo.GetBackend().NewEntity(element) element.placeholder = placeholder - element.placeholderDrawer.SetFace (element.theme.FontFace ( + element.placeholderDrawer.SetFace (element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal)) - element.valueDrawer.SetFace (element.theme.FontFace ( + tomo.FontSizeNormal, textBoxCase)) + element.valueDrawer.SetFace (element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal)) + tomo.FontSizeNormal, textBoxCase)) element.placeholderDrawer.SetText([]rune(placeholder)) element.updateMinimumSize() element.SetValue(value) @@ -63,15 +64,15 @@ func (element *TextBox) Draw (destination artist.Canvas) { bounds := element.entity.Bounds() state := element.state() - pattern := element.theme.Pattern(tomo.PatternInput, state) - padding := element.theme.Padding(tomo.PatternInput) - innerCanvas := canvas.Cut(destination, padding.Apply(bounds)) + pattern := element.entity.Theme().Pattern(tomo.PatternInput, state, textBoxCase) + padding := element.entity.Theme().Padding(tomo.PatternInput, textBoxCase) + innerCanvas := artist.Cut(destination, padding.Apply(bounds)) pattern.Draw(destination, bounds) offset := element.textOffset() if element.entity.Focused() && !element.dot.Empty() { // draw selection bounds - accent := element.theme.Color(tomo.ColorAccent, state) + accent := element.entity.Theme().Color(tomo.ColorAccent, state, textBoxCase) canon := element.dot.Canon() foff := fixedutil.Pt(offset) start := element.valueDrawer.PositionAt(canon.Start).Add(foff) @@ -89,9 +90,9 @@ func (element *TextBox) Draw (destination artist.Canvas) { if len(element.text) == 0 { // draw placeholder textBounds := element.placeholderDrawer.LayoutBounds() - foreground := element.theme.Color ( + foreground := element.entity.Theme().Color ( tomo.ColorForeground, - tomo.State { Disabled: true }) + tomo.State { Disabled: true }, textBoxCase) element.placeholderDrawer.Draw ( innerCanvas, foreground, @@ -99,7 +100,7 @@ func (element *TextBox) Draw (destination artist.Canvas) { } else { // draw input value textBounds := element.valueDrawer.LayoutBounds() - foreground := element.theme.Color(tomo.ColorForeground, state) + foreground := element.entity.Theme().Color(tomo.ColorForeground, state, textBoxCase) element.valueDrawer.Draw ( innerCanvas, foreground, @@ -108,7 +109,7 @@ func (element *TextBox) Draw (destination artist.Canvas) { if element.entity.Focused() && element.dot.Empty() { // draw cursor - foreground := element.theme.Color(tomo.ColorForeground, state) + foreground := element.entity.Theme().Color(tomo.ColorForeground, state, textBoxCase) cursorPosition := fixedutil.RoundPt ( element.valueDrawer.PositionAt(element.dot.End)) shapes.ColorLine ( @@ -144,7 +145,7 @@ func (element *TextBox) HandleMouseDown ( runeIndex := element.atPosition(position) 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.dot = textmanip.WordAround(element.text, runeIndex) } else { @@ -202,7 +203,7 @@ func (element *TextBox) HandleMotion (position 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() innerBounds := padding.Apply(bounds) textHeight := element.valueDrawer.LineHeight().Round() @@ -464,27 +465,17 @@ func (element *TextBox) ScrollAxes () (horizontal, vertical bool) { return true, false } -// SetTheme sets the element's theme. -func (element *TextBox) SetTheme (new tomo.Theme) { - if new == element.theme.Theme { return } - element.theme.Theme = new - face := element.theme.FontFace ( +func (element *TextBox) HandleThemeChange () { + face := element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal) + tomo.FontSizeNormal, + textBoxCase) element.placeholderDrawer.SetFace(face) element.valueDrawer.SetFace(face) element.updateMinimumSize() 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) { window := element.entity.Window() menu, err := window.NewMenu(image.Rectangle { position, position }) @@ -528,12 +519,12 @@ func (element *TextBox) runOnChange () { } 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() } 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 = bounds.Sub(bounds.Min) bounds.Max.X -= element.valueDrawer.Em().Round() @@ -556,7 +547,7 @@ func (element *TextBox) scrollToCursor () { func (element *TextBox) updateMinimumSize () { textBounds := element.placeholderDrawer.LayoutBounds() - padding := element.theme.Padding(tomo.PatternInput) + padding := element.entity.Theme().Padding(tomo.PatternInput, textBoxCase) element.entity.SetMinimumSize ( padding.Horizontal() + textBounds.Dx(), padding.Vertical() + diff --git a/elements/togglebutton.go b/elements/togglebutton.go index 0187de7..55e0e1e 100644 --- a/elements/togglebutton.go +++ b/elements/togglebutton.go @@ -6,6 +6,8 @@ import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/textdraw" +var toggleButtonCase = tomo.C("tomo", "toggleButton") + // ToggleButton is a togglable button. type ToggleButton struct { entity tomo.Entity @@ -30,11 +32,11 @@ func NewToggleButton (text string, on bool) (element *ToggleButton) { enabled: true, on: on, } - element.entity = tomo.NewEntity(element) - element.theme.Case = tomo.C("tomo", "toggleButton") - element.drawer.SetFace (element.theme.FontFace ( + element.entity = tomo.GetBackend().NewEntity(element) + element.drawer.SetFace (element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal)) + tomo.FontSizeNormal, + toggleButtonCase)) element.SetText(text) return } @@ -48,10 +50,10 @@ func (element *ToggleButton) Entity () tomo.Entity { func (element *ToggleButton) Draw (destination artist.Canvas) { state := element.state() 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) - lampPadding := element.theme.Padding(tomo.PatternLamp).Horizontal() + lampPattern := element.entity.Theme().Pattern(tomo.PatternLamp, state, toggleButtonCase) + lampPadding := element.entity.Theme().Padding(tomo.PatternLamp, toggleButtonCase).Horizontal() lampBounds := bounds lampBounds.Max.X = lampBounds.Min.X + lampPadding bounds.Min.X += lampPadding @@ -59,9 +61,9 @@ func (element *ToggleButton) Draw (destination artist.Canvas) { pattern.Draw(destination, bounds) lampPattern.Draw(destination, lampBounds) - foreground := element.theme.Color(tomo.ColorForeground, state) - sink := element.theme.Sink(tomo.PatternButton) - margin := element.theme.Margin(tomo.PatternButton) + foreground := element.entity.Theme().Color(tomo.ColorForeground, state, toggleButtonCase) + sink := element.entity.Theme().Sink(tomo.PatternButton, toggleButtonCase) + margin := element.entity.Theme().Margin(tomo.PatternButton, toggleButtonCase) offset := image.Pt ( bounds.Dx() / 2, @@ -76,7 +78,7 @@ func (element *ToggleButton) Draw (destination artist.Canvas) { } if element.hasIcon { - icon := element.theme.Icon(element.iconId, tomo.IconSizeSmall) + icon := element.entity.Theme().Icon(element.iconId, tomo.IconSizeSmall, toggleButtonCase) if icon != nil { iconBounds := icon.Bounds() addedWidth := iconBounds.Dx() @@ -166,21 +168,10 @@ func (element *ToggleButton) ShowText (showText bool) { element.entity.Invalidate() } -// SetTheme sets the element's theme. -func (element *ToggleButton) SetTheme (new tomo.Theme) { - if new == element.theme.Theme { return } - element.theme.Theme = new - element.drawer.SetFace (element.theme.FontFace ( +func (element *ToggleButton) HandleThemeChange () { + element.drawer.SetFace (element.entity.Theme().FontFace ( tomo.FontStyleRegular, - tomo.FontSizeNormal)) - 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 + tomo.FontSizeNormal, toggleButtonCase)) element.updateMinimumSize() element.entity.Invalidate() } @@ -239,15 +230,15 @@ func (element *ToggleButton) HandleKeyUp(key input.Key, modifiers input.Modifier } func (element *ToggleButton) updateMinimumSize () { - padding := element.theme.Padding(tomo.PatternButton) - margin := element.theme.Margin(tomo.PatternButton) - lampPadding := element.theme.Padding(tomo.PatternLamp) + padding := element.entity.Theme().Padding(tomo.PatternButton, toggleButtonCase) + margin := element.entity.Theme().Margin(tomo.PatternButton, toggleButtonCase) + lampPadding := element.entity.Theme().Padding(tomo.PatternLamp, toggleButtonCase) textBounds := element.drawer.LayoutBounds() minimumSize := textBounds.Sub(textBounds.Min) if element.hasIcon { - icon := element.theme.Icon(element.iconId, tomo.IconSizeSmall) + icon := element.entity.Theme().Icon(element.iconId, tomo.IconSizeSmall, toggleButtonCase) if icon != nil { bounds := icon.Bounds() if element.showText { diff --git a/examples/vbox/main.go b/examples/vbox/main.go index 3c6bfd9..d22a921 100644 --- a/examples/vbox/main.go +++ b/examples/vbox/main.go @@ -1,16 +1,19 @@ package main 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/testing" -import _ "git.tebibyte.media/sashakoshka/tomo/backends/all" func main () { - tomo.Run(run) + nasin.Run(Application { }) } -func run () { - window, _ := tomo.NewWindow(tomo.Bounds(0, 0, 128, 128)) +type Application struct { } + +func (Application) Init () error { + window, err := nasin.NewWindow(tomo.Bounds(0, 0, 128, 128)) + if err != nil { return err } window.SetTitle("vertical stack") container := elements.NewVBox(elements.SpaceBoth) @@ -25,13 +28,15 @@ func run () { container.Adopt(okButton) okButton.Focus() }) - okButton.OnClick(tomo.Stop) + okButton.OnClick(nasin.Stop) container.AdoptExpand(label) container.Adopt(button, okButton) window.Adopt(container) okButton.Focus() - window.OnClose(tomo.Stop) + window.OnClose(nasin.Stop) window.Show() + + return nil } diff --git a/plugins/x/x/entity.go b/plugins/x/x/entity.go index 6d04c02..7718e57 100644 --- a/plugins/x/x/entity.go +++ b/plugins/x/x/entity.go @@ -6,6 +6,7 @@ import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/ability" type entity struct { + backend *backend window *window parent *entity children []*entity @@ -21,7 +22,7 @@ type entity struct { } func (backend *backend) NewEntity (owner tomo.Element) tomo.Entity { - entity := &entity { element: owner } + entity := &entity { element: owner, backend: backend } entity.InvalidateLayout() return entity } @@ -162,7 +163,7 @@ func (entity *entity) DrawBackground (destination artist.Canvas) { if entity.parent != nil { entity.parent.element.(ability.Container).DrawBackground(destination) } else if entity.window != nil { - entity.window.system.theme.Pattern ( + entity.backend.theme.Pattern ( tomo.PatternBackground, tomo.State { }, tomo.C("tomo", "window")).Draw ( @@ -292,11 +293,11 @@ func (entity *entity) NotifyScrollBoundsChange () { // ----------- ThemeableEntity ----------- // func (entity *entity) Theme () tomo.Theme { - return entity.window.theme + return entity.backend.theme } // ----------- ConfigurableEntity ----------- // func (entity *entity) Config () tomo.Config { - return entity.window.config + return entity.backend.config } diff --git a/plugins/x/x/system.go b/plugins/x/x/system.go index 6817aa5..4143774 100644 --- a/plugins/x/x/system.go +++ b/plugins/x/x/system.go @@ -1,11 +1,8 @@ package x import "image" -import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/artist" 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 { } @@ -26,10 +23,7 @@ type system struct { child *entity focused *entity canvas artist.BasicCanvas - - theme tomo.Theme - config tomo.Config - + invalidateIgnore bool drawingInvalid entitySet anyLayoutInvalid bool @@ -43,12 +37,7 @@ func (system *system) initialize () { system.drawingInvalid = make(entitySet) } -func (system *system) setTheme (theme tomo.Theme) { - if theme == nil { - system.theme = defaultTheme.Default { } - } else { - system.theme = theme - } +func (system *system) handleThemeChange () { system.propagate (func (entity *entity) bool { if child, ok := system.child.element.(ability.Themeable); ok { child.HandleThemeChange() @@ -57,12 +46,7 @@ func (system *system) setTheme (theme tomo.Theme) { }) } -func (system *system) setConfig (config tomo.Config) { - if config == nil { - system.config = defaultConfig.Default { } - } else { - system.config = config - } +func (system *system) handleConfigChange () { system.propagate (func (entity *entity) bool { if child, ok := system.child.element.(ability.Configurable); ok { child.HandleConfigChange() diff --git a/plugins/x/x/window.go b/plugins/x/x/window.go index 85b20a1..18cbbf9 100644 --- a/plugins/x/x/window.go +++ b/plugins/x/x/window.go @@ -120,9 +120,6 @@ func (backend *backend) newWindow ( Connect(backend.connection, window.xWindow.Id) xevent.SelectionRequestFun(window.handleSelectionRequest). Connect(backend.connection, window.xWindow.Id) - - window.setTheme(backend.theme) - window.setConfig(backend.config) window.metrics.bounds = bounds window.setMinimumSize(8, 8) diff --git a/plugins/x/x/x.go b/plugins/x/x/x.go index a063129..82a88be 100644 --- a/plugins/x/x/x.go +++ b/plugins/x/x/x.go @@ -1,6 +1,8 @@ package x 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/xgb/xproto" @@ -96,17 +98,26 @@ func (backend *backend) Do (callback func ()) { func (backend *backend) SetTheme (theme tomo.Theme) { backend.assert() - backend.theme = theme + if theme == nil { + backend.theme = defaultTheme.Default { } + } else { + backend.theme = theme + } for _, window := range backend.windows { - window.setTheme(theme) + window.handleThemeChange() } } func (backend *backend) SetConfig (config tomo.Config) { backend.assert() + if config == nil { + backend.config = defaultConfig.Default { } + } else { + backend.config = config + } backend.config = config for _, window := range backend.windows { - window.setConfig(config) + window.handleConfigChange() } }