reflexion 0.3.14 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7614e28fab217b144379d0859803bd318c61cca675b9c67061d0b86878300962
4
- data.tar.gz: 6f9f65b4c99e1f0acadc628f5d190343659e4c89f1ada8a21ca24c1a0d1d8185
3
+ metadata.gz: 8781c6a2e533488f1529b59cdb98ed7922909330e254bdbd80b7ce67f40a8252
4
+ data.tar.gz: 449a81f9c5ba57c2daa68af18cc6e42d90319703beab674dee431e5bc2923d4b
5
5
  SHA512:
6
- metadata.gz: 23d61727fe3c353800913fa85ad9c357cad87e16c4f9be8f0d1a736216028019dce2e75f2fa9e486d78c96a2667fadfbcc4c6521fb6b3a2997e6b352defb9a39
7
- data.tar.gz: ba6c9e81a25ff8195023d26d99da5b9339c2349971716e83680c4a3e6b7bbfe46a8452f4cd37e5200ead581c56e9f68edcbc7248d4b5f8eef5a31dcb7c3c8c5c
6
+ metadata.gz: c746ba6a4b32c05eca98a2bd3c7ab5e459e44222d80794323757a8516401a91404c76aa4e7dbb08b084126bf08d65bf310ab8f3095f06bd2df29577fa7989604
7
+ data.tar.gz: 928aa64c73861dcb0848dc6c6ea6d770ccb3dff1e9e06ba6bb3cf470cb5104311af6ecfc543d4ca015f8efdc3a12d9bb6662b80014f9fc57a2bbc16c74bf5e43
@@ -119,11 +119,11 @@ VALUE get_key(VALUE self)
119
119
  CASE(LBRACKET): SYMBOL1(lbracket);
120
120
  CASE(RBRACKET): SYMBOL1(rbracket);
121
121
 
122
- #if defined(OSX) || defined(IOS) || defined(WIN32) || defined(LINUX)
122
+ #if defined(OSX) || defined(IOS) || defined(WIN32) || defined(LINUX) || defined(WASM)
123
123
  CASE(ENTER): SYMBOL1(enter);
124
124
  #else
125
125
  CASE(ENTER): SYMBOL1(enter);
126
- CASE(RETURN): SYMBOL1(_return, "return");
126
+ CASE(RETURN): SYMBOL2(_return, "return");
127
127
  #endif
128
128
  CASE(SPACE): SYMBOL1(space);
129
129
  CASE(TAB): SYMBOL1(tab);
@@ -141,25 +141,29 @@ VALUE get_key(VALUE self)
141
141
  CASE(PAGEUP): SYMBOL1(pageup);
142
142
  CASE(PAGEDOWN): SYMBOL1(pagedown);
143
143
 
144
- #if !defined(OSX) && !defined(IOS) && !defined(LINUX)
144
+ #if !defined(OSX) && !defined(IOS) && !defined(LINUX) && !defined(WASM)
145
145
  CASE(SHIFT):
146
146
  #endif
147
147
  CASE(LSHIFT):
148
148
  CASE(RSHIFT): SYMBOL1(shift);
149
- #if !defined(OSX) && !defined(IOS) && !defined(LINUX)
149
+ #if !defined(OSX) && !defined(IOS) && !defined(LINUX) && !defined(WASM)
150
150
  CASE(CONTROL):
151
151
  #endif
152
152
  CASE(LCONTROL):
153
153
  CASE(RCONTROL): SYMBOL1(control);
154
154
  CASE(ALT):
155
+ #if !defined(IOS) && !defined(LINUX) && !defined(WASM)
155
156
  CASE(LALT):
157
+ #endif
156
158
  CASE(RALT): SYMBOL1(alt);
157
159
  CASE(LWIN):
158
160
  CASE(RWIN): SYMBOL1(win);
161
+ #if !defined(IOS) && !defined(LINUX) && !defined(WASM)
159
162
  CASE(LCOMMAND):
