reflexion 0.5.3 → 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 (309) hide show
  1. checksums.yaml +4 -4
  2. data/.doc/ext/reflex/application.cpp +56 -7
  3. data/.doc/ext/reflex/capture_event.cpp +1 -1
  4. data/.doc/ext/reflex/chase_constraint.cpp +84 -0
  5. data/.doc/ext/reflex/clipboard.cpp +65 -0
  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 +135 -0
  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 +178 -0
  26. data/.doc/ext/reflex/menu.cpp +303 -0
  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 +40 -10
  31. data/.doc/ext/reflex/note_event.cpp +1 -1
  32. data/.doc/ext/reflex/pin.cpp +156 -0
  33. data/.doc/ext/reflex/pointer.cpp +3 -1
  34. data/.doc/ext/reflex/pointer_event.cpp +3 -3
  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 +606 -237
  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 +5 -5
  41. data/.doc/ext/reflex/shape.cpp +1 -1
  42. data/.doc/ext/reflex/snap_constraint.cpp +125 -0
  43. data/.doc/ext/reflex/style.cpp +1 -1
  44. data/.doc/ext/reflex/style_length.cpp +4 -3
  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 +206 -79
  50. data/.doc/ext/reflex/wheel_constraint.cpp +142 -0
  51. data/.doc/ext/reflex/wheel_event.cpp +1 -1
  52. data/.doc/ext/reflex/window.cpp +282 -31
  53. data/.github/workflows/release-gem.yml +2 -40
  54. data/.github/workflows/tag.yml +5 -28
  55. data/.github/workflows/test.yml +1 -28
  56. data/ChangeLog.md +79 -0
  57. data/Gemfile.lock +13 -8
  58. data/README.md +1 -0
  59. data/Rakefile +13 -2
  60. data/Reflex.podspec +2 -2
  61. data/VERSION +1 -1
  62. data/ext/reflex/application.cpp +62 -7
  63. data/ext/reflex/capture_event.cpp +1 -1
  64. data/ext/reflex/chase_constraint.cpp +89 -0
  65. data/ext/reflex/clipboard.cpp +69 -0
  66. data/ext/reflex/color_event.cpp +80 -0
  67. data/ext/reflex/color_picker.cpp +121 -0
  68. data/ext/reflex/constraint.cpp +146 -0
  69. data/ext/reflex/contact_event.cpp +1 -1
  70. data/ext/reflex/control_change_event.cpp +1 -1
  71. data/ext/reflex/device.cpp +1 -1
  72. data/ext/reflex/device_event.cpp +1 -1
  73. data/ext/reflex/draw_event.cpp +1 -1
  74. data/ext/reflex/ellipse_shape.cpp +1 -1
  75. data/ext/reflex/event.cpp +1 -1
  76. data/ext/reflex/extconf.rb +4 -2
  77. data/ext/reflex/file_dialog.cpp +194 -0
  78. data/ext/reflex/file_event.cpp +108 -0
  79. data/ext/reflex/filter.cpp +1 -1
  80. data/ext/reflex/focus_event.cpp +1 -1
  81. data/ext/reflex/frame_event.cpp +1 -1
  82. data/ext/reflex/gamepad.cpp +50 -0
  83. data/ext/reflex/image_view.cpp +1 -1
  84. data/ext/reflex/key_event.cpp +1 -275
  85. data/ext/reflex/line_shape.cpp +1 -1
  86. data/ext/reflex/link_constraint.cpp +194 -0
  87. data/ext/reflex/menu.cpp +331 -0
  88. data/ext/reflex/midi.cpp +11 -3
  89. data/ext/reflex/midi_event.cpp +1 -1
  90. data/ext/reflex/motion_event.cpp +1 -1
  91. data/ext/reflex/native.cpp +40 -10
  92. data/ext/reflex/note_event.cpp +1 -1
  93. data/ext/reflex/pin.cpp +165 -0
  94. data/ext/reflex/pointer.cpp +3 -1
  95. data/ext/reflex/pointer_event.cpp +3 -3
  96. data/ext/reflex/polygon_shape.cpp +1 -1
  97. data/ext/reflex/rect_shape.cpp +1 -1
  98. data/ext/reflex/reflex.cpp +608 -237
  99. data/ext/reflex/screen.cpp +31 -2
  100. data/ext/reflex/scroll_event.cpp +1 -1
  101. data/ext/reflex/selector.cpp +5 -5
  102. data/ext/reflex/selector.h +10 -10
  103. data/ext/reflex/shape.cpp +1 -1
  104. data/ext/reflex/snap_constraint.cpp +135 -0
  105. data/ext/reflex/style.cpp +1 -1
  106. data/ext/reflex/style_length.cpp +4 -3
  107. data/ext/reflex/text_event.cpp +111 -0
  108. data/ext/reflex/timer.cpp +1 -1
  109. data/ext/reflex/timer_event.cpp +1 -1
  110. data/ext/reflex/update_event.cpp +1 -1
  111. data/ext/reflex/view.cpp +236 -96
  112. data/ext/reflex/wheel_constraint.cpp +154 -0
  113. data/ext/reflex/wheel_event.cpp +1 -1
  114. data/ext/reflex/window.cpp +318 -40
  115. data/include/reflex/application.h +19 -0
  116. data/include/reflex/clipboard.h +53 -0
  117. data/include/reflex/color_picker.h +65 -0
  118. data/include/reflex/constraint.h +275 -0
  119. data/include/reflex/defs.h +204 -195
  120. data/include/reflex/event.h +180 -88
  121. data/include/reflex/file_dialog.h +73 -0
  122. data/include/reflex/gamepad.h +3 -0
  123. data/include/reflex/menu.h +120 -0
  124. data/include/reflex/midi.h +3 -0
  125. data/include/reflex/pin.h +68 -0
  126. data/include/reflex/pointer.h +4 -0
  127. data/include/reflex/reflex.h +2 -0
  128. data/include/reflex/ruby/application.h +6 -1
  129. data/include/reflex/ruby/clipboard.h +23 -0
  130. data/include/reflex/ruby/color_picker.h +89 -0
  131. data/include/reflex/ruby/constraint.h +94 -0
  132. data/include/reflex/ruby/event.h +62 -29
  133. data/include/reflex/ruby/file_dialog.h +89 -0
  134. data/include/reflex/ruby/gamepad.h +46 -0
  135. data/include/reflex/ruby/menu.h +103 -0
  136. data/include/reflex/ruby/pin.h +40 -0
  137. data/include/reflex/ruby/reflex.h +3 -0
  138. data/include/reflex/ruby/view.h +123 -39
  139. data/include/reflex/ruby/window.h +96 -19
  140. data/include/reflex/ruby.h +3 -0
  141. data/include/reflex/screen.h +11 -2
  142. data/include/reflex/view.h +66 -27
  143. data/include/reflex/window.h +57 -12
  144. data/include/reflex.h +8 -0
  145. data/lib/reflex/application.rb +5 -5
  146. data/lib/reflex/autoinit.rb +3 -1
  147. data/lib/reflex/button.rb +3 -3
  148. data/lib/reflex/chase_constraint.rb +15 -0
  149. data/lib/reflex/clipboard.rb +19 -0
  150. data/lib/reflex/color_event.rb +16 -0
  151. data/lib/reflex/color_picker.rb +45 -0
  152. data/lib/reflex/constraint.rb +29 -0
  153. data/lib/reflex/control_change_event.rb +17 -0
  154. data/lib/reflex/extension.rb +1 -1
  155. data/lib/reflex/file_dialog.rb +70 -0
  156. data/lib/reflex/file_event.rb +37 -0
  157. data/lib/reflex/helper.rb +79 -0
  158. data/lib/reflex/key_event.rb +14 -14
  159. data/lib/reflex/link_constraint.rb +31 -0
  160. data/lib/reflex/menu.rb +92 -0
  161. data/lib/reflex/model.rb +19 -19
  162. data/lib/reflex/note_event.rb +1 -1
  163. data/lib/reflex/pin.rb +39 -0
  164. data/lib/reflex/pointer.rb +30 -1
  165. data/lib/reflex/pointer_event.rb +13 -2
  166. data/lib/reflex/reflex.rb +5 -0
  167. data/lib/reflex/shape.rb +14 -14
  168. data/lib/reflex/snap_constraint.rb +28 -0
  169. data/lib/reflex/style.rb +16 -16
  170. data/lib/reflex/text_event.rb +40 -0
  171. data/lib/reflex/timer.rb +3 -3
  172. data/lib/reflex/view.rb +85 -18
  173. data/lib/reflex/wheel_constraint.rb +28 -0
  174. data/lib/reflex/wheel_event.rb +19 -1
  175. data/lib/reflex/window.rb +25 -13
  176. data/lib/reflex.rb +23 -8
  177. data/pod.rake +1 -1
  178. data/reflex.gemspec +4 -4
  179. data/samples/constraint.rb +152 -0
  180. data/samples/ime.rb +100 -0
  181. data/samples/ios/hello/hello.xcodeproj/project.pbxproj +236 -224
  182. data/samples/menu.rb +32 -0
  183. data/samples/model.rb +3 -3
  184. data/samples/test/alert.rb +93 -0
  185. data/samples/test/always_on.rb +76 -0
  186. data/samples/test/app_background.rb +51 -0
  187. data/samples/test/boundary.rb +75 -0
  188. data/samples/test/callback_exception.rb +101 -0
  189. data/samples/test/cancel.rb +53 -0
  190. data/samples/test/color_picker.rb +63 -0
  191. data/samples/test/file_dialog.rb +102 -0
  192. data/samples/window_style.rb +156 -0
  193. data/src/application.cpp +180 -20
  194. data/src/application.h +95 -5
  195. data/src/clipboard.cpp +81 -0
  196. data/src/color_picker.cpp +113 -0
  197. data/src/color_picker.h +36 -0
  198. data/src/constraint.cpp +1662 -0
  199. data/src/constraint.h +64 -0
  200. data/src/event.cpp +416 -252
  201. data/src/event.h +13 -2
  202. data/src/file_dialog.cpp +163 -0
  203. data/src/file_dialog.h +52 -0
  204. data/src/gamepad.cpp +42 -5
  205. data/src/gamepad.h +4 -0
  206. data/src/ios/app_delegate.mm +5 -3
  207. data/src/ios/application.mm +29 -0
  208. data/src/ios/clipboard.mm +44 -0
  209. data/src/ios/color_picker.mm +19 -0
  210. data/src/ios/event.h +6 -2
  211. data/src/ios/event.mm +31 -12
  212. data/src/ios/file_dialog.mm +21 -0
  213. data/src/ios/gamepad.mm +4 -4
  214. data/src/ios/menu.mm +41 -0
  215. data/src/ios/reflex.mm +8 -0
  216. data/src/ios/screen.mm +26 -0
  217. data/src/ios/view_controller.mm +47 -42
  218. data/src/ios/window.mm +44 -9
  219. data/src/menu.cpp +387 -0
  220. data/src/menu.h +81 -0
  221. data/src/midi.cpp +61 -31
  222. data/src/osx/app_delegate.h +4 -0
  223. data/src/osx/app_delegate.mm +168 -139
  224. data/src/osx/application.mm +103 -5
  225. data/src/osx/clipboard.mm +45 -0
  226. data/src/osx/color_picker.mm +112 -0
  227. data/src/osx/event.h +10 -0
  228. data/src/osx/event.mm +114 -30
  229. data/src/osx/file_dialog.mm +168 -0
  230. data/src/osx/gamepad.mm +2 -2
  231. data/src/osx/gamepad_hid.mm +1 -1
  232. data/src/osx/menu.h +25 -0
  233. data/src/osx/menu.mm +516 -0
  234. data/src/osx/native_window.h +16 -2
  235. data/src/osx/native_window.mm +141 -57
  236. data/src/osx/opengl_view.h +2 -1
  237. data/src/osx/opengl_view.mm +229 -37
  238. data/src/osx/reflex.mm +38 -0
  239. data/src/osx/screen.mm +36 -0
  240. data/src/osx/window.mm +117 -24
  241. data/src/pin.cpp +137 -0
  242. data/src/pointer.cpp +36 -16
  243. data/src/pointer.h +10 -0
  244. data/src/rays.h +46 -0
  245. data/src/sdl/application.cpp +73 -8
  246. data/src/sdl/clipboard.cpp +39 -0
  247. data/src/sdl/color_picker.cpp +18 -0
  248. data/src/sdl/event.cpp +44 -6
  249. data/src/sdl/event.h +18 -3
  250. data/src/sdl/file_dialog.cpp +20 -0
  251. data/src/sdl/gamepad.cpp +3 -7
  252. data/src/sdl/menu.cpp +40 -0
  253. data/src/sdl/reflex.cpp +20 -0
  254. data/src/sdl/screen.cpp +36 -0
  255. data/src/sdl/window.cpp +168 -37
  256. data/src/shape.cpp +15 -10
  257. data/src/view.cpp +583 -233
  258. data/src/view.h +22 -4
  259. data/src/vk.h +416 -0
  260. data/src/win32/application.cpp +167 -12
  261. data/src/win32/application.h +20 -0
  262. data/src/win32/clipboard.cpp +210 -0
  263. data/src/win32/color_picker.cpp +82 -0
  264. data/src/win32/event.cpp +123 -11
  265. data/src/win32/event.h +22 -3
  266. data/src/win32/file_dialog.cpp +226 -0
  267. data/src/win32/gamepad.cpp +9 -2
  268. data/src/win32/menu.cpp +490 -0
  269. data/src/win32/menu.h +35 -0
  270. data/src/win32/opengl.cpp +2 -9
  271. data/src/win32/reflex.cpp +44 -3
  272. data/src/win32/screen.cpp +164 -3
  273. data/src/win32/screen.h +8 -0
  274. data/src/win32/tray.cpp +191 -0
  275. data/src/win32/tray.h +22 -0
  276. data/src/win32/vk.cpp +11 -0
  277. data/src/win32/window.cpp +858 -135
  278. data/src/win32/window.h +9 -0
  279. data/src/window.cpp +929 -183
  280. data/src/window.h +77 -33
  281. data/src/world.cpp +86 -15
  282. data/src/world.h +9 -2
  283. data/test/helper.rb +24 -0
  284. data/test/test_application.rb +101 -1
  285. data/test/test_chase_constraint.rb +122 -0
  286. data/test/test_clipboard.rb +54 -0
  287. data/test/test_color_event.rb +36 -0
  288. data/test/test_color_picker.rb +62 -0
  289. data/test/test_constraint.rb +184 -0
  290. data/test/test_event.rb +1 -1
  291. data/test/test_file_dialog.rb +48 -0
  292. data/test/test_file_event.rb +43 -0
  293. data/test/test_has_text_preedit.rb +54 -0
  294. data/test/test_key_event.rb +23 -2
  295. data/test/test_link_constraint.rb +237 -0
  296. data/test/test_menu.rb +237 -0
  297. data/test/test_pin.rb +65 -0
  298. data/test/test_pointer.rb +50 -26
  299. data/test/test_pointer_event.rb +25 -10
  300. data/test/test_reflex.rb +36 -0
  301. data/test/test_screen.rb +24 -2
  302. data/test/test_snap_constraint.rb +140 -0
  303. data/test/test_text_event.rb +54 -0
  304. data/test/test_view.rb +152 -25
  305. data/test/test_wheel_constraint.rb +125 -0
  306. data/test/test_wheel_event.rb +24 -10
  307. data/test/test_window.rb +205 -43
  308. metadata +154 -10
  309. data/.github/workflows/utils.rb +0 -127
