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.h CHANGED
@@ -9,9 +9,11 @@
9
9
  #include <map>
10
10
  #include <xot/time.h>
11
11
  #include <rays/point.h>
12
+ #include <rays/image.h>
12
13
  #include <rays/painter.h>
13
14
  #include "reflex/window.h"
14
15
  #include "reflex/view.h"
16
+ #include "reflex/menu.h"
15
17
  #include "application.h"
16
18
  #include "pointer.h"
17
19
 
@@ -33,34 +35,54 @@ namespace Reflex
33
35
 
34
36
  typedef std::map<View::Ref, CaptureTargetIDList> CaptureMap;
35
37
 
38
+ typedef std::vector<Xot::WeakRef<View>> WeakViewList;
39
+
40
+ typedef std::map<Pointer::ID, WeakViewList> HoveredViewMap;
41
+
36
42
  enum Flag
37
43
  {
38
44
 
39
- ACTIVE = Xot::bit(1, FLAG_LAST),
45
+ ACTIVE = Xot::bit(1, FLAG_LAST),
46
+
47
+ REDRAW = Xot::bit(2, FLAG_LAST),
48
+
49
+ POINTER_THROUGH_PAUSED = Xot::bit(3, FLAG_LAST),
50
+
51
+ POINTER_THROUGH_ENTERED = Xot::bit(4, FLAG_LAST),
40
52
 
41
53
  };// Flag
42
54
 
43
55
  int hide_count = 1;
44
56
 
45
- bool redraw = true;
57
+ uint flags;
46
58
 
47
59
  Painter painter;
48
60
 
61
+ Image draw_cache;
62
+
63
+ Painter draw_cache_painter;
64
+
49
65
  View::Ref root, focus;
50
66
 
67
+ Menu::Ref menu;
68
+
51
69
  Point prev_position, prev_size;
52
70
 
53
71
  double prev_time_update, prev_time_draw, prev_fps = 0;
54
72
 
55
- CaptureMap captures;
73
+ int next_pointer_id = 1;
74
+
75
+ uint pointer_cancels = 0;
56
76
 
57
- int next_pointer_id = 1;
77
+ float pointer_through_alpha = 1;
58
78
 
59
79
  Pointer prev_mouse_pointer;
60
80
 
61
81
  PointerList prev_pointers;
62
82
 
63
- uint flags;
83
+ CaptureMap captures;
84
+
85
+ HoveredViewMap hovered_views;
64
86
 
65
87
  Data ();
66
88
 
@@ -81,7 +103,9 @@ namespace Reflex
81
103
  };// Window::Data
82
104
 
83
105
 
84
- Window::Data* Window_create_data ();
106
+ typedef View* (*Window_CreateRootViewFun) ();
107
+
108
+ void Window_set_create_root_view_fun (Window_CreateRootViewFun fun);
85
109
 
86
110
  void Window_register (Window* win);
87
111
 
@@ -91,6 +115,49 @@ namespace Reflex
91
115
 
92
116
  Window* Window_get_active ();
93
117
 
118
+ void Window_skip_update_time_of_all (double seconds);
119
+
120
+
121
+ void Window_set_focus (Window* window, View* view);
122
+
123
+ void Window_register_capture (
124
+ Window* window, View* view, Pointer::ID target);
125
+
126
+ void Window_unregister_capture (
127
+ Window* window, View* view, Pointer::ID target);
128
+
129
+ void Window_cancel_active_pointers (Window* window);
130
+
131
+ uint Window_get_pointer_cancels (const Window& window);
132
+
133
+ void Window_call_close (Window* window);
134
+
135
+ void Window_call_activate_event (Window* window);
136
+
137
+ void Window_call_deactivate_event (Window* window);
138
+
139
+ void Window_call_update_event (Window* window);
140
+
141
+ void Window_call_draw_event (Window* window, DrawEvent* event);
142
+
143
+ void Window_call_frame_event (Window* window);
144
+
145
+ void Window_call_key_event (
146
+ Window* window, KeyEvent* event, bool capture = true);
147
+
148
+ void Window_call_text_event (
149
+ Window* window, TextEvent* event, bool synthesize_key_events = false);
150
+
151
+ void Window_call_pointer_event (Window* window, PointerEvent* event);
152
+
153
+ void Window_call_wheel_event (Window* window, WheelEvent* event);
154
+
155
+ void Window_call_file_event (Window* window, FileEvent* event);
156
+
157
+ void Window_call_midi_event (Window* window, MIDIEvent* event);
158
+
159
+
160
+ Window::Data* Window_create_data ();
94
161
 
