reflexion 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/src/osx/event.mm CHANGED
@@ -5,6 +5,9 @@
5
5
  #include <assert.h>
6
6
  #include <Carbon/Carbon.h>
7
7
  #import <Cocoa/Cocoa.h>
8
+ #import <GameController/GameController.h>
9
+ #include "reflex/debug.h"
10
+ #include "window.h"
8
11
 
9
12
 
10
13
  namespace Reflex
@@ -12,9 +15,9 @@ namespace Reflex
12
15
 
13
16
 
14
17
  static uint
15
- get_modifiers (const NSEvent* e)
18
+ get_modifiers (const NSEvent* event = nil)
16
19
  {
17
- NSUInteger flags = e.modifierFlags;
20
+ NSUInteger flags = event ? event.modifierFlags : NSEvent.modifierFlags;
18
21
  return
19
22
  (flags & NSAlphaShiftKeyMask) ? MOD_CAPS : 0 |
20
23
  (flags & NSShiftKeyMask) ? MOD_SHIFT : 0 |
@@ -161,4 +164,86 @@ namespace Reflex
161
164
  }
162
165
 
163
166
 
167
+ static void
168
+ call_gamepad_event (int code, bool pressed)
169
+ {
170
+ Window* win = Window_get_active();
171
+ if (!win) return;
172
+
173
+ auto action = pressed ? KeyEvent::DOWN : KeyEvent::UP;
174
+ KeyEvent e(action, NULL, code, get_modifiers(), 0);
175
+ Window_call_key_event(win, &e);
176
+ }
177
+
178
+ static void
179
+ handle_gamepad_event (GCControllerButtonInput* input, int code)
180
+ {
181
+ [input setPressedChangedHandler:
182
+ ^(GCControllerButtonInput* button, float value, BOOL pressed) {
183
+ call_gamepad_event(code, pressed);
184
+ }];
185
+ }
186
+
187
+ static void
188
+ handle_gamepad_events (GCController* controller)
189
+ {
190
+ GCExtendedGamepad* gamepad = controller.extendedGamepad;
191
+ if (!gamepad) return;
192
+
193
+ handle_gamepad_event(gamepad.dpad.left, KEY_GAMEPAD_LEFT);
194
+ handle_gamepad_event(gamepad.dpad.right, KEY_GAMEPAD_RIGHT);
195
+ handle_gamepad_event(gamepad.dpad.up, KEY_GAMEPAD_UP);
196
+ handle_gamepad_event(gamepad.dpad.down, KEY_GAMEPAD_DOWN);
197
+
198
+ handle_gamepad_event(gamepad.buttonA, KEY_GAMEPAD_A);
199
+ handle_gamepad_event(gamepad.buttonB, KEY_GAMEPAD_B);
200
+ handle_gamepad_event(gamepad.buttonX, KEY_GAMEPAD_X);
201
+ handle_gamepad_event(gamepad.buttonY, KEY_GAMEPAD_Y);
202
+
203
+ handle_gamepad_event(gamepad. leftShoulder, KEY_GAMEPAD_SHOULDER_LEFT);
204
+ handle_gamepad_event(gamepad.rightShoulder, KEY_GAMEPAD_SHOULDER_RIGHT);
205
+ handle_gamepad_event(gamepad. leftTrigger, KEY_GAMEPAD_TRIGGER_LEFT);
206
+ handle_gamepad_event(gamepad.rightTrigger, KEY_GAMEPAD_TRIGGER_RIGHT);
207
+
208
+ if (@available(macOS 10.14.1, *))
209
+ {
210
+ handle_gamepad_event(gamepad. leftThumbstickButton, KEY_GAMEPAD_THUMB_LEFT);
211
+ handle_gamepad_event(gamepad.rightThumbstickButton, KEY_GAMEPAD_THUMB_RIGHT);
212
+ }
213
+
214
+ if (@available(macOS 10.15, *))
215
+ {
216
+ handle_gamepad_event(gamepad.buttonMenu, KEY_GAMEPAD_MENU);
217
+ handle_gamepad_event(gamepad.buttonOptions, KEY_GAMEPAD_OPTION);
218
+ }
219
+
220
+ if (@available(macOS 11.0, *))
221
+ handle_gamepad_event(gamepad.buttonHome, KEY_GAMEPAD_HOME);
222
+ }
223
+
224
+ static id game_controllers_observer = nil;
225
+
226
+ void
227
+ init_game_controllers ()
228
+ {
229
+ for (GCController* c in GCController.controllers)
230
+ handle_gamepad_events(c);
231
+
232
+ game_controllers_observer = [NSNotificationCenter.defaultCenter
233
+ addObserverForName: GCControllerDidConnectNotification
234
+ object: nil
235
+ queue: NSOperationQueue.mainQueue
236
+ usingBlock: ^(NSNotification* n) {handle_gamepad_events(n.object);}];
237
+ }
238
+
239
+ void
240
+ fin_game_controllers ()
241
+ {
242
+ if (!game_controllers_observer) return;
243
+
244
+ [NSNotificationCenter.defaultCenter
245
+ removeObserver: game_controllers_observer];
246
+ }
247
+
248
+
164
249
  };// Reflex
