glimmer-dsl-swt 4.18.3.4 → 4.18.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/README.md +600 -331
  4. data/VERSION +1 -1
  5. data/glimmer-dsl-swt.gemspec +8 -3
  6. data/lib/ext/rouge/themes/glimmer.rb +29 -0
  7. data/lib/glimmer-dsl-swt.rb +0 -1
  8. data/lib/glimmer/data_binding/widget_binding.rb +1 -1
  9. data/lib/glimmer/swt/custom/code_text.rb +200 -51
  10. data/lib/glimmer/swt/custom/drawable.rb +1 -4
  11. data/lib/glimmer/swt/custom/shape.rb +50 -6
  12. data/lib/glimmer/swt/date_time_proxy.rb +1 -3
  13. data/lib/glimmer/swt/display_proxy.rb +11 -0
  14. data/lib/glimmer/swt/font_proxy.rb +1 -0
  15. data/lib/glimmer/swt/image_proxy.rb +11 -0
  16. data/lib/glimmer/swt/layout_proxy.rb +4 -1
  17. data/lib/glimmer/swt/shell_proxy.rb +4 -1
  18. data/lib/glimmer/swt/table_proxy.rb +17 -13
  19. data/lib/glimmer/swt/widget_proxy.rb +11 -3
  20. data/samples/elaborate/meta_sample.rb +15 -4
  21. data/samples/elaborate/meta_sample/meta_sample_logo.png +0 -0
  22. data/samples/elaborate/tetris.rb +16 -6
  23. data/samples/elaborate/tetris/model/game.rb +3 -3
  24. data/samples/elaborate/tetris/model/past_game.rb +14 -1
  25. data/samples/elaborate/tetris/view/block.rb +1 -1
  26. data/samples/elaborate/tetris/view/high_score_dialog.rb +1 -10
  27. data/samples/elaborate/tetris/view/playfield.rb +1 -1
  28. data/samples/elaborate/tetris/view/tetris_menu_bar.rb +13 -11
  29. data/samples/hello/hello_canvas.rb +7 -6
  30. data/samples/hello/hello_canvas_animation.rb +3 -3
  31. data/samples/hello/hello_canvas_transform.rb +1 -1
  32. data/samples/hello/hello_canvas_transform/hello_canvas_transform_image.png +0 -0
  33. data/samples/hello/hello_code_text.rb +104 -0
  34. data/samples/hello/hello_table.rb +7 -4
  35. data/samples/hello/hello_table/baseball_park.png +0 -0
  36. metadata +7 -2
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.18.3.4
1
+ 4.18.4.3
@@ -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.3.4 ruby lib
5
+ # stub: glimmer-dsl-swt 4.18.4.3 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "glimmer-dsl-swt".freeze
9
- s.version = "4.18.3.4"
9
+ s.version = "4.18.4.3"
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-01-30"
14
+ s.date = "2021-02-07"
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]
@@ -34,6 +34,7 @@ Gem::Specification.new do |s|
34
34
  "icons/scaffold_app.png",
35
35
  "lib/ext/glimmer.rb",
36
36
  "lib/ext/glimmer/config.rb",
37
+ "lib/ext/rouge/themes/glimmer.rb",
37
38
  "lib/glimmer-dsl-swt.rb",
38
39
  "lib/glimmer/Rakefile",
39
40
  "lib/glimmer/data_binding/list_selection_binding.rb",
@@ -135,6 +136,7 @@ Gem::Specification.new do |s|
135
136
  "samples/elaborate/contact_manager/contact_repository.rb",
136
137
  "samples/elaborate/login.rb",
137
138
  "samples/elaborate/meta_sample.rb",
139
+ "samples/elaborate/meta_sample/meta_sample_logo.png",
138
140
  "samples/elaborate/tetris.rb",
139
141
  "samples/elaborate/tetris/model/block.rb",
140
142
  "samples/elaborate/tetris/model/game.rb",
@@ -154,8 +156,10 @@ Gem::Specification.new do |s|
154
156
  "samples/hello/hello_canvas.rb",
155
157
  "samples/hello/hello_canvas_animation.rb",
156
158
  "samples/hello/hello_canvas_transform.rb",
159
+ "samples/hello/hello_canvas_transform/hello_canvas_transform_image.png",
157
160
  "samples/hello/hello_checkbox.rb",
158
161
  "samples/hello/hello_checkbox_group.rb",