data/src/window.cpp CHANGED
@@ -2,26 +2,56 @@
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
14
19
  {
15
20
 
16
21
 
17
- using ViewList = std::vector<View::Ref>;
18
-
19
22
  using PointerMap = std::map<Pointer::ID, Pointer>;
20
23
 
21
24
  using ExtractedPointerIDSet = std::set<Pointer::ID>;
22
25
 
23
26
  using CaptureTargetIDList = Window::Data::CaptureTargetIDList;
24
27
 
28
+ using WeakViewList = Window::Data::WeakViewList;
29
+
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
+ }
25
55
 
26
56
  void
27
57
  Window_register (Window* win)
@@ -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,15 +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);
254
+ Event e;
255
+ window->on_activate(&e);
256
+ if (e.is_blocked()) return;
257
+
258
+ View_activate_tree(window->root(), &e);
259
+ });
179
260
  }
180
261
 
181
262
  void
@@ -183,25 +264,186 @@ namespace Reflex
183
264
  {
184
265
  if (!window) return;
185
266
 
186
- Event e;
187
- window->on_deactivate(&e);
267
+ Application_guard([&]()
268
+ {
269
+ Xot::remove_flag(&window->self->flags, Window::Data::ACTIVE);
270
+
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
+ }
314
+
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
+ }
188
328
 
189
- Xot::remove_flag(&window->self->flags, Window::Data::ACTIVE);
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);
190
375
  }
