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) drawBackgroundPart (canvas.Canvas) captures (eventCategory) bool } type anyBox interface { tomo.Box canvas.Drawer doDraw () doLayout () doMinimumSize () contentMinimum () image.Point setParent (parent) getParent () parent flushActionQueue () recursiveRedo () canBeFocused () bool boxUnder (image.Point, eventCategory) anyBox transparent () bool 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) box.flushActionQueue() window.invalidateLayout(box) window.root = box } window.minimumClean = false } func (window *window) window () *window { return window } func (window *window) canvas () canvas.Canvas { return window.xCanvas } func (window *window) notifyMinimumSizeChange (anyBox) { window.minimumClean = false } func (window *window) invalidateMinimum (box anyBox) { window.needMinimum.Add(box) } func (window *window) invalidateDraw (box anyBox) { window.needDraw.Add(box) } func (window *window) invalidateLayout (box anyBox) { 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 { previous.handleFocusLeave() } if box != nil && box.canBeFocused() { 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 } type eventCategory int; const ( eventCategoryDND eventCategory = iota eventCategoryMouse eventCategoryScroll eventCategoryKeyboard ) func (this *window) boxUnder (point image.Point, category eventCategory) anyBox { if this.root == nil { return nil } return this.root.boxUnder(point, category) } func (this *window) captures (eventCategory) bool { return false } func (this *window) keyboardTarget () anyBox { focused := this.window().focused if focused == nil { return nil } parent := focused.getParent() for { parentBox, ok := parent.(anyBox) if !ok { break } if parent.captures(eventCategoryKeyboard) { return parentBox } parent = parentBox.getParent() } return focused } 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) if window.root != nil { window.root.SetBounds(childBounds) } // full relayout/redraw if window.root != nil { window.root.recursiveRedo() } window.pushAll() window.needRedo = false return } for len(window.needMinimum) > 0 { window.needMinimum.Pop().doMinimumSize() } if !window.minimumClean { window.doMinimumSize() } 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) } }