lacci 0.4.0 → 0.5.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 +4 -4
- data/Gemfile +0 -2
- data/Gemfile.lock +4 -32
- data/lib/lacci/version.rb +1 -1
- data/lib/scarpe/niente/app.rb +12 -1
- data/lib/scarpe/niente/shoes_spec.rb +4 -5
- data/lib/scarpe/niente.rb +1 -0
- data/lib/shoes/app.rb +166 -61
- data/lib/shoes/constants.rb +1 -0
- data/lib/shoes/drawable.rb +35 -19
- data/lib/shoes/drawables/arc.rb +2 -2
- data/lib/shoes/drawables/arrow.rb +2 -2
- data/lib/shoes/drawables/border.rb +1 -1
- data/lib/shoes/drawables/button.rb +1 -1
- data/lib/shoes/drawables/edit_line.rb +1 -1
- data/lib/shoes/drawables/flow.rb +1 -1
- data/lib/shoes/drawables/line.rb +2 -2
- data/lib/shoes/drawables/link.rb +11 -1
- data/lib/shoes/drawables/oval.rb +2 -2
- data/lib/shoes/drawables/rect.rb +2 -2
- data/lib/shoes/drawables/shape.rb +2 -2
- data/lib/shoes/drawables/slot.rb +5 -3
- data/lib/shoes/drawables/stack.rb +1 -1
- data/lib/shoes/drawables/star.rb +1 -1
- data/lib/shoes/drawables/widget.rb +1 -1
- data/lib/shoes.rb +94 -17
- metadata +3 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e4b8a6a22600196c15c5ed8abbb7710bcc2d24fd2842aaa9190af738ef1e1802
|
|
4
|
+
data.tar.gz: 9bc49da62c2eea40133ca5d0cb01a3af7bec21d0d605478da5c235ce4a34c7a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 77de9f93d6495c6d0092eaf39bc36d46492a4f32725c3d5b2eb2dc07992cb31c21a10489eea8e77d29144dbbdd1daad8b12b8ccfb42bee1124579a8c0c34dbbb
|
|
7
|
+
data.tar.gz: fa322933a6da6fd7f831ac8490ec5b519f4e93f740fb35dcddf973562abfd81efa7f5cc9c89c4dd4d23f33dbdc35b59b90f9c217708d0c3e168d0658419ac85e
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ../scarpe-components
|
|
3
3
|
specs:
|
|
4
|
-
scarpe-components (0.
|
|
4
|
+
scarpe-components (0.4.0)
|
|
5
5
|
|
|
6
6
|
PATH
|
|
7
7
|
remote: .
|
|
8
8
|
specs:
|
|
9
|
-
lacci (0.
|
|
10
|
-
scarpe-components
|
|
9
|
+
lacci (0.4.0)
|
|
10
|
+
scarpe-components (~> 0.4.0)
|
|
11
11
|
|
|
12
12
|
GEM
|
|
13
13
|
remote: https://rubygems.org/
|
|
14
14
|
specs:
|
|
15
15
|
ansi (1.5.0)
|
|
16
|
-
ast (2.4.2)
|
|
17
16
|
builder (3.2.4)
|
|
18
17
|
debug (1.8.0)
|
|
19
18
|
irb (>= 1.5.0)
|
|
@@ -21,46 +20,21 @@ GEM
|
|
|
21
20
|
io-console (0.6.0)
|
|
22
21
|
irb (1.7.2)
|
|
23
22
|
reline (>= 0.3.6)
|
|
24
|
-
json (2.6.3)
|
|
25
|
-
language_server-protocol (3.17.0.3)
|
|
26
23
|
minitest (5.18.1)
|
|
27
24
|
minitest-reporters (1.6.0)
|
|
28
25
|
ansi
|
|
29
26
|
builder
|
|
30
27
|
minitest (>= 5.0)
|
|
31
28
|
ruby-progressbar
|
|
32
|
-
parallel (1.23.0)
|
|
33
|
-
parser (3.2.2.3)
|
|
34
|
-
ast (~> 2.4.1)
|
|
35
|
-
racc
|
|
36
|
-
racc (1.7.1)
|
|
37
|
-
rainbow (3.1.1)
|
|
38
29
|
rake (13.0.6)
|
|
39
30
|
redcarpet (3.6.0)
|
|
40
|
-
regexp_parser (2.8.1)
|
|
41
31
|
reline (0.3.6)
|
|
42
32
|
io-console (~> 0.5)
|
|
43
|
-
rexml (3.2.5)
|
|
44
|
-
rubocop (1.54.1)
|
|
45
|
-
json (~> 2.3)
|
|
46
|
-
language_server-protocol (>= 3.17.0)
|
|
47
|
-
parallel (~> 1.10)
|
|
48
|
-
parser (>= 3.2.2.3)
|
|
49
|
-
rainbow (>= 2.2.2, < 4.0)
|
|
50
|
-
regexp_parser (>= 1.8, < 3.0)
|
|
51
|
-
rexml (>= 3.2.5, < 4.0)
|
|
52
|
-
rubocop-ast (>= 1.28.0, < 2.0)
|
|
53
|
-
ruby-progressbar (~> 1.7)
|
|
54
|
-
unicode-display_width (>= 2.4.0, < 3.0)
|
|
55
|
-
rubocop-ast (1.29.0)
|
|
56
|
-
parser (>= 3.2.1.0)
|
|
57
|
-
rubocop-shopify (2.14.0)
|
|
58
|
-
rubocop (~> 1.51)
|
|
59
33
|
ruby-progressbar (1.13.0)
|
|
60
|
-
unicode-display_width (2.4.2)
|
|
61
34
|
yard (0.9.34)
|
|
62
35
|
|
|
63
36
|
PLATFORMS
|
|
37
|
+
arm64-darwin-21
|
|
64
38
|
x86_64-darwin-22
|
|
65
39
|
|
|
66
40
|
DEPENDENCIES
|
|
@@ -70,8 +44,6 @@ DEPENDENCIES
|
|
|
70
44
|
minitest-reporters
|
|
71
45
|
rake (~> 13.0)
|
|
72
46
|
redcarpet
|
|
73
|
-
rubocop (~> 1.21)
|
|
74
|
-
rubocop-shopify
|
|
75
47
|
scarpe-components!
|
|
76
48
|
yard
|
|
77
49
|
|
data/lib/lacci/version.rb
CHANGED
data/lib/scarpe/niente/app.rb
CHANGED
|
@@ -14,7 +14,18 @@ module Niente
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def run
|
|
17
|
-
send_shoes_event("
|
|
17
|
+
send_shoes_event("return", event_name: "custom_event_loop")
|
|
18
|
+
|
|
19
|
+
@do_shutdown = false
|
|
20
|
+
bind_shoes_event(event_name: "destroy") do
|
|
21
|
+
@do_shutdown = true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
at_exit do
|
|
25
|
+
until @do_shutdown
|
|
26
|
+
Shoes::DisplayService.dispatch_event("heartbeat", nil)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
18
29
|
end
|
|
19
30
|
|
|
20
31
|
def destroy
|
|
@@ -8,7 +8,7 @@ module Niente; end
|
|
|
8
8
|
class Niente::Test
|
|
9
9
|
def self.run_shoes_spec_test_code(code, class_name: nil, test_name: nil)
|
|
10
10
|
if @shoes_spec_init
|
|
11
|
-
raise Shoes::Errors::MultipleShoesSpecRunsError, "
|
|
11
|
+
raise Shoes::Errors::MultipleShoesSpecRunsError, "Niente can only run a single Shoes spec per process!"
|
|
12
12
|
end
|
|
13
13
|
@shoes_spec_init = true
|
|
14
14
|
|
|
@@ -21,7 +21,7 @@ class Niente::Test
|
|
|
21
21
|
Shoes::DisplayService.subscribe_to_event("heartbeat", nil) do
|
|
22
22
|
unless @hb_init
|
|
23
23
|
Minitest.run []
|
|
24
|
-
Shoes
|
|
24
|
+
Shoes.APPS.each(&:destroy)
|
|
25
25
|
end
|
|
26
26
|
@hb_init = true
|
|
27
27
|
end
|
|
@@ -40,9 +40,8 @@ class Niente::ShoesSpecTest < Minitest::Test
|
|
|
40
40
|
finder_name = drawable_class.dsl_name
|
|
41
41
|
|
|
42
42
|
define_method(finder_name) do |*args|
|
|
43
|
-
|
|
43
|
+
drawables = Shoes::App.find_drawables_by(drawable_class, *args)
|
|
44
44
|
|
|
45
|
-
drawables = app.find_drawables_by(drawable_class, *args)
|
|
46
45
|
raise Shoes::Errors::MultipleDrawablesFoundError, "Found more than one #{finder_name} matching #{args.inspect}!" if drawables.size > 1
|
|
47
46
|
raise Shoes::Errors::NoDrawablesFoundError, "Found no #{finder_name} matching #{args.inspect}!" if drawables.empty?
|
|
48
47
|
|
|
@@ -51,7 +50,7 @@ class Niente::ShoesSpecTest < Minitest::Test
|
|
|
51
50
|
end
|
|
52
51
|
|
|
53
52
|
def drawable(*specs)
|
|
54
|
-
drawables = Shoes::App.
|
|
53
|
+
drawables = Shoes::App.find_drawables_by(*specs)
|
|
55
54
|
raise Shoes::Errors::MultipleDrawablesFoundError, "Found more than one #{finder_name} matching #{args.inspect}!" if drawables.size > 1
|
|
56
55
|
raise Shoes::Errors::NoDrawablesFoundError, "Found no #{finder_name} matching #{args.inspect}!" if drawables.empty?
|
|
57
56
|
Niente::ShoesSpecProxy.new(drawables[0])
|
data/lib/scarpe/niente.rb
CHANGED
data/lib/shoes/app.rb
CHANGED
|
@@ -4,10 +4,6 @@ class Shoes
|
|
|
4
4
|
class App < Shoes::Drawable
|
|
5
5
|
include Shoes::Log
|
|
6
6
|
|
|
7
|
-
class << self
|
|
8
|
-
attr_accessor :instance
|
|
9
|
-
end
|
|
10
|
-
|
|
11
7
|
# The Shoes root of the drawable tree
|
|
12
8
|
attr_reader :document_root
|
|
13
9
|
|
|
@@ -18,35 +14,48 @@ class Shoes
|
|
|
18
14
|
shoes_styles :title, :width, :height, :resizable, :features
|
|
19
15
|
|
|
20
16
|
# This is defined to avoid the linkable-id check in the Shoes-style method_missing def'n
|
|
21
|
-
|
|
22
|
-
@features
|
|
23
|
-
end
|
|
17
|
+
attr_reader :features
|
|
24
18
|
|
|
25
|
-
|
|
19
|
+
# These are the allowed values for custom_event_loop events.
|
|
20
|
+
#
|
|
21
|
+
# * displaylib means the display library is not going to return from running the app
|
|
22
|
+
# * return means the display library will return and the loop will be handled outside Lacci's control
|
|
23
|
+
# * wait means Lacci should busy-wait and send eternal heartbeats from the "run" event
|
|
24
|
+
#
|
|
25
|
+
# If the display service grabs control and keeps it, Webview-style, that means "displaylib"
|
|
26
|
+
# should be the value. A Scarpe-Wasm-style "return" is appropriate if the code can finish
|
|
27
|
+
# without Ruby ending the process at the end of the source file. A "wait" can prevent Ruby
|
|
28
|
+
# from finishing early, but also prevents multiple applications. Only "return" will normally
|
|
29
|
+
# allow multiple Shoes applications.
|
|
30
|
+
CUSTOM_EVENT_LOOP_TYPES = %w[displaylib return wait]
|
|
31
|
+
|
|
32
|
+
class << self
|
|
33
|
+
attr_accessor :set_test_code
|
|
34
|
+
end
|
|
26
35
|
|
|
27
36
|
init_args
|
|
28
37
|
def initialize(
|
|
29
|
-
title:
|
|
38
|
+
title: 'Shoes!',
|
|
30
39
|
width: 480,
|
|
31
40
|
height: 420,
|
|
32
41
|
resizable: true,
|
|
33
42
|
features: [],
|
|
34
43
|
&app_code_body
|
|
35
44
|
)
|
|
36
|
-
log_init(
|
|
45
|
+
log_init('Shoes::App')
|
|
37
46
|
|
|
38
|
-
if Shoes::
|
|
39
|
-
|
|
40
|
-
raise Shoes::Errors::TooManyInstancesError, "Cannot create multiple Shoes::App objects!"
|
|
47
|
+
if Shoes::FEATURES.include?(:multi_app) || Shoes.APPS.empty?
|
|
48
|
+
Shoes.APPS.push self
|
|
41
49
|
else
|
|
42
|
-
Shoes::App
|
|
50
|
+
@log.error('Trying to create a second Shoes::App in the same process! Fail!')
|
|
51
|
+
raise Shoes::Errors::TooManyInstancesError, 'Cannot create multiple Shoes::App objects!'
|
|
43
52
|
end
|
|
44
53
|
|
|
45
54
|
# We cd to the app's containing dir when running the app
|
|
46
55
|
@dir = Dir.pwd
|
|
47
56
|
|
|
48
57
|
@do_shutdown = false
|
|
49
|
-
@event_loop_type =
|
|
58
|
+
@event_loop_type = 'displaylib' # the default
|
|
50
59
|
|
|
51
60
|
@features = features
|
|
52
61
|
|
|
@@ -62,19 +71,26 @@ class Shoes
|
|
|
62
71
|
|
|
63
72
|
@slots = []
|
|
64
73
|
|
|
74
|
+
@content_container = nil
|
|
75
|
+
|
|
76
|
+
@routes = {}
|
|
77
|
+
|
|
65
78
|
super
|
|
66
79
|
|
|
67
80
|
# This creates the DocumentRoot, including its corresponding display drawable
|
|
68
|
-
|
|
81
|
+
Drawable.with_current_app(self) do
|
|
82
|
+
@document_root = Shoes::DocumentRoot.new
|
|
83
|
+
end
|
|
69
84
|
|
|
70
85
|
# Now create the App display drawable
|
|
71
86
|
create_display_drawable
|
|
72
87
|
|
|
73
88
|
# Set up testing *after* Display Service basic objects exist
|
|
74
89
|
|
|
75
|
-
if ENV[
|
|
76
|
-
test_code = File.read ENV[
|
|
90
|
+
if ENV['SHOES_SPEC_TEST'] && !Shoes::App.set_test_code
|
|
91
|
+
test_code = File.read ENV['SHOES_SPEC_TEST']
|
|
77
92
|
unless test_code.empty?
|
|
93
|
+
Shoes::App.set_test_code = true
|
|
78
94
|
Shoes::Spec.instance.run_shoes_spec_test_code test_code
|
|
79
95
|
end
|
|
80
96
|
end
|
|
@@ -83,30 +99,34 @@ class Shoes
|
|
|
83
99
|
|
|
84
100
|
# Try to de-dup as much as possible and not send repeat or multiple
|
|
85
101
|
# destroy events
|
|
86
|
-
@watch_for_destroy = bind_shoes_event(event_name:
|
|
102
|
+
@watch_for_destroy = bind_shoes_event(event_name: 'destroy') do
|
|
87
103
|
Shoes::DisplayService.unsub_from_events(@watch_for_destroy) if @watch_for_destroy
|
|
88
104
|
@watch_for_destroy = nil
|
|
89
|
-
|
|
105
|
+
destroy(send_event: false)
|
|
90
106
|
end
|
|
91
107
|
|
|
92
|
-
@watch_for_event_loop = bind_shoes_event(event_name:
|
|
93
|
-
|
|
108
|
+
@watch_for_event_loop = bind_shoes_event(event_name: 'custom_event_loop') do |loop_type|
|
|
109
|
+
unless CUSTOM_EVENT_LOOP_TYPES.include?(loop_type)
|
|
110
|
+
raise(Shoes::Errors::InvalidAttributeValueError,
|
|
111
|
+
"Unknown event loop type: #{loop_type.inspect}!")
|
|
112
|
+
end
|
|
94
113
|
|
|
95
114
|
@event_loop_type = loop_type
|
|
96
115
|
end
|
|
97
116
|
|
|
98
|
-
Signal.trap(
|
|
99
|
-
@log.warn(
|
|
117
|
+
Signal.trap('INT') do
|
|
118
|
+
@log.warn('App interrupted by signal, stopping...')
|
|
100
119
|
puts "\nStopping Shoes app..."
|
|
101
120
|
destroy
|
|
102
121
|
end
|
|
103
122
|
end
|
|
104
123
|
|
|
105
124
|
def init
|
|
106
|
-
send_shoes_event(event_name:
|
|
125
|
+
send_shoes_event(event_name: 'init')
|
|
107
126
|
return if @do_shutdown
|
|
108
127
|
|
|
109
|
-
|
|
128
|
+
with_slot(@document_root, &@app_code_body)
|
|
129
|
+
render_index_if_defined_on_first_boot
|
|
110
130
|
end
|
|
111
131
|
|
|
112
132
|
# "Container" drawables like flows, stacks, masks and the document root
|
|
@@ -130,7 +150,7 @@ class Shoes
|
|
|
130
150
|
return unless block_given?
|
|
131
151
|
|
|
132
152
|
push_slot(slot_item)
|
|
133
|
-
|
|
153
|
+
instance_eval(&block)
|
|
134
154
|
ensure
|
|
135
155
|
pop_slot
|
|
136
156
|
end
|
|
@@ -144,7 +164,9 @@ class Shoes
|
|
|
144
164
|
return super unless klass
|
|
145
165
|
|
|
146
166
|
::Shoes::App.define_method(name) do |*args, **kwargs, &block|
|
|
147
|
-
|
|
167
|
+
Drawable.with_current_app(self) do
|
|
168
|
+
klass.new(*args, **kwargs, &block)
|
|
169
|
+
end
|
|
148
170
|
end
|
|
149
171
|
|
|
150
172
|
send(name, *args, **kwargs, &block)
|
|
@@ -163,34 +185,33 @@ class Shoes
|
|
|
163
185
|
# want to (and/or can't) take control of the event loop.
|
|
164
186
|
def run
|
|
165
187
|
if @do_shutdown
|
|
166
|
-
|
|
188
|
+
warn 'Destroy has already been signaled, but we just called Shoes::App.run!'
|
|
167
189
|
return
|
|
168
190
|
end
|
|
169
191
|
|
|
170
192
|
# The display lib can send us an event to customise the event loop handling.
|
|
171
193
|
# But it must do so before the "run" event returns.
|
|
172
|
-
send_shoes_event(event_name:
|
|
194
|
+
send_shoes_event(event_name: 'run')
|
|
173
195
|
|
|
174
196
|
case @event_loop_type
|
|
175
|
-
when
|
|
197
|
+
when 'wait'
|
|
176
198
|
# Display lib wants us to busy-wait instead of it.
|
|
177
|
-
until @do_shutdown
|
|
178
|
-
|
|
179
|
-
end
|
|
180
|
-
when "displaylib"
|
|
199
|
+
Shoes::DisplayService.dispatch_event('heartbeat', nil) until @do_shutdown
|
|
200
|
+
when 'displaylib'
|
|
181
201
|
# If run event returned, that means we're done.
|
|
182
202
|
destroy
|
|
183
|
-
when
|
|
203
|
+
when 'return'
|
|
184
204
|
# We can just return to the main event loop. But we shouldn't call destroy.
|
|
185
205
|
# Presumably some event loop *outside* our event loop is handling things.
|
|
186
206
|
else
|
|
187
|
-
raise Shoes::Errors::InvalidAttributeValueError,
|
|
207
|
+
raise Shoes::Errors::InvalidAttributeValueError,
|
|
208
|
+
"Internal error! Incorrect event loop type: #{@event_loop_type.inspect}!"
|
|
188
209
|
end
|
|
189
210
|
end
|
|
190
211
|
|
|
191
212
|
def destroy(send_event: true)
|
|
192
213
|
@do_shutdown = true
|
|
193
|
-
send_shoes_event(event_name:
|
|
214
|
+
send_shoes_event(event_name: 'destroy') if send_event
|
|
194
215
|
end
|
|
195
216
|
|
|
196
217
|
def all_drawables
|
|
@@ -205,40 +226,52 @@ class Shoes
|
|
|
205
226
|
out
|
|
206
227
|
end
|
|
207
228
|
|
|
229
|
+
# We can add various ways to find drawables here.
|
|
230
|
+
# These are sort of like Shoes selectors, used for testing.
|
|
231
|
+
# This method finds a drawable across all active Shoes apps.
|
|
232
|
+
def self.find_drawables_by(*specs)
|
|
233
|
+
Shoes.APPS.flat_map do |app|
|
|
234
|
+
app.find_drawables_by(*specs)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
208
238
|
# We can add various ways to find drawables here.
|
|
209
239
|
# These are sort of like Shoes selectors, used for testing.
|
|
210
240
|
def find_drawables_by(*specs)
|
|
211
241
|
drawables = all_drawables
|
|
212
242
|
specs.each do |spec|
|
|
213
243
|
if spec == Shoes::App
|
|
214
|
-
drawables = [
|
|
244
|
+
drawables = [@app]
|
|
215
245
|
elsif spec.is_a?(Class)
|
|
216
246
|
drawables.select! { |w| spec === w }
|
|
217
247
|
elsif spec.is_a?(Symbol) || spec.is_a?(String)
|
|
218
248
|
s = spec.to_s
|
|
219
249
|
case s[0]
|
|
220
|
-
when
|
|
250
|
+
when '$'
|
|
221
251
|
begin
|
|
222
252
|
# I'm not finding a global_variable_get or similar...
|
|
223
253
|
global_value = eval s
|
|
224
254
|
drawables &= [global_value]
|
|
225
255
|
rescue
|
|
226
|
-
raise Shoes::Errors::InvalidAttributeValueError, "Error getting global variable: #{spec.inspect}"
|
|
256
|
+
# raise Shoes::Errors::InvalidAttributeValueError, "Error getting global variable: #{spec.inspect}"
|
|
257
|
+
drawables = []
|
|
227
258
|
end
|
|
228
|
-
when
|
|
229
|
-
if
|
|
230
|
-
drawables &= [
|
|
259
|
+
when '@'
|
|
260
|
+
if @app.instance_variables.include?(spec.to_sym)
|
|
261
|
+
drawables &= [@app.instance_variable_get(spec)]
|
|
231
262
|
else
|
|
232
|
-
raise Shoes::Errors::InvalidAttributeValueError, "Can't find top-level instance variable: #{spec.inspect}!"
|
|
263
|
+
# raise Shoes::Errors::InvalidAttributeValueError, "Can't find top-level instance variable: #{spec.inspect}!"
|
|
264
|
+
drawables = []
|
|
233
265
|
end
|
|
234
266
|
else
|
|
235
|
-
|
|
236
|
-
find_id = Integer(s[3..-1])
|
|
237
|
-
drawable = Shoes::Drawable.drawable_by_id(find_id)
|
|
238
|
-
drawables &= [drawable]
|
|
239
|
-
else
|
|
267
|
+
unless s.start_with?('id:')
|
|
240
268
|
raise Shoes::Errors::InvalidAttributeValueError, "Don't know how to find drawables by #{spec.inspect}!"
|
|
241
269
|
end
|
|
270
|
+
|
|
271
|
+
find_id = Integer(s[3..-1])
|
|
272
|
+
drawable = Shoes::Drawable.drawable_by_id(find_id)
|
|
273
|
+
drawables &= [drawable]
|
|
274
|
+
|
|
242
275
|
end
|
|
243
276
|
else
|
|
244
277
|
raise(Shoes::Errors::InvalidAttributeValueError, "Don't know how to find drawables by #{spec.inspect}!")
|
|
@@ -246,11 +279,67 @@ class Shoes
|
|
|
246
279
|
end
|
|
247
280
|
drawables
|
|
248
281
|
end
|
|
282
|
+
|
|
283
|
+
def page(name, &block)
|
|
284
|
+
@pages ||= {}
|
|
285
|
+
@pages[name] = proc do
|
|
286
|
+
stack(width: 1.0, height: 1.0) do
|
|
287
|
+
instance_eval(&block)
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def visit(name_or_path)
|
|
293
|
+
# First, check for exact page match (symbol)
|
|
294
|
+
if @pages && @pages[name_or_path]
|
|
295
|
+
@document_root.clear do
|
|
296
|
+
instance_eval(&@pages[name_or_path])
|
|
297
|
+
end
|
|
298
|
+
return
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Second, check URL routes
|
|
302
|
+
route, method_name = @routes.find { |r, _| r === name_or_path }
|
|
303
|
+
if route
|
|
304
|
+
@document_root.clear do
|
|
305
|
+
if route.is_a?(Regexp)
|
|
306
|
+
match_data = route.match(name_or_path)
|
|
307
|
+
send(method_name, *match_data.captures)
|
|
308
|
+
else
|
|
309
|
+
send(method_name)
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
return
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Third, if it's a string path like "/page2", try matching page :page2
|
|
316
|
+
if name_or_path.is_a?(String) && name_or_path.start_with?("/")
|
|
317
|
+
page_name = name_or_path[1..-1].to_sym # "/page2" -> :page2
|
|
318
|
+
if @pages && @pages[page_name]
|
|
319
|
+
@document_root.clear do
|
|
320
|
+
instance_eval(&@pages[page_name])
|
|
321
|
+
end
|
|
322
|
+
return
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
puts "Error: URL '#{name_or_path}' not found"
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def url(path, method_name)
|
|
330
|
+
if path.is_a?(String) && path.include?('(')
|
|
331
|
+
# Convert string patterns to regex
|
|
332
|
+
regex = Regexp.new("^#{path.gsub(/\(.*?\)/, '(.*?)')}$")
|
|
333
|
+
@routes[regex] = method_name
|
|
334
|
+
else
|
|
335
|
+
@routes[path] = method_name
|
|
336
|
+
end
|
|
337
|
+
end
|
|
249
338
|
end
|
|
250
339
|
end
|
|
251
340
|
|
|
252
341
|
# Event handler DSLs get defined in both App and Slot - same code, slightly different results
|
|
253
|
-
events = [
|
|
342
|
+
events = %i[motion hover leave click release keypress animate every timer]
|
|
254
343
|
events.each do |event|
|
|
255
344
|
Shoes::App.define_method(event) do |*args, &block|
|
|
256
345
|
subscription_item(args:, shoes_api_name: event.to_s, &block)
|
|
@@ -273,7 +362,7 @@ class Shoes::App < Shoes::Drawable
|
|
|
273
362
|
end
|
|
274
363
|
|
|
275
364
|
# Draw Context methods -- forward to the current slot
|
|
276
|
-
[
|
|
365
|
+
%i[fill nofill stroke strokewidth nostroke rotate].each do |dc_method|
|
|
277
366
|
define_method(dc_method) do |*args|
|
|
278
367
|
current_slot.send(dc_method, *args)
|
|
279
368
|
end
|
|
@@ -282,22 +371,38 @@ class Shoes::App < Shoes::Drawable
|
|
|
282
371
|
# Shape DSL methods
|
|
283
372
|
|
|
284
373
|
def move_to(x, y)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
current_slot.add_shape_command(["move_to", x, y])
|
|
374
|
+
unless x.is_a?(Numeric) && y.is_a?(Numeric)
|
|
375
|
+
raise(Shoes::Errors::InvalidAttributeValueError,
|
|
376
|
+
'Pass only Numeric arguments to move_to!')
|
|
289
377
|
end
|
|
378
|
+
|
|
379
|
+
return unless current_slot.is_a?(::Shoes::Shape)
|
|
380
|
+
|
|
381
|
+
current_slot.add_shape_command(['move_to', x, y])
|
|
290
382
|
end
|
|
291
383
|
|
|
292
384
|
def line_to(x, y)
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
current_slot.add_shape_command(["line_to", x, y])
|
|
385
|
+
unless x.is_a?(Numeric) && y.is_a?(Numeric)
|
|
386
|
+
raise(Shoes::Errors::InvalidAttributeValueError,
|
|
387
|
+
'Pass only Numeric arguments to line_to!')
|
|
297
388
|
end
|
|
389
|
+
|
|
390
|
+
return unless current_slot.is_a?(::Shoes::Shape)
|
|
391
|
+
|
|
392
|
+
current_slot.add_shape_command(['line_to', x, y])
|
|
298
393
|
end
|
|
299
394
|
|
|
300
395
|
# Not implemented yet: curve_to, arc_to
|
|
301
396
|
|
|
302
|
-
|
|
397
|
+
alias info puts
|
|
398
|
+
|
|
399
|
+
private
|
|
400
|
+
|
|
401
|
+
def render_index_if_defined_on_first_boot
|
|
402
|
+
return if @first_boot_finished
|
|
403
|
+
|
|
404
|
+
visit('/') if @routes['/'] == :index
|
|
405
|
+
|
|
406
|
+
@first_boot_finished = true
|
|
407
|
+
end
|
|
303
408
|
end
|
data/lib/shoes/constants.rb
CHANGED
data/lib/shoes/drawable.rb
CHANGED
|
@@ -54,12 +54,12 @@ class Shoes
|
|
|
54
54
|
def validate_as(prop_name, value)
|
|
55
55
|
prop_name = prop_name.to_s
|
|
56
56
|
hashes = shoes_style_hashes
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
h = hashes.detect { |hash| hash[:name] == prop_name }
|
|
59
59
|
raise(Shoes::Errors::NoSuchStyleError, "Can't find property #{prop_name.inspect} in #{self} property list: #{hashes.inspect}!") unless h
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
return value if h[:validator].nil?
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
# Pass both the property name and value to the validator block
|
|
64
64
|
h[:validator].call(value,prop_name)
|
|
65
65
|
end
|
|
@@ -215,7 +215,7 @@ class Shoes
|
|
|
215
215
|
# styles available with no features requested, pass nil to with_features.
|
|
216
216
|
def shoes_style_names(with_features: nil)
|
|
217
217
|
# No with_features given? Use the ones requested by this Shoes::App
|
|
218
|
-
with_features ||=
|
|
218
|
+
with_features ||= @app.features
|
|
219
219
|
parent_prop_names = self != Shoes::Drawable ? self.superclass.shoes_style_names(with_features:) : []
|
|
220
220
|
|
|
221
221
|
if with_features == :all
|
|
@@ -236,6 +236,24 @@ class Shoes
|
|
|
236
236
|
linkable_properties_hash[name.to_s] ||
|
|
237
237
|
(self != Shoes::Drawable && superclass.shoes_style_name?(name))
|
|
238
238
|
end
|
|
239
|
+
|
|
240
|
+
# Current_app is set every time a drawable is created - we don't want to keep a default
|
|
241
|
+
# long because it's possible for apps to alternate who is creating. So make sure it's
|
|
242
|
+
# not kept long, and used up when used once.
|
|
243
|
+
|
|
244
|
+
def with_current_app(app)
|
|
245
|
+
old_cur_app = @current_app
|
|
246
|
+
@current_app = app
|
|
247
|
+
ret = yield
|
|
248
|
+
@current_app = old_cur_app
|
|
249
|
+
ret
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def use_current_app
|
|
253
|
+
cur_app = @current_app
|
|
254
|
+
@current_app = nil
|
|
255
|
+
cur_app
|
|
256
|
+
end
|
|
239
257
|
end
|
|
240
258
|
|
|
241
259
|
# Every Shoes drawable has positioning properties
|
|
@@ -256,17 +274,20 @@ class Shoes
|
|
|
256
274
|
# Their value is set at drawable-create time.
|
|
257
275
|
DRAW_CONTEXT_STYLES = [:fill, :stroke, :strokewidth, :rotate, :transform, :translate]
|
|
258
276
|
|
|
259
|
-
|
|
277
|
+
include MarginHelper
|
|
260
278
|
|
|
261
279
|
def initialize(*args, **kwargs)
|
|
262
280
|
kwargs = margin_parse(kwargs)
|
|
263
281
|
log_init("Shoes::#{self.class.name}") unless @log
|
|
264
282
|
|
|
283
|
+
# Grab the current app, mark it as used
|
|
284
|
+
@app = self.is_a?(Shoes::App) ? self : Drawable.use_current_app
|
|
285
|
+
|
|
265
286
|
# First, get the list of allowed and disallowed styles for the given features
|
|
266
287
|
# and make sure no disallowed styles were given.
|
|
267
288
|
|
|
268
|
-
app_features =
|
|
269
|
-
this_app_styles = self.class.shoes_style_names.map(&:to_sym)
|
|
289
|
+
app_features = @app.features
|
|
290
|
+
this_app_styles = self.class.shoes_style_names(with_features: @app.features).map(&:to_sym)
|
|
270
291
|
not_this_app_styles = self.class.shoes_style_names(with_features: :all).map(&:to_sym) - this_app_styles
|
|
271
292
|
|
|
272
293
|
bad_styles = kwargs.keys & not_this_app_styles
|
|
@@ -309,8 +330,8 @@ class Shoes
|
|
|
309
330
|
end
|
|
310
331
|
end
|
|
311
332
|
|
|
312
|
-
this_drawable_styles = self.class.shoes_style_names.map(&:to_sym)
|
|
313
|
-
dc =
|
|
333
|
+
this_drawable_styles = self.class.shoes_style_names(with_features: @app.features).map(&:to_sym)
|
|
334
|
+
dc = @app.current_draw_context || {}
|
|
314
335
|
|
|
315
336
|
# Styles not passed as arguments can come from the draw context
|
|
316
337
|
|
|
@@ -318,11 +339,6 @@ class Shoes
|
|
|
318
339
|
# given as positional or keyword arguments?
|
|
319
340
|
draw_context_styles = (DRAW_CONTEXT_STYLES & this_drawable_styles) - supplied_args
|
|
320
341
|
unless draw_context_styles.empty?
|
|
321
|
-
# When we first call this, there is no parent. We don't want to set the parent
|
|
322
|
-
# yet because that will send a notification, and *that* should wait until after
|
|
323
|
-
# we've told the display service that this drawable was created. So instead
|
|
324
|
-
# we'll query the parent object's draw context directly.
|
|
325
|
-
|
|
326
342
|
draw_context_styles.each do |style|
|
|
327
343
|
dc_val = dc[style.to_s]
|
|
328
344
|
next if dc_val.nil?
|
|
@@ -363,7 +379,7 @@ class Shoes
|
|
|
363
379
|
|
|
364
380
|
generate_debug_id
|
|
365
381
|
|
|
366
|
-
parent =
|
|
382
|
+
parent = @app.current_slot
|
|
367
383
|
if self.class.expects_parent?
|
|
368
384
|
set_parent(parent, notify: false)
|
|
369
385
|
end
|
|
@@ -416,8 +432,8 @@ class Shoes
|
|
|
416
432
|
# @return [Shoes::App] the Shoes app
|
|
417
433
|
# @yield the block to call with the Shoes App as self
|
|
418
434
|
def app(&block)
|
|
419
|
-
|
|
420
|
-
|
|
435
|
+
@app.with_slot(self, &block) if block_given?
|
|
436
|
+
@app
|
|
421
437
|
end
|
|
422
438
|
|
|
423
439
|
private
|
|
@@ -471,8 +487,8 @@ class Shoes
|
|
|
471
487
|
send_shoes_event(*args, **kwargs, event_name:, target: linkable_id)
|
|
472
488
|
end
|
|
473
489
|
|
|
474
|
-
def shoes_style_values
|
|
475
|
-
all_property_names = self.class.shoes_style_names
|
|
490
|
+
def shoes_style_values(with_features: @app.features)
|
|
491
|
+
all_property_names = self.class.shoes_style_names(with_features:)
|
|
476
492
|
|
|
477
493
|
properties = {}
|
|
478
494
|
all_property_names.each do |prop|
|
data/lib/shoes/drawables/arc.rb
CHANGED
|
@@ -15,10 +15,10 @@ class Shoes
|
|
|
15
15
|
|
|
16
16
|
init_args :left, :top, :width, :height, :angle1, :angle2
|
|
17
17
|
def initialize(*args, **kwargs)
|
|
18
|
-
@draw_context = Shoes::App.instance.current_draw_context
|
|
19
|
-
|
|
20
18
|
super
|
|
21
19
|
|
|
20
|
+
@draw_context = @app.current_draw_context
|
|
21
|
+
|
|
22
22
|
create_display_drawable
|
|
23
23
|
end
|
|
24
24
|
|
data/lib/shoes/drawables/flow.rb
CHANGED
|
@@ -14,7 +14,7 @@ class Shoes
|
|
|
14
14
|
# Create the display-side drawable *before* instance_eval, which will add child drawables with their display drawables
|
|
15
15
|
create_display_drawable
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
@app.with_slot(self, &block) if block_given?
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
end
|
data/lib/shoes/drawables/line.rb
CHANGED
data/lib/shoes/drawables/link.rb
CHANGED
|
@@ -10,12 +10,22 @@ class Shoes
|
|
|
10
10
|
init_args # Empty by the time it reaches Drawable#initialize
|
|
11
11
|
def initialize(*args, **kwargs, &block)
|
|
12
12
|
@block = block
|
|
13
|
+
|
|
14
|
+
# Check if click is an internal route (starts with /)
|
|
15
|
+
click_value = kwargs[:click]
|
|
16
|
+
@internal_route = click_value.is_a?(String) && click_value.start_with?("/")
|
|
17
|
+
|
|
13
18
|
# We can't send a block to the display drawable, but we can send a boolean
|
|
14
|
-
|
|
19
|
+
# Also set has_block if we have an internal route (so display uses onclick, not href)
|
|
20
|
+
@has_block = !block.nil? || @internal_route
|
|
15
21
|
|
|
16
22
|
super
|
|
17
23
|
|
|
18
24
|
bind_self_event("click") do
|
|
25
|
+
if @internal_route
|
|
26
|
+
# Navigate to the internal route
|
|
27
|
+
app.visit(@click)
|
|
28
|
+
end
|
|
19
29
|
@block&.call
|
|
20
30
|
end
|
|
21
31
|
end
|
data/lib/shoes/drawables/oval.rb
CHANGED
|
@@ -18,10 +18,10 @@ class Shoes
|
|
|
18
18
|
init_args :left, :top
|
|
19
19
|
opt_init_args :radius, :height
|
|
20
20
|
def initialize(*args, **options)
|
|
21
|
-
@draw_context = Shoes::App.instance.current_draw_context
|
|
22
|
-
|
|
23
21
|
super # Parse any positional or keyword args
|
|
24
22
|
|
|
23
|
+
@draw_context = @app.current_draw_context
|
|
24
|
+
|
|
25
25
|
unless @left && @top && (@width || @height || @radius)
|
|
26
26
|
raise Shoes::Errors::InvalidAttributeValueError, "Oval requires left, top and one of (width, height, radius) to be specified!"
|
|
27
27
|
end
|
data/lib/shoes/drawables/rect.rb
CHANGED
|
@@ -8,10 +8,10 @@ class Shoes
|
|
|
8
8
|
init_args :left, :top, :width, :height
|
|
9
9
|
opt_init_args :curve
|
|
10
10
|
def initialize(*args, **kwargs)
|
|
11
|
-
@draw_context = Shoes::App.instance.current_draw_context
|
|
12
|
-
|
|
13
11
|
super
|
|
14
12
|
|
|
13
|
+
@draw_context = @app.current_draw_context
|
|
14
|
+
|
|
15
15
|
create_display_drawable
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -16,12 +16,12 @@ class Shoes
|
|
|
16
16
|
init_args # No positional args
|
|
17
17
|
def initialize(**kwargs, &block)
|
|
18
18
|
@shape_commands = []
|
|
19
|
-
@draw_context = Shoes::App.instance.current_draw_context
|
|
20
19
|
|
|
21
20
|
super
|
|
21
|
+
@draw_context = @app.current_draw_context
|
|
22
22
|
create_display_drawable
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
@app.with_slot(self, &block) if block_given?
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# The cmd should be an array of the form:
|
data/lib/shoes/drawables/slot.rb
CHANGED
|
@@ -62,8 +62,10 @@ class Shoes::Slot < Shoes::Drawable
|
|
|
62
62
|
# Look up the Shoes drawable and create it. But first set
|
|
63
63
|
# this slot as the current one so that draw context
|
|
64
64
|
# is handled properly.
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
@app.with_slot(self) do
|
|
66
|
+
Shoes::Drawable.with_current_app(self.app) do
|
|
67
|
+
instance = klass.new(*args, **kwargs, &block)
|
|
68
|
+
end
|
|
67
69
|
end
|
|
68
70
|
|
|
69
71
|
instance
|
|
@@ -173,6 +175,6 @@ class Shoes::Slot < Shoes::Drawable
|
|
|
173
175
|
raise(Shoes::Errors::InvalidAttributeValueError, "append requires a block!") unless block_given?
|
|
174
176
|
raise(Shoes::Errors::InvalidAttributeValueError, "Don't append to something that isn't a slot!") unless self.is_a?(Shoes::Slot)
|
|
175
177
|
|
|
176
|
-
|
|
178
|
+
@app.with_slot(self, &block)
|
|
177
179
|
end
|
|
178
180
|
end
|
|
@@ -15,7 +15,7 @@ class Shoes
|
|
|
15
15
|
|
|
16
16
|
# Create the display-side drawable *before* running the block.
|
|
17
17
|
# Then child drawables have a parent to add themselves to.
|
|
18
|
-
|
|
18
|
+
@app.with_slot(self, &block) if block_given?
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
end
|
data/lib/shoes/drawables/star.rb
CHANGED
|
@@ -67,7 +67,7 @@ class Shoes::Widget < Shoes::Slot
|
|
|
67
67
|
__widget_initialize(*args, **kwargs, &block)
|
|
68
68
|
|
|
69
69
|
# Do Widgets do this?
|
|
70
|
-
|
|
70
|
+
@app.with_slot(self, &block) if block
|
|
71
71
|
end
|
|
72
72
|
@midway_through_adding_initialize = false
|
|
73
73
|
end
|
data/lib/shoes.rb
CHANGED
|
@@ -7,47 +7,47 @@
|
|
|
7
7
|
# to handle the DSL and command-line parts of Shoes without knowing anything about how the
|
|
8
8
|
# display side works at all.
|
|
9
9
|
|
|
10
|
-
if RUBY_VERSION[0..2] <
|
|
11
|
-
Shoes::Log.logger(
|
|
10
|
+
if RUBY_VERSION[0..2] < '3.2'
|
|
11
|
+
Shoes::Log.logger('Shoes').error('Lacci (Scarpe, Shoes) requires Ruby 3.2 or higher!')
|
|
12
12
|
exit(-1)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
class Shoes; end
|
|
16
16
|
class Shoes::Error < StandardError; end
|
|
17
|
-
require_relative
|
|
17
|
+
require_relative 'shoes/errors'
|
|
18
18
|
|
|
19
|
-
require_relative
|
|
20
|
-
require_relative
|
|
19
|
+
require_relative 'shoes/constants'
|
|
20
|
+
require_relative 'shoes/ruby_extensions'
|
|
21
21
|
|
|
22
22
|
# Shoes adds some top-level methods and constants that can be used everywhere. Kernel is where they go.
|
|
23
23
|
module Kernel
|
|
24
24
|
include Shoes::Constants
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
require_relative
|
|
27
|
+
require_relative 'shoes/display_service'
|
|
28
28
|
|
|
29
29
|
# Pre-declare classes that get referenced outside their own require file
|
|
30
30
|
class Shoes::Drawable < Shoes::Linkable; end
|
|
31
31
|
class Shoes::Slot < Shoes::Drawable; end
|
|
32
32
|
class Shoes::Widget < Shoes::Slot; end
|
|
33
33
|
|
|
34
|
-
require_relative
|
|
35
|
-
require_relative
|
|
34
|
+
require_relative 'shoes/log'
|
|
35
|
+
require_relative 'shoes/colors'
|
|
36
36
|
|
|
37
|
-
require_relative
|
|
37
|
+
require_relative 'shoes/builtins'
|
|
38
38
|
|
|
39
|
-
require_relative
|
|
39
|
+
require_relative 'shoes/background'
|
|
40
40
|
|
|
41
|
-
require_relative
|
|
42
|
-
require_relative
|
|
43
|
-
require_relative
|
|
41
|
+
require_relative 'shoes/drawable'
|
|
42
|
+
require_relative 'shoes/app'
|
|
43
|
+
require_relative 'shoes/drawables'
|
|
44
44
|
|
|
45
|
-
require_relative
|
|
45
|
+
require_relative 'shoes/download'
|
|
46
46
|
|
|
47
47
|
# No easy way to tell at this point whether
|
|
48
48
|
# we will later load Shoes-Spec code, e.g.
|
|
49
49
|
# by running a segmented app with test code.
|
|
50
|
-
require_relative
|
|
50
|
+
require_relative 'shoes-spec'
|
|
51
51
|
|
|
52
52
|
# The module containing Shoes in all its glory.
|
|
53
53
|
# Shoes is a platform-independent GUI library, designed to create
|
|
@@ -55,6 +55,40 @@ require_relative "shoes-spec"
|
|
|
55
55
|
#
|
|
56
56
|
class Shoes
|
|
57
57
|
class << self
|
|
58
|
+
attr_accessor :APPS
|
|
59
|
+
|
|
60
|
+
# Track the most recently defined Shoes subclass for the inheritance pattern
|
|
61
|
+
# e.g., class Book < Shoes; end; Shoes.app
|
|
62
|
+
attr_accessor :pending_app_class
|
|
63
|
+
|
|
64
|
+
# When someone does `class MyApp < Shoes`, track it
|
|
65
|
+
def inherited(subclass)
|
|
66
|
+
# Only track direct subclasses of Shoes, not Shoes::App, Shoes::Drawable, etc.
|
|
67
|
+
# Those have their own inheritance tracking
|
|
68
|
+
if self == ::Shoes
|
|
69
|
+
Shoes.pending_app_class = subclass
|
|
70
|
+
end
|
|
71
|
+
super
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Class-level url method for defining routes in Shoes subclasses
|
|
75
|
+
# e.g., class Book < Shoes; url '/', :index; end
|
|
76
|
+
def url(path, method_name)
|
|
77
|
+
@class_routes ||= {}
|
|
78
|
+
if path.is_a?(String) && path.include?('(')
|
|
79
|
+
# Convert string patterns like '/page/(\d+)' to regex
|
|
80
|
+
regex = Regexp.new("^#{path.gsub(/\(.*?\)/, '(.*?)')}$")
|
|
81
|
+
@class_routes[regex] = method_name
|
|
82
|
+
else
|
|
83
|
+
@class_routes[path] = method_name
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Get the routes defined on this class
|
|
88
|
+
def class_routes
|
|
89
|
+
@class_routes ||= {}
|
|
90
|
+
end
|
|
91
|
+
|
|
58
92
|
# Creates a Shoes app with a new window. The block parameter is used to create
|
|
59
93
|
# drawables and set up handlers. Arguments are passed to Shoes::App.new internally.
|
|
60
94
|
#
|
|
@@ -78,7 +112,7 @@ class Shoes
|
|
|
78
112
|
# @return [void]
|
|
79
113
|
# @see Shoes::App#new
|
|
80
114
|
def app(
|
|
81
|
-
title:
|
|
115
|
+
title: 'Shoes!',
|
|
82
116
|
width: 480,
|
|
83
117
|
height: 420,
|
|
84
118
|
resizable: true,
|
|
@@ -87,6 +121,47 @@ class Shoes
|
|
|
87
121
|
)
|
|
88
122
|
f = [features].flatten # Make sure this is a list, not a single symbol
|
|
89
123
|
app = Shoes::App.new(title:, width:, height:, resizable:, features: f, &app_code_body)
|
|
124
|
+
|
|
125
|
+
# If there's a pending Shoes subclass (e.g., class Book < Shoes), use it
|
|
126
|
+
if Shoes.pending_app_class
|
|
127
|
+
subclass = Shoes.pending_app_class
|
|
128
|
+
Shoes.pending_app_class = nil # Clear it so it doesn't affect future apps
|
|
129
|
+
|
|
130
|
+
# Include the subclass as a module to get its instance methods
|
|
131
|
+
# This works because we're extending the singleton class
|
|
132
|
+
methods_to_copy = subclass.instance_methods(false)
|
|
133
|
+
|
|
134
|
+
methods_to_copy.each do |method_name|
|
|
135
|
+
# Get source location and use eval to redefine - but that's fragile
|
|
136
|
+
# Instead, let's use a delegation pattern with the app as context
|
|
137
|
+
|
|
138
|
+
# Read the method's arity and create a proper wrapper
|
|
139
|
+
um = subclass.instance_method(method_name)
|
|
140
|
+
|
|
141
|
+
# Define a wrapper that will eval the original method body in app's context
|
|
142
|
+
# This is a bit hacky but works: we store the subclass and call via instance_eval
|
|
143
|
+
app.define_singleton_method(method_name) do |*args, &block|
|
|
144
|
+
# Create a temporary subclass instance that delegates to app for Shoes methods
|
|
145
|
+
temp = subclass.allocate
|
|
146
|
+
temp.instance_variable_set(:@__shoes_app__, self)
|
|
147
|
+
|
|
148
|
+
# Define method_missing on the temp to delegate Shoes DSL calls to the app
|
|
149
|
+
temp.define_singleton_method(:method_missing) do |name, *a, **kw, &b|
|
|
150
|
+
@__shoes_app__.send(name, *a, **kw, &b)
|
|
151
|
+
end
|
|
152
|
+
temp.define_singleton_method(:respond_to_missing?) { |*| true }
|
|
153
|
+
|
|
154
|
+
# Call the original method on temp (which delegates DSL calls to app)
|
|
155
|
+
temp.send(method_name, *args, &block)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Copy routes from the subclass to the app
|
|
160
|
+
subclass.class_routes.each do |path, method_name|
|
|
161
|
+
app.url(path, method_name)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
90
165
|
app.init
|
|
91
166
|
app.run
|
|
92
167
|
nil
|
|
@@ -125,7 +200,7 @@ class Shoes
|
|
|
125
200
|
proc do |path|
|
|
126
201
|
load path
|
|
127
202
|
true
|
|
128
|
-
end
|
|
203
|
+
end
|
|
129
204
|
]
|
|
130
205
|
end
|
|
131
206
|
|
|
@@ -145,4 +220,6 @@ class Shoes
|
|
|
145
220
|
@file_loaders = loaders
|
|
146
221
|
end
|
|
147
222
|
end
|
|
223
|
+
|
|
224
|
+
Shoes.APPS ||= []
|
|
148
225
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lacci
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Marco Concetto Rudilosso
|
|
8
8
|
- Noah Gibbs
|
|
9
|
-
autorequire:
|
|
10
9
|
bindir: exe
|
|
11
10
|
cert_chain: []
|
|
12
|
-
date:
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
13
12
|
dependencies:
|
|
14
13
|
- !ruby/object:Gem::Dependency
|
|
15
14
|
name: scarpe-components
|
|
@@ -25,7 +24,6 @@ dependencies:
|
|
|
25
24
|
- - "~>"
|
|
26
25
|
- !ruby/object:Gem::Version
|
|
27
26
|
version: 0.4.0
|
|
28
|
-
description:
|
|
29
27
|
email:
|
|
30
28
|
- marcoc.r@outlook.com
|
|
31
29
|
- the.codefolio.guy@gmail.com
|
|
@@ -94,7 +92,6 @@ licenses:
|
|
|
94
92
|
metadata:
|
|
95
93
|
homepage_uri: https://github.com/scarpe-team/scarpe
|
|
96
94
|
changelog_uri: https://github.com/scarpe-team/scarpe/blob/main/CHANGELOG.md
|
|
97
|
-
post_install_message:
|
|
98
95
|
rdoc_options: []
|
|
99
96
|
require_paths:
|
|
100
97
|
- lib
|
|
@@ -109,8 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
109
106
|
- !ruby/object:Gem::Version
|
|
110
107
|
version: '0'
|
|
111
108
|
requirements: []
|
|
112
|
-
rubygems_version: 3.
|
|
113
|
-
signing_key:
|
|
109
|
+
rubygems_version: 3.6.9
|
|
114
110
|
specification_version: 4
|
|
115
111
|
summary: Lacci - a portable Shoes DSL with switchable display backends, part of Scarpe
|
|
116
112
|
test_files: []
|