191
376
 
192
377
  void
193
378
  Window_call_update_event (Window* window)
194
379
  {
195
- Window::Data* self = window->self.get();
380
+ Application_guard([&]()
381
+ {
382
+ Window::Data* self = window->self.get();
196
383
 
197
- double now = Xot::time();
198
- UpdateEvent e(now, now - self->prev_time_update);
199
- self->prev_time_update = now;
384
+ MIDI_process_events();
385
+
386
+ update_pointer_through(window);
200
387
 
201
- window->on_update(&e);
202
- 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;
203
391
 
204
- 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();
205
447
  }
206
448
 
207
449
  void
@@ -212,25 +454,74 @@ namespace Reflex
212
454
  if (!event)
213
455
  argument_error(__FILE__, __LINE__);
214
456
 
215
- Painter* painter = window->painter();
216
- if (!painter)
217
- Xot::invalid_state_error(__FILE__, __LINE__);
457
+ Application_guard([&]()
458
+ {
459
+ Window::Data* self = window->self.get();
218
460
 
219
- Rays::Bounds frame = window->frame();
461
+ Painter* painter = window->painter();
462
+ if (!painter)
463
+ Xot::invalid_state_error(__FILE__, __LINE__);
220
464
 
221
- DrawEvent_set_painter(event, painter);
222
- 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());
223
467
 
224
- painter->begin();
225
- painter->push_state();
226
- painter->clear();
468
+ DrawEvent_set_painter(event, painter);
469
+ DrawEvent_set_bounds(event, bounds);
227
470
 
228
- window->on_draw(event);
229
- if (!event->is_blocked())
230
- 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();
231
476
 
232
- painter->pop_state();
233
- 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
+ });
234
525
  }
