glimmer-dsl-swt 4.18.6.2 → 4.18.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +4 -4
  4. data/VERSION +1 -1
  5. data/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md +64 -6
  6. data/docs/reference/GLIMMER_SAMPLES.md +71 -0
  7. data/glimmer-dsl-swt.gemspec +16 -6
  8. data/lib/glimmer/dsl/swt/animation_expression.rb +1 -1
  9. data/lib/glimmer/dsl/swt/custom_shape_expression.rb +61 -0
  10. data/lib/glimmer/dsl/swt/custom_widget_expression.rb +1 -1
  11. data/lib/glimmer/dsl/swt/dsl.rb +1 -0
  12. data/lib/glimmer/dsl/swt/expand_item_expression.rb +4 -4
  13. data/lib/glimmer/dsl/swt/image_expression.rb +1 -1
  14. data/lib/glimmer/dsl/swt/multiply_expression.rb +1 -1
  15. data/lib/glimmer/dsl/swt/shape_expression.rb +1 -1
  16. data/lib/glimmer/dsl/swt/transform_expression.rb +1 -1
  17. data/lib/glimmer/dsl/swt/widget_expression.rb +2 -1
  18. data/lib/glimmer/swt/custom/shape.rb +473 -180
  19. data/lib/glimmer/swt/custom/shape/image.rb +7 -9
  20. data/lib/glimmer/swt/custom/shape/path.rb +38 -29
  21. data/lib/glimmer/swt/custom/shape/path_segment.rb +21 -19
  22. data/lib/glimmer/swt/custom/shape/polygon.rb +24 -8
  23. data/lib/glimmer/swt/custom/shape/polyline.rb +5 -0
  24. data/lib/glimmer/swt/custom/shape/rectangle.rb +10 -19
  25. data/lib/glimmer/swt/display_proxy.rb +1 -1
  26. data/lib/glimmer/swt/message_box_proxy.rb +1 -1
  27. data/lib/glimmer/swt/shell_proxy.rb +1 -1
  28. data/lib/glimmer/swt/tab_folder_proxy.rb +52 -0
  29. data/lib/glimmer/swt/transform_proxy.rb +1 -1
  30. data/lib/glimmer/swt/widget_proxy.rb +1 -1
  31. data/lib/glimmer/ui/custom_shape.rb +281 -0
  32. data/samples/elaborate/meta_sample.rb +5 -5
  33. data/samples/elaborate/metronome.rb +177 -0
  34. data/samples/elaborate/stock_ticker.rb +0 -6
  35. data/samples/elaborate/tetris.rb +1 -12
  36. data/samples/elaborate/tetris/model/game.rb +3 -0
  37. data/samples/elaborate/tetris/view/bevel.rb +78 -0
  38. data/samples/elaborate/tetris/view/block.rb +6 -29
  39. data/samples/hello/hello_canvas.rb +3 -0
  40. data/samples/hello/hello_canvas_animation_data_binding.rb +66 -0
  41. data/samples/hello/hello_canvas_data_binding.rb +24 -3
  42. data/samples/hello/hello_canvas_path.rb +1 -1
  43. data/samples/hello/hello_custom_shape.rb +78 -0
  44. data/samples/hello/hello_shape.rb +71 -0
  45. data/samples/hello/hello_spinner.rb +7 -2
  46. data/sounds/metronome-down.wav +0 -0
  47. data/sounds/metronome-up.wav +0 -0
  48. metadata +14 -4
@@ -33,7 +33,7 @@ module Glimmer
33
33
  Glimmer::SWT::Custom::Animation.new(parent)
34
34
  end
35
35
 
36
- def add_content(parent, &block)
36
+ def add_content(parent, keyword, *args, &block)
37
37
  super
38
38
  parent.post_add_content
39
39
  end
@@ -0,0 +1,61 @@
1
+ # Copyright (c) 2007-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer'
23
+ require 'glimmer/dsl/expression'
24
+ require 'glimmer/dsl/parent_expression'
25
+ require 'glimmer/dsl/top_level_expression'
26
+ require 'glimmer/ui/custom_shape'
27
+ require 'glimmer/swt/custom/code_text'
28
+ require 'glimmer/swt/custom/checkbox_group'
29
+
30
+ module Glimmer
31
+ module DSL
32
+ module SWT
33
+ class CustomShapeExpression < Expression
34
+ # TODO Make custom shapes automatically generate static expressions
35
+ include ParentExpression
36
+ include TopLevelExpression
37
+
38
+ def can_interpret?(parent, keyword, *args, &block)
39
+ !!UI::CustomShape.for(keyword)
40
+ end
41
+
42
+ def interpret(parent, keyword, *args, &block)
43
+ options = args.last.is_a?(Hash) ? args.pop : {}
44
+ UI::CustomShape.for(keyword).new(parent, *args, options, &block).tap do |new_custom_shape|
45
+ new_custom_shape.body_root.paint_pixel_by_pixel(&block) if block&.parameters&.count == 2
46
+ end
47
+ end
48
+
49
+ def add_content(parent, keyword, *args, &block)
50
+ # TODO consider avoiding source_location
51
+ return if block&.parameters&.count == 2
52
+ if block.source_location == parent.content&.__getobj__.source_location
53
+ parent.content.call(parent) unless parent.content.called?
54
+ else
55
+ super
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -51,7 +51,7 @@ module Glimmer
51
51
  end
52
52
  end
53
53
 
54
- def add_content(parent, &block)
54
+ def add_content(parent, keyword, *args, &block)
55
55
  # TODO consider avoiding source_location
56
56
  return if block&.parameters&.count == 2
57
57
  if block.source_location == parent.content&.__getobj__.source_location
@@ -57,6 +57,7 @@ module Glimmer
57
57
  widget
58
58
  custom_widget
59
59
  shape
60
+ custom_shape
60
61
  ]
61
62
  )
62
63
  end
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2007-2021 Andy Maleh
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -49,7 +49,7 @@ module Glimmer
49
49
  Glimmer::SWT::ExpandItemProxy.new(parent, args)
50
50
  end
51
51
 
52
- def add_content(parent, &block)
52
+ def add_content(parent, keyword, *args, &block)
53
53
  super
54
54
  parent.post_add_content
55
55
  end
@@ -57,7 +57,7 @@ module Glimmer
57
57
  end
58
58
  end
59
59
 
