reflexion 0.3.4 → 0.3.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 92e1368f44fafa2ab8966bfec25f93af411b68a72eaf3f127bc6b95b97ba2fed
4
- data.tar.gz: 6efaa218301242d6c4fea24062f9fd9d74f405e46a347eac1b231d6fe08a16c1
3
+ metadata.gz: 0d6d1b028ab65bd561deb01b032b0fc7ddb1d633f493fecf9c9407bc8321f568
4
+ data.tar.gz: 32f3da5338572a0ce044d64e7c8474f73dbb3d99d0813f86aa1ca0194a18e4c3
5
5
  SHA512:
6
- metadata.gz: 3886ea311fccd85da0e7e3868771d61d32f10fd091612bf306df041dbbe2cf30490e3e6a5fa9cdc049e6cf41f3b8e090ea7af950753dff81db95548ffcb7df99
7
- data.tar.gz: 1704f273a82ebd9b8272e7358c6936b81171a969ad1a6f085ad641dc0f833dfeb5b97d4f5cf71e80be8d00d9b7bdbc5cb4bdba9d2c9e1228ebb6b995e7903444
6
+ metadata.gz: 6f44899a6ae5eaadd962a62c18c1d1dc1d70c5bdf5f09a71fa68ac829c318853a492e390188e7100391255a53b9f84276028d7038b31a5cd5b4b7b57a38bb715
7
+ data.tar.gz: 3dec2aa1ec50884e538b19b46066737cf6cdd3943fba0c4918e388d7be64d75dd4bab0dbd4c7a27989c197428ebcc0e6c093f76697dc1d83ef8ff4385c9b9d9d
@@ -256,6 +256,26 @@ VALUE get_key(VALUE self)
256
256
  CASE(LAUNCH_APP1): SYMBOL1(launch_app1);
257
257
  CASE(LAUNCH_APP2): SYMBOL1(launch_app2);
258
258
 
259
+ CASE(GAMEPAD_LEFT): SYMBOL1(gamepad_left);
260
+ CASE(GAMEPAD_RIGHT): SYMBOL1(gamepad_right);
261
+ CASE(GAMEPAD_UP): SYMBOL1(gamepad_up);
262
+ CASE(GAMEPAD_DOWN): SYMBOL1(gamepad_down);
263
+ CASE(GAMEPAD_A): SYMBOL1(gamepad_a);
264
+ CASE(GAMEPAD_B): SYMBOL1(gamepad_b);
265
+ CASE(GAMEPAD_X): SYMBOL1(gamepad_x);
266
+ CASE(GAMEPAD_Y): SYMBOL1(gamepad_y);
267
+ CASE(GAMEPAD_TRIGGER_LEFT): SYMBOL1(gamepad_trigger_left);
268
+ CASE(GAMEPAD_TRIGGER_RIGHT): SYMBOL1(gamepad_trigger_right);
269
+ CASE(GAMEPAD_SHOULDER_LEFT): SYMBOL1(gamepad_shoulder_left);
270
+ CASE(GAMEPAD_SHOULDER_RIGHT): SYMBOL1(gamepad_shoulder_right);
271
+ CASE(GAMEPAD_THUMB_LEFT): SYMBOL1(gamepad_thumb_left);
272
+ CASE(GAMEPAD_THUMB_RIGHT): SYMBOL1(gamepad_thumb_right);
273
+ CASE(GAMEPAD_HOME): SYMBOL1(gamepad_home);
274
+ CASE(GAMEPAD_MENU): SYMBOL1(gamepad_menu);
275
+ CASE(GAMEPAD_OPTION): SYMBOL1(gamepad_option);
276
+ CASE(GAMEPAD_START): SYMBOL1(gamepad_start);
277
+ CASE(GAMEPAD_SELECT): SYMBOL1(gamepad_select);
278
+
259
279
  #undef CASE
260
280
  #undef SYMBOL1
261
281
  #undef SYMBOL2
@@ -249,6 +249,29 @@ Init_reflex ()
249
249
  DEFINE_CONST(KEY_LAUNCH_APP1);
250
250
  DEFINE_CONST(KEY_LAUNCH_APP2);
251
251
 
252
+ DEFINE_CONST(KEY_GAMEPAD_LEFT);
253
+ DEFINE_CONST(KEY_GAMEPAD_RIGHT);
254
+ DEFINE_CONST(KEY_GAMEPAD_UP);
255
+ DEFINE_CONST(KEY_GAMEPAD_DOWN);
256
+
257
+ DEFINE_CONST(KEY_GAMEPAD_A);
258
+ DEFINE_CONST(KEY_GAMEPAD_B);
259
+ DEFINE_CONST(KEY_GAMEPAD_X);
260
+ DEFINE_CONST(KEY_GAMEPAD_Y);
261
+
262
+ DEFINE_CONST(KEY_GAMEPAD_SHOULDER_LEFT);
263
+ DEFINE_CONST(KEY_GAMEPAD_SHOULDER_RIGHT);
264
+ DEFINE_CONST(KEY_GAMEPAD_TRIGGER_LEFT);
265
+ DEFINE_CONST(KEY_GAMEPAD_TRIGGER_RIGHT);
266
+ DEFINE_CONST(KEY_GAMEPAD_THUMB_LEFT);
267
+ DEFINE_CONST(KEY_GAMEPAD_THUMB_RIGHT);
268
+
269
+ DEFINE_CONST(KEY_GAMEPAD_HOME);
270
+ DEFINE_CONST(KEY_GAMEPAD_MENU);
271
+ DEFINE_CONST(KEY_GAMEPAD_OPTION);
272
+ DEFINE_CONST(KEY_GAMEPAD_START);
273
+ DEFINE_CONST(KEY_GAMEPAD_SELECT);
274
+
252
275
  DEFINE_CONST(MOD_SHIFT);