235
526
 
236
527
  static bool
@@ -243,49 +534,126 @@ namespace Reflex
243
534
  (view->capture() & type) == type;
244
535
  }
245
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
+
246
570
  void
247
- Window_call_key_event (Window* window, KeyEvent* event)
571
+ Window_call_key_event (Window* window, KeyEvent* event, bool capture)
248
572
  {
249
573
  assert(window);
250
574
 
251
575
  if (!event)
252
576
  argument_error(__FILE__, __LINE__);
253
577
 
254
- window->on_key(event);
255
-
256
- if (!event->is_blocked())
578
+ Application_guard([&]()
257
579
  {
258
- switch (event->action())
580
+ window->on_key(event);
581
+
582
+ if (!event->is_blocked())
259
583
  {
260
- case KeyEvent::DOWN: window->on_key_down(event); break;
261
- case KeyEvent::UP: window->on_key_up(event); break;
262
- 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
+ }
263
590
  }
264
- }
265
591
 
266
- if (!event->is_blocked())
267
- {
268
- for (auto& [view, targets] : window->self->captures)
592
+ if (capture && !event->is_blocked())
269
593
  {
270
- if (
271
- !view->window() ||
272
- !is_capturing(view.get(), targets, View::CAPTURE_KEY))
594
+ each_capturing_view(window, View::CAPTURE_KEY, [&](View* view)
273
595
  {
274
- continue;
275
- }
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
+ }
276
603
 
277
- KeyEvent e = event->dup();
278
- KeyEvent_set_captured(&e, true);
279
- View_call_key_event(const_cast<View*>(view.get()), &e);
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
+ }
611
+
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;
625
+
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;
631
+ }
632
+ if (event->is_blocked()) return;
280
633
 
281
- if (e.is_blocked()) event->block();
634
+ if (window->self->focus)
635
+ {
636
+ View_call_text_event(window->self->focus.get(), event);
637
+ if (event->is_blocked()) return;
282
638
  }
283
- }
284
639
 
285
- if (!event->is_blocked() && window->self->focus)
286
- View_call_key_event(window->self->focus.get(), event);
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
+ });
648
+ }
287
649
 
288
- cleanup_captures(window);
650
+ static bool
651
+ need_next_pointer_id (const Pointer& pointer, const Pointer& prev_pointer)
652
+ {
653
+ return
654
+ pointer.action() == Pointer::DOWN ||
655
+ prev_pointer.action() == Pointer::UP ||
656
+ prev_pointer.action() == Pointer::CANCEL;
289
657
  }
290
658
 
291
659
  static Pointer::ID
@@ -297,21 +665,15 @@ namespace Reflex
297
665
  static void
298
666
  setup_mouse_pointer (Window* window, Pointer* pointer)
299
667
  {
300
- static const uint MOUSE_BUTTONS =
301
- Pointer::MOUSE_LEFT |
302
- Pointer::MOUSE_RIGHT |
303
- Pointer::MOUSE_MIDDLE;
304
-
305
668
  Window::Data* self = window->self.get();
306
669
 
307
- auto action = pointer->action();
308
670
  auto& prev_pointer = self->prev_mouse_pointer;
309
671
 
310
672
  auto id = prev_pointer ? prev_pointer.id() : get_next_pointer_id(window);
311
673
  auto* down = prev_pointer.down();
312
674
  if (
313
675
  Pointer_mask_flag(prev_pointer, MOUSE_BUTTONS) == 0 &&
314
- (action == Pointer::DOWN || prev_pointer.action() == Pointer::UP))
676
+ need_next_pointer_id(*pointer, prev_pointer))
315
677
  {
316
678
  id = get_next_pointer_id(window);
317
679
  down = NULL;
@@ -322,6 +684,7 @@ namespace Reflex
322
684
  Pointer_set_prev(pointer, &prev_pointer);
323
685
  Pointer_set_down(pointer, down);
324
686
 
687
+ auto action = pointer->action();
325
688
  if (action == Pointer::DOWN)
326
689
  Pointer_add_flag(pointer, pointer->types() & MOUSE_BUTTONS);
327
690
 
@@ -351,6 +714,21 @@ namespace Reflex
351
714
  });
