reflexion 0.6.0 → 0.7.0

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.
Files changed (275) hide show
  1. checksums.yaml +4 -4
  2. data/.doc/ext/reflex/application.cpp +37 -6
  3. data/.doc/ext/reflex/capture_event.cpp +1 -1
  4. data/.doc/ext/reflex/chase_constraint.cpp +1 -1
  5. data/.doc/ext/reflex/clipboard.cpp +1 -1
  6. data/.doc/ext/reflex/color_event.cpp +76 -0
  7. data/.doc/ext/reflex/color_picker.cpp +113 -0
  8. data/.doc/ext/reflex/constraint.cpp +1 -1
  9. data/.doc/ext/reflex/contact_event.cpp +1 -1
  10. data/.doc/ext/reflex/control_change_event.cpp +1 -1
  11. data/.doc/ext/reflex/device.cpp +1 -1
  12. data/.doc/ext/reflex/device_event.cpp +1 -1
  13. data/.doc/ext/reflex/draw_event.cpp +1 -1
  14. data/.doc/ext/reflex/ellipse_shape.cpp +1 -1
  15. data/.doc/ext/reflex/event.cpp +1 -1
  16. data/.doc/ext/reflex/file_dialog.cpp +181 -0
  17. data/.doc/ext/reflex/file_event.cpp +103 -0
  18. data/.doc/ext/reflex/filter.cpp +1 -1
  19. data/.doc/ext/reflex/focus_event.cpp +1 -1
  20. data/.doc/ext/reflex/frame_event.cpp +1 -1
  21. data/.doc/ext/reflex/gamepad.cpp +48 -0
  22. data/.doc/ext/reflex/image_view.cpp +1 -1
  23. data/.doc/ext/reflex/key_event.cpp +1 -274
  24. data/.doc/ext/reflex/line_shape.cpp +1 -1
  25. data/.doc/ext/reflex/link_constraint.cpp +1 -1
  26. data/.doc/ext/reflex/menu.cpp +6 -5
  27. data/.doc/ext/reflex/midi.cpp +9 -2
  28. data/.doc/ext/reflex/midi_event.cpp +1 -1
  29. data/.doc/ext/reflex/motion_event.cpp +1 -1
  30. data/.doc/ext/reflex/native.cpp +18 -6
  31. data/.doc/ext/reflex/note_event.cpp +1 -1
  32. data/.doc/ext/reflex/pin.cpp +1 -1
  33. data/.doc/ext/reflex/pointer.cpp +1 -1
  34. data/.doc/ext/reflex/pointer_event.cpp +2 -2
  35. data/.doc/ext/reflex/polygon_shape.cpp +1 -1
  36. data/.doc/ext/reflex/rect_shape.cpp +1 -1
  37. data/.doc/ext/reflex/reflex.cpp +566 -232
  38. data/.doc/ext/reflex/screen.cpp +27 -1
  39. data/.doc/ext/reflex/scroll_event.cpp +1 -1
  40. data/.doc/ext/reflex/selector.cpp +1 -1
  41. data/.doc/ext/reflex/shape.cpp +1 -1
  42. data/.doc/ext/reflex/snap_constraint.cpp +1 -1
  43. data/.doc/ext/reflex/style.cpp +1 -1
  44. data/.doc/ext/reflex/style_length.cpp +1 -1
  45. data/.doc/ext/reflex/text_event.cpp +104 -0
  46. data/.doc/ext/reflex/timer.cpp +1 -1
  47. data/.doc/ext/reflex/timer_event.cpp +1 -1
  48. data/.doc/ext/reflex/update_event.cpp +1 -1
  49. data/.doc/ext/reflex/view.cpp +110 -56
  50. data/.doc/ext/reflex/wheel_constraint.cpp +1 -1
  51. data/.doc/ext/reflex/wheel_event.cpp +1 -1
  52. data/.doc/ext/reflex/window.cpp +226 -17
  53. data/.github/workflows/release-gem.yml +2 -49
  54. data/.github/workflows/tag.yml +5 -28
  55. data/.github/workflows/test.yml +1 -37
  56. data/ChangeLog.md +54 -0
  57. data/Gemfile.lock +13 -8
  58. data/Rakefile +13 -2
  59. data/Reflex.podspec +2 -2
  60. data/VERSION +1 -1
  61. data/ext/reflex/application.cpp +41 -6
  62. data/ext/reflex/capture_event.cpp +1 -1
  63. data/ext/reflex/chase_constraint.cpp +1 -1
  64. data/ext/reflex/clipboard.cpp +1 -1
  65. data/ext/reflex/color_event.cpp +80 -0
  66. data/ext/reflex/color_picker.cpp +121 -0
  67. data/ext/reflex/constraint.cpp +1 -1
  68. data/ext/reflex/contact_event.cpp +1 -1
  69. data/ext/reflex/control_change_event.cpp +1 -1
  70. data/ext/reflex/device.cpp +1 -1
  71. data/ext/reflex/device_event.cpp +1 -1
  72. data/ext/reflex/draw_event.cpp +1 -1
  73. data/ext/reflex/ellipse_shape.cpp +1 -1
  74. data/ext/reflex/event.cpp +1 -1
  75. data/ext/reflex/extconf.rb +4 -2
  76. data/ext/reflex/file_dialog.cpp +194 -0
  77. data/ext/reflex/file_event.cpp +108 -0
  78. data/ext/reflex/filter.cpp +1 -1
  79. data/ext/reflex/focus_event.cpp +1 -1
  80. data/ext/reflex/frame_event.cpp +1 -1
  81. data/ext/reflex/gamepad.cpp +50 -0
  82. data/ext/reflex/image_view.cpp +1 -1
  83. data/ext/reflex/key_event.cpp +1 -275
  84. data/ext/reflex/line_shape.cpp +1 -1
  85. data/ext/reflex/link_constraint.cpp +1 -1
  86. data/ext/reflex/menu.cpp +8 -7
  87. data/ext/reflex/midi.cpp +11 -3
  88. data/ext/reflex/midi_event.cpp +1 -1
  89. data/ext/reflex/motion_event.cpp +1 -1
  90. data/ext/reflex/native.cpp +18 -6
  91. data/ext/reflex/note_event.cpp +1 -1
  92. data/ext/reflex/pin.cpp +1 -1
  93. data/ext/reflex/pointer.cpp +1 -1
  94. data/ext/reflex/pointer_event.cpp +2 -2
  95. data/ext/reflex/polygon_shape.cpp +1 -1
  96. data/ext/reflex/rect_shape.cpp +1 -1
  97. data/ext/reflex/reflex.cpp +568 -232
  98. data/ext/reflex/screen.cpp +31 -2
  99. data/ext/reflex/scroll_event.cpp +1 -1
  100. data/ext/reflex/selector.cpp +1 -1
  101. data/ext/reflex/selector.h +9 -9
  102. data/ext/reflex/shape.cpp +1 -1
  103. data/ext/reflex/snap_constraint.cpp +1 -1
  104. data/ext/reflex/style.cpp +1 -1
  105. data/ext/reflex/style_length.cpp +1 -1
  106. data/ext/reflex/text_event.cpp +111 -0
  107. data/ext/reflex/timer.cpp +1 -1
  108. data/ext/reflex/timer_event.cpp +1 -1
  109. data/ext/reflex/update_event.cpp +1 -1
  110. data/ext/reflex/view.cpp +121 -61
  111. data/ext/reflex/wheel_constraint.cpp +1 -1
  112. data/ext/reflex/wheel_event.cpp +1 -1
  113. data/ext/reflex/window.cpp +256 -25
  114. data/include/reflex/application.h +10 -0
  115. data/include/reflex/color_picker.h +65 -0
  116. data/include/reflex/event.h +178 -88
  117. data/include/reflex/file_dialog.h +73 -0
  118. data/include/reflex/gamepad.h +3 -0
  119. data/include/reflex/menu.h +3 -3
  120. data/include/reflex/midi.h +3 -0
  121. data/include/reflex/reflex.h +2 -0
  122. data/include/reflex/ruby/application.h +6 -1
  123. data/include/reflex/ruby/color_picker.h +89 -0
  124. data/include/reflex/ruby/event.h +62 -29
  125. data/include/reflex/ruby/file_dialog.h +89 -0
  126. data/include/reflex/ruby/gamepad.h +46 -0
  127. data/include/reflex/ruby/reflex.h +3 -0
  128. data/include/reflex/ruby/view.h +92 -44
  129. data/include/reflex/ruby/window.h +60 -1
  130. data/include/reflex/ruby.h +3 -0
  131. data/include/reflex/screen.h +11 -2
  132. data/include/reflex/view.h +28 -16
  133. data/include/reflex/window.h +40 -8
  134. data/include/reflex.h +3 -0
  135. data/lib/reflex/application.rb +5 -5
  136. data/lib/reflex/autoinit.rb +3 -1
  137. data/lib/reflex/button.rb +3 -3
  138. data/lib/reflex/color_event.rb +16 -0
  139. data/lib/reflex/color_picker.rb +45 -0
  140. data/lib/reflex/control_change_event.rb +17 -0
  141. data/lib/reflex/file_dialog.rb +70 -0
  142. data/lib/reflex/file_event.rb +37 -0
  143. data/lib/reflex/helper.rb +58 -7
  144. data/lib/reflex/key_event.rb +9 -4
  145. data/lib/reflex/menu.rb +17 -1
  146. data/lib/reflex/model.rb +19 -19
  147. data/lib/reflex/note_event.rb +1 -1
  148. data/lib/reflex/pointer.rb +14 -3
  149. data/lib/reflex/pointer_event.rb +12 -1
  150. data/lib/reflex/reflex.rb +5 -0
  151. data/lib/reflex/shape.rb +14 -14
  152. data/lib/reflex/style.rb +16 -16
  153. data/lib/reflex/text_event.rb +40 -0
  154. data/lib/reflex/timer.rb +3 -3
  155. data/lib/reflex/view.rb +29 -14
  156. data/lib/reflex/wheel_event.rb +12 -3
  157. data/lib/reflex/window.rb +25 -13
  158. data/lib/reflex.rb +8 -2
  159. data/reflex.gemspec +4 -4
  160. data/samples/ime.rb +100 -0
  161. data/samples/ios/hello/hello.xcodeproj/project.pbxproj +236 -224
  162. data/samples/menu.rb +4 -1
  163. data/samples/model.rb +3 -3
  164. data/samples/test/alert.rb +93 -0
  165. data/samples/test/always_on.rb +76 -0
  166. data/samples/test/app_background.rb +51 -0
  167. data/samples/test/boundary.rb +75 -0
  168. data/samples/test/callback_exception.rb +101 -0
  169. data/samples/test/cancel.rb +53 -0
  170. data/samples/test/color_picker.rb +63 -0
  171. data/samples/test/file_dialog.rb +102 -0
  172. data/samples/window_style.rb +156 -0
  173. data/src/application.cpp +159 -18
  174. data/src/application.h +92 -7
  175. data/src/color_picker.cpp +113 -0
  176. data/src/color_picker.h +36 -0
  177. data/src/event.cpp +410 -252
  178. data/src/event.h +12 -0
  179. data/src/file_dialog.cpp +163 -0
  180. data/src/file_dialog.h +52 -0
  181. data/src/gamepad.cpp +42 -5
  182. data/src/gamepad.h +4 -0
  183. data/src/ios/app_delegate.mm +5 -3
  184. data/src/ios/application.mm +24 -0
  185. data/src/ios/color_picker.mm +19 -0
  186. data/src/ios/file_dialog.mm +21 -0
  187. data/src/ios/gamepad.mm +4 -4
  188. data/src/ios/menu.mm +5 -0
  189. data/src/ios/reflex.mm +8 -0
  190. data/src/ios/screen.mm +26 -0
  191. data/src/ios/view_controller.mm +2 -30
  192. data/src/ios/window.mm +38 -9
  193. data/src/menu.cpp +66 -4
  194. data/src/menu.h +17 -1
  195. data/src/midi.cpp +61 -31
  196. data/src/osx/app_delegate.h +4 -0
  197. data/src/osx/app_delegate.mm +102 -22
  198. data/src/osx/application.mm +88 -6
  199. data/src/osx/color_picker.mm +112 -0
  200. data/src/osx/event.h +10 -0
  201. data/src/osx/event.mm +84 -30
  202. data/src/osx/file_dialog.mm +168 -0
  203. data/src/osx/gamepad.mm +2 -2
  204. data/src/osx/gamepad_hid.mm +1 -1
  205. data/src/osx/menu.mm +177 -33
  206. data/src/osx/native_window.h +16 -2
  207. data/src/osx/native_window.mm +114 -57
  208. data/src/osx/opengl_view.h +2 -1
  209. data/src/osx/opengl_view.mm +184 -43
  210. data/src/osx/reflex.mm +38 -0
  211. data/src/osx/screen.mm +36 -0
  212. data/src/osx/window.mm +106 -24
  213. data/src/pointer.cpp +13 -4
  214. data/src/pointer.h +4 -0
  215. data/src/rays.h +46 -0
  216. data/src/sdl/application.cpp +68 -8
  217. data/src/sdl/color_picker.cpp +18 -0
  218. data/src/sdl/event.cpp +24 -3
  219. data/src/sdl/event.h +12 -0
  220. data/src/sdl/file_dialog.cpp +20 -0
  221. data/src/sdl/gamepad.cpp +3 -7
  222. data/src/sdl/menu.cpp +5 -0
  223. data/src/sdl/reflex.cpp +20 -0
  224. data/src/sdl/screen.cpp +36 -0
  225. data/src/sdl/window.cpp +142 -29
  226. data/src/shape.cpp +15 -10
  227. data/src/view.cpp +287 -198
  228. data/src/view.h +4 -2
  229. data/src/vk.h +416 -0
  230. data/src/win32/application.cpp +162 -12
  231. data/src/win32/application.h +20 -0
  232. data/src/win32/color_picker.cpp +82 -0
  233. data/src/win32/event.cpp +114 -12
  234. data/src/win32/event.h +21 -4
  235. data/src/win32/file_dialog.cpp +226 -0
  236. data/src/win32/gamepad.cpp +9 -2
  237. data/src/win32/menu.cpp +191 -53
  238. data/src/win32/menu.h +9 -1
  239. data/src/win32/opengl.cpp +2 -9
  240. data/src/win32/reflex.cpp +44 -3
  241. data/src/win32/screen.cpp +164 -3
  242. data/src/win32/screen.h +8 -0
  243. data/src/win32/tray.cpp +191 -0
  244. data/src/win32/tray.h +22 -0
  245. data/src/win32/vk.cpp +11 -0
  246. data/src/win32/window.cpp +809 -148
  247. data/src/win32/window.h +6 -0
  248. data/src/window.cpp +718 -185
  249. data/src/window.h +66 -33
  250. data/src/world.cpp +9 -1
  251. data/test/helper.rb +17 -0
  252. data/test/test_application.rb +90 -1
  253. data/test/test_chase_constraint.rb +1 -3
  254. data/test/test_color_event.rb +36 -0
  255. data/test/test_color_picker.rb +62 -0
  256. data/test/test_constraint.rb +2 -4
  257. data/test/test_event.rb +1 -1
  258. data/test/test_file_dialog.rb +48 -0
  259. data/test/test_file_event.rb +43 -0
  260. data/test/test_has_text_preedit.rb +54 -0
  261. data/test/test_key_event.rb +20 -5
  262. data/test/test_link_constraint.rb +1 -3
  263. data/test/test_menu.rb +40 -10
  264. data/test/test_pointer.rb +33 -23
  265. data/test/test_pointer_event.rb +24 -9
  266. data/test/test_reflex.rb +36 -0
  267. data/test/test_screen.rb +24 -2
  268. data/test/test_snap_constraint.rb +1 -3
  269. data/test/test_text_event.rb +54 -0
  270. data/test/test_view.rb +41 -4
  271. data/test/test_wheel_constraint.rb +1 -3
  272. data/test/test_wheel_event.rb +13 -5
  273. data/test/test_window.rb +196 -44
  274. metadata +80 -10
  275. data/.github/workflows/utils.rb +0 -127
