Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
win_plugin_input_router.hpp
Go to the documentation of this file.
1#pragma once
2
3// win_plugin_input_router.hpp — the Windows plug-in editor's pointer/keyboard
4// state machine, lifted out of plugin_view_host_win.cpp (WAH-6).
5//
6// ── Why this header contains no Win32 ────────────────────────────────────────
7//
8// This is the most re-entrancy-sensitive code in the Windows host. Every one of
9// these handlers can be re-entered synchronously from inside itself: `SetFocus`,
10// `SetCapture` and `ReleaseCapture` send messages back through the same wndproc
11// on the same thread, and a widget's gesture callback may pump a nested message
12// loop. The generation/terminal protocol in `PointerSession` exists precisely to
13// survive that, and it is exactly the kind of logic that fails in the field
14// rather than at compile time.
15//
16// It used to be unreachable by any test, because it sat in a `_WIN32`-only TU
17// and Pulp's required CI gate is macOS. So the split here is deliberately NOT
18// "move the code to another Windows file":
19//
20// * the ORDERING and RE-ENTRANCY rules live here, platform-free, and run on
21// every macOS CI job through `test_win_plugin_input_router.cpp`;
22// * the native side effects — `SetCapture`, `SetFocus`, `ReleaseCapture`,
23// `TrackMouseEvent` — are named operations on `InputRouterHost`, which the
24// Windows host implements with the real Win32 calls and a test implements
25// with a recorder;
26// * message DECODING (LPARAM unpack, `MK_*` masks, virtual-key mapping, wheel
27// deltas, the DPI/design-viewport transform) stays in the host, on top of
28// the pure helpers in `win_pointer_input.hpp`.
29//
30// The rule for anything added here: if it needs `<windows.h>`, it belongs in the
31// host or behind an `InputRouterHost` method — not in this file.
32
37#include <pulp/view/ui_components.hpp> // ComboBox::notify_global_click
38#include <pulp/view/view.hpp>
39#include <pulp/runtime/log.hpp>
40
41#include <cstdint>
42#include <exception>
43#include <string>
44
46
56public:
57 virtual ~InputRouterHost() = default;
58
60 virtual View& input_root() noexcept = 0;
61
63 virtual void input_capture_pointer() = 0;
64
68
70 virtual void input_take_keyboard_focus() = 0;
71
76
78 virtual void input_request_repaint() = 0;
79};
80
87inline std::string utf16_code_point_to_utf8(char16_t high, char16_t low) {
88 std::uint32_t cp = 0;
89 if (low != 0) {
90 const bool valid_pair = high >= 0xD800 && high <= 0xDBFF &&
91 low >= 0xDC00 && low <= 0xDFFF;
92 if (!valid_pair) return {};
93 cp = 0x10000u + ((static_cast<std::uint32_t>(high) - 0xD800u) << 10) +
94 (static_cast<std::uint32_t>(low) - 0xDC00u);
95 } else {
96 // A lone surrogate is not a code point; dropping it is correct and
97 // keeps invalid UTF-8 out of the text channel.
98 if (high >= 0xD800 && high <= 0xDFFF) return {};
99 cp = high;
100 }
101
102 std::string out;
103 if (cp < 0x80) {
104 out += static_cast<char>(cp);
105 } else if (cp < 0x800) {
106 out += static_cast<char>(0xC0u | (cp >> 6));
107 out += static_cast<char>(0x80u | (cp & 0x3Fu));
108 } else if (cp < 0x10000) {
109 out += static_cast<char>(0xE0u | (cp >> 12));
110 out += static_cast<char>(0x80u | ((cp >> 6) & 0x3Fu));
111 out += static_cast<char>(0x80u | (cp & 0x3Fu));
112 } else {
113 out += static_cast<char>(0xF0u | (cp >> 18));
114 out += static_cast<char>(0x80u | ((cp >> 12) & 0x3Fu));
115 out += static_cast<char>(0x80u | ((cp >> 6) & 0x3Fu));
116 out += static_cast<char>(0x80u | (cp & 0x3Fu));
117 }
118 return out;
119}
120
132public:
133 explicit PluginInputRouter(InputRouterHost& host) noexcept : host_(host) {}
134
137
138 // ── Pointer ─────────────────────────────────────────────────────────────
139
140 void on_mouse_down(Point pt, MouseButton button, std::uint16_t modifiers) {
141 std::uint64_t generation = 0;
142 try {
143 // This host owns one capture bracket. Close a prior button before
144 // accepting a chord mate instead of overwriting its target.
145 if (session_.active()) cancel_gesture();
146 last_point_ = pt;
147 View& root = host_.input_root();
148 // Native ComboBox menus are paint-only overlays. They have first
149 // refusal, matching the macOS host: consulting the generalized
150 // overlay first could dismiss it (and run arbitrary callbacks)
151 // before a click visibly inside this independent popup arrives.
152 if (button == MouseButton::left &&
153 route_press_to_open_combo(root, pt, modifiers)) {
154 host_.input_request_repaint();
155 return;
156 }
157 if (!session_.begin(button)) return;
158 generation = session_.generation();
159 const auto accepts_original = [this, generation, button] {
160 return session_.accepts(generation, button);
161 };
162 // Outside presses must dismiss this editor's generalized overlay
163 // even when a gesture recognizer consumes the press below.
164 const auto overlay_press = route_press_to_active_overlay(root, pt);
165 // Outside-dismiss callbacks are arbitrary application code and may
166 // synchronously replace the pointer bracket. A stale outer frame
167 // must not feed its press into the replacement's gesture session.
168 if (!accepts_original()) return;
169 if (overlay_press.consume_press) {
171 return;
172 }
173 if (overlay_press.routing == OverlayPressRouting::routed)
174 drag_target_.set(overlay_press.target);
175 MouseEvent gesture_event;
176 gesture_event.position = pt;
177 gesture_event.window_position = pt;
178 gesture_event.button = button;
179 gesture_event.modifiers = modifiers;
180 gesture_event.is_down = true;
181 gesture_event.phase = MousePhase::press;
182 // Claimed overlays paint in a separate top layer, so the ordinary
183 // gesture arbiter's root hit-test can resolve a later sibling
184 // underneath. Match the standalone/mac overlay path: deliver the
185 // resolved overlay press directly rather than offering it to an
186 // unrelated underlay recognizer.
187 if (overlay_press.routing != OverlayPressRouting::routed &&
188 yield_to_gesture(gesture_event)) {
189 // A synchronous gesture callback may have terminalized this
190 // bracket. Capture only for the still-current claimed session.
191 if (accepts_original()) host_.input_capture_pointer();
192 return;
193 }
194 if (!accepts_original()) return;
195
196 // Consult the generalized overlay slot before the regular hit
197 // test, so a React / imported-design popover both receives clicks
198 // aimed at it and is dismissed by a click outside it. This host
199 // previously handled only the native ComboBox mechanism below,
200 // which left such a popover open forever.
201 if (overlay_press.routing != OverlayPressRouting::routed)
202 drag_target_.set(root.hit_test(pt));
203 View* drag_target = drag_target_.live_in(root);
204 if (button == MouseButton::left)
206 if (!accepts_original()) return;
207 drag_target = drag_target_.live_in(root);
208 if (!drag_target) {
210 return;
211 }
212 host_.input_take_keyboard_focus();
213 if (!accepts_original()) return;
214 drag_target = drag_target_.live_in(root);
215 if (!drag_target) {
217 return;
218 }
219 if (!transfer_input_focus(root, drag_target)) {
220 if (accepts_original()) {
221 drag_target_.reset();
223 }
224 return;
225 }
226 if (!accepts_original()) return;
227 host_.input_capture_pointer();
228 if (!accepts_original()) return;
229 MouseDownHost down_host;
230 down_host.should_continue = accepts_original;
231 const bool target_alive =
232 deliver_mouse_down(root, drag_target_.live_in(root), pt, modifiers, 1, true,
233 button, down_host);
234 // Only this generation may mutate its captured target. A modern
235 // callback may have synchronously cancelled/replaced the session.
236 if (!accepts_original()) return;
237 if (!target_alive) drag_target_.reset();
238 if (button == MouseButton::right)
239 dispatch_context_menu(root, drag_target_.live_in(root), pt);
240 if (!accepts_original()) return;
241 host_.input_request_repaint();
242 } catch (const std::exception& e) {
243 runtime::log_warn("WinPluginInputRouter: mouse down handler threw: {}",
244 e.what());
245 if (session_.accepts(generation, button)) cancel_gesture();
246 } catch (...) {
247 runtime::log_warn("WinPluginInputRouter: mouse down handler threw");
248 if (session_.accepts(generation, button)) cancel_gesture();
249 }
250 }
251
255 void on_mouse_move(Point pt, std::uint32_t button_mask,
256 std::uint16_t modifiers) {
257 try {
258 last_point_ = pt;
259 if (!tracking_leave_)
260 tracking_leave_ = host_.input_begin_mouse_leave_tracking();
261 View& root = host_.input_root();
262 const bool held = drag_continues(button_mask, session_.button());
263 if (session_.active() && !held) cancel_gesture();
264 if (!session_.active()) {
265 root.simulate_hover(pt);
266 host_.input_request_repaint();
267 return;
268 }
269 MouseEvent gesture_event;
270 gesture_event.position = pt;
271 gesture_event.window_position = pt;
272 gesture_event.button = session_.button();
273 gesture_event.modifiers = modifiers;
274 gesture_event.is_down = true;
275 gesture_event.phase = MousePhase::drag;
276 if (!drag_target_.live_in(root)) drag_target_.reset();
277 const bool gesture_yielded = yield_to_gesture(gesture_event);
278 if (!gesture_yielded) {
279 View* target = drag_target_.live_in(root);
280 if (target)
281 deliver_mouse_drag(root, target, pt, modifiers, 1,
282 PointerType::mouse, 0.5f,
283 session_.button());
284 }
285 host_.input_request_repaint();
286 } catch (const std::exception& e) {
287 runtime::log_warn("WinPluginInputRouter: mouse move handler threw: {}",
288 e.what());
290 } catch (...) {
291 runtime::log_warn("WinPluginInputRouter: mouse move handler threw");
293 }
294 }
295
296 void on_mouse_up(Point pt, MouseButton button, std::uint16_t modifiers) {
297 if (!session_.accepts(button)) return;
298 try {
299 // Publish terminal before gesture/raw callbacks. Those callbacks
300 // can synchronously pump capture-change or another button-up.
301 const auto terminal = session_.terminalize();
302 last_point_ = pt;
303 View& root = host_.input_root();
304 if (!drag_target_.live_in(root)) drag_target_.reset();
305 MouseEvent gesture_event;
306 gesture_event.position = pt;
307 gesture_event.window_position = pt;
308 gesture_event.button = button;
309 gesture_event.modifiers = modifiers;
310 gesture_event.is_down = false;
311 gesture_event.phase = MousePhase::release;
312 const bool gesture_yielded = should_yield_to_gesture(root, gesture_event);
313 // A callback above may have synchronously cancelled this generation
314 // (and closed drag_target_). Never resume the stale outer release.
315 if (session_.phase() != PointerSession::Phase::terminal ||
316 session_.generation() != terminal.generation)
317 return;
318 View* target = drag_target_.live_in(root);
319 drag_target_.reset();
320 if (gesture_yielded) {
321 if (target)
322 deliver_gesture_handoff(root, target, pt, modifiers, 1);
323 } else if (target) {
324 MouseUpHost up_host;
325 if (button == MouseButton::left) {
326 up_host.fire_click =
327 [](const std::function<void()>& click_handler,
328 const std::string&, std::uint16_t) {
329 if (click_handler) click_handler();
330 };
331 }
332 deliver_mouse_up(root, target, pt, modifiers, 1, up_host, button);
333 }
334 if (session_.phase() == PointerSession::Phase::terminal &&
335 session_.generation() == terminal.generation) {
336 host_.input_release_pointer_capture();
337 session_.finish_terminal(terminal);
338 }
339 host_.input_request_repaint();
340 } catch (const std::exception& e) {
341 runtime::log_warn("WinPluginInputRouter: mouse up handler threw: {}",
342 e.what());
344 } catch (...) {
345 runtime::log_warn("WinPluginInputRouter: mouse up handler threw");
347 }
348 }
349
350 void on_capture_lost() noexcept {
351 // WM_CAPTURECHANGED/WM_CANCELMODE can arrive without a matching button
352 // up (host modal UI, Alt+Tab, another HWND taking capture). End the
353 // gesture explicitly so value widgets and the gesture arbiter cannot
354 // remain latched in a drag until the editor is reopened.
356 }
357
359 tracking_leave_ = false;
360 if (session_.active())
361 return; // capture keeps a drag alive outside the window
362 host_.input_root().simulate_hover({-1000000.0f, -1000000.0f});
363 host_.input_request_repaint();
364 }
365
366 void on_mouse_wheel(Point pt, float dx, float dy,
367 std::uint16_t modifiers = 0) {
368 WheelHost wheel_host;
369 wheel_host.request_repaint = [this] { host_.input_request_repaint(); };
370 deliver_mouse_wheel(host_.input_root(), pt, dx, dy, modifiers,
371 wheel_host);
372 }
373
374 // ── Keyboard and text ───────────────────────────────────────────────────
375
376 bool on_key(KeyCode key, std::uint16_t modifiers, bool is_down,
377 bool is_repeat) {
378 // Escape first, before the focus gate. A plugin editor returns the
379 // keyboard to the DAW whenever nothing in its tree holds focus — the
380 // ordinary state while a `<View overlay>` popover is open — so an
381 // Escape path placed after the early-out below can never run, and such
382 // a popover had no keyboard dismissal inside a DAW at all. The
383 // ordering (modal, open ComboBox dropdown, generalized overlay) is the
384 // shared policy's, not this host's.
385 if (is_down && key == KeyCode::escape &&
386 route_escape_to_active_overlay(host_.input_root(), modifiers,
387 is_repeat) !=
389 host_.input_request_repaint();
390 return true;
391 }
392 auto* focused = focused_input_under_root(host_.input_root());
393 if (!focused) return false;
394 if (key == KeyCode::unknown) return false;
395 KeyEvent event;
396 event.key = key;
397 event.modifiers = modifiers;
398 event.is_down = is_down;
399 event.is_repeat = is_repeat;
400 bool consumed = false;
401 if (focused->accepts_text_input()) {
402 consumed = focused->on_key_event(event);
403 } else if (focused->accepts_navigation_input()) {
404 constexpr std::uint16_t kHostChordModifiers =
406 const bool allowed = (modifiers & kHostChordModifiers) == 0 &&
407 (key == KeyCode::left || key == KeyCode::right ||
408 key == KeyCode::up || key == KeyCode::down ||
409 key == KeyCode::home || key == KeyCode::end_ ||
410 key == KeyCode::enter || key == KeyCode::escape);
411 if (!allowed) return false;
412 consumed = focused->on_key_event(event);
413 if (!consumed) {
415 host_.input_root(), static_cast<int>(key), modifiers, is_down);
416 }
417 } else {
418 return false;
419 }
420 if (consumed) host_.input_request_repaint();
421 return consumed;
422 }
423
427 bool on_text_unit(char16_t unit) {
428 auto* focused = focused_input_under_root(host_.input_root());
429 if (!focused || !focused->accepts_text_input()) return false;
430 // Editing/navigation controls are already delivered through KeyEvent.
431 // WM_CHAR repeats them as C0 code units; inserting those into the text
432 // channel would double-handle Backspace/Tab/Return/Escape.
433 if (unit < 0x20) return true;
434
435 std::string text;
436 if (unit >= 0xD800 && unit <= 0xDBFF) {
437 // Hold the high half; the low half arrives in the next WM_CHAR.
438 pending_high_surrogate_ = unit;
439 return true;
440 }
441 if (unit >= 0xDC00 && unit <= 0xDFFF && pending_high_surrogate_ != 0) {
442 text = utf16_code_point_to_utf8(pending_high_surrogate_, unit);
443 } else {
444 text = utf16_code_point_to_utf8(unit, 0);
445 }
446 pending_high_surrogate_ = 0;
447 if (text.empty()) return true;
448 focused->on_text_input(TextInputEvent{.text = text});
449 host_.input_request_repaint();
450 return true;
451 }
452
453 void on_focus_changed(bool gained) {
454 if (gained) return;
455 // A half-typed surrogate pair cannot be completed by a different
456 // window's keystrokes, so drop it rather than pairing it with whatever
457 // arrives after focus returns.
458 pending_high_surrogate_ = 0;
459 View& root = host_.input_root();
460 if (focused_input_under_root(root)) {
461 transfer_input_focus(root, nullptr);
462 host_.input_request_repaint();
463 }
464 }
465
466 // ── Teardown ────────────────────────────────────────────────────────────
467
471 void cancel_gesture() noexcept {
472 const auto terminal = session_.terminalize();
473 try {
474 View& root = host_.input_root();
475 // Unpublish the cancelled generation before any gesture/user
476 // callback. PointerSession remains terminal until those callbacks
477 // return, so a nested button-down cannot overwrite this bracket;
478 // the local capture also survives removal/replacement of the view.
479 ViewCapture cancelled_target = drag_target_;
480 drag_target_.reset();
481 View* target = cancelled_target.live_in(root);
482 if (!target && !terminal.cancel_gesture && !terminal.owns_terminal)
483 return;
484 if (terminal.cancel_gesture) {
485 MouseEvent event;
486 event.position = last_point_;
487 event.window_position = last_point_;
488 event.button = terminal.button;
489 event.is_down = false;
490 event.phase = MousePhase::release;
491 event.is_cancelled = true;
493 }
494 target = cancelled_target.live_in(root);
495 if (target) {
496 deliver_mouse_cancel(root, target, last_point_, 0, 1,
497 terminal.button);
498 }
499 } catch (const std::exception& e) {
500 runtime::log_warn("WinPluginInputRouter: pointer cancellation threw: {}",
501 e.what());
502 } catch (...) {
503 runtime::log_warn("WinPluginInputRouter: pointer cancellation threw");
504 }
505 if (terminal.owns_terminal &&
506 session_.phase() == PointerSession::Phase::terminal &&
507 session_.generation() == terminal.generation) {
508 host_.input_release_pointer_capture();
509 session_.finish_terminal(terminal);
510 }
511 host_.input_request_repaint();
512 }
513
514 // ── Observation (host + tests) ──────────────────────────────────────────
515
516 bool gesture_active() const noexcept { return session_.active(); }
517 MouseButton gesture_button() const noexcept { return session_.button(); }
518 bool has_captured_target() const noexcept {
519 return drag_target_.has_value();
520 }
521 const View* captured_target() const noexcept {
522 return drag_target_.live_in(host_.input_root());
523 }
524 Point last_point() const noexcept { return last_point_; }
525 bool tracking_mouse_leave() const noexcept { return tracking_leave_; }
526 bool has_pending_high_surrogate() const noexcept {
527 return pending_high_surrogate_ != 0;
528 }
529
530private:
531 static bool route_press_to_open_combo(View& root, Point pt,
532 std::uint16_t modifiers) {
533 auto* combo = ComboBox::active_popup_in(root);
534 if (!combo) return false;
535 float x = 0, y = 0, width = 0, height = 0;
536 if (!combo->dropdown_window_rect(x, y, width, height) ||
537 pt.x < x || pt.x > x + width ||
538 pt.y < y || pt.y > y + height)
539 return false;
540 MouseEvent event;
541 event.position = point_to_local(pt, combo, &root);
542 event.window_position = pt;
543 event.button = MouseButton::left;
544 event.modifiers = modifiers;
545 event.is_down = true;
546 event.phase = MousePhase::press;
547 combo->on_mouse_event(event);
548 return true;
549 }
550
553 bool yield_to_gesture(const MouseEvent& event) {
554 if (event.button != MouseButton::left) return false;
555 View& root = host_.input_root();
556 const std::uint64_t generation = session_.generation();
557 const bool yielded = should_yield_to_gesture(root, event);
558 // Gesture callbacks may pump a nested message. Stop this outer frame
559 // if that message terminalized/replaced the session; never stamp its
560 // result onto the newer generation.
561 if (session_.generation() != generation || !session_.active())
562 return true;
563 if (!yielded) return false;
564
565 // Publish claimed and clear raw delivery BEFORE synchronous handoff.
566 session_.mark_claimed();
567 View* handoff_target = drag_target_.live_in(root);
568 drag_target_.reset();
569 if (event.phase != MousePhase::press)
570 deliver_gesture_handoff(root, handoff_target, event.window_position,
571 event.modifiers, 1);
572 return true;
573 }
574
575 InputRouterHost& host_;
576 PointerSession session_;
577 ViewCapture drag_target_;
578 Point last_point_{};
579 bool tracking_leave_ = false;
580 char16_t pending_high_surrogate_ = 0;
581};
582
583} // namespace pulp::view::win_input
static void notify_global_click(View *target)
Called by the root view on any mouse click — closes popup if click is outside.
static ComboBox * active_popup_in(View &scope)
The open dropdown that belongs to scope's own tree, or nullptr.
Identity-safe reference to a View captured across native event boundaries.
Definition view.hpp:56
View * live_in(View &root) const noexcept
void reset() noexcept
Definition view.hpp:73
bool dispatch_gesture_pointer_event(const MouseEvent &root_event, double timestamp_seconds=-1.0)
void simulate_hover(Point root_pos)
virtual View * hit_test(Point local_point)
The native operations the router needs but must not perform itself.
Definition win_plugin_input_router.hpp:55
virtual void input_take_keyboard_focus()=0
SetFocus(hwnd) — take native keyboard focus for the editor window.
virtual void input_release_pointer_capture()=0
ReleaseCapture(), but ONLY if this host currently holds it.
virtual void input_request_repaint()=0
Coalesced repaint request, called after any input that can change pixels.
virtual void input_capture_pointer()=0
SetCapture(hwnd) — route pointer input here until capture is released.
virtual bool input_begin_mouse_leave_tracking()=0
TrackMouseEvent(TME_LEAVE).
virtual View & input_root() noexcept=0
Root of the view tree events are dispatched into.
bool tracking_mouse_leave() const noexcept
Definition win_plugin_input_router.hpp:525
void on_mouse_up(Point pt, MouseButton button, std::uint16_t modifiers)
Definition win_plugin_input_router.hpp:296
bool on_key(KeyCode key, std::uint16_t modifiers, bool is_down, bool is_repeat)
Definition win_plugin_input_router.hpp:376
void on_mouse_down(Point pt, MouseButton button, std::uint16_t modifiers)
Definition win_plugin_input_router.hpp:140
bool has_captured_target() const noexcept
Definition win_plugin_input_router.hpp:518
PluginInputRouter(const PluginInputRouter &)=delete
bool has_pending_high_surrogate() const noexcept
Definition win_plugin_input_router.hpp:526
void on_focus_changed(bool gained)
Definition win_plugin_input_router.hpp:453
Point last_point() const noexcept
Definition win_plugin_input_router.hpp:524
bool on_text_unit(char16_t unit)
One UTF-16 code unit from WM_CHAR.
Definition win_plugin_input_router.hpp:427
PluginInputRouter(InputRouterHost &host) noexcept
Definition win_plugin_input_router.hpp:133
void on_mouse_wheel(Point pt, float dx, float dy, std::uint16_t modifiers=0)
Definition win_plugin_input_router.hpp:366
const View * captured_target() const noexcept
Definition win_plugin_input_router.hpp:521
MouseButton gesture_button() const noexcept
Definition win_plugin_input_router.hpp:517
void cancel_gesture() noexcept
End any live gesture, delivering the cancellation the widgets need.
Definition win_plugin_input_router.hpp:471
PluginInputRouter & operator=(const PluginInputRouter &)=delete
void on_mouse_leave()
Definition win_plugin_input_router.hpp:358
bool gesture_active() const noexcept
Definition win_plugin_input_router.hpp:516
void on_mouse_move(Point pt, std::uint32_t button_mask, std::uint16_t modifiers)
button_mask is the raw MK_* word from the move message; the router asks drag_continues() whether the ...
Definition win_plugin_input_router.hpp:255
void on_capture_lost() noexcept
Definition win_plugin_input_router.hpp:350
@ terminal
Definition win_pointer_input.hpp:33
Bounded event contracts shared by host ingress and graph-authored nodes.
Definition anticipation_eligibility.hpp:9
void log_warn(std::format_string< Args... > fmt, Args &&... args)
Definition log.hpp:190
bool dispatch_key_for_root(View &root, int key_code, uint16_t modifiers, bool is_down)
Definition win_plugin_input_router.hpp:45
constexpr uint32_t button_mask(MouseButton button) noexcept
Definition win_pointer_input.hpp:204
constexpr bool drag_continues(uint32_t wparam) noexcept
Definition win_pointer_input.hpp:200
std::string utf16_code_point_to_utf8(char16_t high, char16_t low)
Encode one Unicode code point, supplied as one or two UTF-16 code units, as UTF-8.
Definition win_plugin_input_router.hpp:87
bool transfer_input_focus(View &root, View *target)
Move input focus within one hosted root while tolerating callbacks that synchronously unmount either ...
void deliver_mouse_drag(View &root, View *target, Point root_pt, uint16_t modifiers, int click_count=1, PointerType pointer_type=PointerType::mouse, float pressure=0.5f)
Deliver one drag tick of an in-flight gesture to the captured target.
View * focused_input_under_root(View &root)
Return this root's live focused input.
@ kModCmd
Command (macOS) — alias for platform primary modifier.
Definition input_events.hpp:21
@ kModMeta
Windows key / Command key.
Definition input_events.hpp:20
@ kModAlt
Alt / Option.
Definition input_events.hpp:19
@ kModCtrl
Control key (Ctrl on Windows/Linux).
Definition input_events.hpp:18
bool should_yield_to_gesture(View &root, const MouseEvent &event)
Feed one pointer event to root's gesture arbiter and report whether the host must now YIELD — i....
@ routed
The press landed inside the overlay and resolved to a view in its subtree.
Definition overlay_dismissal.hpp:67
@ none
Nothing was open under root.
Definition overlay_dismissal.hpp:257
bool dispatch_context_menu(View &root, Point root_pos)
Route a right-click at root_pos to the view under it.
MouseButton
Mouse button identifiers.
Definition input_events.hpp:67
@ right
Definition input_events.hpp:70
@ left
Definition input_events.hpp:69
void deliver_mouse_up(View &root, View *target, Point root_pt, uint16_t modifiers, int click_count, const MouseUpHost &host)
Deliver a release for an in-flight gesture captured on target.
@ mouse
Mouse cursor (default).
Definition input_events.hpp:76
OverlayEscapeResult route_escape_to_active_overlay(View &root, std::uint16_t modifiers=0, bool is_repeat=false)
Dismiss the topmost thing Escape should close under root.
bool deliver_mouse_down(View &root, View *target, Point root_pt, uint16_t modifiers, int click_count=1, bool bubble=true)
Deliver a press to an already-resolved target (the host has done the hit_test, any combo/overlay pre-...
void deliver_gesture_handoff(View &root, View *target, Point root_pt, uint16_t modifiers, int click_count)
Close the press bracket when a recognizer CLAIMS the pointer mid-gesture, after a press was already d...
KeyCode
Definition input_events.hpp:35
@ home
Definition input_events.hpp:54
@ y
Definition input_events.hpp:42
@ up
Definition input_events.hpp:53
@ down
Definition input_events.hpp:53
@ right
Definition input_events.hpp:53
@ left
Definition input_events.hpp:53
@ x
Definition input_events.hpp:41
@ end_
Definition input_events.hpp:54
@ unknown
Definition input_events.hpp:36
@ escape
Definition input_events.hpp:57
@ enter
Definition input_events.hpp:57
void deliver_mouse_wheel(View &root, Point root_pt, float scroll_delta_x, float scroll_delta_y, const WheelHost &host)
Route one wheel/scroll tick to the view tree rooted at root.
@ height
Definition css_animation.hpp:128
Point point_to_local(Point root_pos, View *target, View *root)
Convert root_pos (root-local, i.e.
@ release
Button-up that ends a gesture.
Definition input_events.hpp:98
@ press
Initial button-down that begins a gesture.
Definition input_events.hpp:96
@ drag
Pointer moved with the button held (a drag tick).
Definition input_events.hpp:97
OverlayPressTarget route_press_to_active_overlay(View &root, Point root_pt)
Consult root's generalized overlay slot for a press at root_pt.
void deliver_mouse_cancel(View &root, View *target, Point root_pt, uint16_t modifiers, int click_count, MouseButton button, const PointerAttributes &pointer={})
Cancel an in-flight gesture through the same identity-safe terminal router as deliver_mouse_up.
STL namespace.
The one implementation of Pulp's overlay-dismissal policy.
Rich key event with key code, modifiers, and repeat state.
Definition input_events.hpp:193
KeyCode key
Definition input_events.hpp:194
Optional host-owned continuation check for re-entrant press delivery.
Definition pointer_dispatch.hpp:279
std::function< bool()> should_continue
Definition pointer_dispatch.hpp:280
Rich mouse/pointer event with position, modifiers, button state, pointer ID, and stylus properties.
Definition input_events.hpp:104
bool is_down
True for mouse-down / touch-began events.
Definition input_events.hpp:111
Point window_position
Position in window coordinates.
Definition input_events.hpp:106
Point position
Position in local view coordinates.
Definition input_events.hpp:105
uint16_t modifiers
Bitfield of Modifier flags.
Definition input_events.hpp:108
MouseButton button
Definition input_events.hpp:107
MousePhase phase
Explicit gesture phase (see MousePhase).
Definition input_events.hpp:112
Host hooks the portable mouse-up router calls back into.
Definition pointer_dispatch.hpp:301
std::function< void(const std::function< void()> &click_handler, const std::string &clicked_id, uint16_t modifiers)> fire_click
Invoked exactly when the release lands on the SAME view the press captured (released == target,...
Definition pointer_dispatch.hpp:319
Definition geometry.hpp:20
float x
Definition geometry.hpp:21
float y
Definition geometry.hpp:21
Text input from IME or keyboard composition (separate from KeyEvent).
Definition input_events.hpp:210
Host hooks the portable wheel router calls back into.
Definition pointer_dispatch.hpp:199
std::function< void()> request_repaint
Invoked after a wheel event mutates the tree (a scroll pane scrolled, a value widget stepped,...
Definition pointer_dispatch.hpp:205
Additional UI components: ComboBox, Tooltip, ProgressBar, CallOutBox, TabPanel, ListBox,...