60
- def add_content(parent, &block)
60
+ def add_content(parent, keyword, *args, &block)
61
61
  return if @create_pixel_by_pixel || block&.parameters&.count == 2
62
62
  super
63
63
  parent.post_add_content
@@ -39,7 +39,7 @@ module Glimmer
39
39
  Glimmer::SWT::TransformProxy.new(parent, *args, multiply: true)
40
40
  end
41
41
 
42
- def add_content(parent, &block)
42
+ def add_content(parent, keyword, *args, &block)
43
43
  super
44
44
  parent.post_add_content
45
45
  end
@@ -40,7 +40,7 @@ module Glimmer
40
40
  Glimmer::SWT::Custom::Shape.create(parent, keyword, *args, &block)
41
41
  end
42
42
 
43
- def add_content(parent, &block)
43
+ def add_content(parent, keyword, *args, &block)
44
44
  super
45
45
  parent.post_add_content
46
46
  end
@@ -41,7 +41,7 @@ module Glimmer
41
41
  Glimmer::SWT::TransformProxy.new(parent, *args)
42
42
  end
43
43
 
44
- def add_content(parent, &block)
44
+ def add_content(parent, keyword, *args, &block)
45
45
  super
46
46
  parent.post_add_content
47
47
  end
@@ -46,7 +46,7 @@ module Glimmer
46
46
  end
47
47
  end
48
48
 
49
- def add_content(parent, &block)
49
+ def add_content(parent, keyword, *args, &block)
50
50
  return if block&.parameters&.count == 2
51
51
  super
52
52
  parent.post_add_content
@@ -69,3 +69,4 @@ require 'glimmer/swt/table_column_proxy'
69
69
  require 'glimmer/swt/sash_form_proxy'
70
70
  require 'glimmer/swt/styled_text_proxy'
71
71
  require 'glimmer/swt/date_time_proxy'
72
+ require 'glimmer/swt/tab_folder_proxy'
@@ -73,6 +73,7 @@ module Glimmer
73
73
  end
74
74
 
75
75
  def valid?(parent, keyword, *args, &block)
76
+ return true if keyword.to_s == 'shape'
76
77
  gc_instance_methods.include?(method_name(keyword, arg_options(args))) ||
77
78
  constants.include?(keyword.to_s.camelcase(:upper).to_sym)
78
79
  end
@@ -126,7 +127,7 @@ module Glimmer
126
127
  end
127
128
  end
128
129
 
129
- attr_reader :drawable, :parent, :name, :args, :options, :shapes
130
+ attr_reader :drawable, :parent, :name, :args, :options, :shapes, :properties
130
131
  attr_accessor :extent
131
132
 
132
133
  def initialize(parent, keyword, *args, &property_block)
@@ -134,7 +135,7 @@ module Glimmer
134
135
  @drawable = @parent.is_a?(Drawable) ? @parent : @parent.drawable
135
136
  @name = keyword
136
137
  @options = self.class.arg_options(args, extract: true)
137
- @method_name = self.class.method_name(keyword, @options)
138
+ @method_name = self.class.method_name(keyword, @options) unless keyword.to_s == 'shape'
138
139
  @args = args
139
140
  @properties = {}
140
141
  @shapes = [] # nested shapes
@@ -170,7 +171,13 @@ module Glimmer
170
171
 
171
172
  # The bounding box top-left x, y, width, height in absolute positioning
172
173
  def bounds
173
- org.eclipse.swt.graphics.Rectangle.new(absolute_x, absolute_y, calculated_width, calculated_height)
174
+ bounds_dependencies = [absolute_x, absolute_y, calculated_width, calculated_height]
175
+ if bounds_dependencies != @bounds_dependencies
176
+ # avoid repeating calculations
177
+ absolute_x, absolute_y, calculated_width, calculated_height = @bounds_dependencies = bounds_dependencies
178
+ @bounds = org.eclipse.swt.graphics.Rectangle.new(absolute_x, absolute_y, calculated_width, calculated_height)
179
+ end
180
+ @bounds
174
181
  end
175
182
 
176
183
  # The bounding box top-left x and y
@@ -180,7 +187,13 @@ module Glimmer
180
187
 
181
188
  # The bounding box width and height (as a Point object with x being width and y being height)
182
189
  def size
183
- org.eclipse.swt.graphics.Point.new(calculated_width, calculated_height)
190
+ size_dependencies = [calculated_width, calculated_height]
191
+ if size_dependencies != @size_dependencies
192
+ # avoid repeating calculations
193
+ calculated_width, calculated_height = @size_dependencies = size_dependencies
194
+ @size = org.eclipse.swt.graphics.Point.new(calculated_width, calculated_height)
195
+ end
196
+ @size
184
197
  end
185
198
 
186
199
  def extent
@@ -213,12 +226,12 @@ module Glimmer
213
226
  def move_by(x_delta, y_delta)
214
227
  if respond_to?(:x) && respond_to?(:y) && respond_to?(:x=) && respond_to?(:y=)
215
228
  if default_x?
216
- self.default_x_delta += x_delta
229
+ self.x_delta += x_delta
217
230
  else
218
231
  self.x += x_delta
219
232
  end
220
233
  if default_y?
221
- self.default_y_delta += y_delta
234
+ self.y_delta += y_delta
222
235
  else
223
236
  self.y += y_delta
224
237
  end
@@ -227,7 +240,7 @@ module Glimmer
227
240
 
228
241
  def content(&block)
229
242
  Glimmer::SWT::DisplayProxy.instance.auto_exec do
230
- Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::SWT::ShapeExpression.new, &block)
243
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::SWT::ShapeExpression.new, @name, &block)
231
244
  calculated_args_changed!(children: false)
232
245
  end
233
246
  end
@@ -248,7 +261,8 @@ module Glimmer
248
261
  end
249
262
  end
250
263
 
251
- def apply_property_arg_conversions(method_name, property, args)
264
+ def apply_property_arg_conversions(property, args)
265
+ method_name = attribute_setter(property)
252
266
  args = args.dup
253
267
  the_java_method = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.detect {|m| m.name == method_name}
254
268
  return args if the_java_method.nil?
@@ -262,10 +276,23 @@ module Glimmer
262
276
  args[0] = args.dup
263
277
  args[1..-1] = []
264
278
  end