253
276
  DEFINE_CONST(MOD_CONTROL);
254
277
  DEFINE_CONST(MOD_ALT);
@@ -0,0 +1,12 @@
1
+ ## Pull Requests Not Accepted 🚫
2
+
3
+ Thank you for your interest in contributing!
4
+ However, this repository does not accept pull requests directly.
5
+
6
+ ### Where to Contribute?
7
+
8
+ Please submit your changes to the [xord/all](https://github.com/xord/all) monorepo, which serves as the primary repository for all our main libraries.
9
+
10
+ For more details, please refer to our [contribution guidelines](../CONTRIBUTING.md).
11
+
12
+ Thanks for your understanding! 🙌
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,7 @@
1
+ # Contribution Guide
2
+
3
+ Thank you for your interest in contributing!
4
+ However, this repository does not accept pull requests.
5
+ Instead, please submit your changes to the [xord/all](https://github.com/xord/all) monorepo, which serves as the primary repository for all our main libraries.
6
+
7
+ For any questions, feel free to open an issue.
data/ChangeLog.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # reflex ChangeLog
2
2
 
3
3
 
4
+ ## [v0.3.5] - 2025-03-24
5
+
6
+ - Add PULL_REQUEST_TEMPLATE.md
7
+ - Add CONTRIBUTING.md
8
+ - Handle gamepad events on macOS, iOS, and Win32
9
+ - Call activate/deactivate event for window on Win32
10
+ - Define constants for gamepad keys
11
+ - Delay removing child to avoid breaking child list looped on View_update_tree()
12
+
13
+ - Fix a crash caused by add/remove_child()
14
+
15
+
4
16
  ## [v0.3.4] - 2025-03-07
5
17
 
6
18
  - Captured key and pointer events can be blocked before being sent to the window and views
data/README.md CHANGED
@@ -1,4 +1,48 @@
1
-
2
1
  # Reflex - A Graphical User Interface Tool Kit for Ruby.
3
2
 
4
- by xordog@gmail.com
3
+ ![License](https://img.shields.io/github/license/xord/reflex)
4
+ ![Build Status](https://github.com/xord/reflex/actions/workflows/test.yml/badge.svg)
5
+ ![Gem Version](https://badge.fury.io/rb/reflexion.svg)
6
+
7
+ ## ⚠️ Notice
8
+
9
+ This repository is a read-only mirror of our monorepo.
10
+ We do not accept pull requests or direct contributions here.
11
+
12
+ ### 🔄 Where to Contribute?
13
+
14
+ All development happens in our [xord/all](https://github.com/xord/all) monorepo, which contains all our main libraries.
15
+ If you'd like to contribute, please submit your changes there.
16
+
17
+ For more details, check out our [Contribution Guidelines](./CONTRIBUTING.md).
18
+
19
+ Thanks for your support! 🙌
20
+
21
+ ## 🚀 About
22
+
23
+ **Reflex** is a graphical user interface (GUI) toolkit for Ruby.
24
+
25
+ It is designed to help developers create intuitive and responsive user interfaces for their Ruby applications.
26
+ **Reflex** provides core components such as Window and View, making it easier to build the foundation of your GUI applications.
27
+
28
+ ## 📦 Installation
29
+
30
+ Add this line to your Gemfile:
31
+ ```ruby
32
+ $ gem 'reflexion'
33
+ ```
34
+
35
+ Then, install gem:
36
+ ```bash
37
+ $ bundle install
38
+ ```
39
+
40
+ Or install it directly:
41
+ ```bash
42
+ $ gem install reflexion
43
+ ```
44
+
45
+ ## 📜 License
46
+
47
+ **Reflex** is licensed under the MIT License.
48
+ See the [LICENSE](./LICENSE) file for details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.4
1
+ 0.3.5
@@ -13,9 +13,9 @@ require 'reflex/extension'
13
13
  Xot::ExtConf.new Xot, Rucy, Rays, Reflex do
14
14
  setup do
15
15
  headers << 'ruby.h'
16
- libs.unshift 'gdi32', 'winmm', 'opengl32', 'glew32' if win32?
17
- frameworks << 'Cocoa' if osx?
18
- $LDFLAGS << ' -Wl,--out-implib=native.dll.a' if mingw? || cygwin?
16
+ libs.unshift 'gdi32', 'winmm', 'opengl32', 'glew32', 'xinput' if win32?
17
+ frameworks << 'Cocoa' << 'GameController' if osx?
18
+ $LDFLAGS << ' -Wl,--out-implib=native.dll.a' if mingw? || cygwin?
19
19
 
20
20
  unless osx?
21
21
  lib_dirs << Rays::Extension.ext_dir
@@ -260,6 +260,26 @@ RUCY_DEF0(get_key)
260
260
  CASE(LAUNCH_APP1): SYMBOL1(launch_app1);
261
261
  CASE(LAUNCH_APP2): SYMBOL1(launch_app2);
262
262
 
263
+ CASE(GAMEPAD_LEFT): SYMBOL1(gamepad_left);
264
+ CASE(GAMEPAD_RIGHT): SYMBOL1(gamepad_right);
265
+ CASE(GAMEPAD_UP): SYMBOL1(gamepad_up);
266
+ CASE(GAMEPAD_DOWN): SYMBOL1(gamepad_down);
267
+ CASE(GAMEPAD_A): SYMBOL1(gamepad_a);
268
+ CASE(GAMEPAD_B): SYMBOL1(gamepad_b);
269
+ CASE(GAMEPAD_X): SYMBOL1(gamepad_x);
270
+ CASE(GAMEPAD_Y): SYMBOL1(gamepad_y);
271
+ CASE(GAMEPAD_TRIGGER_LEFT): SYMBOL1(gamepad_trigger_left);
272
+ CASE(GAMEPAD_TRIGGER_RIGHT): SYMBOL1(gamepad_trigger_right);
273
+ CASE(GAMEPAD_SHOULDER_LEFT): SYMBOL1(gamepad_shoulder_left);
274
+ CASE(GAMEPAD_SHOULDER_RIGHT): SYMBOL1(gamepad_shoulder_right);
275
+ CASE(GAMEPAD_THUMB_LEFT): SYMBOL1(gamepad_thumb_left);
276
+ CASE(GAMEPAD_THUMB_RIGHT): SYMBOL1(gamepad_thumb_right);
277
+ CASE(GAMEPAD_HOME): SYMBOL1(gamepad_home);
278
+ CASE(GAMEPAD_MENU): SYMBOL1(gamepad_menu);
279
+ CASE(GAMEPAD_OPTION): SYMBOL1(gamepad_option);
280
+ CASE(GAMEPAD_START): SYMBOL1(gamepad_start);
281
+ CASE(GAMEPAD_SELECT): SYMBOL1(gamepad_select);
282
+
263
283
  #undef CASE
264
284
  #undef SYMBOL1
265
285
  #undef SYMBOL2
@@ -251,6 +251,29 @@ Init_reflex ()
251
251
  DEFINE_CONST(KEY_LAUNCH_APP1);
252
252
  DEFINE_CONST(KEY_LAUNCH_APP2);
253
253
 
254
+ DEFINE_CONST(KEY_GAMEPAD_LEFT);
255
+ DEFINE_CONST(KEY_GAMEPAD_RIGHT);
256
+ DEFINE_CONST(KEY_GAMEPAD_UP);
257
+ DEFINE_CONST(KEY_GAMEPAD_DOWN);
258
+
259
+ DEFINE_CONST(KEY_GAMEPAD_A);
260
+ DEFINE_CONST(KEY_GAMEPAD_B);
261
+ DEFINE_CONST(KEY_GAMEPAD_X);
262
+ DEFINE_CONST(KEY_GAMEPAD_Y);
263
+
264
+ DEFINE_CONST(KEY_GAMEPAD_SHOULDER_LEFT);
265
+ DEFINE_CONST(KEY_GAMEPAD_SHOULDER_RIGHT);
266
+ DEFINE_CONST(KEY_GAMEPAD_TRIGGER_LEFT);
267
+ DEFINE_CONST(KEY_GAMEPAD_TRIGGER_RIGHT);
268
+ DEFINE_CONST(KEY_GAMEPAD_THUMB_LEFT);
269
+ DEFINE_CONST(KEY_GAMEPAD_THUMB_RIGHT);
270
+
271
+ DEFINE_CONST(KEY_GAMEPAD_HOME);
272
+ DEFINE_CONST(KEY_GAMEPAD_MENU);
273
+ DEFINE_CONST(KEY_GAMEPAD_OPTION);
274
+ DEFINE_CONST(KEY_GAMEPAD_START);
275
+ DEFINE_CONST(KEY_GAMEPAD_SELECT);
276
+
254
277
  DEFINE_CONST(MOD_SHIFT);
255
278
  DEFINE_CONST(MOD_CONTROL);
256
279
  DEFINE_CONST(MOD_ALT);
@@ -277,6 +277,29 @@ namespace Reflex
277
277
 
278
278
  #undef NATIVE_VK
279
279
 
280
+ KEY_GAMEPAD_LEFT = 0x100,
281
+ KEY_GAMEPAD_RIGHT = 0x101,
282
+ KEY_GAMEPAD_UP = 0x102,
283
+ KEY_GAMEPAD_DOWN = 0x103,
284
+
285
+ KEY_GAMEPAD_A = 0x104,
286
+ KEY_GAMEPAD_B = 0x105,
287
+ KEY_GAMEPAD_X = 0x106,
288
+ KEY_GAMEPAD_Y = 0x107,
289
+
290
+ KEY_GAMEPAD_SHOULDER_LEFT = 0x108,
291
+ KEY_GAMEPAD_SHOULDER_RIGHT = 0x109,
292
+ KEY_GAMEPAD_TRIGGER_LEFT = 0x10A,
293
+ KEY_GAMEPAD_TRIGGER_RIGHT = 0x10B,
294
+ KEY_GAMEPAD_THUMB_LEFT = 0x10C,
295
+ KEY_GAMEPAD_THUMB_RIGHT = 0x10D,
296
+
297
+ KEY_GAMEPAD_HOME = 0x10E,
298
+ KEY_GAMEPAD_MENU = 0x10F,
299
+ KEY_GAMEPAD_OPTION = 0x110,
300
+ KEY_GAMEPAD_START = 0x111,
301
+ KEY_GAMEPAD_SELECT = 0x112,
302
+
280
303
  };// KeyCode
281
304
 
282
305
 
data/reflex.gemspec CHANGED
@@ -25,9 +25,9 @@ Gem::Specification.new do |s|
25
25
  s.platform = Gem::Platform::RUBY
26
26
  s.required_ruby_version = '>= 3.0.0'
27
27
 
28
- s.add_dependency 'xot', '~> 0.3.4', '>= 0.3.4'
29
- s.add_dependency 'rucy', '~> 0.3.4', '>= 0.3.4'
30
- s.add_dependency 'rays', '~> 0.3.4', '>= 0.3.4'
28
+ s.add_dependency 'xot', '~> 0.3.5', '>= 0.3.5'
29
+ s.add_dependency 'rucy', '~> 0.3.5', '>= 0.3.5'
30
+ s.add_dependency 'rays', '~> 0.3.5', '>= 0.3.5'
31
31
 
32
32
  s.files = `git ls-files`.split $/
33
33
  s.executables = s.files.grep(%r{^bin/}) {|f| File.basename f}
data/src/ios/event.h CHANGED
@@ -22,6 +22,11 @@ namespace Reflex
22
22
  };// NativePointerEvent
23
23
 
24
24
 
25
+ void init_game_controllers ();
26
+
27
+ void fin_game_controllers ();
28
+
29
+
25
30
  }// Reflex
26
31
 
27
32
 
data/src/ios/event.mm CHANGED
@@ -4,7 +4,8 @@
4
4
 
5
5
  #include <assert.h>
6
6
  #include <algorithm>
7
- #include "../pointer.h"
7
+ #import <GameController/GameController.h>
8
+ #include "window.h"
8
9
 
9
10
 
10
11
  namespace Reflex
@@ -98,4 +99,86 @@ namespace Reflex
98
99
  }
99
100
 
100
101
 
102
+ static void
103
+ call_gamepad_event (int code, bool pressed)
104
+ {
105
+ Window* win = Window_get_active();
106
+ if (!win) return;
107
+
108
+ auto action = pressed ? KeyEvent::DOWN : KeyEvent::UP;
109
+ KeyEvent e(action, NULL, code, 0, 0);
110
+ Window_call_key_event(win, &e);
111
+ }
112
+
113
+ static void
114
+ handle_gamepad_event (GCControllerButtonInput* input, int code)
115
+ {
116
+ [input setPressedChangedHandler:
117
+ ^(GCControllerButtonInput* button, float value, BOOL pressed) {
118
+ call_gamepad_event(code, pressed);
119
+ }];
120
+ }
121
+
122
+ static void
123
+ handle_gamepad_events (GCController* controller)
124
+ {
125
+ GCExtendedGamepad* gamepad = controller.extendedGamepad;
126
+ if (!gamepad) return;
127
+
128
+ handle_gamepad_event(gamepad.dpad.left, KEY_GAMEPAD_LEFT);
129
+ handle_gamepad_event(gamepad.dpad.right, KEY_GAMEPAD_RIGHT);
130
+ handle_gamepad_event(gamepad.dpad.up, KEY_GAMEPAD_UP);
131
+ handle_gamepad_event(gamepad.dpad.down, KEY_GAMEPAD_DOWN);
132
+
133
+ handle_gamepad_event(gamepad.buttonA, KEY_GAMEPAD_A);
134
+ handle_gamepad_event(gamepad.buttonB, KEY_GAMEPAD_B);
135
+ handle_gamepad_event(gamepad.buttonX, KEY_GAMEPAD_X);
136
+ handle_gamepad_event(gamepad.buttonY, KEY_GAMEPAD_Y);
137
+
138
+ handle_gamepad_event(gamepad. leftShoulder, KEY_GAMEPAD_SHOULDER_LEFT);
139
+ handle_gamepad_event(gamepad.rightShoulder, KEY_GAMEPAD_SHOULDER_RIGHT);
140
+ handle_gamepad_event(gamepad. leftTrigger, KEY_GAMEPAD_TRIGGER_LEFT);
141
+ handle_gamepad_event(gamepad.rightTrigger, KEY_GAMEPAD_TRIGGER_RIGHT);
142
+
143
+ if (@available(iOS 12.1, *))
144
+ {
145
+ handle_gamepad_event(gamepad. leftThumbstickButton, KEY_GAMEPAD_THUMB_LEFT);
146
+ handle_gamepad_event(gamepad.rightThumbstickButton, KEY_GAMEPAD_THUMB_RIGHT);
147
+ }
148
+
149
+ if (@available(iOS 13.0, *))
150
+ {
151
+ handle_gamepad_event(gamepad.buttonMenu, KEY_GAMEPAD_MENU);
152
+ handle_gamepad_event(gamepad.buttonOptions, KEY_GAMEPAD_OPTION);
153
+ }
154
+
155
+ if (@available(iOS 14.0, *))
156
+ handle_gamepad_event(gamepad.buttonHome, KEY_GAMEPAD_HOME);
157
+ }
158
+
159
+ static id game_controllers_observer = nil;
160
+
161
+ void
162
+ init_game_controllers ()
163
+ {
164
+ for (GCController* c in GCController.controllers)
165
+ handle_gamepad_events(c);
166
+
167
+ game_controllers_observer = [NSNotificationCenter.defaultCenter
168
+ addObserverForName: GCControllerDidConnectNotification
169
+ object: nil
170
+ queue: NSOperationQueue.mainQueue
171
+ usingBlock: ^(NSNotification* n) {handle_gamepad_events(n.object);}];
172
+ }
173
+
174
+ void
175
+ fin_game_controllers ()
176
+ {
177
+ if (!game_controllers_observer) return;
178
+
179
+ [NSNotificationCenter.defaultCenter
180
+ removeObserver: game_controllers_observer];
181
+ }
182
+
183
+
101
184
  };// Reflex
data/src/ios/reflex.mm CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
 
5
5
  #include "reflex/exception.h"
6
+ #include "event.h"
6
7
 
7
8
 
8
9
  namespace Reflex
@@ -24,6 +25,8 @@ namespace Reflex
24
25
  reflex_error(__FILE__, __LINE__, "already initialized.");
25
26
 
26
27
  global::pool = [[NSAutoreleasePool alloc] init];
28
+
29
+ init_game_controllers();
27
30
  }
