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/win32/menu.cpp CHANGED
@@ -1,16 +1,15 @@
1
1
  #include "menu.h"
2
2
 
3
3
 
4
+ #include <string>
5
+ #include <vector>
6
+ #include <xot/time.h>
4
7
  #include "reflex/exception.h"
5
8
  #include "reflex/event.h"
6
9
  #include "reflex/view.h"
10
+ #include "../rays.h"
7
11
  #include "window.h"
8
-
9
-
10
- namespace Rays
11
- {
12
- HBITMAP Bitmap_get_hbitmap (const Bitmap& bmp);
13
- }
12
+ #include "screen.h"
14
13
 
15
14
 
16
15
  namespace Reflex
@@ -63,6 +62,8 @@ namespace Reflex
63
62
 
64
63
  bool menubar = false;
65
64
 
65
+ bool modified = false;
66
+
66
67
  Image image;
67
68
 
68
69
  std::shared_ptr<HBITMAP__> hbitmap;
@@ -99,14 +100,27 @@ namespace Reflex
99
100
  return (MenuData&) *menu->self;
100
101
  }
101
102
 
103
+ static Menu*
104
+ get_root (Menu* menu)
105
+ {
106
+ while (menu->parent()) menu = menu->parent();
107
+ return menu;
108
+ }
109
+
110
+ static void
111
+ set_modified (Menu* menu)
112
+ {
113
+ get_data(get_root(menu)).modified = true;
114
+ }
115
+
102
116
  static Menu*
103
117
  get_item_owner (HMENU hparent, uint index)