279
+ if method_name.to_s == 'setAntialias' && [nil, true, false].include?(args.first)
280
+ args[0] = case args.first
281
+ when true
282
+ args[0] = :on
283
+ when false
284
+ args[0] = :off
285
+ when nil
286
+ args[0] = :default
287
+ end
288
+ end
265
289
  if args.first.is_a?(Symbol) || args.first.is_a?(::String)
266
290
  if the_java_method.parameter_types.first == org.eclipse.swt.graphics.Color.java_class
267
291
  args[0] = ColorProxy.new(args[0])
268
292
  end
293
+ if method_name.to_s == 'setLineStyle'
294
+ args[0] = "line_#{args[0]}" if !args[0].to_s.downcase.start_with?('line_')
295
+ end
269
296
  if the_java_method.parameter_types.first == Java::int.java_class
270
297
  args[0] = SWTProxy.constant(args[0])
271
298
  end
@@ -293,10 +320,10 @@ module Glimmer
293
320
  end
294
321
  @pattern_args ||= {}
295
322
  pattern_type = method_name.to_s.match(/set(.+)Pattern/)[1]
296
- if args.first.is_a?(Pattern)
323
+ if args.first.is_a?(org.eclipse.swt.graphics.Pattern)
297
324
  new_args = @pattern_args[pattern_type]
298
325
  else
299
- new_args = args.first.is_a?(Display) ? args : ([DisplayProxy.instance.swt_display] + args)
326
+ new_args = args.first.is_a?(org.eclipse.swt.widgets.Display) ? args : ([DisplayProxy.instance.swt_display] + args)
300
327
  @pattern_args[pattern_type] = new_args.dup
301
328
  end
302
329
  args[0] = pattern(*new_args, type: pattern_type)
@@ -335,10 +362,16 @@ module Glimmer
335
362
  end
336
363
 
337
364
  def apply_shape_arg_defaults!
338
- self.x = :default if current_parameter_name?(:x) && x.nil?
339
- self.y = :default if current_parameter_name?(:y) && y.nil?
340
- self.dest_x = :default if current_parameter_name?(:dest_x) && dest_x.nil?
341
- self.dest_y = :default if current_parameter_name?(:dest_y) && dest_y.nil?
365
+ if current_parameter_name?(:dest_x) && dest_x.nil?
366
+ self.dest_x = :default
367
+ elsif parameter_name?(:x) && x.nil?
368
+ self.x = :default
369
+ end
370
+ if current_parameter_name?(:dest_y) && dest_y.nil?
371
+ self.dest_y = :default
372
+ elsif parameter_name?(:y) && y.nil?
373
+ self.y = :default
374
+ end
342
375
  self.width = :default if current_parameter_name?(:width) && width.nil?
343
376
  self.height = :default if current_parameter_name?(:height) && height.nil?
344
377
  if @name.include?('rectangle') && round? && @args.size.between?(4, 5)
@@ -383,7 +416,7 @@ module Glimmer
383
416
 
384
417
  # parameter names for arguments to pass to SWT GC.xyz method for rendering shape (e.g. draw_image(image, x, y) yields :image, :x, :y parameter names)
385
418
  def parameter_names
386
- []
419
+ [:x, :y, :width, :height]
387
420
  end
388
421
 
389
422
  # subclasses may override to specify location parameter names if different from x and y (e.g. all polygon points are location parameters)
@@ -408,6 +441,10 @@ module Glimmer
408
441
  parameter_names.index(attribute_name.to_s.to_sym)
409
442
  end
410
443
 
444
+ def get_parameter_attribute(attribute_name)
445
+ @args[parameter_index(ruby_attribute_getter(attribute_name))]
446
+ end
447
+
411
448
  def set_parameter_attribute(attribute_name, *args)
412
449
  @args[parameter_index(ruby_attribute_getter(attribute_name))] = args.size == 1 ? args.first : args
413
450
  end
@@ -423,31 +460,43 @@ module Glimmer
423
460
  args.pop if !options.nil? && !options[:redraw].nil?
424
461
  perform_redraw = @perform_redraw
425
462
  perform_redraw = options[:redraw] if perform_redraw.nil? && !options.nil?
426
- perform_redraw = true if perform_redraw.nil?
463
+ perform_redraw ||= true
464
+ property_change = nil
465
+ ruby_attribute_getter_name = ruby_attribute_getter(attribute_name)
466
+ ruby_attribute_setter_name = ruby_attribute_setter(attribute_name)
427
467
  if parameter_name?(attribute_name)
428
- set_parameter_attribute(attribute_name, *args)
429
- elsif (respond_to?(attribute_name, super: true) and respond_to?(ruby_attribute_setter(attribute_name), super: true))
430
- self.send(ruby_attribute_setter(attribute_name), *args)
468
+ return if ruby_attribute_getter_name == (args.size == 1 ? args.first : args)
469
+ set_parameter_attribute(ruby_attribute_getter_name, *args)
470
+ elsif (respond_to?(attribute_name, super: true) and respond_to?(ruby_attribute_setter_name, super: true))
471
+ return if self.send(ruby_attribute_getter_name) == (args.size == 1 ? args.first : args)
472
+ self.send(ruby_attribute_setter_name, *args)
431
473
  else
432
- @properties[ruby_attribute_getter(attribute_name)] = args
474
+ # TODO consider this optimization of preconverting args (removing conversion from other methods) to reject equal args
475
+ args = apply_property_arg_conversions(ruby_attribute_getter_name, args)
476
+ return if @properties[ruby_attribute_getter_name] == args
477
+ @properties[ruby_attribute_getter_name] = args
478
+ property_change = true
433
479
  end
434
480
  if @content_added && perform_redraw && !drawable.is_disposed
435
- @calculated_paint_args = false
436
- if is_a?(PathSegment)
437
- root_path&.calculated_path_args = @calculated_path_args = false
438
- calculated_args_changed!
439
- root_path&.calculated_args_changed!
440
- end
441
- attribute_name = ruby_attribute_getter(attribute_name)
442
- if location_parameter_names.map(&:to_s).include?(attribute_name)
443
- @calculated_args = nil
444
- parent.calculated_args_changed_for_defaults! if parent.is_a?(Shape)
445
- end
446
- if ['width', 'height'].include?(attribute_name)
447
- calculated_args_changed_for_defaults!
481
+ redrawn = false
482
+ unless property_change
483
+ @calculated_paint_args = false
484
+ if is_a?(PathSegment)
485
+ root_path&.calculated_path_args = @calculated_path_args = false
486
+ calculated_args_changed!
487
+ root_path&.calculated_args_changed!
488
+ end
489
+ if location_parameter_names.map(&:to_s).include?(ruby_attribute_getter_name)
490
+ calculated_args_changed!(children: true)
491
+ redrawn = parent.calculated_args_changed_for_defaults! if parent.is_a?(Shape)
492
+ end
493
+ if ['width', 'height'].include?(ruby_attribute_getter_name)
494
+ redrawn = calculated_args_changed_for_defaults!
495
+ end
448
496
  end
