glimmer-dsl-swt 4.18.4.9 → 4.18.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +73 -0
  3. data/README.md +14 -5
  4. data/VERSION +1 -1
  5. data/bin/glimmer +3 -3
  6. data/docs/reference/GLIMMER_CONFIGURATION.md +7 -3
  7. data/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md +464 -149
  8. data/docs/reference/GLIMMER_SAMPLES.md +91 -4
  9. data/glimmer-dsl-swt.gemspec +24 -13
  10. data/lib/ext/glimmer/config.rb +3 -7
  11. data/lib/glimmer/data_binding/list_selection_binding.rb +13 -7
  12. data/lib/glimmer/data_binding/table_items_binding.rb +22 -17
  13. data/lib/glimmer/data_binding/tree_items_binding.rb +19 -15
  14. data/lib/glimmer/data_binding/widget_binding.rb +13 -15
  15. data/lib/glimmer/dsl/swt/{file_dialog_expression.rb → auto_exec_expression.rb} +6 -18
  16. data/lib/glimmer/dsl/swt/checkbox_group_selection_data_binding_expression.rb +9 -6
  17. data/lib/glimmer/dsl/swt/color_expression.rb +1 -1
  18. data/lib/glimmer/dsl/swt/combo_selection_data_binding_expression.rb +16 -14
  19. data/lib/glimmer/dsl/swt/custom_widget_expression.rb +4 -1
  20. data/lib/glimmer/dsl/swt/data_binding_expression.rb +2 -2
  21. data/lib/glimmer/dsl/swt/dialog_expression.rb +18 -9
  22. data/lib/glimmer/dsl/swt/dsl.rb +1 -0
  23. data/lib/glimmer/dsl/swt/exec_expression.rb +1 -1
  24. data/lib/glimmer/dsl/swt/font_expression.rb +1 -1
  25. data/lib/glimmer/dsl/swt/image_expression.rb +16 -2
  26. data/lib/glimmer/dsl/swt/list_selection_data_binding_expression.rb +11 -8
  27. data/lib/glimmer/dsl/swt/pixel_expression.rb +1 -1
  28. data/lib/glimmer/dsl/swt/radio_group_selection_data_binding_expression.rb +8 -5
  29. data/lib/glimmer/dsl/swt/shape_expression.rb +2 -2
  30. data/lib/glimmer/dsl/swt/shell_expression.rb +5 -2
  31. data/lib/glimmer/dsl/swt/widget_expression.rb +8 -4
  32. data/lib/glimmer/launcher.rb +3 -0
  33. data/lib/glimmer/rake_task/scaffold.rb +3 -0
  34. data/lib/glimmer/swt/color_proxy.rb +1 -1
  35. data/lib/glimmer/swt/custom/code_text.rb +33 -11
  36. data/lib/glimmer/swt/custom/drawable.rb +55 -0
  37. data/lib/glimmer/swt/custom/shape.rb +187 -43
  38. data/lib/glimmer/swt/custom/shape/arc.rb +60 -0
  39. data/lib/glimmer/{dsl/swt/directory_dialog_expression.rb → swt/custom/shape/focus.rb} +15 -20
  40. data/lib/glimmer/swt/custom/shape/image.rb +99 -0
  41. data/lib/glimmer/swt/custom/shape/line.rb +65 -0
  42. data/lib/glimmer/swt/custom/shape/oval.rb +61 -0
  43. data/lib/glimmer/swt/custom/shape/point.rb +54 -0
  44. data/lib/glimmer/swt/custom/shape/polygon.rb +73 -0
  45. data/lib/glimmer/swt/custom/shape/polyline.rb +74 -0
  46. data/lib/glimmer/swt/custom/shape/rectangle.rb +101 -0
  47. data/lib/glimmer/swt/custom/shape/text.rb +85 -0
  48. data/lib/glimmer/swt/date_time_proxy.rb +9 -3
  49. data/lib/glimmer/swt/dialog_proxy.rb +92 -0
  50. data/lib/glimmer/swt/display_proxy.rb +62 -2
  51. data/lib/glimmer/swt/expand_item_proxy.rb +18 -12
  52. data/lib/glimmer/swt/font_proxy.rb +13 -7
  53. data/lib/glimmer/swt/image_proxy.rb +15 -4
  54. data/lib/glimmer/swt/layout_data_proxy.rb +21 -15
  55. data/lib/glimmer/swt/layout_proxy.rb +19 -15
  56. data/lib/glimmer/swt/menu_proxy.rb +2 -2
  57. data/lib/glimmer/swt/message_box_proxy.rb +21 -7
  58. data/lib/glimmer/swt/properties.rb +3 -0
  59. data/lib/glimmer/swt/proxy_properties.rb +145 -0
  60. data/lib/glimmer/swt/scrolled_composite_proxy.rb +6 -2
  61. data/lib/glimmer/swt/shell_proxy.rb +96 -80
  62. data/lib/glimmer/swt/swt_proxy.rb +17 -0
  63. data/lib/glimmer/swt/tab_item_proxy.rb +5 -3
  64. data/lib/glimmer/swt/table_proxy.rb +32 -11
  65. data/lib/glimmer/swt/transform_proxy.rb +39 -35
  66. data/lib/glimmer/swt/tree_proxy.rb +11 -16
  67. data/lib/glimmer/swt/widget_listener_proxy.rb +6 -2
  68. data/lib/glimmer/swt/widget_proxy.rb +192 -141
  69. data/lib/glimmer/ui.rb +5 -0
  70. data/lib/glimmer/ui/custom_shell.rb +13 -7
  71. data/lib/glimmer/ui/custom_widget.rb +4 -5
  72. data/samples/elaborate/contact_manager.rb +7 -7
  73. data/samples/elaborate/login.rb +25 -21
  74. data/samples/elaborate/mandelbrot_fractal.rb +87 -31
  75. data/samples/elaborate/meta_sample.rb +1 -1
  76. data/samples/elaborate/tetris.rb +1 -0
  77. data/samples/elaborate/tic_tac_toe.rb +16 -14
  78. data/samples/elaborate/tic_tac_toe/board.rb +5 -5
  79. data/samples/elaborate/tic_tac_toe/cell.rb +5 -5
  80. data/samples/hello/hello_button.rb +7 -7
  81. data/samples/hello/hello_canvas.rb +143 -41
  82. data/samples/hello/hello_checkbox.rb +16 -14
  83. data/samples/hello/hello_checkbox_group.rb +11 -9
  84. data/samples/hello/hello_color_dialog.rb +66 -0
  85. data/samples/hello/hello_combo.rb +14 -12
  86. data/samples/hello/hello_computed.rb +7 -7
  87. data/samples/hello/hello_cursor.rb +2 -1
  88. data/samples/hello/hello_custom_shell.rb +17 -21
  89. data/samples/hello/hello_custom_widget.rb +4 -6
  90. data/samples/hello/hello_date_time.rb +14 -12
  91. data/samples/hello/hello_directory_dialog.rb +7 -7
  92. data/samples/hello/hello_expand_bar.rb +8 -8
  93. data/samples/hello/hello_file_dialog.rb +7 -7
  94. data/samples/hello/hello_font_dialog.rb +82 -0
  95. data/samples/hello/hello_group.rb +18 -16
  96. data/samples/hello/hello_list_multi_selection.rb +13 -11
  97. data/samples/hello/hello_list_single_selection.rb +13 -11
  98. data/samples/hello/hello_progress_bar.rb +125 -0
  99. data/samples/hello/hello_radio.rb +18 -16
  100. data/samples/hello/hello_radio_group.rb +14 -12
  101. data/samples/hello/hello_spinner.rb +7 -7
  102. data/samples/hello/hello_tab.rb +5 -5
  103. data/samples/hello/hello_table.rb +10 -5
  104. data/samples/hello/hello_tree.rb +485 -0
  105. metadata +22 -22
  106. data/lib/glimmer/swt/directory_dialog_proxy.rb +0 -65
  107. data/lib/glimmer/swt/file_dialog_proxy.rb +0 -66
