glimmer-dsl-swt 4.18.4.6 → 4.18.4.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +43 -0
  3. data/README.md +20 -11
  4. data/VERSION +1 -1
  5. data/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md +273 -66
  6. data/docs/reference/GLIMMER_SAMPLES.md +144 -41
  7. data/glimmer-dsl-swt.gemspec +13 -5
  8. data/lib/glimmer/data_binding/widget_binding.rb +16 -3
  9. data/lib/glimmer/dsl/swt/color_expression.rb +1 -1
  10. data/lib/glimmer/dsl/swt/custom_widget_expression.rb +4 -1
  11. data/lib/glimmer/dsl/swt/data_binding_expression.rb +2 -1
  12. data/lib/glimmer/dsl/swt/image_expression.rb +14 -3
  13. data/lib/glimmer/dsl/swt/radio_group_selection_data_binding_expression.rb +1 -0
  14. data/lib/glimmer/dsl/swt/shell_expression.rb +4 -1
  15. data/lib/glimmer/dsl/swt/widget_expression.rb +4 -1
  16. data/lib/glimmer/swt/color_proxy.rb +5 -4
  17. data/lib/glimmer/swt/custom/animation.rb +1 -0
  18. data/lib/glimmer/swt/custom/code_text.rb +22 -0
  19. data/lib/glimmer/swt/custom/drawable.rb +80 -4
  20. data/lib/glimmer/swt/custom/radio_group.rb +2 -1
  21. data/lib/glimmer/swt/custom/shape.rb +80 -46
  22. data/lib/glimmer/swt/image_proxy.rb +30 -8
  23. data/lib/glimmer/swt/scrolled_composite_proxy.rb +15 -6
  24. data/lib/glimmer/swt/shell_proxy.rb +2 -0
  25. data/lib/glimmer/swt/swt_proxy.rb +1 -0
  26. data/lib/glimmer/swt/tab_item_proxy.rb +1 -0
  27. data/lib/glimmer/swt/widget_proxy.rb +24 -5
  28. data/lib/glimmer/ui/custom_shell.rb +3 -3
  29. data/samples/elaborate/mandelbrot_fractal.rb +348 -39
  30. data/samples/elaborate/meta_sample.rb +1 -1
  31. data/samples/elaborate/tetris.rb +5 -5
  32. data/samples/hello/hello_combo.rb +1 -1
  33. data/samples/hello/hello_cursor.rb +57 -0
  34. data/samples/hello/hello_list_multi_selection.rb +1 -1
  35. data/samples/hello/hello_list_single_selection.rb +1 -1
  36. data/samples/hello/hello_progress_bar.rb +129 -0
  37. data/samples/hello/hello_table.rb +1 -1
  38. data/samples/hello/hello_table/baseball_park.png +0 -0
  39. metadata +11 -3
@@ -2,22 +2,28 @@
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.4.6 ruby lib
5
+ # stub: glimmer-dsl-swt 4.18.4.11 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "glimmer-dsl-swt".freeze
9
- s.version = "4.18.4.6"
9
+ s.version = "4.18.4.11"
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-11"
14
+ s.date = "2021-02-19"
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]
18
18
  s.extra_rdoc_files = [
19
- "LICENSE.txt",
20
- "README.md"
19
+ "README.md",
20
+ "docs/reference/GLIMMER_COMMAND.md",
21
+ "docs/reference/GLIMMER_CONFIGURATION.md",
22
+ "docs/reference/GLIMMER_GIRB.md",
23
+ "docs/reference/GLIMMER_GUI_DSL_SYNTAX.md",
24
+ "docs/reference/GLIMMER_PACKAGING_AND_DISTRIBUTION.md",
25
+ "docs/reference/GLIMMER_SAMPLES.md",
26
+ "docs/reference/GLIMMER_STYLE_GUIDE.md"
21
27
  ]