449
497
  # TODO consider redrawing an image proxy's gc in the future
450
- drawable.redraw unless drawable.is_a?(ImageProxy)
498
+ # TODO consider ensuring only a single redraw happens for a hierarchy of nested shapes
499
+ drawable.redraw unless redrawn || drawable.is_a?(ImageProxy)
451
500
  end
452
501
  end
453
502
 
@@ -458,10 +507,25 @@ module Glimmer
458
507
  elsif (respond_to?(attribute_name, super: true) and respond_to?(ruby_attribute_setter(attribute_name), super: true))
459
508
  self.send(attribute_name)
460
509
  else
461
- @properties.symbolize_keys[attribute_name.to_s.to_sym]
510
+ @properties[attribute_name.to_s]
462
511
  end
463
512
  end
464
513
 
514
+ # Sets data just like SWT widgets
515
+ def set_data(key=nil, value)
516
+ @data ||= {}
517
+ @data[key] = value
518
+ end
519
+ alias setData set_data # for compatibility with SWT APIs
520
+
521
+ # Gets data just like SWT widgets
522
+ def get_data(key=nil)
523
+ @data ||= {}
524
+ @data[key]
525
+ end
526
+ alias getData get_data # for compatibility with SWT APIs
527
+ alias data get_data # for compatibility with SWT APIs
528
+
465
529
  def method_missing(method_name, *args, &block)
466
530
  if method_name.to_s.end_with?('=')
467
531
  set_attribute(method_name, *args)
@@ -503,7 +567,7 @@ module Glimmer
503
567
  pattern_args(type: 'foreground')
504
568
  end
505
569
 
506
- def dispose(dispose_images: true, dispose_patterns: true)
570
+ def dispose(dispose_images: true, dispose_patterns: true, redraw: true)
507
571
  shapes.each { |shape| shape.is_a?(Shape::Path) && shape.dispose }
508
572
  if dispose_patterns
509
573
  @background_pattern&.dispose
@@ -516,10 +580,80 @@ module Glimmer
516
580
  @image = nil
517
581
  end
518
582
  @parent.shapes.delete(self)
583
+ drawable.redraw if redraw && !drawable.is_a?(ImageProxy)
584
+ end
585
+
586
+ # Indicate if this is a container shape (meaning a shape bag that is just there to contain nested shapes, but doesn't render anything of its own)
587
+ def container?
588
+ @name == 'shape'
589
+ end
590
+
591
+ # Indicate if this is a composite shape (meaning a shape that contains nested shapes like a rectangle with ovals inside it)
592
+ def composite?
593
+ !shapes.empty?
594
+ end
595
+
596
+ # ordered from closest to farthest parent
597
+ def parent_shapes
598
+ if @parent_shapes.nil?
599
+ if parent.is_a?(Drawable)
600
+ @parent_shapes = []
601
+ else
602
+ @parent_shapes = parent.parent_shapes + [parent]
603
+ end
604
+ end
605
+ @parent_shapes
606
+ end
607
+
608
+ # ordered from closest to farthest parent
609
+ def parent_shape_containers
610
+ if @parent_shape_containers.nil?
611
+ if parent.is_a?(Drawable)
612
+ @parent_shape_containers = []
613
+ elsif !parent.container?
614
+ @parent_shape_containers = parent.parent_shape_containers
615
+ else
616
+ @parent_shape_containers = parent.parent_shape_containers + [parent]
617
+ end
618
+ end
619
+ @parent_shape_containers
620
+ end
621
+
622
+ # ordered from closest to farthest parent
623
+ def parent_shape_composites
624
+ if @parent_shape_composites.nil?
625
+ if parent.is_a?(Drawable)
626
+ @parent_shape_composites = []
627
+ elsif !parent.container?
628
+ @parent_shape_composites = parent.parent_shape_composites
629
+ else
630
+ @parent_shape_composites = parent.parent_shape_composites + [parent]
631
+ end
632
+ end
633
+ @parent_shape_composites
634
+ end
635
+
636
+ def convert_properties!
637
+ if @properties != @converted_properties
638
+ @properties.each do |property, args|
639
+ @properties[property] = apply_property_arg_conversions(property, args)
640
+ end
641
+ @converted_properties = @properties.dup
642
+ end
643
+ end
644
+
645
+ def converted_properties
646
+ convert_properties!
647
+ @properties
648
+ end
649
+
650
+ def all_parent_properties
651
+ @all_parent_properties ||= parent_shape_containers.reverse.reduce({}) do |all_properties, parent_shape|
652
+ all_properties.merge(parent_shape.converted_properties)
653
+ end
519
654
  end
520
655
 
521
656
  def paint(paint_event)
522
- # pre-paint children an extra-time first when default width/height need to be calculated for defaults
523
657
  paint_children(paint_event) if default_width? || default_height?
524
658
  paint_self(paint_event)
525
659
  # re-paint children from scratch in the special case of pre-calculating parent width/height to re-center within new parent dimensions
@@ -532,28 +666,30 @@ module Glimmer
532
666
 
533
667
  def paint_self(paint_event)
534
668
  @painting = true
535
- calculate_paint_args!
536
- @original_properties_backup = {}
537
- @properties.each do |property, args|
538
- method_name = attribute_setter(property)
539
- @original_properties_backup[method_name] = paint_event.gc.send(method_name.sub('set', 'get')) rescue nil
540
- paint_event.gc.send(method_name, *args)
541
- if property == 'transform' && args.first.is_a?(TransformProxy)
542
- args.first.swt_transform.dispose
669
+ unless container?
670
+ calculate_paint_args!
671
+ @original_gc_properties = {} # this stores GC properties before making calls to updates TODO avoid using in pixel graphics
672
+ @properties.each do |property, args|
673
+ method_name = attribute_setter(property)
674
+ @original_gc_properties[method_name] = paint_event.gc.send(method_name.sub('set', 'get')) rescue nil
675
+ paint_event.gc.send(method_name, *args)
676
+ if property == 'transform' && args.first.is_a?(TransformProxy)
677
+ args.first.swt_transform.dispose
678
+ end
543
679
  end