data/src/window.cpp CHANGED
@@ -2,12 +2,17 @@
2
2
 
3
3
 
4
4
  #include <assert.h>
5
+ #include <math.h>
5
6
  #include <algorithm>
6
7
  #include <set>
8
+ #include <rays/rays.h>
7
9
  #include "reflex/exception.h"
8
10
  #include "reflex/debug.h"
11
+ #include "application.h"
9
12
  #include "view.h"
10
13
  #include "event.h"
14
+ #include "midi.h"
15
+ #include "rays.h"
11
16
 
12
17
 
13
18
  namespace Reflex
@@ -23,6 +28,31 @@ namespace Reflex
23
28
  using WeakViewList = Window::Data::WeakViewList;
24
29
 
25
30
 
31
+ Window::Data::Data ()
32
+ : flags(Window_default_flags() | REDRAW)
33
+ {
34
+ prev_time_update = prev_time_draw = Xot::time();
35
+ }
36
+
37
+ Window::Data::~Data ()
38
+ {
39
+ }
40
+
41
+
42
+ static Window_CreateRootViewFun create_root_view_fun = NULL;
43
+
44
+ void
45
+ Window_set_create_root_view_fun (Window_CreateRootViewFun fun)
46
+ {
47
+ create_root_view_fun = fun;
48
+ }
49
+
50
+ static View*
51
+ create_root_view ()
52
+ {
53
+ return create_root_view_fun ? create_root_view_fun() : new View();
54
+ }
55
+
26
56
  void
