MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Context.cpp
Go to the documentation of this file.
1#include "Context.hpp"
2
4
9
11
12Context::Context(std::shared_ptr<Layer> layer,
13 std::shared_ptr<Core::Window> window,
14 Vruta::EventManager& event_manager,
15 std::string name)
16 : m_layer(std::move(layer))
17 , m_window(std::move(window))
18 , m_event_manager(event_manager)
19 , m_name(std::move(name))
20{
22}
23
28
29// =============================================================================
30// Per-element callback registration
31// =============================================================================
32
33void Context::on_press(uint32_t id, IO::MouseButtons btn, PressFn fn)
34{
35 m_callbacks[id].press[static_cast<int>(btn)] = std::move(fn);
36}
37
39{
40 m_callbacks[id].release[static_cast<int>(btn)] = std::move(fn);
41}
42
43void Context::on_move(uint32_t id, MoveFn fn)
44{
45 m_callbacks[id].move = std::move(fn);
46}
47
48void Context::on_drag(uint32_t id, IO::MouseButtons btn, MoveFn fn)
49{
50 m_callbacks[id].drag[static_cast<int>(btn)] = std::move(fn);
51}
52
53void Context::on_enter(uint32_t id, EnterFn fn)
54{
55 m_callbacks[id].enter = std::move(fn);
56}
57
58void Context::on_leave(uint32_t id, LeaveFn fn)
59{
60 m_callbacks[id].leave = std::move(fn);
61}
62
63void Context::on_scroll(uint32_t id, ScrollFn fn)
64{
65 m_callbacks[id].scroll = std::move(fn);
66}
67
68void Context::on_press(uint32_t id, IO::Keys key, KeyFn fn)
69{
70 const int key_code = static_cast<int>(key);
71 m_callbacks[id].key_press[key_code] = std::move(fn);
72
73 if (!m_registered_keys[key_code].has_press) {
74 const std::string key_name = std::to_string(key_code);
76 std::make_shared<Vruta::Event>(
78 [this, key]() { handle_key_press(key); })),
79 m_name + "_key_press_" + key_name);
80 m_registered_keys[key_code].has_press = true;
81 }
82}
83
84void Context::on_release(uint32_t id, IO::Keys key, KeyFn fn)
85{
86 const int key_code = static_cast<int>(key);
87 m_callbacks[id].key_release[key_code] = std::move(fn);
88
89 if (!m_registered_keys[key_code].has_release) {
90 const std::string key_name = std::to_string(key_code);
92 std::make_shared<Vruta::Event>(
94 [this, key]() { handle_key_release(key); })),
95 m_name + "_key_release_" + key_name);
96 m_registered_keys[key_code].has_release = true;
97 }
98}
99
100void Context::on_held(uint32_t id, IO::Keys key, KeyFn fn)
101{
102 const int key_code = static_cast<int>(key);
103 m_callbacks[id].key_held[key_code] = std::move(fn);
104
105 if (!m_registered_keys[key_code].has_held) {
106 const std::string key_name = std::to_string(key_code);
108 std::make_shared<Vruta::Event>(
110 [this, key]() { handle_key_held(key); })),
111 m_name + "_key_held_" + key_name);
112 m_registered_keys[key_code].has_held = true;
113 }
114}
115
117{
118 m_callbacks[id].focus_gained = std::move(fn);
119}
120
121void Context::on_focus_lost(uint32_t id, LeaveFn fn)
122{
123 m_callbacks[id].focus_lost = std::move(fn);
124}
125
127{
128 if (m_focused) {
129 auto it = m_callbacks.find(*m_focused);
130 if (it != m_callbacks.end() && it->second.focus_lost)
131 it->second.focus_lost(*m_focused);
132 m_focused = std::nullopt;
133 }
134}
135
136void Context::unbind(uint32_t id)
137{
138 if (m_focused && *m_focused == id) {
139 auto it = m_callbacks.find(id);
140 if (it != m_callbacks.end() && it->second.focus_lost)
141 it->second.focus_lost(id);
142 m_focused = std::nullopt;
143 }
144
145 m_callbacks.erase(id);
146}
147
149{
150 return m_window->get_event_source();
151}
152
154 uint32_t id,
155 std::shared_ptr<MappedState<float>> state,
156 IO::Keys decrease,
157 IO::Keys increase,
158 float delta,
159 float clamp_min,
160 float clamp_max)
161{
162 on_held(id, decrease, [state, delta, clamp_min, clamp_max](uint32_t) {
163 state->write(std::clamp(state->value - delta, clamp_min, clamp_max));
164 });
165
166 on_held(id, increase, [state, delta, clamp_min, clamp_max](uint32_t) {
167 state->write(std::clamp(state->value + delta, clamp_min, clamp_max));
168 });
169
170 return *this;
171}
172
174 uint32_t id,
175 std::function<void(glm::vec2)> sink,
177{
178 if (!sink)
179 return *this;
180
181 on_drag(id, btn,
182 [sink = std::move(sink)](uint32_t, glm::vec2 ndc) { sink(ndc); });
183
184 return *this;
185}
186
187// =============================================================================
188// Handler registration / cancellation
189// =============================================================================
190
192{
194 std::make_shared<Vruta::Event>(
196 [this](double px, double py) { handle_move(px, py); })),
197 m_name + "_move");
198
200 std::make_shared<Vruta::Event>(
202 [this](double px, double py) { handle_press(px, py, IO::MouseButtons::Left); })),
203 m_name + "_press_left");
204
206 std::make_shared<Vruta::Event>(
208 [this](double px, double py) { handle_release(px, py, IO::MouseButtons::Left); })),
209 m_name + "_release_left");
210
212 std::make_shared<Vruta::Event>(
214 [this](double px, double py) { handle_press(px, py, IO::MouseButtons::Right); })),
215 m_name + "_press_right");
216
218 std::make_shared<Vruta::Event>(
220 [this](double px, double py) { handle_release(px, py, IO::MouseButtons::Right); })),
221 m_name + "_release_right");
222
224 std::make_shared<Vruta::Event>(
226 [this](double px, double py) { handle_drag(px, py, IO::MouseButtons::Left); })),
227 m_name + "_drag_left");
228
230 std::make_shared<Vruta::Event>(
232 [this](double px, double py) { handle_drag(px, py, IO::MouseButtons::Right); })),
233 m_name + "_drag_right");
234
236 std::make_shared<Vruta::Event>(
238 [this](double dx, double dy) { handle_scroll(dx, dy); })),
239 m_name + "_scroll");
240}
241
243{
244 for (const char* suffix : {
245 "_move",
246 "_press_left", "_release_left",
247 "_press_right", "_release_right",
248 "_scroll",
249 "_drag_left", "_drag_right" }) {
251 }
252
253 for (const auto& [key_code, handlers] : m_registered_keys) {
254 const std::string key_name = std::to_string(key_code);
255 if (handlers.has_press)
256 m_event_manager.cancel_event(m_name + "_key_press_" + key_name);
257 if (handlers.has_release)
258 m_event_manager.cancel_event(m_name + "_key_release_" + key_name);
259 if (handlers.has_held)
260 m_event_manager.cancel_event(m_name + "_key_held_" + key_name);
261 }
262}
263
264// =============================================================================
265// Event dispatch
266// =============================================================================
267
268glm::vec2 Context::to_ndc(double px, double py) const noexcept
269{
270 const auto& s = m_window->get_state();
271 return {
272 (static_cast<float>(px) / static_cast<float>(s.current_width)) * 2.0F - 1.0F,
273 1.0F - (static_cast<float>(py) / static_cast<float>(s.current_height)) * 2.0F
274 };
275}
276
277void Context::handle_move(double px, double py)
278{
279 const glm::vec2 ndc = to_ndc(px, py);
280 const auto hit = m_layer->hit_test(ndc);
281
282 if (hit != m_hovered) {
283 if (m_hovered) {
284 auto it = m_callbacks.find(*m_hovered);
285 if (it != m_callbacks.end() && it->second.leave)
286 it->second.leave(*m_hovered);
287 }
288 if (hit) {
289 auto it = m_callbacks.find(*hit);
290 if (it != m_callbacks.end() && it->second.enter)
291 it->second.enter(*hit);
292 }
293 m_hovered = hit;
294 }
295
296 if (hit) {
297 auto it = m_callbacks.find(*hit);
298 if (it != m_callbacks.end() && it->second.move)
299 it->second.move(*hit, ndc);
300 }
301}
302
303void Context::handle_press(double px, double py, IO::MouseButtons btn)
304{
305 const glm::vec2 ndc = to_ndc(px, py);
306 const auto hit = m_layer->hit_test(ndc);
307
308 const int btn_idx = static_cast<int>(btn);
309
310 if (hit != m_focused) {
311 if (m_focused) {
312 auto it = m_callbacks.find(*m_focused);
313 if (it != m_callbacks.end() && it->second.focus_lost)
314 it->second.focus_lost(*m_focused);
315 }
316
317 m_focused = hit;
318
319 if (hit) {
320 auto it = m_callbacks.find(*hit);
321 if (it != m_callbacks.end() && it->second.focus_gained)
322 it->second.focus_gained(*hit);
323 }
324 }
325
326 if (hit && m_callbacks.contains(*hit) && m_callbacks[*hit].drag.contains(btn_idx)) {
327 m_dragging[btn_idx] = hit;
328 }
329
330 if (hit) {
331 auto it = m_callbacks.find(*hit);
332 if (it != m_callbacks.end() && it->second.press.count(btn_idx))
333 it->second.press[btn_idx](*hit, ndc);
334 }
335}
336
337void Context::handle_release(double px, double py, IO::MouseButtons btn)
338{
339 const int btn_idx = static_cast<int>(btn);
340 m_dragging[btn_idx] = std::nullopt;
341
342 const glm::vec2 ndc = to_ndc(px, py);
343 const auto hit = m_layer->hit_test(ndc);
344 if (!hit)
345 return;
346
347 auto it = m_callbacks.find(*hit);
348 if (it == m_callbacks.end())
349 return;
350
351 auto btn_it = it->second.release.find(static_cast<int>(btn));
352 if (btn_it != it->second.release.end() && btn_it->second)
353 btn_it->second(*hit, ndc);
354}
355
356void Context::handle_drag(double px, double py, IO::MouseButtons btn)
357{
358 const glm::vec2 ndc = to_ndc(px, py);
359 const int btn_idx = static_cast<int>(btn);
360
361 if (!m_dragging[btn_idx]) {
362 const auto hit = m_layer->hit_test(ndc);
363 if (hit)
364 m_dragging[btn_idx] = hit;
365 else
366 return;
367 }
368
369 auto it = m_callbacks.find(*m_dragging[btn_idx]);
370 if (it != m_callbacks.end() && it->second.drag.count(btn_idx))
371 it->second.drag[btn_idx](*m_dragging[btn_idx], ndc);
372}
373
374void Context::handle_scroll(double dx, double dy)
375{
376 if (!m_hovered)
377 return;
378
379 auto it = m_callbacks.find(*m_hovered);
380 if (it == m_callbacks.end() || !it->second.scroll)
381 return;
382
383 const auto [px, py] = m_window->get_event_source().get_mouse_position();
384 it->second.scroll(*m_hovered, to_ndc(px, py), dx, dy);
385}
386
387// =============================================================================
388// Keyboard event dispatch
389// =============================================================================
390
392{
393 if (!m_focused)
394 return;
395
396 auto it = m_callbacks.find(*m_focused);
397 if (it == m_callbacks.end())
398 return;
399
400 const int key_code = static_cast<int>(key);
401 auto key_it = it->second.key_press.find(key_code);
402 if (key_it != it->second.key_press.end())
403 key_it->second(*m_focused);
404}
405
407{
408 if (!m_focused)
409 return;
410
411 auto it = m_callbacks.find(*m_focused);
412 if (it == m_callbacks.end())
413 return;
414
415 const int key_code = static_cast<int>(key);
416 auto key_it = it->second.key_release.find(key_code);
417 if (key_it != it->second.key_release.end())
418 key_it->second(*m_focused);
419}
420
422{
423 if (!m_focused)
424 return;
425
426 auto it = m_callbacks.find(*m_focused);
427 if (it == m_callbacks.end())
428 return;
429
430 const int key_code = static_cast<int>(key);
431 auto key_it = it->second.key_held.find(key_code);
432 if (key_it != it->second.key_held.end())
433 key_it->second(*m_focused);
434}
435
436} // namespace MayaFlux::Portal::Forma
std::string name
Definition VKDevice.cpp:143
std::function< void(uint32_t id)> KeyFn
Definition Context.hpp:47
std::unordered_map< int, KeyHandlerState > m_registered_keys
Definition Context.hpp:261
Context & wire_drag(uint32_t id, std::function< void(glm::vec2)> sink, IO::MouseButtons btn=IO::MouseButtons::Left)
Register a drag handler receiving only the cursor position.
Definition Context.cpp:173
std::optional< uint32_t > m_dragging[3]
Definition Context.hpp:285
void handle_key_release(IO::Keys key)
Definition Context.cpp:406
std::shared_ptr< Layer > m_layer
Definition Context.hpp:263
void on_scroll(uint32_t id, ScrollFn fn)
Called on scroll while the cursor is over an element.
Definition Context.cpp:63
void handle_move(double px, double py)
Definition Context.cpp:277
void on_focus_gained(uint32_t id, EnterFn fn)
Called once when an element gains keyboard focus (via click).
Definition Context.cpp:116
void handle_key_press(IO::Keys key)
Definition Context.cpp:391
void on_enter(uint32_t id, EnterFn fn)
Called once when the cursor enters an element's region.
Definition Context.cpp:53
std::function< void(uint32_t id, glm::vec2 ndc)> MoveFn
Definition Context.hpp:43
Context(std::shared_ptr< Layer > layer, std::shared_ptr< Core::Window > window, Vruta::EventManager &event_manager, std::string name)
Construct and immediately register event coroutines.
Definition Context.cpp:12
void handle_key_held(IO::Keys key)
Definition Context.cpp:421
void unbind(uint32_t id)
Remove all callbacks registered for an element id.
Definition Context.cpp:136
std::function< void(uint32_t id, glm::vec2 ndc, double dx, double dy)> ScrollFn
Definition Context.hpp:46
Vruta::EventManager & m_event_manager
Definition Context.hpp:265
std::shared_ptr< Core::Window > m_window
Definition Context.hpp:264
std::function< void(uint32_t id, glm::vec2 ndc)> PressFn
Definition Context.hpp:42
void handle_release(double px, double py, IO::MouseButtons btn)
Definition Context.cpp:337
const Vruta::WindowEventSource & event_source() const
Definition Context.cpp:148
void handle_drag(double px, double py, IO::MouseButtons btn)
Definition Context.cpp:356
std::function< void(uint32_t id)> LeaveFn
Definition Context.hpp:45
void on_focus_lost(uint32_t id, LeaveFn fn)
Called once when an element loses keyboard focus.
Definition Context.cpp:121
void on_move(uint32_t id, MoveFn fn)
Called each move event while the cursor is over an element.
Definition Context.cpp:43
glm::vec2 to_ndc(double px, double py) const noexcept
Definition Context.cpp:268
std::unordered_map< uint32_t, ElementCallbacks > m_callbacks
Definition Context.hpp:269
void on_release(uint32_t id, IO::MouseButtons btn, PressFn fn)
Called when a mouse button is released over an element.
Definition Context.cpp:38
void on_press(uint32_t id, IO::MouseButtons btn, PressFn fn)
Called when a mouse button is pressed over an element.
Definition Context.cpp:33
void on_drag(uint32_t id, IO::MouseButtons btn, MoveFn fn)
Called on each mouse-move event while btn is held, tracking the element where the drag began even whe...
Definition Context.cpp:48
std::optional< uint32_t > m_hovered
Definition Context.hpp:267
void on_leave(uint32_t id, LeaveFn fn)
Called once when the cursor leaves an element's region.
Definition Context.cpp:58
std::function< void(uint32_t id)> EnterFn
Definition Context.hpp:44
void handle_press(double px, double py, IO::MouseButtons btn)
Definition Context.cpp:303
void handle_scroll(double dx, double dy)
Definition Context.cpp:374
Context & key_step(uint32_t id, std::shared_ptr< MappedState< float > > state, IO::Keys decrease, IO::Keys increase, float delta, float clamp_min=0.0F, float clamp_max=1.0F)
Attach key-delta handlers to a Mapped<float> element.
Definition Context.cpp:153
void on_held(uint32_t id, IO::Keys key, KeyFn fn)
Called repeatedly while a key is held and the element has focus.
Definition Context.cpp:100
void clear_focus()
Clear keyboard focus (no element focused).
Definition Context.cpp:126
~Context()
Cancel all registered event coroutines.
Definition Context.cpp:24
std::optional< uint32_t > m_focused
Definition Context.hpp:286
Event wiring between a Layer and a window surface.
Definition Context.hpp:40
void add_event(const std::shared_ptr< Event > &event, const std::string &name="")
Add a event to the manager.
bool cancel_event(const std::shared_ptr< Event > &event)
Cancels and removes a event from the manager.
Awaitable stream of GLFW window input events.
MouseButtons
Enumeration for mouse buttons.
Definition Keys.hpp:147
Vruta::Event key_released(std::shared_ptr< Core::Window > window, IO::Keys key, std::function< void()> callback)
Creates an Event coroutine that triggers on specific key release.
Vruta::Event key_pressed(std::shared_ptr< Core::Window > window, IO::Keys key, std::function< void()> callback)
Creates an Event coroutine that triggers on specific key press.
Vruta::Event mouse_moved(std::shared_ptr< Core::Window > window, std::function< void(double, double)> callback)
Creates an Event coroutine that triggers on mouse movement.
Vruta::Event mouse_scrolled(std::shared_ptr< Core::Window > window, std::function< void(double, double)> callback)
Creates an Event coroutine that triggers on mouse scroll.
Vruta::Event key_held(std::shared_ptr< Core::Window > window, IO::Keys key, std::function< void()> callback)
Creates an Event coroutine that triggers on key press and repeats while held.
Vruta::Event mouse_released(std::shared_ptr< Core::Window > window, IO::MouseButtons button, std::function< void(double, double)> callback)
Creates an Event coroutine that triggers on specific mouse button release.
Vruta::Event mouse_pressed(std::shared_ptr< Core::Window > window, IO::MouseButtons button, std::function< void(double, double)> callback)
Creates an Event coroutine that triggers on specific mouse button press.
Vruta::Event mouse_dragged(std::shared_ptr< Core::Window > window, IO::MouseButtons button, std::function< void(double, double)> callback)
Creates an Event coroutine that triggers on mouse drag with specific button.
Value carrier for a Mapped primitive.
Definition Mapped.hpp:36