680
+ ensure_extent(paint_event)
544
681
  end
545
- ensure_extent(paint_event)
546
- if !@calculated_args || parent_shape_absolute_location_changed?
547
- @calculated_args = calculated_args
548
- end
549
- # paint unless parent's calculated args are not calculated yet, meaning it is about to get painted and trigger a paint on this child anyways
550
- paint_event.gc.send(@method_name, *@calculated_args) unless parent.is_a?(Shape) && !parent.calculated_args?
551
- @original_properties_backup.each do |method_name, value|
552
- paint_event.gc.send(method_name, value)
682
+ @calculated_args ||= calculate_args!
683
+ unless container?
684
+ # paint unless parent's calculated args are not calculated yet, meaning it is about to get painted and trigger a paint on this child anyways
685
+ paint_event.gc.send(@method_name, *@calculated_args) unless (parent.is_a?(Shape) && !parent.calculated_args?)
686
+ @original_gc_properties.each do |method_name, value|
687
+ paint_event.gc.send(method_name, value)
688
+ end
553
689
  end
554
690
  @painting = false
555
691
  rescue => e
556
- Glimmer::Config.logger.error {"Error encountered in painting shape (#{self.inspect}) with calculated args (#{@calculated_args}) and args (#{@args})"}
692
+ Glimmer::Config.logger.error {"Error encountered in painting shape (#{self.inspect}) with method (#{@method_name}) calculated args (#{@calculated_args}) and args (#{@args})"}
557
693
  Glimmer::Config.logger.error {e.full_message}
558
694
  ensure
559
695
  @painting = false
@@ -590,26 +726,27 @@ module Glimmer
590
726
  end
591
727
  end
592
728
 
593
- def parent_shape_absolute_location_changed?
594
- (parent.is_a?(Shape) && (parent.absolute_x != @parent_absolute_x || parent.absolute_y != @parent_absolute_y))
595
- end
596
-
597
729
  def calculated_args_changed!(children: true)
598
730
  # TODO add a children: true option to enable setting to false to avoid recalculating children args
599
731
  @calculated_args = nil
600
732
  shapes.each(&:calculated_args_changed!) if children
601
733
  end
602
734
 
735
+ # Notifies object that calculated args changed for defaults. Returns true if redrawing and false otherwise.
603
736
  def calculated_args_changed_for_defaults!
604
737
  has_default_dimensions = default_width? || default_height?
605
738
  parent_calculated_args_changed_for_defaults = has_default_dimensions
606
- @calculated_args = nil if default_x? || default_y? || has_default_dimensions
739
+ calculated_args_changed!(children: false) if default_x? || default_y? || has_default_dimensions
607
740
  if has_default_dimensions && parent.is_a?(Shape)
608
741
  parent.calculated_args_changed_for_defaults!
609
742
  elsif @content_added && !drawable.is_disposed
610
743
  # TODO consider optimizing in the future if needed by ensuring one redraw for all parents in the hierarchy at the end instead of doing one per parent that needs it
611
- drawable.redraw if !@painting && !drawable.is_a?(ImageProxy)
744
+ if !@painting && !drawable.is_a?(ImageProxy)
745
+ drawable.redraw
746
+ return true
747
+ end
612
748
  end
749
+ false
613
750
  end
614
751
 
615
752
  def calculated_args?
@@ -617,68 +754,101 @@ module Glimmer
617
754
  end
618
755
 
619
756
  # args translated to absolute coordinates
620
- def calculated_args
621
- return @args if !default_x? && !default_y? && !default_width? && !default_height? && parent.is_a?(Drawable)
622
- # Note: Must set x and move_by because not all shapes have a real x and some must translate all their points with move_by
623
- # TODO change that by setting a bounding box for all shapes with a calculated top-left x, y and
624
- # a setter that does the moving inside them instead so that I could rely on absolute_x and absolute_y
625
- # here to get the job done of calculating absolute args
626
- @perform_redraw = false
627
- original_x = nil
628
- original_y = nil
629
- original_width = nil
630
- original_height = nil
631
- if parent.is_a?(Shape)
632
- @parent_absolute_x = parent.absolute_x
633
- @parent_absolute_y = parent.absolute_y
634
- end
635
- if default_width?
636
- original_width = width
637
- self.width = default_width + default_width_delta
638
- end
639
- if default_height?
640
- original_height = height
641
- self.height = default_height + default_height_delta
642
- end
643
- if default_x?
644
- original_x = x
645
- self.x = default_x + default_x_delta
646
- end
647
- if default_y?
648
- original_y = y
649
- self.y = default_y + default_y_delta
650
- end
651
- if parent.is_a?(Shape)
652
- move_by(@parent_absolute_x, @parent_absolute_y)
653
- result_args = @args.clone
654
- move_by(-1*@parent_absolute_x, -1*@parent_absolute_y)
655
- else
656
- result_args = @args.clone
657
- end
658
- if original_x
659
- self.x = original_x
660
- end
661
- if original_y
662
- self.y = original_y
663
- end
664
- if original_width
665
- self.width = original_width
666
- end
667
- if original_height
668
- self.height = original_height
757
+ def calculate_args!
758
+ # TODO add conditions for parent having default width/height too
759
+ return @args if parent.is_a?(Drawable) && !default_x? && !default_y? && !default_width? && !default_height? && !max_width? && !max_height?
760
+ calculated_args_dependencies = [
761
+ x,
762
+ y,
763
+ parent.is_a?(Shape) && parent.absolute_x,
764
+ parent.is_a?(Shape) && parent.absolute_y,
765
+ default_width? && default_width,
766
+ default_width? && width_delta,
767
+ default_height? && default_height,
768
+ default_height? && height_delta,
769
+ max_width? && max_width,
770
+ max_width? && width_delta,
771
+ max_height? && max_height,
772
+ max_height? && height_delta,
773
+ default_x? && default_x,
774
+ default_x? && x_delta,
775
+ default_y? && default_y,
776
+ default_y? && y_delta,
777
+ ]
778
+ if calculated_args_dependencies != @calculated_args_dependencies
779
+ # avoid recalculating values again
780
+ x, y, parent_absolute_x, parent_absolute_y, default_width, default_width_delta, default_height, default_height_delta, max_width, max_width_delta, max_height, max_height_delta, default_x, default_x_delta, default_y, default_y_delta = @calculated_args_dependencies = calculated_args_dependencies
781
+ # Note: Must set x and move_by because not all shapes have a real x and some must translate all their points with move_by
782
+ # TODO change that by setting a bounding box for all shapes with a calculated top-left x, y and
783
+ # a setter that does the moving inside them instead so that I could rely on absolute_x and absolute_y
784
+ # here to get the job done of calculating absolute args
785
+ @perform_redraw = false
786
+ original_x = nil
787
+ original_y = nil
788
+ original_width = nil
789
+ original_height = nil
790
+ if parent.is_a?(Shape)
791
+ @parent_absolute_x = parent_absolute_x
792
+ @parent_absolute_y = parent_absolute_y
793
+ end
794
+ if default_width?
795
+ original_width = width
796
+ self.width = default_width + default_width_delta
797
+ end
798
+ if default_height?
799
+ original_height = height
800
+ self.height = default_height + default_height_delta
801
+ end
802
+ if max_width?
803
+ original_width = width
804
+ self.width = max_width + max_width_delta
805
+ end
806
+ if max_height?
807
+ original_height = height
808
+ self.height = max_height + max_height_delta
809
+ end
810
+ if default_x?
811
+ original_x = x
812
+ self.x = default_x + default_x_delta
813
+ end
814
+ if default_y?
815
+ original_y = y
816
+ self.y = default_y + default_y_delta
817
+ end
818
+ if parent.is_a?(Shape)
819
+ move_by(@parent_absolute_x, @parent_absolute_y)
820
+ @result_calculated_args = @args.clone
821
+ move_by(-1*@parent_absolute_x, -1*@parent_absolute_y)
822
+ else
823
+ @result_calculated_args = @args.clone
824
+ end
825
+ if original_x
826
+ self.x = original_x
827
+ end
828
+ if original_y
829
+ self.y = original_y
830
+ end
831
+ if original_width
832
+ self.width = original_width
833
+ end
834
+ if original_height
835
+ self.height = original_height
836
+ end
837
+ @perform_redraw = true
669
838
  end