162
+ "samples/hello/hello_code_text.rb",
159
163
  "samples/hello/hello_combo.rb",
160
164
  "samples/hello/hello_computed.rb",
161
165
  "samples/hello/hello_computed/contact.rb",
@@ -181,6 +185,7 @@ Gem::Specification.new do |s|
181
185
  "samples/hello/hello_styled_text.rb",
182
186
  "samples/hello/hello_tab.rb",
183
187
  "samples/hello/hello_table.rb",
188
+ "samples/hello/hello_table/baseball_park.png",
184
189
  "samples/hello/hello_world.rb",
185
190
  "vendor/swt/linux/swt.jar",
186
191
  "vendor/swt/mac/swt.jar",
@@ -0,0 +1,29 @@
1
+ module Rouge
2
+ module Themes
3
+ # A port of the pastie style from Pygments.
4
+ # See https://bitbucket.org/birkenfeld/pygments-main/src/default/pygments/styles/pastie.py
5
+ class Glimmer < Github
6
+ name 'glimmer'
7
+ style Comment::Single, fg: [106,115,125], italic: true # Also, Comments
8
+ style Keyword::Pseudo, fg: [:dark_red]
9
+ style Keyword, fg: [:blue]
10
+ style Literal::String::Single, fg: [106,115,125] # Also, Comments
11
+ style Literal::String::Double, fg: [0,92,197]
12
+ style Literal::String::Escape, fg: [:red]
13
+ style Literal::Number::Integer, fg: [:blue]
14
+ style Literal::String::Interpol, fg: [:blue]
15
+ style Literal::String::Symbol, fg: [:dark_green]
16
+ style Literal::String, fg: [:dark_blue]
17
+ style Name::Builtin, fg: [215,58,73]
18
+ style Name::Class, fg: [3,47,98]
19
+ style Name::Namespace, fg: [3,47,98]
20
+ style Name::Constant, fg: [0,92,197]
21
+ style Name::Function, fg: [:blue]
22
+ style Name::Variable::Instance, fg: [227,98,9]
23
+ style Name, fg: [111,66,193] #purple
24
+ style Operator, fg: [:red]
25
+ style Punctuation, fg: [:blue]
26
+ style Text, fg: [75, 75, 75]
27
+ end
28
+ end
29
+ end
@@ -34,7 +34,6 @@ require 'glimmer'
34
34
  require 'logging'
35
35
  require 'nested_inherited_jruby_include_package'
36
36
  require 'super_module'
37
- require 'rouge'
38
37
  require 'date'
39
38
  require 'facets/string/capitalized'
40
39
  require 'facets/hash/symbolize_keys'
@@ -37,7 +37,7 @@ module Glimmer
37
37
  @property = property
38
38
  @translator = translator || proc {|value| value} #TODO check on this it doesn't seem used
39
39
 
40
- if @widget.respond_to?(:dispose)
40
+ if @widget.respond_to?(:on_widget_disposed)
41
41
  @widget.on_widget_disposed do |dispose_event|
42
42
  unregister_all_observables
43
43
  end
@@ -7,65 +7,143 @@ module Glimmer
7
7
  class CodeText
8
8
  include Glimmer::UI::CustomWidget
9
9
 
10
- SYNTAX_COLOR_MAP = {
11
- Builtin: [215,58,73],
12
- Class: [3,47,98],
13
- Constant: [0,92,197],
14
- Double: [0,92,197],
15
- Escape: [:red],
16
- Function: [:blue],
17
- Instance: [227,98,9],
18
- Integer: [:blue],
19
- Interpol: [:blue],
20
- Keyword: [:blue],
21
- Name: [111,66,193], #purple
22
- Operator: [:red],
23
- Pseudo: [:dark_red],
24
- Punctuation: [:blue],
25
- Single: [106,115,125], # Also, Comments
26
- Symbol: [:dark_green],
27
- Text: [75, 75, 75],
28
- }
10
+ class << self
11
+ def languages
12
+ require 'rouge'
13
+ Rouge::Lexer.all.map {|lexer| lexer.tag}.sort
14
+ end
15
+
16
+ def lexers
17
+ require 'rouge'
18
+ Rouge::Lexer.all.sort_by(&:title)
19
+ end
20
+ end
21
+
22
+ REGEX_COLOR_HEX6 = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/
29
23
 