@@ -32,12 +32,12 @@ 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
 
39
39
  def interpret(parent, keyword, *args, &block)
40
- Glimmer::SWT::Custom::Shape.new(parent, keyword, *args, &block)
40
+ Glimmer::SWT::Custom::Shape.create(parent, keyword, *args, &block)
41
41
  end
42
42
 
43
43
  def add_content(parent, &block)
@@ -32,15 +32,18 @@ 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
 
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
+ class WindowExpression < ShellExpression
45
+ # Alias
46
+ end
44
47
  end
45
48
  end
46
49
  end
@@ -33,17 +33,21 @@ module Glimmer
33
33
  EXCLUDED_KEYWORDS = %w[shell display tab_item] + Glimmer::SWT::Custom::Shape.keywords - ['text']
34
34
 
35
35
  def can_interpret?(parent, keyword, *args, &block)
36
- result = !EXCLUDED_KEYWORDS.include?(keyword) &&
37
- parent.respond_to?(:swt_widget) && #TODO change to composite?(parent)
36
+ !EXCLUDED_KEYWORDS.include?(keyword) and
37
+ parent.respond_to?(:swt_widget) and
38
+ !parent.is_a?(Glimmer::SWT::Custom::Shape) and
39
+ !((keyword.to_s == 'text') and (args.first.is_a?(String) or parent.swt_widget.class == org.eclipse.swt.widgets.Canvas)) and
38
40
  Glimmer::SWT::WidgetProxy.widget_exists?(keyword)