670
- @perform_redraw = true
671
- result_args
839
+ @result_calculated_args
672
840
  end
673
841
 
674
842
  def default_x?
675
- current_parameter_name?(:x) and
676
- (x.nil? || x.to_s == 'default' || (x.is_a?(Array) && x.first.to_s == 'default'))
843
+ return false unless current_parameter_name?(:x)
844
+ x = self.x
845
+ x.nil? || x.to_s == 'default' || (x.is_a?(Array) && x.first.to_s == 'default')
677
846
  end
678
847
 
679
848
  def default_y?
680
- current_parameter_name?(:y) and
681
- (y.nil? || y.to_s == 'default' || (y.is_a?(Array) && y.first.to_s == 'default'))
849
+ return false unless current_parameter_name?(:y)
850
+ y = self.y
851
+ y.nil? || y.to_s == 'default' || (y.is_a?(Array) && y.first.to_s == 'default')
682
852
  end
683
853
 
684
854
  def default_width?
@@ -693,120 +863,248 @@ module Glimmer
693
863
  (height.nil? || height == :default || height == 'default' || (height.is_a?(Array) && (height.first.to_s == :default || height.first.to_s == 'default')))
694
864
  end
695
865
 
866
+ def max_width?
867
+ return false unless current_parameter_name?(:width)
868
+ width = self.width
869
+ (width.nil? || width.to_s == 'max' || (width.is_a?(Array) && width.first.to_s == 'max'))
870
+ end
871
+
872
+ def max_height?
873
+ return false unless current_parameter_name?(:height)
874
+ height = self.height
875
+ (height.nil? || height.to_s == 'max' || (height.is_a?(Array) && height.first.to_s == 'max'))
876
+ end
877
+
696
878
  def default_x
697
- result = ((parent.size.x - size.x) / 2)
698
- result += parent.bounds.x - parent.absolute_x if parent.is_a?(Shape) && parent.irregular?
699
- result
879
+ default_x_dependencies = [parent.size.x, size.x, parent.is_a?(Shape) && parent.irregular? && parent.bounds.x, parent.is_a?(Shape) && parent.irregular? && parent.absolute_x]
880
+ if default_x_dependencies != @default_x_dependencies
881
+ @default_x_dependencies = default_x_dependencies
882
+ result = ((parent.size.x - size.x) / 2)
883
+ result += parent.bounds.x - parent.absolute_x if parent.is_a?(Shape) && parent.irregular?
884
+ @default_x = result
885
+ end
886
+ @default_x
700
887
  end
701
888
 
702
889
  def default_y
703
- result = ((parent.size.y - size.y) / 2)
704
- result += parent.bounds.y - parent.absolute_y if parent.is_a?(Shape) && parent.irregular?
705
- result
890
+ default_y_dependencies = [parent.size.y, size.y, parent.is_a?(Shape) && parent.irregular? && parent.bounds.y, parent.is_a?(Shape) && parent.irregular? && parent.absolute_y]
891
+ if default_y_dependencies != @default_y_dependencies
892
+ result = ((parent.size.y - size.y) / 2)
893
+ result += parent.bounds.y - parent.absolute_y if parent.is_a?(Shape) && parent.irregular?
894
+ @default_y = result
895
+ end
896
+ @default_y
897
+ end
898
+
899
+ # right-most x coordinate in this shape (adding up its width and location)
900
+ def x_end
901
+ x_end_dependencies = [calculated_width, default_x?, !default_x? && x]
902
+ if x_end_dependencies != @x_end_dependencies
903
+ # avoid recalculation of dependencies
904
+ calculated_width, is_default_x, x = @x_end_dependencies = x_end_dependencies
905
+ shape_width = calculated_width.to_f
906
+ shape_x = is_default_x ? 0 : x.to_f
907
+ @x_end = shape_x + shape_width
908
+ end
909
+ @x_end
910
+ end
911
+
912
+ # right-most y coordinate in this shape (adding up its height and location)
913
+ def y_end
914
+ y_end_dependencies = [calculated_height, default_y?, !default_y? && y]
915
+ if y_end_dependencies != @y_end_dependencies
916
+ # avoid recalculation of dependencies
917
+ calculated_height, is_default_y, y = @y_end_dependencies = y_end_dependencies
918
+ shape_height = calculated_height.to_f
919
+ shape_y = is_default_y ? 0 : y.to_f
920
+ @y_end = shape_y + shape_height
921
+ end
922
+ @y_end
706
923
  end
