Migrated some more elements

This commit is contained in:
Sasha Koshka 2023-04-15 01:45:11 -04:00
parent a43f5ce595
commit ca86328506
6 changed files with 165 additions and 340 deletions

View File

@ -1,177 +0,0 @@
package elements
import "golang.org/x/image/math/fixed"
import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/canvas"
import "git.tebibyte.media/sashakoshka/tomo/textdraw"
import "git.tebibyte.media/sashakoshka/tomo/default/theme"
import "git.tebibyte.media/sashakoshka/tomo/default/config"
// Label is a simple text box.
type Label struct {
entity tomo.FlexibleEntity
align textdraw.Align
wrap bool
text string
drawer textdraw.Drawer
forcedColumns int
forcedRows int
minHeight int
config config.Wrapped
theme theme.Wrapped
}
// NewLabel creates a new label. If wrap is set to true, the text inside will be
// wrapped.
func NewLabel (text string, wrap bool) (element *Label) {
element = &Label { }
element.theme.Case = tomo.C("tomo", "label")
element.drawer.SetFace (element.theme.FontFace (
tomo.FontStyleRegular,
tomo.FontSizeNormal))
element.SetWrap(wrap)
element.SetText(text)
return
}
// Bind binds this element to an entity.
func (element *Label) Bind (entity tomo.Entity) {
element.entity = entity.(tomo.FlexibleEntity)
if element.entity == nil { return }
element.updateMinimumSize()
}
// EmCollapse forces a minimum width and height upon the label. The width is
// measured in emspaces, and the height is measured in lines. If a zero value is
// given for a dimension, its minimum will be determined by the label's content.
// If the label's content is greater than these dimensions, it will be truncated
// to fit.
func (element *Label) EmCollapse (columns int, rows int) {
element.forcedColumns = columns
element.forcedRows = rows
if element.entity == nil { return }
element.updateMinimumSize()
}
// FlexibleHeightFor returns the reccomended height for this element based on
// the given width in order to allow the text to wrap properly.
func (element *Label) FlexibleHeightFor (width int) (height int) {
if element.wrap {
return element.drawer.ReccomendedHeightFor(width)
} else {
return element.minHeight
}
}
// SetText sets the label's text.
func (element *Label) SetText (text string) {
if element.text == text { return }
element.text = text
element.drawer.SetText([]rune(text))
if element.entity == nil { return }
element.updateMinimumSize()
element.entity.Invalidate()
}
// SetWrap sets wether or not the label's text wraps. If the text is set to
// wrap, the element will have a minimum size of a single character and
// automatically wrap its text. If the text is set to not wrap, the element will
// have a minimum size that fits its text.
func (element *Label) SetWrap (wrap bool) {
if wrap == element.wrap { return }
if !wrap {
element.drawer.SetMaxWidth(0)
element.drawer.SetMaxHeight(0)
}
element.wrap = wrap
if element.entity == nil { return }
element.updateMinimumSize()
element.entity.Invalidate()
}
// SetAlign sets the alignment method of the label.
func (element *Label) SetAlign (align textdraw.Align) {
if align == element.align { return }
element.align = align
element.drawer.SetAlign(align)
if element.entity == nil { return }
element.updateMinimumSize()
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 (
tomo.FontStyleRegular,
tomo.FontSizeNormal))
if element.entity == nil { return }
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
if element.entity == nil { return }
element.updateMinimumSize()
element.entity.Invalidate()
}
// Draw causes the element to draw to the specified destination canvas.
func (element *Label) Draw (destination canvas.Canvas) {
if element.entity == nil { return }
bounds := element.entity. Bounds()
if element.wrap {
element.drawer.SetMaxWidth(bounds.Dx())
element.drawer.SetMaxHeight(bounds.Dy())
}
element.entity.DrawBackground(destination, bounds)
textBounds := element.drawer.LayoutBounds()
foreground := element.theme.Color (
tomo.ColorForeground,
tomo.State { })
element.drawer.Draw(destination, foreground, bounds.Min.Sub(textBounds.Min))
}
func (element *Label) updateMinimumSize () {
var width, height int
if element.wrap {
em := element.drawer.Em().Round()
if em < 1 {
em = element.theme.Padding(tomo.PatternBackground)[0]
}
width, height = em, element.drawer.LineHeight().Round()
// FIXME we shoudl not have to pass in the element here
element.entity.NotifyFlexibleHeightChange(element)
} else {
bounds := element.drawer.LayoutBounds()
width, height = bounds.Dx(), bounds.Dy()
}
if element.forcedColumns > 0 {
width =
element.drawer.Em().
Mul(fixed.I(element.forcedColumns)).Floor()
}
if element.forcedRows > 0 {
height =
element.drawer.LineHeight().
Mul(fixed.I(element.forcedRows)).Floor()
}
element.minHeight = height
element.entity.SetMinimumSize(width, height)
}

