lacci 0.3.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/scarpe_cli.rb +0 -1
- data/lib/lacci/version.rb +1 -1
- data/lib/scarpe/niente/app.rb +12 -1
- data/lib/scarpe/niente/display_service.rb +5 -1
- data/lib/scarpe/niente/drawable.rb +2 -0
- data/lib/scarpe/niente/shoes_spec.rb +10 -5
- data/lib/scarpe/niente.rb +15 -2
- data/lib/shoes/app.rb +204 -105
- data/lib/shoes/constants.rb +24 -2
- data/lib/shoes/display_service.rb +43 -4
- data/lib/shoes/drawable.rb +326 -36
- data/lib/shoes/drawables/arc.rb +4 -26
- data/lib/shoes/drawables/arrow.rb +3 -23
- data/lib/shoes/drawables/border.rb +28 -0
- data/lib/shoes/drawables/button.rb +5 -21
- data/lib/shoes/drawables/check.rb +7 -3
- data/lib/shoes/drawables/document_root.rb +4 -4
- data/lib/shoes/drawables/edit_box.rb +6 -5
- data/lib/shoes/drawables/edit_line.rb +5 -4
- data/lib/shoes/drawables/flow.rb +4 -6
- data/lib/shoes/drawables/font_helper.rb +62 -0
- data/lib/shoes/drawables/image.rb +2 -2
- data/lib/shoes/drawables/line.rb +3 -6
- data/lib/shoes/drawables/link.rb +16 -9
- data/lib/shoes/drawables/list_box.rb +8 -5
- data/lib/shoes/drawables/oval.rb +48 -0
- data/lib/shoes/drawables/para.rb +106 -18
- data/lib/shoes/drawables/progress.rb +2 -1
- data/lib/shoes/drawables/radio.rb +5 -3
- data/lib/shoes/drawables/rect.rb +7 -6
- data/lib/shoes/drawables/shape.rb +4 -3
- data/lib/shoes/drawables/slot.rb +102 -9
- data/lib/shoes/drawables/stack.rb +7 -12
- data/lib/shoes/drawables/star.rb +9 -31
- data/lib/shoes/drawables/text_drawable.rb +93 -34
- data/lib/shoes/drawables/video.rb +3 -2
- data/lib/shoes/drawables/widget.rb +9 -4
- data/lib/shoes/drawables.rb +2 -1
- data/lib/shoes/errors.rb +13 -3
- data/lib/shoes/margin_helper.rb +79 -0
- data/lib/shoes.rb +98 -20
- metadata +11 -15
- data/lib/scarpe/niente/logger.rb +0 -29
- data/lib/shoes/drawables/span.rb +0 -27
- data/lib/shoes/spacing.rb +0 -9
data/lib/shoes/drawable.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
2
|
+
require_relative 'margin_helper'
|
|
3
3
|
class Shoes
|
|
4
4
|
# Shoes::Drawable
|
|
5
5
|
#
|
|
@@ -15,7 +15,7 @@ class Shoes
|
|
|
15
15
|
include Shoes::Colors
|
|
16
16
|
|
|
17
17
|
# All Drawables have these so they go in Shoes::Drawable and are inherited
|
|
18
|
-
@shoes_events = ["parent", "destroy", "prop_change"]
|
|
18
|
+
@shoes_events = ["parent", "destroy", "prop_change", "hover", "leave", "motion"]
|
|
19
19
|
|
|
20
20
|
class << self
|
|
21
21
|
attr_accessor :drawable_classes
|
|
@@ -43,7 +43,8 @@ class Shoes
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def drawable_class_by_name(name)
|
|
46
|
-
|
|
46
|
+
name = name.to_s
|
|
47
|
+
drawable_classes.detect { |k| k.dsl_name == name }
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
def is_widget_class?(name)
|
|
@@ -59,7 +60,8 @@ class Shoes
|
|
|
59
60
|
|
|
60
61
|
return value if h[:validator].nil?
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
# Pass both the property name and value to the validator block
|
|
64
|
+
h[:validator].call(value,prop_name)
|
|
63
65
|
end
|
|
64
66
|
|
|
65
67
|
# Return a list of Shoes events for this class.
|
|
@@ -67,18 +69,65 @@ class Shoes
|
|
|
67
69
|
# @return Array[String] the list of event names
|
|
68
70
|
def get_shoes_events
|
|
69
71
|
if @shoes_events.nil?
|
|
70
|
-
raise
|
|
72
|
+
raise Shoes::Errors::UnknownEventsForClassError, "Drawable type #{self} hasn't defined its list of Shoes events!"
|
|
71
73
|
end
|
|
72
74
|
|
|
73
75
|
@shoes_events
|
|
74
76
|
end
|
|
75
77
|
|
|
78
|
+
# Return whether Shoes events have already been registered for this class
|
|
79
|
+
#
|
|
80
|
+
# @return [Boolean] true if events have been registered, false if not
|
|
81
|
+
def registered_shoes_events?
|
|
82
|
+
!@shoes_events.nil?
|
|
83
|
+
end
|
|
84
|
+
|
|
76
85
|
# Set the list of Shoes event names that are allowed for this class.
|
|
77
86
|
#
|
|
78
87
|
# @param args [Array] an array of event names, which will be coerced to Strings
|
|
79
88
|
# @return [void]
|
|
80
89
|
def shoes_events(*args)
|
|
81
|
-
@shoes_events
|
|
90
|
+
if @shoes_events
|
|
91
|
+
raise Shoes::Errors::DoubleRegisteredShoesEventError, "Registering shoes events #{args.inspect} for class #{self} but already registered events as #{@shoes_events.inspect}!"
|
|
92
|
+
end
|
|
93
|
+
@shoes_events = args.map(&:to_s) + self.superclass.get_shoes_events
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Require supplying these Shoes style values as positional arguments to
|
|
97
|
+
# initialize. Initialize will get the arg list, then set the specified styles
|
|
98
|
+
# if args are given for them. @see opt_init_args for additional non-required
|
|
99
|
+
# init args.
|
|
100
|
+
#
|
|
101
|
+
# @param args [Array<String,Symbol>] an array of Shoes style names
|
|
102
|
+
# @return [void]
|
|
103
|
+
def init_args(*args)
|
|
104
|
+
raise Shoes::Errors::BadArgumentListError, "Positional init args already set for #{self}!" if @required_init_args
|
|
105
|
+
@required_init_args = args.map(&:to_s)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Allow supplying these Shoes style values as optional positional arguments to
|
|
109
|
+
# initialize after the mandatory args. @see init_args for setting required
|
|
110
|
+
# init args.
|
|
111
|
+
#
|
|
112
|
+
# @param args [Array<String,Symbol>] an array of Shoes style names
|
|
113
|
+
# @return [void]
|
|
114
|
+
def opt_init_args(*args)
|
|
115
|
+
raise Shoes::Errors::BadArgumentListError, "Positional init args already set for #{self}!" if @opt_init_args
|
|
116
|
+
@opt_init_args = args.map(&:to_s)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Return the list of style names for required init args for this class
|
|
120
|
+
#
|
|
121
|
+
# @return [Array<String>] the array of style names as strings
|
|
122
|
+
def required_init_args
|
|
123
|
+
@required_init_args ||= [] # TODO: eventually remove the ||= here
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Return the list of style names for optional init args for this class
|
|
127
|
+
#
|
|
128
|
+
# @return [Array<String>] the array of style names as strings
|
|
129
|
+
def optional_init_args
|
|
130
|
+
@opt_init_args ||= []
|
|
82
131
|
end
|
|
83
132
|
|
|
84
133
|
# Assign a new Shoes Drawable ID number, starting from 1.
|
|
@@ -125,24 +174,56 @@ class Shoes
|
|
|
125
174
|
# Shoes styles in Shoes Linkables are automatically sync'd with the display side objects.
|
|
126
175
|
# If a block is passed to shoes_style, that's the validation for the property. It should
|
|
127
176
|
# convert a given value to a valid value for the property or throw an exception.
|
|
128
|
-
|
|
177
|
+
#
|
|
178
|
+
# If feature is non-nil, it's the feature that an app must request in order to see this
|
|
179
|
+
# property.
|
|
180
|
+
#
|
|
181
|
+
# @param name [String,Symbol] the style name
|
|
182
|
+
# @param feature [Symbol,NilClass] the feature that must be defined for an app to request this style, or nil
|
|
183
|
+
# @block if block is given, call it to map the given style value to a valid value, or raise an exception
|
|
184
|
+
def shoes_style(name, feature: nil, &validator)
|
|
129
185
|
name = name.to_s
|
|
130
186
|
|
|
131
187
|
return if linkable_properties_hash[name]
|
|
132
188
|
|
|
133
|
-
linkable_properties << { name: name, validator: }
|
|
189
|
+
linkable_properties << { name: name, validator:, feature: }
|
|
134
190
|
linkable_properties_hash[name] = true
|
|
135
191
|
end
|
|
136
192
|
|
|
137
|
-
# Add these names as Shoes styles
|
|
138
|
-
def shoes_styles(*names)
|
|
139
|
-
names.each { |n| shoes_style(n) }
|
|
193
|
+
# Add these names as Shoes styles with the given validator and feature, if any
|
|
194
|
+
def shoes_styles(*names, feature: nil, &validator)
|
|
195
|
+
names.each { |n| shoes_style(n, feature:, &validator) }
|
|
140
196
|
end
|
|
141
197
|
|
|
142
|
-
|
|
143
|
-
|
|
198
|
+
# Query what feature, if any, is required to use a specific shoes_style.
|
|
199
|
+
# If no specific feature is needed, nil will be returned.
|
|
200
|
+
def feature_for_shoes_style(style_name)
|
|
201
|
+
style_name = style_name.to_s
|
|
202
|
+
lp = linkable_properties.detect { |prop| prop[:name] == style_name }
|
|
203
|
+
return lp[:feature] if lp
|
|
144
204
|
|
|
145
|
-
|
|
205
|
+
# If we get to the top of the superclass tree and we didn't find it, it's not here
|
|
206
|
+
if self.class == ::Shoes::Drawable
|
|
207
|
+
raise Shoes::Errors::NoSuchStyleError, "Can't find information for style #{style_name.inspect}!"
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
super
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Return a list of shoes_style names with the given features. If with_features is nil,
|
|
214
|
+
# return them with a list of features for the current Shoes::App. For the list of
|
|
215
|
+
# styles available with no features requested, pass nil to with_features.
|
|
216
|
+
def shoes_style_names(with_features: nil)
|
|
217
|
+
# No with_features given? Use the ones requested by this Shoes::App
|
|
218
|
+
with_features ||= @app.features
|
|
219
|
+
parent_prop_names = self != Shoes::Drawable ? self.superclass.shoes_style_names(with_features:) : []
|
|
220
|
+
|
|
221
|
+
if with_features == :all
|
|
222
|
+
subclass_props = linkable_properties
|
|
223
|
+
else
|
|
224
|
+
subclass_props = linkable_properties.select { |prop| !prop[:feature] || with_features.include?(prop[:feature]) }
|
|
225
|
+
end
|
|
226
|
+
parent_prop_names | subclass_props.map { |prop| prop[:name] }
|
|
146
227
|
end
|
|
147
228
|
|
|
148
229
|
def shoes_style_hashes
|
|
@@ -155,33 +236,188 @@ class Shoes
|
|
|
155
236
|
linkable_properties_hash[name.to_s] ||
|
|
156
237
|
(self != Shoes::Drawable && superclass.shoes_style_name?(name))
|
|
157
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
|
|
158
257
|
end
|
|
159
258
|
|
|
259
|
+
# Every Shoes drawable has positioning properties
|
|
260
|
+
shoes_styles :top, :left, :width, :height
|
|
261
|
+
|
|
262
|
+
# Margins around drawable
|
|
263
|
+
shoes_styles :margin, :margin_top, :margin_bottom, :margin_left, :margin_right
|
|
264
|
+
|
|
265
|
+
# Padding around drawable
|
|
266
|
+
shoes_styles :padding, :padding_top, :padding_bottom, :padding_left, :padding_right
|
|
267
|
+
|
|
160
268
|
# Shoes uses a "hidden" style property for hide/show
|
|
161
269
|
shoes_style :hidden
|
|
162
270
|
|
|
163
271
|
attr_reader :debug_id
|
|
164
272
|
|
|
273
|
+
# These styles can be set to a current per-slot value and inherited from parent slots.
|
|
274
|
+
# Their value is set at drawable-create time.
|
|
275
|
+
DRAW_CONTEXT_STYLES = [:fill, :stroke, :strokewidth, :rotate, :transform, :translate]
|
|
276
|
+
|
|
277
|
+
include MarginHelper
|
|
278
|
+
|
|
165
279
|
def initialize(*args, **kwargs)
|
|
166
|
-
|
|
280
|
+
kwargs = margin_parse(kwargs)
|
|
281
|
+
log_init("Shoes::#{self.class.name}") unless @log
|
|
167
282
|
|
|
168
|
-
|
|
283
|
+
# Grab the current app, mark it as used
|
|
284
|
+
@app = self.is_a?(Shoes::App) ? self : Drawable.use_current_app
|
|
285
|
+
|
|
286
|
+
# First, get the list of allowed and disallowed styles for the given features
|
|
287
|
+
# and make sure no disallowed styles were given.
|
|
288
|
+
|
|
289
|
+
app_features = @app.features
|
|
290
|
+
this_app_styles = self.class.shoes_style_names(with_features: @app.features).map(&:to_sym)
|
|
291
|
+
not_this_app_styles = self.class.shoes_style_names(with_features: :all).map(&:to_sym) - this_app_styles
|
|
292
|
+
|
|
293
|
+
bad_styles = kwargs.keys & not_this_app_styles
|
|
294
|
+
unless bad_styles.empty?
|
|
295
|
+
features_needed = bad_styles.map { |s| self.class.feature_for_shoes_style(s) }.uniq
|
|
296
|
+
raise Shoes::Errors::UnsupportedFeatureError, "The style(s) #{bad_styles.inspect} are only defined for applications that request specific features: #{features_needed.inspect} (you requested #{app_features.inspect})!"
|
|
297
|
+
end
|
|
169
298
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
299
|
+
# Next, check positional arguments and make sure the correct number and type
|
|
300
|
+
# were passed and match positional args with style names.
|
|
301
|
+
|
|
302
|
+
supplied_args = kwargs.keys
|
|
303
|
+
|
|
304
|
+
req_args = self.class.required_init_args
|
|
305
|
+
opt_args = self.class.optional_init_args
|
|
306
|
+
pos_args = req_args + opt_args
|
|
307
|
+
if req_args != ["any"]
|
|
308
|
+
if args.size > pos_args.size
|
|
309
|
+
raise Shoes::Errors::BadArgumentListError, "Too many arguments given for #{self.class}#initialize! #{args.inspect}"
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
if args.size == 0
|
|
313
|
+
# It's fine to use keyword args instead, but we should make sure they're actually there
|
|
314
|
+
needed_args = req_args.map(&:to_sym) - kwargs.keys
|
|
315
|
+
unless needed_args.empty?
|
|
316
|
+
raise Shoes::Errors::BadArgumentListError, "Keyword arguments for #{self.class}#initialize should also supply #{needed_args.inspect}! #{args.inspect}"
|
|
317
|
+
end
|
|
318
|
+
elsif args.size < req_args.size
|
|
319
|
+
raise Shoes::Errors::BadArgumentListError, "Too few arguments given for #{self.class}#initialize! #{args.inspect}"
|
|
178
320
|
end
|
|
321
|
+
|
|
322
|
+
# Set each positional argument
|
|
323
|
+
args.each.with_index do |val, idx|
|
|
324
|
+
style_name = pos_args[idx]
|
|
325
|
+
next if style_name.nil? || style_name == "" # It's possible to have non-style positional args
|
|
326
|
+
|
|
327
|
+
val = self.class.validate_as(style_name, args[idx])
|
|
328
|
+
instance_variable_set("@#{style_name}", val)
|
|
329
|
+
supplied_args << style_name.to_sym
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
this_drawable_styles = self.class.shoes_style_names(with_features: @app.features).map(&:to_sym)
|
|
334
|
+
dc = @app.current_draw_context || {}
|
|
335
|
+
|
|
336
|
+
# Styles not passed as arguments can come from the draw context
|
|
337
|
+
|
|
338
|
+
# What styles are in the draw context, are used by this drawable, and weren't
|
|
339
|
+
# given as positional or keyword arguments?
|
|
340
|
+
draw_context_styles = (DRAW_CONTEXT_STYLES & this_drawable_styles) - supplied_args
|
|
341
|
+
unless draw_context_styles.empty?
|
|
342
|
+
draw_context_styles.each do |style|
|
|
343
|
+
dc_val = dc[style.to_s]
|
|
344
|
+
next if dc_val.nil?
|
|
345
|
+
|
|
346
|
+
val = self.class.validate_as(style, dc[style.to_s])
|
|
347
|
+
instance_variable_set("@#{style}", val)
|
|
348
|
+
supplied_args << style
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Styles that were *not* passed should be set to defaults
|
|
353
|
+
|
|
354
|
+
default_styles = Shoes::Drawable.drawable_default_styles[self.class]
|
|
355
|
+
|
|
356
|
+
# No arg specified for a property with a default value? Set it to default.
|
|
357
|
+
(default_styles.keys - supplied_args).each do |key|
|
|
358
|
+
val = self.class.validate_as(key, default_styles[key])
|
|
359
|
+
instance_variable_set("@#{key}", val)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# If we have a keyword arg for a style, set it as specified.
|
|
363
|
+
(this_drawable_styles & kwargs.keys).each do |key|
|
|
364
|
+
val = self.class.validate_as(key, kwargs[key])
|
|
365
|
+
instance_variable_set("@#{key}", val)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# We'd like to avoid unexpected keywords. But we're not disciplined enough to
|
|
369
|
+
# raise an error by default yet. Non-style keywords passed to Drawable#initialize
|
|
370
|
+
# are deprecated at this point, but I need to hunt down the last of them
|
|
371
|
+
# and prevent them.
|
|
372
|
+
unexpected = (kwargs.keys - this_drawable_styles)
|
|
373
|
+
unless unexpected.empty?
|
|
374
|
+
STDERR.puts "Unexpected non-style keyword(s) in #{self.class} initialize: #{unexpected.inspect}"
|
|
179
375
|
end
|
|
180
376
|
|
|
181
377
|
super(linkable_id: Shoes::Drawable.allocate_drawable_id)
|
|
182
378
|
Shoes::Drawable.register_drawable_id(self.linkable_id, self)
|
|
183
379
|
|
|
184
380
|
generate_debug_id
|
|
381
|
+
|
|
382
|
+
parent = @app.current_slot
|
|
383
|
+
if self.class.expects_parent?
|
|
384
|
+
set_parent(parent, notify: false)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
unless self.class.registered_shoes_events?
|
|
388
|
+
# No Shoes events declared and we're creating an instance?
|
|
389
|
+
# Default to no class-specific events.
|
|
390
|
+
self.class.shoes_events
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# Binding the motion events here isn't perfect.
|
|
394
|
+
# What about drawables like SubscriptionItem that
|
|
395
|
+
# have no motion events? With the current Lacci
|
|
396
|
+
# implementation, the answer is that those events
|
|
397
|
+
# will never be sent. Calling .hover on one will
|
|
398
|
+
# be useless, harmless, and allowed. If you want
|
|
399
|
+
# to make it disallowed, you can do something like
|
|
400
|
+
# define a SubscriptionItem#hover that raises an
|
|
401
|
+
# exception instead.
|
|
402
|
+
|
|
403
|
+
bind_self_event("hover") do
|
|
404
|
+
@hover&.call
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
bind_self_event("leave") do
|
|
408
|
+
@leave&.call
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
bind_self_event("motion") do |x, y|
|
|
412
|
+
@motion&.call(x, y)
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
def self.expects_parent?
|
|
417
|
+
return false if [::Shoes::App, ::Shoes::DocumentRoot].include?(self)
|
|
418
|
+
return false if self < ::Shoes::TextDrawable
|
|
419
|
+
|
|
420
|
+
true
|
|
185
421
|
end
|
|
186
422
|
|
|
187
423
|
# Calling stack.app or drawable.app will execute the block
|
|
@@ -196,8 +432,8 @@ class Shoes
|
|
|
196
432
|
# @return [Shoes::App] the Shoes app
|
|
197
433
|
# @yield the block to call with the Shoes App as self
|
|
198
434
|
def app(&block)
|
|
199
|
-
|
|
200
|
-
|
|
435
|
+
@app.with_slot(self, &block) if block_given?
|
|
436
|
+
@app
|
|
201
437
|
end
|
|
202
438
|
|
|
203
439
|
private
|
|
@@ -223,13 +459,14 @@ class Shoes
|
|
|
223
459
|
private
|
|
224
460
|
|
|
225
461
|
def validate_event_name(event_name)
|
|
226
|
-
|
|
227
|
-
|
|
462
|
+
events = self.class.get_shoes_events
|
|
463
|
+
unless events.include?(event_name.to_s)
|
|
464
|
+
raise Shoes::Errors::UnregisteredShoesEventError, "Drawable #{self.inspect} tried to bind Shoes event #{event_name}, which is not in #{events.inspect}!"
|
|
228
465
|
end
|
|
229
466
|
end
|
|
230
467
|
|
|
231
468
|
def bind_self_event(event_name, &block)
|
|
232
|
-
raise(Shoes::Errors::
|
|
469
|
+
raise(Shoes::Errors::NoSuchLinkableIdError, "Drawable has no linkable_id! #{inspect}") unless linkable_id
|
|
233
470
|
|
|
234
471
|
validate_event_name(event_name)
|
|
235
472
|
|
|
@@ -250,8 +487,8 @@ class Shoes
|
|
|
250
487
|
send_shoes_event(*args, **kwargs, event_name:, target: linkable_id)
|
|
251
488
|
end
|
|
252
489
|
|
|
253
|
-
def shoes_style_values
|
|
254
|
-
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:)
|
|
255
492
|
|
|
256
493
|
properties = {}
|
|
257
494
|
all_property_names.each do |prop|
|
|
@@ -292,10 +529,11 @@ class Shoes
|
|
|
292
529
|
klass_name = self.class.name.delete_prefix("Scarpe::").delete_prefix("Shoes::")
|
|
293
530
|
|
|
294
531
|
is_widget = Shoes::Drawable.is_widget_class?(klass_name)
|
|
532
|
+
parent_id = @parent&.linkable_id
|
|
295
533
|
|
|
296
534
|
# Should we send an event so this can be discovered from someplace other than
|
|
297
535
|
# the DisplayService?
|
|
298
|
-
::Shoes::DisplayService.display_service.create_display_drawable_for(klass_name, self.linkable_id, shoes_style_values, is_widget:)
|
|
536
|
+
::Shoes::DisplayService.display_service.create_display_drawable_for(klass_name, self.linkable_id, shoes_style_values, parent_id:, is_widget:)
|
|
299
537
|
end
|
|
300
538
|
|
|
301
539
|
public
|
|
@@ -303,11 +541,17 @@ class Shoes
|
|
|
303
541
|
attr_reader :parent
|
|
304
542
|
attr_reader :destroyed
|
|
305
543
|
|
|
306
|
-
|
|
544
|
+
# Set the Drawable's parent drawable. Notify the display service
|
|
545
|
+
# that the parent has changed unless instructed not to.
|
|
546
|
+
# We don't notify when first creating the Drawable. The create
|
|
547
|
+
# event that is first sent will include the parent.
|
|
548
|
+
def set_parent(new_parent, notify: true)
|
|
307
549
|
@parent&.remove_child(self)
|
|
308
550
|
new_parent&.add_child(self)
|
|
309
551
|
@parent = new_parent
|
|
310
|
-
|
|
552
|
+
return unless notify
|
|
553
|
+
|
|
554
|
+
send_shoes_event(new_parent&.linkable_id, event_name: "parent", target: linkable_id)
|
|
311
555
|
end
|
|
312
556
|
|
|
313
557
|
# Removes the element from the Shoes::Drawable tree and removes all event subscriptions
|
|
@@ -336,6 +580,28 @@ class Shoes
|
|
|
336
580
|
self.hidden = !self.hidden
|
|
337
581
|
end
|
|
338
582
|
|
|
583
|
+
# Set the hover handler. Not every drawable may do something useful with this.
|
|
584
|
+
#
|
|
585
|
+
# @yield A block to be called when the cursor moves to be over the drawable.
|
|
586
|
+
def hover(&block)
|
|
587
|
+
@hover = block
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
# Set the leave handler. Not every drawable may do something useful with this.
|
|
591
|
+
#
|
|
592
|
+
# @yield A block to be called when the cursor moves to be off of the drawable.
|
|
593
|
+
def leave(&block)
|
|
594
|
+
@leave = block
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
# Set the motion handler, called with x and y coordinates as params.
|
|
598
|
+
# Not every drawable may do something useful with this.
|
|
599
|
+
#
|
|
600
|
+
# @yield A block to be called when the cursor moves around over the drawable.
|
|
601
|
+
def motion(&block)
|
|
602
|
+
@motion = block
|
|
603
|
+
end
|
|
604
|
+
|
|
339
605
|
# We use method_missing to auto-create Shoes style getters and setters.
|
|
340
606
|
def method_missing(name, *args, **kwargs, &block)
|
|
341
607
|
name_s = name.to_s
|
|
@@ -344,7 +610,7 @@ class Shoes
|
|
|
344
610
|
prop_name = name_s[0..-2]
|
|
345
611
|
if self.class.shoes_style_name?(prop_name)
|
|
346
612
|
self.class.define_method(name) do |new_value|
|
|
347
|
-
raise(Shoes::Errors::
|
|
613
|
+
raise(Shoes::Errors::NoSuchLinkableIdError, "Trying to set Shoes styles in a #{self.class} with no linkable ID!") unless linkable_id
|
|
348
614
|
|
|
349
615
|
new_value = self.class.validate_as(prop_name, new_value)
|
|
350
616
|
instance_variable_set("@" + prop_name, new_value)
|
|
@@ -357,7 +623,7 @@ class Shoes
|
|
|
357
623
|
|
|
358
624
|
if self.class.shoes_style_name?(name_s)
|
|
359
625
|
self.class.define_method(name) do
|
|
360
|
-
raise(Shoes::Errors::
|
|
626
|
+
raise(Shoes::Errors::NoSuchLinkableIdError, "Trying to get Shoes styles in an object with no linkable ID! #{inspect}") unless linkable_id
|
|
361
627
|
|
|
362
628
|
instance_variable_get("@" + name_s)
|
|
363
629
|
end
|
|
@@ -376,5 +642,29 @@ class Shoes
|
|
|
376
642
|
|
|
377
643
|
super
|
|
378
644
|
end
|
|
645
|
+
|
|
646
|
+
def self.convert_to_integer(value, attribute_name)
|
|
647
|
+
begin
|
|
648
|
+
value = Integer(value)
|
|
649
|
+
raise Shoes::Errors::InvalidAttributeValueError, "Negative number '#{value}' not allowed for attribute '#{attribute_name}'" if value < 0
|
|
650
|
+
|
|
651
|
+
value
|
|
652
|
+
rescue ArgumentError
|
|
653
|
+
error_message = "Invalid value '#{value}' provided for attribute '#{attribute_name}'. The value should be a number."
|
|
654
|
+
raise Shoes::Errors::InvalidAttributeValueError, error_message
|
|
655
|
+
end
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
def self.convert_to_float(value, attribute_name)
|
|
659
|
+
begin
|
|
660
|
+
value = Float(value)
|
|
661
|
+
raise Shoes::Errors::InvalidAttributeValueError, "Negative number '#{value}' not allowed for attribute '#{attribute_name}'" if value < 0
|
|
662
|
+
|
|
663
|
+
value
|
|
664
|
+
rescue ArgumentError
|
|
665
|
+
error_message = "Invalid value '#{value}' provided for attribute '#{attribute_name}'. The value should be a number."
|
|
666
|
+
raise Shoes::Errors::InvalidAttributeValueError, error_message
|
|
667
|
+
end
|
|
668
|
+
end
|
|
379
669
|
end
|
|
380
670
|
end
|
data/lib/shoes/drawables/arc.rb
CHANGED
|
@@ -13,37 +13,15 @@ class Shoes
|
|
|
13
13
|
shoes_style(prop) { |val| convert_to_float(val, prop) }
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
init_args :left, :top, :width, :height, :angle1, :angle2
|
|
17
|
+
def initialize(*args, **kwargs)
|
|
19
18
|
super
|
|
20
|
-
self.left, self.top, self.width, self.height, self.angle1, self.angle2 = args
|
|
21
|
-
|
|
22
|
-
create_display_drawable
|
|
23
|
-
end
|
|
24
19
|
|
|
25
|
-
|
|
26
|
-
begin
|
|
27
|
-
value = Integer(value)
|
|
28
|
-
raise Shoes::Errors::InvalidAttributeValueError, "Negative number '#{value}' not allowed for attribute '#{attribute_name}'" if value < 0
|
|
20
|
+
@draw_context = @app.current_draw_context
|
|
29
21
|
|
|
30
|
-
|
|
31
|
-
rescue ArgumentError
|
|
32
|
-
error_message = "Invalid value '#{value}' provided for attribute '#{attribute_name}'. The value should be a number."
|
|
33
|
-
raise Shoes::Errors::InvalidAttributeValueError, error_message
|
|
34
|
-
end
|
|
22
|
+
create_display_drawable
|
|
35
23
|
end
|
|
36
24
|
|
|
37
|
-
def self.convert_to_float(value, attribute_name)
|
|
38
|
-
begin
|
|
39
|
-
value = Float(value)
|
|
40
|
-
raise Shoes::Errors::InvalidAttributeValueError, "Negative number '#{value}' not allowed for attribute '#{attribute_name}'" if value < 0
|
|
41
25
|
|
|
42
|
-
value
|
|
43
|
-
rescue ArgumentError
|
|
44
|
-
error_message = "Invalid value '#{value}' provided for attribute '#{attribute_name}'. The value should be a number."
|
|
45
|
-
raise Shoes::Errors::InvalidAttributeValueError, error_message
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
26
|
end
|
|
49
27
|
end
|
|
@@ -9,33 +9,13 @@ class Shoes
|
|
|
9
9
|
shoes_style(prop) { |val| val.is_a?(Hash) ? val : convert_to_integer(val, prop) }
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
init_args :left, :top, :width
|
|
13
|
+
def initialize(*args, **kwargs)
|
|
15
14
|
super
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
options = args[0]
|
|
19
|
-
self.left = options[:left]
|
|
20
|
-
self.top = options[:top]
|
|
21
|
-
self.width = options[:width]
|
|
22
|
-
else
|
|
23
|
-
self.left, self.top, self.width = args
|
|
24
|
-
end
|
|
16
|
+
@draw_context = @app.current_draw_context
|
|
25
17
|
|
|
26
18
|
create_display_drawable
|
|
27
19
|
end
|
|
28
|
-
|
|
29
|
-
def self.convert_to_integer(value, attribute_name)
|
|
30
|
-
begin
|
|
31
|
-
value = Integer(value)
|
|
32
|
-
raise Shoes::Errors::InvalidAttributeValueError, "Negative number '#{value}' not allowed for attribute '#{attribute_name}'" if value < 0
|
|
33
|
-
|
|
34
|
-
value
|
|
35
|
-
rescue ArgumentError
|
|
36
|
-
error_message = "Invalid value '#{value}' provided for attribute '#{attribute_name}'. The value should be a number."
|
|
37
|
-
raise Shoes::Errors::InvalidAttributeValueError, error_message
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
20
|
end
|
|
41
21
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Shoes
|
|
4
|
+
class Border < Shoes::Drawable
|
|
5
|
+
# Shoes style with verification or value mapping:
|
|
6
|
+
# shoes_style(:left) { |val| convert_to_integer(val, "left") }
|
|
7
|
+
|
|
8
|
+
shoes_styles :stroke, :strokewidth # Write your shoes styles here
|
|
9
|
+
|
|
10
|
+
shoes_style(:strokewidth) { |val| convert_to_integer(val, "strokewidth") }
|
|
11
|
+
shoes_style(:curve) { |val| convert_to_integer(val, "curve") }
|
|
12
|
+
|
|
13
|
+
Shoes::Drawable.drawable_default_styles[Shoes::Border][:stroke] = :black
|
|
14
|
+
Shoes::Drawable.drawable_default_styles[Shoes::Border][:strokewidth] = 1
|
|
15
|
+
Shoes::Drawable.drawable_default_styles[Shoes::Border][:curve] = 0
|
|
16
|
+
|
|
17
|
+
opt_init_args :stroke, :strokewidth, :curve
|
|
18
|
+
def initialize(*args, **kwargs)
|
|
19
|
+
super
|
|
20
|
+
@draw_context = @app.current_draw_context
|
|
21
|
+
|
|
22
|
+
create_display_drawable
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
class Shoes
|
|
4
4
|
class Button < Shoes::Drawable
|
|
5
|
-
include Shoes::Log
|
|
6
5
|
shoes_styles :text, :width, :height, :top, :left, :color, :padding_top, :padding_bottom, :text_color, :size, :font_size, :tooltip
|
|
7
|
-
shoes_events :click
|
|
6
|
+
shoes_events :click
|
|
8
7
|
|
|
8
|
+
init_args :text
|
|
9
9
|
# Creates a new Button object.
|
|
10
10
|
#
|
|
11
11
|
# @param text [String] The text displayed on the button.
|
|
@@ -32,25 +32,16 @@ class Shoes
|
|
|
32
32
|
# )
|
|
33
33
|
# }
|
|
34
34
|
# end
|
|
35
|
-
def initialize(
|
|
36
|
-
font_size: nil, tooltip: nil, &block)
|
|
37
|
-
|
|
38
|
-
log_init("Button")
|
|
39
|
-
|
|
35
|
+
def initialize(*args, **kwargs, &block)
|
|
40
36
|
# Properties passed as positional args, not keywords, don't get auto-set
|
|
41
|
-
@text = text
|
|
42
37
|
@block = block
|
|
43
38
|
|
|
44
39
|
super
|
|
45
40
|
|
|
46
|
-
# Bind to a handler named "click"
|
|
41
|
+
# Bind block to a handler named "click"
|
|
47
42
|
bind_self_event("click") do
|
|
48
43
|
@log.debug("Button clicked, calling handler") if @block
|
|
49
|
-
@block&.call
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
bind_self_event("hover") do
|
|
53
|
-
@hover&.call
|
|
44
|
+
@block&.call if @block
|
|
54
45
|
end
|
|
55
46
|
|
|
56
47
|
create_display_drawable
|
|
@@ -62,12 +53,5 @@ class Shoes
|
|
|
62
53
|
def click(&block)
|
|
63
54
|
@block = block
|
|
64
55
|
end
|
|
65
|
-
|
|
66
|
-
# Set the hover handler
|
|
67
|
-
#
|
|
68
|
-
# @yield A block to be called when the cursor moves to be over the button.
|
|
69
|
-
def hover(&block)
|
|
70
|
-
@hover = block
|
|
71
|
-
end
|
|
72
56
|
end
|
|
73
57
|
end
|