30
- # TODO support `option :language`
31
24
  # TODO support auto language detection
25
+ # TODO support end of line via CMD+E and beginning of line via CMD+A
26
+ # TODO support select all via CMD+A
27
+
28
+ option :language, default: 'ruby'
29
+ # TODO consider supporting data-binding of language
30
+ # TODO support switcher of language that automatically updates the lexer
31
+ # TODO support method for redrawing the syntax highlighting
32
+ option :theme, default: 'glimmer'
33
+ option :lines, default: false
32
34
 
33
- def text=(value)
34
- swt_widget&.text = value
35
- end
35
+ alias lines? lines
36
+ attr_accessor :styled_text_proxy_text, :styled_text_proxy_top_pixel
37
+ attr_reader :styled_text_proxy, :lines_width, :line_numbers_styled_text_proxy
36
38
 
37
- def text
38
- swt_widget&.text
39
+ def method_missing(method_name, *args, &block)
40
+ dsl_mode = @dsl_mode || args.last.is_a?(Hash) && args.last[:dsl]
41
+ if dsl_mode
42
+ args.pop if args.last.is_a?(Hash) && args.last[:dsl]
43
+ super(method_name, *args, &block)
44
+ elsif @styled_text_proxy&.respond_to?(method_name, *args, &block)
45
+ @styled_text_proxy&.send(method_name, *args, &block)
46
+ else
47
+ super
48
+ end
39
49
  end
40
50
 
41
- def syntax_highlighting(text)
42
- return [] if text.to_s.strip.empty?
43
- @syntax_highlighting ||= {}
44
- unless @syntax_highlighting.keys.include?(text)
45
- lex = lexer.lex(text).to_a
46
- text_size = 0
47
- @syntax_highlighting[text] = lex.map do |pair|
48
- {token_type: pair.first, token_text: pair.last}
49
- end.each do |hash|
50
- hash[:token_index] = text_size
51
- text_size += hash[:token_text].size
52
- end
51
+ def respond_to?(method_name, *args, &block)
52
+ dsl_mode = @dsl_mode || args.last.is_a?(Hash) && args.last[:dsl]
53
+ if dsl_mode
54
+ args = args[0...-1] if args.last.is_a?(Hash) && args.last[:dsl]
55
+ super(method_name, *args, &block)
56
+ else
57
+ super || @styled_text_proxy&.respond_to?(method_name, *args, &block)
53
58
  end
54
- @syntax_highlighting[text]
55
59
  end
56
60
 
57
- def lexer
58
- # TODO Try to use Rouge::Lexer.find_fancy('guess', code) in the future to guess the language or otherwise detect it from file extension
59
- @lexer ||= Rouge::Lexer.find_fancy('ruby')
61
+ def has_instance_method?(method_name)
62
+ respond_to?(method_name)
63
+ end
64
+
65
+ def root_block=(block)
66
+ body_root.content(&block)
67
+ end
68
+
69
+ def line_numbers_block=(block)
70
+ @line_numbers_styled_text_proxy.content(&block)
60
71
  end
61
72
 
62
73
  before_body {
74
+ require 'rouge'
75
+ require 'ext/rouge/themes/glimmer'
63
76
  @swt_style = swt_style == 0 ? [:border, :multi, :v_scroll, :h_scroll] : swt_style
64
77
  @font_name = display.get_font_list(nil, true).map(&:name).include?('Consolas') ? 'Consolas' : 'Courier'
78
+ if lines == true
79
+ @lines_width = 4
80
+ elsif lines.is_a?(Hash)
81
+ @lines_width = lines[:width]
82
+ end
83
+ @dsl_mode = true
84
+ }
85
+
86
+ after_body {
87
+ @dsl_mode = nil
65
88
  }
66
89
 