@@ -201,13 +201,7 @@ move_to_main_screen_origin (NativeWindow* window)
201
201
 
202
202
  ++update_count;
203
203
 
204
- double now = Xot::time();
205
- Reflex::UpdateEvent e(now, now - win->self->prev_time_update);
206
- win->self->prev_time_update = now;
207
-
208
- win->on_update(&e);
209
- if (!e.is_blocked())
210
- Reflex::View_update_tree(win->root(), e);
204
+ Window_call_update_event(win);
211
205
 
212
206
  if (win->self->redraw)
213
207
  {
data/src/osx/reflex.mm CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  #import <Cocoa/Cocoa.h>
6
6
  #include "reflex/exception.h"
7
+ #include "event.h"
7
8
 
8
9
 
9
10
  namespace Reflex
@@ -25,6 +26,8 @@ namespace Reflex
25
26
  reflex_error(__FILE__, __LINE__, "already initialized.");
26
27
 
27
28
  global::pool = [[NSAutoreleasePool alloc] init];
29
+
30
+ init_game_controllers();
28
31
  }
29
32
 
30
33
  void
@@ -33,6 +36,8 @@ namespace Reflex
33
36
  if (!global::pool)
34
37
  reflex_error(__FILE__, __LINE__, "not initialized.");
35
38
 
39
+ fin_game_controllers();
40
+
36
41
  [global::pool release];
37
42
  global::pool = nil;
38
43
  }
data/src/view.cpp CHANGED
@@ -39,23 +39,29 @@ namespace Reflex
39
39
  enum Flag
40
40
  {
41
41
 
42
- REDRAW = Xot::bit(1, FLAG_LAST),
42
+ UPDATING = Xot::bit(1, FLAG_LAST),
43
43
 
44
- APPLY_STYLE = Xot::bit(2, FLAG_LAST),
44
+ NO_SHAPE = Xot::bit(2, FLAG_LAST),
45
45
 
46
- UPDATE_STYLE = Xot::bit(3, FLAG_LAST),
46
+ HAS_VARIABLE_LENGTHS = Xot::bit(3, FLAG_LAST),
47
47
 
48
- UPDATE_SHAPES = Xot::bit(4, FLAG_LAST),
48
+ HAS_CHILDREN_TO_REMOVE = Xot::bit(4, FLAG_LAST),
49
49
 
50
- UPDATE_LAYOUT = Xot::bit(5, FLAG_LAST),
50
+ REMOVE_FROM_PARENT = Xot::bit(5, FLAG_LAST),
51
51
 
52
- SORT_CHILDREN = Xot::bit(6, FLAG_LAST),
52
+ REDRAW = Xot::bit(6, FLAG_LAST),
53
53
 
54
- FIT_TO_CONTENT = Xot::bit(7, FLAG_LAST),
54
+ APPLY_STYLE = Xot::bit(7, FLAG_LAST),
55
55
 
56
- HAS_VARIABLE_LENGTHS = Xot::bit(8, FLAG_LAST),
56
+ UPDATE_STYLE = Xot::bit(8, FLAG_LAST),
57
57
 
58
- NO_SHAPE = Xot::bit(9, FLAG_LAST),
58
+ UPDATE_SHAPES = Xot::bit(9, FLAG_LAST),
59
+
60
+ UPDATE_LAYOUT = Xot::bit(10, FLAG_LAST),
61
+
62
+ SORT_CHILDREN = Xot::bit(11, FLAG_LAST),
63
+
64
+ FIT_TO_CONTENT = Xot::bit(12, FLAG_LAST),
59
65
 
60
66
  };// Flag