27
57
  Window_register (Window* win)
28
58
  {
@@ -64,15 +94,11 @@ namespace Reflex
64
94
  return NULL;
65
95
  }
66
96
 
67
-
68
- Window::Data::Data ()
69
- : flags(Window_default_flags())
70
- {
71
- prev_time_update = prev_time_draw = Xot::time();
72
- }
73
-
74
- Window::Data::~Data ()
97
+ void
98
+ Window_skip_update_time_of_all (double seconds)
75
99
  {
100
+ for (auto& window : Window_all())
101
+ window->self->prev_time_update += seconds;
76
102
  }
77
103
 
78
104
 
@@ -104,20 +130,6 @@ namespace Reflex
104
130
  }
105
131
  }
106
132
 
107
- static Window_CreateRootViewFun create_root_view_fun = NULL;
108
-
109
- void
110
- Window_set_create_root_view_fun (Window_CreateRootViewFun fun)
111
- {
112
- create_root_view_fun = fun;
113
- }
114
-
115
- static View*
116
- create_root_view ()
117
- {
118
- return create_root_view_fun ? create_root_view_fun() : new View();
119
- }
120
-
121
133
  void
122
134
  Window_register_capture (Window* window, View* view, Pointer::ID target)
123
135
  {
@@ -167,18 +179,84 @@ namespace Reflex
167
179
  }
168
180
  }
169
181
 
182
+ static const uint MOUSE_BUTTONS =
183
+ Pointer::MOUSE_LEFT |
184
+ Pointer::MOUSE_RIGHT |
185
+ Pointer::MOUSE_MIDDLE;
186
+
187
+ void
188
+ Window_cancel_active_pointers (Window* window)
189
+ {
190
+ assert(window);
191
+
192
+ // synthesizes a cancel for every pointer being pressed and delivers
193
+ // it through the normal path, so the existing up/cancel handling
194
+ // releases captures and pressed states
195
+
196
+ Window::Data* self = window->self.get();
197
+
198
+ double now = Xot::time();
199
+
200
+ PointerEvent event;
201
+
202
+ const auto& mouse = self->prev_mouse_pointer;
203
+ uint pressed = Pointer_mask_flag(mouse, MOUSE_BUTTONS);
204
+ if (pressed != 0)
205
+ {
206
+ Pointer pointer = mouse;
207
+ Pointer_set_types(&pointer, Pointer::MOUSE | pressed);
208
+ Pointer_set_action(&pointer, Pointer::CANCEL);
209
+ Pointer_set_time(&pointer, now);
210
+ PointerEvent_add_pointer(&event, pointer);
211
+ }
212
+
213
+ for (const auto& p : self->prev_pointers)
214
+ {
215
+ if (!p.down()) continue;
216
+ Pointer pointer = p;
217
+ Pointer_set_action(&pointer, Pointer::CANCEL);
218
+ Pointer_set_time(&pointer, now);
219
+ PointerEvent_add_pointer(&event, pointer);
220
+ }
221
+
222
+ if (event.empty()) return;
223
+
224
+ ++self->pointer_cancels;
225
+ Window_call_pointer_event(window, &event);
226
+ }
227
+
228
+ uint
229
+ Window_get_pointer_cancels (const Window& window)
230
+ {
231
+ return window.self->pointer_cancels;
232
+ }
233
+
234
+ void
235
+ Window_call_close (Window* window)
236
+ {
237
+ if (!window) return;
238
+
239
+ Application_guard([&]()
240
+ {
241
+ window->close();
242
+ });
243
+ }
244
+
170
245
  void
171
246
  Window_call_activate_event (Window* window)
172
247
  {
173
248
  if (!window) return;
174
249
 
175
- Xot::add_flag(&window->self->flags, Window::Data::ACTIVE);
250
+ Application_guard([&]()
251
+ {
252
+ Xot::add_flag(&window->self->flags, Window::Data::ACTIVE);
176
253
 
177
- Event e;
178
- window->on_activate(&e);
179
- if (e.is_blocked()) return;
254
+ Event e;
255
+ window->on_activate(&e);
256
+ if (e.is_blocked()) return;
180
257
 
181
- View_activate_tree(window->root(), &e);
258
+ View_activate_tree(window->root(), &e);
259
+ });
182
260
  }