67
90
  body {
68
- styled_text(swt_style) {
91
+ # TODO enable this once fully implemented
92
+ if lines
93
+ composite {
94
+ grid_layout(2, false)
95
+
96
+ @line_numbers_styled_text_proxy = styled_text(swt(swt(swt_style), :h_scroll!, :v_scroll!)) {
97
+ layout_data(:right, :fill, false, true)
98
+ text bind(self, :styled_text_proxy_text, read_only: true) { |text_value|
99
+ line_count = "#{text_value} ".split("\n").count
100
+ line_count = 1 if line_count == 0
101
+ lines_text_size = [line_count.to_s.size, @lines_width].max
102
+ if lines_text_size > @lines_width
103
+ async_exec {
104
+ swt_widget.layout
105
+ }
106
+ @lines_width = lines_text_size
107
+ end
108
+ async_exec {
109
+ @line_numbers_styled_text_proxy&.top_pixel = styled_text_proxy_top_pixel
110
+ }
111
+ line_count.times.map {|n| (' ' * (lines_text_size - (n+1).to_s.size)) + (n+1).to_s }.join("\n") + "\n"
112
+ }
113
+ top_pixel bind(self, :styled_text_proxy_top_pixel, read_only: true)
114
+ font name: @font_name, height: OS.mac? ? 15 : 12
115
+ background color(:widget_background)
116
+ foreground :dark_blue
117
+ top_margin 5
118
+ right_margin 5
119
+ bottom_margin 5
120
+ left_margin 5
121
+ editable false
122
+ caret nil
123
+ on_focus_gained {
124
+ @styled_text_proxy&.swt_widget.setFocus
125
+ }
126
+ on_key_pressed {
127
+ @styled_text_proxy&.swt_widget.setFocus
128
+ }
129
+ on_mouse_up {
130
+ @styled_text_proxy&.swt_widget.setFocus
131
+ }
132
+ }
133
+
134
+ code_text_widget
135
+ }
136
+ else
137
+ code_text_widget
138
+ end
139
+ }
140
+
141
+ def code_text_widget
142
+ @styled_text_proxy = styled_text(swt_style) {
143
+ # custom_widget_property_owner # TODO implement to route properties here without declaring method_missing
144
+ layout_data :fill, :fill, true, true if lines
145
+ text bind(self, :styled_text_proxy_text) if lines
146
+ top_pixel bind(self, :styled_text_proxy_top_pixel) if lines
69
147
  font name: @font_name, height: 15
70
148
  foreground rgb(75, 75, 75)
71
149
  left_margin 5
@@ -73,6 +151,20 @@ module Glimmer
73
151
  right_margin 5
74
152
  bottom_margin 5
75
153
 
154
+ if lines
155
+ on_key_pressed { |event|
156
+ character = event.keyCode.chr rescue nil
157
+ case [event.stateMask, character]
158
+ when [(OS.mac? ? swt(:command) : swt(:ctrl)), 'a']
159
+ @styled_text_proxy.swt_widget.selectAll
160
+ when [(swt(:ctrl) unless OS.windows?), 'a']
161
+ jump_to_beginning_of_line
162
+ when [(swt(:ctrl) unless OS.windows?), 'e']
163
+ jump_to_end_of_line
164
+ end
165
+ }
166
+ end
167
+
76
168
  on_modify_text { |event|
77
169
  # clear unnecessary syntax highlighting cache on text updates, and do it async to avoid affecting performance
78
170
  new_text = event.data
@@ -88,18 +180,75 @@ module Glimmer
88
180
  }
89
181
 
90
182
  on_line_get_style { |line_style_event|
91
- styles = []
92
- syntax_highlighting(line_style_event.lineText).to_a.each do |token_hash|
93
- start_index = token_hash[:token_index]
94
- size = token_hash[:token_text].size
95
- token_color = SYNTAX_COLOR_MAP[token_hash[:token_type].name] || [:black]
96
- token_color = color(*token_color).swt_color
97
- styles << StyleRange.new(line_style_event.lineOffset + start_index, size, token_color, nil)
183
+ begin
184
+ styles = []
185
+ style_data = nil
186
+ syntax_highlighting(line_style_event.lineText).to_a.each do |token_hash|
187
+ start_index = token_hash[:token_index]
188
+ size = token_hash[:token_text].size
189
+ style_data = Rouge::Theme.find(theme).new.style_for(token_hash[:token_type])
190
+ foreground_color = hex_color_to_swt_color(style_data[:fg], [:black])
191
+ background_color = hex_color_to_swt_color(style_data[:bg], [:white])
192
+ font_styles = []
193
+ font_styles << :bold if style_data[:bold]
194
+ font_styles << :italic if style_data[:italic]
195
+ font_style = SWTProxy[*font_styles]
196
+ styles << StyleRange.new(line_style_event.lineOffset + start_index, size, foreground_color, background_color, font_style)
197
+ end
198
+ line_style_event.styles = styles.to_java(StyleRange) unless styles.empty?
199
+ rescue => e
200
+ Glimmer::Config.logger.error {"Error encountered with style data: #{style_data}"}
201
+ Glimmer::Config.logger.error {e.message}
202
+ Glimmer::Config.logger.error {e.full_message}
98
203
  end
99
- line_style_event.styles = styles.to_java(StyleRange) unless styles.empty?
100
204
  }
101
205
  }
102
- }
206
+ end
207
+
208
+ def syntax_highlighting(text)
209
+ return [] if text.to_s.strip.empty?
210
+ @syntax_highlighting ||= {}
211
+ unless @syntax_highlighting.keys.include?(text)
212
+ lex = lexer.lex(text).to_a
213
+ text_size = 0
214
+ @syntax_highlighting[text] = lex.map do |pair|
215
+ {token_type: pair.first, token_text: pair.last}
216
+ end.each do |hash|
217
+ hash[:token_index] = text_size
218
+ text_size += hash[:token_text].size
219
+ end
220
+ end
221
+ @syntax_highlighting[text]
222
+ end
223
+
224
+ def lexer
225
+ require 'rouge'
226
+ # TODO Try to use Rouge::Lexer.find_fancy('guess', code) in the future to guess the language or otherwise detect it from file extension
227
+ @lexer ||= Rouge::Lexer.find_fancy(language)
228
+ @lexer ||= Rouge::Lexer.find_fancy('ruby') # default to Ruby if no lexer is found
229
+ end
230
+
231
+ def hex_color_to_swt_color(color_data, default_color)
232
+ color_data = "##{color_data.chars.drop(1).map {|c| c*2}.join}" if color_data.is_a?(String) && color_data.start_with?('#') && color_data&.size == 4
233
+ color_data = color_data.match(REGEX_COLOR_HEX6).to_a.drop(1).map {|c| "0x#{c}".hex}.to_a if color_data.is_a?(String) && color_data.start_with?('#')
234
+ color_data = [color_data] unless color_data.nil? || color_data.empty? || color_data.is_a?(Array)
235
+ color_data = default_color if color_data.nil? || color_data.empty?
236
+ color(*color_data).swt_color
237
+ end
238
+
239
+ def jump_to_beginning_of_line
240
+ current_line_index = @styled_text_proxy.swt_widget.getLineAtOffset(@styled_text_proxy.swt_widget.getCaretOffset)
241
+ beginning_of_current_line_offset = @styled_text_proxy.swt_widget.getOffsetAtLine(current_line_index)
242
+ @styled_text_proxy.swt_widget.setSelection(beginning_of_current_line_offset, beginning_of_current_line_offset)
243
+ end
244
+
245
+ def jump_to_end_of_line
246
+ current_line_index = @styled_text_proxy.swt_widget.getLineAtOffset(@styled_text_proxy.swt_widget.getCaretOffset)
247
+ current_line = @styled_text_proxy.swt_widget.getLine(current_line_index)
248
+ beginning_of_current_line_offset = @styled_text_proxy.swt_widget.getOffsetAtLine(current_line_index)
249
+ new_offset = beginning_of_current_line_offset + current_line.size
250
+ @styled_text_proxy.swt_widget.setSelection(new_offset, new_offset)
251
+ end
103
252
  end
