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.GetBox()) 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 { // FIXME why are we invalidating draw here window.invalidateDraw(previous) previous.handleFocusLeave() } if box != nil && box.canBeFocused() { // FIXME why are we invalidating draw here window.invalidateDraw(box) box.handleFocusEnter() } } func (window *window) hover (box anyBox) { if window.hovered == box { return } previous := window.hovered window.hovered = box if previous != nil { previous.handleMouseLeave() } if box != nil { box.handleMouseEnter() } } 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) } }