MayaFlux 0.4.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
173// =============================================================================
174// Handler registration / cancellation
175// =============================================================================
176
178{
180 std::make_shared<Vruta::Event>(
182 [this](double px, double py) { handle_move(px, py); })),
183 m_name + "_move");
184
186 std::make_shared<Vruta::Event>(
188 [this](double px, double py) { handle_press(px, py, IO::MouseButtons::Left); })),
189 m_name + "_press_left");
190
192 std::make_shared<Vruta::Event>(
194 [this](double px, double py) { handle_release(px, py, IO::MouseButtons::Left); })),
195 m_name + "_release_left");
196
198 std::make_shared<Vruta::Event>(
200 [this](double px, double py) { handle_press(px, py, IO::MouseButtons::Right); })),
201 m_name + "_press_right");
202
204 std::make_shared<Vruta::Event>(
206 [this](double px, double py) { handle_release(px, py, IO::MouseButtons::Right); })),
207 m_name + "_release_right");
208
210 std::make_shared<Vruta::Event>(
212 [this](double px, double py) { handle_drag(px, py, IO::MouseButtons::Left); })),
213 m_name + "_drag_left");
214
216 std::make_shared<Vruta::Event>(
218 [this](double px, double py) { handle_drag(px, py, IO::MouseButtons::Right); })),
219 m_name + "_drag_right");
220
222 std::make_shared<Vruta::Event>(
224 [this](double dx, double dy) { handle_scroll(dx, dy); })),
225 m_name + "_scroll");
226}
227
229{
230 for (const char* suffix : {
231 "_move",
232 "_press_left", "_release_left",
233 "_press_right", "_release_right",
234 "_scroll",
235 "_drag_left", "_drag_right" }) {
237 }
238
239 for (const auto& [key_code, handlers] : m_registered_keys) {
240 const std::string key_name = std::to_string(key_code);
241 if (handlers.has_press)
242 m_event_manager.cancel_event(m_name + "_key_press_" + key_name);
243 if (handlers.has_release)
244 m_event_manager.cancel_event(m_name + "_key_release_" + key_name);
245 if (handlers.has_held)
246 m_event_manager.cancel_event(m_name + "_key_held_" + key_name);
247 }
248}
249
250// =============================================================================
251// Event dispatch
252// =============================================================================
253
254glm::vec2 Context::to_ndc(double px, double py) const noexcept
255{
256 const auto& s = m_window->get_state();
257 return {
258 (static_cast<float>(px) / static_cast<float>(s.current_width)) * 2.0F - 1.0F,
259 1.0F - (static_cast<float>(py) / static_cast<float>(s.current_height)) * 2.0F
260 };
261}
262
263void Context::handle_move(double px, double py)
264{
265 const glm::vec2 ndc = to_ndc(px, py);
266 const auto hit = m_layer->hit_test(ndc);
267
268 if (hit != m_hovered) {
269 if (m_hovered) {
270 auto it = m_callbacks.find(*m_hovered);
271 if (it != m_callbacks.end() && it->second.leave)
272 it->second.leave(*m_hovered);
273 }
274 if (hit) {
275 auto it = m_callbacks.find(*hit);
276 if (it != m_callbacks.end() && it->second.enter)
277 it->second.enter(*hit);
278 }
279 m_hovered = hit;
280 }
281
282 if (hit) {
283 auto it = m_callbacks.find(*hit);
284 if (it != m_callbacks.end() && it->second.move)
285 it->second.move(*hit, ndc);
286 }
287}
288
289void Context::handle_press(double px, double py, IO::MouseButtons btn)
290{
291 const glm::vec2 ndc = to_ndc(px, py);
292 const auto hit = m_layer->hit_test(ndc);
293
294 const int btn_idx = static_cast<int>(btn);
295
296 if (hit != m_focused) {
297 if (m_focused) {
298 auto it = m_callbacks.find(*m_focused);
299 if (it != m_callbacks.end() && it->second.focus_lost)
300 it->second.focus_lost(*m_focused);
301 }
302
303 m_focused = hit;
304
305 if (hit) {
306 auto it = m_callbacks.find(*hit);
307 if (it != m_callbacks.end() && it->second.focus_gained)
308 it->second.focus_gained(*hit);
309 }
310 }
311
312 if (hit && m_callbacks.contains(*hit) && m_callbacks[*hit].drag.contains(btn_idx)) {
313 m_dragging[btn_idx] = hit;
314 }
315
316 if (hit) {
317 auto it = m_callbacks.find(*hit);
318 if (it != m_callbacks.end() && it->second.press.count(btn_idx))
319 it->second.press[btn_idx](*hit, ndc);
320 }
321}
322
323void Context::handle_release(double px, double py, IO::MouseButtons btn)
324{
325 const int btn_idx = static_cast<int>(btn);
326 m_dragging[btn_idx] = std::nullopt;
327
328 const glm::vec2 ndc = to_ndc(px, py);
329 const auto hit = m_layer->hit_test(ndc);
330 if (!hit)
331 return;
332
333 auto it = m_callbacks.find(*hit);
334 if (it == m_callbacks.end())
335 return;
336
337 auto btn_it = it->second.release.find(static_cast<int>(btn));
338 if (btn_it != it->second.release.end() && btn_it->second)
339 btn_it->second(*hit, ndc);
340}
341
342void Context::handle_drag(double px, double py, IO::MouseButtons btn)
343{
344 const glm::vec2 ndc = to_ndc(px, py);
345 const int btn_idx = static_cast<int>(btn);
346
347 if (!m_dragging[btn_idx]) {
348 const auto hit = m_layer->hit_test(ndc);
349 if (hit)
350 m_dragging[btn_idx] = hit;
351 else
352 return;
353 }
354
355 auto it = m_callbacks.find(*m_dragging[btn_idx]);
356 if (it != m_callbacks.end() && it->second.drag.count(btn_idx))
357 it->second.drag[btn_idx](*m_dragging[btn_idx], ndc);
358}
359
360void Context::handle_scroll(double dx, double dy)
361{
362 if (!m_hovered)
363 return;
364
365 auto it = m_callbacks.find(*m_hovered);
366 if (it == m_callbacks.end() || !it->second.scroll)
367 return;
368
369 const auto [px, py] = m_window->get_event_source().get_mouse_position();
370 it->second.scroll(*m_hovered, to_ndc(px, py), dx, dy);
371}
372
373// =============================================================================
374// Keyboard event dispatch
375// =============================================================================
376
378{
379 if (!m_focused)
380 return;
381
382 auto it = m_callbacks.find(*m_focused);
383 if (it == m_callbacks.end())
384 return;
385
386 const int key_code = static_cast<int>(key);
387 auto key_it = it->second.key_press.find(key_code);
388 if (key_it != it->second.key_press.end())
389 key_it->second(*m_focused);
390}
391
393{
394 if (!m_focused)
395 return;
396
397 auto it = m_callbacks.find(*m_focused);
398 if (it == m_callbacks.end())
399 return;
400
401 const int key_code = static_cast<int>(key);
402 auto key_it = it->second.key_release.find(key_code);
403 if (key_it != it->second.key_release.end())
404 key_it->second(*m_focused);
405}
406
408{
409 if (!m_focused)
410 return;
411
412 auto it = m_callbacks.find(*m_focused);
413 if (it == m_callbacks.end())
414 return;
415
416 const int key_code = static_cast<int>(key);
417 auto key_it = it->second.key_held.find(key_code);
418 if (key_it != it->second.key_held.end())
419 key_it->second(*m_focused);
420}
421
422} // namespace MayaFlux::Portal::Forma
std::function< void(uint32_t id)> KeyFn
Definition Context.hpp:47
std::unordered_map< int, KeyHandlerState > m_registered_keys
Definition Context.hpp:244
std::optional< uint32_t > m_dragging[3]
Definition Context.hpp:268
void handle_key_release(IO::Keys key)
Definition Context.cpp:392
std::shared_ptr< Layer > m_layer
Definition Context.hpp:246
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:263
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:377
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:407
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:248
std::shared_ptr< Core::Window > m_window
Definition Context.hpp:247
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:323
const Vruta::WindowEventSource & event_source() const
Definition Context.cpp:148
void handle_drag(double px, double py, IO::MouseButtons btn)
Definition Context.cpp:342
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:254
std::unordered_map< uint32_t, ElementCallbacks > m_callbacks
Definition Context.hpp:252
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:250
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:289
void handle_scroll(double dx, double dy)
Definition Context.cpp:360
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:269
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