104
253
  end
105
254
  end
@@ -29,10 +29,7 @@ module Glimmer
29
29
  end
30
30
 
31
31
  def clear_shapes
32
- shapes.dup.each do |shape|
33
- shape.paint_listener_proxy&.unregister
34
- shapes.delete(shape)
35
- end
32
+ shapes.dup.each(&:dispose)
36
33
  end
37
34
 
38
35
  def resetup_shape_painting
@@ -78,7 +78,17 @@ module Glimmer
78
78
  @flyweight_method_names ||= {}
79
79
  end
80
80
 
81
+ def pattern(*args)
82
+ found_pattern = flyweigh_patterns[args]
83
+ if found_pattern.nil? || found_pattern.is_disposed
84
+ found_pattern = flyweigh_patterns[args] = org.eclipse.swt.graphics.Pattern.new(*args)
85
+ end
86
+ found_pattern
87
+ end
81
88
 
89
+ def flyweigh_patterns
90
+ @flyweigh_patterns ||= {}
91
+ end
82
92
  end
83
93
 
84
94
  attr_reader :parent, :name, :args, :options, :paint_listener_proxy
@@ -110,6 +120,14 @@ module Glimmer
110
120
  @options[:round]
111
121
  end
112
122
 
123
+ def has_some_background?
124
+ @properties.keys.map(&:to_s).include?('background') || @properties.keys.map(&:to_s).include?('background_pattern')
125
+ end
126
+
127
+ def has_some_foreground?
128
+ @properties.keys.map(&:to_s).include?('foreground') || @properties.keys.map(&:to_s).include?('foreground_pattern')
129
+ end
130
+
113
131
  def post_add_content