39
- (keyword.to_s == 'text' && args.first.is_a?(String)) ? false : result
40
41
  end
41
42
 
42
43
  def interpret(parent, keyword, *args, &block)
43
- Glimmer::SWT::WidgetProxy.create(keyword, parent, args)
44
+ Glimmer::SWT::WidgetProxy.create(keyword, parent, args).tap do |new_widget_proxy|
45
+ new_widget_proxy.paint_pixel_by_pixel(&block) if block&.parameters&.count == 2
46
+ end
44
47
  end
45
48
 
46
49
  def add_content(parent, &block)
50
+ return if block&.parameters&.count == 2
47
51
  super
48
52
  parent.post_add_content
49
53
  parent.finish_add_content!
@@ -27,9 +27,12 @@ require 'fileutils'
27
27
  require 'os'
28
28
 
29
29
  module Glimmer
30
+ # Launcher of glimmer applications and main entry point for the `glimmer` command.
30
31
  class Launcher
31
32
  OPERATING_SYSTEMS_SUPPORTED = ["mac", "windows", "linux"]
32
33
 
34
+ # TODO convert to a bash script to achieve faster startup time
35
+
33
36
  TEXT_USAGE = <<~MULTI_LINE_STRING
34
37
  Glimmer (JRuby Desktop Development GUI Framework) - JRuby Gem: glimmer-dsl-swt v#{File.read(File.expand_path('../../../VERSION', __FILE__))}
35
38
  Usage: glimmer [--bundler] [--pd] [--quiet] [--debug] [--log-level=VALUE] [[ENV_VAR=VALUE]...] [[-jruby-option]...] (application.rb or task[task_args]) [[application2.rb]...]
@@ -94,6 +94,9 @@ module Glimmer
94
94
  # Mac
95
95
  .DS_Store
96
96
 
97
+ # Warbler
98
+ Jars.lock
99
+
97
100
  # Gladiator (Glimmer Editor)
98
101
  .gladiator
99
102
 
@@ -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
 
@@ -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
@@ -124,13 +136,13 @@ module Glimmer
124
136
  editable false
125
137
  caret nil
126
138
  on_focus_gained {
127
- @styled_text_proxy&.swt_widget.setFocus
139
+ @styled_text_proxy&.setFocus
128
140
  }
129
141
  on_key_pressed {
130
- @styled_text_proxy&.swt_widget.setFocus
142
+ @styled_text_proxy&.setFocus
131
143
  }
132
144
  on_mouse_up {
133
- @styled_text_proxy&.swt_widget.setFocus
145
+ @styled_text_proxy&.setFocus
134
146
  }
135
147
  }
136
148
 
@@ -153,19 +165,29 @@ 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|
159
172
  character = event.keyCode.chr rescue nil
160
173
  case [event.stateMask, character]
161
174
  when [(OS.mac? ? swt(:command) : swt(:ctrl)), 'a']
162
- @styled_text_proxy.swt_widget.selectAll
175
+ @styled_text_proxy.selectAll
163
176
  when [(swt(:ctrl) if OS.mac?), 'a']
164
177
  jump_to_beginning_of_line
165
178
  when [(swt(:ctrl) if OS.mac?), 'e']
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|
@@ -241,17 +263,17 @@ module Glimmer
241
263
  end
242
264
 
243
265
  def jump_to_beginning_of_line
244
- current_line_index = @styled_text_proxy.swt_widget.getLineAtOffset(@styled_text_proxy.swt_widget.getCaretOffset)
245
- beginning_of_current_line_offset = @styled_text_proxy.swt_widget.getOffsetAtLine(current_line_index)
246
- @styled_text_proxy.swt_widget.setSelection(beginning_of_current_line_offset, beginning_of_current_line_offset)
266
+ current_line_index = @styled_text_proxy.getLineAtOffset(@styled_text_proxy.getCaretOffset)
267
+ beginning_of_current_line_offset = @styled_text_proxy.getOffsetAtLine(current_line_index)
268
+ @styled_text_proxy.setSelection(beginning_of_current_line_offset, beginning_of_current_line_offset)
247
269
  end
