Redid the entity system a bit to make it more reliable
Now it supports things like parenting elements before they are added to a window and elements no longer have to constantly check for a nil entity
This commit is contained in:
parent
5cf0b162c0
commit
437aef0c27
@ -17,6 +17,9 @@ type Backend interface {
|
|||||||
// possible. This method must be safe to call from other threads.
|
// possible. This method must be safe to call from other threads.
|
||||||
Do (callback func ())
|
Do (callback func ())
|
||||||
|
|
||||||
|
// NewEntity creates a new entity for the specified element.
|
||||||
|
NewEntity (owner Element) Entity
|
||||||
|
|
||||||
// NewWindow creates a new window within the specified bounding
|
// NewWindow creates a new window within the specified bounding
|
||||||
// rectangle. The position on screen may be overridden by the backend or
|
// rectangle. The position on screen may be overridden by the backend or
|
||||||
// operating system.
|
// operating system.
|
||||||
|
@ -19,29 +19,46 @@ type entity struct {
|
|||||||
isContainer bool
|
isContainer bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func bind (parent *entity, window *window, element tomo.Element) *entity {
|
func (backend *Backend) NewEntity (owner tomo.Element) tomo.Entity {
|
||||||
entity := &entity {
|
entity := &entity { element: owner }
|
||||||
window: window,
|
if _, ok := owner.(tomo.Container); ok {
|
||||||
parent: parent,
|
|
||||||
element: element,
|
|
||||||
}
|
|
||||||
entity.Invalidate()
|
|
||||||
if _, ok := element.(tomo.Container); ok {
|
|
||||||
entity.isContainer = true
|
entity.isContainer = true
|
||||||
entity.InvalidateLayout()
|
entity.InvalidateLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
element.Bind(entity)
|
|
||||||
return entity
|
return entity
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entity *entity) unbind () {
|
func (ent *entity) unlink () {
|
||||||
entity.element.Bind(nil)
|
ent.propagate (func (child *entity) bool {
|
||||||
for _, childEntity := range entity.children {
|
child.window = nil
|
||||||
childEntity.unbind()
|
delete(ent.window.system.drawingInvalid, child)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
delete(ent.window.system.drawingInvalid, ent)
|
||||||
|
ent.parent = nil
|
||||||
|
ent.window = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entity *entity) link (parent *entity) {
|
||||||
|
entity.parent = parent
|
||||||
|
if parent.window != nil {
|
||||||
|
entity.setWindow(parent.window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ent *entity) setWindow (window *window) {
|
||||||
|
ent.window = window
|
||||||
|
ent.Invalidate()
|
||||||
|
ent.InvalidateLayout()
|
||||||
|
ent.propagate (func (child *entity) bool {
|
||||||
|
child.window = window
|
||||||
|
ent.Invalidate()
|
||||||
|
ent.InvalidateLayout()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (entity *entity) propagate (callback func (*entity) bool) {
|
func (entity *entity) propagate (callback func (*entity) bool) {
|
||||||
for _, child := range entity.children {
|
for _, child := range entity.children {
|
||||||
if callback(child) { break }
|
if callback(child) { break }
|
||||||
@ -61,6 +78,7 @@ func (entity *entity) childAt (point image.Point) *entity {
|
|||||||
// ----------- Entity ----------- //
|
// ----------- Entity ----------- //
|
||||||
|
|
||||||
func (entity *entity) Invalidate () {
|
func (entity *entity) Invalidate () {
|
||||||
|
if entity.window == nil { return }
|
||||||
if entity.window.system.invalidateIgnore { return }
|
if entity.window.system.invalidateIgnore { return }
|
||||||
entity.window.drawingInvalid.Add(entity)
|
entity.window.drawingInvalid.Add(entity)
|
||||||
}
|
}
|
||||||
@ -77,39 +95,54 @@ func (entity *entity) SetMinimumSize (width, height int) {
|
|||||||
entity.minWidth = width
|
entity.minWidth = width
|
||||||
entity.minHeight = height
|
entity.minHeight = height
|
||||||
if entity.parent == nil {
|
if entity.parent == nil {
|
||||||
entity.window.setMinimumSize(width, height)
|
if entity.window != nil {
|
||||||
|
entity.window.setMinimumSize(width, height)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
entity.parent.element.(tomo.Container).
|
entity.parent.element.(tomo.Container).
|
||||||
HandleChildMinimumSizeChange(entity.element)
|
HandleChildMinimumSizeChange(entity.element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entity *entity) DrawBackground (destination canvas.Canvas, bounds image.Rectangle) {
|
func (entity *entity) DrawBackground (destination canvas.Canvas) {
|
||||||
if entity.parent == nil { return }
|
if entity.parent != nil {
|
||||||
entity.parent.element.(tomo.Container).DrawBackground(destination, bounds)
|
entity.parent.element.(tomo.Container).DrawBackground(destination)
|
||||||
|
} else if entity.window != nil {
|
||||||
|
entity.window.system.theme.Pattern (
|
||||||
|
tomo.PatternBackground,
|
||||||
|
tomo.State { }).Draw (
|
||||||
|
destination,
|
||||||
|
entity.window.canvas.Bounds())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------- ContainerEntity ----------- //
|
// ----------- ContainerEntity ----------- //
|
||||||
|
|
||||||
func (entity *entity) InvalidateLayout () {
|
func (entity *entity) InvalidateLayout () {
|
||||||
|
if entity.window == nil { return }
|
||||||
if !entity.isContainer { return }
|
if !entity.isContainer { return }
|
||||||
entity.layoutInvalid = true
|
entity.layoutInvalid = true
|
||||||
entity.window.system.anyLayoutInvalid = true
|
entity.window.system.anyLayoutInvalid = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entity *entity) Adopt (child tomo.Element) {
|
func (ent *entity) Adopt (child tomo.Element) {
|
||||||
entity.children = append(entity.children, bind(entity, entity.window, child))
|
childEntity, ok := child.Entity().(*entity)
|
||||||
|
if !ok || childEntity == nil { return }
|
||||||
|
childEntity.link(ent)
|
||||||
|
ent.children = append(ent.children, childEntity)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entity *entity) Insert (index int, child tomo.Element) {
|
func (ent *entity) Insert (index int, child tomo.Element) {
|
||||||
entity.children = append (
|
childEntity, ok := child.Entity().(*entity)
|
||||||
entity.children[:index + 1],
|
if !ok || childEntity == nil { return }
|
||||||
entity.children[index:]...)
|
ent.children = append (
|
||||||
entity.children[index] = bind(entity, entity.window, child)
|
ent.children[:index + 1],
|
||||||
|
ent.children[index:]...)
|
||||||
|
ent.children[index] = childEntity
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entity *entity) Disown (index int) {
|
func (entity *entity) Disown (index int) {
|
||||||
entity.children[index].unbind()
|
entity.children[index].unlink()
|
||||||
entity.children = append (
|
entity.children = append (
|
||||||
entity.children[:index],
|
entity.children[:index],
|
||||||
entity.children[index + 1:]...)
|
entity.children[index + 1:]...)
|
||||||
@ -138,9 +171,7 @@ func (entity *entity) PlaceChild (index int, bounds image.Rectangle) {
|
|||||||
child.bounds = bounds
|
child.bounds = bounds
|
||||||
child.clippedBounds = entity.bounds.Intersect(bounds)
|
child.clippedBounds = entity.bounds.Intersect(bounds)
|
||||||
child.Invalidate()
|
child.Invalidate()
|
||||||
if child.isContainer {
|
child.InvalidateLayout()
|
||||||
child.InvalidateLayout()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entity *entity) ChildMinimumSize (index int) (width, height int) {
|
func (entity *entity) ChildMinimumSize (index int) (width, height int) {
|
||||||
@ -151,10 +182,12 @@ func (entity *entity) ChildMinimumSize (index int) (width, height int) {
|
|||||||
// ----------- FocusableEntity ----------- //
|
// ----------- FocusableEntity ----------- //
|
||||||
|
|
||||||
func (entity *entity) Focused () bool {
|
func (entity *entity) Focused () bool {
|
||||||
|
if entity.window == nil { return false }
|
||||||
return entity.window.focused == entity
|
return entity.window.focused == entity
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entity *entity) Focus () {
|
func (entity *entity) Focus () {
|
||||||
|
if entity.window == nil { return }
|
||||||
previous := entity.window.focused
|
previous := entity.window.focused
|
||||||
entity.window.focused = entity
|
entity.window.focused = entity
|
||||||
if previous != nil {
|
if previous != nil {
|
||||||
|
@ -3,6 +3,8 @@ package x
|
|||||||
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/canvas"
|
||||||
|
import "git.tebibyte.media/sashakoshka/tomo/default/theme"
|
||||||
|
import "git.tebibyte.media/sashakoshka/tomo/default/config"
|
||||||
|
|
||||||
type entitySet map[*entity] struct { }
|
type entitySet map[*entity] struct { }
|
||||||
|
|
||||||
@ -24,8 +26,8 @@ type system struct {
|
|||||||
focused *entity
|
focused *entity
|
||||||
canvas canvas.BasicCanvas
|
canvas canvas.BasicCanvas
|
||||||
|
|
||||||
theme tomo.Theme
|
theme theme.Wrapped
|
||||||
config tomo.Config
|
config config.Wrapped
|
||||||
|
|
||||||
invalidateIgnore bool
|
invalidateIgnore bool
|
||||||
drawingInvalid entitySet
|
drawingInvalid entitySet
|
||||||
@ -41,7 +43,7 @@ func (system *system) initialize () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (system *system) SetTheme (theme tomo.Theme) {
|
func (system *system) SetTheme (theme tomo.Theme) {
|
||||||
system.theme = theme
|
system.theme.Theme = theme
|
||||||
system.propagate (func (entity *entity) bool {
|
system.propagate (func (entity *entity) bool {
|
||||||
if child, ok := system.child.element.(tomo.Themeable); ok {
|
if child, ok := system.child.element.(tomo.Themeable); ok {
|
||||||
child.SetTheme(theme)
|
child.SetTheme(theme)
|
||||||
@ -51,7 +53,7 @@ func (system *system) SetTheme (theme tomo.Theme) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (system *system) SetConfig (config tomo.Config) {
|
func (system *system) SetConfig (config tomo.Config) {
|
||||||
system.config = config
|
system.config.Config = config
|
||||||
system.propagate (func (entity *entity) bool {
|
system.propagate (func (entity *entity) bool {
|
||||||
if child, ok := system.child.element.(tomo.Configurable); ok {
|
if child, ok := system.child.element.(tomo.Configurable); ok {
|
||||||
child.SetConfig(config)
|
child.SetConfig(config)
|
||||||
|
@ -67,6 +67,7 @@ func (backend *Backend) newWindow (
|
|||||||
|
|
||||||
window.system.initialize()
|
window.system.initialize()
|
||||||
window.system.pushFunc = window.pasteAndPush
|
window.system.pushFunc = window.pasteAndPush
|
||||||
|
window.theme.Case = tomo.C("tomo", "window")
|
||||||
|
|
||||||
window.xWindow, err = xwindow.Generate(backend.connection)
|
window.xWindow, err = xwindow.Generate(backend.connection)
|
||||||
if err != nil { return }
|
if err != nil { return }
|
||||||
@ -142,14 +143,18 @@ func (window *window) Window () tomo.Window {
|
|||||||
func (window *window) Adopt (child tomo.Element) {
|
func (window *window) Adopt (child tomo.Element) {
|
||||||
// disown previous child
|
// disown previous child
|
||||||
if window.child != nil {
|
if window.child != nil {
|
||||||
window.child.unbind()
|
window.child.unlink()
|
||||||
window.child = nil
|
window.child = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// adopt new child
|
// adopt new child
|
||||||
if child != nil {
|
if child != nil {
|
||||||
window.child = bind(nil, window, child)
|
childEntity, ok := child.Entity().(*entity)
|
||||||
window.resizeChildToFit()
|
if ok && childEntity != nil {
|
||||||
|
window.child = childEntity
|
||||||
|
childEntity.setWindow(window)
|
||||||
|
window.resizeChildToFit()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
14
element.go
14
element.go
@ -6,13 +6,13 @@ import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
|||||||
|
|
||||||
// Element represents a basic on-screen object.
|
// Element represents a basic on-screen object.
|
||||||
type Element interface {
|
type Element interface {
|
||||||
// Bind assigns an Entity to this element.
|
|
||||||
Bind (Entity)
|
|
||||||
|
|
||||||
// Draw causes the element to draw to the specified canvas. The bounds
|
// Draw causes the element to draw to the specified canvas. The bounds
|
||||||
// of this canvas specify the area that is actually drawn to, while the
|
// of this canvas specify the area that is actually drawn to, while the
|
||||||
// Entity bounds specify the actual area of the element.
|
// Entity bounds specify the actual area of the element.
|
||||||
Draw (canvas.Canvas)
|
Draw (canvas.Canvas)
|
||||||
|
|
||||||
|
// Entity returns this element's entity.
|
||||||
|
Entity () Entity
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container is an element capable of containing child elements.
|
// Container is an element capable of containing child elements.
|
||||||
@ -22,9 +22,11 @@ type Container interface {
|
|||||||
// Layout causes this element to arrange its children.
|
// Layout causes this element to arrange its children.
|
||||||
Layout ()
|
Layout ()
|
||||||
|
|
||||||
// DrawBackground draws this element's background pattern at the
|
// DrawBackground causes the element to draw its background pattern to
|
||||||
// specified bounds to any canvas.
|
// the specified canvas. The bounds of this canvas specify the area that
|
||||||
DrawBackground (destination canvas.Canvas, bounds image.Rectangle)
|
// is actually drawn to, while the Entity bounds specify the actual area
|
||||||
|
// of the element.
|
||||||
|
DrawBackground (canvas.Canvas)
|
||||||
|
|
||||||
// HandleChildMinimumSizeChange is called when a child's minimum size is
|
// HandleChildMinimumSizeChange is called when a child's minimum size is
|
||||||
// changed.
|
// changed.
|
||||||
|
@ -30,6 +30,7 @@ type Button struct {
|
|||||||
// NewButton creates a new button with the specified label text.
|
// NewButton creates a new button with the specified label text.
|
||||||
func NewButton (text string) (element *Button) {
|
func NewButton (text string) (element *Button) {
|
||||||
element = &Button { showText: true, enabled: true }
|
element = &Button { showText: true, enabled: true }
|
||||||
|
element.entity = tomo.NewEntity(element).(tomo.FocusableEntity)
|
||||||
element.theme.Case = tomo.C("tomo", "button")
|
element.theme.Case = tomo.C("tomo", "button")
|
||||||
element.drawer.SetFace (element.theme.FontFace (
|
element.drawer.SetFace (element.theme.FontFace (
|
||||||
tomo.FontStyleRegular,
|
tomo.FontStyleRegular,
|
||||||
@ -38,11 +39,9 @@ func NewButton (text string) (element *Button) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind binds this element to an entity.
|
// Entity returns this element's entity.
|
||||||
func (element *Button) Bind (entity tomo.Entity) {
|
func (element *Button) Entity () tomo.Entity {
|
||||||
if entity == nil { element.entity = nil; return }
|
return element.entity
|
||||||
element.entity = entity.(tomo.FocusableEntity)
|
|
||||||
element.updateMinimumSize()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnClick sets the function to be called when the button is clicked.
|
// OnClick sets the function to be called when the button is clicked.
|
||||||
@ -52,7 +51,6 @@ func (element *Button) OnClick (callback func ()) {
|
|||||||
|
|
||||||
// Focus gives this element input focus.
|
// Focus gives this element input focus.
|
||||||
func (element *Button) Focus () {
|
func (element *Button) Focus () {
|
||||||
if element.entity == nil { return }
|
|
||||||
if !element.entity.Focused() { element.entity.Focus() }
|
if !element.entity.Focused() { element.entity.Focus() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +63,6 @@ func (element *Button) Enabled () bool {
|
|||||||
func (element *Button) SetEnabled (enabled bool) {
|
func (element *Button) SetEnabled (enabled bool) {
|
||||||
if element.enabled == enabled { return }
|
if element.enabled == enabled { return }
|
||||||
element.enabled = enabled
|
element.enabled = enabled
|
||||||
if element.entity == nil { return }
|
|
||||||
element.entity.Invalidate()
|
element.entity.Invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +71,6 @@ func (element *Button) SetText (text string) {
|
|||||||
if element.text == text { return }
|
if element.text == text { return }
|
||||||
element.text = text
|
element.text = text
|
||||||
element.drawer.SetText([]rune(text))
|
element.drawer.SetText([]rune(text))
|
||||||
if element.entity == nil { return }
|
|
||||||
element.updateMinimumSize()
|
element.updateMinimumSize()
|
||||||
element.entity.Invalidate()
|
element.entity.Invalidate()
|
||||||
}
|
}
|
||||||
@ -97,7 +93,6 @@ func (element *Button) SetIcon (id tomo.Icon) {
|
|||||||
func (element *Button) ShowText (showText bool) {
|
func (element *Button) ShowText (showText bool) {
|
||||||
if element.showText == showText { return }
|
if element.showText == showText { return }
|
||||||
element.showText = showText
|
element.showText = showText
|
||||||
if element.entity == nil { return }
|
|
||||||
element.updateMinimumSize()
|
element.updateMinimumSize()
|
||||||
element.entity.Invalidate()
|
element.entity.Invalidate()
|
||||||
}
|
}
|
||||||
@ -109,7 +104,6 @@ func (element *Button) SetTheme (new tomo.Theme) {
|
|||||||
element.drawer.SetFace (element.theme.FontFace (
|
element.drawer.SetFace (element.theme.FontFace (
|
||||||
tomo.FontStyleRegular,
|
tomo.FontStyleRegular,
|
||||||
tomo.FontSizeNormal))
|
tomo.FontSizeNormal))
|
||||||
if element.entity == nil { return }
|
|
||||||
element.updateMinimumSize()
|
element.updateMinimumSize()
|
||||||
element.entity.Invalidate()
|
element.entity.Invalidate()
|
||||||
}
|
}
|
||||||
@ -118,14 +112,12 @@ func (element *Button) SetTheme (new tomo.Theme) {
|
|||||||
func (element *Button) SetConfig (new tomo.Config) {
|
func (element *Button) SetConfig (new tomo.Config) {
|
||||||
if new == element.config.Config { return }
|
if new == element.config.Config { return }
|
||||||
element.config.Config = new
|
element.config.Config = new
|
||||||
if element.entity == nil { return }
|
|
||||||
element.updateMinimumSize()
|
element.updateMinimumSize()
|
||||||
element.entity.Invalidate()
|
element.entity.Invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw causes the element to draw to the specified destination canvas.
|
// Draw causes the element to draw to the specified destination canvas.
|
||||||
func (element *Button) Draw (destination canvas.Canvas) {
|
func (element *Button) Draw (destination canvas.Canvas) {
|
||||||
if element.entity == nil { return }
|
|
||||||
state := element.state()
|
state := element.state()
|
||||||
bounds := element.entity.Bounds()
|
bounds := element.entity.Bounds()
|
||||||
pattern := element.theme.Pattern(tomo.PatternButton, state)
|
pattern := element.theme.Pattern(tomo.PatternButton, state)
|
||||||
@ -182,7 +174,7 @@ func (element *Button) Draw (destination canvas.Canvas) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (element *Button) HandleFocusChange () {
|
func (element *Button) HandleFocusChange () {
|
||||||
if element.entity != nil { element.entity.Invalidate() }
|
element.entity.Invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (element *Button) HandleMouseDown (x, y int, button input.Button) {
|
func (element *Button) HandleMouseDown (x, y int, button input.Button) {
|
||||||
@ -190,32 +182,31 @@ func (element *Button) HandleMouseDown (x, y int, button input.Button) {
|
|||||||
element.Focus()
|
element.Focus()
|
||||||
if button != input.ButtonLeft { return }
|
if button != input.ButtonLeft { return }
|
||||||
element.pressed = true
|
element.pressed = true
|
||||||
if element.entity != nil { element.entity.Invalidate() }
|
element.entity.Invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (element *Button) HandleMouseUp (x, y int, button input.Button) {
|
func (element *Button) HandleMouseUp (x, y int, button input.Button) {
|
||||||
if element.entity == nil { return }
|
|
||||||
if button != input.ButtonLeft { return }
|
if button != input.ButtonLeft { return }
|
||||||
element.pressed = false
|
element.pressed = false
|
||||||
within := image.Point { x, y }.In(element.entity.Bounds())
|
within := image.Point { x, y }.In(element.entity.Bounds())
|
||||||
if element.Enabled() && within && element.onClick != nil {
|
if element.Enabled() && within && element.onClick != nil {
|
||||||
element.onClick()
|
element.onClick()
|
||||||
}
|
}
|
||||||
if element.entity != nil { element.entity.Invalidate() }
|
element.entity.Invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (element *Button) HandleKeyDown (key input.Key, modifiers input.Modifiers) {
|
func (element *Button) HandleKeyDown (key input.Key, modifiers input.Modifiers) {
|
||||||
if !element.Enabled() { return }
|
if !element.Enabled() { return }
|
||||||
if key == input.KeyEnter {
|
if key == input.KeyEnter {
|
||||||
element.pressed = true
|
element.pressed = true
|
||||||
if element.entity != nil { element.entity.Invalidate() }
|
element.entity.Invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (element *Button) HandleKeyUp(key input.Key, modifiers input.Modifiers) {
|
func (element *Button) HandleKeyUp(key input.Key, modifiers input.Modifiers) {
|
||||||
if key == input.KeyEnter && element.pressed {
|
if key == input.KeyEnter && element.pressed {
|
||||||
element.pressed = false
|
element.pressed = false
|
||||||
if element.entity != nil { element.entity.Invalidate() }
|
element.entity.Invalidate()
|
||||||
if !element.Enabled() { return }
|
if !element.Enabled() { return }
|
||||||
if element.onClick != nil {
|
if element.onClick != nil {
|
||||||
element.onClick()
|
element.onClick()
|
||||||
|
@ -27,6 +27,7 @@ type Checkbox struct {
|
|||||||
// NewCheckbox creates a new cbeckbox with the specified label text.
|
// NewCheckbox creates a new cbeckbox with the specified label text.
|
||||||
func NewCheckbox (text string, checked bool) (element *Checkbox) {
|
func NewCheckbox (text string, checked bool) (element *Checkbox) {
|
||||||
element = &Checkbox { checked: checked, enabled: true }
|
element = &Checkbox { checked: checked, enabled: true }
|
||||||
|
element.entity = tomo.NewEntity(element).(tomo.FocusableEntity)
|
||||||
element.theme.Case = tomo.C("tomo", "checkbox")
|
element.theme.Case = tomo.C("tomo", "checkbox")
|
||||||
element.drawer.SetFace (element.theme.FontFace (
|
element.drawer.SetFace (element.theme.FontFace (
|
||||||
tomo.FontStyleRegular,
|
tomo.FontStyleRegular,
|
||||||
@ -35,11 +36,9 @@ func NewCheckbox (text string, checked bool) (element *Checkbox) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind binds this element to an entity.
|
// Entity returns this element's entity.
|
||||||
func (element *Checkbox) Bind (entity tomo.Entity) {
|
func (element *Checkbox) Entity () tomo.Entity {
|
||||||
if entity == nil { element.entity = nil; return }
|
return element.entity
|
||||||
element.entity = entity.(tomo.FocusableEntity)
|
|
||||||
element.updateMinimumSize()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnToggle sets the function to be called when the checkbox is toggled.
|
// OnToggle sets the function to be called when the checkbox is toggled.
|
||||||
@ -54,7 +53,6 @@ func (element *Checkbox) Value () (checked bool) {
|
|||||||
|
|
||||||
// Focus gives this element input focus.
|
// Focus gives this element input focus.
|
||||||
func (element *Checkbox) Focus () {
|
func (element *Checkbox) Focus () {
|
||||||
if element.entity == nil { return }
|
|
||||||
if !element.entity.Focused() { element.entity.Focus() }
|
if !element.entity.Focused() { element.entity.Focus() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +74,6 @@ func (element *Checkbox) SetText (text string) {
|
|||||||
if element.text == text { return }
|
if element.text == text { return }
|
||||||
element.text = text
|
element.text = text
|
||||||
element.drawer.SetText([]rune(text))
|
element.drawer.SetText([]rune(text))
|
||||||
if element.entity == nil { return }
|
|
||||||
element.updateMinimumSize()
|
element.updateMinimumSize()
|
||||||
element.entity.Invalidate()
|
element.entity.Invalidate()
|
||||||
}
|
}
|
||||||
@ -88,7 +85,6 @@ func (element *Checkbox) SetTheme (new tomo.Theme) {
|
|||||||
element.drawer.SetFace (element.theme.FontFace (
|
element.drawer.SetFace (element.theme.FontFace (
|
||||||
tomo.FontStyleRegular,
|
tomo.FontStyleRegular,
|
||||||
tomo.FontSizeNormal))
|
tomo.FontSizeNormal))
|
||||||
if element.entity == nil { return }
|
|
||||||
element.updateMinimumSize()
|
element.updateMinimumSize()
|
||||||
element.entity.Invalidate()
|
element.entity.Invalidate()
|
||||||
}
|
}
|
||||||
@ -97,15 +93,12 @@ func (element *Checkbox) SetTheme (new tomo.Theme) {
|
|||||||
func (element *Checkbox) SetConfig (new tomo.Config) {
|
func (element *Checkbox) SetConfig (new tomo.Config) {
|
||||||
if new == element.config.Config { return }
|
if new == element.config.Config { return }
|
||||||
element.config.Config = new
|
element.config.Config = new
|
||||||
if element.entity == nil { return }
|
|
||||||
element.updateMinimumSize()
|
element.updateMinimumSize()
|
||||||
element.entity.Invalidate()
|
element.entity.Invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw causes the element to draw to the specified destination canvas.
|
// Draw causes the element to draw to the specified destination canvas.
|
||||||
func (element *Checkbox) Draw (destination canvas.Canvas) {
|
func (element *Checkbox) Draw (destination canvas.Canvas) {
|
||||||
if element.entity == nil { return }
|
|
||||||
|
|
||||||
bounds := element.entity.Bounds()
|
bounds := element.entity.Bounds()
|
||||||
boxBounds := image.Rect(0, 0, bounds.Dy(), bounds.Dy()).Add(bounds.Min)
|
boxBounds := image.Rect(0, 0, bounds.Dy(), bounds.Dy()).Add(bounds.Min)
|
||||||
|
|
||||||
@ -116,7 +109,7 @@ func (element *Checkbox) Draw (destination canvas.Canvas) {
|
|||||||
On: element.checked,
|
On: element.checked,
|
||||||
}
|
}
|
||||||
|
|
||||||
element.entity.DrawBackground(destination, bounds)
|
element.entity.DrawBackground(destination)
|
||||||
|
|
||||||
pattern := element.theme.Pattern(tomo.PatternButton, state)
|
pattern := element.theme.Pattern(tomo.PatternButton, state)
|
||||||
pattern.Draw(destination, boxBounds)
|
pattern.Draw(destination, boxBounds)
|
||||||
@ -135,7 +128,6 @@ func (element *Checkbox) Draw (destination canvas.Canvas) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (element *Checkbox) HandleMouseDown (x, y int, button input.Button) {
|
func (element *Checkbox) HandleMouseDown (x, y int, button input.Button) {
|
||||||
if element.entity == nil { return }
|
|
||||||
if !element.Enabled() { return }
|
if !element.Enabled() { return }
|
||||||
element.Focus()
|
element.Focus()
|
||||||
element.pressed = true
|
element.pressed = true
|
||||||
@ -143,7 +135,6 @@ func (element *Checkbox) HandleMouseDown (x, y int, button input.Button) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (element *Checkbox) HandleMouseUp (x, y int, button input.Button) {
|
func (element *Checkbox) HandleMouseUp (x, y int, button input.Button) {
|
||||||
if element.entity == nil { return }
|
|
||||||
if button != input.ButtonLeft || !element.pressed { return }
|
if button != input.ButtonLeft || !element.pressed { return }
|
||||||
|
|
||||||
element.pressed = false
|
element.pressed = false
|
||||||
@ -167,7 +158,6 @@ func (element *Checkbox) HandleKeyDown (key input.Key, modifiers input.Modifiers
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (element *Checkbox) HandleKeyUp (key input.Key, modifiers input.Modifiers) {
|
func (element *Checkbox) HandleKeyUp (key input.Key, modifiers input.Modifiers) {
|
||||||
if element.entity == nil { return }
|
|
||||||
if key == input.KeyEnter && element.pressed {
|
if key == input.KeyEnter && element.pressed {
|
||||||
element.pressed = false
|
element.pressed = false
|
||||||
element.checked = !element.checked
|
element.checked = !element.checked
|
||||||
|
@ -20,15 +20,15 @@ func NewIcon (id tomo.Icon, size tomo.IconSize) (element *Icon) {
|
|||||||
id: id,
|
id: id,
|
||||||
size: size,
|
size: size,
|
||||||
}
|
}
|
||||||
|
element.entity = tomo.NewEntity(element)
|
||||||
element.theme.Case = tomo.C("tomo", "icon")
|
element.theme.Case = tomo.C("tomo", "icon")
|
||||||
|
element.updateMinimumSize()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind binds this element to an entity.
|
// Entity returns this element's entity.
|
||||||
func (element *Icon) Bind (entity tomo.Entity) {
|
func (element *Icon) Entity () tomo.Entity {
|
||||||
if entity == nil { element.entity = nil; return }
|
return element.entity
|
||||||
element.entity = entity
|
|
||||||
element.updateMinimumSize()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetIcon sets the element's icon.
|
// SetIcon sets the element's icon.
|
||||||
|
@ -16,15 +16,15 @@ type Image struct {
|
|||||||
// NewImage creates a new image element.
|
// NewImage creates a new image element.
|
||||||
func NewImage (image image.Image) (element *Image) {
|
func NewImage (image image.Image) (element *Image) {
|
||||||
element = &Image { buffer: canvas.FromImage(image) }
|
element = &Image { buffer: canvas.FromImage(image) }
|
||||||
|
element.entity = tomo.NewEntity(element)
|
||||||
|
bounds := element.buffer.Bounds()
|
||||||
|
element.entity.SetMinimumSize(bounds.Dx(), bounds.Dy())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind binds this element to an entity.
|
// Entity returns this element's entity.
|
||||||
func (element *Image) Bind (entity tomo.Entity) {
|
func (element *Image) Entity () tomo.Entity {
|
||||||
if entity == nil { element.entity = nil; return }
|
return element.entity
|
||||||
element.entity = entity
|
|
||||||
bounds := element.buffer.Bounds()
|
|
||||||
element.entity.SetMinimumSize(bounds.Dx(), bounds.Dy())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw causes the element to draw to the specified destination canvas.
|
// Draw causes the element to draw to the specified destination canvas.
|
||||||
|
@ -29,6 +29,7 @@ type Label struct {
|
|||||||
func NewLabel (text string, wrap bool) (element *Label) {
|
func NewLabel (text string, wrap bool) (element *Label) {
|
||||||
element = &Label { }
|
element = &Label { }
|
||||||
element.theme.Case = tomo.C("tomo", "label")
|
element.theme.Case = tomo.C("tomo", "label")
|
||||||
|
element.entity = tomo.NewEntity(element).(tomo.FlexibleEntity)
|
||||||
element.drawer.SetFace (element.theme.FontFace (
|
element.drawer.SetFace (element.theme.FontFace (
|
||||||
tomo.FontStyleRegular,
|
tomo.FontStyleRegular,
|
||||||
tomo.FontSizeNormal))
|
tomo.FontSizeNormal))
|
||||||
@ -37,11 +38,9 @@ func NewLabel (text string, wrap bool) (element *Label) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind binds this element to an entity.
|
// Entity returns this element's entity.
|
||||||
func (element *Label) Bind (entity tomo.Entity) {
|
func (element *Label) Entity () tomo.Entity {
|
||||||
if entity == nil { element.entity = nil; return }
|
return element.entity
|
||||||
element.entity = entity.(tomo.FlexibleEntity)
|
|
||||||
element.updateMinimumSize()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmCollapse forces a minimum width and height upon the label. The width is
|
// EmCollapse forces a minimum width and height upon the label. The width is
|
||||||
@ -128,14 +127,14 @@ func (element *Label) SetConfig (new tomo.Config) {
|
|||||||
func (element *Label) Draw (destination canvas.Canvas) {
|
func (element *Label) Draw (destination canvas.Canvas) {
|
||||||
if element.entity == nil { return }
|
if element.entity == nil { return }
|
||||||
|
|
||||||
bounds := element.entity. Bounds()
|
bounds := element.entity.Bounds()
|
||||||
|
|
||||||
if element.wrap {
|
if element.wrap {
|
||||||
element.drawer.SetMaxWidth(bounds.Dx())
|
element.drawer.SetMaxWidth(bounds.Dx())
|
||||||
element.drawer.SetMaxHeight(bounds.Dy())
|
element.drawer.SetMaxHeight(bounds.Dy())
|
||||||
}
|
}
|
||||||
|
|
||||||
element.entity.DrawBackground(destination, bounds)
|
element.entity.DrawBackground(destination)
|
||||||
|
|
||||||
textBounds := element.drawer.LayoutBounds()
|
textBounds := element.drawer.LayoutBounds()
|
||||||
foreground := element.theme.Color (
|
foreground := element.theme.Color (
|
||||||
|
@ -24,9 +24,11 @@ type Entity interface {
|
|||||||
SetMinimumSize (width, height int)
|
SetMinimumSize (width, height int)
|
||||||
|
|
||||||
// DrawBackground asks the parent element to draw its background pattern
|
// DrawBackground asks the parent element to draw its background pattern
|
||||||
// within the specified rectangle. This should be used for transparent
|
// to a canvas. This should be used for transparent elements like text
|
||||||
// elements like text labels.
|
// labels. If there is no parent element (that is, the element is
|
||||||
DrawBackground (destination canvas.Canvas, bounds image.Rectangle)
|
// directly inside of the window), the backend will draw a default
|
||||||
|
// background pattern.
|
||||||
|
DrawBackground (canvas.Canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerEntity is given to elements that support the Container interface.
|
// ContainerEntity is given to elements that support the Container interface.
|
||||||
|
6
tomo.go
6
tomo.go
@ -29,6 +29,12 @@ func Do (callback func ()) {
|
|||||||
backend.Do(callback)
|
backend.Do(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewEntity generates an entity for an element using the current backend.
|
||||||
|
func NewEntity (owner Element) Entity {
|
||||||
|
assertBackend()
|
||||||
|
return backend.NewEntity(owner)
|
||||||
|
}
|
||||||
|
|
||||||
// NewWindow creates a new window using the current backend, and returns it as a
|
// NewWindow creates a new window using the current backend, and returns it as a
|
||||||
// MainWindow. If the window could not be created, an error is returned
|
// MainWindow. If the window could not be created, an error is returned
|
||||||
// explaining why.
|
// explaining why.
|
||||||
|
Reference in New Issue
Block a user