104
118
  {
105
119
  if (!hparent) return NULL;
106
120
 
107
- MENUITEMINFO mii = {sizeof(mii)};
108
- mii.fMask = MIIM_DATA;
109
- if (!GetMenuItemInfo(hparent, index, TRUE, &mii))
121
+ MENUITEMINFOW mii = {sizeof(mii)};
122
+ mii.fMask = MIIM_DATA;
123
+ if (!GetMenuItemInfoW(hparent, index, TRUE, &mii))
110
124
  return NULL;
111
125
 
112
126
  return (Menu*) mii.dwItemData;
@@ -130,14 +144,14 @@ namespace Reflex
130
144
  {
131
145
  MenuData& self = get_data(item);
132
146
 
133
- MENUITEMINFO mii = {sizeof(mii)};
134
- mii.fMask = MIIM_ID | MIIM_DATA | MIIM_FTYPE | MIIM_STRING;
135
- mii.fType = MFT_STRING;
136
- mii.wID = self.id;
137
- mii.dwItemData = (ULONG_PTR) item;
138
- mii.dwTypeData = (LPSTR) "";
147
+ MENUITEMINFOW mii = {sizeof(mii)};
148
+ mii.fMask = MIIM_ID | MIIM_DATA | MIIM_FTYPE | MIIM_STRING;
149
+ mii.fType = MFT_STRING;
150
+ mii.wID = self.id;
151
+ mii.dwItemData = (ULONG_PTR) item;
152
+ mii.dwTypeData = (LPWSTR) L"";
139
153
 
140
- if (!InsertMenuItem(hparent, (UINT) index, TRUE, &mii))
154
+ if (!InsertMenuItemW(hparent, (UINT) index, TRUE, &mii))
141
155
  system_error(__FILE__, __LINE__);
142
156
 
143
157
  self.hparent = hparent;
@@ -151,21 +165,54 @@ namespace Reflex
151
165
  return new MenuData();
152
166
  }
153
167
 
168
+ void
169
+ Menu_validate_shortcut_modifiers (uint modifiers)
170
+ {
171
+ static const uint SUPPORTED =
172
+ MOD_SHIFT | MOD_CONTROL | MOD_ALT | MOD_OPTION;
173
+
174
+ if (modifiers & ~SUPPORTED)
175
+ {
176
+ argument_error(
177
+ __FILE__, __LINE__,
178
+ "modifiers 0x%X can not be used for a shortcut on Windows.",
179
+ modifiers & ~SUPPORTED);
180
+ }
181
+ }
182
+
183
+ static String
184
+ get_shortcut_key_name (int key)
185
+ {
186
+ if (key < 0) return "";
187
+
188
+ UINT vsc = MapVirtualKeyW((UINT) key, MAPVK_VK_TO_VSC_EX);
189
+ if (vsc == 0) return "";
190
+
191
+ LONG lparam = (LONG) ((vsc & 0xff) << 16);
192
+ if (vsc & 0xff00) lparam |= 1 << 24;// extended key
193
+
194
+ wchar_t name[64] = {0};
195
+ int len = GetKeyNameTextW(lparam, name, (int) (sizeof(name) / sizeof(*name)));
196
+ if (len <= 0) return "";
197
+
198
+ return String(name, (size_t) len);
199
+ }
200
+
154
201
  static String
155
202
  make_item_string (const Menu* menu)
156
203
  {
157
- String s = menu->label();
158
- const char* key = menu->shortcut_key();
159
- if (key && *key)
204
+ String str = menu->label();
205
+ String key = menu->empty() ? get_shortcut_key_name(menu->shortcut_key()) : "";
206
+ if (!key.empty())
160
207
  {
161
- s += '\t';
208
+ str += '\t';
162
209
  uint mods = menu->shortcut_modifiers();
163
- if (mods & MOD_CONTROL) s += "Ctrl+";
164
- if (mods & MOD_SHIFT) s += "Shift+";
165
- if (mods & (MOD_ALT | MOD_OPTION)) s += "Alt+";
166
- s += key;
210
+ if (mods & MOD_CONTROL) str += "Ctrl+";
211
+ if (mods & MOD_SHIFT) str += "Shift+";
212
+ if (mods & (MOD_ALT | MOD_OPTION)) str += "Alt+";
213
+ str += key;
167
214
  }
168
- return s;
215
+ return str;
169
216
  }
170
217
 
171
218
  void
@@ -175,22 +222,22 @@ namespace Reflex
175
222
 
176
223
  if (!self.hparent) return;
177
224
 
178
- MENUITEMINFO mii = {sizeof(mii)};
179
- mii.fMask =
225
+ MENUITEMINFOW mii = {sizeof(mii)};
226
+ mii.fMask =
180
227
  MIIM_ID | MIIM_DATA | MIIM_FTYPE | MIIM_STATE | MIIM_BITMAP | MIIM_SUBMENU;
181
- mii.wID = self.id;
182
- mii.dwItemData = (ULONG_PTR) menu;
228
+ mii.wID = self.id;
229
+ mii.dwItemData = (ULONG_PTR) menu;
183
230
 
184
- String label;
231
+ std::wstring label;
185
232
  if (menu->is_separator())
186
233
  mii.fType = MFT_SEPARATOR;
187
234
  else
188
235
  {
189
- label = make_item_string(menu);
236
+ label = make_item_string(menu).to_wstr();
190
237
 
191
238
  mii.fMask |= MIIM_STRING;
192
239
  mii.fType = MFT_STRING;
193
- mii.dwTypeData = (LPSTR) label.c_str();
240
+ mii.dwTypeData = (LPWSTR) label.c_str();
194
241
  mii.fState =
195
242
  (menu->is_enabled() ? MFS_ENABLED : MFS_GRAYED) |
196
243
  (menu->is_checked() ? MFS_CHECKED : MFS_UNCHECKED);
@@ -198,20 +245,24 @@ namespace Reflex
198
245
  mii.hSubMenu = menu->empty() ? NULL : self.hsubmenu;
199
246
  }
200
247
 
201
- SetMenuItemInfo(self.hparent, self.id, FALSE, &mii);
248
+ SetMenuItemInfoW(self.hparent, self.id, FALSE, &mii);
249
+
250
+ set_modified(menu);
202
251
  }