248
270
 
249
271
  def jump_to_end_of_line
250
- current_line_index = @styled_text_proxy.swt_widget.getLineAtOffset(@styled_text_proxy.swt_widget.getCaretOffset)
251
- current_line = @styled_text_proxy.swt_widget.getLine(current_line_index)
252
- beginning_of_current_line_offset = @styled_text_proxy.swt_widget.getOffsetAtLine(current_line_index)
272
+ current_line_index = @styled_text_proxy.getLineAtOffset(@styled_text_proxy.getCaretOffset)
273
+ current_line = @styled_text_proxy.getLine(current_line_index)
274
+ beginning_of_current_line_offset = @styled_text_proxy.getOffsetAtLine(current_line_index)
253
275
  new_offset = beginning_of_current_line_offset + current_line.size
254
- @styled_text_proxy.swt_widget.setSelection(new_offset, new_offset)
276
+ @styled_text_proxy.setSelection(new_offset, new_offset)
255
277
  end
256
278
  end
257
279
  end
@@ -38,6 +38,17 @@ module Glimmer
38
38
  @image_buffered_shapes ||= []
39
39
  end
40
40
 
41
+ # Returns shapes expanded with regards to nested shapes
42
+ def expand_shapes(shapes)
43
+ # TODO
44
+ end
45
+
46
+ # TODO add a method like shapes that specifies drawable_properties to be able to adjust properties like transform in between shapes
47
+
48
+ def shape_at_location(x, y)
49
+ shapes.reverse.detect {|shape| shape.include?(x, y)}
50
+ end
51
+
41
52
  def add_shape(shape)
42
53
  if !@image_double_buffered || shape.args.first == @image_proxy_buffer
43
54
  shapes << shape
@@ -51,6 +62,50 @@ module Glimmer
51
62
  shapes.dup.each {|s| s.dispose(dispose_images: dispose_images, dispose_patterns: dispose_patterns) } if requires_shape_disposal?
52
63
  end
53
64
 
65
+ def paint_pixel_by_pixel(width = nil, height = nil, &each_pixel_color)
66
+ if @image_double_buffered
67
+ work = lambda do |paint_event|
68
+ width ||= swt_drawable.bounds.width
69
+ height ||= swt_drawable.bounds.height
70
+ @image_proxy_buffer ||= ImageProxy.create_pixel_by_pixel(width, height, &each_pixel_color)
71
+ @image_proxy_buffer.shape(self).paint(paint_event)
72
+ end
73
+ else
74
+ work = lambda do |paint_event_or_image|
75
+ the_gc = paint_event_or_image.gc
76
+ current_foreground = nil
77
+ width ||= swt_drawable.bounds.width
78
+ height ||= swt_drawable.bounds.height
79
+ height.times do |y|
80
+ width.times do |x|
81
+ new_foreground = each_pixel_color.call(x, y)
82
+ new_foreground = Glimmer::SWT::ColorProxy.create(new_foreground, ensure_bounds: false) unless new_foreground.is_a?(ColorProxy) || new_foreground.is_a?(Color)
83
+ new_foreground = new_foreground.swt_color if new_foreground.is_a?(Glimmer::SWT::ColorProxy)
84
+ the_gc.foreground = current_foreground = new_foreground unless new_foreground == current_foreground
85
+ the_gc.draw_point x, y
86
+ end
87
+ end
88
+ end
89
+ end
90
+ if respond_to?(:gc)
91
+ work.call(self)
92
+ else
93
+ on_swt_paint(&work)
94
+ end
95
+ end
96
+
97
+ def swt_drawable
98
+ swt_drawable = nil
99
+ if respond_to?(:swt_image)
100
+ swt_drawable = swt_image
101
+ elsif respond_to?(:swt_display)
102
+ swt_drawable = swt_display
103
+ elsif respond_to?(:swt_widget)
104
+ swt_drawable = swt_widget
105
+ end
106
+ swt_drawable
107
+ end
108
+
54
109
  def deregister_shape_painting
55
110
  @paint_listener_proxy&.deregister
56
111
  end
@@ -34,10 +34,17 @@ module Glimmer
34
34
  class Shape
35
35
  include Packages