114
132
  amend_method_name_options_based_on_properties
115
133
  setup_painting
@@ -148,7 +166,7 @@ module Glimmer
148
166
  end
149
167
  end
150
168
  new_args = [DisplayProxy.instance.swt_display] + args
151
- args[0] = org.eclipse.swt.graphics.Pattern.new(*new_args)
169
+ args[0] = pattern(*new_args, type: method_name.to_s.match(/set(.+)Pattern/)[1])
152
170
  args[1..-1] = []
153
171
  end
154
172
  args
@@ -173,7 +191,7 @@ module Glimmer
173
191
  args[0] = ImageProxy.new(args[0])
174
192
  end
175
193
  if method_name.include?('image') && args.first.is_a?(ImageProxy)
176
- args[0] = args[0].swt_image
194
+ @image = args[0] = args[0].swt_image
177
195
  end
178
196
  end
179
197
 
@@ -189,10 +207,16 @@ module Glimmer
189
207
  end
190
208
 
191
209
  def amend_method_name_options_based_on_properties
192
- if @properties.keys.map(&:to_s).include?('background') && !@properties.keys.map(&:to_s).include?('foreground')
210
+ if has_some_background? && !has_some_foreground?
193
211
  @options[:fill] = true
194
- elsif @properties.keys.map(&:to_s).include?('foreground') && !@properties.keys.map(&:to_s).include?('background')
212
+ elsif !has_some_background? && has_some_foreground?
195
213
  @options[:fill] = false
214
+ elsif @name == 'rectangle' && has_some_background? && has_some_foreground?
215
+ @options[:fill] = true
216
+ @options[:gradient] = true
217
+ end
218
+ if @name == 'rectangle' && @args.size > 4 && @args.last.is_a?(Numeric)
219
+ @options[:round] = true
196
220
  end
197
221
  @method_name = self.class.method_name(@name, @args + [@options])
198
222
  end
@@ -213,6 +237,26 @@ module Glimmer
213
237
  @properties.symbolize_keys[attribute_name.to_s.to_sym]
214
238
  end
215
239
 
240
+ def pattern(*args, type: nil)
241
+ instance_variable_name = "@#{type}_pattern"
242
+ the_pattern = instance_variable_get(instance_variable_name)
243
+ if the_pattern.nil?
244
+ the_pattern = self.class.pattern(*args)
245
+ end
246
+ the_pattern
247
+ end
248
+
249
+ def dispose
250
+ paint_listener_proxy&.unregister
251
+ @background_pattern&.dispose
252
+ @background_pattern = nil
253
+ @foreground_pattern&.dispose
254
+ @foreground_pattern = nil
255
+ @image&.dispose
256
+ @image = nil
257
+ @parent.shapes.delete(self)
258
+ end
259
+
216
260
  def setup_painting
217
261
  # TODO consider moving this method to parent (making the logic polymorphic)
218
262
  return if @parent.is_disposed
@@ -226,8 +270,8 @@ module Glimmer
226
270
  end
227
271
 
228
272
  def paint(paint_event)
229
- @properties['background'] = [@parent.background] if fill? && !@properties.keys.map(&:to_s).include?('background')
230
- @properties['foreground'] = [@parent.foreground] if @parent.respond_to?(:foreground) && draw? && !@properties.keys.map(&:to_s).include?('foreground')
273
+ @properties['background'] = [@parent.background] if fill? && !has_some_background?
274
+ @properties['foreground'] = [@parent.foreground] if @parent.respond_to?(:foreground) && draw? && !has_some_foreground?
231
275
  @properties['font'] = [@parent.font] if @parent.respond_to?(:font) && draw? && !@properties.keys.map(&:to_s).include?('font')
232
276
  @properties['transform'] = [nil] if @parent.respond_to?(:transform) && !@properties.keys.map(&:to_s).include?('transform')
233
277
  @properties.each do |property, args|