Migrated some more elements
This commit is contained in:
		
							parent
							
								
									a43f5ce595
								
							
						
					
					
						commit
						ca86328506
					
				@ -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)
 | 
			
		||||
}
 | 
			
		||||
@ -2,11 +2,13 @@ package elements
 | 
			
		||||
 | 
			
		||||
import "image"
 | 
			
		||||
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/config"
 | 
			
		||||
 | 
			
		||||
// ProgressBar displays a visual indication of how far along a task is.
 | 
			
		||||
type ProgressBar struct {
 | 
			
		||||
	entity tomo.Entity
 | 
			
		||||
	progress float64
 | 
			
		||||
	
 | 
			
		||||
	config config.Wrapped
 | 
			
		||||
@ -17,20 +19,38 @@ type ProgressBar struct {
 | 
			
		||||
// level.
 | 
			
		||||
func NewProgressBar (progress float64) (element *ProgressBar) {
 | 
			
		||||
	element = &ProgressBar { progress: progress }
 | 
			
		||||
	element.entity = tomo.NewEntity(element)
 | 
			
		||||
	element.theme.Case = tomo.C("tomo", "progressBar")
 | 
			
		||||
	element.Core, element.core = core.NewCore(element, element.draw)
 | 
			
		||||
	element.updateMinimumSize()
 | 
			
		||||
	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.
 | 
			
		||||
func (element *ProgressBar) SetProgress (progress float64) {
 | 
			
		||||
	if progress == element.progress { return }
 | 
			
		||||
	element.progress = progress
 | 
			
		||||
	if element.core.HasImage() {
 | 
			
		||||
		element.draw()
 | 
			
		||||
		element.core.DamageAll()
 | 
			
		||||
	}
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetTheme sets the element's theme.
 | 
			
		||||
@ -38,7 +58,7 @@ func (element *ProgressBar) SetTheme (new tomo.Theme) {
 | 
			
		||||
	if new == element.theme.Theme { return }
 | 
			
		||||
	element.theme.Theme = new
 | 
			
		||||
	element.updateMinimumSize()
 | 
			
		||||
	element.redo()
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 }
 | 
			
		||||
	element.config.Config = new
 | 
			
		||||
	element.updateMinimumSize()
 | 
			
		||||
	element.redo()
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *ProgressBar) updateMinimumSize() {
 | 
			
		||||
	padding      := element.theme.Padding(tomo.PatternSunken)
 | 
			
		||||
	innerPadding := element.theme.Padding(tomo.PatternMercury)
 | 
			
		||||
	element.core.SetMinimumSize (
 | 
			
		||||
	element.entity.SetMinimumSize (
 | 
			
		||||
		padding.Horizontal() + innerPadding.Horizontal(),
 | 
			
		||||
		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)
 | 
			
		||||
}
 | 
			
		||||
@ -3,6 +3,7 @@ package elements
 | 
			
		||||
import "image"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo"
 | 
			
		||||
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/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
 | 
			
		||||
// better for most cases.
 | 
			
		||||
type ScrollBar struct {
 | 
			
		||||
	entity tomo.ContainerEntity
 | 
			
		||||
 | 
			
		||||
	vertical bool
 | 
			
		||||
	enabled  bool
 | 
			
		||||
	dragging bool
 | 
			
		||||
@ -46,16 +49,31 @@ func NewScrollBar (vertical bool) (element *ScrollBar) {
 | 
			
		||||
	} else {
 | 
			
		||||
		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()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *ScrollBar) handleResize () {
 | 
			
		||||
	if element.core.HasImage() {
 | 
			
		||||
		element.recalculate()
 | 
			
		||||
		element.draw()
 | 
			
		||||
// Entity returns this element's entity.
 | 
			
		||||
func (element *ScrollBar) Entity () tomo.Entity {
 | 
			
		||||
	return element.entity
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
@ -65,10 +83,10 @@ func (element *ScrollBar) HandleMouseDown (x, y int, button input.Button) {
 | 
			
		||||
	if point.In(element.bar) {
 | 
			
		||||
		// the mouse is pressed down within the bar's handle
 | 
			
		||||
		element.dragging   = true
 | 
			
		||||
		element.drawAndPush()
 | 
			
		||||
		element.entity.Invalidate()
 | 
			
		||||
		element.dragOffset =
 | 
			
		||||
			point.Sub(element.bar.Min).
 | 
			
		||||
			Add(element.Bounds().Min)
 | 
			
		||||
			Add(element.entity.Bounds().Min)
 | 
			
		||||
		element.dragTo(point)
 | 
			
		||||
	} else {
 | 
			
		||||
		// 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) {
 | 
			
		||||
	if element.dragging {
 | 
			
		||||
		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) {
 | 
			
		||||
	if element.enabled == enabled { return }
 | 
			
		||||
	element.enabled = enabled
 | 
			
		||||
	element.drawAndPush()
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
	element.contentBounds  = content
 | 
			
		||||
	element.viewportBounds = viewport
 | 
			
		||||
	element.recalculate()
 | 
			
		||||
	element.drawAndPush()
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
	if new == element.theme.Theme { return }
 | 
			
		||||
	element.theme.Theme = new
 | 
			
		||||
	element.drawAndPush()
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetConfig sets the element's configuration.
 | 
			
		||||
@ -167,7 +184,7 @@ func (element *ScrollBar) SetConfig (new tomo.Config) {
 | 
			
		||||
	if new == element.config.Config { return }
 | 
			
		||||
	element.config.Config = new
 | 
			
		||||
	element.updateMinimumSize()
 | 
			
		||||
	element.drawAndPush()
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
	if element.vertical {
 | 
			
		||||
		return element.Bounds().Min.
 | 
			
		||||
		return element.entity.Bounds().Min.
 | 
			
		||||
			Add(image.Pt(0, element.bar.Dy() / 2))
 | 
			
		||||
	} else {
 | 
			
		||||
		return element.Bounds().Min.
 | 
			
		||||
		return element.entity.Bounds().Min.
 | 
			
		||||
			Add(image.Pt(element.bar.Dx() / 2, 0))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -232,7 +249,7 @@ func (element *ScrollBar) recalculate () {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *ScrollBar) recalculateVertical () {
 | 
			
		||||
	bounds := element.Bounds()
 | 
			
		||||
	bounds := element.entity.Bounds()
 | 
			
		||||
	padding := element.theme.Padding(tomo.PatternGutter)
 | 
			
		||||
	element.track = padding.Apply(bounds)
 | 
			
		||||
 | 
			
		||||
@ -259,7 +276,7 @@ func (element *ScrollBar) recalculateVertical () {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *ScrollBar) recalculateHorizontal () {
 | 
			
		||||
	bounds := element.Bounds()
 | 
			
		||||
	bounds := element.entity.Bounds()
 | 
			
		||||
	padding := element.theme.Padding(tomo.PatternGutter)
 | 
			
		||||
	element.track = padding.Apply(bounds)
 | 
			
		||||
 | 
			
		||||
@ -289,33 +306,12 @@ func (element *ScrollBar) updateMinimumSize () {
 | 
			
		||||
	gutterPadding := element.theme.Padding(tomo.PatternGutter)
 | 
			
		||||
	handlePadding := element.theme.Padding(tomo.PatternHandle)
 | 
			
		||||
	if element.vertical {
 | 
			
		||||
		element.core.SetMinimumSize (
 | 
			
		||||
		element.entity.SetMinimumSize (
 | 
			
		||||
			gutterPadding.Horizontal() + handlePadding.Horizontal(),
 | 
			
		||||
			gutterPadding.Vertical()   + handlePadding.Vertical() * 2)
 | 
			
		||||
	} else {
 | 
			
		||||
		element.core.SetMinimumSize (
 | 
			
		||||
		element.entity.SetMinimumSize (
 | 
			
		||||
			gutterPadding.Horizontal() + handlePadding.Horizontal() * 2,
 | 
			
		||||
			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)
 | 
			
		||||
}
 | 
			
		||||
@ -3,17 +3,21 @@ package elements
 | 
			
		||||
import "image"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo"
 | 
			
		||||
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/config"
 | 
			
		||||
 | 
			
		||||
// Slider is a slider control with a floating point value between zero and one.
 | 
			
		||||
type Slider struct {
 | 
			
		||||
	value   float64
 | 
			
		||||
	vertical bool
 | 
			
		||||
	dragging bool
 | 
			
		||||
	entity tomo.FocusableEntity
 | 
			
		||||
	
 | 
			
		||||
	value      float64
 | 
			
		||||
	vertical   bool
 | 
			
		||||
	dragging   bool
 | 
			
		||||
	enabled    bool
 | 
			
		||||
	dragOffset int
 | 
			
		||||
	track image.Rectangle
 | 
			
		||||
	bar image.Rectangle
 | 
			
		||||
	track      image.Rectangle
 | 
			
		||||
	bar        image.Rectangle
 | 
			
		||||
	
 | 
			
		||||
	config config.Wrapped
 | 
			
		||||
	theme  theme.Wrapped
 | 
			
		||||
@ -34,13 +38,62 @@ func NewSlider (value float64, vertical bool) (element *Slider) {
 | 
			
		||||
	} else {
 | 
			
		||||
		element.theme.Case = tomo.C("tomo", "sliderHorizontal")
 | 
			
		||||
	}
 | 
			
		||||
	element.Core, element.core = core.NewCore(element, element.draw)
 | 
			
		||||
	element.FocusableCore,
 | 
			
		||||
	element.focusableControl = core.NewFocusableCore(element.core, element.redo)
 | 
			
		||||
	element.entity = tomo.NewEntity(element).(tomo.FocusableEntity)
 | 
			
		||||
	element.updateMinimumSize()
 | 
			
		||||
	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) {
 | 
			
		||||
	if !element.Enabled() { return }
 | 
			
		||||
	element.Focus()
 | 
			
		||||
@ -50,7 +103,7 @@ func (element *Slider) HandleMouseDown (x, y int, button input.Button) {
 | 
			
		||||
		if element.onSlide != nil {
 | 
			
		||||
			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 {
 | 
			
		||||
		element.onRelease()
 | 
			
		||||
	}
 | 
			
		||||
	element.redo()
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *Slider) HandleMotion (x, y int) {
 | 
			
		||||
@ -70,7 +123,7 @@ func (element *Slider) HandleMotion (x, y int) {
 | 
			
		||||
		if element.onSlide != nil {
 | 
			
		||||
			element.onSlide()
 | 
			
		||||
		}
 | 
			
		||||
		element.redo()
 | 
			
		||||
		element.entity.Invalidate()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -104,11 +157,6 @@ func (element *Slider) Value () (value float64) {
 | 
			
		||||
	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.
 | 
			
		||||
func (element *Slider) SetValue (value float64) {
 | 
			
		||||
	if value < 0 { value = 0 }
 | 
			
		||||
@ -120,7 +168,7 @@ func (element *Slider) SetValue (value float64) {
 | 
			
		||||
	if element.onRelease != nil {
 | 
			
		||||
		element.onRelease()
 | 
			
		||||
	}
 | 
			
		||||
	element.redo()
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
	if new == element.theme.Theme { return }
 | 
			
		||||
	element.theme.Theme = new
 | 
			
		||||
	element.redo()
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetConfig sets the element's configuration.
 | 
			
		||||
@ -146,7 +194,7 @@ func (element *Slider) SetConfig (new tomo.Config) {
 | 
			
		||||
	if new == element.config.Config { return }
 | 
			
		||||
	element.config.Config = new
 | 
			
		||||
	element.updateMinimumSize()
 | 
			
		||||
	element.redo()
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *Slider) changeValue (delta float64) {
 | 
			
		||||
@ -160,7 +208,7 @@ func (element *Slider) changeValue (delta float64) {
 | 
			
		||||
	if element.onRelease != nil {
 | 
			
		||||
		element.onRelease()
 | 
			
		||||
	}
 | 
			
		||||
	element.redo()
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *Slider) valueFor (x, y int) (value float64) {
 | 
			
		||||
@ -184,51 +232,12 @@ func (element *Slider) updateMinimumSize () {
 | 
			
		||||
	gutterPadding := element.theme.Padding(tomo.PatternGutter)
 | 
			
		||||
	handlePadding := element.theme.Padding(tomo.PatternHandle)
 | 
			
		||||
	if element.vertical {
 | 
			
		||||
		element.core.SetMinimumSize (
 | 
			
		||||
		element.entity.SetMinimumSize (
 | 
			
		||||
			gutterPadding.Horizontal() + handlePadding.Horizontal(),
 | 
			
		||||
			gutterPadding.Vertical()   + handlePadding.Vertical() * 2)
 | 
			
		||||
	} else {
 | 
			
		||||
		element.core.SetMinimumSize (
 | 
			
		||||
		element.entity.SetMinimumSize (
 | 
			
		||||
			gutterPadding.Horizontal() + handlePadding.Horizontal() * 2,
 | 
			
		||||
			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)
 | 
			
		||||
}
 | 
			
		||||
@ -1,11 +1,14 @@
 | 
			
		||||
package elements
 | 
			
		||||
 | 
			
		||||
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/config"
 | 
			
		||||
 | 
			
		||||
// Spacer can be used to put space between two elements..
 | 
			
		||||
type Spacer struct {
 | 
			
		||||
	entity tomo.Entity
 | 
			
		||||
	
 | 
			
		||||
	line bool
 | 
			
		||||
	
 | 
			
		||||
	config config.Wrapped
 | 
			
		||||
@ -17,67 +20,63 @@ type Spacer struct {
 | 
			
		||||
// will appear as a line.
 | 
			
		||||
func NewSpacer (line bool) (element *Spacer) {
 | 
			
		||||
	element = &Spacer { line: line }
 | 
			
		||||
	element.entity = tomo.NewEntity(element)
 | 
			
		||||
	element.theme.Case = tomo.C("tomo", "spacer")
 | 
			
		||||
	element.Core, element.core = core.NewCore(element, element.draw)
 | 
			
		||||
	element.updateMinimumSize()
 | 
			
		||||
	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.
 | 
			
		||||
func (element *Spacer) SetLine (line bool) {
 | 
			
		||||
	if element.line == line { return }
 | 
			
		||||
	element.line = line
 | 
			
		||||
	element.updateMinimumSize()
 | 
			
		||||
	if element.core.HasImage() {
 | 
			
		||||
		element.draw()
 | 
			
		||||
		element.core.DamageAll()
 | 
			
		||||
	}
 | 
			
		||||
	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.redo()
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetConfig sets the element's configuration.
 | 
			
		||||
func (element *Spacer) SetConfig (new tomo.Config) {
 | 
			
		||||
	if new == element.config.Config { return }
 | 
			
		||||
	element.config.Config = new
 | 
			
		||||
	element.redo()
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *Spacer) updateMinimumSize () {
 | 
			
		||||
	if element.line {
 | 
			
		||||
		padding := element.theme.Padding(tomo.PatternLine)
 | 
			
		||||
		element.core.SetMinimumSize (
 | 
			
		||||
		element.entity.SetMinimumSize (
 | 
			
		||||
			padding.Horizontal(),
 | 
			
		||||
			padding.Vertical())
 | 
			
		||||
	} else {
 | 
			
		||||
		element.core.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)
 | 
			
		||||
		element.entity.SetMinimumSize(1, 1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user