Sasha Koshka
ff8875535d
Boxes that need their minimum size to be updated now use a map like for layout and drawing. Size set with MinimumSize is now treated as separate from the content size and the larger size is used.
243 lines
4.8 KiB
Go
243 lines
4.8 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
|
|
canvas.Drawer
|
|
|
|
doDraw ()
|
|
doLayout ()
|
|
doMinimumSize ()
|
|
contentMinimum () image.Point
|
|
setParent (parent)
|
|
flushActionQueue ()
|
|
recursiveRedo ()
|
|
canBeFocused () bool
|
|
boxUnder (image.Point) anyBox
|
|
|
|
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
|
|
}
|
|
|
|
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.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)
|
|
}
|
|
}
|