28
31
 
29
32
  void
@@ -32,6 +35,8 @@ namespace Reflex
32
35
  if (!global::pool)
33
36
  reflex_error(__FILE__, __LINE__, "not initialized.");
34
37
 
38
+ fin_game_controllers();
39
+
35
40
  [global::pool release];
36
41
  global::pool = nil;
37
42
  }
data/src/osx/event.h CHANGED
@@ -53,6 +53,11 @@ namespace Reflex
53
53
  };// NativeWheelEvent
54
54
 
55
55
 
56
+ void init_game_controllers ();
57
+
58
+ void fin_game_controllers ();
59
+
60
+
56
61
  }// Reflex
57
62
 
58
63
 
data/src/osx/event.mm CHANGED
@@ -5,6 +5,9 @@
5
5
  #include <assert.h>
6
6
  #include <Carbon/Carbon.h>
7
7
  #import <Cocoa/Cocoa.h>
8
+ #import <GameController/GameController.h>
9
+ #include "reflex/debug.h"
10
+ #include "window.h"
8
11
 
9
12
 
10
13
  namespace Reflex
@@ -12,9 +15,9 @@ namespace Reflex
12
15
 
13
16
 
14
17
  static uint
15
- get_modifiers (const NSEvent* e)
18
+ get_modifiers (const NSEvent* event = nil)
16
19
  {
17
- NSUInteger flags = e.modifierFlags;
20
+ NSUInteger flags = event ? event.modifierFlags : NSEvent.modifierFlags;
18
21
  return
19
22
  (flags & NSAlphaShiftKeyMask) ? MOD_CAPS : 0 |
20
23
  (flags & NSShiftKeyMask) ? MOD_SHIFT : 0 |
@@ -161,4 +164,86 @@ namespace Reflex
161
164
  }