View File

@ -2,11 +2,13 @@ package elements
import "image" import "image"
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/canvas"
import "git.tebibyte.media/sashakoshka/tomo/default/theme" import "git.tebibyte.media/sashakoshka/tomo/default/theme"
import "git.tebibyte.media/sashakoshka/tomo/default/config" import "git.tebibyte.media/sashakoshka/tomo/default/config"
// ProgressBar displays a visual indication of how far along a task is. // ProgressBar displays a visual indication of how far along a task is.
type ProgressBar struct { type ProgressBar struct {
entity tomo.Entity
progress float64 progress float64
config config.Wrapped config config.Wrapped
@ -17,20 +19,38 @@ type ProgressBar struct {
// level. // level.
func NewProgressBar (progress float64) (element *ProgressBar) { func NewProgressBar (progress float64) (element *ProgressBar) {
element = &ProgressBar { progress: progress } element = &ProgressBar { progress: progress }
element.entity = tomo.NewEntity(element)
element.theme.Case = tomo.C("tomo", "progressBar") element.theme.Case = tomo.C("tomo", "progressBar")
element.Core, element.core = core.NewCore(element, element.draw)
element.updateMinimumSize() element.updateMinimumSize()
return return
} }
// Entity returns this element's entity.
func (element *ProgressBar) Entity () tomo.Entity {
return element.entity
}
// Draw causes the element to draw to the specified destination canvas.
func (element *ProgressBar) Draw (destination canvas.Canvas) {
bounds := element.entity.Bounds()
pattern := element.theme.Pattern(tomo.PatternSunken, tomo.State { })
padding := element.theme.Padding(tomo.PatternSunken)
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.Draw(destination, meterBounds)
}
// SetProgress sets the progress level of the bar. // SetProgress sets the progress level of the bar.
func (element *ProgressBar) SetProgress (progress float64) { func (element *ProgressBar) SetProgress (progress float64) {
if progress == element.progress { return } if progress == element.progress { return }
element.progress = progress element.progress = progress
if element.core.HasImage() { element.entity.Invalidate()
element.draw()
element.core.DamageAll()
}
} }
// SetTheme sets the element's theme. // SetTheme sets the element's theme.
@ -38,7 +58,7 @@ func (element *ProgressBar) SetTheme (new tomo.Theme) {
if new == element.theme.Theme { return } if new == element.theme.Theme { return }
element.theme.Theme = new element.theme.Theme = new
element.updateMinimumSize() element.updateMinimumSize()
element.redo() element.entity.Invalidate()
} }
// SetConfig sets the element's configuration. // SetConfig sets the element's configuration.
@ -46,35 +66,13 @@ func (element *ProgressBar) SetConfig (new tomo.Config) {
if new == nil || new == element.config.Config { return } if new == nil || new == element.config.Config { return }
element.config.Config = new element.config.Config = new
element.updateMinimumSize() element.updateMinimumSize()
element.redo() element.entity.Invalidate()
} }
func (element *ProgressBar) updateMinimumSize() { func (element *ProgressBar) updateMinimumSize() {
padding := element.theme.Padding(tomo.PatternSunken) padding := element.theme.Padding(tomo.PatternSunken)
innerPadding := element.theme.Padding(tomo.PatternMercury) innerPadding := element.theme.Padding(tomo.PatternMercury)
element.core.SetMinimumSize ( element.entity.SetMinimumSize (
padding.Horizontal() + innerPadding.Horizontal(), padding.Horizontal() + innerPadding.Horizontal(),
padding.Vertical() + innerPadding.Vertical()) padding.Vertical() + innerPadding.Vertical())
} }
func (element *ProgressBar) redo () {
if element.core.HasImage() {
element.draw()
element.core.DamageAll()
}
}
func (element *ProgressBar) draw () {
bounds := element.Bounds()
pattern := element.theme.Pattern(tomo.PatternSunken, tomo.State { })
padding := element.theme.Padding(tomo.PatternSunken)
pattern.Draw(element.core, 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.Draw(element.core, meterBounds)
}

View File

@ -3,6 +3,7 @@ package elements
import "image" import "image"
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/canvas"
import "git.tebibyte.media/sashakoshka/tomo/default/theme" import "git.tebibyte.media/sashakoshka/tomo/default/theme"
import "git.tebibyte.media/sashakoshka/tomo/default/config" import "git.tebibyte.media/sashakoshka/tomo/default/config"
@ -18,6 +19,8 @@ import "git.tebibyte.media/sashakoshka/tomo/default/config"
// Typically, you wont't want to use a ScrollBar by itself. A ScrollContainer is // Typically, you wont't want to use a ScrollBar by itself. A ScrollContainer is
// better for most cases. // better for most cases.
type ScrollBar struct { type ScrollBar struct {
entity tomo.ContainerEntity
vertical bool vertical bool
enabled bool enabled bool
dragging bool dragging bool
@ -46,16 +49,31 @@ func NewScrollBar (vertical bool) (element *ScrollBar) {
} else { } else {
element.theme.Case = tomo.C("tomo", "scrollBarVertical") element.theme.Case = tomo.C("tomo", "scrollBarVertical")
} }
element.Core, element.core = core.NewCore(element, element.handleResize) element.entity = tomo.NewEntity(element).(tomo.ContainerEntity)
element.updateMinimumSize() element.updateMinimumSize()
return return
} }
func (element *ScrollBar) handleResize () { // Entity returns this element's entity.
if element.core.HasImage() { func (element *ScrollBar) Entity () tomo.Entity {
element.recalculate() return element.entity
element.draw() }
// Draw causes the element to draw to the specified destination canvas.
func (element *ScrollBar) Draw (destination canvas.Canvas) {
element.recalculate()
bounds := element.entity.Bounds()
state := tomo.State {
Disabled: !element.Enabled(),
Pressed: element.dragging,
} }
element.theme.Pattern(tomo.PatternGutter, state).Draw (
destination,
bounds)
element.theme.Pattern(tomo.PatternHandle, state).Draw (
destination,
element.bar)
} }
func (element *ScrollBar) HandleMouseDown (x, y int, button input.Button) { func (element *ScrollBar) HandleMouseDown (x, y int, button input.Button) {
@ -65,10 +83,10 @@ func (element *ScrollBar) HandleMouseDown (x, y int, button input.Button) {
if point.In(element.bar) { if point.In(element.bar) {
// the mouse is pressed down within the bar's handle // the mouse is pressed down within the bar's handle
element.dragging = true element.dragging = true
element.drawAndPush() element.entity.Invalidate()
element.dragOffset = element.dragOffset =
point.Sub(element.bar.Min). point.Sub(element.bar.Min).
Add(element.Bounds().Min) Add(element.entity.Bounds().Min)
element.dragTo(point) element.dragTo(point)
} else { } else {
// the mouse is pressed down within the bar's gutter // the mouse is pressed down within the bar's gutter
@ -108,7 +126,7 @@ func (element *ScrollBar) HandleMouseDown (x, y int, button input.Button) {
func (element *ScrollBar) HandleMouseUp (x, y int, button input.Button) { func (element *ScrollBar) HandleMouseUp (x, y int, button input.Button) {
if element.dragging { if element.dragging {
element.dragging = false element.dragging = false
element.drawAndPush() element.entity.Invalidate()
} }
} }
@ -130,7 +148,7 @@ func (element *ScrollBar) HandleScroll (x, y int, deltaX, deltaY float64) {
func (element *ScrollBar) SetEnabled (enabled bool) { func (element *ScrollBar) SetEnabled (enabled bool) {
if element.enabled == enabled { return } if element.enabled == enabled { return }
element.enabled = enabled element.enabled = enabled
element.drawAndPush() element.entity.Invalidate()
} }
// Enabled returns whether or not the element is enabled. // Enabled returns whether or not the element is enabled.
@ -142,8 +160,7 @@ func (element *ScrollBar) Enabled () (enabled bool) {
func (element *ScrollBar) SetBounds (content, viewport image.Rectangle) { func (element *ScrollBar) SetBounds (content, viewport image.Rectangle) {
element.contentBounds = content element.contentBounds = content
element.viewportBounds = viewport element.viewportBounds = viewport
element.recalculate() element.entity.Invalidate()
element.drawAndPush()
} }
// OnScroll sets a function to be called when the user tries to move the scroll // OnScroll sets a function to be called when the user tries to move the scroll
@ -159,7 +176,7 @@ func (element *ScrollBar) OnScroll (callback func (viewport image.Point)) {
func (element *ScrollBar) SetTheme (new tomo.Theme) { func (element *ScrollBar) SetTheme (new tomo.Theme) {
if new == element.theme.Theme { return } if new == element.theme.Theme { return }
element.theme.Theme = new element.theme.Theme = new
element.drawAndPush() element.entity.Invalidate()
} }
// SetConfig sets the element's configuration. // SetConfig sets the element's configuration.
@ -167,7 +184,7 @@ func (element *ScrollBar) SetConfig (new tomo.Config) {
if new == element.config.Config { return } if new == element.config.Config { return }
element.config.Config = new element.config.Config = new
element.updateMinimumSize() element.updateMinimumSize()
element.drawAndPush() element.entity.Invalidate()
} }
func (element *ScrollBar) isAfterHandle (point image.Point) bool { func (element *ScrollBar) isAfterHandle (point image.Point) bool {
@ -180,10 +197,10 @@ func (element *ScrollBar) isAfterHandle (point image.Point) bool {
func (element *ScrollBar) fallbackDragOffset () image.Point { func (element *ScrollBar) fallbackDragOffset () image.Point {
if element.vertical { if element.vertical {
return element.Bounds().Min. return element.entity.Bounds().Min.
Add(image.Pt(0, element.bar.Dy() / 2)) Add(image.Pt(0, element.bar.Dy() / 2))
} else { } else {
return element.Bounds().Min. return element.entity.Bounds().Min.
Add(image.Pt(element.bar.Dx() / 2, 0)) Add(image.Pt(element.bar.Dx() / 2, 0))
} }
} }
@ -232,7 +249,7 @@ func (element *ScrollBar) recalculate () {
} }
func (element *ScrollBar) recalculateVertical () { func (element *ScrollBar) recalculateVertical () {
bounds := element.Bounds() bounds := element.entity.Bounds()
padding := element.theme.Padding(tomo.PatternGutter) padding := element.theme.Padding(tomo.PatternGutter)
element.track = padding.Apply(bounds) element.track = padding.Apply(bounds)
@ -259,7 +276,7 @@ func (element *ScrollBar) recalculateVertical () {
} }
func (element *ScrollBar) recalculateHorizontal () { func (element *ScrollBar) recalculateHorizontal () {
bounds := element.Bounds() bounds := element.entity.Bounds()
padding := element.theme.Padding(tomo.PatternGutter) padding := element.theme.Padding(tomo.PatternGutter)
element.track = padding.Apply(bounds) element.track = padding.Apply(bounds)
@ -289,33 +306,12 @@ func (element *ScrollBar) updateMinimumSize () {
gutterPadding := element.theme.Padding(tomo.PatternGutter) gutterPadding := element.theme.Padding(tomo.PatternGutter)
handlePadding := element.theme.Padding(tomo.PatternHandle) handlePadding := element.theme.Padding(tomo.PatternHandle)
if element.vertical { if element.vertical {
element.core.SetMinimumSize ( element.entity.SetMinimumSize (
gutterPadding.Horizontal() + handlePadding.Horizontal(), gutterPadding.Horizontal() + handlePadding.Horizontal(),
gutterPadding.Vertical() + handlePadding.Vertical() * 2) gutterPadding.Vertical() + handlePadding.Vertical() * 2)
} else { } else {
element.core.SetMinimumSize ( element.entity.SetMinimumSize (
gutterPadding.Horizontal() + handlePadding.Horizontal() * 2, gutterPadding.Horizontal() + handlePadding.Horizontal() * 2,
gutterPadding.Vertical() + handlePadding.Vertical()) gutterPadding.Vertical() + handlePadding.Vertical())
} }
} }
func (element *ScrollBar) drawAndPush () {
if element.core.HasImage () {
element.draw()
element.core.DamageAll()
}
}
func (element *ScrollBar) draw () {
bounds := element.Bounds()
state := tomo.State {
Disabled: !element.Enabled(),
Pressed: element.dragging,
}
element.theme.Pattern(tomo.PatternGutter, state).Draw (
element.core,
bounds)
element.theme.Pattern(tomo.PatternHandle, state).Draw (
element.core,
element.bar)
}

View File

@ -3,17 +3,21 @@ package elements
import "image" import "image"
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/canvas"
import "git.tebibyte.media/sashakoshka/tomo/default/theme" import "git.tebibyte.media/sashakoshka/tomo/default/theme"
import "git.tebibyte.media/sashakoshka/tomo/default/config" import "git.tebibyte.media/sashakoshka/tomo/default/config"
// Slider is a slider control with a floating point value between zero and one. // Slider is a slider control with a floating point value between zero and one.
type Slider struct { type Slider struct {
value float64 entity tomo.FocusableEntity
vertical bool
dragging bool value float64
vertical bool
dragging bool
enabled bool
dragOffset int dragOffset int
track image.Rectangle track image.Rectangle
bar image.Rectangle bar image.Rectangle
config config.Wrapped config config.Wrapped
theme theme.Wrapped theme theme.Wrapped
@ -34,13 +38,62 @@ func NewSlider (value float64, vertical bool) (element *Slider) {
} else { } else {
element.theme.Case = tomo.C("tomo", "sliderHorizontal") element.theme.Case = tomo.C("tomo", "sliderHorizontal")
} }
element.Core, element.core = core.NewCore(element, element.draw) element.entity = tomo.NewEntity(element).(tomo.FocusableEntity)
element.FocusableCore,
element.focusableControl = core.NewFocusableCore(element.core, element.redo)
element.updateMinimumSize() element.updateMinimumSize()
return return
} }
// Entity returns this element's entity.
func (element *Slider) Entity () tomo.Entity {
return element.entity
}
// Draw causes the element to draw to the specified destination canvas.
func (element *Slider) Draw (destination canvas.Canvas) {
bounds := element.entity.Bounds()
element.track = element.theme.Padding(tomo.PatternGutter).Apply(bounds)
if element.vertical {
barSize := element.track.Dx()
element.bar = image.Rect(0, 0, barSize, barSize).Add(bounds.Min)
barOffset :=
float64(element.track.Dy() - barSize) *
(1 - element.value)
element.bar = element.bar.Add(image.Pt(0, int(barOffset)))
} else {
barSize := element.track.Dy()
element.bar = image.Rect(0, 0, barSize, barSize).Add(bounds.Min)
barOffset :=
float64(element.track.Dx() - barSize) *
element.value
element.bar = element.bar.Add(image.Pt(int(barOffset), 0))
}
state := tomo.State {
Disabled: !element.Enabled(),
Focused: element.entity.Focused(),
Pressed: element.dragging,
}
element.theme.Pattern(tomo.PatternGutter, state).Draw(destination, bounds)
element.theme.Pattern(tomo.PatternHandle, state).Draw(destination, bounds)
}
// Focus gives this element input focus.
func (element *Slider) Focus () {
if !element.entity.Focused() { element.entity.Focus() }
}
// Enabled returns whether this slider can be dragged or not.
func (element *Slider) Enabled () bool {
return element.enabled
}
// SetEnabled sets whether this slider can be dragged or not.
func (element *Slider) SetEnabled (enabled bool) {
if element.enabled == enabled { return }
element.enabled = enabled
element.entity.Invalidate()
}
func (element *Slider) HandleMouseDown (x, y int, button input.Button) { func (element *Slider) HandleMouseDown (x, y int, button input.Button) {
if !element.Enabled() { return } if !element.Enabled() { return }
element.Focus() element.Focus()
@ -50,7 +103,7 @@ func (element *Slider) HandleMouseDown (x, y int, button input.Button) {
if element.onSlide != nil { if element.onSlide != nil {
element.onSlide() element.onSlide()
} }
element.redo() element.entity.Invalidate()
} }
} }
@ -60,7 +113,7 @@ func (element *Slider) HandleMouseUp (x, y int, button input.Button) {
if element.onRelease != nil { if element.onRelease != nil {
element.onRelease() element.onRelease()
} }
element.redo() element.entity.Invalidate()
} }
func (element *Slider) HandleMotion (x, y int) { func (element *Slider) HandleMotion (x, y int) {
@ -70,7 +123,7 @@ func (element *Slider) HandleMotion (x, y int) {
if element.onSlide != nil { if element.onSlide != nil {
element.onSlide() element.onSlide()
} }
element.redo() element.entity.Invalidate()
} }
} }
@ -104,11 +157,6 @@ func (element *Slider) Value () (value float64) {
return element.value return element.value
} }
// SetEnabled sets whether or not the slider can be interacted with.
func (element *Slider) SetEnabled (enabled bool) {
element.focusableControl.SetEnabled(enabled)
}
// SetValue sets the slider's value. // SetValue sets the slider's value.
func (element *Slider) SetValue (value float64) { func (element *Slider) SetValue (value float64) {
if value < 0 { value = 0 } if value < 0 { value = 0 }
@ -120,7 +168,7 @@ func (element *Slider) SetValue (value float64) {
if element.onRelease != nil { if element.onRelease != nil {
element.onRelease() element.onRelease()
} }
element.redo() element.entity.Invalidate()
} }
// OnSlide sets a function to be called every time the slider handle changes // OnSlide sets a function to be called every time the slider handle changes
@ -138,7 +186,7 @@ func (element *Slider) OnRelease (callback func ()) {
func (element *Slider) SetTheme (new tomo.Theme) { func (element *Slider) SetTheme (new tomo.Theme) {
if new == element.theme.Theme { return } if new == element.theme.Theme { return }
element.theme.Theme = new element.theme.Theme = new
element.redo() element.entity.Invalidate()
} }
// SetConfig sets the element's configuration. // SetConfig sets the element's configuration.
@ -146,7 +194,7 @@ func (element *Slider) SetConfig (new tomo.Config) {
if new == element.config.Config { return } if new == element.config.Config { return }
element.config.Config = new element.config.Config = new
element.updateMinimumSize() element.updateMinimumSize()
element.redo() element.entity.Invalidate()
} }
func (element *Slider) changeValue (delta float64) { func (element *Slider) changeValue (delta float64) {
@ -160,7 +208,7 @@ func (element *Slider) changeValue (delta float64) {
if element.onRelease != nil { if element.onRelease != nil {
element.onRelease() element.onRelease()
} }
element.redo() element.entity.Invalidate()
} }
func (element *Slider) valueFor (x, y int) (value float64) { func (element *Slider) valueFor (x, y int) (value float64) {
@ -184,51 +232,12 @@ func (element *Slider) updateMinimumSize () {
gutterPadding := element.theme.Padding(tomo.PatternGutter) gutterPadding := element.theme.Padding(tomo.PatternGutter)
handlePadding := element.theme.Padding(tomo.PatternHandle) handlePadding := element.theme.Padding(tomo.PatternHandle)
if element.vertical { if element.vertical {
element.core.SetMinimumSize ( element.entity.SetMinimumSize (
gutterPadding.Horizontal() + handlePadding.Horizontal(), gutterPadding.Horizontal() + handlePadding.Horizontal(),
gutterPadding.Vertical() + handlePadding.Vertical() * 2) gutterPadding.Vertical() + handlePadding.Vertical() * 2)
} else { } else {
element.core.SetMinimumSize ( element.entity.SetMinimumSize (
gutterPadding.Horizontal() + handlePadding.Horizontal() * 2, gutterPadding.Horizontal() + handlePadding.Horizontal() * 2,
gutterPadding.Vertical() + handlePadding.Vertical()) gutterPadding.Vertical() + handlePadding.Vertical())
} }
} }
func (element *Slider) redo () {
if element.core.HasImage () {
element.draw()
element.core.DamageAll()
}
}
func (element *Slider) draw () {
bounds := element.Bounds()
element.track = element.theme.Padding(tomo.PatternGutter).Apply(bounds)
if element.vertical {
barSize := element.track.Dx()
element.bar = image.Rect(0, 0, barSize, barSize).Add(bounds.Min)
barOffset :=
float64(element.track.Dy() - barSize) *
(1 - element.value)
element.bar = element.bar.Add(image.Pt(0, int(barOffset)))
} else {
barSize := element.track.Dy()
element.bar = image.Rect(0, 0, barSize, barSize).Add(bounds.Min)
barOffset :=
float64(element.track.Dx() - barSize) *
element.value
element.bar = element.bar.Add(image.Pt(int(barOffset), 0))
}
state := tomo.State {
Focused: element.Focused(),
Disabled: !element.Enabled(),
Pressed: element.dragging,
}
element.theme.Pattern(tomo.PatternGutter, state).Draw (
element.core,
bounds)
element.theme.Pattern(tomo.PatternHandle, state).Draw (
element.core,
element.bar)
}

View File

@ -1,11 +1,14 @@
package elements package elements
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/canvas"
import "git.tebibyte.media/sashakoshka/tomo/default/theme" import "git.tebibyte.media/sashakoshka/tomo/default/theme"
import "git.tebibyte.media/sashakoshka/tomo/default/config" import "git.tebibyte.media/sashakoshka/tomo/default/config"
// Spacer can be used to put space between two elements.. // Spacer can be used to put space between two elements..
type Spacer struct { type Spacer struct {
entity tomo.Entity
line bool line bool
config config.Wrapped config config.Wrapped
@ -17,67 +20,63 @@ type Spacer struct {
// will appear as a line. // will appear as a line.
func NewSpacer (line bool) (element *Spacer) { func NewSpacer (line bool) (element *Spacer) {
element = &Spacer { line: line } element = &Spacer { line: line }
element.entity = tomo.NewEntity(element)
element.theme.Case = tomo.C("tomo", "spacer") element.theme.Case = tomo.C("tomo", "spacer")
element.Core, element.core = core.NewCore(element, element.draw)
element.updateMinimumSize() element.updateMinimumSize()
return return
} }
// Entity returns this element's entity.
func (element *Spacer) Entity () tomo.Entity {
return element.entity
}
// Draw causes the element to draw to the specified destination canvas.
func (element *Spacer) Draw (destination canvas.Canvas) {
bounds := element.entity.Bounds()
if element.line {
pattern := element.theme.Pattern (
tomo.PatternLine,
tomo.State { })
pattern.Draw(destination, bounds)
} else {
pattern := element.theme.Pattern (
tomo.PatternBackground,
tomo.State { })
pattern.Draw(destination, bounds)
}
}
/// SetLine sets whether or not the spacer will appear as a colored line. /// SetLine sets whether or not the spacer will appear as a colored line.
func (element *Spacer) SetLine (line bool) { func (element *Spacer) SetLine (line bool) {
if element.line == line { return } if element.line == line { return }
element.line = line element.line = line
element.updateMinimumSize() element.updateMinimumSize()
if element.core.HasImage() { element.entity.Invalidate()
element.draw()
element.core.DamageAll()
}
} }
// SetTheme sets the element's theme. // SetTheme sets the element's theme.
func (element *Spacer) SetTheme (new tomo.Theme) { func (element *Spacer) SetTheme (new tomo.Theme) {
if new == element.theme.Theme { return } if new == element.theme.Theme { return }
element.theme.Theme = new element.theme.Theme = new
element.redo() element.entity.Invalidate()
} }
// SetConfig sets the element's configuration. // SetConfig sets the element's configuration.
func (element *Spacer) SetConfig (new tomo.Config) { func (element *Spacer) SetConfig (new tomo.Config) {
if new == element.config.Config { return } if new == element.config.Config { return }
element.config.Config = new element.config.Config = new
element.redo() element.entity.Invalidate()
} }
func (element *Spacer) updateMinimumSize () { func (element *Spacer) updateMinimumSize () {
if element.line { if element.line {
padding := element.theme.Padding(tomo.PatternLine) padding := element.theme.Padding(tomo.PatternLine)
element.core.SetMinimumSize ( element.entity.SetMinimumSize (
padding.Horizontal(), padding.Horizontal(),
padding.Vertical()) padding.Vertical())
} else { } else {
element.core.SetMinimumSize(1, 1) element.entity.SetMinimumSize(1, 1)
}
}
func (element *Spacer) redo () {
if !element.core.HasImage() {
element.draw()
element.core.DamageAll()
}
}
func (element *Spacer) draw () {
bounds := element.Bounds()
if element.line {
pattern := element.theme.Pattern (
tomo.PatternLine,
tomo.State { })
pattern.Draw(element.core, bounds)
} else {
pattern := element.theme.Pattern (
tomo.PatternBackground,
tomo.State { })
pattern.Draw(element.core, bounds)
} }
} }