95
162
  uint Window_default_flags ();
96
163
 
@@ -111,40 +178,17 @@ namespace Reflex
111
178
 
112
179
  Bounds Window_get_frame (const Window& window);
113
180
 
181
+ void Window_set_menu (Window* window, Menu* menu);
182
+
114
183
  Screen Window_get_screen (const Window& window);
115
184
 
116
185
  void Window_set_flags (Window* window, uint flags);
117
186
 
118
187
  float Window_get_pixel_density (const Window& window);
119
188
 
120
- void Window_set_focus (Window* window, View* view);
121
-
122
- void Window_register_capture (
123
- Window* window, View* view, Pointer::ID target);
124
-
125
- void Window_unregister_capture (
126
- Window* window, View* view, Pointer::ID target);
127
-
128
- void Window_call_activate_event (Window* window);
129
-
130
- void Window_call_deactivate_event (Window* window);
131
-
132
- void Window_call_update_event (Window* window);
133
-
134
- void Window_call_draw_event (Window* window, DrawEvent* event);
189
+ void Window_set_pointer_through (Window* window, bool through);
135
190
 
136
- void Window_call_key_event (Window* window, KeyEvent* event);
137
-
138
- void Window_call_pointer_event (Window* window, PointerEvent* event);
139
-
140
- void Window_call_wheel_event (Window* window, WheelEvent* event);
141
-
142
- void Window_call_midi_event (Window* window, MIDIEvent* event);
143
-
144
-
145
- typedef View* (*Window_CreateRootViewFun) ();
146
-
147
- void Window_set_create_root_view_fun (Window_CreateRootViewFun fun);
191
+ bool Window_is_pointer_over_and_uncovered (Point* position, const Window& window);
148
192
 
149
193
 
150
194
  }// Reflex
data/src/world.cpp CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
 
4
4
  #include <assert.h>
5
+ #include <algorithm>
5
6
  #include <memory>
6
7
  #include <vector>
7
8
  #include <map>
@@ -9,8 +10,9 @@
9
10
  #include <box2d/box2d.h>
10
11
  #include "reflex/event.h"
11
12
  #include "reflex/exception.h"
12
- #include "shape.h"
13
13
  #include "view.h"
14
+ #include "shape.h"
15
+ #include "constraint.h"
14
16
  #include "body.h"
15
17
  #include "fixture.h"
16
18
 
@@ -218,7 +220,7 @@ namespace Reflex
218
220
  static Shape*
219
221
  get_shape (b2ShapeId b2shape)
220
222
  {
221
- if (B2_IS_NULL(b2shape)) return NULL;
223
+ if (!b2Shape_IsValid(b2shape)) return NULL;
222
224
  return (Shape*) b2Shape_GetUserData(b2shape);
223
225
  }
224
226
 
@@ -257,6 +259,11 @@ namespace Reflex
257
259
  if (!v1 || !v2 || !View_is_active(*v1) || !View_is_active(*v2))
258
260
  return;
259
261
 
262
+ // the first handler is free to remove either view, so hold everything
263
+ // the second one needs until both have run
264
+ Shape::Ref sref1 = s1, sref2 = s2;
265
+ View::Ref vref1 = v1, vref2 = v2;
266
+
260
267
  ContactEvent e1(action, s2), e2(action, s1);
261
268
  Shape_call_contact_event(s1, &e1);
262
269
  Shape_call_contact_event(s2, &e2);
@@ -274,12 +281,24 @@ namespace Reflex
274
281
 