203
252
 
204
253
  void
205
254
  Menu_popup (Menu* menu, View* view, coord x, coord y)
206
255
  {
256
+ Menu_cancel_active_pointers_for_popup(view);
257
+
207
258
  MenuData& self = get_data(menu);
208
259
 
209
- HMENU hmenu = self.get_hsubmenu(menu, false);
260
+ HMENU hmenu = self.get_hsubmenu(menu);
210
261
  if (!hmenu)
211
262
  invalid_state_error(__FILE__, __LINE__);
212
263
 
213
264
  HWND hwnd = NULL;
214
- POINT pos = {(int) x, (int) y};
265
+ POINT pos = Screen_to_native_coord(x, y);
215
266
 
216
267
  if (view)
217
268
  {
@@ -223,9 +274,7 @@ namespace Reflex
223
274
  if (!hwnd)
224
275
  invalid_state_error(__FILE__, __LINE__);
225
276
 
226
- Point p = view->to_window(Point(x, y));
227
- pos.x = (int) p.x;
228
- pos.y = (int) p.y;
277
+ pos = Window_to_native_coord(*win, view->to_window(Point(x, y)));
229
278
  ClientToScreen(hwnd, &pos);
230
279
  }
231
280
  else
@@ -233,14 +282,8 @@ namespace Reflex
233
282
 
234
283
  if (!hwnd) return;
235
284
 
236
- // TrackPopupMenu needs the owner window in the foreground, and the
237
- // trailing WM_NULL is the documented workaround for the menu not closing
238
- // on the first outside click.
239
- SetForegroundWindow(hwnd);
240
- TrackPopupMenu(
241
- hmenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON,
242
- pos.x, pos.y, 0, hwnd, NULL);
243
- PostMessage(hwnd, WM_NULL, 0, 0);
285
+ Menu_track_popup(
286
+ hmenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON, pos, hwnd);
244
287
  }
245
288
 
246
289
  void
@@ -267,12 +310,107 @@ namespace Reflex
267
310
 
268
311
  RemoveMenu(p.hsubmenu, c.id, MF_BYCOMMAND);
269
312
  c.hparent = NULL;
313
+
314
+ set_modified(parent);
315
+ }
316
+
317
+
318
+ void
319
+ Menu_track_popup (HMENU hmenu, UINT flags, POINT pos, HWND owner)
320
+ {
321
+ double start = Xot::time();
322
+
323
+ // TrackPopupMenu needs the owner window in the foreground
324
+ SetForegroundWindow(owner);
325
+
326
+ TrackPopupMenu(hmenu, flags, pos.x, pos.y, 0, owner, NULL);
327
+
328
+ // the documented workaround for the menu not closing
329
+ // on the first outside click
330
+ PostMessageW(owner, WM_NULL, 0, 0);
331
+
332
+ Window_skip_update_time_of_all(Xot::time() - start);
270
333
  }
271
334
 
272
335
  HMENU