61
67
 
@@ -235,6 +241,8 @@ namespace Reflex
235
241
  }
236
242
  else
237
243
  pbody->set_transform(frame.x, frame.y, angle);
244
+
245
+ pbody->awake();
238
246
  }
239
247
 
240
248
  void update_body_states ()
@@ -1043,6 +1051,20 @@ namespace Reflex
1043
1051
  }
1044
1052
  }
1045
1053
 
1054
+ static void
1055
+ remove_children (View* view, View::ChildList* children)
1056
+ {
1057
+ assert(children);
1058
+
1059
+ int size = (int) children->size();
1060
+ for (int i = size - 1; i >= 0; --i)
1061
+ {
1062
+ View* child = (*children)[i].get();
1063
+ if (child->self->check_and_remove_flag(View::Data::REMOVE_FROM_PARENT))
1064
+ view->remove_child(child);
1065
+ }
1066
+ }
1067
+
1046
1068
  void
1047
1069
  View_update_tree (View* view, const UpdateEvent& event)
1048
1070
  {
@@ -1051,13 +1073,16 @@ namespace Reflex
1051
1073
 
1052
1074
  View::Data* self = view->self.get();
1053
1075
 
1076
+ self->add_flag(View::Data::UPDATING);
1077
+
1054
1078
  fire_timers(view, event.now());
1055
1079
 
1056
1080
  View::ChildList* children = self->children();
1057
1081
  if (children)
1058
1082
  {
1059
- for (auto& child : *children)
1060
- View_update_tree(child.get(), event);
1083
+ size_t size = children->size();
1084
+ for (size_t i = 0; i < size; ++i)
1085
+ View_update_tree((*children)[i].get(), event);
1061
1086
  }
1062
1087
 
1063
1088
  update_view_shapes(view);
@@ -1079,6 +1104,11 @@ namespace Reflex
1079
1104
 
1080
1105
  if (self->check_and_remove_flag(View::Data::FIT_TO_CONTENT))
1081
1106
  fit_view_to_content(view);
1107
+
1108
+ self->remove_flag(View::Data::UPDATING);
1109
+
1110
+ if (self->check_and_remove_flag(View::Data::HAS_CHILDREN_TO_REMOVE))
1111
+ remove_children(view, children);
1082
1112
  }
1083
1113
 
1084
1114
  static bool
@@ -1790,13 +1820,19 @@ namespace Reflex
1790
1820
  else if (found != belong)
1791
1821
  invalid_state_error(__FILE__, __LINE__);
1792
1822
 
1793
- set_parent(child, NULL);
1794
-
1795
- erase_child_from_children(this, child);
1796
-
1797
- self->sort_children();
1798
-
1799
- update_view_layout(this);
1823
+ if (self->has_flag(Data::UPDATING))
1824
+ {
1825
+ // delay removing child to avoid breaking child list looped on View_update_tree()
1826
+ self->add_flag(Data::HAS_CHILDREN_TO_REMOVE);
1827
+ child->self->add_flag(Data::REMOVE_FROM_PARENT);
1828
+ }
1829
+ else
1830
+ {
1831
+ set_parent(child, NULL);
1832
+ erase_child_from_children(this, child);
1833
+ self->sort_children();
1834
+ update_view_layout(this);
1835
+ }
1800
1836
  }