183
261
 
184
262
  void
@@ -186,28 +264,186 @@ namespace Reflex
186
264
  {
187
265
  if (!window) return;
188
266
 
189
- Xot::remove_flag(&window->self->flags, Window::Data::ACTIVE);
267
+ Application_guard([&]()
268
+ {
269
+ Xot::remove_flag(&window->self->flags, Window::Data::ACTIVE);
190
270
 
191
- Event e;
192
- window->on_deactivate(&e);
193
- if (e.is_blocked()) return;
271
+ Event e;
272
+ window->on_deactivate(&e);
273
+ if (e.is_blocked()) return;
274
+
275
+ View_deactivate_tree(window->root(), &e);
276
+ });
277
+ }
278
+
279
+ static bool
280
+ is_pointer_through_by_alpha (const Window::Data& self)
281
+ {
282
+ return
283
+ Xot::has_flag(self.flags, Window::FLAG_POINTER_THROUGH) &&
284
+ self.pointer_through_alpha < 1;
285
+ }
286
+
287
+ static bool
288
+ has_pressed_pointer (const Window::Data& self)
289
+ {
290
+ if (Pointer_mask_flag(self.prev_mouse_pointer, MOUSE_BUTTONS) != 0)
291
+ return true;
292
+
293
+ for (const auto& p : self.prev_pointers)
294
+ {
295
+ if (p.down())
296
+ return true;
297
+ }
298
+ return false;
299
+ }
300
+
301
+ static float
302
+ get_cache_alpha (Window::Data* self, const Point& position)
303
+ {
304
+ if (!self->draw_cache)
305
+ return 0;
306
+
307
+ Rays::activate_offscreen_context();
308
+
309
+ Color color;
310
+ bool ret = Rays::Painter_get_pixel(
311
+ &color, &self->draw_cache_painter, position.x, position.y);
312
+ return ret ? color.alpha : 0;
313
+ }
194
314
 
195
- View_deactivate_tree(window->root(), &e);
315
+ static void
316
+ apply_pointer_through (Window* window)
317
+ {
318
+ using Data = Window::Data;
319
+
320
+ Data* self = window->self.get();
321
+
322
+ if (!is_pointer_through_by_alpha(*self))
323
+ {
324
+ Xot::remove_flag(
325
+ &self->flags,
326
+ Data::POINTER_THROUGH_PAUSED | Data::POINTER_THROUGH_ENTERED);
327
+ }
328
+
329
+ Window_set_pointer_through(
330
+ window,
331
+ Xot::has_flag(self->flags, Window::FLAG_POINTER_THROUGH) &&
332
+ !Xot::has_flag(self->flags, Data::POINTER_THROUGH_PAUSED));
333
+ }
334
+
335
+ static bool is_inside_window (Window* window, const Point& position);
336
+
337
+ static void call_pointer_event (Window* window, PointerEvent* event);
338
+
339
+ static void
340
+ update_pointer_through (Window* window)
341
+ {
342
+ using Data = Window::Data;
343
+
344
+ Data* self = window->self.get();
345
+ if (!is_pointer_through_by_alpha(*self))
346
+ return;
347
+ if (has_pressed_pointer(*self))
348
+ return;
349
+
350
+ Point pos;
351
+ bool over_and_uncov = Window_is_pointer_over_and_uncovered(&pos, *window);
352
+ bool inside = over_and_uncov && is_inside_window(window, pos);
353
+ bool opaque =
354
+ over_and_uncov &&
355
+ (!inside || get_cache_alpha(self, pos) > self->pointer_through_alpha);
356
+ if (opaque != Xot::has_flag(self->flags, Data::POINTER_THROUGH_PAUSED))
357
+ {
358
+ Xot::update_flag(&self->flags, Data::POINTER_THROUGH_PAUSED, opaque);
359
+ apply_pointer_through(window);
360
+ }
361
+
362
+ bool entered = opaque && inside;
363
+ if (entered == Xot::has_flag(self->flags, Data::POINTER_THROUGH_ENTERED))
364
+ return;
365
+
366
+ Xot::update_flag(&self->flags, Data::POINTER_THROUGH_ENTERED, entered);
367
+
368
+ PointerEvent event;
369
+ PointerEvent_add_pointer(&event, Pointer(
370
+ 0, Pointer::MOUSE,
371
+ entered ? Pointer::ENTER : Pointer::LEAVE,
372
+ entered ? pos : self->prev_mouse_pointer.position(),
373
+ 0, 0, false, Xot::time()));
374
+ call_pointer_event(window, &event);
196
375
  }
197
376
 
198
377
  void
199
378
  Window_call_update_event (Window* window)
200
379
  {
201
- Window::Data* self = window->self.get();
380
+ Application_guard([&]()
381
+ {
382
+ Window::Data* self = window->self.get();
202
383
 
203
- double now = Xot::time();
204
- UpdateEvent e(now, now - self->prev_time_update);
205
- self->prev_time_update = now;
384
+ MIDI_process_events();
385
+
386
+ update_pointer_through(window);
206
387
 
207
- window->on_update(&e);
208
- if (e.is_blocked()) return;
388
+ double now = Xot::time();
389
+ UpdateEvent e(now, std::max(now - self->prev_time_update, 0.0));
390
+ self->prev_time_update = now;
209
391
 
210
- View_update_tree(window->root(), e);
392
+ window->on_update(&e);
393
+ if (!e.is_blocked())
394
+ View_update_tree(window->root(), e);
395
+ });
396
+ }
397
+
398
+ static bool
399
+ use_draw_cache (const Window::Data& self)
400
+ {
401
+ return is_pointer_through_by_alpha(self);
402
+ }
403
+
404
+ static void
405
+ setup_draw_cache (Window::Data* self, const Bounds& bounds, float density)
406
+ {
407
+ int w = (int) ceil(bounds.width);
408
+ int h = (int) ceil(bounds.height);
409
+ if (w == 0 || h == 0 || !use_draw_cache(*self))
410
+ {
411
+ if (self->draw_cache)
412
+ {
413
+ self->draw_cache = Image();
414
+ self->draw_cache_painter = Painter();
415
+ }
416
+ return;
417
+ }
418
+
419
+ Image& cache = self->draw_cache;
420
+ if (
421
+ !cache ||
422
+ cache.width() != w ||
423
+ cache.height() != h ||
424
+ cache.pixel_density() != density)
425
+ {
426
+ self->draw_cache = Image(w, h, Rays::RGBA, density);
427
+ self->draw_cache_painter = self->draw_cache.painter();
428
+ }
429
+ }
430
+
431
+ static void
432
+ draw_window (Window* window, DrawEvent* event)
433
+ {
434
+ Painter* painter = event->painter();
435
+
436
+ painter->begin();
437
+ painter->push_state();
438
+ {
439
+ painter->clear();
440
+
441
+ window->on_draw(event);
442
+ if (!event->is_blocked())
443
+ View_draw_tree(window->root(), event, 0, event->bounds());
444
+ }
445
+ painter->pop_state();
446
+ painter->end();
211
447
  }
212
448
 
213
449
  void
@@ -218,25 +454,74 @@ namespace Reflex
218
454
  if (!event)