352
715
  }
353
716
 
717
+ static bool
718
+ is_last_pointer_event (const Pointer& pointer)
719
+ {
720
+ auto action = pointer.action();
721
+ if (action == Pointer::UP || action == Pointer::CANCEL)
722
+ {
723
+ // a mouse cursor and a floatable pen (Apple Pencil) survive their
724
+ // up/cancel; a finger or non-floatable pen ends here.
725
+ return
726
+ !(pointer.types() & Pointer::MOUSE) &&
727
+ !Pointer_is_floatable(pointer);
728
+ }
729
+ return action == Pointer::LEAVE;
730
+ }
731
+
354
732
  static void
355
733
  setup_pointer (Window* window, Pointer* pointer)
356
734
  {
@@ -362,22 +740,44 @@ namespace Reflex
362
740
 
363
741
  if (prev_pointer)
364
742
  {
365
- Pointer_set_id(pointer, prev_pointer->id());
743
+ if (need_next_pointer_id(*pointer, *prev_pointer))
744
+ Pointer_set_id(pointer, get_next_pointer_id(window));
745
+ else
746
+ {
747
+ Pointer_set_id(pointer, prev_pointer->id());
748
+ Pointer_set_down(pointer, prev_pointer->down());
749
+ }
750
+ Pointer_set_floatable(pointer, Pointer_is_floatable(*prev_pointer));
366
751
  Pointer_set_prev(pointer, prev_pointer);
367
- Pointer_set_down(pointer, prev_pointer->down());
368
752
  prev_pointers.erase(it);
369
753
  }
370
754
  else
371
755
  Pointer_set_id(pointer, get_next_pointer_id(window));
372
756
 
373
- auto action = pointer->action();
374
- if (action != Pointer::UP && action != Pointer::CANCEL)
757
+ if (!is_last_pointer_event(*pointer))
375
758
  {
376
759
  prev_pointers.emplace_back(*pointer);
377
760
  Pointer_set_prev(&prev_pointers.back(), NULL);
378
761
  }
379
762
  }
380
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
+
381
781
  static void
382
782
  setup_pointer_event (Window* window, PointerEvent* event)
383
783
  {
@@ -389,6 +789,8 @@ namespace Reflex
389
789
  else
390
790
  setup_pointer(window, &pointer);
391
791
  }
792
+
793
+ reject_unpaired_up_pointers(event);
392
794
  }
393
795
 
394
796
  static void
@@ -451,20 +853,18 @@ namespace Reflex
451
853
  ExtractedPointerIDSet* extracteds, bool* blocked,
452
854
  Window* window, const PointerMap& pointers)
453
855
  {
454
- for (auto& [view, targets] : window->self->captures)
856
+ each_capture(window, [&](View* view, const CaptureTargetIDList& targets)
455
857
  {
456
- if (!view->window() || targets.empty()) continue;
457
-
458
858
  PointerEvent event;
459
859
  PointerEvent_set_captured(&event, true);
460
860
  extract_targeted_pointers(&event, extracteds, targets, pointers);
461
- if (event.empty()) continue;
861
+ if (event.empty()) return;
462
862
 
463
- PointerEvent_update_for_capturing_view(&event, view);
464
- View_call_pointer_event(const_cast<View*>(view.get()), &event);
863
+ PointerEvent_to_view_coord(&event, view);
864
+ View_call_pointer_event(view, &event);
465
865
 
466
866
  if (event.is_blocked()) *blocked = true;
467
- }
867
+ });
468
868
  }
469
869
 
470
870
  static void
@@ -502,7 +902,7 @@ namespace Reflex
502
902
  if (!view->window()) continue;
503
903
 
504
904
  PointerEvent e = event.dup();
505
- PointerEvent_update_for_capturing_view(&e, view);
905
+ PointerEvent_to_view_coord(&e, view);
506
906
  View_call_pointer_event(const_cast<View*>(view.get()), &e);
507
907
 
508
908
  if (e.is_blocked()) *blocked = true;
@@ -561,79 +961,346 @@ namespace Reflex
561
961
  if (blocked) event->block();
562
962
  }
563
963
 
564
- void
565
- Window_call_pointer_event (Window* window, PointerEvent* event)
964
+ static bool
965
+ is_inside_window (Window* window, const Point& position)
566
966
  {
567
- assert(window);
967
+ View* root = window->root();
968
+ return root && Bounds(root->frame().size()).is_include(position);
969
+ }
568
970
 
569
- if (!event)
570
- argument_error(__FILE__, __LINE__);
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
+ }
571
987
 
572
- setup_pointer_event(window, event);
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);
573
994
 
