This repository has been archived on 2023-08-08. You can view files and clone it, but cannot push or open issues or pull requests.
tomo-old/backends/x/entity.go
2023-04-20 18:40:05 -04:00

293 lines
6.8 KiB
Go

package x
import "image"
import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/canvas"
type entity struct {
window *window
parent *entity
children []*entity
element tomo.Element
bounds image.Rectangle
clippedBounds image.Rectangle
minWidth int
minHeight int
selected bool
layoutInvalid bool
isContainer bool
}
func (backend *Backend) NewEntity (owner tomo.Element) tomo.Entity {
entity := &entity { element: owner }
if _, ok := owner.(tomo.Container); ok {
entity.isContainer = true
entity.InvalidateLayout()
}
return entity
}
func (ent *entity) unlink () {
ent.propagate (func (child *entity) bool {
if child.window != nil {
delete(ent.window.system.drawingInvalid, child)
}
child.window = nil
return true
})
if ent.window != nil {
delete(ent.window.system.drawingInvalid, ent)
}
ent.parent = nil
ent.window = nil
if element, ok := ent.element.(tomo.Selectable); ok {
ent.selected = false
element.HandleSelectionChange()
}
}
func (entity *entity) link (parent *entity) {
entity.parent = parent
entity.clip(parent.clippedBounds)
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) bool {
for _, child := range entity.children {
if !child.propagate(callback) {
return false
}
}
return callback(entity)
}
func (entity *entity) propagateAlt (callback func (*entity) bool) bool {
if !callback(entity) {
return false
}
for _, child := range entity.children {
if !child.propagate(callback) {
return false
}
}
return true
}
func (entity *entity) childAt (point image.Point) *entity {
for _, child := range entity.children {
if point.In(child.bounds) {
return child.childAt(point)
}
}
return entity
}
func (entity *entity) scrollTargetChildAt (point image.Point) *entity {
for _, child := range entity.children {
if point.In(child.bounds) {
result := child.scrollTargetChildAt(point)
if result != nil { return result }
break
}
}
if _, ok := entity.element.(tomo.ScrollTarget); ok {
return entity
}
return nil
}
func (entity *entity) forMouseTargetContainers (callback func (tomo.MouseTargetContainer, tomo.Element)) {
if entity.parent == nil { return }
if parent, ok := entity.parent.element.(tomo.MouseTargetContainer); ok {
callback(parent, entity.element)
}
entity.parent.forMouseTargetContainers(callback)
}
func (entity *entity) clip (bounds image.Rectangle) {
entity.clippedBounds = entity.bounds.Intersect(bounds)
for _, child := range entity.children {
child.clip(entity.clippedBounds)
}
}
// ----------- Entity ----------- //
func (entity *entity) Invalidate () {
if entity.window == nil { return }
if entity.window.system.invalidateIgnore { return }
entity.window.drawingInvalid.Add(entity)
}
func (entity *entity) Bounds () image.Rectangle {
return entity.bounds
}
func (entity *entity) Window () tomo.Window {
return entity.window
}
func (entity *entity) SetMinimumSize (width, height int) {
entity.minWidth = width
entity.minHeight = height
if entity.parent == nil {
if entity.window != nil {
entity.window.setMinimumSize(width, height)
}
} else {
entity.parent.element.(tomo.Container).
HandleChildMinimumSizeChange(entity.element)
}
}
func (entity *entity) DrawBackground (destination canvas.Canvas) {
if entity.parent != nil {
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 ----------- //
func (entity *entity) InvalidateLayout () {
if entity.window == nil { return }
if !entity.isContainer { return }
entity.layoutInvalid = true
entity.window.system.anyLayoutInvalid = true
}
func (ent *entity) Adopt (child tomo.Element) {
childEntity, ok := child.Entity().(*entity)
if !ok || childEntity == nil { return }
childEntity.link(ent)
ent.children = append(ent.children, childEntity)
}
func (ent *entity) Insert (index int, child tomo.Element) {
childEntity, ok := child.Entity().(*entity)
if !ok || childEntity == nil { return }
ent.children = append (
ent.children[:index + 1],
ent.children[index:]...)
ent.children[index] = childEntity
}
func (entity *entity) Disown (index int) {
entity.children[index].unlink()
entity.children = append (
entity.children[:index],
entity.children[index + 1:]...)
}
func (entity *entity) IndexOf (child tomo.Element) int {
for index, childEntity := range entity.children {
if childEntity.element == child {
return index
}
}
return -1
}
func (entity *entity) Child (index int) tomo.Element {
return entity.children[index].element
}
func (entity *entity) CountChildren () int {
return len(entity.children)
}
func (entity *entity) PlaceChild (index int, bounds image.Rectangle) {
child := entity.children[index]
child.bounds = bounds
child.clip(entity.clippedBounds)
child.Invalidate()
child.InvalidateLayout()
}
func (entity *entity) SelectChild (index int, selected bool) {
child := entity.children[index]
if element, ok := child.element.(tomo.Selectable); ok {
if child.selected == selected { return }
child.selected = selected
element.HandleSelectionChange()
}
}
func (entity *entity) ChildMinimumSize (index int) (width, height int) {
childEntity := entity.children[index]
return childEntity.minWidth, childEntity.minHeight
}
// ----------- FocusableEntity ----------- //
func (entity *entity) Focused () bool {
if entity.window == nil { return false }
return entity.window.focused == entity
}
func (entity *entity) Focus () {
if entity.window == nil { return }
entity.window.system.focus(entity)
}
func (entity *entity) FocusNext () {
entity.window.system.focusNext()
}
func (entity *entity) FocusPrevious () {
entity.window.system.focusPrevious()
}
// ----------- SelectableEntity ----------- //
func (entity *entity) Selected () bool {
return entity.selected
}
// ----------- FlexibleEntity ----------- //
func (entity *entity) NotifyFlexibleHeightChange () {
if entity.parent == nil { return }
if parent, ok := entity.parent.element.(tomo.FlexibleContainer); ok {
parent.HandleChildFlexibleHeightChange (
entity.element.(tomo.Flexible))
}
}
// ----------- ScrollableEntity ----------- //
func (entity *entity) NotifyScrollBoundsChange () {
if entity.parent == nil { return }
if parent, ok := entity.parent.element.(tomo.ScrollableContainer); ok {
parent.HandleChildScrollBoundsChange (
entity.element.(tomo.Scrollable))
}
}