2023-07-02 00:52:14 -06:00
|
|
|
package x
|
|
|
|
|
2023-07-03 22:04:00 -06:00
|
|
|
import "image"
|
2023-07-02 00:52:14 -06:00
|
|
|
import "git.tebibyte.media/tomo/tomo"
|
2023-07-15 22:42:47 -06:00
|
|
|
import "git.tebibyte.media/tomo/tomo/input"
|
2023-07-03 22:04:00 -06:00
|
|
|
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 {
|
2023-07-12 16:36:11 -06:00
|
|
|
window () *window
|
|
|
|
canvas () canvas.Canvas
|
|
|
|
notifyMinimumSizeChange (anyBox)
|
2023-08-22 11:17:48 -06:00
|
|
|
drawBackgroundPart (canvas.Canvas)
|
2023-09-09 13:05:08 -06:00
|
|
|
captures (eventCategory) bool
|
2023-07-03 22:04:00 -06:00
|
|
|
}
|
2023-07-02 00:52:14 -06:00
|
|
|
|
|
|
|
type anyBox interface {
|
|
|
|
tomo.Box
|
2023-08-17 21:20:08 -06:00
|
|
|
canvas.Drawer
|
2023-09-07 16:22:04 -06:00
|
|
|
|
2023-08-17 21:20:08 -06:00
|
|
|
doDraw ()
|
|
|
|
doLayout ()
|
|
|
|
doMinimumSize ()
|
|
|
|
contentMinimum () image.Point
|
|
|
|
setParent (parent)
|
2023-09-09 13:05:08 -06:00
|
|
|
getParent () parent
|
2023-08-17 21:20:08 -06:00
|
|
|
flushActionQueue ()
|
|
|
|
recursiveRedo ()
|
|
|
|
canBeFocused () bool
|
2023-09-09 13:05:08 -06:00
|
|
|
boxUnder (image.Point, eventCategory) anyBox
|
2023-09-07 16:22:04 -06:00
|
|
|
transparent () bool
|
2023-07-15 22:42:47 -06:00
|
|
|
|
2023-07-21 21:22:03 -06:00
|
|
|
propagate (func (anyBox) bool) bool
|
|
|
|
propagateAlt (func (anyBox) bool) bool
|
|
|
|
|
2023-07-15 22:54:25 -06:00
|
|
|
handleFocusEnter ()
|
|
|
|
handleFocusLeave ()
|
|
|
|
// handleDndEnter ()
|
|
|
|
// handleDndLeave ()
|
|
|
|
// handleDndDrop (data.Data)
|
2023-08-12 10:10:47 -06:00
|
|
|
handleMouseEnter ()
|
|
|
|
handleMouseLeave ()
|
2023-08-05 15:56:15 -06:00
|
|
|
handleMouseMove ()
|
2023-07-15 22:54:25 -06:00
|
|
|
handleMouseDown (input.Button)
|
|
|
|
handleMouseUp (input.Button)
|
2023-09-09 13:05:08 -06:00
|
|
|
handleScroll (float64, float64)
|
2023-07-21 21:22:03 -06:00
|
|
|
handleKeyDown (input.Key, bool)
|
|
|
|
handleKeyUp (input.Key, bool)
|
2023-07-02 00:52:14 -06:00
|
|
|
}
|
|
|
|
|
2023-07-05 01:25:50 -06:00
|
|
|
func assertAnyBox (unknown tomo.Box) anyBox {
|
|
|
|
if box, ok := unknown.(anyBox); ok {
|
|
|
|
return box
|
|
|
|
} else {
|
|
|
|
panic("foregin box implementation, i did not make this!")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-02 00:52:14 -06:00
|
|
|
func (window *window) SetRoot (root tomo.Object) {
|
2023-07-04 22:44:56 -06:00
|
|
|
if window.root != nil {
|
|
|
|
window.root.setParent(nil)
|
|
|
|
}
|
|
|
|
if root == nil {
|
|
|
|
window.root = nil
|
|
|
|
} else {
|
2023-08-11 23:03:34 -06:00
|
|
|
box := assertAnyBox(root.GetBox())
|
2023-07-04 22:44:56 -06:00
|
|
|
box.setParent(window)
|
2023-08-17 21:20:08 -06:00
|
|
|
box.flushActionQueue()
|
2023-07-04 22:44:56 -06:00
|
|
|
window.invalidateLayout(box)
|
|
|
|
window.root = box
|
|
|
|
}
|
2023-08-17 21:20:08 -06:00
|
|
|
window.minimumClean = false
|
2023-07-02 00:52:14 -06:00
|
|
|
}
|
|
|
|
|
2023-07-03 22:04:00 -06:00
|
|
|
func (window *window) window () *window {
|
|
|
|
return window
|
|
|
|
}
|
|
|
|
|
|
|
|
func (window *window) canvas () canvas.Canvas {
|
|
|
|
return window.xCanvas
|
|
|
|
}
|
|
|
|
|
2023-07-12 16:36:11 -06:00
|
|
|
func (window *window) notifyMinimumSizeChange (anyBox) {
|
2023-08-17 21:20:08 -06:00
|
|
|
window.minimumClean = false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (window *window) invalidateMinimum (box anyBox) {
|
|
|
|
window.needMinimum.Add(box)
|
2023-07-12 16:36:11 -06:00
|
|
|
}
|
|
|
|
|
2023-07-03 22:04:00 -06:00
|
|
|
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 }
|
2023-09-07 16:22:04 -06:00
|
|
|
|
2023-07-15 22:54:25 -06:00
|
|
|
previous := window.focused
|
2023-07-03 22:04:00 -06:00
|
|
|
window.focused = box
|
2023-09-07 16:22:04 -06:00
|
|
|
|
2023-07-15 22:54:25 -06:00
|
|
|
if previous != nil {
|
|
|
|
previous.handleFocusLeave()
|
|
|
|
}
|
2023-07-21 21:22:03 -06:00
|
|
|
if box != nil && box.canBeFocused() {
|
2023-07-15 22:54:25 -06:00
|
|
|
box.handleFocusEnter()
|
|
|
|
}
|
2023-07-03 22:04:00 -06:00
|
|
|
}
|
|
|
|
|
2023-08-12 10:10:47 -06:00
|
|
|
func (window *window) hover (box anyBox) {
|
|
|
|
if window.hovered == box { return }
|
2023-09-07 16:22:04 -06:00
|
|
|
|
2023-08-12 10:10:47 -06:00
|
|
|
previous := window.hovered
|
|
|
|
window.hovered = box
|
2023-09-07 16:22:04 -06:00
|
|
|
|
2023-08-12 10:10:47 -06:00
|
|
|
if previous != nil {
|
|
|
|
previous.handleMouseLeave()
|
|
|
|
}
|
|
|
|
if box != nil {
|
|
|
|
box.handleMouseEnter()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-21 21:22:03 -06:00
|
|
|
func (window *window) anyFocused () bool {
|
|
|
|
return window.focused != nil
|
|
|
|
}
|
|
|
|
|
2023-09-09 13:05:08 -06:00
|
|
|
type eventCategory int; const (
|
|
|
|
eventCategoryDND eventCategory = iota
|
|
|
|
eventCategoryMouse
|
|
|
|
eventCategoryScroll
|
|
|
|
eventCategoryKeyboard
|
|
|
|
)
|
|
|
|
|
|
|
|
func (this *window) boxUnder (point image.Point, category eventCategory) anyBox {
|
2023-07-15 22:42:47 -06:00
|
|
|
if this.root == nil { return nil }
|
2023-09-09 13:05:08 -06:00
|
|
|
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
|
2023-07-15 22:42:47 -06:00
|
|
|
}
|
|
|
|
|
2023-07-21 21:22:03 -06:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2023-07-03 22:04:00 -06:00
|
|
|
func (window *window) afterEvent () {
|
2023-07-04 22:44:56 -06:00
|
|
|
if window.xCanvas == nil { return }
|
|
|
|
|
2023-07-03 22:04:00 -06:00
|
|
|
if window.needRedo {
|
2023-07-04 22:44:56 -06:00
|
|
|
// set child bounds
|
|
|
|
childBounds := window.metrics.bounds
|
|
|
|
childBounds = childBounds.Sub(childBounds.Min)
|
2024-05-26 13:33:40 -06:00
|
|
|
if window.root != nil {
|
|
|
|
window.root.SetBounds(childBounds)
|
|
|
|
}
|
2023-07-04 22:44:56 -06:00
|
|
|
|
2023-09-07 16:22:04 -06:00
|
|
|
// full relayout/redraw
|
2023-07-03 22:04:00 -06:00
|
|
|
if window.root != nil {
|
|
|
|
window.root.recursiveRedo()
|
|
|
|
}
|
2023-07-04 22:44:56 -06:00
|
|
|
window.pushAll()
|
|
|
|
window.needRedo = false
|
2023-07-03 22:04:00 -06:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-08-17 21:20:08 -06:00
|
|
|
for len(window.needMinimum) > 0 {
|
|
|
|
window.needMinimum.Pop().doMinimumSize()
|
|
|
|
}
|
|
|
|
if !window.minimumClean {
|
|
|
|
window.doMinimumSize()
|
|
|
|
}
|
2023-07-03 22:04:00 -06:00
|
|
|
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())
|
|
|
|
}
|
2023-07-04 22:44:56 -06:00
|
|
|
if !toPush.Empty() {
|
|
|
|
window.pushRegion(toPush)
|
|
|
|
}
|
2023-07-02 00:52:14 -06:00
|
|
|
}
|