162
165
 
163
166
 
167
+ static void
168
+ call_gamepad_event (int code, bool pressed)
169
+ {
170
+ Window* win = Window_get_active();
171
+ if (!win) return;
172
+
173
+ auto action = pressed ? KeyEvent::DOWN : KeyEvent::UP;
174
+ KeyEvent e(action, NULL, code, get_modifiers(), 0);
175
+ Window_call_key_event(win, &e);
176
+ }
177
+
178
+ static void
179
+ handle_gamepad_event (GCControllerButtonInput* input, int code)
180
+ {
181
+ [input setPressedChangedHandler:
182
+ ^(GCControllerButtonInput* button, float value, BOOL pressed) {
183
+ call_gamepad_event(code, pressed);
184
+ }];
185
+ }
186
+
187
+ static void
188
+ handle_gamepad_events (GCController* controller)
189
+ {
190
+ GCExtendedGamepad* gamepad = controller.extendedGamepad;
191
+ if (!gamepad) return;
192
+
193
+ handle_gamepad_event(gamepad.dpad.left, KEY_GAMEPAD_LEFT);
194
+ handle_gamepad_event(gamepad.dpad.right, KEY_GAMEPAD_RIGHT);
195
+ handle_gamepad_event(gamepad.dpad.up, KEY_GAMEPAD_UP);
196
+ handle_gamepad_event(gamepad.dpad.down, KEY_GAMEPAD_DOWN);
197
+
198
+ handle_gamepad_event(gamepad.buttonA, KEY_GAMEPAD_A);
199
+ handle_gamepad_event(gamepad.buttonB, KEY_GAMEPAD_B);
200
+ handle_gamepad_event(gamepad.buttonX, KEY_GAMEPAD_X);
201
+ handle_gamepad_event(gamepad.buttonY, KEY_GAMEPAD_Y);
202
+
203
+ handle_gamepad_event(gamepad. leftShoulder, KEY_GAMEPAD_SHOULDER_LEFT);
204
+ handle_gamepad_event(gamepad.rightShoulder, KEY_GAMEPAD_SHOULDER_RIGHT);
205
+ handle_gamepad_event(gamepad. leftTrigger, KEY_GAMEPAD_TRIGGER_LEFT);
206
+ handle_gamepad_event(gamepad.rightTrigger, KEY_GAMEPAD_TRIGGER_RIGHT);
207
+
208
+ if (@available(macOS 10.14.1, *))
209
+ {
210
+ handle_gamepad_event(gamepad. leftThumbstickButton, KEY_GAMEPAD_THUMB_LEFT);
211
+ handle_gamepad_event(gamepad.rightThumbstickButton, KEY_GAMEPAD_THUMB_RIGHT);
212
+ }
213
+
214
+ if (@available(macOS 10.15, *))
215
+ {
216
+ handle_gamepad_event(gamepad.buttonMenu, KEY_GAMEPAD_MENU);
217
+ handle_gamepad_event(gamepad.buttonOptions, KEY_GAMEPAD_OPTION);
218
+ }
219
+
220
+ if (@available(macOS 11.0, *))
221
+ handle_gamepad_event(gamepad.buttonHome, KEY_GAMEPAD_HOME);
222
+ }
223
+
224
+ static id game_controllers_observer = nil;
225
+
226
+ void
227
+ init_game_controllers ()
228
+ {
229
+ for (GCController* c in GCController.controllers)
230
+ handle_gamepad_events(c);
231
+
232
+ game_controllers_observer = [NSNotificationCenter.defaultCenter
233
+ addObserverForName: GCControllerDidConnectNotification
234
+ object: nil
235
+ queue: NSOperationQueue.mainQueue
236
+ usingBlock: ^(NSNotification* n) {handle_gamepad_events(n.object);}];
237
+ }
238
+
239
+ void
240
+ fin_game_controllers ()
241
+ {
242
+ if (!game_controllers_observer) return;
243
+
244
+ [NSNotificationCenter.defaultCenter
245
+ removeObserver: game_controllers_observer];
246
+ }
247
+
248
+
164
249
  };// Reflex