707
924
 
708
925
  def default_width
709
- # TODO consider caching
710
- x_ends = shapes.map do |shape|
711
- shape_width = shape.calculated_width.to_f
712
- shape_x = shape.default_x? ? 0 : shape.x.to_f
713
- shape_x + shape_width
926
+ default_width_dependencies = [shapes.empty? && max_width, shapes.size == 1 && shapes.first.max_width? && parent.size.x, shapes.size >= 1 && !shapes.first.max_width? && shapes.map {|s| s.max_width? ? 0 : s.x_end}]
927
+ if default_width_dependencies != @default_width_dependencies
928
+ # Do not repeat calculations
929
+ max_width, parent_size_x, x_ends = @default_width_dependencies = default_width_dependencies
930
+ @default_width = if shapes.empty?
931
+ max_width
932
+ elsif shapes.size == 1 && shapes.first.max_width?
933
+ parent_size_x
934
+ else
935
+ x_ends.max.to_f
936
+ end
714
937
  end
715
- x_ends.max.to_f
938
+ @default_width
716
939
  end
717
940
 
718
941
  def default_height
719
- # TODO consider caching
720
- y_ends = shapes.map do |shape|
721
- shape_height = shape.calculated_height.to_f
722
- shape_y = shape.default_y? ? 0 : shape.y.to_f
723
- shape_y + shape_height
942
+ default_height_dependencies = [shapes.empty? && max_height, shapes.size == 1 && shapes.first.max_height? && parent.size.y, shapes.size >= 1 && !shapes.first.max_height? && shapes.map {|s| s.max_height? ? 0 : s.y_end}]
943
+ if default_height_dependencies != @default_height_dependencies
944
+ # Do not repeat calculations
945
+ max_height, parent_size_y, y_ends = @default_height_dependencies = default_height_dependencies
946
+ @default_height = if shapes.empty?
947
+ max_height
948
+ elsif shapes.size == 1 && shapes.first.max_height?
949
+ parent_size_y
950
+ else
951
+ y_ends.max.to_f
952
+ end
953
+ end
954
+ @default_height
955
+ end
956
+
957
+ def max_width
958
+ max_width_dependencies = [parent.is_a?(Drawable) && parent.size.x, !parent.is_a?(Drawable) && parent.calculated_width]
959
+ if max_width_dependencies != @max_width_dependencies
960
+ # do not repeat calculations
961
+ parent_size_x, parent_calculated_width = @max_width_dependencies = max_width_dependencies
962
+ @max_width = parent.is_a?(Drawable) ? parent_size_x : parent_calculated_width
963
+ end
964
+ @max_width
965
+ end
966
+
967
+ def max_height
968
+ max_height_dependencies = [parent.is_a?(Drawable) && parent.size.y, !parent.is_a?(Drawable) && parent.calculated_height]
969
+ if max_height_dependencies != @max_height_dependencies
970
+ # do not repeat calculations
971
+ parent_size_y, parent_calculated_height = @max_height_dependencies = max_height_dependencies
972
+ @max_height = parent.is_a?(Drawable) ? parent_size_y : parent_calculated_height
724
973
  end
725
- y_ends.max.to_f
974
+ @max_height
726
975
  end
727
976
 
728
977
  def calculated_width
729
- default_width? ? (default_width + default_width_delta) : width
978
+ calculated_width_dependencies = [width, default_width? && (default_width + width_delta), max_width? && (max_width + width_delta)]
979
+ if calculated_width_dependencies != @calculated_width_dependencies
980
+ @calculated_width_dependencies = calculated_width_dependencies
981
+ result_width = width
982
+ result_width = (default_width + width_delta) if default_width?
983
+ result_width = (max_width + width_delta) if max_width?
984
+ @calculated_width = result_width
985
+ end
986
+ @calculated_width
730
987
  end
731
988
 
732
989
  def calculated_height
733
- default_height? ? (default_height + default_height_delta) : height
990
+ calculated_height_dependencies = [height, default_height? && (default_height + height_delta), max_height? && (max_height + height_delta)]
991
+ if calculated_height_dependencies != @calculated_height_dependencies
992
+ @calculated_height_dependencies = calculated_height_dependencies
993
+ result_height = height
994
+ result_height = (default_height + height_delta) if default_height?
995
+ result_height = (max_height + height_delta) if max_height?
996
+ @calculated_height = result_height
997
+ end
998
+ @calculated_height
734
999
  end
735
1000
 
736
- def default_x_delta
737
- return 0 unless default_x? && x.is_a?(Array)
1001
+ def x_delta
1002
+ return 0 unless x.is_a?(Array) && default_x?
738
1003
  x[1].to_f
739
1004
  end
740
1005
 
741
- def default_y_delta
742
- return 0 unless default_y? && y.is_a?(Array)
1006
+ def y_delta
1007
+ return 0 unless y.is_a?(Array) && default_y?
743
1008
  y[1].to_f
744
1009
  end
745
1010
 
746
- def default_width_delta
747
- return 0 unless default_width? && width.is_a?(Array)
1011
+ def width_delta
1012
+ return 0 unless width.is_a?(Array) && (default_width? || max_width?)
748
1013
  width[1].to_f
749
1014
  end
750
1015
 
751
- def default_height_delta
752
- return 0 unless default_height? && height.is_a?(Array)
1016
+ def height_delta
1017
+ return 0 unless height.is_a?(Array) && (default_height? || max_height?)
753
1018
  height[1].to_f
754
1019
  end
755
1020
 
756
- def default_x_delta=(delta)
1021
+ def x_delta=(delta)
757
1022
  return unless default_x?
758
- self.x = [:default, delta]
1023
+ symbol = x.is_a?(Array) ? x.first : x
1024
+ self.x = [symbol, delta]
759
1025
  end
760
1026
 
761
- def default_y_delta=(delta)
1027
+ def y_delta=(delta)
762
1028
  return unless default_y?
763
- self.y = [:default, delta]
1029
+ symbol = y.is_a?(Array) ? y.first : y
1030
+ self.y = [symbol, delta]
764
1031
  end
765
1032
 
766
- def default_width_delta=(delta)
1033
+ def width_delta=(delta)
767
1034
  return unless default_width?
