Module: Thaum::Dispatch
- Defined in:
- lib/thaum/dispatch.rb
Overview
Routes an event to the right handler.
Two entry points share the same per-event-type case in #invoke_handler:
{.from_queue} — events popped off the main run-loop queue. Routes by
target (modal vs focused Sigil vs App) and returns
true when the caller should mark the app dirty.
{.from_child} — bubbled events from a Sigil's emit. Routes to the
handler parent (next outer Octagram, or the App).
Class Method Summary collapse
-
.dispatch_modal_mouse(modal:, event:, rect:) ⇒ Object
Route a MouseEvent to the modal Sigil, translating absolute coords to rect-relative.
-
.dispatch_mouse_event(app:, event:) ⇒ Object
Hit-test by absolute coords, set canvas-relative x/y on the event, and dispatch to the hit Sigil’s on_mouse.
-
.from_queue(app:, event:) ⇒ Object
Route one event popped off the main queue.
- .handle_modal_key(app:, modal:, event:) ⇒ Object
-
.invoke_handler(target:, event:, label:) ⇒ Object
Bubbled from a Sigil emit.
- .quit_shortcut?(event) ⇒ Boolean
- .route_key(app:, modal:, event:) ⇒ Object
- .route_mouse(app:, modal:, event:) ⇒ Object
- .route_paste(app:, modal:, event:) ⇒ Object
- .route_tick(app:, modal:, event:) ⇒ Object
Class Method Details
.dispatch_modal_mouse(modal:, event:, rect:) ⇒ Object
Route a MouseEvent to the modal Sigil, translating absolute coords to rect-relative. Out-of-bounds clicks are eaten.
142 143 144 145 146 147 |
# File 'lib/thaum/dispatch.rb', line 142 def dispatch_modal_mouse(modal:, event:, rect:) return unless rect && HitTest.point_in_rect?(x: event.abs_x, y: event.abs_y, rect: rect) local = event.with(x: event.abs_x - rect.x, y: event.abs_y - rect.y) Thaum.safe_invoke("#{modal.class}#on_mouse") { modal.on_mouse(local) } end |
.dispatch_mouse_event(app:, event:) ⇒ Object
Hit-test by absolute coords, set canvas-relative x/y on the event, and dispatch to the hit Sigil’s on_mouse. If no Sigil is hit, dispatch to the App’s on_mouse with abs_x/abs_y unchanged. On :press, transfer focus to the hit Sigil if focusable.
128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/thaum/dispatch.rb', line 128 def dispatch_mouse_event(app:, event:) hit = HitTest.hit(app: app, abs_x: event.abs_x, abs_y: event.abs_y) if hit r = hit.rect localized = event.with(x: event.abs_x - r.x, y: event.abs_y - r.y) Thaum.safe_invoke("App#focus") { app.focus(hit) } if event.action == :press && hit.focusable? Thaum.safe_invoke("#{hit.class}#on_mouse") { hit.on_mouse(localized) } else Thaum.safe_invoke("App#on_mouse") { app.on_mouse(event) } end end |
.from_queue(app:, event:) ⇒ Object
Route one event popped off the main queue. Sets the dirty flag for every routed event that does not opt out (TickEvent and unknown objects are the only opt-outs).
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/thaum/dispatch.rb', line 19 def from_queue(app:, event:) if quit_shortcut?(event) app.quit return end modal = app.modal_sigil auto_dirty = case event when KeyEvent then route_key(app: app, modal: modal, event: event) when PasteEvent then route_paste(app: app, modal: modal, event: event) when MouseEvent then route_mouse(app: app, modal: modal, event: event) when ResizeEvent Thaum.safe_invoke("App#on_resize") { app.on_resize(event) } true when TickEvent route_tick(app: app, modal: modal, event: event) false when Event Thaum.safe_invoke("App#on_event") { app.on_event(event) } true else false end app.request_render if auto_dirty end |
.handle_modal_key(app:, modal:, event:) ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/thaum/dispatch.rb', line 77 def handle_modal_key(app:, modal:, event:) # Plain Escape dismisses the modal — handler never sees it. if event.key == :escape && !event.ctrl? && !event.alt? && !event.shift? app.hide_modal return true end # Tab / Shift-Tab are eaten while a modal is active. return false if event.key == :tab Thaum.safe_invoke("#{modal.class}#on_key") { modal.on_key(event) } true end |
.invoke_handler(target:, event:, label:) ⇒ Object
Bubbled from a Sigil emit. Caller (App or Octagram) supplies its own safe_invoke label so the stderr trace identifies the handler.
49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/thaum/dispatch.rb', line 49 def invoke_handler(target:, event:, label:) Thaum.safe_invoke(label) do case event when KeyEvent then target.on_key(event) when MouseEvent then target.on_mouse(event) when PasteEvent then target.on_paste(event) else target.on_event(event) if event.is_a?(Event) end end end |
.quit_shortcut?(event) ⇒ Boolean
61 62 63 |
# File 'lib/thaum/dispatch.rb', line 61 def quit_shortcut?(event) event.is_a?(KeyEvent) && event.ctrl? && event.key == "c" end |
.route_key(app:, modal:, event:) ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/thaum/dispatch.rb', line 65 def route_key(app:, modal:, event:) return handle_modal_key(app: app, modal: modal, event: event) if modal if (focused = app.focused_sigil) Thaum.safe_invoke("#{focused.class}#on_key") { focused.on_key(event) } else app.handle_tab_cycle(event) if event.key == :tab Thaum.safe_invoke("App#on_key") { app.on_key(event) } end true end |
.route_mouse(app:, modal:, event:) ⇒ Object
104 105 106 107 108 109 110 111 |
# File 'lib/thaum/dispatch.rb', line 104 def route_mouse(app:, modal:, event:) if modal dispatch_modal_mouse(modal: modal, event: event, rect: app.modal_rect) else dispatch_mouse_event(app: app, event: event) end true end |
.route_paste(app:, modal:, event:) ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/thaum/dispatch.rb', line 90 def route_paste(app:, modal:, event:) target = if modal modal elsif (focused = app.focused_sigil) focused else app end label = target.equal?(app) ? "App#on_paste" : "#{target.class}#on_paste" Thaum.safe_invoke(label) { target.on_paste(event) } true end |
.route_tick(app:, modal:, event:) ⇒ Object
113 114 115 116 117 118 119 120 121 122 |
# File 'lib/thaum/dispatch.rb', line 113 def route_tick(app:, modal:, event:) Thaum.safe_invoke("App#on_tick") { app.on_tick(event) } Tree.walk(app) do |node| next unless node.is_a?(Sigil) || node.is_a?(Octagram) Thaum.safe_invoke("#{node.class}#on_tick") { node.on_tick(event) } end # Modal Sigil ticks last, after the layout tree (per spec). Thaum.safe_invoke("#{modal.class}#on_tick") { modal.on_tick(event) } if modal end |