273
- Menu_get_hmenu (Menu* menu)
336
+ Menu_get_hmenu (Menu* menu, bool menubar)
337
+ {
338
+ return menu ? get_data(menu).get_hsubmenu(menu, menubar) : NULL;
339
+ }
340
+
341
+ bool
342
+ Menu_is_modified (Menu* menu)
274
343
  {
275
- return menu ? get_data(menu).get_hsubmenu(menu, true) : NULL;
344
+ return menu && get_data(menu).modified;
345
+ }
346
+
347
+ static void
348
+ collect_accels (std::vector<ACCEL>* accels, Menu* menu)
349
+ {
350
+ for (auto& child : *menu)
351
+ {
352
+ Menu* item = child.get();
353
+ if (!item) continue;
354
+
355
+ int key = item->shortcut_key();
356
+ if (key >= 0 && item->empty())
357
+ {
358
+ uint mods = item->shortcut_modifiers();
359
+
360
+ ACCEL accel = {0};
361
+ accel.key = (WORD) key;// KeyCode == VK
362
+ accel.cmd = (WORD) get_data(item).id;
363
+ accel.fVirt = (BYTE) (FVIRTKEY |
364
+ (mods & MOD_CONTROL ? FCONTROL : 0) |
365
+ (mods & MOD_SHIFT ? FSHIFT : 0) |
366
+ (mods & (MOD_ALT | MOD_OPTION) ? FALT : 0));
367
+ accels->emplace_back(accel);
368
+ }
369
+
370
+ collect_accels(accels, item);
371
+ }
372
+ }
373
+
374
+ HACCEL
375
+ Menu_create_accelerator_table (Menu* menu)
376
+ {
377
+ if (!menu) return NULL;
378
+
379
+ get_data(menu).modified = false;
380
+
381
+ std::vector<ACCEL> accels;
382
+ collect_accels(&accels, menu);
383
+ if (accels.empty()) return NULL;
384
+
385
+ return CreateAcceleratorTableW(accels.data(), (int) accels.size());
386
+ }
387
+
388
+ static Menu*
389
+ find_item (Menu* menu, UINT id)
390
+ {
391
+ for (auto& child : *menu)
392
+ {
393
+ Menu* item = child.get();
394
+ if (!item || !item->is_enabled()) continue;
395
+
396
+ if (get_data(item).id == id) return item;
397
+
398
+ Menu* found = find_item(item, id);
399
+ if (found) return found;
400
+ }
401
+ return NULL;
402
+ }
403
+
404
+ void
405
+ Menu_call_command_event (Menu* menu, uint id)
406
+ {
407
+ if (!menu) return;
408
+
409
+ Menu* item = find_item(menu, (UINT) id);
410
+ if (!item) return;
411
+
412
+ Event e;
413
+ Menu_call_click_event(item, &e);
276
414
  }
277
415
 
278
416
  void
@@ -282,7 +420,7 @@ namespace Reflex
282
420
  if (!menu) return;
283
421
 
284
422
  Event e;
285
- menu->on_click(&e);
423
+ Menu_call_click_event(menu, &e);
286
424
  }
287
425
 
288
426
  void
@@ -292,7 +430,7 @@ namespace Reflex
292
430
  if (!menu) return;
293
431
 
294
432
  Event e;
295
- menu->on_open_submenu(&e);
433
+ Menu_call_open_submenu_event(menu, &e);
296
434
 
297
435
  int count = GetMenuItemCount(hmenu);
298
436
  for (int i = 0; i < count; ++i)
@@ -301,7 +439,7 @@ namespace Reflex
301
439
  if (!child) continue;
302
440
 
303
441
  Event ce;
304
- child->on_show(&ce);
442
+ Menu_call_show_event(child, &ce);
305
443
  }
306
444
  }
307
445
 
@@ -318,11 +456,11 @@ namespace Reflex
318
456
  if (!child) continue;
319
457
 
320
458
  Event ce;
321
- child->on_hide(&ce);
459
+ Menu_call_hide_event(child, &ce);
322
460
  }
323
461
 
324
462
  Event e;
325
- menu->on_close_submenu(&e);
463
+ Menu_call_close_submenu_event(menu, &e);
326
464
  }
327
465
 
328
466
 