36
36
  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
37
 
40
38
  class << self
39
+ def create(parent, keyword, *args, &property_block)
40
+ potential_shape_class_name = keyword.to_s.camelcase(:upper).to_sym
41
+ if constants.include?(potential_shape_class_name)
42
+ const_get(potential_shape_class_name).new(parent, keyword, *args, &property_block)
43
+ else
44
+ new(parent, keyword, *args, &property_block)
45
+ end
46
+ end
47
+
41
48
  def valid?(parent, keyword, *args, &block)
42
49
  gc_instance_methods.include?(method_name(keyword, arg_options(args)))
43
50
  end
@@ -79,27 +86,29 @@ module Glimmer
79
86
  end
80
87
 
81
88
  def pattern(*args)
82
- found_pattern = flyweigh_patterns[args]
89
+ found_pattern = flyweight_patterns[args]
83
90
  if found_pattern.nil? || found_pattern.is_disposed
84
- found_pattern = flyweigh_patterns[args] = org.eclipse.swt.graphics.Pattern.new(*args)
91
+ found_pattern = flyweight_patterns[args] = org.eclipse.swt.graphics.Pattern.new(*args)
85
92
  end
86
93
  found_pattern
87
94
  end
88
95
 
89
- def flyweigh_patterns
90
- @flyweigh_patterns ||= {}
96
+ def flyweight_patterns
97
+ @flyweight_patterns ||= {}
91
98
  end
92
99
  end
93
100
 
94
- attr_reader :parent, :name, :args, :options
101
+ attr_reader :drawable, :parent, :name, :args, :options, :shapes
95
102
 
96
103
  def initialize(parent, keyword, *args, &property_block)
97
104
  @parent = parent
105
+ @drawable = @parent.is_a?(Drawable) ? @parent : @parent.drawable
98
106
  @name = keyword
99
107
  @options = self.class.arg_options(args, extract: true)
100
108
  @method_name = self.class.method_name(keyword, @options)
101
109
  @args = args
102
110
  @properties = {}
111
+ @shapes = [] # nested shapes
103
112
  @options.reject {|key, value| %w[fill gradient round].include?(key.to_s)}.each do |property, property_args|
104
113
  @properties[property] = property_args
105
114
  end
@@ -107,13 +116,19 @@ module Glimmer
107
116
  post_add_content if property_block.nil?
108
117
  end
109
118
 
119
+ def add_shape(shape)
120
+ @shapes << shape
121
+ end
122
+
110
123
  def draw?
111
124
  !fill?
112
125
  end
126
+ alias drawn? draw?
113
127
 
114
128
  def fill?
115
129
  @options[:fill]
116
130
  end
131
+ alias filled? fill?
117
132
 
118
133
  def gradient?
119
134
  @options[:gradient]
@@ -123,6 +138,37 @@ module Glimmer
123
138
  @options[:round]
124
139
  end
125
140
 
141
+ # subclasses (like polygon) may override to indicate if a point x,y coordinates falls inside the shape
142
+ # some shapes may choose to provide a fuzz factor to make usage of this method for mouse clicking more user friendly
143
+ def contain?(x, y)
144
+ # assume a rectangular filled shape by default (works for several shapes like image, text, and focus)
145
+ if respond_to?(:x) && respond_to?(:y) && respond_to?(:width) && respond_to?(:height) && self.x && self.y && width && height
146
+ x.between?(self.x, self.x + width) && y.between?(self.y, self.y + height)
147
+ else
148
+ false # subclasses must provide implementation
149
+ end
150
+ end
151
+
152
+ # 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
153
+ # some shapes may choose to provide a fuzz factor to make usage of this method for mouse clicking more user friendly
154
+ def include?(x, y)
155
+ # assume a rectangular shape by default
156
+ if respond_to?(:x) && respond_to?(:y) && respond_to?(:width) && respond_to?(:height)
157
+ contain?(x, y)
158
+ else
159
+ false # subclasses must provide implementation
160
+ end
161
+ end
162
+
163
+ # moves by x delta and y delta. Subclasses must implement
164
+ # provdies a default implementation that assumes moving x and y is sufficient by default (not for polygons though, which must override)
165
+ def move_by(x_delta, y_delta)
166
+ if respond_to?(:x) && respond_to?(:y) && x && y
167
+ self.x += x_delta
168
+ self.y += y_delta
169
+ end
170
+ end
171
+
126
172
  def has_some_background?