219
455
  argument_error(__FILE__, __LINE__);
220
456
 
221
- Painter* painter = window->painter();
222
- if (!painter)
223
- Xot::invalid_state_error(__FILE__, __LINE__);
457
+ Application_guard([&]()
458
+ {
459
+ Window::Data* self = window->self.get();
224
460
 
225
- Rays::Bounds frame = window->frame();
461
+ Painter* painter = window->painter();
462
+ if (!painter)
463
+ Xot::invalid_state_error(__FILE__, __LINE__);
226
464
 
227
- DrawEvent_set_painter(event, painter);
228
- DrawEvent_set_bounds(event, Bounds(0, 0, frame.width, frame.height));
465
+ Bounds bounds = window->frame().move_to(0);
466
+ setup_draw_cache(self, bounds, painter->pixel_density());
229
467
 
230
- painter->begin();
231
- painter->push_state();
232
- painter->clear();
468
+ DrawEvent_set_painter(event, painter);
469
+ DrawEvent_set_bounds(event, bounds);
233
470
 
234
- window->on_draw(event);
235
- if (!event->is_blocked())
236
- View_draw_tree(window->root(), event, 0, frame.move_to(0));
471
+ if (self->draw_cache)
472
+ {
473
+ painter->bind(self->draw_cache);
474
+ draw_window(window, event);
475
+ painter->unbind();
237
476
 
238
- painter->pop_state();
239
- painter->end();
477
+ painter->begin();
478
+ painter->push_state();
479
+ {
480
+ painter->clear();
481
+ painter->set_fill(1);
482
+ painter->no_stroke();
483
+ painter->no_shader();
484
+ painter->image(self->draw_cache, 0, 0);
485
+ }
486
+ painter->pop_state();
487
+ painter->end();
488
+ }
489
+ else
490
+ draw_window(window, event);
491
+ });
492
+ }
493
+
494
+ void
495
+ Window_call_frame_event (Window* window)
496
+ {
497
+ if (!window) return;
498
+
499
+ Application_guard([&]()
500
+ {
501
+ Rays::Bounds b = window->frame();
502
+ Rays::Point dpos = b.position() - window->self->prev_position;
503
+ Rays::Point dsize = b.size() - window->self->prev_size;
504
+ window->self->prev_position = b.position();
505
+ window->self->prev_size = b.size();
506
+
507
+ if (dpos == 0 && dsize == 0) return;
508
+
509
+ FrameEvent e(b, dpos.x, dpos.y, 0, dsize.x, dsize.y, 0);
510
+ if (dpos != 0) window->on_move(&e);
511
+ if (dsize != 0)
512
+ {
513
+ Rays::Bounds b = window->frame();
514
+ b.move_to(0, 0);
515
+
516
+ if (window->painter())
517
+ window->painter()->canvas(b, window->painter()->pixel_density());
518
+
519
+ if (window->root())
520
+ View_set_frame(window->root(), b);
521
+
522
+ window->on_resize(&e);
523
+ }
524
+ });
240
525
  }
241
526
 
242
527
  static bool
@@ -249,49 +534,117 @@ namespace Reflex
249
534
  (view->capture() & type) == type;
250
535
  }
251
536
 
537
+ static void
538
+ each_capture (Window* window, auto&& fun)
539
+ {
540
+ assert(window);
541
+
542
+ auto& captures = window->self->captures;
543
+
544
+ ViewList views;
545
+ views.reserve(captures.size());
546
+ for (const auto& [view, targets] : captures)
547
+ views.emplace_back(view);
548
+
549
+ for (auto& view : views)
550
+ {
551
+ if (!view->window()) continue;
552
+
553
+ auto it = captures.find(view);
554
+ if (it == captures.end() || it->second.empty()) continue;
555
+
556
+ fun(const_cast<View*>(view.get()), it->second);
557
+ }
558
+ }
559
+
560
+ static void
561
+ each_capturing_view (Window* window, View::Capture type, auto&& fun)
562
+ {
563
+ each_capture(window, [&](View* view, const CaptureTargetIDList& targets)
564
+ {
565
+ if (is_capturing(view, targets, type))
566
+ fun(view);
567
+ });
568
+ }
569
+
252
570
  void
253
- Window_call_key_event (Window* window, KeyEvent* event)
571
+ Window_call_key_event (Window* window, KeyEvent* event, bool capture)
254
572
  {
255
573
  assert(window);
256
574
 
257
575
  if (!event)
258
576
  argument_error(__FILE__, __LINE__);
259
577
 
260
- window->on_key(event);
261
-
262
- if (!event->is_blocked())
578
+ Application_guard([&]()
263
579
  {
264
- switch (event->action())
580
+ window->on_key(event);
581
+
582
+ if (!event->is_blocked())
265
583
  {
266
- case KeyEvent::DOWN: window->on_key_down(event); break;
267
- case KeyEvent::UP: window->on_key_up(event); break;
268
- default: break;
584
+ switch (event->action())
585
+ {
586
+ case KeyEvent::DOWN: window->on_key_down(event); break;
587
+ case KeyEvent::UP: window->on_key_up(event); break;
588
+ default: break;
589
+ }
269
590
  }
270
- }
271
591
 
272
- if (!event->is_blocked())
273
- {
274
- for (auto& [view, targets] : window->self->captures)
592
+ if (capture && !event->is_blocked())
275
593
  {
276
- if (
277
- !view->window() ||
278
- !is_capturing(view.get(), targets, View::CAPTURE_KEY))
594
+ each_capturing_view(window, View::CAPTURE_KEY, [&](View* view)
279
595
  {
280
- continue;
281
- }
596
+ KeyEvent e = event->dup();
597
+ KeyEvent_set_captured(&e, true);
598
+ View_call_key_event(view, &e);
599
+
600
+ if (e.is_blocked()) event->block();
601
+ });
602
+ }
603
+
604
+ if (!event->is_blocked() && window->self->focus)
605
+ View_call_key_event(window->self->focus.get(), event);
606
+
607
+ if (capture)
608
+ cleanup_captures(window);
609
+ });
610
+ }
282
611
 
283
- KeyEvent e = event->dup();
284
- KeyEvent_set_captured(&e, true);
285
- View_call_key_event(const_cast<View*>(view.get()), &e);
612
+ void
613
+ Window_call_text_event (
614
+ Window* window, TextEvent* event, bool synthesize_key_events)
615
+ {
616
+ assert(window);
617
+
618
+ if (!event)
619
+ argument_error(__FILE__, __LINE__);
620
+
621
+ Application_guard([&]()
622
+ {
623
+ window->on_text(event);
624
+ if (event->is_blocked()) return;
286
625
 
287
- if (e.is_blocked()) event->block();
626
+ switch (event->action())
627
+ {
628
+ case TextEvent::PREEDIT: window->on_text_preedit(event); break;
629
+ case TextEvent::COMMIT: window->on_text_commit(event); break;
630
+ default: break;
288
631
  }
289
- }
632
+ if (event->is_blocked()) return;
290
633
 
291
- if (!event->is_blocked() && window->self->focus)
292
- View_call_key_event(window->self->focus.get(), event);
634
+ if (window->self->focus)
635
+ {
636
+ View_call_text_event(window->self->focus.get(), event);
637
+ if (event->is_blocked()) return;
638
+ }
293
639
 
294
- cleanup_captures(window);
640
+ if (synthesize_key_events)
641
+ {
642
+ KeyEvent down(KeyEvent::DOWN, event->text(), KEY_NONE);
643
+ KeyEvent up( KeyEvent::UP, event->text(), KEY_NONE);
644
+ Window_call_key_event(window, &down, false);
645
+ Window_call_key_event(window, &up, false);
646
+ }
647
+ });
295
648
  }
