ough
This commit is contained in:
parent
2ac23185e6
commit
37df313544
@ -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")
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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()
|
||||
|
@ -1,7 +1,6 @@
|
||||
package elements
|
||||
|
||||
import "git.tebibyte.media/sashakoshka/tomo"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/ability"
|
||||
|
||||
type scratchEntry struct {
|
||||
expand bool
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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 (
|
||||
|
@ -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 () {
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -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())
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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() +
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user