574
- if (!event->empty())
575
- window->on_pointer(event);
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
+
1023
+ static void
1024
+ get_current_hovered_views (
1025
+ ViewList* result, Window* window, const Pointer& pointer)
1026
+ {
1027
+ if (pointer.is_drag())
1028
+ {
1029
+ // while dragging, only the capturing view(s) take part in enter/leave,
1030
+ // tested against their own frame; views merely passed over are ignored.
1031
+ for (auto& [view, targets] : window->self->captures)
1032
+ {
1033
+ if (!view->window()) continue;
1034
+ if (std::find(targets.begin(), targets.end(), pointer.id()) == targets.end())
1035
+ continue;
1036
+
1037
+ Point pos = view->from_window(pointer.position());
1038
+ if (Bounds(view->frame().size()).is_include(pos))
1039
+ result->emplace_back(view);
1040
+ }
1041
+ }
1042
+ else
1043
+ View_get_hovered_views(result, window->root(), pointer.position());
1044
+ }
1045
+
1046
+ static WeakViewList*
1047
+ get_prev_hovered_views (Window* window, const Pointer& pointer)
1048
+ {
1049
+ // a mouse pointer gets a fresh id on every click, so follow the prev()
1050
+ // chain to keep tracking the same physical pointer across id changes.
1051
+ auto& map = window->self->hovered_views;
576
1052
 
577
- if (!event->empty() && !event->is_blocked())
1053
+ auto it = map.find(pointer.id());
1054
+ if (
1055
+ it == map.end() &&
1056
+ pointer.prev() && pointer.prev()->id() != pointer.id())
578
1057
  {
579
- switch ((*event)[0].action())
1058
+ auto prev_it = map.find(pointer.prev()->id());
1059
+ if (prev_it != map.end())
580
1060
  {
581
- case Pointer::DOWN: window->on_pointer_down(event); break;
582
- case Pointer::UP: window->on_pointer_up(event); break;
583
- case Pointer::MOVE: window->on_pointer_move(event); break;
584
- case Pointer::CANCEL: window->on_pointer_cancel(event); break;
585
- default: break;
1061
+ it = map.emplace(pointer.id(), std::move(prev_it->second)).first;
1062
+ map.erase(prev_it);
1063
+ }
1064
+ }
1065
+
1066
+ return it != map.end() ? &it->second : NULL;
1067
+ }
1068
+
1069
+ static void
1070
+ call_pointer_boundary_event (
1071
+ View* view, const Pointer& pointer, Pointer::Action action)
1072
+ {
1073
+ if (!view->window()) return;
1074
+
1075
+ Pointer p = pointer;
1076
+ Pointer_set_action(&p, action);
1077
+
1078
+ PointerEvent event(&p, 1);
1079
+ PointerEvent_to_view_coord(&event, view);
1080
+ View_call_pointer_event(view, &event);
1081
+ }
1082
+
1083
+ static void
1084
+ call_pointer_boundary_events (
1085
+ Window* window, const Pointer& pointer, ViewList* news)
1086
+ {
1087
+ ViewList olds;
1088
+ if (auto* weak_views = get_prev_hovered_views(window, pointer))
1089
+ {
1090
+ for (auto& view : *weak_views)
1091
+ if (view) olds.emplace_back(view.get());
1092
+
1093
+ for (auto it = olds.rbegin(); it != olds.rend(); ++it)
1094
+ {
1095
+ if (std::find(news->begin(), news->end(), it->get()) != news->end())
1096
+ continue;
1097
+ call_pointer_boundary_event(it->get(), pointer, Pointer::LEAVE);
586
1098
  }
587
1099
  }
588
1100
 
589
- if (!event->empty() && !event->is_blocked())
590
- call_captured_pointer_events(window, event);
1101
+ for (auto& view : *news)
1102
+ {
1103
+ if (std::find(olds.begin(), olds.end(), view.get()) != olds.end())
1104
+ continue;
1105
+ call_pointer_boundary_event(view.get(), pointer, Pointer::ENTER);
1106
+ }
1107
+
1108
+ auto& map = window->self->hovered_views;
1109
+ if (news->empty())
1110
+ map.erase(pointer.id());
1111
+ else
1112
+ map[pointer.id()] = WeakViewList(news->begin(), news->end());
1113
+ }
1114
+
1115
+ static void
1116
+ call_pointer_enter_events (Window* window, const auto& pointers)
1117
+ {
1118
+ for (const auto& pointer : pointers)
1119
+ {
1120
+ auto a = pointer.action();
1121
+ if (a == Pointer::DOWN || a == Pointer::MOVE || a == Pointer::ENTER)
1122
+ {
1123
+ ViewList views;
1124
+ get_current_hovered_views(&views, window, pointer);
1125
+ call_pointer_boundary_events(window, pointer, &views);
1126
+ }
1127
+ }
1128
+ }
1129
+
1130
+ static void
1131
+ call_pointer_leave_events (Window* window, const auto& pointers)
1132
+ {
1133
+ for (const auto& pointer : pointers)
1134
+ {
1135
+ auto a = pointer.action();
1136
+ if (a == Pointer::UP || a == Pointer::CANCEL || a == Pointer::LEAVE)
1137
+ {
1138
+ // a pointer that survives this event keeps hovering whatever is under
1139
+ // it; a pointer that ends here hovers nothing and leaves every view.
1140
+ ViewList views;
1141
+ if (!is_last_pointer_event(pointer))
1142
+ get_current_hovered_views(&views, window, pointer);
1143
+ call_pointer_boundary_events(window, pointer, &views);
1144
+ }
1145
+ }
1146
+ }
1147
+
1148
+ static void
1149
+ do_call_pointer_event (Window* window, PointerEvent* event)
1150
+ {
1151
+ auto action = (*event)[0].action();
1152
+ bool boundary = action == Pointer::ENTER || action == Pointer::LEAVE;
1153
+
1154
+ std::vector<Pointer> pointers;
1155
+ PointerEvent_each_pointer(event, [&](const auto& p)
1156
+ {
1157
+ pointers.emplace_back(p);
1158
+ });
591
1159
 
592
- if (!event->empty() && !event->is_blocked())
1160
+ call_pointer_window_boundary_events(window, pointers, Pointer::ENTER);
1161
+ call_pointer_enter_events(window, pointers);
1162
+
1163
+ if (!boundary)
593
1164
  {
594
- PointerEvent_update_for_child_view(event, window->root());
595
- View_call_pointer_event(window->root(), event);
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
+ };
1172
+
1173
+ window->on_pointer(event);
1174
+
1175
+ if (!block_if_canceled())
1176
+ {
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
+ }
1185
+ }
1186
+
1187
+ if (!block_if_canceled())
1188
+ call_captured_pointer_events(window, event);
1189
+
1190
+ if (!block_if_canceled())
1191
+ {
1192
+ PointerEvent_update_for_child_view(event, window->root());
1193
+ View_call_pointer_event(window->root(), event);
1194
+ }
596
1195
  }