1801
1837
 
1802
1838
  void
@@ -2295,9 +2331,9 @@ namespace Reflex
2295
2331
  bool capture = types != CAPTURE_NONE;
2296
2332
 
2297
2333
  if (capture && !registered)
2298
- Window_register_capture(w, this, CAPTURE_ALL);
2334
+ Window_register_capture(w, this, CAPTURE_ALL_EVENTS);
2299
2335
  else if (!capture && registered)
2300
- Window_unregister_capture(w, this, CAPTURE_ALL);
2336
+ Window_unregister_capture(w, this, CAPTURE_ALL_EVENTS);
2301
2337
 
2302
2338
  CaptureEvent e(~old & types, old & ~types);
2303
2339
  on_capture(&e);
data/src/win32/event.cpp CHANGED
@@ -1,9 +1,11 @@
1
1
  #include "event.h"
2
2
 
3
3
 
4
+ #include <xinput.h>
4
5
  #include <xot/time.h>
5
6
  #include "reflex/exception.h"
6
- #include "../pointer.h"
7
+ #include "reflex/debug.h"
8
+ #include "window.h"
7
9
 
8
10
 
9
11
  namespace Reflex
@@ -216,4 +218,86 @@ namespace Reflex
216
218
  }
217
219
 
218
220
 
221
+ static void
222
+ call_gamepad_event (Window* win, int code, bool pressed)
223
+ {
224
+ auto action = pressed ? KeyEvent::DOWN : KeyEvent::UP;
225
+ KeyEvent e(action, NULL, code, get_modifiers(), 0);
226
+ Window_call_key_event(win, &e);
227
+ }
228
+
229
+ static void
230
+ handle_gamepad_button_event (
231
+ Window* win, const XINPUT_STATE& state, const XINPUT_STATE& prev_state,
232
+ WORD mask, int code)
233
+ {
234
+ WORD pressed = state.Gamepad.wButtons & mask;
235
+ WORD prev = prev_state.Gamepad.wButtons & mask;
236
+ if (pressed == prev) return;
237
+
238
+ call_gamepad_event(win, code, pressed);
239
+ }
240
+
241
+ static void
242
+ handle_gamepad_trigger_event (Window* win, BYTE value, BYTE prev_value, int code)
243
+ {
244
+ WORD pressed = value > XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
245
+ WORD prev = prev_value > XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
246
+ if (pressed == prev) return;
247
+
248
+ call_gamepad_event(win, code, pressed);
249
+ }
250
+
251
+ static void
252
+ handle_gamepad_events (const XINPUT_STATE& state, const XINPUT_STATE& prev_state)
253
+ {
254
+ Window* win = Window_get_active();
255
+ if (!win) return;
256
+
257
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_DPAD_LEFT, KEY_GAMEPAD_LEFT);
258
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_DPAD_RIGHT, KEY_GAMEPAD_RIGHT);
259
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_DPAD_UP, KEY_GAMEPAD_UP);
260
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_DPAD_DOWN, KEY_GAMEPAD_DOWN);
261
+
262
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_A, KEY_GAMEPAD_A);
263
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_B, KEY_GAMEPAD_B);
264
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_X, KEY_GAMEPAD_X);
265
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_Y, KEY_GAMEPAD_Y);
266
+
267
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_LEFT_SHOULDER, KEY_GAMEPAD_SHOULDER_LEFT);
268
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_RIGHT_SHOULDER, KEY_GAMEPAD_SHOULDER_RIGHT);
269
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_LEFT_THUMB, KEY_GAMEPAD_THUMB_LEFT);
270
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_RIGHT_THUMB, KEY_GAMEPAD_THUMB_RIGHT);
271
+
272
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_START, KEY_GAMEPAD_START);
273
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_BACK, KEY_GAMEPAD_SELECT);
274
+
275
+ handle_gamepad_trigger_event(win, state.Gamepad.bLeftTrigger, prev_state.Gamepad.bLeftTrigger, KEY_GAMEPAD_TRIGGER_LEFT);
276
+ handle_gamepad_trigger_event(win, state.Gamepad.bRightTrigger, prev_state.Gamepad.bRightTrigger, KEY_GAMEPAD_TRIGGER_RIGHT);
277
+ }
278
+
279
+ void
280
+ poll_gamepads ()
281
+ {
282
+ static XINPUT_STATE prev_state;
283
+ static bool prev_detected = false;
284
+
285
+ XINPUT_STATE state = {0};
286
+ bool detected = XInputGetState(0, &state) == ERROR_SUCCESS;
287
+
288
+ if (detected != prev_detected)
289
+ {
290
+ prev_detected = detected;
291
+ if (detected) prev_state = {0};
292
+ }
293
+
294
+ if (!detected) return;
295
+
296
+ if (state.dwPacketNumber != prev_state.dwPacketNumber)
297
+ handle_gamepad_events(state, prev_state);
298
+
299
+ prev_state = state;
300
+ }
301
+
302
+
219
303
  }// Reflex