data/src/osx/reflex.mm CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  #import <Cocoa/Cocoa.h>
6
6
  #include "reflex/exception.h"
7
+ #include "event.h"
7
8
 
8
9
 
9
10
  namespace Reflex
@@ -25,6 +26,8 @@ namespace Reflex
25
26
  reflex_error(__FILE__, __LINE__, "already initialized.");
26
27
 
27
28
  global::pool = [[NSAutoreleasePool alloc] init];
29
+
30
+ init_game_controllers();
28
31
  }
29
32
 
30
33
  void
@@ -33,6 +36,8 @@ namespace Reflex
33
36
  if (!global::pool)
34
37
  reflex_error(__FILE__, __LINE__, "not initialized.");
35
38
 
39
+ fin_game_controllers();
40
+
36
41
  [global::pool release];
37
42
  global::pool = nil;
38
43
  }
data/src/view.cpp CHANGED
@@ -39,23 +39,29 @@ namespace Reflex
39
39
  enum Flag
40
40
  {
41
41
 
42
- REDRAW = Xot::bit(1, FLAG_LAST),
42
+ UPDATING = Xot::bit(1, FLAG_LAST),
43
43
 
44
- APPLY_STYLE = Xot::bit(2, FLAG_LAST),
44
+ NO_SHAPE = Xot::bit(2, FLAG_LAST),
45
45
 
46
- UPDATE_STYLE = Xot::bit(3, FLAG_LAST),
46
+ HAS_VARIABLE_LENGTHS = Xot::bit(3, FLAG_LAST),
47
47
 
48
- UPDATE_SHAPES = Xot::bit(4, FLAG_LAST),
48
+ HAS_CHILDREN_TO_REMOVE = Xot::bit(4, FLAG_LAST),
49
49
 
50
- UPDATE_LAYOUT = Xot::bit(5, FLAG_LAST),
50
+ REMOVE_FROM_PARENT = Xot::bit(5, FLAG_LAST),
51
51
 
52
- SORT_CHILDREN = Xot::bit(6, FLAG_LAST),
52
+ REDRAW = Xot::bit(6, FLAG_LAST),
53
53
 
54
- FIT_TO_CONTENT = Xot::bit(7, FLAG_LAST),
54
+ APPLY_STYLE = Xot::bit(7, FLAG_LAST),
55
55
 
56
- HAS_VARIABLE_LENGTHS = Xot::bit(8, FLAG_LAST),
56
+ UPDATE_STYLE = Xot::bit(8, FLAG_LAST),
57
57
 
58
- NO_SHAPE = Xot::bit(9, FLAG_LAST),
58
+ UPDATE_SHAPES = Xot::bit(9, FLAG_LAST),
59
+
60
+ UPDATE_LAYOUT = Xot::bit(10, FLAG_LAST),
61
+
62
+ SORT_CHILDREN = Xot::bit(11, FLAG_LAST),
63
+
64
+ FIT_TO_CONTENT = Xot::bit(12, FLAG_LAST),
59
65
 
60
66
  };// Flag
