glimmer-dsl-swt 4.18.5.0 → 4.18.5.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +43 -0
  3. data/README.md +16 -11
  4. data/VERSION +1 -1
  5. data/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md +179 -33
  6. data/docs/reference/GLIMMER_SAMPLES.md +58 -0
  7. data/glimmer-dsl-swt.gemspec +7 -7
  8. data/lib/glimmer/data_binding/widget_binding.rb +4 -1
  9. data/lib/glimmer/dsl/swt/dialog_expression.rb +18 -9
  10. data/lib/glimmer/dsl/swt/dsl.rb +1 -0
  11. data/lib/glimmer/dsl/swt/font_expression.rb +1 -1
  12. data/lib/glimmer/dsl/swt/shape_expression.rb +1 -1
  13. data/lib/glimmer/dsl/swt/shell_expression.rb +1 -1
  14. data/lib/glimmer/swt/custom/drawable.rb +10 -2
  15. data/lib/glimmer/swt/custom/shape.rb +458 -58
  16. data/lib/glimmer/swt/custom/shape/arc.rb +35 -0
  17. data/lib/glimmer/swt/custom/shape/focus.rb +2 -2
  18. data/lib/glimmer/swt/custom/shape/image.rb +35 -9
  19. data/lib/glimmer/swt/custom/shape/line.rb +88 -4
  20. data/lib/glimmer/swt/custom/shape/oval.rb +18 -0
  21. data/lib/glimmer/swt/custom/shape/point.rb +10 -5
  22. data/lib/glimmer/swt/custom/shape/polygon.rb +105 -15
  23. data/lib/glimmer/swt/custom/shape/polyline.rb +88 -15
  24. data/lib/glimmer/swt/custom/shape/rectangle.rb +19 -0
  25. data/lib/glimmer/swt/custom/shape/text.rb +13 -3
  26. data/lib/glimmer/swt/{directory_dialog_proxy.rb → dialog_proxy.rb} +36 -7
  27. data/lib/glimmer/swt/font_proxy.rb +12 -6
  28. data/lib/glimmer/swt/message_box_proxy.rb +1 -0
  29. data/lib/glimmer/swt/properties.rb +3 -0
  30. data/lib/glimmer/swt/proxy_properties.rb +145 -0
  31. data/lib/glimmer/swt/transform_proxy.rb +39 -35
  32. data/lib/glimmer/swt/widget_proxy.rb +32 -60
  33. data/samples/elaborate/contact_manager.rb +2 -0
  34. data/samples/elaborate/login.rb +2 -0
  35. data/samples/elaborate/mandelbrot_fractal.rb +1 -0
  36. data/samples/elaborate/meta_sample.rb +1 -0
  37. data/samples/elaborate/tetris.rb +2 -1
  38. data/samples/elaborate/tic_tac_toe.rb +2 -0
  39. data/samples/elaborate/user_profile.rb +10 -8
  40. data/samples/hello/hello_browser.rb +2 -0
  41. data/samples/hello/hello_button.rb +2 -0
  42. data/samples/hello/hello_canvas.rb +157 -77
  43. data/samples/hello/hello_canvas_animation.rb +2 -0
  44. data/samples/hello/hello_canvas_transform.rb +2 -0
  45. data/samples/hello/hello_checkbox.rb +2 -0
  46. data/samples/hello/hello_checkbox_group.rb +2 -0
  47. data/samples/hello/hello_code_text.rb +2 -0
  48. data/{lib/glimmer/dsl/swt/directory_dialog_expression.rb → samples/hello/hello_color_dialog.rb} +44 -24
  49. data/samples/hello/hello_combo.rb +2 -0
  50. data/samples/hello/hello_computed.rb +2 -0
  51. data/samples/hello/hello_cursor.rb +2 -0
  52. data/samples/hello/hello_custom_shell.rb +1 -0
  53. data/samples/hello/hello_custom_widget.rb +2 -0
  54. data/samples/hello/hello_date_time.rb +2 -0
  55. data/samples/hello/hello_dialog.rb +2 -0
  56. data/samples/hello/hello_directory_dialog.rb +2 -0
  57. data/samples/hello/hello_drag_and_drop.rb +5 -3
  58. data/samples/hello/hello_expand_bar.rb +2 -0
  59. data/samples/hello/hello_file_dialog.rb +2 -0
  60. data/samples/hello/hello_font_dialog.rb +84 -0
  61. data/samples/hello/hello_group.rb +2 -0
  62. data/samples/hello/hello_link.rb +2 -0
  63. data/samples/hello/hello_list_multi_selection.rb +2 -0
  64. data/samples/hello/hello_list_single_selection.rb +2 -0
  65. data/samples/hello/hello_menu_bar.rb +2 -0
  66. data/samples/hello/hello_message_box.rb +2 -0
  67. data/samples/hello/hello_pop_up_context_menu.rb +2 -0
  68. data/samples/hello/hello_progress_bar.rb +2 -0
  69. data/samples/hello/hello_radio.rb +2 -0
  70. data/samples/hello/hello_radio_group.rb +2 -0
  71. data/samples/hello/hello_sash_form.rb +2 -0
  72. data/samples/hello/hello_spinner.rb +2 -0
  73. data/samples/hello/hello_styled_text.rb +19 -17
  74. data/samples/hello/hello_tab.rb +2 -0
  75. data/samples/hello/hello_table.rb +2 -0
  76. data/samples/hello/hello_world.rb +2 -0
  77. metadata +6 -6
  78. data/lib/glimmer/dsl/swt/file_dialog_expression.rb +0 -48
  79. data/lib/glimmer/swt/file_dialog_proxy.rb +0 -68