127
173
  @properties.keys.map(&:to_s).include?('background') || @properties.keys.map(&:to_s).include?('background_pattern')
128
174
  end
@@ -134,7 +180,7 @@ module Glimmer
134
180
  def post_add_content
135
181
  unless @content_added
136
182
  amend_method_name_options_based_on_properties!
137
- @parent.setup_shape_painting unless @parent.is_a?(ImageProxy)
183
+ @drawable.setup_shape_painting unless @drawable.is_a?(ImageProxy)
138
184
  @content_added = true
139
185
  end
140
186
  end
@@ -142,10 +188,13 @@ module Glimmer
142
188
  def apply_property_arg_conversions(method_name, property, args)
143
189
  args = args.dup
144
190
  the_java_method = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.detect {|m| m.name == method_name}
191
+ if the_java_method.parameter_types.first == Color.java_class && args.first.is_a?(RGB)
192
+ args[0] = [args[0].red, args[0].green, args[0].blue]
193
+ end
145
194
  if ['setBackground', 'setForeground'].include?(method_name.to_s) && args.first.is_a?(Array)
146
195
  args[0] = ColorProxy.new(args[0])
147
196
  end
148
- if args.first.is_a?(Symbol) || args.first.is_a?(String)
197
+ if args.first.is_a?(Symbol) || args.first.is_a?(::String)
149
198
  if the_java_method.parameter_types.first == Color.java_class
150
199
  args[0] = ColorProxy.new(args[0])
151
200
  end
@@ -156,7 +205,7 @@ module Glimmer
156
205
  if args.first.is_a?(ColorProxy)
157
206
  args[0] = args[0].swt_color
158
207
  end
159
- if args.first.is_a?(Hash) && the_java_method.parameter_types.first == Font.java_class
208
+ if (args.first.is_a?(Hash) || args.first.is_a?(FontData)) && the_java_method.parameter_types.first == Font.java_class
160
209
  args[0] = FontProxy.new(args[0])
161
210
  end
162
211
  if args.first.is_a?(FontProxy)
@@ -166,16 +215,23 @@ module Glimmer
166
215
  args[0] = args[0].swt_transform
167
216
  end
168
217
  if ['setBackgroundPattern', 'setForegroundPattern'].include?(method_name.to_s)
169
- @parent.requires_shape_disposal = true
218
+ @drawable.requires_shape_disposal = true
219
+ args = args.first if args.first.is_a?(Array)
170
220
  args.each_with_index do |arg, i|
171
- if arg.is_a?(Symbol) || arg.is_a?(String)
172
- args[i] = ColorProxy.new(arg).swt_color
173
- elsif arg.is_a?(ColorProxy)
174
- args[i] = arg.swt_color
175
- end
221
+ arg = ColorProxy.new(arg.red, arg.green, arg.blue) if arg.is_a?(RGB)
222
+ arg = ColorProxy.new(arg) if arg.is_a?(Symbol) || arg.is_a?(::String)
223
+ arg = arg.swt_color if arg.is_a?(ColorProxy)
224
+ args[i] = arg
225
+ end
226
+ @pattern_args ||= {}
227
+ pattern_type = method_name.to_s.match(/set(.+)Pattern/)[1]
228
+ if args.first.is_a?(Pattern)
229
+ new_args = @pattern_args[pattern_type]
230
+ else
231
+ new_args = args.first.is_a?(Display) ? args : ([DisplayProxy.instance.swt_display] + args)
232
+ @pattern_args[pattern_type] = new_args.dup
176
233
  end
177
- new_args = [DisplayProxy.instance.swt_display] + args
178
- args[0] = pattern(*new_args, type: method_name.to_s.match(/set(.+)Pattern/)[1])
234
+ args[0] = pattern(*new_args, type: pattern_type)
179
235
  args[1..-1] = []
180
236
  end
181
237
  args
@@ -186,28 +242,44 @@ module Glimmer
186
242
  @args[0] = @args.dup
187
243
  @args[1..-1] = []
188
244
  end
245
+ if @name == 'image'
246
+ if @args.first.is_a?(::String)
247
+ @args[0] = ImageProxy.new(@args[0])
248
+ end
249
+ if @args.first.is_a?(ImageProxy)
250
+ @image = @args[0] = @args[0].swt_image
251
+ end
252
+ if @args.first.nil?
253
+ @image = nil
254
+ end
255
+ end
256
+ if @name == 'text'
257
+ if @args[3].is_a?(Symbol) || @args[3].is_a?(::String)
258
+ @args[3] = [@args[3]]
259
+ end
260
+ if @args[3].is_a?(Array)
261
+ if @args[3].size == 1 && @args[3].first.is_a?(Array)
262
+ @args[3] = @args[3].first
263
+ end
264
+ @args[3] = SWTProxy[*@args[3]]
265
+ end
266
+ end
189
267
  end