61
67
 
@@ -1045,6 +1051,20 @@ namespace Reflex
1045
1051
  }
1046
1052
  }
1047
1053
 
1054
+ static void
1055
+ remove_children (View* view, View::ChildList* children)
1056
+ {
1057
+ assert(children);
1058
+
1059
+ int size = (int) children->size();
1060
+ for (int i = size - 1; i >= 0; --i)
1061
+ {
1062
+ View* child = (*children)[i].get();
1063
+ if (child->self->check_and_remove_flag(View::Data::REMOVE_FROM_PARENT))
1064
+ view->remove_child(child);
1065
+ }
1066
+ }
1067
+
1048
1068
  void
1049
1069
  View_update_tree (View* view, const UpdateEvent& event)
1050
1070
  {
@@ -1053,13 +1073,16 @@ namespace Reflex
1053
1073
 
1054
1074
  View::Data* self = view->self.get();
1055
1075
 
1076
+ self->add_flag(View::Data::UPDATING);
1077
+
1056
1078
  fire_timers(view, event.now());
1057
1079
 
1058
1080
  View::ChildList* children = self->children();
1059
1081
  if (children)
1060
1082
  {
1061
- for (auto& child : *children)
1062
- View_update_tree(child.get(), event);
1083
+ size_t size = children->size();
1084
+ for (size_t i = 0; i < size; ++i)
1085
+ View_update_tree((*children)[i].get(), event);
1063
1086
  }
1064
1087
 
1065
1088
  update_view_shapes(view);
@@ -1081,6 +1104,11 @@ namespace Reflex
1081
1104
 
1082
1105
  if (self->check_and_remove_flag(View::Data::FIT_TO_CONTENT))
1083
1106
  fit_view_to_content(view);
1107
+
1108
+ self->remove_flag(View::Data::UPDATING);
1109
+
1110
+ if (self->check_and_remove_flag(View::Data::HAS_CHILDREN_TO_REMOVE))
1111
+ remove_children(view, children);
1084
1112
  }