768
- self.width = [:default, delta]
1035
+ symbol = width.is_a?(Array) ? width.first : width
1036
+ self.width = [symbol, delta]
769
1037
  end
770
1038
 
771
- def default_height_delta=(delta)
1039
+ def height_delta=(delta)
772
1040
  return unless default_height?
773
- self.height = [:default, delta]
1041
+ symbol = height.is_a?(Array) ? height.first : height
1042
+ self.height = [symbol, delta]
774
1043
  end
775
1044
 
776
1045
  def calculated_x
777
- result = default_x? ? default_x : self.x
778
- result += default_x_delta
779
- result
1046
+ calculated_x_dependencies = [default_x? && default_x, !default_x? && self.x, self.x_delta]
1047
+ if calculated_x_dependencies != @calculated_x_dependencies
1048
+ default_x, x, x_delta = @calculated_x_dependencies = calculated_x_dependencies
1049
+ result = default_x? ? default_x : x
1050
+ result += x_delta
1051
+ @calculated_x = result
1052
+ end
1053
+ @calculated_x
780
1054
  end
781
1055
 
782
1056
  def calculated_y
783
- result = default_y? ? default_y : self.y
784
- result += default_y_delta
785
- result
1057
+ calculated_y_dependencies = [default_y? && default_y, !default_y? && self.y, self.y_delta]
1058
+ if calculated_y_dependencies != @calculated_y_dependencies
1059
+ default_y, y, y_delta = @calculated_y_dependencies = calculated_y_dependencies
1060
+ result = default_y? ? default_y : y
1061
+ result += y_delta
1062
+ @calculated_y = result
1063
+ end
1064
+ @calculated_y
786
1065
  end
787
1066
 
788
1067
  def absolute_x
789
- x = calculated_x
790
- if parent.is_a?(Shape)
791
- parent.absolute_x + x
792
- else
793
- x
1068
+ absolute_x_dependencies = [calculated_x, parent.is_a?(Shape) && parent.absolute_x]
1069
+ if absolute_x_dependencies != @absolute_x_dependencies
1070
+ # do not repeat calculations
1071
+ calculated_x, parent_absolute_x = @absolute_x_dependencies = absolute_x_dependencies
1072
+ x = calculated_x
1073
+ @absolute_x = if parent.is_a?(Shape)
1074
+ parent_absolute_x + x
1075
+ else
1076
+ x
1077
+ end
794
1078
  end
1079
+ @absolute_x
795
1080
  end
796
1081
 
797
1082
  def absolute_y
798
- y = calculated_y
799
- if parent.is_a?(Shape)
800
- parent.absolute_y + y
801
- else
802
- y
1083
+ absolute_y_dependencies = [calculated_y, parent.is_a?(Shape) && parent.absolute_y]
1084
+ if absolute_y_dependencies != @absolute_y_dependencies
1085
+ calculated_y, parent_absolute_y = @absolute_y_dependencies = absolute_y_dependencies
1086
+ y = calculated_y
1087
+ @absolute_y = if parent.is_a?(Shape)
1088
+ parent_absolute_y + y
1089
+ else
1090
+ y
1091
+ end
803
1092
  end
804
- end
805
-
806
- # Overriding inspect to avoid printing very long shape hierarchies
807
- def inspect
808
- "#<#{self.class.name}:0x#{self.hash.to_s(16)} args=#{@args.inspect}, properties=#{@properties.inspect}}>"
1093
+ @absolute_y
1094
+ end
1095
+
1096
+ # Overriding inspect to avoid printing very long nested shape hierarchies (recurses onces only)
1097
+ def inspect(recursive: 1, calculated: false, args: true, properties: true, calculated_args: false)
1098
+ recurse = recursive == true || recursive.is_a?(Integer) && recursive.to_i > 0
1099
+ recursive = [recursive -= 1, 0].max if recursive.is_a?(Integer)
1100
+ args_string = " args=#{@args.inspect}" if args
1101
+ properties_string = " properties=#{@properties.inspect}}" if properties
1102
+ calculated_args_string = " calculated_args=#{@calculated_args.inspect}" if calculated_args
1103
+ calculated_string = " absolute_x=#{absolute_x} absolute_y=#{absolute_y} calculated_width=#{calculated_width} calculated_height=#{calculated_height}" if calculated
1104
+ recursive_string = " shapes=#{@shapes.map {|s| s.inspect(recursive: recursive, calculated: calculated, args: args, properties: properties)}}" if recurse
1105
+ "#<#{self.class.name}:0x#{self.hash.to_s(16)}#{args_string}#{properties_string}#{calculated_args_string}#{calculated_string}#{recursive_string}>"
809
1106
  rescue => e
1107
+ Glimmer::Config.logger.error { e.full_message }
810
1108
  "#<#{self.class.name}:0x#{self.hash.to_s(16)}"
811
1109
  end
812
1110
 
@@ -827,19 +1125,14 @@ module Glimmer
827
1125
  end
828
1126
  end
829
1127
  else
1128
+ @properties = all_parent_properties.merge(@properties)
830
1129
  @properties['background'] = [@drawable.background] if fill? && !has_some_background?
831
1130
  @properties['foreground'] = [@drawable.foreground] if @drawable.respond_to?(:foreground) && draw? && !has_some_foreground?
832
1131
  # TODO regarding alpha, make sure to reset it to parent stored alpha once we allow setting shape properties on parents directly without shapes
833
- @properties['alpha'] ||= [255]
834
- @properties['font'] = [@drawable.font] if @drawable.respond_to?(:font) && draw? && !@properties.keys.map(&:to_s).include?('font')
1132
+ @properties['font'] = [@drawable.font] if @drawable.respond_to?(:font) && @name == 'text' && draw? && !@properties.keys.map(&:to_s).include?('font')
835
1133
  # TODO regarding transform, make sure to reset it to parent stored transform once we allow setting shape properties on parents directly without shapes
836
1134
  # Also do that with all future-added properties
837
- @properties['transform'] = [nil] if @drawable.respond_to?(:transform) && !@properties.keys.map(&:to_s).include?('transform')
838
- @properties.each do |property, args|
839
- method_name = attribute_setter(property)
840
- converted_args = apply_property_arg_conversions(method_name, property, args)
841
- @properties[property] = converted_args
842
- end
1135
+ convert_properties!
843
1136
  apply_shape_arg_conversions!
844
1137
  apply_shape_arg_defaults!
845
1138
  tolerate_shape_extra_args!