296
649
 
297
650
  static bool
@@ -299,7 +652,8 @@ namespace Reflex
299
652
  {
300
653
  return
301
654
  pointer.action() == Pointer::DOWN ||
302
- prev_pointer.action() == Pointer::UP;
655
+ prev_pointer.action() == Pointer::UP ||
656
+ prev_pointer.action() == Pointer::CANCEL;
303
657
  }
304
658
 
305
659
  static Pointer::ID
@@ -311,11 +665,6 @@ namespace Reflex
311
665
  static void
312
666
  setup_mouse_pointer (Window* window, Pointer* pointer)
313
667
  {
314
- static const uint MOUSE_BUTTONS =
315
- Pointer::MOUSE_LEFT |
316
- Pointer::MOUSE_RIGHT |
317
- Pointer::MOUSE_MIDDLE;
318
-
319
668
  Window::Data* self = window->self.get();
320
669
 
321
670
  auto& prev_pointer = self->prev_mouse_pointer;
@@ -412,6 +761,23 @@ namespace Reflex
412
761
  }
413
762
  }
414
763
 
764
+ static void
765
+ reject_unpaired_up_pointers (PointerEvent* event)
766
+ {
767
+ // a canceled pointer must deliver nothing until its next down, so
768
+ // an up whose down was never delivered stays silent
769
+
770
+ std::vector<Pointer::ID> ids;
771
+ PointerEvent_each_pointer(event, [&](const auto& pointer)
772
+ {
773
+ if (pointer.action() == Pointer::UP && !pointer.down())
774
+ ids.emplace_back(pointer.id());
775
+ });
776
+
777
+ for (auto id : ids)
778
+ PointerEvent_erase_pointer(event, id);
779
+ }
780
+
415
781
  static void
416
782
  setup_pointer_event (Window* window, PointerEvent* event)
417
783
  {
@@ -423,6 +789,8 @@ namespace Reflex
423
789
  else
424
790
  setup_pointer(window, &pointer);
425
791
  }
792
+
793
+ reject_unpaired_up_pointers(event);
426
794
  }
427
795
 
428
796
  static void
@@ -485,20 +853,18 @@ namespace Reflex
485
853
  ExtractedPointerIDSet* extracteds, bool* blocked,
486
854
  Window* window, const PointerMap& pointers)
487
855
  {
488
- for (auto& [view, targets] : window->self->captures)
856
+ each_capture(window, [&](View* view, const CaptureTargetIDList& targets)
489
857
  {
490
- if (!view->window() || targets.empty()) continue;
491
-
492
858
  PointerEvent event;
493
859
  PointerEvent_set_captured(&event, true);
494
860
  extract_targeted_pointers(&event, extracteds, targets, pointers);
495
- if (event.empty()) continue;
861
+ if (event.empty()) return;
496
862
 
497
863
  PointerEvent_to_view_coord(&event, view);
498
- View_call_pointer_event(const_cast<View*>(view.get()), &event);
864
+ View_call_pointer_event(view, &event);
499
865
 
500
866
  if (event.is_blocked()) *blocked = true;
501
- }
867
+ });
502
868
  }
503
869
 
504
870
  static void
@@ -595,6 +961,65 @@ namespace Reflex
595
961
  if (blocked) event->block();
596
962
  }
597
963
 
964
+ static bool
965
+ is_inside_window (Window* window, const Point& position)
966
+ {
967
+ View* root = window->root();
968
+ return root && Bounds(root->frame().size()).is_include(position);
969
+ }
970
+
971
+ static bool
972
+ is_inside_window (Window* window, const Pointer& pointer)
973
+ {
974
+ // a boundary event says which side the pointer came out on and is taken
975
+ // at its word; the position it carries cannot be, since sdl reports the
976
+ // last one inside and win32 the one under a menu
977
+ auto action = pointer.action();
978
+ if (action == Pointer::ENTER) return true;
979
+ if (action == Pointer::LEAVE) return false;
980
+
981
+ // a pointer that ends here is nowhere afterwards: a lifted finger, or a
982
+ // pen taken out of hover range
983
+ return
984
+ is_inside_window(window, pointer.position()) &&
985
+ !is_last_pointer_event(pointer);
986
+ }
987
+
988
+ static Pointer::Action
989
+ get_window_boundary_transition (Window* window, const Pointer& pointer)
990
+ {
991
+ const Pointer* prev = pointer.prev();
992
+ bool is_inside = is_inside_window(window, pointer);
993
+ bool was_inside = prev && is_inside_window(window, *prev);
994
+
995
+ if ( is_inside && !was_inside) return Pointer::ENTER;
996
+ if (!is_inside && was_inside) return Pointer::LEAVE;
997
+ return Pointer::ACTION_NONE;
998
+ }
999
+
1000
+ static void
1001
+ call_pointer_window_boundary_events (
1002
+ Window* window, const auto& pointers, Pointer::Action action)
1003
+ {
1004
+ for (const auto& pointer : pointers)
1005
+ {
1006
+ if (get_window_boundary_transition(window, pointer) != action)
1007
+ continue;
1008
+
1009
+ Pointer p = pointer;
1010
+ Pointer_set_action(&p, action);
1011
+
1012
+ PointerEvent event(&p, 1);
1013
+ window->on_pointer(&event);
1014
+ if (event.is_blocked()) continue;
1015
+
1016
+ if (action == Pointer::ENTER)
1017
+ window->on_pointer_enter(&event);
1018
+ else
1019
+ window->on_pointer_leave(&event);
1020
+ }
1021
+ }
1022
+
598
1023
  static void
599
1024
  get_current_hovered_views (
600
1025
  ViewList* result, Window* window, const Pointer& pointer)
@@ -720,96 +1145,162 @@ namespace Reflex
720
1145
  }
721
1146
  }
722
1147
 