160
163
  CASE(RCOMMAND): SYMBOL1(command);
161
164
  CASE(LOPTION):
162
165
  CASE(ROPTION): SYMBOL1(option);
166
+ #endif
163
167
  CASE(FUNCTION): SYMBOL1(function);
164
168
 
165
169
  CASE(F1): SYMBOL1(f1);
@@ -194,7 +198,9 @@ VALUE get_key(VALUE self)
194
198
  CASE(NUM_EQUAL): SYMBOL1(equal);
195
199
  CASE(NUM_COMMA): SYMBOL1(comma);
196
200
  CASE(NUM_DECIMAL): SYMBOL1(decimal);
201
+ #if !defined(IOS) && !defined(LINUX) && !defined(WASM)
197
202
  CASE(NUM_CLEAR): SYMBOL1(clear);
203
+ #endif
198
204
  CASE(NUM_ENTER): SYMBOL1(enter);
199
205
 
200
206
  CASE(CAPSLOCK): SYMBOL1(capslock);
@@ -197,8 +197,7 @@ VALUE each_child(VALUE self)
197
197
  CHECK;
198
198
 
199
199
  Value ret;
200
- Reflex::View::child_iterator end = THIS->child_end();
201
- for (Reflex::View::child_iterator it = THIS->child_begin(); it != end; ++it)
200
+ for (auto it = THIS->child_begin(), end = THIS->child_end(); it != end; ++it)
202
201
  ret = rb_yield(value(it->get()));
203
202
  return ret;
204
203
  }
@@ -4,6 +4,9 @@ on:
4
4
  push:
5
5
  tags: ['v[0-9]*']
6
6
 
7
+ permissions:
8
+ contents: write
9
+
7
10
  jobs:
8
11
  release:
9
12
  runs-on: macos-latest
@@ -33,23 +36,9 @@ jobs:
33
36
  echo path=$(ruby -e 'print Dir.glob("*.gem").first') >> $GITHUB_OUTPUT
34
37
 
35
38
  - name: create github release
36
- id: release
37
- uses: actions/create-release@v1
38
39
  env:
39
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40
- with:
41
- tag_name: ${{ github.ref }}
42
- release_name: ${{ github.ref }}
43
-
44
- - name: upload to github release
45
- uses: actions/upload-release-asset@v1
46
- env:
47
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
48
- with:
49
- upload_url: ${{ steps.release.outputs.upload_url }}
50
- asset_path: ./${{ steps.gem.outputs.path }}
51
- asset_name: ${{ steps.gem.outputs.path }}
52
- asset_content_type: application/zip
40
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
41
+ run: ruby -I.github/workflows -rutils -e 'release(*ARGV)' ./${{ steps.gem.outputs.path }}
53
42
 
54
43
  - name: upload to rubygems
55
44
  env:
@@ -1,3 +1,9 @@
1
+ require 'shellwords'
2
+
3
+ ALL_REPO = 'xord/all'
4
+ ALL_DIR = '../all'
5
+ ALL_FETCH_DEPTH = 100
6
+
1
7
  RENAMES = {reflex: 'reflexion'}
2
8
 
3
9
  def sh(cmd)
@@ -5,7 +11,7 @@ def sh(cmd)
5
11
  system cmd
6
12
  end
7
13
 
8
- def setup_dependencies(build: true, only: nil)
14
+ def setup_dependencies(only: nil)
9
15
  gemspec_path = `git ls-files`.lines(chomp: true).find {|l| l =~ /\.gemspec$/}
10
16
  return unless gemspec_path
11
17
 
@@ -13,44 +19,109 @@ def setup_dependencies(build: true, only: nil)
13
19
  name = File.basename gemspec_path, '.gemspec'
14
20
 
15
21
  exts = File.readlines('Rakefile')
