MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Context.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "Layer.hpp"
4
6
7namespace MayaFlux::Core {
8class Window;
9}
10
11namespace MayaFlux::Vruta {
12class EventManager;
13class WindowEventSource;
14}
15
17
18template <typename T>
19struct MappedState;
20
21/**
22 * @class Context
23 * @brief Event wiring between a Layer and a window surface.
24 *
25 * Subscribes to mouse events on a window by constructing Kriya input
26 * coroutines and registering them with a Vruta::EventManager, following
27 * the same pattern as Nexus::Fabric. The EventManager and Window are
28 * injected; Context holds no engine-level singletons.
29 *
30 * Callbacks fire on the graphics thread, from window polling at the end of
31 * each frame, after that frame's Bridge tasks and buffer processing have run.
32 * They may write a MappedState directly, since it is the same thread, and the
33 * value is read by the next frame. They must not touch GPU resources directly.
34 *
35 * All registered events are cancelled on destruction via
36 * EventManager::cancel_event, keyed by names scoped to this Context.
37 *
38 * Window dimensions are read from window->get_state() at event time,
39 * so resize is handled automatically.
40 */
41class MAYAFLUX_API Context {
42public:
43 using PressFn = std::function<void(uint32_t id, glm::vec2 ndc)>;
44 using MoveFn = std::function<void(uint32_t id, glm::vec2 ndc)>;
45 using EnterFn = std::function<void(uint32_t id)>;
46 using LeaveFn = std::function<void(uint32_t id)>;
47 using ScrollFn = std::function<void(uint32_t id, glm::vec2 ndc, double dx, double dy)>;
48 using KeyFn = std::function<void(uint32_t id)>;
49 using TextFn = std::function<void(uint32_t id, uint32_t codepoint)>;
50 using ResizeFn = std::function<void(uint32_t width, uint32_t height)>;
51 using CloseFn = std::function<void()>;
52
53 /**
54 * @brief Construct and immediately register event coroutines.
55 * @param layer Layer to hit-test against. Must outlive Context.
56 * @param window Window whose surface the layer lives on.
57 * @param event_manager Engine EventManager for coroutine registration.
58 * @param name Unique name scoping all registered event handlers.
59 * Must be unique across all live Contexts.
60 */
61 Context(std::shared_ptr<Layer> layer,
62 std::shared_ptr<Core::Window> window,
63 Vruta::EventManager& event_manager,
64 std::string name);
65
66 /**
67 * @brief Cancel all registered event coroutines.
68 */
69 ~Context();
70
71 Context(const Context&) = delete;
72 Context& operator=(const Context&) = delete;
73 Context(Context&&) = delete;
75
76 // =========================================================================
77 // Per-element callbacks
78 // =========================================================================
79
80 /**
81 * @brief Called when a mouse button is pressed over an element.
82 * @param id Element id to bind to.
83 * @param btn Mouse button.
84 * @param fn Callback receiving element id and NDC press position.
85 */
86 void on_press(uint32_t id, IO::MouseButtons btn, PressFn fn);
87
88 /**
89 * @brief Called when a mouse button is released over an element.
90 */
91 void on_release(uint32_t id, IO::MouseButtons btn, PressFn fn);
92
93 /**
94 * @brief Called each move event while the cursor is over an element.
95 */
96 void on_move(uint32_t id, MoveFn fn);
97
98 /**
99 * @brief Called on each mouse-move event while @p btn is held, tracking
100 * the element where the drag began even when the cursor leaves its bounds.
101 *
102 * Drag tracking begins when the element is first hit (either at initial press
103 * or first drag motion while button is held). Once tracking starts, the callback
104 * continues to fire with the current cursor position until the button is released,
105 * regardless of whether the cursor remains over the element.
106 *
107 * This matches standard UI expectations for sliders, scrollbars, resize handles,
108 * and other controls that must respond continuously during a drag gesture.
109 *
110 * Driven by the same motion event as on_move, for every button the
111 * window's event source reports as held.
112 *
113 * @param id Element id to bind to.
114 * @param btn Mouse button that must be held.
115 * @param fn Callback receiving element id and current NDC cursor position
116 * (which may be outside the element's bounds during drag).
117 */
118 void on_drag(uint32_t id, IO::MouseButtons btn, MoveFn fn);
119
120 /**
121 * @brief Called once when the cursor enters an element's region.
122 */
123 void on_enter(uint32_t id, EnterFn fn);
124
125 /**
126 * @brief Called once when the cursor leaves an element's region.
127 */
128 void on_leave(uint32_t id, LeaveFn fn);
129
130 /**
131 * @brief Called on scroll while the cursor is over an element.
132 */
133 void on_scroll(uint32_t id, ScrollFn fn);
134
135 /**
136 * @brief Remove all callbacks registered for an element id.
137 *
138 * Also clears m_hovered/m_dragging entries pointing at @p id, so a
139 * removed element cannot be left as the stale current hover or drag
140 * target.
141 */
142 void unbind(uint32_t id);
143
144 /**
145 * @brief Called when a key is pressed while the element has focus.
146 *
147 * Focus is transferred on mouse press. The callback fires only once per
148 * key press, even if the key is held.
149 *
150 * @param id Element id to bind to.
151 * @param key The key to listen for.
152 * @param fn Callback receiving element id.
153 */
154 void on_press(uint32_t id, IO::Keys key, KeyFn fn);
155
156 /**
157 * @brief Called when a key is released while the element has focus.
158 *
159 * @param id Element id to bind to.
160 * @param key The key to listen for.
161 * @param fn Callback receiving element id.
162 */
163 void on_release(uint32_t id, IO::Keys key, KeyFn fn);
164
165 /**
166 * @brief Called repeatedly while a key is held and the element has focus.
167 *
168 * Fires on initial press and continues on each repeat tick until the key
169 * is released. Useful for continuous adjustments (arrow key nudging,
170 * value increments) without requiring the user to repeatedly press.
171 *
172 * @param id Element id to bind to.
173 * @param key The key to listen for.
174 * @param fn Callback receiving element id, fired on press and each repeat.
175 */
176 void on_held(uint32_t id, IO::Keys key, KeyFn fn);
177
178 /**
179 * @brief Called with each resolved Unicode codepoint while the element
180 * has keyboard focus.
181 *
182 * Delivers composed text (shift, layout, dead-key/compose, IME already
183 * resolved by the backend), distinct from on_press/on_held's raw keycodes.
184 * Focus transfers on mouse press, same as the other keyboard callbacks.
185 *
186 * @param id Element id to bind to.
187 * @param fn Callback receiving element id and the resolved codepoint.
188 */
189 void on_text(uint32_t id, TextFn fn);
190
191 /**
192 * @brief Called once when an element gains keyboard focus (via click).
193 */
194 void on_focus_gained(uint32_t id, EnterFn fn);
195
196 /**
197 * @brief Called once when an element loses keyboard focus.
198 */
199 void on_focus_lost(uint32_t id, LeaveFn fn);
200
201 /**
202 * @brief Clear keyboard focus (no element focused).
203 */
204 void clear_focus();
205
206 /**
207 * @brief Called on every window resize.
208 *
209 * Per-element like the other callbacks, so more than one bound id (e.g.
210 * several pixel-anchored TextFields on the same Context) can each react
211 * independently. Cleared by unbind(id) same as every other callback.
212 *
213 * Hit-test-driven placement (to_ndc() at event time) already tracks
214 * resize for free; this is for callers anchored by a fixed pixel rect
215 * via to_ndc_rect(), which must be recomputed explicitly when the
216 * window's dimensions change.
217 *
218 * @param id Element id to bind to.
219 * @param fn Callback receiving the new (width, height).
220 */
221 void on_resize(uint32_t id, ResizeFn fn);
222
223 /**
224 * @brief Run the resize callbacks if the window's size differs from the
225 * size they last saw.
226 *
227 * A window can reach its real size after the callbacks were registered,
228 * for instance when the compositor answers the requested size with
229 * another, and the resize event that carried it may predate them. Input
230 * events call this first, so the callbacks catch up on the first pointer
231 * or key event; call it directly to catch up sooner. A no-op when the
232 * size has not changed.
233 */
234 void sync_size();
235
236 /**
237 * @brief Get currently focused element, if any.
238 */
239 [[nodiscard]] std::optional<uint32_t> focused() const { return m_focused; }
240
241 /**
242 * @brief Register a callback invoked when the Context window closes.
243 * @param id Element id associated with the callback.
244 * @param fn Callback invoked during close handling.
245 */
246 void on_close(uint32_t id, CloseFn fn);
247
248 /**
249 * @brief Attach key-delta handlers to a Mapped<float> element.
250 *
251 * Binds key handlers that adjust the element's state by a delta on each
252 * press/hold. The element must be focused (via mouse click) for keys to fire.
253 *
254 * @param id Element id.
255 * @param state MappedState to adjust.
256 * @param decrease Key that decreases the value.
257 * @param increase Key that increases the value.
258 * @param delta Step size per key event.
259 * @param clamp_min Minimum clamped value (default 0.0F).
260 * @param clamp_max Maximum clamped value (default 1.0F).
261 * @return *this for chaining.
262 */
263 Context& key_step(
264 uint32_t id,
265 std::shared_ptr<MappedState<float>> state,
266 IO::Keys decrease,
267 IO::Keys increase,
268 float delta,
269 float clamp_min = 0.0F,
270 float clamp_max = 1.0F);
271
272 /**
273 * @brief Register a key handler that fires on the initial press and on
274 * every repeat, with a deterministic first event.
275 *
276 * on_held already fires on the initial KEY_PRESSED and on each repeat, so
277 * this registers it alone; also registering on_press would count the
278 * initial press twice, since every listener on a key sees every event.
279 * One fire per physical press, a clean repeat cadence after.
280 *
281 * @param id Element id to bind to.
282 * @param key The key to listen for.
283 * @param fn Callback receiving element id, fired once on press and
284 * again on each repeat tick.
285 * @return *this for chaining.
286 */
287 Context& press_and_hold(uint32_t id, IO::Keys key, KeyFn fn);
288
289 /**
290 * @brief Register a drag handler receiving only the cursor position.
291 *
292 * Thin composition over on_drag for callers that already know the element
293 * id and have no use for it in the callback. The sink performs whatever
294 * mapping and write the gesture implies.
295 *
296 * @param id Element id.
297 * @param sink Called with the current NDC cursor position on each drag event.
298 * @param btn Mouse button that must be held.
299 * @return *this for chaining.
300 */
301 Context& wire_drag(
302 uint32_t id,
303 std::function<void(glm::vec2)> sink,
304 IO::MouseButtons btn = IO::MouseButtons::Left);
305
306 // =========================================================================
307 // State query
308 // =========================================================================
309
310 [[nodiscard]] std::optional<uint32_t> hovered() const { return m_hovered; }
311
312 [[nodiscard]] const Vruta::WindowEventSource& event_source() const;
313
314 /// @brief The Layer this Context hit-tests against.
315 [[nodiscard]] const std::shared_ptr<Layer>& layer() const noexcept { return m_layer; }
316
317 /**
318 * @brief Rect of pixel size (@p w, @p h) at pixel position (@p x, @p y),
319 * top-left origin, converted to NDC from the window's current
320 * dimensions.
321 *
322 * Reads window state live at call time via to_ndc() - call again after
323 * a resize for that size's new NDC bounds.
324 */
325 [[nodiscard]] Kinesis::AABB2D to_ndc_rect(double x, double y, double w, double h) const noexcept;
326
327private:
329 std::unordered_map<int, PressFn> press;
330 std::unordered_map<int, PressFn> release;
331 std::unordered_map<int, MoveFn> drag;
332 std::unordered_map<int, KeyFn> key_press;
333 std::unordered_map<int, KeyFn> key_release;
334 std::unordered_map<int, KeyFn> key_held;
335
340
346 };
347
349 bool has_press = false;
350 bool has_release = false;
351 bool has_held = false;
352 };
353 std::unordered_map<int, KeyHandlerState> m_registered_keys;
354
355 std::shared_ptr<Layer> m_layer;
356 std::shared_ptr<Core::Window> m_window;
358 std::string m_name;
359 std::optional<uint32_t> m_hovered;
360
361 std::unordered_map<uint32_t, ElementCallbacks> m_callbacks;
362
363 void register_handlers();
364 void cancel_handlers(bool cancel_close = true);
365
366 [[nodiscard]] glm::vec2 to_ndc(double px, double py) const noexcept;
367
368 void handle_move(double px, double py);
369 void handle_press(double px, double py, IO::MouseButtons btn);
370 void handle_release(double px, double py, IO::MouseButtons btn);
371 void handle_scroll(double dx, double dy);
372 void handle_drag(double px, double py, IO::MouseButtons btn);
373 void handle_drags(double px, double py);
374 void handle_key_press(IO::Keys key);
375 void handle_key_release(IO::Keys key);
376 void handle_key_held(IO::Keys key);
377 void handle_text(uint32_t codepoint);
378 void handle_resize(uint32_t width, uint32_t height);
379 void handle_close();
380 void detach_window(bool cancel_close = true);
381
382 std::optional<uint32_t> m_dragging[3];
383 std::optional<uint32_t> m_focused;
384 uint32_t m_known_width {};
385 uint32_t m_known_height {};
386};
387
388} // namespace MayaFlux::Portal::Forma
Expanse::CrossingFn on_enter
Definition Decoder.cpp:518
uint32_t h
Definition InkPress.cpp:29
std::weak_ptr< Core::Window > window
Definition Picker.cpp:23
std::string name
Definition VKDevice.cpp:123
uint32_t py
uint32_t px
uint32_t width
uint32_t height
std::function< void(uint32_t id)> KeyFn
Definition Context.hpp:48
std::unordered_map< int, KeyHandlerState > m_registered_keys
Definition Context.hpp:353
Context(const Context &)=delete
Context & operator=(Context &&)=delete
std::function< void(uint32_t id, uint32_t codepoint)> TextFn
Definition Context.hpp:49
std::shared_ptr< Layer > m_layer
Definition Context.hpp:355
std::optional< uint32_t > hovered() const
Definition Context.hpp:310
std::function< void(uint32_t id, glm::vec2 ndc)> MoveFn
Definition Context.hpp:44
std::function< void(uint32_t width, uint32_t height)> ResizeFn
Definition Context.hpp:50
std::function< void(uint32_t id, glm::vec2 ndc, double dx, double dy)> ScrollFn
Definition Context.hpp:47
Vruta::EventManager & m_event_manager
Definition Context.hpp:357
std::shared_ptr< Core::Window > m_window
Definition Context.hpp:356
std::function< void(uint32_t id, glm::vec2 ndc)> PressFn
Definition Context.hpp:43
std::function< void(uint32_t id)> LeaveFn
Definition Context.hpp:46
std::unordered_map< uint32_t, ElementCallbacks > m_callbacks
Definition Context.hpp:361
std::optional< uint32_t > focused() const
Get currently focused element, if any.
Definition Context.hpp:239
std::optional< uint32_t > m_hovered
Definition Context.hpp:359
std::function< void(uint32_t id)> EnterFn
Definition Context.hpp:45
std::function< void()> CloseFn
Definition Context.hpp:51
const std::shared_ptr< Layer > & layer() const noexcept
The Layer this Context hit-tests against.
Definition Context.hpp:315
Context & operator=(const Context &)=delete
std::optional< uint32_t > m_focused
Definition Context.hpp:383
Event wiring between a Layer and a window surface.
Definition Context.hpp:41
Awaitable stream of window input events.
MouseButtons
Enumeration for mouse buttons.
Definition Keys.hpp:147
void on_scroll(const std::shared_ptr< Core::Window > &window, std::function< void(double, double)> callback, std::string name)
Schedule a mouse scroll handler.
Definition Chronie.cpp:264
Axis-aligned bounding rectangle in a 2D coordinate space.
Definition Bounds.hpp:63
std::unordered_map< int, KeyFn > key_press
Definition Context.hpp:332
std::unordered_map< int, KeyFn > key_release
Definition Context.hpp:333
std::unordered_map< int, PressFn > press
Definition Context.hpp:329
std::unordered_map< int, KeyFn > key_held
Definition Context.hpp:334
std::unordered_map< int, MoveFn > drag
Definition Context.hpp:331
std::unordered_map< int, PressFn > release
Definition Context.hpp:330
Value carrier for a Mapped primitive.
Definition Mapped.hpp:40