190
268
 
191
269
  def apply_shape_arg_defaults!
192
270
  if @name.include?('rectangle') && round? && @args.size.between?(4, 5)
193
271
  (6 - @args.size).times {@args << 60}
194
272
  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
273
+ @args << true # vertical is true by default
274
+ elsif (@name.include?('text') || @name.include?('string')) && !@properties.keys.map(&:to_s).include?('background') && @args.size == 3
275
+ @args << true # is_transparent is true by default
198
276
  end
199
277
  if @name.include?('image')
200
- @parent.requires_shape_disposal = true
278
+ @drawable.requires_shape_disposal = true
201
279
  if @args.size == 1
202
280
  @args[1] = 0
203
281
  @args[2] = 0
204
282
  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
211
283
  end
212
284
  end
213
285
 
@@ -217,14 +289,14 @@ module Glimmer
217
289
  the_java_method_arg_count = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.select do |m|
218
290
  m.name == @method_name.camelcase(:lower)
219
291
  end.map(&:parameter_types).map(&:size).max
220
- if @args.size > the_java_method_arg_count
292
+ if the_java_method_arg_count && @args.to_a.size > the_java_method_arg_count
221
293
  @args[the_java_method_arg_count..-1] = []
222
294
  end
223
295
  end
224
296
 
225
297
  def amend_method_name_options_based_on_properties!
226
298
  return if @name == 'point'
227
- if has_some_background? && !has_some_foreground?
299
+ if @name != 'text' && @name != 'string' && has_some_background? && !has_some_foreground?
228
300
  @options[:fill] = true
229
301
  elsif !has_some_background? && has_some_foreground?
230
302
  @options[:fill] = false
@@ -237,32 +309,93 @@ module Glimmer
237
309
  end
238
310
  @method_name = self.class.method_name(@name, @options)
239
311
  end
240
-
312
+
313
+ # parameter names for arguments to pass to SWT GC.xyz method for rendering shape (e.g. draw_image(image, x, y) yields :image, :x, :y parameter names)
314
+ def parameter_names
315
+ []
316
+ end
317
+
318
+ def possible_parameter_names
319
+ parameter_names
320
+ end
321
+
322
+ def parameter_name?(attribute_name)
323
+ possible_parameter_names.map(&:to_s).include?(ruby_attribute_getter(attribute_name))
324
+ end
325
+
326
+ def parameter_index(attribute_name)
327
+ parameter_names.map(&:to_s).index(attribute_name.to_s)
328
+ end
329
+
330
+ def set_parameter_attribute(attribute_name, *args)
331
+ @args[parameter_index(ruby_attribute_getter(attribute_name))] = args.size == 1 ? args.first : args
332
+ end
333
+
241
334
  def has_attribute?(attribute_name, *args)
242
- self.class.gc_instance_methods.include?(attribute_setter(attribute_name))
335
+ self.class.gc_instance_methods.include?(attribute_setter(attribute_name)) or
336
+ parameter_name?(attribute_name)
243
337
  end
244
338
 
245
339
  def set_attribute(attribute_name, *args)
246
- @properties[attribute_name] = args
247
- if @content_added && !@parent.is_disposed
340
+ if parameter_name?(attribute_name)
341
+ set_parameter_attribute(attribute_name, *args)
342
+ else
343
+ @properties[ruby_attribute_getter(attribute_name)] = args
344
+ end
345
+ if @content_added && !@drawable.is_disposed
248
346
  @calculated_paint_args = false
249
- @parent.redraw
347
+ @drawable.redraw
250
348
  end
251
349
  end
252
350
 
253
351
  def get_attribute(attribute_name)
254
- @properties.symbolize_keys[attribute_name.to_s.to_sym]
352
+ if parameter_name?(attribute_name)
353
+ arg_index = parameter_index(attribute_name)
354
+ @args[arg_index] if arg_index
355
+ else
356
+ @properties.symbolize_keys[attribute_name.to_s.to_sym]
357
+ end
358
+ end
359
+
360
+ def method_missing(method_name, *args, &block)
361
+ if method_name.to_s.end_with?('=')
362
+ set_attribute(method_name, *args)
363
+ elsif has_attribute?(method_name)
364
+ get_attribute(method_name)
365
+ else
366
+ super
367
+ end
368
+ end
369
+
370
+ def respond_to?(method_name, *args, &block)
371
+ if has_attribute?(method_name)
372
+ true
373
+ else
374
+ super
375
+ end
255
376
  end