16
- .map {|l| l[%r|^\s*require\W+(\w+)/extension\W+$|, 1]}
22
+ .map {|l| l[%r|^\s*require\W+([\w\-\_]+)/extension\W+$|, 1]}
17
23
  .compact
18
24
  .reject {|ext| ext == name}
19
25
  exts = exts & [only].flatten.map(&:to_s) if only
26
+ return if exts.empty?
27
+
28
+ unless setup_dependencies_via_monorepo(exts)
29
+ setup_dependencies_via_each_repo_by_version(gemspec, exts)
30
+ end
31
+
32
+ exts.each {|ext| sh %( cd ../#{ext} && rake ext )}
33
+ end
34
+
35
+ def setup_dependencies_via_monorepo(exts)
36
+ return false unless checkout_monorepo
37
+ exts.each {|ext| sh %( ln -snf all/#{ext} ../#{ext} )}
38
+ true
39
+ end
40
+
41
+ def checkout_monorepo()
42
+ uuid = `git log -1 --format=%B`[/^\[\[([0-9a-fA-F-]+)\]\]$/, 1]
43
+ return false unless uuid
44
+
45
+ commit = setup_monorepo uuid
46
+ return false unless commit
47
+
48
+ Dir.chdir(ALL_DIR) {sh %( git checkout -q #{commit} )}
49
+ true
50
+ end
51
+
52
+ def setup_monorepo(uuid)
53
+ unless File.directory? ALL_DIR
54
+ url = "https://github.com/#{ALL_REPO}.git"
55
+ sh %( git clone --no-tags --depth #{ALL_FETCH_DEPTH} #{url} #{ALL_DIR} )
56
+ end
57
+ loop do
58
+ commit = find_monorepo_commit uuid
59
+ return commit if commit
60
+
61
+ deepened = Dir.chdir ALL_DIR do
62
+ before = `git rev-list --count HEAD`.to_i
63
+ sh %( git fetch --deepen #{ALL_FETCH_DEPTH} )
64
+ `git rev-list --count HEAD`.to_i > before
65
+ end
66
+ return nil unless deepened
67
+ end
68
+ end
69
+
70
+ def find_monorepo_commit(uuid)
71
+ Dir.chdir ALL_DIR do
72
+ out = `git log origin/HEAD -F --grep="[[#{uuid}]]" --format=%H -1`.strip
73
+ out.empty? ? nil : out
74
+ end
75
+ end
20
76
 
77
+ def setup_dependencies_via_each_repo_by_version(gemspec, exts)
21
78
  exts.each do |ext|
22
79
  gem = RENAMES[ext.to_sym].then {|s| s || ext}
23
- ver = gemspec[/add_dependency.*['"]#{gem}['"].*['"]\s*>=\s*([\d\.]+)\s*['"]/, 1]
80
+ ver = gemspec[/add_dependency.*['"]#{gem}['"].*['"]\s*~>\s*([\d\.]+)\s*['"]/, 1]
24
81
  opts = '-c advice.detachedHead=false --depth 1'
25
82
  clone = "git clone #{opts} https://github.com/xord/#{ext}.git ../#{ext}"
26
83
 
27
84
  # 'rake subtree:push' pushes all subrepos, so cloning by new tag
28
85
  # often fails before tagging each new tag
29
86
  sh %( #{clone} --branch v#{ver} || #{clone} )
30
- sh %( cd ../#{ext} && rake ext )
31
87
  end
32
88
  end
33
89
 
34
90
  def tag_versions()
35
- tags = `git tag`.lines chomp: true
36
- vers = `git log --oneline ./VERSION`
91
+ changes = changelogs
92
+ tags = `git tag`.lines chomp: true
93
+ vers = `git log --oneline ./VERSION`
37
94
  .lines(chomp: true)
38
95
  .map {|line| line.split.first[/^\w+$/]}
39
- .map {|hash| [`git cat-file -p #{hash}:./VERSION 2>/dev/null`[/[\d\.]+/], hash]}
40
- .select {|ver, hash| ver && hash}
96
+ .map {|sha| [`git cat-file -p #{sha}:./VERSION 2>/dev/null`[/[\d\.]+/], sha]}
97
+ .select {|ver, sha| ver && sha}
41
98
  .reverse
42
99
  .to_h
43
100
 
44
- changes = File.read('ChangeLog.md')
45
- .split(/^\s*##\s*\[\s*v([\d\.]+)\s*\].*$/)
46
- .slice(1..-1)
47
- .each_slice(2)
48
- .to_h
49
- .transform_values(&:strip!)
50
-
51
- vers.to_a.reverse.each do |ver, hash|
101
+ vers.to_a.reverse.each do |ver, sha|
52
102
  tag = "v#{ver}"
53
103
  break if tags.include?(tag)
54
- sh %( git tag -a -m \"#{changes[ver]&.gsub '"', '\\"'}\" #{tag} #{hash} )
104
+ sh %( git tag -a -m \"#{changes[tag]&.gsub '"', '\\"'}\" #{tag} #{sha} )
55
105
  end
56
106
  end
107
+
108
+ def release(*paths)
109
+ tag = ENV['GITHUB_REF']&.sub(%r|^refs/tags/|, '') || raise('GITHUB_REF tag not set')
110
+ notes = (changelogs[tag] || '').shellescape
111
+ paths = paths.flatten.join ' '
112
+
113
+ sh(%( gh release create #{tag} #{paths} --notes #{notes} )) ||
114
+ sh(%( gh release upload #{tag} #{paths} --clobber )) ||
115
+ raise('failed to upload to releases')
116
+ end
117
+
118
+ def changelogs()
119
+ File.read('ChangeLog.md')
120
+ .split(/^\s*##\s*\[\s*(v[\d\.]+)\s*\].*$/)
121
+ .slice(1..)
122
+ .each_slice(2)
123
+ .to_h
124
+ .transform_values(&:strip!)
125
+ rescue Errno::ENOENT
126
+ raise 'failed to get changelogs'
127
+ end
data/ChangeLog.md CHANGED
@@ -1,6 +1,31 @@
1
1
  # reflex ChangeLog
2
2
 
3
3
 
4
+ ## [v0.4.0] - 2026-05-17
5
+
6
+ - [BREAKING] Normalize wheel Y delta to top-left origin convention
7
+
8
+ - Add keyboard event handling on iOS
9
+ - Add mouse event handling on iOS
10
+ - Rewrite README.md
11
+ - CI: Migrate release-gem.yml from actions/create-release to gh release create
12
+ - Refresh hello sample Xcode projects
13
+
14
+ - Skip duplicate KEY_CODE aliases on WASM build
15
+
16
+
17
+ ## [v0.3.15] - 2026-05-10
18
+
19
+ - Support WebAssembly
20
+ - Dispatch SDL touch events as multi-pointer PointerEvents
21
+ - Add SDL scancode mapping and input event handling
22
+ - Enable HiDPI support on SDL backend
23
+ - Handle SDL_QUIT via app->quit() instead of breaking loop
24
+ - Remove deprecated has_rdoc= from gemspecs
25
+
26
+ - Fix quit re-entrancy in Application_call_quit
27
+
28
+
4
29
  ## [v0.3.14] - 2026-04-17
5
30
 
6
31
  - Fix HID gamepad stick direction release not firing on direction switch
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Reflex - A Graphical User Interface Tool Kit for Ruby.
1
+ # Reflex - A GUI toolkit for Ruby
2
2
 
3
3
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/xord/reflex)
4
4
  ![License](https://img.shields.io/github/license/xord/reflex)
@@ -21,19 +21,38 @@ Thanks for your support! 🙌
21
21
 
22
22
  ## 🚀 About
23
23
 
24
- **Reflex** is a graphical user interface (GUI) toolkit for Ruby.
24
+ **Reflex** is a cross-platform GUI toolkit for Ruby. It gives you a `Window` with a tree of `View` objects, an event-driven runtime (`Application` / `on_draw`, `on_update`, `on_pointer`, ...), and bindings to a 2D physics engine, MIDI I/O, and live camera capture — all sitting on top of the [Rays](https://github.com/xord/rays) drawing engine.
25
25
 
26
- It is designed to help developers create intuitive and responsive user interfaces for their Ruby applications.
27
- **Reflex** provides core components such as Window and View, making it easier to build the foundation of your GUI applications.
26
+ It is the application layer underneath [Processing](https://github.com/xord/processing), [RubySketch](https://github.com/xord/rubysketch), and [Reight](https://github.com/xord/reight). Like the rest of the `xord/*` family, Reflex is primarily developed for our own use, but it also works as a standalone GUI / creative-coding toolkit.
27
+
28
+ > **The gem name is `reflexion`** (not `reflex`) — `gem install reflexion`. The Ruby namespace is `Reflex`.
29
+
30
+ ## 📋 Requirements
31
+
32
+ - Ruby **3.0.0** or later
33
+ - A C++ compiler with C++20 support
34
+ - [Xot](https://rubygems.org/gems/xot), [Rucy](https://rubygems.org/gems/rucy), and [Rays](https://rubygems.org/gems/rays) (declared as runtime dependencies)
35
+ - Platform GUI backend:
36
+ - **macOS** — AppKit (bundled with the OS)
37
+ - **iOS** — UIKit (bundled with the OS)
38
+ - **Windows** — native Win32
39
+ - **Linux** — `libsdl2-dev`
40
+
41
+ The following third-party libraries are cloned from GitHub and statically linked while the native extension is being built:
42
+
43
+ | Library | Role |
44
+ | --------------------------------------------- | ----------------------------------- |
45
+ | [Box2D](https://github.com/erincatto/box2d) | 2D physics simulation (`View` bodies, `ContactEvent`, gravity, fixtures) |
46
+ | [RtMidi](https://github.com/thestk/rtmidi) | MIDI I/O — exposes `MidiEvent`, `NoteEvent`, `ControlChangeEvent` to views |
28
47
 
29
48
  ## 📦 Installation
30
49
 
31
50
  Add this line to your Gemfile:
32
51
  ```ruby
33
- $ gem 'reflexion'
52
+ gem 'reflexion'
34
53
  ```
35
54
 
36
- Then, install gem:
55
+ Then install:
37
56
  ```bash
38
57
  $ bundle install
39
58
  ```
@@ -43,7 +62,187 @@ Or install it directly:
43
62
  $ gem install reflexion
44
63
  ```
45
64
 
65
+ `require 'reflex'` automatically calls `Reflex.init!` (and `Rays.init!`) and registers `Reflex.fin!` at exit. Set `$REFLEX_NOAUTOINIT = true` before requiring if you want to manage the lifetime yourself.
66
+
67
+ ## 📚 What's Included
68
+
69
+ ### Application / Window / View
70
+
71
+ | Class | Purpose |
72
+ | ------------------------ | ----------------------------------------------------------------------- |
73
+ | `Reflex::Application` | Run loop; created and started by `Reflex.start { ... }` |
74
+ | `Reflex::Window` | OS-level window with title, frame, flags (closable / resizable / fullscreen / portrait / landscape) |
75
+ | `Reflex::View` | Recursive UI node: position, size, transform, styles, child views, optional clipping / caching |
76
+ | `Reflex::Screen` | Information about a display monitor |
77
+ | `Reflex::Timer` | One-shot or interval timer that delivers `TimerEvent` |
78
+
79
+ ### Shapes (drawing + physics body)
80
+
81
+ A view can carry one or more `Shape` objects that act both as its drawn appearance and as its physics fixture. Built-ins:
82
+
83
+ - `Reflex::RectShape`
84
+ - `Reflex::EllipseShape`
85
+ - `Reflex::LineShape`
86
+ - `Reflex::PolygonShape` — wraps a `Rays::Polygon`
87
+
88
+ ### Events
89
+
90
+ Every event class inherits from `Reflex::Event`. Views receive them via `on_<name>` hooks (or `on(:name)` / `before(:name)` / `after(:name)` from `Xot::Hookable`).
91
+
92
+ | Event class | View hook | When it fires |
93
+ | ---------------------------------------------------- | -------------------- | --------------------------------------------------- |
94
+ | `UpdateEvent` | `on_update` | Every frame, before drawing |
95
+ | `DrawEvent` | `on_draw` | Every frame, to render with `e.painter` |
96
+ | `FrameEvent` | `on_frame_*` | Frame resize / move |
97
+ | `FocusEvent` | `on_focus` | Keyboard focus gained / lost |
98
+ | `KeyEvent` | `on_key` | Key down / up / repeat |
99
+ | `PointerEvent` | `on_pointer` | Mouse / touch down / move / up |
100
+ | `WheelEvent` | `on_wheel` | Scroll wheel / trackpad scroll |
101
+ | `ScrollEvent` | `on_scroll` | The view itself scrolled |
102
+ | `MidiEvent` / `NoteEvent` / `ControlChangeEvent` | `on_midi` / `on_note` / `on_control_change` | Incoming MIDI message |
103
+ | `CaptureEvent` | `on_capture` | New frame from a `Rays::Camera` |
104
+ | `TimerEvent` | `on_timer` | Fired by `start_timer` / `start_interval` |
105
+ | `ContactEvent` | `on_contact_*` | Two physics bodies began / ended overlapping |
106
+ | `DeviceEvent` / `MotionEvent` | various | Device-level signals (accelerometer / gyro / connection) |
107
+
108
+ ### Styling and selectors
109
+
110
+ `Reflex::Style` and `Reflex::Selector` (with `HasSelector`) provide a lightweight CSS-style mechanism for setting background, padding, layout, etc., on views.
111
+
112
+ ### Two ways to use the gem
113
+
114
+ The gem ships **two complementary APIs**:
115
+
116
+ 1. **`require 'reflex'`** — the full OO API. Subclass `Reflex::Window`, override `on_draw` / `on_update` / `on_pointer`, build a view hierarchy, etc.
117
+ 2. **`require 'reflexion/include'`** — a single-file, Processing-style API that exposes top-level `setup`, `draw`, `update`, `key`, `pointer`, `motion` blocks and auto-starts the application on `at_exit`.
118
+
119
+ ## 💡 Usage
120
+
121
+ ### Hello, Reflex (OO style)
122
+
123
+ ```ruby
124
+ require 'reflex'
125
+
126
+ class HelloWindow < Reflex::Window
127
+ def initialize
128
+ super title: 'Hello Reflex!', frame: [100, 100, 320, 240]
129
+ painter.font = Reflex::Font.new('Menlo', 32)
130
+ painter.background = 0
131
+ painter.fill = 1
132
+ end
133
+
134
+ def on_draw(e)
135
+ e.painter.text 'hello world!', 5, 5
136
+ end
137
+
138
+ def on_update(e)
139
+ painter.background = rand, rand, rand
140
+ redraw
141
+ end
142
+ end
143
+
144
+ Reflex.start do
145
+ HelloWindow.new.show
146
+ end
147
+ ```
148
+
149
+ ### Block / DSL style
150
+
151
+ ```ruby
152
+ require 'reflex'
153
+
154
+ Reflex.start do
155
+ Reflex::Window.show title: 'Shapes', frame: [100, 100, 500, 300] do
156
+ def on_draw(e)
157
+ e.painter.push do
158
+ fill :pink
159
+ stroke 1
160
+ stroke_width 2
161
+ rect 10, 10, 80, 80, 10
162
+ ellipse 120, 10, 80, 80
163
+ end
164
+ end
165
+ end
166
+ end
167
+ ```
168
+
169
+ ### Reflexion (Processing-style single-file)
170
+
171
+ ```ruby
172
+ require 'reflexion/include'
173
+
174
+ setup do
175
+ window.title = 'Reflexion!'
176
+ end
177
+
178
+ draw do |p|
179
+ p.background 0
180
+ p.fill 1
181
+ p.text 'hello from reflexion', 10, 30
182
+ end
183
+
184
+ pointer do |e|
185
+ puts "pointer at #{e.pos.to_a.inspect}"
186
+ end
187
+ # Reflexion.start is called automatically at_exit
188
+ ```
189
+
190
+ ### 2D physics with Box2D
191
+
192
+ ```ruby
193
+ require 'reflex'
194
+
195
+ Reflex.start name: 'Physics' do |app|
196
+ Reflex::Window.show title: app.name, frame: [100, 100, 500, 500] do
197
+ gravity 0, 9.8 * meter
198
+
199
+ 50.times do
200
+ add Reflex::View.new {
201
+ pos rand(10..400), rand(10..100)
202
+ size rand(5..50)
203
+ background [:red, :green, :blue, :yellow, :orange].sample
204
+ dynamic true
205
+ shape Reflex::EllipseShape.new(density: 1)
206
+ }
207
+ end
208
+
209
+ add Reflex::View.new { # a static ground
210
+ pos 0, 480
211
+ size 500, 20
212
+ background :darkgray
213
+ static true
214
+ }
215
+
216
+ on :pointer do |e|
217
+ if e.down? || e.drag?
218
+ add Reflex::View.new(pos: e.pos, size: 10, dynamic: true,
219
+ shape: Reflex::EllipseShape.new(density: 1))
220
+ end
221
+ end
222
+ end
223
+ end
224
+ ```
225
+
226
+ See the [`samples/`](./samples) directory for more examples covering shapes, layout, models, MIDI, camera capture, etc.
227
+
228
+ ## 🛠️ Development
229
+
230
+ ```bash
231
+ $ rake vendor # clone Box2D and RtMidi into vendor/
232
+ $ rake lib # build the native C++ library (libreflex)
233
+ $ rake ext # build the Ruby C extension
234
+ $ rake test # run the test suite
235
+ $ rake doc # generate RDoc from C++ sources
236
+ $ rake # default: builds the extension
237
+ ```
238
+
239
+ The test suite requires a windowing system, so CI only runs it on macOS. The `test_reflex_init.rb` test must run in its own process and is listed in `TESTS_ALONE`.
240
+
241
+ In the [`xord/all`](https://github.com/xord/all) monorepo you can scope by module, e.g. `rake reflex test`.
242
+
46
243
  ## 📜 License
47
244
 
48
245
  **Reflex** is licensed under the MIT License.
49
246
  See the [LICENSE](./LICENSE) file for details.
247
+
248
+ The third-party libraries listed above retain their own licenses (Box2D: MIT, RtMidi: MIT-style).
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.14
1
+ 0.4.0
@@ -15,12 +15,7 @@ Xot::ExtConf.new Xot, Rucy, Rays, Reflex do
15
15
  headers << 'ruby.h'
16
16
  libs.unshift 'gdi32', 'winmm', 'opengl32', 'glew32', 'xinput' if win32?
17
17
  frameworks << 'Cocoa' << 'GameController' if osx?
18
- $LDFLAGS << ' -Wl,--out-implib=reflex_ext.dll.a' if mingw? || cygwin?
19
-
20
- unless osx? || linux?
21
- lib_dirs << Rays::Extension.ext_dir
22
- libs << 'rays_ext'
23
- end
18
+ $LDFLAGS << ' -Wl,--out-implib=libreflex.dll.a' if mingw? || cygwin?
24
19
  end
25
20
 
26
21
  create_makefile 'reflex_ext'
@@ -123,11 +123,11 @@ RUCY_DEF0(get_key)
123
123
  CASE(LBRACKET): SYMBOL1(lbracket);
124
124
  CASE(RBRACKET): SYMBOL1(rbracket);
125
125
 
126
- #if defined(OSX) || defined(IOS) || defined(WIN32) || defined(LINUX)
126
+ #if defined(OSX) || defined(IOS) || defined(WIN32) || defined(LINUX) || defined(WASM)
127
127
  CASE(ENTER): SYMBOL1(enter);
128
128
  #else
129
129
  CASE(ENTER): SYMBOL1(enter);
130
- CASE(RETURN): SYMBOL1(_return, "return");
130
+ CASE(RETURN): SYMBOL2(_return, "return");
131
131
  #endif
132
132
  CASE(SPACE): SYMBOL1(space);
133
133
  CASE(TAB): SYMBOL1(tab);
@@ -145,25 +145,29 @@ RUCY_DEF0(get_key)
145
145
  CASE(PAGEUP): SYMBOL1(pageup);
146
146
  CASE(PAGEDOWN): SYMBOL1(pagedown);
147
147
 
148
- #if !defined(OSX) && !defined(IOS) && !defined(LINUX)
148
+ #if !defined(OSX) && !defined(IOS) && !defined(LINUX) && !defined(WASM)
149
149
  CASE(SHIFT):
150
150
  #endif
151
151
  CASE(LSHIFT):
152
152
  CASE(RSHIFT): SYMBOL1(shift);
153
- #if !defined(OSX) && !defined(IOS) && !defined(LINUX)
153
+ #if !defined(OSX) && !defined(IOS) && !defined(LINUX) && !defined(WASM)
154
154
  CASE(CONTROL):
155
155
  #endif
156
156
  CASE(LCONTROL):
157
157
  CASE(RCONTROL): SYMBOL1(control);
158
158
  CASE(ALT):
159
+ #if !defined(IOS) && !defined(LINUX) && !defined(WASM)
159
160
  CASE(LALT):
161
+ #endif
160
162
  CASE(RALT): SYMBOL1(alt);
161
163
  CASE(LWIN):
162
164
  CASE(RWIN): SYMBOL1(win);
165
+ #if !defined(IOS) && !defined(LINUX) && !defined(WASM)
163
166
  CASE(LCOMMAND):
164
167
  CASE(RCOMMAND): SYMBOL1(command);
165
168
  CASE(LOPTION):
166
169
  CASE(ROPTION): SYMBOL1(option);
170
+ #endif
167
171
  CASE(FUNCTION): SYMBOL1(function);
168
172
 
169
173
  CASE(F1): SYMBOL1(f1);
@@ -198,7 +202,9 @@ RUCY_DEF0(get_key)
198
202
  CASE(NUM_EQUAL): SYMBOL1(equal);
199
203
  CASE(NUM_COMMA): SYMBOL1(comma);
200
204
  CASE(NUM_DECIMAL): SYMBOL1(decimal);
205
+ #if !defined(IOS) && !defined(LINUX) && !defined(WASM)
201
206
  CASE(NUM_CLEAR): SYMBOL1(clear);
207
+ #endif
202
208
  CASE(NUM_ENTER): SYMBOL1(enter);
203
209
 
204
210
  CASE(CAPSLOCK): SYMBOL1(capslock);
data/ext/reflex/view.cpp CHANGED
@@ -217,8 +217,7 @@ RUCY_DEF0(each_child)
217
217
  CHECK;
218
218
 
219
219
  Value ret;
220
- Reflex::View::child_iterator end = THIS->child_end();
221
- for (Reflex::View::child_iterator it = THIS->child_begin(); it != end; ++it)
220
+ for (auto it = THIS->child_begin(), end = THIS->child_end(); it != end; ++it)
222
221
  ret = rb_yield(value(it->get()));
223
222
  return ret;
224
223
  }