data/src/win32/event.h CHANGED
@@ -44,6 +44,9 @@ namespace Reflex
44
44
  };// NativeWheelEvent
45
45
 
46
46
 
47
+ void poll_gamepads ();
48
+
49
+
47
50
  }// Reflex
48
51
 
49
52
 
data/src/win32/window.cpp CHANGED
@@ -154,13 +154,9 @@ namespace Reflex
154
154
  {
155
155
  WindowData* self = get_data(win);
156
156
 
157
- double now = Xot::time();
158
- Reflex::UpdateEvent e(now, now - self->prev_time_update);
159
- self->prev_time_update = now;
157
+ poll_gamepads();
160
158
 
161
- win->on_update(&e);
162
- if (!e.is_blocked())
163
- Reflex::View_update_tree(win->root(), e);
159
+ Window_call_update_event(win);
164
160
 
165
161
  if (self->redraw)
166
162
  {
@@ -347,8 +343,13 @@ namespace Reflex
347
343
  {
348
344
  case WM_ACTIVATE:
349
345
  {
350
- if ((wp & 0xFFFF) == WA_INACTIVE)
346
+ if (LOWORD(wp) == WA_INACTIVE)
347
+ {
348
+ Window_call_deactivate_event(win);
351
349
  self->pressing_keys.clear();
350
+ }
351
+ else
352
+ Window_call_activate_event(win);
352
353
  break;
353
354
  }
354
355
 
data/src/window.cpp CHANGED
@@ -52,6 +52,17 @@ namespace Reflex
52
52
  return windows;
53
53
  }
54
54
 
55
+ Window*
56
+ Window_get_active ()
57
+ {
58
+ for (auto& w : Window_all())
59
+ {
60
+ if (Xot::has_flag(w->self->flags, Window::Data::ACTIVE))
61
+ return w;
62
+ }
63
+ return NULL;
64
+ }
65
+
55
66
 
56
67
  Window::Data::Data ()
57
68
  : flags(Window_default_flags())
@@ -129,7 +140,7 @@ namespace Reflex
129
140
  return;
130
141
 
131
142
  targets.insert(
132
- target == CAPTURE_ALL ? targets.begin() : targets.end(),
143
+ target == CAPTURE_ALL_EVENTS ? targets.begin() : targets.end(),
133
144
  target);
134
145
  }
135
146
 
@@ -163,21 +174,40 @@ namespace Reflex
163
174
  }
164
175
 
165
176
  void
166
- Window_call_activate_event (Reflex::Window* window)
177
+ Window_call_activate_event (Window* window)
167
178
  {
168
179
  if (!window) return;
169
180
 
170
- Reflex::Event e;
181
+ Xot::add_flag(&window->self->flags, Window::Data::ACTIVE);
182
+
183
+ Event e;
171
184
  window->on_activate(&e);
172
185
  }