@@ -36,6 +36,8 @@
36
36
  - [Hello, Cursor!](#hello-cursor)
37
37
  - [Hello, Progress Bar!](#hello-progress-bar)
38
38
  - [Hello, Tree!](#hello-tree)
39
+ - [Hello, Color Dialog!](#hello-color-dialog)
40
+ - [Hello, Font Dialog!](#hello-font-dialog)
39
41
  - [Elaborate Samples](#elaborate-samples)
40
42
  - [User Profile](#user-profile)
41
43
  - [Login](#login)
@@ -562,6 +564,30 @@ Hello, Canvas!
562
564
 
563
565
  ![Hello Canvas](/images/glimmer-hello-canvas.png)
564
566
 
567
+ Hello, Canvas! Moving Shapes and Nested Shapes via Drag'n'Drop
568
+
569
+ ![Hello Canvas Moving Shapes](/images/glimmer-hello-canvas-moving-shapes.gif)
570
+
571
+ Hello, Canvas! with Moved Shapes (via Drag'n'Drop)
572
+
573
+ ![Hello Canvas Moved Shapes](/images/glimmer-hello-canvas-moved-shapes.png)
574
+
575
+ Hello, Canvas! Menu (for background/foreground color changes)
576
+
577
+ ![Hello Canvas Menu](/images/glimmer-hello-canvas-menu.png)
578
+
579
+ Hello, Canvas! Color Dialog
580
+
581
+ ![Hello Canvas Color Dialog](/images/glimmer-hello-canvas-color-dialog.png)
582
+
583
+ Hello, Canvas! Colors Changed
584
+
585
+ ![Hello Canvas Colors Changed](/images/glimmer-hello-canvas-colors-changed.png)
586
+
587
+ Hello, Canvas! Data-Binding (changing a `text` shape `string` via data-binding changes from another thread)
588
+
589
+ ![Hello Canvas Data Binding](/images/glimmer-hello-canvas-data-binding.gif)
590
+
565
591
  #### Hello, Canvas Animation!
566
592
 
567
593
  This sample demonstrates the use of the `canvas` widget and [Animation DSL](#canvas-animation-dsl) in Glimmer.
@@ -630,6 +656,38 @@ Hello, Tree!
630
656
 
631
657
  ![Hello Tree](/images/glimmer-hello-tree.png)
632
658
 
659
+ #### Hello, Color Dialog!
660
+
661
+ This sample demonstrates the use of the `color_dialog` keyword.
662
+
663
+ Code:
664
+
665
+ [samples/hello/hello_color_dialog.rb](/samples/hello/hello_color_dialog.rb)
666
+
667
+ Hello, Color Dialog!
668
+
669
+ ![Hello Color Dialog](/images/glimmer-hello-color-dialog.png)
670
+
671
+ ![Hello Color Dialog Choose Color](/images/glimmer-hello-color-dialog-choose-color.png)
672
+
673
+ ![Hello Color Dialog Color Changed](/images/glimmer-hello-color-dialog-color-changed.png)
674
+
675
+ #### Hello, Font Dialog!
676
+
677
+ This sample demonstrates the use of the `font_dialog` keyword.
678
+
679
+ Code:
680
+
681
+ [samples/hello/hello_font_dialog.rb](/samples/hello/hello_font_dialog.rb)
682
+
683
+ Hello, Font Dialog!
684
+
685
+ ![Hello Font Dialog](/images/glimmer-hello-font-dialog.png)
686
+
687
+ ![Hello Font Dialog Choose Font](/images/glimmer-hello-font-dialog-choose-font.png)
688
+
689
+ ![Hello Font Dialog Font Changed](/images/glimmer-hello-font-dialog-font-changed.png)
690
+
633
691
  ### Elaborate Samples
634
692
 
635
693
  For more elaborate samples, check the following:
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: glimmer-dsl-swt 4.18.5.0 ruby lib
5
+ # stub: glimmer-dsl-swt 4.18.5.5 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "glimmer-dsl-swt".freeze
9
- s.version = "4.18.5.0"
9
+ s.version = "4.18.5.5"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["AndyMaleh".freeze]
14
- s.date = "2021-02-22"
14
+ s.date = "2021-02-27"
15
15
  s.description = "Glimmer DSL for SWT (JRuby Desktop Development GUI Framework) is a native-GUI cross-platform desktop development library written in JRuby, an OS-threaded faster JVM version of Ruby. Glimmer's main innovation is a declarative Ruby DSL that enables productive and efficient authoring of desktop application user-interfaces by relying on the robust Eclipse SWT library. Glimmer additionally innovates by having built-in data-binding support, which greatly facilitates synchronizing the GUI with domain models, thus achieving true decoupling of object oriented components and enabling developers to solve business problems (test-first) without worrying about GUI concerns, or alternatively drive development GUI-first, and then write clean business models (test-first) afterwards. Not only does Glimmer provide a large set of GUI widgets, but it also supports drawing Canvas Graphics like Shapes and Animations. To get started quickly, Glimmer offers scaffolding options for Apps, Gems, and Custom Widgets. Glimmer also includes native-executable packaging support, sorely lacking in other libraries, thus enabling the delivery of desktop apps written in Ruby as truly native DMG/PKG/APP files on the Mac + App Store, MSI/EXE files on Windows, and Gem Packaged Shell Scripts on Linux.".freeze
16
16
  s.email = "andy.am@gmail.com".freeze
17
17
  s.executables = ["glimmer".freeze, "girb".freeze]
@@ -69,13 +69,11 @@ Gem::Specification.new do |s|
69
69
  "lib/glimmer/dsl/swt/custom_widget_expression.rb",
70
70
  "lib/glimmer/dsl/swt/data_binding_expression.rb",
71
71
  "lib/glimmer/dsl/swt/dialog_expression.rb",
72
- "lib/glimmer/dsl/swt/directory_dialog_expression.rb",
73
72
  "lib/glimmer/dsl/swt/display_expression.rb",
74
73
  "lib/glimmer/dsl/swt/dnd_expression.rb",
75
74
  "lib/glimmer/dsl/swt/dsl.rb",
76
75
  "lib/glimmer/dsl/swt/exec_expression.rb",
77
76
  "lib/glimmer/dsl/swt/expand_item_expression.rb",
78
- "lib/glimmer/dsl/swt/file_dialog_expression.rb",
79
77
  "lib/glimmer/dsl/swt/font_expression.rb",
80
78
  "lib/glimmer/dsl/swt/image_expression.rb",
81
79
  "lib/glimmer/dsl/swt/layout_data_expression.rb",
@@ -127,11 +125,10 @@ Gem::Specification.new do |s|
127
125
  "lib/glimmer/swt/custom/shape/rectangle.rb",
128
126
  "lib/glimmer/swt/custom/shape/text.rb",
129
127
  "lib/glimmer/swt/date_time_proxy.rb",
130
- "lib/glimmer/swt/directory_dialog_proxy.rb",
128
+ "lib/glimmer/swt/dialog_proxy.rb",
131
129
  "lib/glimmer/swt/display_proxy.rb",
132
130
  "lib/glimmer/swt/dnd_proxy.rb",
133
131
  "lib/glimmer/swt/expand_item_proxy.rb",
134
- "lib/glimmer/swt/file_dialog_proxy.rb",
135
132
  "lib/glimmer/swt/font_proxy.rb",
136
133
  "lib/glimmer/swt/image_proxy.rb",
137
134
  "lib/glimmer/swt/layout_data_proxy.rb",
@@ -140,6 +137,7 @@ Gem::Specification.new do |s|
140
137
  "lib/glimmer/swt/message_box_proxy.rb",
141
138
  "lib/glimmer/swt/packages.rb",
142
139
  "lib/glimmer/swt/properties.rb",
140
+ "lib/glimmer/swt/proxy_properties.rb",
143
141
  "lib/glimmer/swt/sash_form_proxy.rb",
144
142
  "lib/glimmer/swt/scrolled_composite_proxy.rb",
145
143
  "lib/glimmer/swt/shell_proxy.rb",
@@ -186,6 +184,7 @@ Gem::Specification.new do |s|
186
184
  "samples/hello/hello_checkbox.rb",
187
185
  "samples/hello/hello_checkbox_group.rb",
188
186
  "samples/hello/hello_code_text.rb",
187
+ "samples/hello/hello_color_dialog.rb",
189
188
  "samples/hello/hello_combo.rb",
190
189
  "samples/hello/hello_computed.rb",
191
190
  "samples/hello/hello_computed/contact.rb",
@@ -198,6 +197,7 @@ Gem::Specification.new do |s|
198
197
  "samples/hello/hello_drag_and_drop.rb",
199
198
  "samples/hello/hello_expand_bar.rb",
200
199
  "samples/hello/hello_file_dialog.rb",
200
+ "samples/hello/hello_font_dialog.rb",
201
201
  "samples/hello/hello_group.rb",
202
202
  "samples/hello/hello_link.rb",
203
203
  "samples/hello/hello_list_multi_selection.rb",
@@ -52,7 +52,10 @@ module Glimmer
52
52
  unregister_all_observables
53
53
  return
54
54
  end
55
- @widget.set_attribute(@property, value) unless evaluate_property == value
55
+ # need the rescue false for a scenario with tree items not being equal to model objects raising an exception
56
+ unless ((value == evaluate_property) rescue false) # need the rescue false for a scenario with tree items not being equal to model objects raising an exception
57
+ @widget.set_attribute(@property, value)
58
+ end
56
59
  end
57
60
  end
58
61
 
@@ -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
@@ -23,24 +23,33 @@ require 'glimmer/dsl/static_expression'
23
23
  require 'glimmer/dsl/parent_expression'
24
24
  require 'glimmer/dsl/top_level_expression'
25
25
  require 'glimmer/swt/shell_proxy'
26
+ require 'glimmer/swt/dialog_proxy'
26
27
 
27
28
  module Glimmer
28
29
  module DSL
29
30
  module SWT
30
- class DialogExpression < StaticExpression
31
+ class DialogExpression < Expression
31
32
  include TopLevelExpression
32
33
  include ParentExpression
33
34
 
34
35
  def can_interpret?(parent, keyword, *args, &block)
35
- keyword == 'dialog' and
36
- (parent.nil? or parent.is_a?(Glimmer::SWT::ShellProxy))
36
+ (
37
+ (keyword == 'dialog') or
38
+ (keyword.to_s.end_with?('dialog') and Glimmer::SWT::DialogProxy.dialog_class(keyword))
39
+ ) and
40
+ (parent.nil? or parent.is_a?(org.eclipse.swt.widgets.Shell) or parent.is_a?(Glimmer::SWT::ShellProxy))
37
41
  end
38
42
 
39
43
  def interpret(parent, keyword, *args, &block)
40
44
  # TODO reconcile this with the actual org.eclipse.swt.widgets.Dialog widget (maybe rename this as dialog_shell)
41
- args = [parent] + args unless parent.nil?
42
- args += [:dialog_trim, :application_modal]
43
- Glimmer::SWT::ShellProxy.send(:new, *args)
45
+ if keyword == 'dialog'
46
+ args = [parent] + args unless parent.nil?
47
+ args += [:dialog_trim, :application_modal]
48
+ Glimmer::SWT::ShellProxy.new(*args)
49
+ else
50
+ args = [parent] + args unless parent.nil?
51
+ Glimmer::SWT::DialogProxy.new(keyword, *args)
52
+ end
44
53
  end
45
54
  end
46
55
  end
@@ -53,6 +53,7 @@ module Glimmer
53
53
  multiply
54
54
  property
55
55
  block_property
56
+ dialog
56
57
  widget
57
58
  custom_widget
58
59
  shape
@@ -37,7 +37,7 @@ module Glimmer
37
37
  (parent.nil? || !parent.respond_to?('font')) and
38
38
  !parent.is_a?(Glimmer::SWT::Custom::Shape) and
39
39
  args.size == 1 and
40
- args.first.is_a?(Hash)
40
+ (args.first.is_a?(Hash) || args.first.is_a?(org.eclipse.swt.graphics.FontData))
41
41
  end
42
42
 
43
43
  def interpret(parent, keyword, *args, &block)
@@ -32,7 +32,7 @@ module Glimmer
32
32
  include ParentExpression
33
33
 
34
34
  def can_interpret?(parent, keyword, *args, &block)
35
- parent.is_a?(Glimmer::SWT::Custom::Drawable) and
35
+ (parent.is_a?(Glimmer::SWT::Custom::Drawable) or parent.is_a?(Glimmer::SWT::Custom::Shape)) and
36
36
  Glimmer::SWT::Custom::Shape.valid?(parent, keyword, *args, &block)
37
37
  end
38
38
 
@@ -38,7 +38,7 @@ module Glimmer
38
38
 
39
39
  def interpret(parent, keyword, *args, &block)
40
40
  args = [parent] + args unless parent.nil?
41
- Glimmer::SWT::ShellProxy.send(:new, *args)
41
+ Glimmer::SWT::ShellProxy.new(*args)
42
42
  end
43
43
  end
44
44
  class WindowExpression < ShellExpression
@@ -34,12 +34,20 @@ module Glimmer
34
34
  @shapes ||= []
35
35
  end
36
36
 
37
+ def expanded_shapes
38
+ @shapes.map do |shape|
39
+ [shape] + shape.expanded_shapes
40
+ end.flatten
41
+ end
42
+
37
43
  def image_buffered_shapes
38
44
  @image_buffered_shapes ||= []
39
45
  end
40
46
 
47
+ # TODO add a method like shapes that specifies drawable_properties to be able to adjust properties like transform in between shapes
48
+
41
49
  def shape_at_location(x, y)
42
- shapes.reverse.detect {|shape| shape.include?(x, y)}
50
+ expanded_shapes.reverse.detect {|shape| shape.include?(x, y)}
43
51
  end
44
52
 
45
53
  def add_shape(shape)
@@ -132,7 +140,7 @@ module Glimmer
132
140
  @paint_listener_proxy = on_swt_paint(&shape_painter)
133
141
  end
134
142
  else
135
- redraw if @finished_add_content && !is_disposed
143
+ redraw if respond_to?(:redraw) && @finished_add_content && !is_disposed
136
144
  end
137
145
  end
138
146
  alias resetup_shape_painting setup_shape_painting
@@ -26,6 +26,34 @@ require 'glimmer/swt/color_proxy'
26
26
  require 'glimmer/swt/font_proxy'
27
27
  require 'glimmer/swt/transform_proxy'
28
28
 
29
+ class Java::OrgEclipseSwtGraphics::GC
30
+ def setLineDashOffset(value)
31
+ lineMiterLimit = getLineAttributes&.miterLimit || 999_999
32
+ setLineAttributes(Java::OrgEclipseSwtGraphics::LineAttributes.new(getLineWidth, getLineCap, getLineJoin, getLineStyle, getLineDash.map(&:to_f).to_java(:float), value, lineMiterLimit))
33
+ end
34
+ alias set_line_dash_offset setLineDashOffset
35
+ alias line_dash_offset= setLineDashOffset
36
+
37
+ def getLineDashOffset
38
+ getLineAttributes&.dashOffset
39
+ end
40
+ alias get_line_dash_offset getLineDashOffset
41
+ alias line_dash_offset getLineDashOffset
42
+
43
+ def setLineMiterLimit(value)
44
+ lineDashOffset = getLineAttributes&.dashOffset || 0
45
+ setLineAttributes(Java::OrgEclipseSwtGraphics::LineAttributes.new(getLineWidth, getLineCap, getLineJoin, getLineStyle, getLineDash.map(&:to_f).to_java(:float), lineDashOffset, value))
46
+ end
47
+ alias set_line_miter_limit setLineMiterLimit
48
+ alias line_miter_limit= setLineMiterLimit
49
+
50
+ def getLineMiterLimit
51
+ getLineAttributes&.miterLimit
52
+ end
53
+ alias get_line_miter_limit getLineMiterLimit
54
+ alias line_miter_limit getLineMiterLimit
55
+ end
56
+
29
57
  module Glimmer
30
58
  module SWT
31
59
  module Custom
@@ -34,8 +62,6 @@ module Glimmer
34
62
  class Shape
35
63
  include Packages
36
64
  include Properties
37
- # TODO support textExtent sized shapes nested within text/string
38
- # TODO support a Pattern DSL for methods that take Pattern arguments
39
65
 
40
66
  class << self
41
67
  def create(parent, keyword, *args, &property_block)
@@ -100,15 +126,18 @@ module Glimmer
100
126
  end
101
127
  end
102
128
 
103
- attr_reader :parent, :name, :args, :options
129
+ attr_reader :drawable, :parent, :name, :args, :options, :shapes
130
+ attr_accessor :extent
104
131
 
105
132
  def initialize(parent, keyword, *args, &property_block)
106
133
  @parent = parent
134
+ @drawable = @parent.is_a?(Drawable) ? @parent : @parent.drawable
107
135
  @name = keyword
108
136
  @options = self.class.arg_options(args, extract: true)
109
137
  @method_name = self.class.method_name(keyword, @options)
110
138
  @args = args
111
139
  @properties = {}
140
+ @shapes = [] # nested shapes
112
141
  @options.reject {|key, value| %w[fill gradient round].include?(key.to_s)}.each do |property, property_args|
113
142
  @properties[property] = property_args
114
143
  end
@@ -116,13 +145,20 @@ module Glimmer
116
145
  post_add_content if property_block.nil?
117
146
  end
118
147
 
148
+ def add_shape(shape)
149
+ @shapes << shape
150
+ calculated_args_changed_for_defaults!
151
+ end
152
+
119
153
  def draw?
120
154
  !fill?
121
155
  end
156
+ alias drawn? draw?
122
157
 
123
158
  def fill?
124
159
  @options[:fill]
125
160
  end
161
+ alias filled? fill?
126
162
 
127
163
  def gradient?
128
164
  @options[:gradient]
@@ -132,26 +168,60 @@ module Glimmer
132
168
  @options[:round]
133
169
  end
134
170
 
135
- # subclasses (like polygon) may override to indicate if a point x,y coordinates fall inside the shape
136
- # has a default implementation for rectangle and oval
171
+ # The bounding box top-left x, y, width, height in absolute positioning
172
+ def bounds
173
+ org.eclipse.swt.graphics.Rectangle.new(absolute_x, absolute_y, calculated_width, calculated_height)
174
+ end
175
+
176
+ # The bounding box top-left x and y
177
+ def location
178
+ org.eclipse.swt.graphics.Point.new(bounds.x, bounds.y)
179
+ end
180
+
181
+ # The bounding box width and height (as a Point object with x being width and y being height)
182
+ def size
183
+ org.eclipse.swt.graphics.Point.new(calculated_width, calculated_height)
184
+ end
185
+
186
+ def extent
187
+ @extent || size
188
+ end
189
+
190
+ # Returns if shape contains a point
191
+ # Subclasses (like polygon) may override to indicate if a point x,y coordinates falls inside the shape
192
+ # some shapes may choose to provide a fuzz factor to make usage of this method for mouse clicking more user friendly
193
+ def contain?(x, y)
194
+ # assume a rectangular filled shape by default (works for several shapes like image, text, and focus)
195
+ x.between?(self.absolute_x, self.absolute_x + calculated_width) && y.between?(self.absolute_y, self.absolute_y + calculated_height)
196
+ end
197
+
198
+ # Returns if shape includes a point. When the shape is filled, this is the same as contain. When the shape is drawn, it only returns true if the point lies on the edge (boundary/border)
199
+ # Subclasses (like polygon) may override to indicate if a point x,y coordinates falls on the edge of a drawn shape or inside a filled shape
200
+ # some shapes may choose to provide a fuzz factor to make usage of this method for mouse clicking more user friendly
137
201
  def include?(x, y)
138
- case @name
139
- when 'rectangle', 'oval', 'arc'
140
- self_x = self.x
141
- self_y = self.y
142
- width = self.width
143
- height = self.height
144
- x.between?(self_x, self_x + width) && y.between?(self_y, self_y + height)
145
- else
146
- false
147
- end
202
+ # assume a rectangular shape by default
203
+ contain?(x, y)
204
+ end
205
+
206
+ # Indicates if a shape's x, y, width, height differ from its bounds calculation (e.g. arc / polygon)
207
+ def irregular?
208
+ false
148
209
  end
149
210
 
211
+ # moves by x delta and y delta. Subclasses must implement
212
+ # provdies a default implementation that assumes moving x and y is sufficient by default (not for polygons though, which must override)
150
213
  def move_by(x_delta, y_delta)
151
- case @name
152
- when 'rectangle', 'oval', 'arc'
153
- self.x += x_delta
154
- self.y += y_delta
214
+ if respond_to?(:x) && respond_to?(:y) && respond_to?(:x=) && respond_to?(:y=)
215
+ if default_x?
216
+ self.default_x_delta += x_delta
217
+ else
218
+ self.x += x_delta
219
+ end
220
+ if default_y?
221
+ self.default_y_delta += y_delta
222
+ else
223
+ self.y += y_delta
224
+ end
155
225
  end
156
226
  end
157
227
 
@@ -166,7 +236,7 @@ module Glimmer
166
236
  def post_add_content
167
237
  unless @content_added
168
238
  amend_method_name_options_based_on_properties!
169
- @parent.setup_shape_painting unless @parent.is_a?(ImageProxy)
239
+ @drawable.setup_shape_painting unless @drawable.is_a?(ImageProxy)
170
240
  @content_added = true
171
241
  end
172
242
  end
@@ -174,10 +244,18 @@ module Glimmer
174
244
  def apply_property_arg_conversions(method_name, property, args)
175
245
  args = args.dup
176
246
  the_java_method = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.detect {|m| m.name == method_name}
247
+ return args if the_java_method.nil?
248
+ if the_java_method.parameter_types.first == Color.java_class && args.first.is_a?(RGB)
249
+ args[0] = [args[0].red, args[0].green, args[0].blue]
250
+ end
177
251
  if ['setBackground', 'setForeground'].include?(method_name.to_s) && args.first.is_a?(Array)
178
252
  args[0] = ColorProxy.new(args[0])
179
253
  end
180
- if args.first.is_a?(Symbol) || args.first.is_a?(String)
254
+ if method_name.to_s == 'setLineDash' && args.size > 1
255
+ args[0] = args.dup
256
+ args[1..-1] = []
257
+ end
258
+ if args.first.is_a?(Symbol) || args.first.is_a?(::String)
181
259
  if the_java_method.parameter_types.first == Color.java_class
182
260
  args[0] = ColorProxy.new(args[0])
183
261
  end
@@ -188,7 +266,7 @@ module Glimmer
188
266
  if args.first.is_a?(ColorProxy)
189
267
  args[0] = args[0].swt_color
190
268
  end
191
- if args.first.is_a?(Hash) && the_java_method.parameter_types.first == Font.java_class
269
+ if (args.first.is_a?(Hash) || args.first.is_a?(FontData)) && the_java_method.parameter_types.first == Font.java_class
192
270
  args[0] = FontProxy.new(args[0])
193
271
  end
194
272
  if args.first.is_a?(FontProxy)
@@ -198,21 +276,21 @@ module Glimmer
198
276
  args[0] = args[0].swt_transform
199
277
  end
200
278
  if ['setBackgroundPattern', 'setForegroundPattern'].include?(method_name.to_s)
201
- @parent.requires_shape_disposal = true
279
+ @drawable.requires_shape_disposal = true
280
+ args = args.first if args.first.is_a?(Array)
202
281
  args.each_with_index do |arg, i|
203
- if arg.is_a?(Symbol) || arg.is_a?(String)
204
- args[i] = ColorProxy.new(arg).swt_color
205
- elsif arg.is_a?(ColorProxy)
206
- args[i] = arg.swt_color
207
- end
282
+ arg = ColorProxy.new(arg.red, arg.green, arg.blue) if arg.is_a?(RGB)
283
+ arg = ColorProxy.new(arg) if arg.is_a?(Symbol) || arg.is_a?(::String)
284
+ arg = arg.swt_color if arg.is_a?(ColorProxy)
285
+ args[i] = arg
208
286
  end
209
287
  @pattern_args ||= {}
210
288
  pattern_type = method_name.to_s.match(/set(.+)Pattern/)[1]
211
289
  if args.first.is_a?(Pattern)
212
290
  new_args = @pattern_args[pattern_type]
213
291
  else
214
- new_args = [DisplayProxy.instance.swt_display] + args
215
- @pattern_args[pattern_type] = new_args
292
+ new_args = args.first.is_a?(Display) ? args : ([DisplayProxy.instance.swt_display] + args)
293
+ @pattern_args[pattern_type] = new_args.dup
216
294
  end
217
295
  args[0] = pattern(*new_args, type: pattern_type)
218
296
  args[1..-1] = []
@@ -226,7 +304,7 @@ module Glimmer
226
304
  @args[1..-1] = []
227
305
  end
228
306
  if @name == 'image'
229
- if @args.first.is_a?(String)
307
+ if @args.first.is_a?(::String)
230
308
  @args[0] = ImageProxy.new(@args[0])
231
309
  end
232
310
  if @args.first.is_a?(ImageProxy)
@@ -237,7 +315,7 @@ module Glimmer
237
315
  end
238
316
  end
239
317
  if @name == 'text'
240
- if @args[3].is_a?(Symbol) || @args[3].is_a?(String)
318
+ if @args[3].is_a?(Symbol) || @args[3].is_a?(::String)
241
319
  @args[3] = [@args[3]]
242
320
  end
243
321
  if @args[3].is_a?(Array)
@@ -250,19 +328,21 @@ module Glimmer
250
328
  end
251
329
 
252
330
  def apply_shape_arg_defaults!
331
+ self.x = :default if current_parameter_name?(:x) && x.nil?
332
+ self.y = :default if current_parameter_name?(:y) && y.nil?
333
+ self.dest_x = :default if current_parameter_name?(:dest_x) && dest_x.nil?
334
+ self.dest_y = :default if current_parameter_name?(:dest_y) && dest_y.nil?
335
+ self.width = :default if current_parameter_name?(:width) && width.nil?
336
+ self.height = :default if current_parameter_name?(:height) && height.nil?
253
337
  if @name.include?('rectangle') && round? && @args.size.between?(4, 5)
254
338
  (6 - @args.size).times {@args << 60}
255
339
  elsif @name.include?('rectangle') && gradient? && @args.size == 4
256
- @args << true
257
- elsif (@name.include?('text') || @name.include?('String')) && !@properties.keys.map(&:to_s).include?('background') && @args.size == 3
258
- @args << true
340
+ set_attribute('vertical', true, redraw: false)
341
+ elsif (@name.include?('text') || @name.include?('string')) && !@properties.keys.map(&:to_s).include?('background') && @args.size < 4
342
+ set_attribute('is_transparent', true, redraw: false)
259
343
  end
260
344
  if @name.include?('image')
261
- @parent.requires_shape_disposal = true
262
- if @args.size == 1
263
- @args[1] = 0
264
- @args[2] = 0
265
- end
345
+ @drawable.requires_shape_disposal = true
266
346
  end
267
347
  end
268
348
 
@@ -279,7 +359,7 @@ module Glimmer
279
359
 
280
360
  def amend_method_name_options_based_on_properties!
281
361
  return if @name == 'point'
282
- if @name != 'text' && has_some_background? && !has_some_foreground?
362
+ if @name != 'text' && @name != 'string' && has_some_background? && !has_some_foreground?
283
363
  @options[:fill] = true
284
364
  elsif !has_some_background? && has_some_foreground?
285
365
  @options[:fill] = false
@@ -298,6 +378,12 @@ module Glimmer
298
378
  []
299
379
  end
300
380
 
381
+ # subclasses may override to specify location parameter names if different from x and y (e.g. all polygon points are location parameters)
382
+ # used in calculating movement changes
383
+ def location_parameter_names
384
+ [:x, :y]
385
+ end
386
+
301
387
  def possible_parameter_names
302
388
  parameter_names
303
389
  end
@@ -306,8 +392,12 @@ module Glimmer
306
392
  possible_parameter_names.map(&:to_s).include?(ruby_attribute_getter(attribute_name))
307
393
  end
308
394
 
395
+ def current_parameter_name?(attribute_name)
396
+ parameter_names.include?(attribute_name.to_s.to_sym)
397
+ end
398
+
309
399
  def parameter_index(attribute_name)
310
- parameter_names.map(&:to_s).index(attribute_name.to_s)
400
+ parameter_names.index(attribute_name.to_s.to_sym)
311
401
  end
312
402
 
313
403
  def set_parameter_attribute(attribute_name, *args)
@@ -316,24 +406,44 @@ module Glimmer
316
406
 
317
407
  def has_attribute?(attribute_name, *args)
318
408
  self.class.gc_instance_methods.include?(attribute_setter(attribute_name)) or
319
- parameter_name?(attribute_name)
409
+ parameter_name?(attribute_name) or
410
+ (respond_to?(attribute_name, super: true) and respond_to?(ruby_attribute_setter(attribute_name), super: true))
320
411
  end
321
-
412
+
322
413
  def set_attribute(attribute_name, *args)
414
+ options = args.last if args.last.is_a?(Hash)
415
+ args.pop if !options.nil? && !options[:redraw].nil?
416
+ perform_redraw = @perform_redraw
417
+ perform_redraw = options[:redraw] if perform_redraw.nil? && !options.nil?
418
+ perform_redraw = true if perform_redraw.nil?
323
419
  if parameter_name?(attribute_name)
324
420
  set_parameter_attribute(attribute_name, *args)
421
+ elsif (respond_to?(attribute_name, super: true) and respond_to?(ruby_attribute_setter(attribute_name), super: true))
422
+ self.send(ruby_attribute_setter(attribute_name), *args)
325
423
  else
326
- @properties[attribute_name] = args
424
+ @properties[ruby_attribute_getter(attribute_name)] = args
327
425
  end
328
- if @content_added && !@parent.is_disposed
426
+ if @content_added && perform_redraw && !drawable.is_disposed
329
427
  @calculated_paint_args = false
330
- @parent.redraw
428
+ attribute_name = ruby_attribute_getter(attribute_name)
429
+ if location_parameter_names.map(&:to_s).include?(attribute_name)
430
+ @calculated_args = nil
431
+ parent.calculated_args_changed_for_defaults! if parent.is_a?(Shape)
432
+ end
433
+ if ['width', 'height'].include?(attribute_name)
434
+ calculated_args_changed_for_defaults!
435
+ end
436
+ # TODO consider redrawing an image proxy's gc in the future
437
+ drawable.redraw unless drawable.is_a?(ImageProxy)
331
438
  end
332
439
  end
333
440
 
334
441
  def get_attribute(attribute_name)
335
442
  if parameter_name?(attribute_name)
336
- @args[parameter_index(attribute_name)]
443
+ arg_index = parameter_index(attribute_name)
444
+ @args[arg_index] if arg_index
445
+ elsif (respond_to?(attribute_name, super: true) and respond_to?(ruby_attribute_setter(attribute_name), super: true))
446
+ self.send(attribute_name)
337
447
  else
338
448
  @properties.symbolize_keys[attribute_name.to_s.to_sym]
339
449
  end
@@ -350,7 +460,9 @@ module Glimmer
350
460
  end
351
461
 
352
462
  def respond_to?(method_name, *args, &block)
353
- if has_attribute?(method_name)
463
+ options = args.last if args.last.is_a?(Hash)
464
+ super_invocation = options && options[:super]
465
+ if !super_invocation && has_attribute?(method_name)
354
466
  true
355
467
  else
356
468
  super
@@ -366,6 +478,18 @@ module Glimmer
366
478
  the_pattern
367
479
  end
368
480
 
481
+ def pattern_args(type: nil)
482
+ @pattern_args && @pattern_args[type.to_s.capitalize]
483
+ end
484
+
485
+ def background_pattern_args
486
+ pattern_args(type: 'background')
487
+ end
488
+
489
+ def foreground_pattern_args
490
+ pattern_args(type: 'foreground')
491
+ end
492
+
369
493
  def dispose(dispose_images: true, dispose_patterns: true)
370
494
  if dispose_patterns
371
495
  @background_pattern&.dispose
@@ -379,21 +503,297 @@ module Glimmer
379
503
  end
380
504
  @parent.shapes.delete(self)
381
505
  end
382
-
506
+
383
507
  def paint(paint_event)
508
+ # pre-paint children an extra-time first when default width/height need to be calculated for defaults
509
+ paint_children(paint_event) if default_width? || default_height?
510
+ paint_self(paint_event)
511
+ # re-paint children from scratch in the special case of pre-calculating parent width/height to re-center within new parent dimensions
512
+ shapes.each(&:calculated_args_changed!) if default_width? || default_height?
513
+ paint_children(paint_event)
514
+ rescue => e
515
+ Glimmer::Config.logger.error {"Error encountered in painting shape (#{self.inspect}) with calculated args (#{@calculated_args}) and args (#{@args})"}
516
+ Glimmer::Config.logger.error {e.full_message}
517
+ end
518
+
519
+ def paint_self(paint_event)
520
+ @painting = true
384
521
  calculate_paint_args!
522
+ @original_properties_backup = {}
385
523
  @properties.each do |property, args|
386
524
  method_name = attribute_setter(property)
387
- # TODO consider optimization of not setting a background/foreground/font if it didn't change from last shape
525
+ @original_properties_backup[method_name] = paint_event.gc.send(method_name.sub('set', 'get')) rescue nil
388
526
  paint_event.gc.send(method_name, *args)
389
527
  if property == 'transform' && args.first.is_a?(TransformProxy)
390
528
  args.first.swt_transform.dispose
391
529
  end
392
530
  end
393
- paint_event.gc.send(@method_name, *@args)
531
+ ensure_extent(paint_event)
532
+ if !@calculated_args || parent_shape_absolute_location_changed?
533
+ @calculated_args = calculated_args
534
+ end
535
+ # 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
536
+ paint_event.gc.send(@method_name, *@calculated_args) unless parent.is_a?(Shape) && !parent.calculated_args?
537
+ @original_properties_backup.each do |method_name, value|
538
+ paint_event.gc.send(method_name, value)
539
+ end
540
+ @painting = false
394
541
  rescue => e
395
- Glimmer::Config.logger.error {"Error encountered in painting shape: #{self.inspect}"}
542
+ Glimmer::Config.logger.error {"Error encountered in painting shape (#{self.inspect}) with calculated args (#{@calculated_args}) and args (#{@args})"}
396
543
  Glimmer::Config.logger.error {e.full_message}
544
+ ensure
545
+ @painting = false
546
+ end
547
+
548
+ def paint_children(paint_event)
549
+ shapes.to_a.each do |shape|
550
+ shape.paint(paint_event)
551
+ end
552
+ end
553
+
554
+ def ensure_extent(paint_event)
555
+ old_extent = @extent
556
+ if ['text', 'string'].include?(@name)
557
+ extent_args = [string]
558
+ extent_flags = SWTProxy[:draw_transparent] if current_parameter_name?(:is_transparent) && is_transparent
559
+ extent_flags = flags if current_parameter_name?(:flags)
560
+ extent_args << extent_flags unless extent_flags.nil?
561
+ self.extent = paint_event.gc.send("#{@name}Extent", *extent_args)
562
+ end
563
+ if !@extent.nil? && (old_extent&.x != @extent&.x || old_extent&.y != @extent&.y)
564
+ calculated_args_changed!
565
+ parent.calculated_args_changed_for_defaults! if parent.is_a?(Shape)
566
+ end
567
+ end
568
+
569
+ def expanded_shapes
570
+ if shapes.to_a.any?
571
+ shapes.map do |shape|
572
+ [shape] + shape.expanded_shapes
573
+ end.flatten
574
+ else
575
+ []
576
+ end
577
+ end
578
+
579
+ def parent_shape_absolute_location_changed?
580
+ (parent.is_a?(Shape) && (parent.absolute_x != @parent_absolute_x || parent.absolute_y != @parent_absolute_y))
581
+ end
582
+
583
+ def calculated_args_changed!(children: true)
584
+ # TODO add a children: true option to enable setting to false to avoid recalculating children args
585
+ @calculated_args = nil
586
+ shapes.each(&:calculated_args_changed!) if children
587
+ end
588
+
589
+ def calculated_args_changed_for_defaults!
590
+ has_default_dimensions = default_width? || default_height?
591
+ parent_calculated_args_changed_for_defaults = has_default_dimensions
592
+ @calculated_args = nil if default_x? || default_y? || has_default_dimensions
593
+ if has_default_dimensions && parent.is_a?(Shape)
594
+ parent.calculated_args_changed_for_defaults!
595
+ elsif @content_added && !drawable.is_disposed
596
+ # 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
597
+ drawable.redraw if !@painting && !drawable.is_a?(ImageProxy)
598
+ end
599
+ end
600
+
601
+ def calculated_args?
602
+ !!@calculated_args
603
+ end
604
+
605
+ # args translated to absolute coordinates
606
+ def calculated_args
607
+ return @args if !default_x? && !default_y? && !default_width? && !default_height? && parent.is_a?(Drawable)
608
+ # 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
609
+ # TODO change that by setting a bounding box for all shapes with a calculated top-left x, y and
610
+ # a setter that does the moving inside them instead so that I could rely on absolute_x and absolute_y
611
+ # here to get the job done of calculating absolute args
612
+ @perform_redraw = false
613
+ original_x = nil
614
+ original_y = nil
615
+ original_width = nil
616
+ original_height = nil
617
+ if parent.is_a?(Shape)
618
+ @parent_absolute_x = parent.absolute_x
619
+ @parent_absolute_y = parent.absolute_y
620
+ end
621
+ if default_width?
622
+ original_width = width
623
+ self.width = default_width + default_width_delta
624
+ end
625
+ if default_height?
626
+ original_height = height
627
+ self.height = default_height + default_height_delta
628
+ end
629
+ if default_x?
630
+ original_x = x
631
+ self.x = default_x + default_x_delta
632
+ end
633
+ if default_y?
634
+ original_y = y
635
+ self.y = default_y + default_y_delta
636
+ end
637
+ if parent.is_a?(Shape)
638
+ move_by(@parent_absolute_x, @parent_absolute_y)
639
+ result_args = @args.clone
640
+ move_by(-1*@parent_absolute_x, -1*@parent_absolute_y)
641
+ else
642
+ result_args = @args.clone
643
+ end
644
+ if original_x
645
+ self.x = original_x
646
+ end
647
+ if original_y
648
+ self.y = original_y
649
+ end
650
+ if original_width
651
+ self.width = original_width
652
+ end
653
+ if original_height
654
+ self.height = original_height
655
+ end
656
+ @perform_redraw = true
657
+ result_args
658
+ end
659
+
660
+ def default_x?
661
+ current_parameter_name?(:x) and
662
+ (x.nil? || x.to_s == 'default' || (x.is_a?(Array) && x.first.to_s == 'default'))
663
+ end
664
+
665
+ def default_y?
666
+ current_parameter_name?(:y) and
667
+ (y.nil? || y.to_s == 'default' || (y.is_a?(Array) && y.first.to_s == 'default'))
668
+ end
669
+
670
+ def default_width?
671
+ return false unless current_parameter_name?(:width)
672
+ width = self.width
673
+ (width.nil? || width == :default || width == 'default' || (width.is_a?(Array) && (width.first.to_s == :default || width.first.to_s == 'default')))
674
+ end
675
+
676
+ def default_height?
677
+ return false unless current_parameter_name?(:height)
678
+ height = self.height
679
+ (height.nil? || height == :default || height == 'default' || (height.is_a?(Array) && (height.first.to_s == :default || height.first.to_s == 'default')))
680
+ end
681
+
682
+ def default_x
683
+ result = ((parent.size.x - size.x) / 2)
684
+ result += parent.bounds.x - parent.absolute_x if parent.is_a?(Shape) && parent.irregular?
685
+ result
686
+ end
687
+
688
+ def default_y
689
+ result = ((parent.size.y - size.y) / 2)
690
+ result += parent.bounds.y - parent.absolute_y if parent.is_a?(Shape) && parent.irregular?
691
+ result
692
+ end
693
+
694
+ def default_width
695
+ # TODO consider caching
696
+ x_ends = shapes.map do |shape|
697
+ shape_width = shape.calculated_width.to_f
698
+ shape_x = shape.default_x? ? 0 : shape.x.to_f
699
+ shape_x + shape_width
700
+ end
701
+ x_ends.max.to_f
702
+ end
703
+
704
+ def default_height
705
+ # TODO consider caching
706
+ y_ends = shapes.map do |shape|
707
+ shape_height = shape.calculated_height.to_f
708
+ shape_y = shape.default_y? ? 0 : shape.y.to_f
709
+ shape_y + shape_height
710
+ end
711
+ y_ends.max.to_f
712
+ end
713
+
714
+ def calculated_width
715
+ default_width? ? (default_width + default_width_delta) : width
716
+ end
717
+
718
+ def calculated_height
719
+ default_height? ? (default_height + default_height_delta) : height
720
+ end
721
+
722
+ def default_x_delta
723
+ return 0 unless default_x? && x.is_a?(Array)
724
+ x[1].to_f
725
+ end
726
+
727
+ def default_y_delta
728
+ return 0 unless default_y? && y.is_a?(Array)
729
+ y[1].to_f
730
+ end
731
+
732
+ def default_width_delta
733
+ return 0 unless default_width? && width.is_a?(Array)
734
+ width[1].to_f
735
+ end
736
+
737
+ def default_height_delta
738
+ return 0 unless default_height? && height.is_a?(Array)
739
+ height[1].to_f
740
+ end
741
+
742
+ def default_x_delta=(delta)
743
+ return unless default_x?
744
+ self.x = [:default, delta]
745
+ end
746
+
747
+ def default_y_delta=(delta)
748
+ return unless default_y?
749
+ self.y = [:default, delta]
750
+ end
751
+
752
+ def default_width_delta=(delta)
753
+ return unless default_width?
754
+ self.width = [:default, delta]
755
+ end
756
+
757
+ def default_height_delta=(delta)
758
+ return unless default_height?
759
+ self.height = [:default, delta]
760
+ end
761
+
762
+ def calculated_x
763
+ result = default_x? ? default_x : self.x
764
+ result += default_x_delta
765
+ result
766
+ end
767
+
768
+ def calculated_y
769
+ result = default_y? ? default_y : self.y
770
+ result += default_y_delta
771
+ result
772
+ end
773
+
774
+ def absolute_x
775
+ x = calculated_x
776
+ if parent.is_a?(Shape)
777
+ parent.absolute_x + x
778
+ else
779
+ x
780
+ end
781
+ end
782
+
783
+ def absolute_y
784
+ y = calculated_y
785
+ if parent.is_a?(Shape)
786
+ parent.absolute_y + y
787
+ else
788
+ y
789
+ end
790
+ end
791
+
792
+ # Overriding inspect to avoid printing very long shape hierarchies
793
+ def inspect
794
+ "#<#{self.class.name}:0x#{self.hash.to_s(16)} args=#{@args.inspect}, properties=#{@properties.inspect}}>"
795
+ rescue => e
796
+ "#<#{self.class.name}:0x#{self.hash.to_s(16)}"
397
797
  end
398
798
 
399
799
  def calculate_paint_args!
@@ -405,7 +805,7 @@ module Glimmer
405
805
  if @properties[:foreground].is_a?(Array)
406
806
  @properties[:foreground] = ColorProxy.new(@properties[:foreground], ensure_bounds: false)
407
807
  end
408
- if @properties[:foreground].is_a?(Symbol) || @properties[:foreground].is_a?(String)
808
+ if @properties[:foreground].is_a?(Symbol) || @properties[:foreground].is_a?(::String)
409
809
  @properties[:foreground] = ColorProxy.new(@properties[:foreground], ensure_bounds: false)
410
810
  end
411
811
  if @properties[:foreground].is_a?(ColorProxy)
@@ -413,14 +813,14 @@ module Glimmer
413
813
  end
414
814
  end
415
815
  else
416
- @properties['background'] = [@parent.background] if fill? && !has_some_background?
417
- @properties['foreground'] = [@parent.foreground] if @parent.respond_to?(:foreground) && draw? && !has_some_foreground?
816
+ @properties['background'] = [@drawable.background] if fill? && !has_some_background?
817
+ @properties['foreground'] = [@drawable.foreground] if @drawable.respond_to?(:foreground) && draw? && !has_some_foreground?
418
818
  # TODO regarding alpha, make sure to reset it to parent stored alpha once we allow setting shape properties on parents directly without shapes
419
819
  @properties['alpha'] ||= [255]
420
- @properties['font'] = [@parent.font] if @parent.respond_to?(:font) && draw? && !@properties.keys.map(&:to_s).include?('font')
820
+ @properties['font'] = [@drawable.font] if @drawable.respond_to?(:font) && draw? && !@properties.keys.map(&:to_s).include?('font')
421
821
  # TODO regarding transform, make sure to reset it to parent stored alpha once we allow setting shape properties on parents directly without shapes
422
822
  # Also do that with all future-added properties
423
- @properties['transform'] = [nil] if @parent.respond_to?(:transform) && !@properties.keys.map(&:to_s).include?('transform')
823
+ @properties['transform'] = [nil] if @drawable.respond_to?(:transform) && !@properties.keys.map(&:to_s).include?('transform')
424
824
  @properties.each do |property, args|
425
825
  method_name = attribute_setter(property)
426
826
  converted_args = apply_property_arg_conversions(method_name, property, args)