723
- static bool
724
- is_boundary_action (Pointer::Action action)
725
- {
726
- return action == Pointer::ENTER || action == Pointer::LEAVE;
727
- }
728
-
729
- void
730
- Window_call_pointer_event (Window* window, PointerEvent* event)
1148
+ static void
1149
+ do_call_pointer_event (Window* window, PointerEvent* event)
731
1150
  {
732
- assert(window);
1151
+ auto action = (*event)[0].action();
1152
+ bool boundary = action == Pointer::ENTER || action == Pointer::LEAVE;
733
1153
 
734
- if (!event)
735
- argument_error(__FILE__, __LINE__);
1154
+ std::vector<Pointer> pointers;
1155
+ PointerEvent_each_pointer(event, [&](const auto& p)
1156
+ {
1157
+ pointers.emplace_back(p);
1158
+ });
736
1159
 
737
- setup_pointer_event(window, event);
1160
+ call_pointer_window_boundary_events(window, pointers, Pointer::ENTER);
1161
+ call_pointer_enter_events(window, pointers);
738
1162
 
739
- bool boundary = !event->empty() && is_boundary_action((*event)[0].action());
740
- std::vector<Pointer> pointers;
741
- PointerEvent_each_pointer(event, [&](const auto& p) {pointers.emplace_back(p);});
1163
+ if (!boundary)
1164
+ {
1165
+ uint cancels = Window_get_pointer_cancels(*window);
1166
+ auto block_if_canceled = [&]()
1167
+ {
1168
+ if (Window_get_pointer_cancels(*window) != cancels)
1169
+ event->block();
1170
+ return event->is_blocked();
1171
+ };
742
1172
 
743
- if (!event->empty())
744
1173
  window->on_pointer(event);
745
1174
 
746
- if (!event->empty() && !event->is_blocked())
747
- {
748
- switch ((*event)[0].action())
1175
+ if (!block_if_canceled())
749
1176
  {
750
- case Pointer::DOWN: window->on_pointer_down(event); break;
751
- case Pointer::UP: window->on_pointer_up(event); break;
752
- case Pointer::MOVE: window->on_pointer_move(event); break;
753
- case Pointer::CANCEL: window->on_pointer_cancel(event); break;
754
- case Pointer::ENTER: window->on_pointer_enter(event); break;
755
- case Pointer::LEAVE: window->on_pointer_leave(event); break;
756
- default: break;
1177
+ switch (action)
1178
+ {
1179
+ case Pointer::DOWN: window->on_pointer_down(event); break;
1180
+ case Pointer::UP: window->on_pointer_up(event); break;
1181
+ case Pointer::MOVE: window->on_pointer_move(event); break;
1182
+ case Pointer::CANCEL: window->on_pointer_cancel(event); break;
1183
+ default: break;
1184
+ }
757
1185
  }
758
- }
759
1186
 
760
- call_pointer_enter_events(window, pointers);
761
- if (!boundary)
762
- {
763
- if (!event->empty() && !event->is_blocked())
1187
+ if (!block_if_canceled())
764
1188
  call_captured_pointer_events(window, event);
765
1189
 
766
- if (!event->empty() && !event->is_blocked())
1190
+ if (!block_if_canceled())
767
1191
  {
768
1192
  PointerEvent_update_for_child_view(event, window->root());
769
1193
  View_call_pointer_event(window->root(), event);
770
1194
  }
771
1195
  }
1196
+
772
1197
  call_pointer_leave_events(window, pointers);
1198
+ call_pointer_window_boundary_events(window, pointers, Pointer::LEAVE);
1199
+ }
773
1200
 
774
- cleanup_captures(window);
1201
+ static void
1202
+ call_pointer_event (Window* window, PointerEvent* event)
1203
+ {
1204
+ Application_guard([&]()
1205
+ {
1206
+ setup_pointer_event(window, event);
1207
+ if (!event->empty())
1208
+ do_call_pointer_event(window, event);
1209
+
1210
+ cleanup_captures(window);
1211
+ });
775
1212
  }
776
1213
 
777
1214
  void
778
- Window_call_wheel_event (Window* window, WheelEvent* event)
1215
+ Window_call_pointer_event (Window* window, PointerEvent* event)
779
1216
  {
780
1217
  assert(window);
781
1218
 
782
1219
  if (!event)
783
1220
  argument_error(__FILE__, __LINE__);
784
1221
 
785
- if (!event->is_blocked())
786
- window->on_wheel(event);
1222
+ const Window::Data& self = *window->self;
787
1223
 
788
- if (!event->is_blocked())
789
- View_call_wheel_event(window->root(), event);
1224
+ if (is_pointer_through_by_alpha(self) && !event->empty())
1225
+ {
1226
+ // with pointers going through by alpha, update_pointer_through()
1227
+ // decides when the pointer is in the content area, so the native
1228
+ // enter and leave at the edge are dropped, and so is any event in
1229
+ // the content area before it says the pointer is there: macos moves
1230
+ // the key window's pointer wherever it is, ignored or not, and a
1231
+ // move from the titlebar lands a frame before the pixel under it
1232
+ // is judged
1233
+
1234
+ const auto& pointer0 = (*event)[0];
1235
+ auto action = pointer0.action();
1236
+ if (action == Pointer::ENTER || action == Pointer::LEAVE)
1237
+ return;
1238
+
1239
+ if (
1240
+ !Xot::has_flag(self.flags, Window::Data::POINTER_THROUGH_ENTERED) &&
1241
+ is_inside_window(window, pointer0.position()))
1242
+ {
1243
+ return;
1244
+ }
1245
+ }
1246
+
1247
+ call_pointer_event(window, event);
790
1248
  }
791
1249
 
792
- static void
793
- Window_call_note_event (Window* window, NoteEvent* event)
1250
+ void
1251
+ Window_call_wheel_event (Window* window, WheelEvent* event)
794
1252
  {
795
1253
  assert(window);
796
1254
 
797
1255
  if (!event)
798
1256
  argument_error(__FILE__, __LINE__);
799
1257
 
800
- for (auto& [view, targets] : window->self->captures)
1258
+ Application_guard([&]()
801
1259
  {
802
- if (
803
- !view->window() ||
804
- !is_capturing(view.get(), targets, View::CAPTURE_MIDI))
1260
+ if (!event->is_blocked())
1261
+ window->on_wheel(event);
1262
+
1263
+ if (!event->is_blocked())
1264
+ View_call_wheel_event(window->root(), event);
1265
+ });
1266
+ }
1267
+
1268
+ void
1269
+ Window_call_file_event (Window* window, FileEvent* event)
1270
+ {
1271
+ if (!window)
1272
+ argument_error(__FILE__, __LINE__);
1273
+ if (!event)
1274
+ argument_error(__FILE__, __LINE__);
1275
+
1276
+ Application_guard([&]()
1277
+ {
1278
+ window->on_file(event);
1279
+ if (event->is_blocked()) return;
1280
+
1281
+ switch (event->action())
805
1282
  {
806
- continue;
1283
+ case FileEvent::OPEN: window->on_file_open(event); break;
1284
+ case FileEvent::SAVE: window->on_file_save(event); break;
1285
+ default: break;
807
1286
  }
1287
+ });
1288
+ }
1289
+
1290
+ static void
1291
+ Window_call_note_event (Window* window, NoteEvent* event)
1292
+ {
1293
+ assert(window);
808
1294
 
1295
+ if (!event)
1296
+ argument_error(__FILE__, __LINE__);
1297
+
1298
+ each_capturing_view(window, View::CAPTURE_MIDI, [&](View* view)
1299
+ {
809
1300
  NoteEvent e = event->dup();
810
1301
  NoteEvent_set_captured(&e, true);
811
- View_call_note_event(const_cast<View*>(view.get()), &e);
812
- }
1302
+ View_call_note_event(view, &e);
1303
+ });
813
1304
 
814
1305
  if (!event->is_blocked())
815
1306
  window->on_note(event);
@@ -836,19 +1327,12 @@ namespace Reflex
836
1327
  if (!event)
837
1328
  argument_error(__FILE__, __LINE__);
838
1329
 
839
- for (auto& [view, targets] : window->self->captures)
1330
+ each_capturing_view(window, View::CAPTURE_MIDI, [&](View* view)
840
1331
  {
841
- if (
842
- !view->window() ||
843
- !is_capturing(view.get(), targets, View::CAPTURE_MIDI))
844
- {
845
- continue;
846
- }
847
-
848
1332
  ControlChangeEvent e = event->dup();
849
1333
  ControlChangeEvent_set_captured(&e, true);
850
- View_call_control_change_event(const_cast<View*>(view.get()), &e);
851
- }
1334
+ View_call_control_change_event(view, &e);
1335
+ });
852
1336
 