597
1196
 
598
- cleanup_captures(window);
1197
+ call_pointer_leave_events(window, pointers);
1198
+ call_pointer_window_boundary_events(window, pointers, Pointer::LEAVE);
1199
+ }
1200
+
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
+ });
599
1212
  }
600
1213
 
601
1214
  void
602
- Window_call_wheel_event (Window* window, WheelEvent* event)
1215
+ Window_call_pointer_event (Window* window, PointerEvent* event)
603
1216
  {
604
1217
  assert(window);
605
1218
 
606
1219
  if (!event)
607
1220
  argument_error(__FILE__, __LINE__);
608
1221
 
609
- if (!event->is_blocked())
610
- window->on_wheel(event);
1222
+ const Window::Data& self = *window->self;
611
1223
 
612
- if (!event->is_blocked())
613
- 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);
614
1248
  }
615
1249
 
616
- static void
617
- Window_call_note_event (Window* window, NoteEvent* event)
1250
+ void
1251
+ Window_call_wheel_event (Window* window, WheelEvent* event)
618
1252
  {
619
1253
  assert(window);
620
1254
 
621
1255
  if (!event)
622
1256
  argument_error(__FILE__, __LINE__);
623
1257
 
624
- for (auto& [view, targets] : window->self->captures)
1258
+ Application_guard([&]()
625
1259
  {
626
- if (
627
- !view->window() ||
628
- !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())
629
1282
  {
630
- continue;
1283
+ case FileEvent::OPEN: window->on_file_open(event); break;
1284
+ case FileEvent::SAVE: window->on_file_save(event); break;
1285
+ default: break;
631
1286
  }
1287
+ });
1288
+ }
1289
+
1290
+ static void
1291
+ Window_call_note_event (Window* window, NoteEvent* event)
1292
+ {
1293
+ assert(window);
1294
+
1295
+ if (!event)
1296
+ argument_error(__FILE__, __LINE__);
632
1297
 
1298
+ each_capturing_view(window, View::CAPTURE_MIDI, [&](View* view)
1299
+ {
633
1300
  NoteEvent e = event->dup();
634
1301
  NoteEvent_set_captured(&e, true);
635
- View_call_note_event(const_cast<View*>(view.get()), &e);
636
- }
1302
+ View_call_note_event(view, &e);
1303
+ });
637
1304
 
638
1305
  if (!event->is_blocked())
639
1306
  window->on_note(event);
@@ -660,19 +1327,12 @@ namespace Reflex
660
1327
  if (!event)
661
1328
  argument_error(__FILE__, __LINE__);
662
1329
 
663
- for (auto& [view, targets] : window->self->captures)
1330
+ each_capturing_view(window, View::CAPTURE_MIDI, [&](View* view)
664
1331
  {
665
- if (
666
- !view->window() ||
667
- !is_capturing(view.get(), targets, View::CAPTURE_MIDI))
668
- {
669
- continue;
670
- }
671
-
672
1332
  ControlChangeEvent e = event->dup();
673
1333
  ControlChangeEvent_set_captured(&e, true);
674
- View_call_control_change_event(const_cast<View*>(view.get()), &e);
675
- }
1334
+ View_call_control_change_event(view, &e);
1335
+ });
676
1336
 
677
1337
  if (!event->is_blocked())
678
1338
  window->on_control_change(event);
@@ -689,38 +1349,34 @@ namespace Reflex
689
1349
  if (!event)
690
1350
  argument_error(__FILE__, __LINE__);
691
1351
 
692
- for (auto& [view, targets] : window->self->captures)
1352
+ Application_guard([&]()
693
1353
  {
694
- if (
695
- !view->window() ||
696
- !is_capturing(view.get(), targets, View::CAPTURE_MIDI))
1354
+ each_capturing_view(window, View::CAPTURE_MIDI, [&](View* view)
697
1355
  {
698
- continue;
699
- }
700
-
701
- MIDIEvent e = event->dup();
702
- MIDIEvent_set_captured(&e, true);
703
- View_call_midi_event(const_cast<View*>(view.get()), &e);
704
- }
1356
+ MIDIEvent e = event->dup();
1357
+ MIDIEvent_set_captured(&e, true);
1358
+ View_call_midi_event(view, &e);
1359
+ });
705
1360
 
706
- if (!event->is_blocked())
707
- window->on_midi(event);
1361
+ if (!event->is_blocked())
1362
+ window->on_midi(event);
708
1363
 
709
- if (!event->is_blocked() && window->self->focus)
710
- 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);
711
1366
 
712
- if (!event->is_blocked())
713
- {
714
- NoteEvent note_e;
715
- if (MIDIEvent_to_note_event(&note_e, *event))
716
- 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);
717
1372
 
718
- ControlChangeEvent cc_e;
719
- if (MIDIEvent_to_control_change_event(&cc_e, *event))
720
- Window_call_control_change_event(window, &cc_e);
721
- }
1373
+ ControlChangeEvent cc_e;
1374
+ if (MIDIEvent_to_control_change_event(&cc_e, *event))
1375
+ Window_call_control_change_event(window, &cc_e);
1376
+ }
722
1377
 