22
28
  s.files = [
23
29
  "CHANGELOG.md",
@@ -171,6 +177,7 @@ Gem::Specification.new do |s|
171
177
  "samples/hello/hello_combo.rb",
172
178
  "samples/hello/hello_computed.rb",
173
179
  "samples/hello/hello_computed/contact.rb",
180
+ "samples/hello/hello_cursor.rb",
174
181
  "samples/hello/hello_custom_shell.rb",
175
182
  "samples/hello/hello_custom_widget.rb",
176
183
  "samples/hello/hello_date_time.rb",
@@ -186,6 +193,7 @@ Gem::Specification.new do |s|
186
193
  "samples/hello/hello_menu_bar.rb",
187
194
  "samples/hello/hello_message_box.rb",
188
195
  "samples/hello/hello_pop_up_context_menu.rb",
196
+ "samples/hello/hello_progress_bar.rb",
189
197
  "samples/hello/hello_radio.rb",
190
198
  "samples/hello/hello_radio_group.rb",
191
199
  "samples/hello/hello_sash_form.rb",
@@ -32,10 +32,12 @@ module Glimmer
32
32
  include Observer
33
33
 
34
34
  attr_reader :widget, :property
35
- def initialize(widget, property, translator = nil)
35
+ def initialize(widget, property, translator = nil, sync_exec: false, async_exec: false)
36
36
  @widget = widget
37
37
  @property = property
38
38
  @translator = translator || proc {|value| value} #TODO check on this it doesn't seem used
39
+ @sync_exec = sync_exec
40
+ @async_exec = async_exec
39
41
 
40
42
  if @widget.respond_to?(:on_widget_disposed)
41
43
  @widget.on_widget_disposed do |dispose_event|
@@ -54,8 +56,10 @@ module Glimmer
54
56
  end
55
57
  @widget.set_attribute(@property, converted_value) unless evaluate_property == converted_value
56
58
  end
57
- if Config.auto_sync_exec? && Config.require_sync_exec?
59
+ if @sync_exec || Config.auto_sync_exec? && Config.require_sync_exec?
58
60
  SWT::DisplayProxy.instance.sync_exec(&update_operation)
61
+ elsif @async_exec
62
+ SWT::DisplayProxy.instance.async_exec(&update_operation)
59
63
  else
60
64
  update_operation.call
61
65
  end
@@ -66,7 +70,16 @@ module Glimmer
66
70
  unregister_all_observables
67
71
  return
68
72
  end
69
- @widget.get_attribute(@property)
73
+ read_operation = lambda do
74
+ @widget.get_attribute(@property)
75
+ end
76
+ if @sync_exec || Config.auto_sync_exec? && Config.require_sync_exec?
77
+ SWT::DisplayProxy.instance.sync_exec(&read_operation)
78
+ elsif @async_exec
79
+ SWT::DisplayProxy.instance.async_exec(&read_operation)
80
+ else
81
+ read_operation.call
82
+ end
70
83
  end
71
84
  end
72
85
  end
@@ -32,7 +32,7 @@ module Glimmer
32
32
  include_package 'org.eclipse.swt.widgets'
33
33
 
34
34
  def interpret(parent, keyword, *args, &block)
35
- Glimmer::SWT::ColorProxy.flyweight(*args)
35
+ Glimmer::SWT::ColorProxy.create(*args)
36
36
  end
37
37
  end
38
38
  end
@@ -46,11 +46,14 @@ module Glimmer
46
46
 
47
47
  def interpret(parent, keyword, *args, &block)
48
48
  options = args.last.is_a?(Hash) ? args.pop : {}
49
- UI::CustomWidget.for(keyword).new(parent, *args, options, &block)
49
+ UI::CustomWidget.for(keyword).new(parent, *args, options, &block).tap do |new_custom_widget|
50
+ new_custom_widget.body_root.paint_pixel_by_pixel(&block) if block&.parameters&.count == 2
51
+ end
50
52
  end
51
53
 
52
54
  def add_content(parent, &block)
53
55
  # TODO consider avoiding source_location
56
+ return if block&.parameters&.count == 2
54
57
  if block.source_location == parent.content&.__getobj__.source_location
55
58
  parent.content.call(parent) unless parent.content.called?
56
59
  else
@@ -22,6 +22,7 @@
22
22
  require 'glimmer/dsl/expression'
23
23
  require 'glimmer/data_binding/model_binding'
24
24
  require 'glimmer/data_binding/widget_binding'
25
+ require 'glimmer/swt/display_proxy'
25
26
 
26
27
  module Glimmer
27
28
  module DSL
@@ -41,7 +42,7 @@ module Glimmer
41
42
 
42
43
  def interpret(parent, keyword, *args, &block)
43
44
  model_binding = args[0]
44
- widget_binding_parameters = [parent, keyword]
45
+ widget_binding_parameters = [parent, keyword, {async_exec: model_binding.binding_options[:async_exec], sync_exec: model_binding.binding_options[:sync_exec]}]
45
46
  widget_binding = DataBinding::WidgetBinding.new(*widget_binding_parameters)
46
47
  widget_binding.call(model_binding.evaluate_property)
47
48
  #TODO make this options observer dependent and all similar observers in widget specific data binding handlers
@@ -20,7 +20,9 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  require 'glimmer/dsl/expression'
23
+ require 'glimmer/dsl/top_level_expression'
23
24
  require 'glimmer/swt/image_proxy'
25
+ require 'glimmer/swt/widget_proxy'
24
26
 
25
27
  module Glimmer
26
28
  module DSL
@@ -28,19 +30,28 @@ module Glimmer
28
30
  # image expression
29
31
  # Note: Cannot be a static expression because it clashes with image property expression
30
32
  class ImageExpression < Expression
33
+ include TopLevelExpression
31
34
  include ParentExpression
32
35
 
33
36
  def can_interpret?(parent, keyword, *args, &block)
34
37
  (keyword == 'image') and
35
- (parent.nil? or parent.respond_to?('image='))
38
+ (parent.nil? or parent.respond_to?('image=') or args.first.is_a?(Numeric))
36
39
  end
37
40
 
38
41
  def interpret(parent, keyword, *args, &block)
39
- args.unshift(parent) unless parent.nil?
40
- Glimmer::SWT::ImageProxy.new(*args, &block)
42
+ options = args.last.is_a?(Hash) ? args.last : {}
43
+ coordinate_args = args.size == (options.empty? ? 2 : 3)
44
+ args.unshift(parent) unless parent.nil? || options[:top_level]
45
+ @create_pixel_by_pixel = coordinate_args && block&.parameters&.count == 2
46
+ if @create_pixel_by_pixel
47
+ Glimmer::SWT::ImageProxy.create_pixel_by_pixel(*args, &block)
48
+ else
49
+ Glimmer::SWT::ImageProxy.create(*args, &block)
50
+ end
41
51
  end
42
52
 
43
53
  def add_content(parent, &block)
54
+ return if @create_pixel_by_pixel || block&.parameters&.count == 2
44
55
  super
45
56
  parent.post_add_content
46
57
  end
@@ -51,6 +51,7 @@ module Glimmer
51
51
  widget_binding.call(model_binding.evaluate_property)
52
52
  widget_binding.observe(model, model_binding.property_name_expression)
53
53
 
54
+ raise(Glimmer::Error, "No radios found! Make sure radio selection is data-bound to a property having property_options as non-empty array!") if parent.items.empty?
54
55
  parent.on_widget_selected do
55
56
  model_binding.call(widget_binding.evaluate_property)
56
57
  end
@@ -32,7 +32,7 @@ module Glimmer
32
32
  include ParentExpression
33
33
 
34
34
  def can_interpret?(parent, keyword, *args, &block)
35
- keyword == 'shell' and
35
+ super and
36
36
  (parent.nil? or parent.is_a?(Glimmer::SWT::ShellProxy))
37
37
  end
38
38
 
@@ -41,6 +41,9 @@ module Glimmer
41
41
  Glimmer::SWT::ShellProxy.send(:new, *args)
42
42
  end
43
43
  end
44
+ class WindowExpression < ShellExpression
45
+ # Alias
46
+ end
44
47
  end
45
48
  end
46
49
  end
@@ -40,10 +40,13 @@ module Glimmer
40
40
  end
41
41
 
42
42
  def interpret(parent, keyword, *args, &block)
43
- Glimmer::SWT::WidgetProxy.create(keyword, parent, args)
43
+ Glimmer::SWT::WidgetProxy.create(keyword, parent, args).tap do |new_widget_proxy|
44
+ new_widget_proxy.paint_pixel_by_pixel(&block) if block&.parameters&.count == 2
45
+ end
44
46
  end
45
47
 
46
48
  def add_content(parent, &block)
49
+ return if block&.parameters&.count == 2
47
50
  super
48
51
  parent.post_add_content
49
52
  parent.finish_add_content!
@@ -33,7 +33,7 @@ module Glimmer
33
33
  include_package 'org.eclipse.swt.graphics'
34
34
 
35
35
  class << self
36
- def flyweight(*args)
36
+ def create(*args)
37
37
  flyweight_color_proxies[args] ||= new(*args)
38
38
  end
39
39
 
@@ -59,8 +59,10 @@ module Glimmer
59
59
  # rgba is 4 arguments representing Red, Green, Blue, and Alpha numeric values
60
60
  #
61
61
  def initialize(*args)
62
+ @options = args.last.is_a?(Hash) ? args.pop : {}
62
63
  @args = args
63
- ensure_arg_values_within_valid_bounds
64
+ @args = @args.first if @args.first.is_a?(Array)
65
+ ensure_arg_values_within_valid_bounds unless @options[:ensure_bounds] == false
64
66
  end
65
67
 
66
68
  def swt_color
@@ -75,8 +77,7 @@ module Glimmer
75
77
  @swt_color = @args.first
76
78
  end
77
79
  when 3..4
78
- red, green, blue, alpha = @args
79
- @swt_color = Color.new(*[red, green, blue, alpha].compact)
80
+ @swt_color = Color.new(*@args)
80
81
  end
81
82
  end
82
83
  @swt_color
@@ -84,6 +84,7 @@ module Glimmer
84
84
 
85
85
  def initialize(parent)
86
86
  @parent = parent
87
+ @parent.requires_shape_disposal = true
87
88
  @started = true
88
89
  @frame_index = 0
89
90
  @cycle_count_index = 0
@@ -64,6 +64,18 @@ module Glimmer
64
64
  respond_to?(method_name)
65
65
  end
66
66
 
67
+ def can_handle_observation_request?(observation_request)
68
+ @styled_text_proxy.can_handle_observation_request?(observation_request)
69
+ rescue
70
+ super
71
+ end
72
+
73
+ def handle_observation_request(observation_request, &block)
74
+ @styled_text_proxy.handle_observation_request(observation_request, &block)
75
+ rescue
76
+ super
77
+ end
78
+
67
79
  def root_block=(block)
68
80
  body_root.content(&block)
69
81
  end
@@ -153,6 +165,7 @@ module Glimmer
153
165
  top_margin 5
154
166
  right_margin 5
155
167
  bottom_margin 5
168
+ tabs 2
156
169
 
157
170
  if default_behavior
158
171
  on_key_pressed { |event|
@@ -166,6 +179,15 @@ module Glimmer
166
179
  jump_to_end_of_line
167
180
  end
168
181
  }
182
+ on_verify_text { |verify_event|
183
+ if verify_event.text == "\n"
184
+ line_index = verify_event.widget.get_line_at_offset(verify_event.widget.get_caret_offset)
185
+ line = verify_event.widget.get_line(line_index)
186
+ line_indent = line.match(/^([ ]*)/)[1].to_s.size
187
+ verify_event.text += ' '*line_indent
188
+ verify_event.text += ' '*2 if line.strip.end_with?('{') || line.strip.match(/do([ ]*[|][^|]*[|])?$/) || line.start_with?('class') || line.start_with?('module') || line.strip.start_with?('def')
189
+ end
190
+ }
169
191
  end
170
192
 
171
193
  on_modify_text { |event|
@@ -24,12 +24,75 @@ module Glimmer
24
24
  module Custom
25
25
  # Represents SWT drawable controls (widgets like canvas) and display
26
26
  module Drawable
27
+ attr_accessor :requires_shape_disposal, :image_double_buffered
28
+ alias requires_shape_disposal? requires_shape_disposal
29
+ alias image_double_buffered? image_double_buffered
30
+
31
+ include_package 'org.eclipse.swt.graphics'
32
+
27
33
  def shapes
28
34
  @shapes ||= []
29
35
  end
30
36
 
31
- def clear_shapes
32
- shapes.dup.each(&:dispose)
37
+ def image_buffered_shapes
38
+ @image_buffered_shapes ||= []
39
+ end
40
+
41
+ def add_shape(shape)
42
+ if !@image_double_buffered || shape.args.first == @image_proxy_buffer
43
+ shapes << shape
44
+ else
45
+ image_buffered_shapes << shape
46
+ end
47
+ end
48
+
49
+ def clear_shapes(dispose_images: true, dispose_patterns: true)
50
+ # Optimize further by having a collection of disposable_shapes independent of shapes, which is much smaller and only has shapes that require disposal (shapes with patterns or image)
51
+ shapes.dup.each {|s| s.dispose(dispose_images: dispose_images, dispose_patterns: dispose_patterns) } if requires_shape_disposal?
52
+ end
53
+
54
+ def paint_pixel_by_pixel(width = nil, height = nil, &each_pixel_color)
55
+ if @image_double_buffered
56
+ work = lambda do |paint_event|
57
+ width ||= swt_drawable.bounds.width
58
+ height ||= swt_drawable.bounds.height
59
+ @image_proxy_buffer ||= ImageProxy.create_pixel_by_pixel(width, height, &each_pixel_color)
60
+ @image_proxy_buffer.shape(self).paint(paint_event)
61
+ end
62
+ else
63
+ work = lambda do |paint_event_or_image|
64
+ the_gc = paint_event_or_image.gc
65
+ current_foreground = nil
66
+ width ||= swt_drawable.bounds.width
67
+ height ||= swt_drawable.bounds.height
68
+ height.times do |y|
69
+ width.times do |x|
70
+ new_foreground = each_pixel_color.call(x, y)
71
+ new_foreground = Glimmer::SWT::ColorProxy.create(new_foreground, ensure_bounds: false) unless new_foreground.is_a?(ColorProxy) || new_foreground.is_a?(Color)
72
+ new_foreground = new_foreground.swt_color if new_foreground.is_a?(Glimmer::SWT::ColorProxy)
73
+ the_gc.foreground = current_foreground = new_foreground unless new_foreground == current_foreground
74
+ the_gc.draw_point x, y
75
+ end
76
+ end
77
+ end
78
+ end
79
+ if respond_to?(:gc)
80
+ work.call(self)
81
+ else
82
+ on_swt_paint(&work)
83
+ end
84
+ end
85
+
86
+ def swt_drawable
87
+ swt_drawable = nil
88
+ if respond_to?(:swt_image)
89
+ swt_drawable = swt_image
90
+ elsif respond_to?(:swt_display)
91
+ swt_drawable = swt_display
92
+ elsif respond_to?(:swt_widget)
93
+ swt_drawable = swt_widget
94
+ end
95
+ swt_drawable
33
96
  end
34
97
 
35
98
  def deregister_shape_painting
@@ -40,8 +103,21 @@ module Glimmer
40
103
  # TODO consider performance optimization relating to order of shape rendering (affecting only further shapes not previous ones)
41
104
  if @paint_listener_proxy.nil?
42
105
  shape_painter = lambda do |paint_event|
43
- shapes.each do |shape|
44
- shape.paint(paint_event)
106
+ shape_painting_work = lambda do |paint_event|
107
+ paintable_shapes = @image_double_buffered ? image_buffered_shapes : shapes
108
+ paintable_shapes.each do |shape|
109
+ shape.paint(paint_event)
110
+ end
111
+ end
112
+ if @image_double_buffered
113
+ if @image_proxy_buffer.nil?
114
+ swt_image = Image.new(DisplayProxy.instance.swt_display, bounds.width, bounds.height)
115
+ @image_proxy_buffer = ImageProxy.new(swt_image: swt_image)
116
+ shape_painting_work.call(@image_proxy_buffer)
117
+ end
118
+ @image_proxy_buffer.shape(self).paint(paint_event)
119
+ else
120
+ shape_painting_work.call(paint_event)
45
121
  end
46
122
  end
47
123
 
@@ -73,7 +73,8 @@ module Glimmer
73
73
  end
74
74
 
75
75
  def can_handle_observation_request?(observation_request)
76
- radios.first&.can_handle_observation_request?(observation_request) || super(observation_request)
76
+ radios.first&.can_handle_observation_request?(observation_request) or
77
+ super(observation_request)
77
78
  end
78
79
 
79
80
  def handle_observation_request(observation_request, &block)
@@ -103,7 +103,7 @@ module Glimmer
103
103
  @options.reject {|key, value| %w[fill gradient round].include?(key.to_s)}.each do |property, property_args|
104
104
  @properties[property] = property_args
105
105
  end
106
- @parent.shapes << self
106
+ @parent.add_shape(self)
107
107
  post_add_content if property_block.nil?
108
108
  end
109
109
 
@@ -133,8 +133,8 @@ module Glimmer
133
133
 
134
134
  def post_add_content
135
135
  unless @content_added
136
- amend_method_name_options_based_on_properties
137
- @parent.setup_shape_painting
136
+ amend_method_name_options_based_on_properties!
137
+ @parent.setup_shape_painting unless @parent.is_a?(ImageProxy)
138
138
  @content_added = true
139
139
  end
140
140
  end
@@ -142,7 +142,10 @@ module Glimmer
142
142
  def apply_property_arg_conversions(method_name, property, args)
143
143
  args = args.dup
144
144
  the_java_method = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.detect {|m| m.name == method_name}
145
- if (args.first.is_a?(Symbol) || args.first.is_a?(String))
145
+ if ['setBackground', 'setForeground'].include?(method_name.to_s) && args.first.is_a?(Array)
146
+ args[0] = ColorProxy.new(args[0])
147
+ end
148
+ if args.first.is_a?(Symbol) || args.first.is_a?(String)
146
149
  if the_java_method.parameter_types.first == Color.java_class
147
150
  args[0] = ColorProxy.new(args[0])
148
151
  end
@@ -163,6 +166,7 @@ module Glimmer
163
166
  args[0] = args[0].swt_transform
164
167
  end
165
168
  if ['setBackgroundPattern', 'setForegroundPattern'].include?(method_name.to_s)
169
+ @parent.requires_shape_disposal = true
166
170
  args.each_with_index do |arg, i|
167
171
  if arg.is_a?(Symbol) || arg.is_a?(String)
168
172
  args[i] = ColorProxy.new(arg).swt_color
@@ -177,41 +181,48 @@ module Glimmer
177
181
  args
178
182
  end
179
183
 
180
- def apply_shape_arg_conversions(method_name, args)
181
- if args.size > 1 && (method_name.include?('polygon') || method_name.include?('polyline'))
182
- args[0] = args.dup
183
- args[1..-1] = []
184
+ def apply_shape_arg_conversions!
185
+ if @args.size > 1 && (['polygon', 'polyline'].include?(@name))
186
+ @args[0] = @args.dup
187
+ @args[1..-1] = []
184
188
  end
185
189
  end
186
190
 
187
- def apply_shape_arg_defaults(method_name, args)
188
- if method_name.include?('round_rectangle') && args.size.between?(4, 5)
189
- (6 - args.size).times {args << 60}
190
- elsif method_name.include?('rectangle') && gradient? && args.size == 4
191
- args << true
192
- elsif (method_name.include?('text') || method_name.include?('string')) && !@properties.keys.map(&:to_s).include?('background') && args.size == 3
193
- args << true
194
- end
195
- if method_name.include?('image') && args.first.is_a?(String)
196
- args[0] = ImageProxy.new(args[0])
191
+ def apply_shape_arg_defaults!
192
+ if @name.include?('rectangle') && round? && @args.size.between?(4, 5)
193
+ (6 - @args.size).times {@args << 60}
194
+ elsif @name.include?('rectangle') && gradient? && @args.size == 4
195
+ @args << true
196
+ elsif (@name.include?('text') || @name.include?('String')) && !@properties.keys.map(&:to_s).include?('background') && @args.size == 3
197
+ @args << true
197
198
  end
198
- if method_name.include?('image') && args.first.is_a?(ImageProxy)
199
- @image = args[0] = args[0].swt_image
199
+ if @name.include?('image')
200
+ @parent.requires_shape_disposal = true
201
+ if @args.size == 1
202
+ @args[1] = 0
203
+ @args[2] = 0
204
+ end
205
+ if @args.first.is_a?(String)
206
+ @args[0] = ImageProxy.new(@args[0])
207
+ end
208
+ if @args.first.is_a?(ImageProxy)
209
+ @image = @args[0] = @args[0].swt_image
210
+ end
200
211
  end
201
212
  end
202
213
 
203
214
  # Tolerates shape extra args added by user by mistake
204
215
  # (e.g. happens when switching from round rectangle to a standard one without removing all extra args)
205
- def tolerate_shape_extra_args(method_name, args)
216
+ def tolerate_shape_extra_args!
206
217
  the_java_method_arg_count = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.select do |m|
207
- m.name == method_name.camelcase(:lower)
218
+ m.name == @method_name.camelcase(:lower)
208
219
  end.map(&:parameter_types).map(&:size).max
209
- if args.size > the_java_method_arg_count
210
- args[the_java_method_arg_count..-1] = []
220
+ if @args.size > the_java_method_arg_count
221
+ @args[the_java_method_arg_count..-1] = []
211
222
  end
212
223
  end
213
224
 
214
- def amend_method_name_options_based_on_properties
225
+ def amend_method_name_options_based_on_properties!
215
226
  return if @name == 'point'
216
227
  if has_some_background? && !has_some_foreground?
217
228
  @options[:fill] = true
@@ -252,16 +263,20 @@ module Glimmer
252
263
  the_pattern
253
264
  end
254
265
 
255
- def dispose
256
- @background_pattern&.dispose
257
- @background_pattern = nil
258
- @foreground_pattern&.dispose
259
- @foreground_pattern = nil
260
- @image&.dispose
261
- @image = nil
266
+ def dispose(dispose_images: true, dispose_patterns: true)
267
+ if dispose_patterns
268
+ @background_pattern&.dispose
269
+ @background_pattern = nil
270
+ @foreground_pattern&.dispose
271
+ @foreground_pattern = nil
272
+ end
273
+ if dispose_images
274
+ @image&.dispose
275
+ @image = nil
276
+ end
262
277
  @parent.shapes.delete(self)
263
278
  end
264
-
279
+
265
280
  def paint(paint_event)
266
281
  calculate_paint_args!
267
282
  @properties.each do |property, args|
@@ -276,20 +291,39 @@ module Glimmer
276
291
  end
277
292
 
278
293
  def calculate_paint_args!
279
- unless @name == 'point' || @calculated_paint_args
280
- @properties['background'] = [@parent.background] if fill? && !has_some_background?
281
- @properties['foreground'] = [@parent.foreground] if @parent.respond_to?(:foreground) && draw? && !has_some_foreground?
282
- @properties['font'] = [@parent.font] if @parent.respond_to?(:font) && draw? && !@properties.keys.map(&:to_s).include?('font')
283
- @properties['transform'] = [nil] if @parent.respond_to?(:transform) && !@properties.keys.map(&:to_s).include?('transform')
284
- @properties.each do |property, args|
285
- method_name = attribute_setter(property)
286
- converted_args = apply_property_arg_conversions(method_name, property, args)
287
- @properties[property] = converted_args
294
+ unless @calculated_paint_args
295
+ if @name == 'point'
296
+ # optimized performance calculation for pixel points
297
+ if !@properties[:foreground].is_a?(Color)
298
+ if @properties[:foreground].is_a?(Array)
299
+ @properties[:foreground] = ColorProxy.new(@properties[:foreground], ensure_bounds: false)
300
+ end
301
+ if @properties[:foreground].is_a?(Symbol) || @properties[:foreground].is_a?(String)
302
+ @properties[:foreground] = ColorProxy.new(@properties[:foreground], ensure_bounds: false)
303
+ end
304
+ if @properties[:foreground].is_a?(ColorProxy)
305
+ @properties[:foreground] = @properties[:foreground].swt_color
306
+ end
307
+ end
308
+ else
309
+ @properties['background'] = [@parent.background] if fill? && !has_some_background?
310
+ @properties['foreground'] = [@parent.foreground] if @parent.respond_to?(:foreground) && draw? && !has_some_foreground?
311
+ # TODO regarding alpha, make sure to reset it to parent stored alpha once we allow setting shape properties on parents directly without shapes
312
+ @properties['alpha'] ||= [255]
313
+ @properties['font'] = [@parent.font] if @parent.respond_to?(:font) && draw? && !@properties.keys.map(&:to_s).include?('font')
314
+ # TODO regarding transform, make sure to reset it to parent stored alpha once we allow setting shape properties on parents directly without shapes
315
+ # Also do that with all future-added properties
316
+ @properties['transform'] = [nil] if @parent.respond_to?(:transform) && !@properties.keys.map(&:to_s).include?('transform')
317
+ @properties.each do |property, args|
318
+ method_name = attribute_setter(property)
319
+ converted_args = apply_property_arg_conversions(method_name, property, args)
320
+ @properties[property] = converted_args
321
+ end
322
+ apply_shape_arg_conversions!
323
+ apply_shape_arg_defaults!
324
+ tolerate_shape_extra_args!
325
+ @calculated_paint_args = true
288
326
  end
289
- apply_shape_arg_conversions(@method_name, @args)
290
- apply_shape_arg_defaults(@method_name, @args)
291
- tolerate_shape_extra_args(@method_name, @args)
292
- @calculated_paint_args = true
293
327
  end
294
328
  end
295
329