853
1337
  if (!event->is_blocked())
854
1338
  window->on_control_change(event);
@@ -865,38 +1349,34 @@ namespace Reflex
865
1349
  if (!event)
866
1350
  argument_error(__FILE__, __LINE__);
867
1351
 
868
- for (auto& [view, targets] : window->self->captures)
1352
+ Application_guard([&]()
869
1353
  {
870
- if (
871
- !view->window() ||
872
- !is_capturing(view.get(), targets, View::CAPTURE_MIDI))
1354
+ each_capturing_view(window, View::CAPTURE_MIDI, [&](View* view)
873
1355
  {
874
- continue;
875
- }
876
-
877
- MIDIEvent e = event->dup();
878
- MIDIEvent_set_captured(&e, true);
879
- View_call_midi_event(const_cast<View*>(view.get()), &e);
880
- }
1356
+ MIDIEvent e = event->dup();
1357
+ MIDIEvent_set_captured(&e, true);
1358
+ View_call_midi_event(view, &e);
1359
+ });
881
1360
 
882
- if (!event->is_blocked())
883
- window->on_midi(event);
1361
+ if (!event->is_blocked())
1362
+ window->on_midi(event);
884
1363
 
885
- if (!event->is_blocked() && window->self->focus)
886
- View_call_midi_event(window->self->focus.get(), event);
1364
+ if (!event->is_blocked() && window->self->focus)
1365
+ View_call_midi_event(window->self->focus.get(), event);
887
1366
 
888
- if (!event->is_blocked())
889
- {
890
- NoteEvent note_e;
891
- if (MIDIEvent_to_note_event(&note_e, *event))
892
- Window_call_note_event(window, &note_e);
1367
+ if (!event->is_blocked())
1368
+ {
1369
+ NoteEvent note_e;
1370
+ if (MIDIEvent_to_note_event(&note_e, *event))
1371
+ Window_call_note_event(window, &note_e);
893
1372
 
894
- ControlChangeEvent cc_e;
895
- if (MIDIEvent_to_control_change_event(&cc_e, *event))
896
- Window_call_control_change_event(window, &cc_e);
897
- }
1373
+ ControlChangeEvent cc_e;
1374
+ if (MIDIEvent_to_control_change_event(&cc_e, *event))
1375
+ Window_call_control_change_event(window, &cc_e);
1376
+ }
898
1377
 
899
- cleanup_captures(window);
1378
+ cleanup_captures(window);
1379
+ });
900
1380
  }
901
1381
 
902
1382
 
@@ -958,6 +1438,8 @@ namespace Reflex
958
1438
  void
959
1439
  Window::close (bool force)
960
1440
  {
1441
+ if (!*this) return;
1442
+
961
1443
  Event e;
962
1444
  on_close(&e);
963
1445
  if (!force && e.is_blocked()) return;
@@ -972,7 +1454,7 @@ namespace Reflex
972
1454
  void
973
1455
  Window::redraw ()
974
1456
  {
975
- self->redraw = true;
1457
+ Xot::add_flag(&self->flags, Data::REDRAW);
976
1458
  }
977
1459
 
978
1460
  Point
@@ -1043,17 +1525,44 @@ namespace Reflex
1043
1525
  }
1044
1526
 
1045
1527
  void
1046
- Window::set_flag (uint flags)
1528
+ Window::set_pointer_through_alpha (float alpha)
1047
1529
  {
1048
- Window_set_flags(this, flags);
1530
+ if (alpha < 0 || 1 < alpha)
1531
+ argument_error(__FILE__, __LINE__);
1532
+
1533
+ if (alpha == self->pointer_through_alpha)
1534
+ return;
1049
1535
 
1536
+ self->pointer_through_alpha = alpha;
1537
+ apply_pointer_through(this);
1538
+ }
1539
+
1540
+ float
1541
+ Window::pointer_through_alpha () const
1542
+ {
1543
+ return self->pointer_through_alpha;
1544
+ }
1545
+
1546
+ static const uint PUBLIC_FLAGS = (Window::FLAG_LAST << 1) - 1;
1547
+
1548
+ void
1549
+ Window::set_flags (uint flags)
1550
+ {
1551
+ flags &= PUBLIC_FLAGS;
1552
+ flags |= self->flags & ~PUBLIC_FLAGS;
1553
+ if (flags == self->flags)
1554
+ return;
1555
+
1556
+ Window_set_flags(this, flags);
1050
1557
  self->flags = flags;
1558
+
1559
+ apply_pointer_through(this);
1051
1560
  }
1052
1561
 
1053
1562
  uint
1054
1563
  Window::flags () const
1055
1564
  {
1056
- return self->flags;
1565
+ return self->flags & PUBLIC_FLAGS;
1057
1566
  }
1058
1567
 
1059
1568
  void
@@ -1061,10 +1570,7 @@ namespace Reflex
1061
1570
  {
1062
1571
  uint value = self->flags;
1063
1572
  Xot::add_flag(&value, flags);
1064
-
1065
- Window_set_flags(this, value);
1066
-
1067
- self->flags = value;
1573
+ set_flags(value);
1068
1574
  }
1069
1575
 
1070
1576
  void
@@ -1072,16 +1578,13 @@ namespace Reflex
1072
1578
  {
1073
1579
  uint value = self->flags;
1074
1580
  Xot::remove_flag(&value, flags);
1075
-
1076
- Window_set_flags(this, value);
1077
-
1078
- self->flags = value;
1581
+ set_flags(value);
1079
1582
  }
1080
1583
 
1081
1584
  bool
1082
1585
  Window::has_flag (uint flags) const
1083
1586
  {
1084
- return Xot::has_flag(self->flags, flags);
1587
+ return Xot::has_flag(this->flags(), flags);
1085
1588
  }
1086
1589
 
1087
1590
  Screen
@@ -1186,6 +1689,21 @@ namespace Reflex
1186
1689
  {
1187
1690
  }
1188
1691
 
1692
+ void
1693
+ Window::on_text (TextEvent* e)
1694
+ {
1695
+ }
1696
+
1697
+ void
1698
+ Window::on_text_preedit (TextEvent* e)
1699
+ {
1700
+ }
1701
+
1702
+ void
1703
+ Window::on_text_commit (TextEvent* e)
1704
+ {
1705
+ }
1706
+
1189
1707
  void
1190
1708
  Window::on_pointer (PointerEvent* e)
1191
1709
  {
@@ -1226,6 +1744,21 @@ namespace Reflex
1226
1744
  {
1227
1745
  }
1228
1746
 
1747
+ void
1748
+ Window::on_file (FileEvent* e)
1749
+ {
1750
+ }
1751
+
1752
+ void
1753
+ Window::on_file_open (FileEvent* e)
1754
+ {
1755
+ }
1756
+
1757
+ void
1758
+ Window::on_file_save (FileEvent* e)
1759
+ {
1760
+ }
1761
+
1229
1762
  void
1230
1763
  Window::on_midi (MIDIEvent* e)
1231
1764
  {