275
282
  bool stepping = false;
276
283
 
277
- std::unique_ptr<DebugDraw> debug_draw;
278
-
279
284
  // tracked to deliver contact-end events for shapes that Box2D has already
280
285
  // forgotten on destruction
281
286
  ShapePairMap touching_pairs;
282
287
 
288
+ std::vector<Xot::WeakRef<Constraint>> constraints;
289
+
290
+ // fixture-less static body for constraints that anchor to the world
291
+ // (e.g. chasing a target point) without affecting any collision
292
+ std::unique_ptr<Body> pground;
293
+
294
+ std::unique_ptr<DebugDraw> pdebug_draw;
295
+
296
+ Body* ground (World* world)
297
+ {
298
+ if (!pground) pground.reset(new Body(world));
299
+ return pground.get();
300
+ }
301
+
283
302
  void begin_contact (b2ShapeId b2shape1, b2ShapeId b2shape2, bool is_sensor_event)
284
303
  {
285
304
  Shape* s1 = get_shape(b2shape1);
@@ -308,6 +327,9 @@ namespace Reflex
308
327
  ShapePair pair = it->second;
309
328
  touching_pairs.erase(it);
310
329
 
330
+ if (!b2Shape_IsValid(id1) || !b2Shape_IsValid(id2))
331
+ return;
332
+
311
333
  call_contact_events(pair.first, pair.second, ContactEvent::END);
312
334
  }
313
335
 
@@ -338,6 +360,25 @@ namespace Reflex
338
360
  }
339
361
  }
340
362
 
363
+ void update_constraints ()
364
+ {
365
+ for (auto& constraint : constraints)
366
+ {
367
+ if (constraint)
368
+ Constraint_on_world_update(constraint);
369
+ }
370
+ }
371
+
372
+ void clear_constraints ()
373
+ {
374
+ for (auto& constraint : constraints)
375
+ {
376
+ if (constraint)
377
+ Constraint_on_world_destroyed(constraint);
378
+ }
379
+ constraints.clear();
380
+ }
381
+
341
382
  };// World::Data
342
383
 
343
384
 
@@ -361,6 +402,8 @@ namespace Reflex
361
402
  World::~World ()
362
403
  {
363
404
  self->touching_pairs.clear();
405
+ self->clear_constraints();
406
+ self->pground.reset();
364
407
 
365
408
  b2DestroyWorld(self->b2world);
366
409
  self->b2world = b2_nullWorldId;
@@ -377,6 +420,8 @@ namespace Reflex
377
420
 
378
421
  for (int i = 0; i < count; ++i)
379
422
  {
423
+ self->update_constraints();
424
+
380
425
  self->stepping = true;
381
426
  b2World_Step(self->b2world, dt, 4);
382
427
  self->stepping = false;
@@ -426,19 +471,19 @@ namespace Reflex
426
471
  if (state == debug()) return;
427
472
 
428
473
  if (state)
429
- self->debug_draw.reset(new DebugDraw(self->ppm));
474
+ self->pdebug_draw.reset(new DebugDraw(self->ppm));
430
475
  else
431
- self->debug_draw.reset();
476
+ self->pdebug_draw.reset();
432
477
  }
433
478
 
434
479
  bool
435
480
  World::debug () const
436
481
  {
437
- return !!self->debug_draw;
482
+ return !!self->pdebug_draw;
438
483
  }
439
484
 
440
485
  void
441
- World::on_update (float dt)
486
+ World::on_update ()
442
487
  {
443
488
  update(DELTA_TIME);
444
489
  }
@@ -446,11 +491,11 @@ namespace Reflex
446
491
  void
447
492
  World::on_draw (Painter* painter)
448
493
  {
449
- if (!self->debug_draw) return;
494
+ if (!self->pdebug_draw) return;
450
495
 
451
- self->debug_draw->begin(painter);
452
- b2World_Draw(self->b2world, self->debug_draw->b2ptr());
453
- self->debug_draw->end();
496
+ self->pdebug_draw->begin(painter);
497
+ b2World_Draw(self->b2world, self->pdebug_draw->b2ptr());
498
+ self->pdebug_draw->end();
454
499
  }
455
500
 
456
501
 
@@ -460,10 +505,24 @@ namespace Reflex
460
505
  return world ? world->self->b2world : b2_nullWorldId;
461
506
  }
