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) }) }