This repository has been archived on 2024-06-03. You can view files and clone it, but cannot push or open issues or pull requests.
x/system.go

218 lines
4.4 KiB
Go

package x
import "image"
import "git.tebibyte.media/tomo/tomo"
import "git.tebibyte.media/tomo/tomo/input"
import "git.tebibyte.media/tomo/tomo/canvas"
type boxSet map[anyBox] struct { }
func (set boxSet) Empty () bool {
return set == nil || len(set) == 0
}
func (set boxSet) Has (box anyBox) bool {
if set == nil { return false }
_, ok := set[box]
return ok
}
func (set *boxSet) Add (box anyBox) {
if *set == nil {
*set = make(boxSet)
}
(*set)[box] = struct { } { }
}
func (set *boxSet) Pop () anyBox {
for box := range *set {
delete(*set, box)
return box
}
return nil
}
type parent interface {
window () *window
canvas () canvas.Canvas
notifyMinimumSizeChange (anyBox)
}
type anyBox interface {
tomo.Box
doDraw ()
doLayout ()
setParent (parent)
recursiveRedo ()
canBeFocused () bool
boxUnder (image.Point) anyBox
recalculateMinimumSize ()
propagate (func (anyBox) bool) bool
propagateAlt (func (anyBox) bool) bool
handleFocusEnter ()
handleFocusLeave ()
// handleDndEnter ()
// handleDndLeave ()
// handleDndDrop (data.Data)
// handleMouseEnter ()
// handleMouseLeave ()
// handleMouseMove ()
handleMouseDown (input.Button)
handleMouseUp (input.Button)
// handleScroll (float64, float64)
handleKeyDown (input.Key, bool)
handleKeyUp (input.Key, bool)
}
func assertAnyBox (unknown tomo.Box) anyBox {
if box, ok := unknown.(anyBox); ok {
return box
} else {
panic("foregin box implementation, i did not make this!")
}
}
func (window *window) SetRoot (root tomo.Object) {
if window.root != nil {
window.root.setParent(nil)
}
if root == nil {
window.root = nil
} else {
box := assertAnyBox(root.Box())
box.setParent(window)
window.invalidateLayout(box)
window.root = box
}
window.recalculateMinimumSize()
}
func (window *window) window () *window {
return window
}
func (window *window) canvas () canvas.Canvas {
return window.xCanvas
}
func (window *window) notifyMinimumSizeChange (anyBox) {
window.recalculateMinimumSize()
}
func (window *window) invalidateDraw (box anyBox) {
window.needDraw.Add(box)
}
func (window *window) invalidateLayout (box anyBox) {
// TODO: use a priority queue for this and have the value be the amount
// of parents a box has
window.needLayout.Add(box)
window.invalidateDraw(box)
}
func (window *window) focus (box anyBox) {
if window.focused == box { return }
previous := window.focused
window.focused = box
if previous != nil {
window.invalidateDraw(previous)
previous.handleFocusLeave()
}
if box != nil && box.canBeFocused() {
window.invalidateDraw(box)
box.handleFocusEnter()
}
}
func (window *window) anyFocused () bool {
return window.focused != nil
}
func (this *window) boxUnder (point image.Point) anyBox {
if this.root == nil { return nil }
return this.root.boxUnder(point)
}
func (this *window) focusNext () {
found := !this.anyFocused()
focused := false
this.propagateAlt (func (box anyBox) bool {
if found {
// looking for the next box to select
if box.canBeFocused() {
// found it
this.focus(box)
focused = true
return false
}
} else {
// looking for the current focused element
if box == this.focused {
// found it
found = true
}
}
return true
})
if !focused { this.focus(nil) }
}
func (this *window) focusPrevious () {
var behind anyBox
this.propagate (func (box anyBox) bool {
if box == this.focused {
return false
}
if box.canBeFocused() { behind = box }
return true
})
this.focus(behind)
}
func (this *window) propagate (callback func (box anyBox) bool) {
if this.root == nil { return }
this.root.propagate(callback)
}
func (this *window) propagateAlt (callback func (box anyBox) bool) {
if this.root == nil { return }
this.root.propagateAlt(callback)
}
func (window *window) afterEvent () {
if window.xCanvas == nil { return }
if window.needRedo {
// set child bounds
childBounds := window.metrics.bounds
childBounds = childBounds.Sub(childBounds.Min)
window.root.SetBounds(childBounds)
// full relayout/redraw
if window.root != nil {
window.root.recursiveRedo()
}
window.pushAll()
window.needRedo = false
return
}
for len(window.needLayout) > 0 {
window.needLayout.Pop().doLayout()
}
var toPush image.Rectangle
for len(window.needDraw) > 0 {
box := window.needDraw.Pop()
box.doDraw()
toPush = toPush.Union(box.Bounds())
}
if !toPush.Empty() {
window.pushRegion(toPush)
}
}