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/samples/model.rb CHANGED
@@ -41,9 +41,9 @@ class SliderView < View
41
41
 
42
42
  private
43
43
 
44
- def knob_x_max ()
45
- width - @knob.width
46
- end
44
+ def knob_x_max ()
45
+ width - @knob.width
46
+ end
47
47
 
48
48
  end# SliderView
49
49
 
@@ -0,0 +1,93 @@
1
+ # Manual check for Reflex.alert.
2
+ #
3
+ # ruby alert.rb
4
+ # -> a window counts its frames and shows the largest dt so far.
5
+ # each key shows an alert, which returns only when closed
6
+ # 1 with a title
7
+ # macos: the title in bold, the message under it
8
+ # windows: the title in the title bar
9
+ # 2 with no title
10
+ # macos: the message in bold
11
+ # windows: an empty title bar, not 'Error'
12
+ # 3 a long message over many lines, as a backtrace
13
+ # 4 a message and a title in japanese
14
+ # 5 in 3 seconds: switch to another app meanwhile
15
+ # -> the alert comes to the front and takes the keyboard
16
+ # q quit
17
+ # while an alert is open, the frame count stops. once it is
18
+ # closed, 'closed' is logged and the largest dt jumps by about the
19
+ # time it was open, as requestAnimationFrame does after alert()
20
+ # ruby alert.rb before
21
+ # -> an alert shows before the app starts, in front of the
22
+ # terminal, and closes with return. then the window opens and
23
+ # 'on_start' is logged: an alert does not keep the app from
24
+ # finishing its launch
25
+
26
+ %w[xot rays reflex]
27
+ .map {|s| File.expand_path "../../../#{s}/lib", __dir__}
28
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
29
+
30
+ require 'reflex'
31
+
32
+
33
+ KEYS = [
34
+ '1: with a title 2: with no title',
35
+ '3: long message 4: japanese',
36
+ '5: in 3 seconds q: quit'
37
+ ]
38
+
39
+ LOG = []
40
+
41
+ def log(text)
42
+ LOG.unshift text
43
+ puts text
44
+ end
45
+
46
+
47
+ Reflex.alert 'Shown before the app starts.', title: 'Before' if ARGV[0] == 'before'
48
+
49
+ Reflex.start do
50
+ log 'on_start'
51
+
52
+ frames, max_dt = 0, 0
53
+
54
+ win = Reflex::Window.new title: 'Reflex.alert', frame: [100, 100, 560, 360]
55
+
56
+ alert = -> (message, title = nil) {
57
+ Reflex.alert message, title: title
58
+ log "closed: #{title.inspect}"
59
+ }
60
+
61
+ win.on(:key_down) do |e|
62
+ case e.chars
63
+ when '1' then alert['The message.', 'The Title']
64
+ when '2' then alert['The message with no title.']
65
+ when '3'
66
+ lines = 40.times.map {" from /path/to/app/file_#{_1}.rb:#{_1 * 7}:in 'method_#{_1}'"}
67
+ alert[["boom (RuntimeError)", *lines].join("\n"), 'Long']
68
+ when '4' then alert['日本語のメッセージ', '日本語のタイトル']
69
+ when '5'
70
+ log 'an alert in 3 seconds'
71
+ win.root.delay(3) {alert['Shown by a timer.', 'Timer']}
72
+ when 'q' then Reflex.quit
73
+ end
74
+ end
75
+
76
+ win.on(:update) do |e|
77
+ frames += 1
78
+ max_dt = e.dt if e.dt > max_dt
79
+ win.redraw
80
+ end
81
+
82
+ win.on(:draw) do |e|
83
+ p = e.painter
84
+ p.background 0.1, 0.1, 0.15
85
+ p.fill 1
86
+ KEYS.each_with_index {|text, i| p.text text, 10, 10 + i * 18}
87
+ p.text "frames: #{frames} max dt: #{'%.3f' % max_dt}", 10, 76
88
+ p.fill 0.5, 1, 0.5
89
+ LOG.first(12).each_with_index {|text, i| p.text text, 10, 110 + i * 18}
90
+ end
91
+
92
+ win.show
93
+ end
@@ -0,0 +1,76 @@
1
+ # Manual check for always_on_top and always_on_bottom.
2
+ #
3
+ # Three windows sit on top of each other, one kept above every other
4
+ # application, one kept below, and one that behaves like any other window.
5
+ # The colour and the text say which is which.
6
+ #
7
+ # 1. click another application, a browser or a finder window
8
+ # -> the red one stays in front of it, the green one goes behind it,
9
+ # and the blue one behaves as usual
10
+ # 2. press 2 on the blue window
11
+ # -> it turns red and comes to the front, and stays there
12
+ # 3. press 3 on it
13
+ # -> it turns green and drops behind the other application
14
+ # 4. press 1 on it
15
+ # -> it is a plain window again
16
+ # 5. press 2 on the green window
17
+ # -> it turns red. the two ends are never both on
18
+ # 6. windows only: click the green one where another application covers it
19
+ # -> it must not come to the front
20
+
21
+ %w[xot rays reflex]
22
+ .map {|s| File.expand_path "../../../#{s}/lib", __dir__}
23
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
24
+
25
+ require 'reflex'
26
+
27
+
28
+ COLORS = {
29
+ normal: [0.2, 0.3, 0.6],
30
+ top: [0.7, 0.2, 0.2],
31
+ bottom: [0.2, 0.5, 0.3]
32
+ }
33
+
34
+ def level_of(win)
35
+ case
36
+ when win.always_on_top? then :top
37
+ when win.always_on_bottom? then :bottom
38
+ else :normal
39
+ end
40
+ end
41
+
42
+ def set_level(win, level)
43
+ win.always_on_top = level == :top
44
+ win.always_on_bottom = level == :bottom
45
+ puts "#{win.title}: #{level_of win}"
46
+ end
47
+
48
+ %i[normal top bottom].each_with_index do |level, i|
49
+ win = Reflex::Window.new(
50
+ title: level.to_s,
51
+ frame: [100 + i * 50, 100 + i * 50, 320, 140])
52
+
53
+ set_level win, level
54
+
55
+ win.on(:draw) do |e|
56
+ p = e.painter
57
+ p.background(*COLORS[level_of(win)])
58
+ p.fill 1, 1, 1
59
+ p.text level_of(win).to_s, 10, 10
60
+ p.text '1: normal 2: top 3: bottom', 10, 40
61
+ end
62
+
63
+ win.on(:key_down) do |e|
64
+ case e.chars
65
+ when '1' then set_level win, :normal
66
+ when '2' then set_level win, :top
67
+ when '3' then set_level win, :bottom
68
+ end
69
+ win.redraw
70
+ end
71
+
72
+ win.show
73
+ end
74
+
75
+
76
+ Reflex.start
@@ -0,0 +1,51 @@
1
+ # Manual check for Application#background and #background_menu.
2
+ #
3
+ # The application lives in the menu bar: no dock icon, a status item
4
+ # with a black circle, and it keeps running with no window open.
5
+ #
6
+ # 1. start and look at the dock
7
+ # -> the application is not there, and nothing steals the focus
8
+ # 2. click the black circle at the right end of the menu bar
9
+ # -> a menu opens with Settings, Toggle Dock and Quit
10
+ # 3. click Settings
11
+ # -> a small window opens, once per click
12
+ # 4. close every window
13
+ # -> the application keeps running, the status item stays
14
+ # 5. click Toggle Dock
15
+ # -> the application appears in the dock with the standard menu
16
+ # bar, and the status item stays
17
+ # 6. close every window, then click Toggle Dock in the status item menu
18
+ # -> gone from the dock again, still running
19
+ # 7. click Quit
20
+ # -> the application quits
21
+
22
+ %w[xot rays reflex]
23
+ .map {|s| File.expand_path "../../../#{s}/lib", __dir__}
24
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
25
+
26
+ require 'reflex'
27
+
28
+ icon = Rays::Image.new(16, 16).paint {fill :black; ellipse 2, 2, 12}
29
+
30
+ Reflex.start name: 'BackgroundApp' do
31
+ settings = Reflex::Menu.new('Settings')
32
+ toggle = Reflex::Menu.new('Toggle Dock')
33
+ quit = Reflex::Menu.new('Quit')
34
+
35
+ settings.on(:click) do |e|
36
+ Reflex::Window.new(title: 'Settings', frame: [100, 100, 300, 200]).show
37
+ end
38
+ toggle.on(:click) do |e|
39
+ app = Reflex::Application.instance
40
+ app.background = !app.background?
41
+ end
42
+ quit.on(:click) {|e| Reflex::Application.instance.quit}
43
+
44
+ self.background_menu = Reflex::Menu.new('BG', image: icon) do
45
+ add settings
46
+ add toggle
47
+ add Reflex::Menu.new('-')
48
+ add quit
49
+ end
50
+ self.background = true
51
+ end
@@ -0,0 +1,75 @@
1
+ # Manual check for the synthesized window enter/leave.
2
+ #
3
+ # The window's edge is drawn as a frame: green while the pointer is inside,
4
+ # red while it is outside, so what the log says can be seen at a glance.
5
+ #
6
+ # 1. move the mouse in and out with NO button held
7
+ # -> one :enter on the way in, one :leave on the way out, never a pair
8
+ # 2. press LEFT inside, drag out, drag back in, release inside
9
+ # -> :leave on the way out, :enter on the way back in, both WHILE
10
+ # dragging. this is what no platform used to send
11
+ # 3. release the button outside, then move back in
12
+ # -> a single :enter, not one for the release and one for the move
13
+ # 4. cross the edge quickly, several times
14
+ # -> the count of :enter and :leave stays balanced, never two in a row
15
+ # 5. macos only: after step 2, watch for a late :enter once the button is
16
+ # released
17
+ # -> there must NOT be one; it was already announced during the drag
18
+ # 6. right click inside to pop a menu up, dismiss it, then move in and out
19
+ # -> :cancel is logged, and the boundary keeps working afterwards. the
20
+ # frame may turn red while the menu covers the window
21
+ # 7. hold LEFT, drag outside, then right click to pop the menu up
22
+ # -> :cancel is logged for the left press; moving back in still gives a
23
+ # single :enter, and releasing left produces nothing
24
+
25
+ %w[xot rays reflex]
26
+ .map {|s| File.expand_path "../../../#{s}/lib", __dir__}
27
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
28
+
29
+ require 'reflex'
30
+
31
+ Reflex.start do
32
+ menu = Reflex::Menu.new
33
+ item = menu.add Reflex::Menu.new('Hello')
34
+ item.on(:click) {puts 'menu item clicked'}
35
+
36
+ win = Reflex::Window.new frame: [100, 100, 400, 300]
37
+ inside = false
38
+ last = nil
39
+ pressed = false
40
+
41
+ show = -> action, e do
42
+ puts "%-6s pressed:%-5s pos:%s" % [
43
+ action, pressed, e.position.to_a.map {_1.round}
44
+ ]
45
+ end
46
+
47
+ boundary = -> action, e do
48
+ warn "TWO #{action} IN A ROW" if action == last
49
+ last = action
50
+ show[action, e]
51
+ end
52
+
53
+ win.on(:pointer_enter) {|e| inside = true; boundary[:enter, e]; win.redraw}
54
+ win.on(:pointer_leave) {|e| inside = false; boundary[:leave, e]; win.redraw}
55
+
56
+ win.on(:pointer_down) do |e|
57
+ pressed = true
58
+ menu.popup win.root, e.x, e.y if e.right?
59
+ end
60
+
61
+ win.on(:pointer_up) {|e| pressed = false}
62
+
63
+ win.on(:pointer_cancel) do |e|
64
+ pressed = false
65
+ show[:cancel, e]
66
+ end
67
+
68
+ win.on(:draw) do |e|
69
+ e.painter.push fill: nil, stroke: (inside ? :green : :red), stroke_width: 8 do
70
+ e.painter.rect 4, 4, e.bounds.width - 8, e.bounds.height - 8
71
+ end
72
+ end
73
+
74
+ win.show
75
+ end
@@ -0,0 +1,101 @@
1
+ # Manual check for the paths into ruby that need a hand on the mouse or the
2
+ # keyboard, the rest is in test_application.rb. Reflex.start must return
3
+ # when the app quits, and a raise in a callback must come out of it as a
4
+ # ruby exception with a backtrace, never as 'libc++abi: terminating'.
5
+ #
6
+ # ruby callback_exception.rb
7
+ # -> quit with cmd-q, then again with the close button, then again
8
+ # from the dock menu, then again with the q key. 'after start'
9
+ # and 'at_exit' must be printed every time
10
+ # ruby callback_exception.rb block
11
+ # -> press cmd-q twice. the first one is blocked by on_quit, the
12
+ # second one goes. the close button would close the window
13
+ # first and leave the app running without one
14
+ # ruby callback_exception.rb HOOK
15
+ # -> raise in that hook, and expect a ruby backtrace and status 1
16
+ # pointer click the window
17
+ # key press a key
18
+ # resize resize the window
19
+ # activate switch to another app and back
20
+ # menu choose Raise from the File menu, or press cmd-r (ctrl-r on windows)
21
+ # file press o and choose a file in the dialog
22
+ # quit quit the app any way, on_quit raises
23
+
24
+ %w[xot rays reflex]
25
+ .map {|s| File.expand_path "../../../#{s}/lib", __dir__}
26
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
27
+
28
+ require 'xot/util'
29
+ require 'reflex'
30
+
31
+
32
+ MODE = ARGV[0] || 'none'
33
+ MOD = Xot.osx? ? :command : :control
34
+
35
+ NOTES = {
36
+ 'none' => 'quit with cmd-q, the close button or the dock menu',
37
+ 'block' => 'press cmd-q twice. the first one is blocked, the second one goes',
38
+ 'pointer' => 'click the window',
39
+ 'key' => 'press any key',
40
+ 'resize' => 'resize the window',
41
+ 'activate' => 'switch to another app and back',
42
+ 'menu' => "choose Raise from the File menu, or press #{MOD}-r",
43
+ 'file' => 'press o and choose a file',
44
+ 'quit' => 'quit any way, on_quit raises'
45
+ }
46
+
47
+ unless NOTES.key? MODE
48
+ warn "unknown mode: #{MODE}"
49
+ exit 2
50
+ end
51
+
52
+ at_exit {puts 'at_exit'}
53
+
54
+ def boom(where)
55
+ raise "boom in #{where}"
56
+ end
57
+
58
+
59
+ quit_count = 0
60
+
61
+ Reflex.start do
62
+ on(:quit) do |e|
63
+ quit_count += 1
64
+ boom 'on_quit' if MODE == 'quit'
65
+ e.block if MODE == 'block' && quit_count == 1
66
+ end
67
+
68
+ win = Reflex::Window.new title: "callback_exception: #{MODE}", frame: [100, 100, 480, 240]
69
+
70
+ win.on(:draw) do |e|
71
+ p = e.painter
72
+ p.fill 1
73
+ p.text "mode: #{MODE}", 20, 30
74
+ p.text NOTES[MODE], 20, 60
75
+ p.text 'q: quit o: file dialog', 20, 100
76
+ end
77
+
78
+ win.on(:pointer_down) {|e| boom 'on_pointer_down' if MODE == 'pointer'}
79
+ win.on(:resize) {|e| boom 'on_resize' if MODE == 'resize'}
80
+ win.on(:activate) {|e| boom 'on_activate' if MODE == 'activate'}
81
+
82
+ win.on(:key_down) do |e|
83
+ boom 'on_key_down' if MODE == 'key'
84
+ case e.chars
85
+ when 'q' then Reflex.quit
86
+ when 'o'
87
+ dialog = Reflex::FileDialog.new owner: win
88
+ dialog.on(:file) {|e| boom 'on_file' if MODE == 'file'}
89
+ dialog.open_file {|path| puts "chosen: #{path}"}
90
+ end
91
+ end
92
+
93
+ app_menu = Reflex::Application.instance.menu || Reflex::Menu.new
94
+ file = app_menu.add Reflex::Menu.new('File'), index: 1
95
+ item = file .add Reflex::Menu.new('Raise', shortcut: [:r, MOD])
96
+ item.on(:click) {|e| boom 'menu on_click' if MODE == 'menu'}
97
+
98
+ win.show
99
+ end
100
+
101
+ puts 'after start'
@@ -0,0 +1,53 @@
1
+ # Manual check for Window_cancel_active_pointers and
2
+ # reject_unpaired_up_pointers.
3
+ #
4
+ # 1. right click
5
+ # -> :cancel logged (down_id set), then the menu opens
6
+ # 2. close the menu (click away or select), keep watching the log
7
+ # -> NO :up line for the canceled id must appear
8
+ # 3. move the mouse
9
+ # -> the white dot follows (tracking alive)
10
+ # 4. hold LEFT, then right click while holding
11
+ # -> :cancel logged for the left press, menu opens
12
+ # close the menu, release left
13
+ # -> NO :up for the left press
14
+ # press left again
15
+ # -> :down with a NEW id, then :up pairs with it as usual
16
+ # 5. plain left clicks behave as before (:down then :up, ids advance)
17
+
18
+ %w[xot rays reflex]
19
+ .map {|s| File.expand_path "../../../#{s}/lib", __dir__}
20
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
21
+
22
+ require 'reflex'
23
+
24
+ Reflex.start do
25
+ menu = Reflex::Menu.new
26
+ item = menu.add Reflex::Menu.new('Hello')
27
+ item.on(:click) {puts "menu item clicked"}
28
+
29
+ win = Reflex::Window.new frame: [100, 100, 400, 300]
30
+ pos = nil
31
+
32
+ win.on(:pointer) do |e|
33
+ e.each do |p|
34
+ next if p.move? && p.down.nil? # skip hover moves to keep the log readable
35
+ puts "%-7s id:%-3d down_id:%-5s buttons:%p pos:%s" % [
36
+ p.action, p.id, p.down&.id.inspect, p.types, p.position.to_a.map {_1.round}
37
+ ]
38
+ end
39
+ if e.move?
40
+ pos = e.position
41
+ win.redraw
42
+ end
43
+ menu.popup win.root, e.x, e.y if e.down? && e.right?
44
+ end
45
+
46
+ win.on(:draw) do |e|
47
+ e.painter.push fill: :white do
48
+ e.painter.ellipse pos.x - 5, pos.y - 5, 10, 10 if pos
49
+ end
50
+ end
51
+
52
+ win.show
53
+ end
@@ -0,0 +1,63 @@
1
+ # Manual check for ColorPicker, the rest is in test_color_picker.rb.
2
+ #
3
+ # ruby color_picker.rb
4
+ # -> press c to open the picker. the window background follows the
5
+ # picked color, and 'close' is printed when the picker closes
6
+ # macos: the block is called on every change while the panel is open
7
+ # windows: the block is called once when the dialog is closed with ok
8
+ # a toggle alpha, then press c again (macos only)
9
+ # d open a second picker. on macos the first one prints 'close' at
10
+ # once because the shared panel moves to the second one
11
+ # q quit
12
+ # ruby color_picker.rb raise
13
+ # -> the block raises. expect a ruby backtrace and status 1
14
+
15
+ %w[xot rays reflex]
16
+ .map {|s| File.expand_path "../../../#{s}/lib", __dir__}
17
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
18
+
19
+ require 'reflex'
20
+
21
+
22
+ RAISE = ARGV[0] == 'raise'
23
+
24
+ at_exit {puts 'at_exit'}
25
+
26
+
27
+ Reflex.start do
28
+ win = Reflex::Window.new title: 'color_picker', frame: [100, 100, 480, 240]
29
+ pickers = 2.times.map do |i|
30
+ Reflex::ColorPicker.new(owner: win).tap do |picker|
31
+ picker.on(:close) {|e| puts "close #{i + 1}"}
32
+ end
33
+ end
34
+ color = Reflex::Color.new 0.5, 0.5, 0.5
35
+
36
+ pick = -> picker do
37
+ picker.pick_color(color) do |c|
38
+ raise 'boom in the color block' if RAISE
39
+ color = c
40
+ win.redraw
41
+ end
42
+ end
43
+
44
+ win.on(:draw) do |e|
45
+ p = e.painter
46
+ p.fill color
47
+ p.rect e.bounds
48
+ p.fill 1
49
+ p.text "color: #{color} alpha: #{pickers[0].alpha?}", 20, 30
50
+ p.text 'c: pick d: pick with 2nd a: toggle alpha q: quit', 20, 60
51
+ end
52
+
53
+ win.on(:key_down) do |e|
54
+ case e.chars
55
+ when 'c' then pick.call pickers[0]
56
+ when 'd' then pick.call pickers[1]
57
+ when 'a' then pickers[0].alpha = !pickers[0].alpha?; win.redraw
58
+ when 'q' then Reflex.quit
59
+ end
60
+ end
61
+
62
+ win.show
63
+ end
@@ -0,0 +1,102 @@
1
+ # Manual check for FileDialog.
2
+ #
3
+ # Each key opens a dialog. The window logs what came back, newest first.
4
+ # A dialog with an owner appears as a sheet on macos.
5
+ #
6
+ # Three things answer a selection: the block given to open_file and
7
+ # friends, the on_file hook of the dialog, and the on_file hook of the
8
+ # owner window, in that order. A hook does not cost the block its turn,
9
+ # even though it hides the method the block used to be called from.
10
+ #
11
+ # 1. press 1 and pick a file
12
+ # -> 'block', 'dialog on_file' and 'window on_file_open' all log
13
+ # the path, in that order, the hook here costing nothing
14
+ # 2. press 1 and cancel
15
+ # -> 'dialog on_cancel' logs, and no path is logged
16
+ # 3. press 2 and pick more than one file
17
+ # -> every path is logged
18
+ # 4. press 3
19
+ # -> only directories can be chosen, and files are greyed out
20
+ # 5. press 4, type a name and save
21
+ # -> the path is logged, and 'window on_file_save' follows it
22
+ # 6. press 5
23
+ # -> the dialog opens in the home directory
24
+ # 7. press 6
25
+ # -> the name field is already filled with untitled.png
26
+ # 8. press 7
27
+ # -> the dialog is a window of its own, not a sheet, and the log
28
+ # shows no 'window on_file_open': it has no owner
29
+ # 9. press 1 and look at the file list
30
+ # -> only .png and .jpg files can be chosen, and folders stay open
31
+ # 10. press 1 where a file is named in capitals, IMAGE.PNG say
32
+ # -> it can be chosen too, the extension is matched either case
33
+
34
+ %w[xot rays reflex]
35
+ .map {|s| File.expand_path "../../../#{s}/lib", __dir__}
36
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
37
+
38
+ require 'reflex'
39
+
40
+
41
+ KEYS = [
42
+ '1: open_file 2: open_files',
43
+ '3: open_directory 4: save_file',
44
+ '5: open_files in ~ 6: save_file named untitled.png',
45
+ '7: open_files with no owner'
46
+ ]
47
+
48
+ LOG = []
49
+
50
+ def log(text)
51
+ LOG.unshift text
52
+ puts text
53
+ end
54
+
55
+
56
+ Reflex.start do
57
+ win = Reflex::Window.new title: 'FileDialog', frame: [100, 100, 560, 400]
58
+
59
+ # the event reaches the owner window too, after the dialog itself
60
+ win.on(:file_open) {|e| log "window on_file_open: #{e.paths}"}
61
+ win.on(:file_save) {|e| log "window on_file_save: #{e.path}"}
62
+
63
+ make = -> (title, owner: win) {
64
+ dialog = Reflex::FileDialog.new owner: owner, title: title do
65
+ extensions %w[png jpg]
66
+ end
67
+ # no super, and the block still runs: it is called from the wrapper,
68
+ # before this hook, rather than from the method this hook hides
69
+ dialog.on(:file) {|e| log "dialog on_file: #{e.action} #{e.paths}"}
70
+ dialog.on(:cancel) {|e| log 'dialog on_cancel'}
71
+ dialog
72
+ }
73
+
74
+ win.on(:key_down) do |e|
75
+ case e.chars
76
+ when '1' then make['Open a file'] .open_file {|path| log "block: #{path}"}
77
+ when '2' then make['Open files'] .open_files {|paths| log "block: #{paths}"}
78
+ when '3' then make['Open a directory'].open_directory {|path| log "block: #{path}"}
79
+ when '4' then make['Save a file'] .save_file {|path| log "block: #{path}"}
80
+ when '5' then make['Open files in ~'] .open_files('~') {|paths| log "block: #{paths}"}
81
+ when '6'
82
+ make['Save as'].save_file(name: 'untitled.png') {|path| log "block: #{path}"}
83
+ when '7'
84
+ make['No owner', owner: nil].open_files {|paths| log "block: #{paths}"}
85
+ else next
86
+ end
87
+ log "-- #{e.chars}"
88
+ end
89
+
90
+ win.on(:draw) do |e|
91
+ p = e.painter
92
+ p.background 0.1, 0.1, 0.15
93
+ p.fill 1
94
+ KEYS.each_with_index {|text, i| p.text text, 10, 10 + i * 18}
95
+ p.fill 0.5, 1, 0.5
96
+ LOG.first(14).each_with_index {|text, i| p.text text, 10, 100 + i * 18}
97
+ end
98
+
99
+ win.on(:update) {|e| win.redraw}
100
+
101
+ win.show
102
+ end