1085
1113
 
1086
1114
  static bool
@@ -1792,13 +1820,19 @@ namespace Reflex
1792
1820
  else if (found != belong)
1793
1821
  invalid_state_error(__FILE__, __LINE__);
1794
1822
 
1795
- set_parent(child, NULL);
1796
-
1797
- erase_child_from_children(this, child);
1798
-
1799
- self->sort_children();
1800
-
1801
- update_view_layout(this);
1823
+ if (self->has_flag(Data::UPDATING))
1824
+ {
1825
+ // delay removing child to avoid breaking child list looped on View_update_tree()
1826
+ self->add_flag(Data::HAS_CHILDREN_TO_REMOVE);
1827
+ child->self->add_flag(Data::REMOVE_FROM_PARENT);
1828
+ }
1829
+ else
1830
+ {
1831
+ set_parent(child, NULL);
1832
+ erase_child_from_children(this, child);
1833
+ self->sort_children();
1834
+ update_view_layout(this);
1835
+ }
1802
1836
  }
1803
1837
 
1804
1838
  void
data/src/win32/event.cpp CHANGED
@@ -1,9 +1,11 @@
1
1
  #include "event.h"
2
2
 
3
3
 
4
+ #include <xinput.h>
4
5
  #include <xot/time.h>
5
6
  #include "reflex/exception.h"
6
- #include "../pointer.h"
7
+ #include "reflex/debug.h"
8
+ #include "window.h"
7
9
 
8
10
 
9
11
  namespace Reflex
@@ -216,4 +218,86 @@ namespace Reflex
216
218
  }
217
219
 
218
220
 
221
+ static void
222
+ call_gamepad_event (Window* win, int code, bool pressed)
223
+ {
224
+ auto action = pressed ? KeyEvent::DOWN : KeyEvent::UP;
225
+ KeyEvent e(action, NULL, code, get_modifiers(), 0);
226
+ Window_call_key_event(win, &e);
227
+ }
228
+
229
+ static void
230
+ handle_gamepad_button_event (
231
+ Window* win, const XINPUT_STATE& state, const XINPUT_STATE& prev_state,
232
+ WORD mask, int code)
233
+ {
234
+ WORD pressed = state.Gamepad.wButtons & mask;
235
+ WORD prev = prev_state.Gamepad.wButtons & mask;
236
+ if (pressed == prev) return;
237
+
238
+ call_gamepad_event(win, code, pressed);
239
+ }
240
+
241
+ static void
242
+ handle_gamepad_trigger_event (Window* win, BYTE value, BYTE prev_value, int code)
243
+ {
244
+ WORD pressed = value > XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
245
+ WORD prev = prev_value > XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
246
+ if (pressed == prev) return;
247
+
248
+ call_gamepad_event(win, code, pressed);
249
+ }
250
+
251
+ static void
252
+ handle_gamepad_events (const XINPUT_STATE& state, const XINPUT_STATE& prev_state)
253
+ {
254
+ Window* win = Window_get_active();
255
+ if (!win) return;
256
+
257
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_DPAD_LEFT, KEY_GAMEPAD_LEFT);
258
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_DPAD_RIGHT, KEY_GAMEPAD_RIGHT);
259
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_DPAD_UP, KEY_GAMEPAD_UP);
260
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_DPAD_DOWN, KEY_GAMEPAD_DOWN);
261
+
262
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_A, KEY_GAMEPAD_A);
263
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_B, KEY_GAMEPAD_B);
264
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_X, KEY_GAMEPAD_X);
265
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_Y, KEY_GAMEPAD_Y);
266
+
267
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_LEFT_SHOULDER, KEY_GAMEPAD_SHOULDER_LEFT);
268
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_RIGHT_SHOULDER, KEY_GAMEPAD_SHOULDER_RIGHT);
269
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_LEFT_THUMB, KEY_GAMEPAD_THUMB_LEFT);
270
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_RIGHT_THUMB, KEY_GAMEPAD_THUMB_RIGHT);
271
+
272
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_START, KEY_GAMEPAD_START);
273
+ handle_gamepad_button_event(win, state, prev_state, XINPUT_GAMEPAD_BACK, KEY_GAMEPAD_SELECT);
274
+
275
+ handle_gamepad_trigger_event(win, state.Gamepad.bLeftTrigger, prev_state.Gamepad.bLeftTrigger, KEY_GAMEPAD_TRIGGER_LEFT);
276
+ handle_gamepad_trigger_event(win, state.Gamepad.bRightTrigger, prev_state.Gamepad.bRightTrigger, KEY_GAMEPAD_TRIGGER_RIGHT);
277
+ }
278
+
279
+ void
280
+ poll_gamepads ()
281
+ {
282
+ static XINPUT_STATE prev_state;
283
+ static bool prev_detected = false;
284
+
285
+ XINPUT_STATE state = {0};
286
+ bool detected = XInputGetState(0, &state) == ERROR_SUCCESS;
287
+
288
+ if (detected != prev_detected)
289
+ {
290
+ prev_detected = detected;
291
+ if (detected) prev_state = {0};
292
+ }
293
+
294
+ if (!detected) return;
295
+
296
+ if (state.dwPacketNumber != prev_state.dwPacketNumber)
297
+ handle_gamepad_events(state, prev_state);
298
+
299
+ prev_state = state;
300
+ }
301
+
302
+
219
303
  }// Reflex