462
507
 
463
- bool
464
- World_is_stepping (const World* world)
508
+ void
509
+ World_add_constraint (World* world, Constraint* constraint)
465
510
  {
466
- return world ? world->self->stepping : false;
511
+ if (!world || !constraint)
512
+ argument_error(__FILE__, __LINE__);
513
+
514
+ world->self->constraints.emplace_back(constraint);
515
+ }
516
+
517
+ void
518
+ World_remove_constraint (World* world, Constraint* constraint)
519
+ {
520
+ if (!world || !constraint)
521
+ argument_error(__FILE__, __LINE__);
522
+
523
+ std::erase_if(
524
+ world->self->constraints,
525
+ [=](auto& c) {return !c || c == constraint;});
467
526
  }
468
527
 
469
528
  void
@@ -490,6 +549,18 @@ namespace Reflex
490
549
  call_contact_events(pair.first, pair.second, ContactEvent::END);
491
550
  }
492
551
 
552
+ bool
553
+ World_is_stepping (const World* world)
554
+ {
555
+ return world ? world->self->stepping : false;
556
+ }
557
+
558
+ const Body*
559
+ World_get_ground (World* world)
560
+ {
561
+ return world ? world->self->ground(world) : NULL;
562
+ }
563
+
493
564
  World*
494
565
  World_get_temporary ()