723
- cleanup_captures(window);
1378
+ cleanup_captures(window);
1379
+ });
724
1380
  }
725
1381
 
726
1382
 
@@ -782,10 +1438,14 @@ namespace Reflex
782
1438
  void
783
1439
  Window::close (bool force)
784
1440
  {
1441
+ if (!*this) return;
1442
+
785
1443
  Event e;
786
1444
  on_close(&e);
787
1445
  if (!force && e.is_blocked()) return;
788
1446
 
1447
+ self->hovered_views.clear();
1448
+
789
1449
  View_set_window(self->root.get(), NULL);
790
1450
 
791
1451
  Window_close(this);
@@ -794,7 +1454,7 @@ namespace Reflex
794
1454
  void
795
1455
  Window::redraw ()
796
1456
  {
797
- self->redraw = true;
1457
+ Xot::add_flag(&self->flags, Data::REDRAW);
798
1458
  }
799
1459
 
800
1460
  Point
@@ -809,6 +1469,12 @@ namespace Reflex
809
1469
  return point + frame().position();
810
1470
  }
811
1471
 
1472
+ bool
1473
+ Window::active () const
1474
+ {
1475
+ return Xot::has_flag(self->flags, Data::ACTIVE);
1476
+ }
1477
+
812
1478
  void
813
1479
  Window::set_title (const char* title)
814
1480
  {
@@ -840,17 +1506,63 @@ namespace Reflex
840
1506
  }
841
1507
 
842
1508
  void
843
- Window::set_flag (uint flags)
1509
+ Window::set_menu (Menu* menu)
844
1510
  {
845
- Window_set_flags(this, flags);
1511
+ self->menu = menu;
1512
+ Window_set_menu(this, menu);
1513
+ }
1514
+
1515
+ Menu*
1516
+ Window::menu ()
1517
+ {
1518
+ return self->menu.get();
1519
+ }
1520
+
1521
+ const Menu*
1522
+ Window::menu () const
1523
+ {
1524
+ return const_cast<Window*>(this)->menu();
1525
+ }
846
1526
 
1527
+ void
1528
+ Window::set_pointer_through_alpha (float alpha)
1529
+ {
1530
+ if (alpha < 0 || 1 < alpha)
1531
+ argument_error(__FILE__, __LINE__);
1532
+
1533
+ if (alpha == self->pointer_through_alpha)
1534
+ return;
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);
847
1557
  self->flags = flags;
1558
+
1559
+ apply_pointer_through(this);
848
1560
  }
849
1561
 
850
1562
  uint
851
1563
  Window::flags () const
852
1564
  {
853
- return self->flags;
1565
+ return self->flags & PUBLIC_FLAGS;
854
1566
  }
855
1567
 
856
1568
  void
@@ -858,10 +1570,7 @@ namespace Reflex
858
1570
  {
859
1571
  uint value = self->flags;
860
1572
  Xot::add_flag(&value, flags);
861
-
862
- Window_set_flags(this, value);
863
-
864
- self->flags = value;
1573
+ set_flags(value);
865
1574
  }
866
1575
 
867
1576
  void
@@ -869,16 +1578,13 @@ namespace Reflex
869
1578
  {
870
1579
  uint value = self->flags;
871
1580
  Xot::remove_flag(&value, flags);
872
-
873
- Window_set_flags(this, value);
874
-
875
- self->flags = value;
1581
+ set_flags(value);
876
1582
  }
877
1583
 
878
1584
  bool
879
1585
  Window::has_flag (uint flags) const
880
1586
  {
881
- return Xot::has_flag(self->flags, flags);
1587
+ return Xot::has_flag(this->flags(), flags);
882
1588
  }
883
1589
 
884
1590
  Screen
@@ -924,27 +1630,27 @@ namespace Reflex
924
1630
  }
925
1631
 
926
1632
  void
927
- Window::on_activate (Event* e)
1633
+ Window::on_show (Event* e)
928
1634
  {
929
1635
  }
930
1636
 
931
1637
  void
932
- Window::on_deactivate (Event* e)
1638
+ Window::on_hide (Event* e)
933
1639
  {
934
1640
  }
935
1641
 
936
1642
  void
937
- Window::on_show (Event* e)
1643
+ Window::on_close (Event* e)
938
1644
  {
939
1645
  }
940
1646
 
941
1647
  void
942
- Window::on_hide (Event* e)
1648
+ Window::on_activate (Event* e)
943
1649
  {
944
1650
  }
945
1651
 
946
1652
  void
947
- Window::on_close (Event* e)
1653
+ Window::on_deactivate (Event* e)
948
1654
  {
949
1655
  }
950
1656
 
@@ -983,6 +1689,21 @@ namespace Reflex
983
1689
  {
984
1690
  }
985
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
+
986
1707
  void
987
1708
  Window::on_pointer (PointerEvent* e)
988
1709
  {
@@ -1008,11 +1729,36 @@ namespace Reflex
1008
1729
  {
1009
1730
  }
1010
1731
 
1732
+ void
1733
+ Window::on_pointer_enter (PointerEvent* e)
1734
+ {
1735
+ }
1736
+
1737
+ void
1738
+ Window::on_pointer_leave (PointerEvent* e)
1739
+ {
1740
+ }
1741
+
1011
1742
  void
1012
1743
  Window::on_wheel (WheelEvent* e)
1013
1744
  {
1014
1745
  }
1015
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
+
1016
1762
  void
1017
1763
  Window::on_midi (MIDIEvent* e)
1018
1764
  {