173
186
 
174
187
  void
175
- Window_call_deactivate_event (Reflex::Window* window)
188
+ Window_call_deactivate_event (Window* window)
176
189
  {
177
190
  if (!window) return;
178
191
 
179
- Reflex::Event e;
192
+ Event e;
180
193
  window->on_deactivate(&e);
194
+
195
+ Xot::remove_flag(&window->self->flags, Window::Data::ACTIVE);
196
+ }
197
+
198
+ void
199
+ Window_call_update_event (Window* window)
200
+ {
201
+ Window::Data* self = window->self.get();
202
+
203
+ double now = Xot::time();
204
+ UpdateEvent e(now, now - self->prev_time_update);
205
+ self->prev_time_update = now;
206
+
207
+ window->on_update(&e);
208
+ if (e.is_blocked()) return;
209
+
210
+ View_update_tree(window->root(), e);
181
211
  }
182
212
 
183
213
  void
@@ -203,19 +233,19 @@ namespace Reflex
203
233
 
204
234
  window->on_draw(event);
205
235
  if (!event->is_blocked())
206
- Reflex::View_draw_tree(window->root(), event, 0, frame.move_to(0));
236
+ View_draw_tree(window->root(), event, 0, frame.move_to(0));
207
237
 
208
238
  painter->pop_state();
209
239
  painter->end();
210
240
  }
211
241
 
212
242
  static bool
213
- is_capturing_all (
243
+ is_capturing (
214
244
  const View* view, const CaptureTargetIDList& targets, View::Capture type)
215
245
  {
216
246
  return
217
247
  !targets.empty() &&
218
- targets[0] == CAPTURE_ALL &&
248
+ targets[0] == CAPTURE_ALL_EVENTS &&
219
249
  (view->capture() & type) == type;
220
250
  }
221
251
 
@@ -227,20 +257,11 @@ namespace Reflex
227
257
  if (!event)
228
258
  argument_error(__FILE__, __LINE__);
229
259
 
230
- window->on_key(event);
231
-
232
- switch (event->action())
233
- {
234
- case KeyEvent::DOWN: window->on_key_down(event); break;
235
- case KeyEvent::UP: window->on_key_up(event); break;
236
- default: break;
237
- }
238
-
239
260
  for (auto& [view, targets] : window->self->captures)
240
261
  {
241
262
  if (
242
263
  !view->window() ||
243
- !is_capturing_all(view.get(), targets, View::CAPTURE_KEY))
264
+ !is_capturing(view.get(), targets, View::CAPTURE_KEY))
244
265
  {
245
266
  continue;
246
267
  }
@@ -248,9 +269,24 @@ namespace Reflex
248
269
  KeyEvent e = event->dup();
249
270
  KeyEvent_set_captured(&e, true);
250
271
  View_call_key_event(const_cast<View*>(view.get()), &e);
272
+
273
+ if (e.is_blocked()) event->block();
274
+ }
275
+
276
+ if (!event->is_blocked())
277
+ window->on_key(event);
278
+
279
+ if (!event->is_blocked())
280
+ {
281
+ switch (event->action())
282
+ {
283
+ case KeyEvent::DOWN: window->on_key_down(event); break;
284
+ case KeyEvent::UP: window->on_key_up(event); break;
285
+ default: break;
286
+ }
251
287
  }
252
288
 
253
- if (window->self->focus)
289
+ if (!event->is_blocked() && window->self->focus)
254
290
  View_call_key_event(window->self->focus.get(), event);
255
291
 
256
292
  cleanup_captures(window);
@@ -367,7 +403,7 @@ namespace Reflex
367
403
  result->clear();
368
404
  for (const auto& [view, targets] : window->self->captures)
369
405
  {
370
- if (is_capturing_all(view.get(), targets, View::CAPTURE_POINTER))
406
+ if (is_capturing(view.get(), targets, View::CAPTURE_POINTER))
371
407
  result->emplace_back(view);
372
408
  }
