ecs #15

Merged
sashakoshka merged 41 commits from ecs into main 2023-04-20 04:29:08 +00:00
4 changed files with 187 additions and 3 deletions
Showing only changes of commit bb9c5df088 - Show all commits

154
backends/x/entity.go Normal file
View File

@ -0,0 +1,154 @@
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
drawDirty bool
layoutDirty bool
bounds image.Rectangle
minWidth int
minHeight int
}
func bind (element tomo.Element) *entity {
entity := &entity { drawDirty: true }
if _, ok := element.(tomo.Container); ok {
entity.layoutDirty = true
}
element.Bind(entity)
return entity
}
func (entity *entity) unbind () {
entity.element.Bind(nil)
for _, childEntity := range entity.children {
childEntity.unbind()
}
}
// ----------- Entity ----------- //
func (entity *entity) Invalidate () {
entity.drawDirty = true
}
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 { return }
entity.parent.element.(tomo.Container).HandleChildMinimumSizeChange()
}
func (entity *entity) DrawBackground (destination canvas.Canvas, bounds image.Rectangle) {
if entity.parent == nil { return }
entity.parent.element.(tomo.Container).DrawBackground(destination, bounds)
}
// ----------- ContainerEntity ----------- //
func (entity *entity) InvalidateLayout () {
entity.layoutDirty = true
}
func (entity *entity) Adopt (child tomo.Element) {
entity.children = append(entity.children, bind(child))
}
func (entity *entity) Insert (index int, child tomo.Element) {
entity.children = append (
entity.children[:index + 1],
entity.children[index:]...)
entity.children[index] = bind(child)
}
func (entity *entity) Disown (index int) {
entity.children[index].unbind()
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) {
entity.children[index].bounds = bounds
}
func (entity *entity) ChildMinimumSize (index int) (width, height int) {
childEntity := entity.children[index]
return childEntity.minWidth, childEntity.minHeight
}
// ----------- FocusableEntity ----------- //
func (entity *entity) Focused () bool {
return entity.window.focused == entity
}
func (entity *entity) Focus () {
previous := entity.window.focused
entity.window.focused = entity
if previous != nil {
previous.element.(tomo.Focusable).HandleFocusChange()
}
entity.element.(tomo.Focusable).HandleFocusChange()
}
func (entity *entity) FocusNext () {
// TODO
}
func (entity *entity) FocusPrevious () {
// TODO
}
// ----------- FlexibleEntity ----------- //
func (entity *entity) NotifyFlexibleHeightChange () {
if entity.parent == nil { return }
if parent, ok := entity.parent.element.(tomo.FlexibleContainer); ok {
parent.HandleChildFlexibleHeightChange()
}
}
// ----------- ScrollableEntity ----------- //
func (entity *entity) NotifyScrollBoundsChange () {
if entity.parent == nil { return }
if parent, ok := entity.parent.element.(tomo.ScrollableContainer); ok {
parent.HandleChildScrollBoundsChange()
}
}

View File

@ -24,9 +24,9 @@ type window struct {
xWindow *xwindow.Window
xCanvas *xgraphics.Image
canvas canvas.BasicCanvas
child tomo.Element
child *entity
focused *entity
onClose func ()
skipChildDrawCallback bool
title, application string

View File

@ -25,6 +25,10 @@ type Container interface {
// DrawBackground draws this element's background pattern at the
// specified bounds to any canvas.
DrawBackground (destination canvas.Canvas, bounds image.Rectangle)
// HandleChildMinimumSizeChange is called when a child's minimum size is
// changed.
HandleChildMinimumSizeChange ()
}
// Focusable represents an element that has keyboard navigation support.
@ -104,6 +108,16 @@ type Flexible interface {
FlexibleHeightFor (width int) int
}
// FlexibleContainer represents an element that is capable of containing
// flexible children.
type FlexibleContainer interface {
Container
// HandleChildFlexibleHeightChange is called when the parameters
// affecting a child's flexible height are changed.
HandleChildFlexibleHeightChange ()
}
// Scrollable represents an element that can be scrolled. It acts as a viewport
// through which its contents can be observed.
type Scrollable interface {
@ -124,6 +138,16 @@ type Scrollable interface {
ScrollAxes () (horizontal, vertical bool)
}
// ScrollableContainer represents an element that is capable of containing
// scrollable children.
type ScrollableContainer interface {
Container
// HandleChildScrollBoundsChange is called when the content bounds,
// viewport bounds, or scroll axes of a child are changed.
HandleChildScrollBoundsChange()
}
// Collapsible represents an element who's minimum width and height can be
// manually resized. Scrollable elements should implement this if possible.
type Collapsible interface {

View File

@ -1,6 +1,7 @@
package tomo
import "image"
import "git.tebibyte.media/sashakoshka/tomo/canvas"
// Entity is a handle given to elements by the backend. Different types of
// entities may be assigned to elements that support different capabilities.
@ -25,13 +26,18 @@ type Entity interface {
// DrawBackground asks the parent element to draw its background pattern
// within the specified rectangle. This should be used for transparent
// elements like text labels.
DrawBackground (bounds image.Rectangle)
DrawBackground (destination canvas.Canvas, bounds image.Rectangle)
}
// ContainerEntity is given to elements that support the Container interface.
type ContainerEntity interface {
Entity
// InvalidateLayout marks the element's layout as invalid. At the end of
// every event, the backend will ask all invalid containers to
// recalculate their layouts.
InvalidateLayout ()
// Adopt adds an element as a child.
Adopt (child Element)