backend/internal/system/event.go

143 lines
4.8 KiB
Go

package system
import "image"
import "git.tebibyte.media/tomo/tomo/input"
import "git.tebibyte.media/tomo/tomo/config"
// TODO: once go v1.23 comes out, replace the explicit iterator calls here with
// range loops
// 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) {
caught := false
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)
}
}
if caught { return }
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()
}
}
// 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) {
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)
}
}
}
// 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.
func (this *Hierarchy) HandleMouseDown (button input.Button) {
boxes := []anyBox { }
first := true
this.boxesUnder(this.mousePosition)(func (box anyBox) bool {
if first {
if box.canBeFocused() {
this.focus(box)
} else {
this.focus(nil)
}
first = false
}
boxes = append(boxes, box)
return !box.handleMouseDown(button)
})
this.drags[button] = boxes
}
// 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
// information, HandleMouseMove must be caleld *before* HandleMouseUp
func (this *Hierarchy) HandleMouseUp (button input.Button) {
for _, box := range this.drags[button] {
box.handleMouseUp(button)
}
this.drags[button] = nil
}
// HandleMouseMove sends a mouse move event to any Boxes currently being
// "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.
func (this *Hierarchy) HandleMouseMove (position image.Point) {
if this.mousePosition == position { return }
this.mousePosition = position
dragged := false
for _, dragSet := range this.drags {
for _, box := range dragSet {
if box.handleMouseMove() { break }
dragged = true
}
}
if dragged { return }
// 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
// it. or perhaps doing *this* is the better way? we may never know.
box := this.boxUnder(position)
if box != nil {
box := this.considerMaskingParents(box)
this.hover(box)
box.handleMouseMove()
}
}
// HandleScroll sends a scroll event to the Box currently underneath the mouse
// cursor. If the event which triggers this comes with mouse position
// information, HandleMouseMove must be called *before* HandleScroll.
func (this *Hierarchy) HandleScroll (x, y float64) {
this.boxesUnder(this.mousePosition)(func (box anyBox) bool {
return !box.handleScroll(x, y)
})
}