data/src/win32/event.h CHANGED
@@ -44,6 +44,9 @@ namespace Reflex
44
44
  };// NativeWheelEvent
45
45
 
46
46
 
47
+ void poll_gamepads ();
48
+
49
+
47
50
  }// Reflex
48
51
 
49
52
 
data/src/win32/window.cpp CHANGED
@@ -154,6 +154,8 @@ namespace Reflex
154
154
  {
155
155
  WindowData* self = get_data(win);
156
156
 
157
+ poll_gamepads();
158
+
157
159
  Window_call_update_event(win);
158
160
 
159
161
  if (self->redraw)
@@ -341,8 +343,13 @@ namespace Reflex
341
343
  {
342
344
  case WM_ACTIVATE:
343
345
  {
344
- if ((wp & 0xFFFF) == WA_INACTIVE)
346
+ if (LOWORD(wp) == WA_INACTIVE)
347
+ {
348
+ Window_call_deactivate_event(win);
345
349
  self->pressing_keys.clear();
350
+ }
351
+ else
352
+ Window_call_activate_event(win);
346
353
  break;
347
354
  }
348
355
 
data/src/window.cpp CHANGED
@@ -52,6 +52,17 @@ namespace Reflex
52
52
  return windows;
53
53
  }
54
54
 
55
+ Window*
56
+ Window_get_active ()
57
+ {
58
+ for (auto& w : Window_all())
59
+ {
60
+ if (Xot::has_flag(w->self->flags, Window::Data::ACTIVE))
61
+ return w;
62
+ }
63
+ return NULL;
64
+ }
65
+
55
66
 
56
67
  Window::Data::Data ()
57
68
  : flags(Window_default_flags())
@@ -167,6 +178,8 @@ namespace Reflex
167
178
  {
168
179
  if (!window) return;
169
180
 
181
+ Xot::add_flag(&window->self->flags, Window::Data::ACTIVE);
182
+
170
183
  Event e;
171
184
  window->on_activate(&e);
172
185
  }
@@ -178,6 +191,8 @@ namespace Reflex
178
191
 
179
192
  Event e;
180
193
  window->on_deactivate(&e);
194
+
195
+ Xot::remove_flag(&window->self->flags, Window::Data::ACTIVE);
181
196
  }
182
197
 
183
198
  void
data/src/window.h CHANGED
@@ -33,6 +33,13 @@ namespace Reflex
33
33
 
34
34
  typedef std::map<View::Ref, CaptureTargetIDList> CaptureMap;
35
35
 
36
+ enum Flag
37
+ {
38
+
39
+ ACTIVE = Xot::bit(1, FLAG_LAST),
40
+
41
+ };// Flag
42
+
36
43
  int hide_count = 1;
37
44
 
38
45
  bool redraw = true;
@@ -82,6 +89,8 @@ namespace Reflex
82
89
 
83
90
  Application::WindowList& Window_all ();
84
91
 
92
+ Window* Window_get_active ();
93
+
85
94
 
86
95
  uint Window_default_flags ();
87
96
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reflexion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - xordog
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-06 00:00:00.000000000 Z
11
+ date: 2025-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: xot
@@ -16,60 +16,60 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.3.4
19
+ version: 0.3.5
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 0.3.4
22
+ version: 0.3.5
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - "~>"
28
28
  - !ruby/object:Gem::Version
29
- version: 0.3.4
29
+ version: 0.3.5
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 0.3.4
32
+ version: 0.3.5
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: rucy
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 0.3.4
39
+ version: 0.3.5
40
40
  - - ">="
41
41
  - !ruby/object:Gem::Version
42
- version: 0.3.4
42
+ version: 0.3.5
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
46
46
  requirements:
47
47
  - - "~>"
48
48
  - !ruby/object:Gem::Version
49
- version: 0.3.4
49
+ version: 0.3.5
50
50
  - - ">="
51
51
  - !ruby/object:Gem::Version
52
- version: 0.3.4
52
+ version: 0.3.5
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: rays
55
55
  requirement: !ruby/object:Gem::Requirement
56
56
  requirements:
57
57
  - - "~>"
58
58
  - !ruby/object:Gem::Version
59
- version: 0.3.4
59
+ version: 0.3.5
60
60
  - - ">="
61
61
  - !ruby/object:Gem::Version
62
- version: 0.3.4
62
+ version: 0.3.5
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - "~>"
68
68
  - !ruby/object:Gem::Version
69
- version: 0.3.4
69
+ version: 0.3.5
70
70
  - - ">="
71
71
  - !ruby/object:Gem::Version
72
- version: 0.3.4
72
+ version: 0.3.5
73
73
  description: This library helps you to develop interactive graphical user interface.
74
74
  email: xordog@gmail.com
75
75
  executables: []
@@ -143,10 +143,12 @@ files:
143
143
  - ".doc/ext/reflex/view.cpp"
144
144
  - ".doc/ext/reflex/wheel_event.cpp"
145
145
  - ".doc/ext/reflex/window.cpp"
146
+ - ".github/PULL_REQUEST_TEMPLATE.md"
146
147
  - ".github/workflows/release-gem.yml"
147
148
  - ".github/workflows/tag.yml"
148
149
  - ".github/workflows/test.yml"
149
150
  - ".github/workflows/utils.rb"
151
+ - CONTRIBUTING.md
150
152
  - ChangeLog.md
151
153
  - Gemfile
152
154
  - Gemfile.lock