495
566
  {
data/src/world.h CHANGED
@@ -19,6 +19,7 @@ namespace Reflex
19
19
 
20
20
  class View;
21
21
  class Body;
22
+ class Constraint;
22
23
 
23
24
 
24
25
  class World : public Xot::NonCopyable
@@ -51,7 +52,7 @@ namespace Reflex
51
52
 
52
53
  bool debug () const;
53
54
 
54
- virtual void on_update (float dt);
55
+ virtual void on_update ();
55
56
 
56
57
  virtual void on_draw (Painter* painter);
57
58
 
@@ -104,10 +105,16 @@ namespace Reflex
104
105
 
105
106
  b2WorldId World_get_id (const World* world);
106
107
 
107
- bool World_is_stepping (const World* world);
108
+ void World_add_constraint (World* world, Constraint* constraint);
109
+
110
+ void World_remove_constraint (World* world, Constraint* constraint);
108
111
 
109
112
  void World_end_contacts_for (World* world, b2ShapeId b2shape);
110
113
 
114
+ bool World_is_stepping (const World* world);
115
+
116
+ const Body* World_get_ground (World* world);
117
+
111
118
  World* World_get_temporary ();
112
119
 
113
120
 
data/test/helper.rb CHANGED
@@ -8,3 +8,27 @@ require 'reflex'
8
8
  require 'test/unit'
9
9
 
10
10
  include Xot::Test
11
+
12
+
13
+ module HasWindow
14
+
15
+ def window(*args, **kwargs, &block)
16
+ win = Reflex::Window.new(*args, **kwargs, &block)
17
+ (@windows_for_test__ ||= []).push win
18
+ win
19
+ end
20
+
21
+ def teardown()
22
+ super
23
+ @windows_for_test__&.each(&:close)
24
+ @windows_for_test__ = nil
25
+ end
26
+
27
+ end# HasWindow
28
+
29
+
30
+ def assert_equal_point(expected, actual, delta = 0.01, *args)
31
+ assert_in_delta expected.x, actual.x, delta, *args
32
+ assert_in_delta expected.y, actual.y, delta, *args
33
+ assert_in_delta expected.z, actual.z, delta, *args
34
+ end
@@ -5,12 +5,112 @@ class TestApplication < Test::Unit::TestCase
5
5
 
6
6
  @@app = Reflex::Application.new
7
7
 
8
+ def start(&block)
9
+ @@app.on(:start) {|e| block.call}
10
+ @@app.start
11
+ ensure
12
+ @@app.singleton_class.remove_method :on_start
13
+ end
14
+
15
+ def omit_on_macos_ci()
16
+ omit 'the macos runner aborts in CoreAnimation on an event loop' if ci? && osx?
17
+ end
18
+
19
+ def test_start_returns_on_quit()
20
+ omit_on_macos_ci
21
+
22
+ started = false
23
+ start {started = true; @@app.quit}
24
+ assert_true started
25
+ end
26
+
27
+ def test_start_returns_when_last_window_closed()
28
+ omit_on_macos_ci
29
+
30
+ quit = false
31
+ @@app.on(:quit) {|e| quit = true}
32
+ start {w = Reflex.window; w.on(:update) {|e| w.close}}
33
+ assert_true quit
34
+ end
35
+
36
+ def test_start_raises_on_start_block()
37
+ omit_on_macos_ci
38
+
39
+ e = assert_raise(RuntimeError) {start {raise 'boom'}}
40
+ assert_equal 'boom', e.message
41
+ assert_match(/#{__FILE__}/, e.backtrace.first)
42
+ end
43
+
44
+ def test_start_raises_from_window_event()
45
+ omit_on_macos_ci
46
+
47
+ w = nil
48
+ e = assert_raise(RuntimeError) do
49
+ start {w = Reflex::Window.new; w.on(:update) {|e| raise 'in update'}}
50
+ end
51
+ assert_equal 'in update', e.message
52
+ ensure
53
+ w&.close
54
+ end
55
+
56
+ def test_start_passes_exit()
57
+ omit_on_macos_ci
58
+
59
+ e = assert_raise(SystemExit) {start {exit 3}}
60
+ assert_equal 3, e.status
61
+ end
62
+
63
+ def test_start_passes_throw()
64
+ omit_on_macos_ci
65
+
66
+ assert_throw(:tag) {start {throw :tag}}
67
+ end
68
+
69
+ def test_start_twice()
70
+ omit_on_macos_ci
71
+
72
+ count = 0
73
+ 2.times {start {count += 1; @@app.quit}}
74
+ assert_equal 2, count
75
+ end
76
+
8
77
  def test_name()
9
- assert_equal '', @@app.name
78
+ assert_not_empty @@app.name
79
+
10
80
  @@app.name = 'AppName'
11
81
  assert_equal 'AppName', @@app.name
12
82
  end
13
83
 
84
+ def test_menu()
85
+ m = Reflex::Menu.new
86
+ assert_nil @@app.menu
87
+ @@app.menu = m
88
+ assert_equal m, @@app.menu
89
+ @@app.menu = nil
90
+ assert_nil @@app.menu
91
+
92
+ assert_raise(TypeError) {@@app.menu = 1}
93
+ end
94
+
95
+ def test_background()
96
+ assert_false @@app.background?
97
+ @@app.background = true
98
+ assert_true @@app.background?
99
+ @@app.background = false
100
+ assert_false @@app.background?
101
+ end
102
+
103
+ def test_background_menu()
104
+ m = Reflex::Menu.new
105
+ assert_nil @@app.background_menu
106
+ @@app.background_menu = m
107
+ assert_equal m, @@app.background_menu
108
+ @@app.background_menu = nil
109
+ assert_nil @@app.background_menu
110
+
111
+ assert_raise(TypeError) {@@app.background_menu = 1}
112
+ end
113
+
14
114
  def test_inspect()
15
115
  assert_match %r|#<Reflex::Application:0x\w{16}>|, @@app.inspect
16
116
  end
@@ -0,0 +1,122 @@
1
+ require_relative 'helper'
2
+
3
+
4
+ class TestChaseConstraint < Test::Unit::TestCase
5
+
6
+ include HasWindow
7
+
8
+ def view(x = 0, y = 0, w = 50, h = 50, **options, &block)
9
+ Reflex::View.new(
10
+ frame: [x, y, w, h],
11
+ shape: Reflex::RectShape.new(density: 1),
12
+ **options, &block)
13
+ end
14
+
15
+ def point(...)
16
+ Reflex::Point.new(...)
17
+ end
18
+
19
+ def setup_views(win = window)
20
+ v1 = view 100, 100, dynamic: true
21
+ v2 = view 200, 100, static: true
22
+ win&.add v1
23
+ win&.add v2
24
+ [v1, v2, win, win&.root]
25
+ end
26
+
27
+ def test_initial_state()
28
+ v1, v2, = setup_views
29
+
30
+ assert_equal v1, v1.chase(v2) .pins[0].view
31
+ assert_nil v1.chase(v2) .pins[1].view
32
+ assert_equal v2, v1.chase(v2) .target.view
33
+ assert_nil v1.chase(v2) .force
34
+ assert_equal 1, v1.chase(v2, force: 1) .force
35
+ assert_equal 5, v1.chase(v2) .spring
36
+ assert_equal 2, v1.chase(v2, spring: 2) .spring
37
+ assert_in_delta 0.7, v1.chase(v2) .damping
38
+ assert_in_delta 0.1, v1.chase(v2, damping: 0.1).damping
39
+ assert_false v1.chase(v2) .collide?
40
+ assert_true v1.chase(v2) .active?
41
+ assert_false v1.chase(v2) .removed?
42
+ end
43
+
44
+ def test_chase_view()
45
+ v1, v2, _, root = setup_views
46
+
47
+ c = v1.chase v2
48
+ assert_equal v2, c.target.view
49
+
50
+ root.update_world
51
+ assert_operator v1.linear_velocity.x, :>, 0
52
+ end
53
+
54
+ def test_chase_world_point()
55
+ v1, _, _, root = setup_views
56
+
57
+ c = v1.chase [300, 300]
58
+ assert_nil c.target.view
59
+ assert_equal_point point(300, 300), c.target.pos
60
+
61
+ root.update_world
62
+ assert_operator v1.linear_velocity.x, :>, 0
63
+ assert_operator v1.linear_velocity.y, :>, 0
64
+ end
65
+
66
+ def test_grabbing_corner_creates_torque()
67
+ v1, _, _, root = setup_views
68
+
69
+ c = v1.pin(0, 0).chase [300, 100], spring: 8
70
+ assert_equal_point point(0, 0), c.pins[0].pos
71
+
72
+ 3.times {root.update_world}
73
+ assert_operator v1.angular_velocity.abs, :>, 1
74
+ end
75
+
76
+ def test_chase_to_itself_error()
77
+ v1, v2, = setup_views
78
+
79
+ assert_raise(ArgumentError) {v1.chase v1}
80
+ assert_raise(ArgumentError) {v1.chase(v2).target = v1}
81
+ end
82
+
83
+ def test_target()
84
+ v1, v2, = setup_views
85
+
86
+ c = v1.chase [300, 300]
87
+ assert_nil c.target.view
88
+ assert_equal_point point(300, 300), c.target.pos
89
+
90
+ c.target = v2
91
+ assert_equal v2, c.target.view
92
+ assert_nil c.target.pos
93
+
94
+ c.target = v2.pin(0, 0)
95
+ assert_equal v2, c.target.view
96
+ assert_equal_point point(0, 0), c.target.pos
97
+
98
+ c.target = [v2, 5, 5]
99
+ assert_equal v2, c.target.view
100
+ assert_equal_point point(5, 5), c.target.pos
101
+
102
+ c.target = nil
103
+ assert_nil c.target.view
104
+ assert_nil c.target.pos
105
+ end
106
+
107
+ def test_force()
108
+ v1, = setup_views
109
+
110
+ c = v1.chase [300, 100], force: 500
111
+ assert_equal 500, c.force
112
+
113
+ c.force = nil
114
+ assert_nil c.force
115
+
116
+ c.force = 100
117
+ assert_equal 100, c.force
118
+
119
+ assert_raise(ArgumentError) {c.force = -1}
120
+ end
121
+
122
+ end# TestChaseConstraint