256
377
 
257
378
  def pattern(*args, type: nil)
258
379
  instance_variable_name = "@#{type}_pattern"
259
380
  the_pattern = instance_variable_get(instance_variable_name)
260
- if the_pattern.nil?
381
+ if the_pattern.nil? || the_pattern.is_disposed
261
382
  the_pattern = self.class.pattern(*args)
262
383
  end
263
384
  the_pattern
264
385
  end
265
386
 
387
+ def pattern_args(type: nil)
388
+ @pattern_args && @pattern_args[type.to_s.capitalize]
389
+ end
390
+
391
+ def background_pattern_args
392
+ pattern_args(type: 'background')
393
+ end
394
+
395
+ def foreground_pattern_args
396
+ pattern_args(type: 'foreground')
397
+ end
398
+
266
399
  def dispose(dispose_images: true, dispose_patterns: true)
267
400
  if dispose_patterns
268
401
  @background_pattern&.dispose
@@ -287,18 +420,23 @@ module Glimmer
287
420
  args.first.swt_transform.dispose
288
421
  end
289
422
  end
423
+ self.extent = paint_event.gc.send("#{@name}Extent", *(([string, flags] if respond_to?(:flags)).compact)) if ['text', 'string'].include?(@name)
290
424
  paint_event.gc.send(@method_name, *@args)
425
+ rescue => e
426
+ Glimmer::Config.logger.error {"Error encountered in painting shape: #{self.inspect}"}
427
+ Glimmer::Config.logger.error {e.full_message}
291
428
  end
292
429
 
293
430
  def calculate_paint_args!
294
431
  unless @calculated_paint_args
295
- if @name == 'point'
432
+ if @name == 'pixel'
433
+ @name = 'point'
296
434
  # optimized performance calculation for pixel points
297
435
  if !@properties[:foreground].is_a?(Color)
298
436
  if @properties[:foreground].is_a?(Array)
299
437
  @properties[:foreground] = ColorProxy.new(@properties[:foreground], ensure_bounds: false)
300
438
  end
301
- if @properties[:foreground].is_a?(Symbol) || @properties[:foreground].is_a?(String)
439
+ if @properties[:foreground].is_a?(Symbol) || @properties[:foreground].is_a?(::String)
302
440
  @properties[:foreground] = ColorProxy.new(@properties[:foreground], ensure_bounds: false)
303
441
  end
304
442
  if @properties[:foreground].is_a?(ColorProxy)
@@ -306,10 +444,14 @@ module Glimmer
306
444
  end
307
445
  end
308
446
  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
- @properties['font'] = [@parent.font] if @parent.respond_to?(:font) && draw? && !@properties.keys.map(&:to_s).include?('font')
312
- @properties['transform'] = [nil] if @parent.respond_to?(:transform) && !@properties.keys.map(&:to_s).include?('transform')
447
+ @properties['background'] = [@drawable.background] if fill? && !has_some_background?
448
+ @properties['foreground'] = [@drawable.foreground] if @drawable.respond_to?(:foreground) && draw? && !has_some_foreground?
449
+ # TODO regarding alpha, make sure to reset it to parent stored alpha once we allow setting shape properties on parents directly without shapes
450
+ @properties['alpha'] ||= [255]
451
+ @properties['font'] = [@drawable.font] if @drawable.respond_to?(:font) && draw? && !@properties.keys.map(&:to_s).include?('font')
452
+ # TODO regarding transform, make sure to reset it to parent stored alpha once we allow setting shape properties on parents directly without shapes
453
+ # Also do that with all future-added properties
454
+ @properties['transform'] = [nil] if @drawable.respond_to?(:transform) && !@properties.keys.map(&:to_s).include?('transform')
313
455
  @properties.each do |property, args|
314
456
  method_name = attribute_setter(property)
315
457
  converted_args = apply_property_arg_conversions(method_name, property, args)
@@ -330,3 +472,5 @@ module Glimmer
330
472
  end
331
473
 
332
474
  end
475
+
476
+ Dir[File.expand_path(File.join(__dir__, 'shape', '**', '*.rb'))].each {|shape_file| require(shape_file)}