373
409
  }
@@ -416,7 +452,7 @@ namespace Reflex
416
452
 
417
453
  static void
418
454
  capture_targeted_pointers_and_call_events (
419
- ExtractedPointerIDSet* extracteds,
455
+ ExtractedPointerIDSet* extracteds, bool* blocked,
420
456
  Window* window, const PointerMap& pointers)
421
457
  {
422
458
  for (auto& [view, targets] : window->self->captures)
@@ -430,6 +466,8 @@ namespace Reflex
430
466
 
431
467
  PointerEvent_update_for_capturing_view(&event, view);
432
468
  View_call_pointer_event(const_cast<View*>(view.get()), &event);
469
+
470
+ if (event.is_blocked()) *blocked = true;
433
471
  }
434
472
  }
435
473
 
@@ -451,7 +489,7 @@ namespace Reflex
451
489
 
452
490
  static void
453
491
  capture_hovering_pointers_and_call_events (
454
- ExtractedPointerIDSet* extracteds,
492
+ ExtractedPointerIDSet* extracteds, bool* blocked,
455
493
  const ViewList& views_capturing_all, const PointerMap& pointers)
456
494
  {
457
495
  assert(extracteds);
@@ -470,6 +508,8 @@ namespace Reflex
470
508
  PointerEvent e = event.dup();
471
509
  PointerEvent_update_for_capturing_view(&e, view);
472
510
  View_call_pointer_event(const_cast<View*>(view.get()), &e);
511
+
512
+ if (e.is_blocked()) *blocked = true;
473
513
  }
474
514
  }
475
515
 
@@ -512,11 +552,17 @@ namespace Reflex
512
552
  });
513
553
 
514
554
  ExtractedPointerIDSet extracteds;
515
- capture_targeted_pointers_and_call_events(&extracteds, window, pointers);
555
+ bool blocked = false;
556
+
557
+ capture_targeted_pointers_and_call_events(
558
+ &extracteds, &blocked, window, pointers);
516
559
  erase_extracted_pointers(&pointers, extracteds);
517
560
 
518
- capture_hovering_pointers_and_call_events(&extracteds, views_capturing_all, pointers);
561
+ capture_hovering_pointers_and_call_events(
562
+ &extracteds, &blocked, views_capturing_all, pointers);
519
563
  erase_extracted_pointers(event, extracteds);
564
+
565
+ if (blocked) event->block();
520
566
  }
521
567
 
522
568
  void
@@ -529,20 +575,24 @@ namespace Reflex
529
575
 
530
576
  setup_pointer_event(window, event);
531
577
 
532
- window->on_pointer(event);
578
+ call_captured_pointer_events(window, event);
579
+
580
+ if (!event->is_blocked() && !event->empty())
581
+ window->on_pointer(event);
533
582
 
534
- switch ((*event)[0].action())
583
+ if (!event->is_blocked() && !event->empty())
535
584
  {
536
- case Pointer::DOWN: window->on_pointer_down(event); break;
537
- case Pointer::UP: window->on_pointer_up(event); break;
538
- case Pointer::MOVE: window->on_pointer_move(event); break;
539
- case Pointer::CANCEL: window->on_pointer_cancel(event); break;
540
- default: break;
585
+ switch ((*event)[0].action())
586
+ {
587
+ case Pointer::DOWN: window->on_pointer_down(event); break;
588
+ case Pointer::UP: window->on_pointer_up(event); break;
589
+ case Pointer::MOVE: window->on_pointer_move(event); break;
590
+ case Pointer::CANCEL: window->on_pointer_cancel(event); break;
591
+ default: break;
592
+ }
541
593
  }
542
594
 
543
- call_captured_pointer_events(window, event);
544
-
545
- if (!event->empty())
595
+ if (!event->is_blocked() && !event->empty())
546
596
  {
547
597
  PointerEvent_update_for_child_view(event, window->root());
548
598
  View_call_pointer_event(window->root(), event);