data/src/win32/menu.h CHANGED
@@ -12,7 +12,15 @@ namespace Reflex
12
12
  {
13
13
 
14
14
 
15
- HMENU Menu_get_hmenu (Menu* menu);
15
+ void Menu_track_popup (HMENU hmenu, UINT flags, POINT pos, HWND owner);
16
+
17
+ HMENU Menu_get_hmenu (Menu* menu, bool menubar = false);
18
+
19
+ bool Menu_is_modified (Menu* menu);
20
+
21
+ HACCEL Menu_create_accelerator_table (Menu* menu);
22
+
23
+ void Menu_call_command_event (Menu* menu, uint id);
16
24
 
17
25
  void Menu_call_command_event (HMENU hmenu, uint index);
18
26
 
data/src/win32/opengl.cpp CHANGED
@@ -3,14 +3,7 @@
3
3
 
4
4
  #include <rays/rays.h>
5
5
  #include "reflex/exception.h"
6
-
7
-
8
- namespace Rays
9
- {
10
-
11
- const PIXELFORMATDESCRIPTOR* get_pixel_format_descriptor ();
12
-
13
- }// Rays
6
+ #include "../rays.h"
14
7
 
15
8
 
16
9
  namespace Reflex
@@ -40,7 +33,7 @@ namespace Reflex
40
33
  if (!hdc)
41
34
  system_error(__FILE__, __LINE__);
42
35
 
43
- const auto* pfd = Rays::get_pixel_format_descriptor();
36
+ const auto* pfd = Rays::OpenGL_get_pixel_format_descriptor();
44
37
  int pf = ChoosePixelFormat(hdc, pfd);
45
38
  if (pf == 0)
46
39
  system_error(__FILE__, __LINE__);
data/src/win32/reflex.cpp CHANGED
@@ -1,17 +1,38 @@
1
1
  #include "reflex/reflex.h"
2
2
 
3
3
 
4
+ #include <xot/windows.h>
4
5
  #include "reflex/exception.h"
6
+ #include "../rays.h"
5
7
 
6
8
 
7
9
  namespace Reflex
8
10
  {
9
11
 
10
12
 
13
+ static struct ReflexLoaded
14
+ {
15
+ ReflexLoaded ()
16
+ {
17
+ Rays::Rays_set_pre_init_fun(pre_init);
18
+ }
19
+
20
+ static void pre_init ()
21
+ {
22
+ // enable High DPI: before Rays makes the hidden window for its OpenGL
23
+ // context, as Windows takes no declaration after a window
24
+ SetProcessDPIAware();
25
+ }
26
+
27
+ } reflex_loaded;
28
+
29
+
11
30
  namespace global
12
31
  {
13
32
 
14
- static bool initialized = false;
33
+ static bool initialized = false;
34
+
35
+ static bool com_initialized = false;
15
36
 
16
37
  }// global
17
38
 
@@ -21,8 +42,10 @@ namespace Reflex
21
42
  {
22
43
  if (global::initialized)
23
44
  reflex_error(__FILE__, __LINE__, "already initialized.");
24
-
25
45
  global::initialized = true;
46
+
47
+ global::com_initialized =
48
+ SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
26
49
  }
27
50
 
28
51
  void
@@ -30,8 +53,26 @@ namespace Reflex
30
53
  {
31
54
  if (!global::initialized)
32
55
  reflex_error(__FILE__, __LINE__, "not initialized.");
33
-
34
56
  global::initialized = false;
57
+
58
+ if (global::com_initialized)
59
+ {
60
+ CoUninitialize();
61
+ global::com_initialized = false;
62
+ }
63
+ }
64
+
65
+ void
66
+ alert (const char* message, const char* title)
67
+ {
68
+ if (!message)
69
+ argument_error(__FILE__, __LINE__);
70
+
71
+ MessageBoxW(
72
+ GetActiveWindow(),
73
+ String(message) .to_wstr().c_str(),
74
+ String(title ? title : "").to_wstr().c_str(),
75
+ MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
35
76
  }
36
77
 
37
78
 
data/src/win32/screen.cpp CHANGED
@@ -1,6 +1,11 @@
1
1
  #include "screen.h"
2
2
 
3
3
 
4
+ #include <math.h>
5
+ #include <wchar.h>
6
+ #include <algorithm>
7
+ #include <memory>
8
+ #include <vector>
4
9
  #include "reflex/exception.h"
5
10
 
6
11
 
@@ -22,6 +27,56 @@ namespace Reflex
22
27
  pthis->self->handle = hmonitor;
23
28
  }
24
29
 
30
+ float
31
+ Screen_get_pixel_density (HMONITOR hmonitor)
32
+ {
33
+ static float density = 0;
34
+ if (density <= 0)
35
+ {
36
+ std::shared_ptr<HDC__> dc(GetDC(NULL), [](HDC dc) {if (dc) ReleaseDC(NULL, dc);});
37
+ int dpi = dc ? GetDeviceCaps(dc.get(), LOGPIXELSX) : 0;
38
+ density = dpi > 0 ? dpi / (float) USER_DEFAULT_SCREEN_DPI : 1;
39
+ }
40
+ return density;
41
+ }
42
+
43
+ Bounds
44
+ Screen_from_native_coord (const RECT& rect)
45
+ {
46
+ float density = Screen_get_pixel_density(
47
+ MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST));
48
+ return Bounds(
49
+ rect.left / density,
50
+ rect.top / density,
51
+ (rect.right - rect.left) / density,
52
+ (rect.bottom - rect.top) / density);
53
+ }
54
+
55
+ POINT
56
+ Screen_to_native_coord (coord x, coord y)
57
+ {
58
+ // NULL: the monitor is unknown from points, but every monitor has the
59
+ // same system density for now
60
+ float density = Screen_get_pixel_density(NULL);
61
+
62
+ return {
63
+ (LONG) lround(x * density),
64
+ (LONG) lround(y * density)
65
+ };
66
+ }
67
+
68
+ RECT
69
+ Screen_to_native_coord (coord x, coord y, coord width, coord height)
70
+ {
71
+ float density = Screen_get_pixel_density(NULL);
72
+ return {
73
+ (LONG) lround( x * density),
74
+ (LONG) lround( y * density),
75
+ (LONG) lround((x + width) * density),
76
+ (LONG) lround((y + height) * density)
77
+ };
78
+ }
79
+
25
80
 
26
81
  Screen::Screen ()
27
82
  {
@@ -31,6 +86,73 @@ namespace Reflex
31
86
  {
32
87
  }
33
88
 
89
+ static String
90
+ get_monitor_name (const wchar_t* device)
91
+ {
92
+ UINT32 npaths = 0, nmodes = 0;
93
+ if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &npaths, &nmodes) != ERROR_SUCCESS)
94
+ return "";
95
+
96
+ std::vector<DISPLAYCONFIG_PATH_INFO> paths(npaths);
97
+ std::vector<DISPLAYCONFIG_MODE_INFO> modes(nmodes);
98
+ LONG ret = QueryDisplayConfig(
99
+ QDC_ONLY_ACTIVE_PATHS, &npaths, paths.data(), &nmodes, modes.data(), NULL);
100
+ if (ret != ERROR_SUCCESS)
101
+ return "";
102
+
103
+ for (UINT32 i = 0; i < npaths; ++i)
104
+ {
105
+ const auto& path = paths[i];
106
+
107
+ DISPLAYCONFIG_SOURCE_DEVICE_NAME source = {};
108
+ source.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
109
+ source.header.size = sizeof(source);
110
+ source.header.adapterId = path.sourceInfo.adapterId;
111
+ source.header.id = path.sourceInfo.id;
112
+ if (
113
+ DisplayConfigGetDeviceInfo(&source.header) != ERROR_SUCCESS ||
114
+ wcscmp(source.viewGdiDeviceName, device) != 0)
115
+ {
116
+ continue;
117
+ }
118
+
119
+ DISPLAYCONFIG_TARGET_DEVICE_NAME target = {};
120
+ target.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
121
+ target.header.size = sizeof(target);
122
+ target.header.adapterId = path.targetInfo.adapterId;
123
+ target.header.id = path.targetInfo.id;
124
+ if (DisplayConfigGetDeviceInfo(&target.header) != ERROR_SUCCESS)
125
+ continue;
126
+
127
+ const wchar_t* name = target.monitorFriendlyDeviceName;
128
+ if (*name)
129
+ return String(name, wcslen(name));
130
+ }
131
+ return "";
132
+ }
133
+
134
+ String
135
+ Screen::name () const
136
+ {
137
+ if (!*this)
138
+ invalid_state_error(__FILE__, __LINE__);
139
+
140
+ MONITORINFOEXW mi = {};
141
+ mi.cbSize = sizeof(mi);
142
+ if (!GetMonitorInfoW(self->handle, &mi))
143
+ system_error(__FILE__, __LINE__);
144
+
145
+ String name = get_monitor_name(mi.szDevice);
146
+ if (name.empty())
147
+ {
148
+ const wchar_t* device = mi.szDevice;
149
+ if (wcsncmp(device, L"\\\\.\\", 4) == 0) device += 4;
150
+ name = String(device, wcslen(device));
151
+ }
152
+
153
+ return name;
154
+ }
155
+
34
156
  Bounds
