2024-06-02 11:23:03 -06:00
|
|
|
package system
|
|
|
|
|
|
|
|
import "image"
|
|
|
|
import "git.tebibyte.media/tomo/tomo/input"
|
2024-09-11 23:10:52 -06:00
|
|
|
import "git.tebibyte.media/tomo/tomo/config"
|
2024-06-02 11:23:03 -06:00
|
|
|
|
2024-07-25 11:01:15 -06:00
|
|
|
// TODO: once go v1.23 comes out, replace the explicit iterator calls here with
|
|
|
|
// range loops
|
|
|
|
|
2024-06-02 11:23:03 -06:00
|
|
|
// HandleFocusChange sets whether or not the window containing this Hierarchy
|
|
|
|
// has input focus.
|
|
|
|
func (this *Hierarchy) HandleFocusChange (focused bool) {
|
|
|
|
if this.windowFocused == focused { return }
|
|
|
|
this.windowFocused = focused
|
|
|
|
}
|
|
|
|
|
|
|
|
// HandleModifiers sets the modifier keys that are currently being pressed.
|
|
|
|
func (this *Hierarchy) HandleModifiers (modifiers input.Modifiers) {
|
|
|
|
if this.modifiers == modifiers { return }
|
|
|
|
this.modifiers = modifiers
|
|
|
|
}
|
|
|
|
|
|
|
|
// HandleKeyDown sends a key down event to the currently focused Box. If the
|
|
|
|
// event which triggers this comes with modifier key information,
|
|
|
|
// HandleModifiers must be called *before* HandleKeyDown.
|
|
|
|
func (this *Hierarchy) HandleKeyDown (key input.Key, numberPad bool) {
|
2024-07-25 11:01:15 -06:00
|
|
|
caught := false
|
2024-07-27 00:20:06 -06:00
|
|
|
if this.anyFocused() {
|
|
|
|
this.keyboardTargets(func (target anyBox) bool {
|
|
|
|
if target.handleKeyDown(key, numberPad) {
|
|
|
|
caught = true
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
if this.root != nil {
|
|
|
|
caught = this.root.handleKeyDown(key, numberPad)
|
2024-07-25 11:01:15 -06:00
|
|
|
}
|
2024-07-27 00:20:06 -06:00
|
|
|
}
|
2024-07-25 11:01:15 -06:00
|
|
|
if caught { return }
|
2024-07-26 18:55:34 -06:00
|
|
|
|
2024-09-11 23:10:52 -06:00
|
|
|
switch input.KC(key, this.modifiers) {
|
|
|
|
case config.KeyChordFocusNext: this.focusNext()
|
|
|
|
case config.KeyChordFocusPrevious: this.focusPrevious()
|
|
|
|
// TODO: up, down, left, and right should find a box to the top, bottom,
|
|
|
|
// left, and right respectively to move the focus to. we might want to
|
|
|
|
// have four corresponding key chords in tomo/config.
|
|
|
|
case input.KC(input.KeyDown, input.ModNone): this.focusNext()
|
|
|
|
case input.KC(input.KeyUp, input.ModNone): this.focusPrevious()
|
2024-06-02 11:23:03 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// HandleKeyUp sends a key up event to the currently focused Box. If the event
|
|
|
|
// which triggers this comes with modifier key information, HandleModifiers must
|
|
|
|
// be called *before* HandleKeyUp.
|
|
|
|
func (this *Hierarchy) HandleKeyUp (key input.Key, numberPad bool) {
|
2024-07-27 00:20:06 -06:00
|
|
|
if this.anyFocused() {
|
|
|
|
this.keyboardTargets(func (target anyBox) bool {
|
|
|
|
if target.handleKeyUp(key, numberPad) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
if this.root != nil {
|
|
|
|
this.root.handleKeyUp(key, numberPad)
|
2024-07-25 11:01:15 -06:00
|
|
|
}
|
2024-07-27 00:20:06 -06:00
|
|
|
}
|
2024-06-02 11:23:03 -06:00
|
|
|
}
|
|
|
|
|
2024-07-25 11:01:15 -06:00
|
|
|
// HandleMouseDown sends a mouse down event to the Boxes positioned underneath
|
|
|
|
// the mouse cursor and marks them as being "dragged" by that mouse button,
|
|
|
|
// starting at the first Box to mask events and ending at the first box to
|
|
|
|
// catch the event. If the event which triggers this comes with mouse position
|
|
|
|
// information, HandleMouseMove must be called *before* HandleMouseDown.
|
2024-06-02 11:23:03 -06:00
|
|
|
func (this *Hierarchy) HandleMouseDown (button input.Button) {
|
2024-07-25 11:01:15 -06:00
|
|
|
boxes := []anyBox { }
|
2024-07-26 15:34:14 -06:00
|
|
|
first := true
|
2024-07-25 11:01:15 -06:00
|
|
|
this.boxesUnder(this.mousePosition)(func (box anyBox) bool {
|
2024-07-26 15:34:14 -06:00
|
|
|
if first {
|
|
|
|
if box.canBeFocused() {
|
|
|
|
this.focus(box)
|
|
|
|
} else {
|
|
|
|
this.focus(nil)
|
|
|
|
}
|
|
|
|
first = false
|
|
|
|
}
|
2024-07-25 11:01:15 -06:00
|
|
|
boxes = append(boxes, box)
|
|
|
|
return !box.handleMouseDown(button)
|
|
|
|
})
|
|
|
|
this.drags[button] = boxes
|
2024-06-02 11:23:03 -06:00
|
|
|
}
|
|
|
|
|
2024-07-25 11:01:15 -06:00
|
|
|
// HandleMouseUp sends a mouse up event to the Boxes currently being "dragged"
|
|
|
|
// by the specified mouse button, and marks them as being "not dragged" by that
|
|
|
|
// mouse button. If the event which triggers this comes with mouse position
|
2024-06-02 11:23:03 -06:00
|
|
|
// information, HandleMouseMove must be caleld *before* HandleMouseUp
|
|
|
|
func (this *Hierarchy) HandleMouseUp (button input.Button) {
|
2024-07-25 11:01:15 -06:00
|
|
|
for _, box := range this.drags[button] {
|
|
|
|
box.handleMouseUp(button)
|
2024-06-02 11:23:03 -06:00
|
|
|
}
|
2024-07-25 11:01:15 -06:00
|
|
|
this.drags[button] = nil
|
2024-06-02 11:23:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// HandleMouseMove sends a mouse move event to any Boxes currently being
|
2024-07-25 11:01:15 -06:00
|
|
|
// "dragged" by a mouse button. If none are, it sends the event to the Boxes
|
|
|
|
// which are underneath the mouse pointer, starting at the first Box to mask
|
|
|
|
// events and ending at the first box to catch the event.
|
2024-06-02 11:23:03 -06:00
|
|
|
func (this *Hierarchy) HandleMouseMove (position image.Point) {
|
|
|
|
if this.mousePosition == position { return }
|
|
|
|
this.mousePosition = position
|
|
|
|
|
2024-07-25 11:01:15 -06:00
|
|
|
dragged := false
|
|
|
|
for _, dragSet := range this.drags {
|
|
|
|
for _, box := range dragSet {
|
|
|
|
if box.handleMouseMove() { break }
|
2024-07-27 13:13:49 -06:00
|
|
|
dragged = true
|
2024-07-25 11:01:15 -06:00
|
|
|
}
|
2024-06-02 11:23:03 -06:00
|
|
|
}
|
2024-07-25 11:01:15 -06:00
|
|
|
if dragged { return }
|
2024-06-02 11:23:03 -06:00
|
|
|
|
2024-07-25 11:01:15 -06:00
|
|
|
// TODO we can hover over multiple boxes at once. however, any way of
|
|
|
|
// detecting this involves several slice allocations every time we
|
|
|
|
// process a MouseMove event. perhaps we just ought to suck it up and do
|
2024-07-27 13:13:49 -06:00
|
|
|
// it. or perhaps doing *this* is the better way? we may never know.
|
2024-07-25 11:01:15 -06:00
|
|
|
box := this.boxUnder(position)
|
|
|
|
if box != nil {
|
2024-07-27 13:13:49 -06:00
|
|
|
box := this.considerMaskingParents(box)
|
2024-07-25 11:01:15 -06:00
|
|
|
this.hover(box)
|
|
|
|
box.handleMouseMove()
|
2024-06-02 11:23:03 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// HandleScroll sends a scroll event to the Box currently underneath the mouse
|
2024-07-25 11:01:15 -06:00
|
|
|
// cursor. If the event which triggers this comes with mouse position
|
|
|
|
// information, HandleMouseMove must be called *before* HandleScroll.
|
2024-06-02 11:23:03 -06:00
|
|
|
func (this *Hierarchy) HandleScroll (x, y float64) {
|
2024-07-25 11:01:15 -06:00
|
|
|
this.boxesUnder(this.mousePosition)(func (box anyBox) bool {
|
|
|
|
return !box.handleScroll(x, y)
|
|
|
|
})
|
2024-06-02 11:23:03 -06:00
|
|
|
}
|