35
157
  Screen::frame () const
36
158
  {
@@ -39,11 +161,19 @@ namespace Reflex
39
161
 
40
162
  MONITORINFO mi = {0};
41
163
  mi.cbSize = sizeof(mi);
42
- if (!GetMonitorInfo(self->handle, &mi))
164
+ if (!GetMonitorInfoW(self->handle, &mi))
43
165
  system_error(__FILE__, __LINE__);
44
166
 
45
- const auto& r = mi.rcMonitor;
46
- return Bounds(r.left, r.top, r.right - r.left, r.bottom - r.top);
167
+ return Screen_from_native_coord(mi.rcMonitor);
168
+ }
169
+
170
+ float
171
+ Screen::pixel_density () const
172
+ {
173
+ if (!*this)
174
+ invalid_state_error(__FILE__, __LINE__);
175
+
176
+ return Screen_get_pixel_density(self->handle);
47
177
  }
48
178
 
49
179
  Screen::operator bool () const
@@ -57,5 +187,36 @@ namespace Reflex
57
187
  return !operator bool();
58
188
  }
59
189
 
190
+ static BOOL CALLBACK
191
+ add_screen (HMONITOR hmonitor, HDC, LPRECT, LPARAM lp)
192
+ {
193
+ Screen s;
194
+ Screen_initialize(&s, hmonitor);
195
+ ((Screen::List*) lp)->emplace_back(s);
196
+ return TRUE;
197
+ }
198
+
199
+ static bool
200
+ is_primary (const Screen& screen)
201
+ {
202
+ MONITORINFO mi = {0};
203
+ mi.cbSize = sizeof(mi);
204
+ if (!GetMonitorInfoW(screen.self->handle, &mi))
205
+ return false;
206
+
207
+ return mi.dwFlags & MONITORINFOF_PRIMARY;
208
+ }
209
+
210
+ Screen::List
211
+ Screen::all ()
212
+ {
213
+ List list;
214
+ if (!EnumDisplayMonitors(NULL, NULL, add_screen, (LPARAM) &list))
215
+ system_error(__FILE__, __LINE__);
216
+
217
+ std::stable_partition(list.begin(), list.end(), is_primary);
218
+ return list;
219
+ }
220
+
60
221
 
61
222
  }// Reflex
data/src/win32/screen.h CHANGED
@@ -14,6 +14,14 @@ namespace Reflex
14
14
 
15
15
  void Screen_initialize (Screen* pthis, HMONITOR hmonitor);
16
16
 
17
+ float Screen_get_pixel_density (HMONITOR hmonitor);
18
+
19
+ Bounds Screen_from_native_coord (const RECT& rect);
20
+
21
+ POINT Screen_to_native_coord (coord x, coord y);
22
+
23
+ RECT Screen_to_native_coord (coord x, coord y, coord width, coord height);
24
+
17
25
 
18
26
  }// Reflex
19
27