glimmer-dsl-swt 4.18.3.2 → 4.18.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +52 -0
  3. data/README.md +674 -323
  4. data/VERSION +1 -1
  5. data/glimmer-dsl-swt.gemspec +7 -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/table_items_binding.rb +8 -5
  9. data/lib/glimmer/data_binding/widget_binding.rb +9 -1
  10. data/lib/glimmer/dsl/swt/image_expression.rb +14 -6
  11. data/lib/glimmer/dsl/swt/layout_data_expression.rb +4 -4
  12. data/lib/glimmer/dsl/swt/layout_expression.rb +5 -3
  13. data/lib/glimmer/swt/custom/code_text.rb +196 -51
  14. data/lib/glimmer/swt/custom/drawable.rb +4 -6
  15. data/lib/glimmer/swt/custom/shape.rb +69 -12
  16. data/lib/glimmer/swt/date_time_proxy.rb +1 -3
  17. data/lib/glimmer/swt/display_proxy.rb +12 -1
  18. data/lib/glimmer/swt/font_proxy.rb +1 -0
  19. data/lib/glimmer/swt/image_proxy.rb +79 -1
  20. data/lib/glimmer/swt/layout_proxy.rb +4 -1
  21. data/lib/glimmer/swt/shell_proxy.rb +10 -1
  22. data/lib/glimmer/swt/table_proxy.rb +27 -14
  23. data/lib/glimmer/swt/widget_proxy.rb +12 -2
  24. data/lib/glimmer/ui/custom_widget.rb +6 -2
  25. data/samples/elaborate/meta_sample.rb +5 -2
  26. data/samples/elaborate/meta_sample/meta_sample_logo.png +0 -0
  27. data/samples/elaborate/tetris.rb +60 -7
  28. data/samples/elaborate/tetris/model/game.rb +36 -7
  29. data/samples/elaborate/tetris/model/past_game.rb +14 -1
  30. data/samples/elaborate/tetris/model/tetromino.rb +18 -5
  31. data/samples/elaborate/tetris/view/block.rb +8 -13
  32. data/samples/elaborate/tetris/view/high_score_dialog.rb +23 -6
  33. data/samples/elaborate/tetris/view/playfield.rb +1 -1
  34. data/samples/elaborate/tetris/view/tetris_menu_bar.rb +22 -6
  35. data/samples/hello/hello_canvas.rb +10 -9
  36. data/samples/hello/hello_canvas_animation.rb +5 -5
  37. data/samples/hello/hello_code_text.rb +104 -0
  38. data/samples/hello/hello_table.rb +6 -4
  39. data/samples/hello/hello_table/baseball_park.png +0 -0
  40. metadata +6 -2
@@ -29,17 +29,15 @@ 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
- def resetup_shape_paint_listeners
35
+ def resetup_shape_painting
39
36
  # TODO consider performance optimization relating to order of shape rendering (affecting only further shapes not previous ones)
37
+ reset_gc if respond_to?(:reset_gc)
40
38
  shapes.each do |shape|
41
39
  shape.paint_listener_proxy&.unregister
42
- shape.setup_paint_listener
40
+ shape.setup_painting
43
41
  end
44
42
  end
45
43
  end
@@ -30,7 +30,6 @@ module Glimmer
30
30
  module SWT
31
31
  module Custom
32
32
  # Represents a shape (graphics) to be drawn on a control/widget/canvas/display
33
- # swt_widget returns the parent (e.g. a `canvas` WidgetProxy), equivalent to `parent.swt_widget`
34
33
  # That is because Shape is drawn on a parent as graphics and doesn't have an SWT widget for itself
35
34
  class Shape
36
35
  include Packages
@@ -78,9 +77,21 @@ module Glimmer
78
77
  def flyweight_method_names
79
78
  @flyweight_method_names ||= {}
80
79
  end
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
88
+
89
+ def flyweigh_patterns
90
+ @flyweigh_patterns ||= {}
91
+ end
81
92
  end
82
93
 
83
- attr_reader :parent, :name, :args, :options, :swt_widget, :paint_listener_proxy
94
+ attr_reader :parent, :name, :args, :options, :paint_listener_proxy
84
95
 
85
96
  def initialize(parent, keyword, *args, &property_block)
86
97
  @parent = parent
@@ -88,7 +99,6 @@ module Glimmer
88
99
  @method_name = self.class.method_name(keyword, args)
89
100
  @options = self.class.arg_options(args, extract: true)
90
101
  @args = args
91
- @swt_widget = parent.respond_to?(:swt_display) ? parent.swt_display : parent.swt_widget
92
102
  @properties = {}
93
103
  @parent.shapes << self
94
104
  post_add_content if property_block.nil?
@@ -110,8 +120,17 @@ 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
- setup_paint_listener
132
+ amend_method_name_options_based_on_properties
133
+ setup_painting
115
134
  @content_added = true
116
135
  end
117
136
 
@@ -147,7 +166,7 @@ module Glimmer
147
166
  end
148
167
  end
149
168
  new_args = [DisplayProxy.instance.swt_display] + args
150
- 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])
151
170
  args[1..-1] = []
152
171
  end
153
172
  args
@@ -172,7 +191,7 @@ module Glimmer
172
191
  args[0] = ImageProxy.new(args[0])
173
192
  end
174
193
  if method_name.include?('image') && args.first.is_a?(ImageProxy)
175
- args[0] = args[0].swt_image
194
+ @image = args[0] = args[0].swt_image
176
195
  end
177
196
  end
178
197
 
@@ -186,6 +205,21 @@ module Glimmer
186
205
  args[the_java_method_arg_count..-1] = []
187
206
  end
188
207
  end
208
+
209
+ def amend_method_name_options_based_on_properties
210
+ if has_some_background? && !has_some_foreground?
211
+ @options[:fill] = true
212
+ elsif !has_some_background? && has_some_foreground?
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
220
+ end
221
+ @method_name = self.class.method_name(@name, @args + [@options])
222
+ end
189
223
 
190
224
  def has_attribute?(attribute_name, *args)
191
225
  self.class.gc_instance_methods.include?(attribute_setter(attribute_name))
@@ -194,7 +228,7 @@ module Glimmer
194
228
  def set_attribute(attribute_name, *args)
195
229
  @properties[attribute_name] = args
196
230
  if @content_added && !@parent.is_disposed
197
- @parent.resetup_shape_paint_listeners
231
+ @parent.resetup_shape_painting
198
232
  @parent.redraw
199
233
  end
200
234
  end
@@ -203,20 +237,43 @@ module Glimmer
203
237
  @properties.symbolize_keys[attribute_name.to_s.to_sym]
204
238
  end
205
239
 
206
- def setup_paint_listener
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
+
260
+ def setup_painting
261
+ # TODO consider moving this method to parent (making the logic polymorphic)
207
262
  return if @parent.is_disposed
208
263
  if parent.respond_to?(:swt_display)
209
264
  @paint_listener_proxy = @parent.on_swt_paint(&method(:paint))
265
+ elsif parent.respond_to?(:swt_image)
266
+ paint(parent) # treat parent as paint event since you don't do repaints with images, it's a one time deal.
210
267
  elsif parent.respond_to?(:swt_widget)
211
268
  @paint_listener_proxy = @parent.on_paint_control(&method(:paint))
212
269
  end
213
270
  end
214
271
 
215
272
  def paint(paint_event)
216
- @properties['background'] = [@parent.background] if fill? && !@properties.keys.map(&:to_s).include?('background')
217
- @properties['foreground'] = [@parent.foreground] if draw? && !@properties.keys.map(&:to_s).include?('foreground')
218
- @properties['font'] = [@parent.font] if draw? && !@properties.keys.map(&:to_s).include?('font')
219
- @properties['transform'] = [nil] if !@properties.keys.map(&:to_s).include?('transform')
273
+ @properties['background'] = [@parent.background] if fill? && !has_some_background?
274
+ @properties['foreground'] = [@parent.foreground] if @parent.respond_to?(:foreground) && draw? && !has_some_foreground?
275
+ @properties['font'] = [@parent.font] if @parent.respond_to?(:font) && draw? && !@properties.keys.map(&:to_s).include?('font')
276
+ @properties['transform'] = [nil] if @parent.respond_to?(:transform) && !@properties.keys.map(&:to_s).include?('transform')
220
277
  @properties.each do |property, args|
221
278
  method_name = attribute_setter(property)
222
279
  converted_args = apply_property_arg_conversions(method_name, property, args)
@@ -43,9 +43,7 @@ module Glimmer
43
43
  end
44
44
 
45
45
  def date=(date_value)
46
- self.year = date_value.year
47
- self.month = date_value.month
48
- self.day = date_value.day
46
+ swt_widget.setDate(date_value.year, date_value.month - 1, date_value.day)
49
47
  end
50
48
 
51
49
  def time
@@ -40,7 +40,7 @@ module Glimmer
40
40
 
41
41
  include Custom::Drawable
42
42
 
43
- OBSERVED_MENU_ITEMS = ['about', 'preferences']
43
+ OBSERVED_MENU_ITEMS = ['about', 'preferences', 'quit']
44
44
 
45
45
  class FilterListener
46
46
  include org.eclipse.swt.widgets.Listener
@@ -71,6 +71,9 @@ module Glimmer
71
71
  Display.app_name ||= 'Glimmer'
72
72
  @swt_display = Display.new(*args)
73
73
  @swt_display.set_data('proxy', self)
74
+ on_swt_Dispose {
75
+ clear_shapes
76
+ }
74
77
  end
75
78
 
76
79
  def content(&block)
@@ -88,6 +91,14 @@ module Glimmer
88
91
  def timer_exec(&block)
89
92
  @swt_display.timer_exec(&block)
90
93
  end
94
+
95
+ def on_widget_disposed(&block)
96
+ on_swt_Dispose(&block)
97
+ end
98
+
99
+ def disposed?
100
+ @swt_display.isDisposed
101
+ end
91
102
 
92
103
  def method_missing(method, *args, &block)
93
104
  if can_handle_observation_request?(method)
@@ -43,6 +43,7 @@ module Glimmer
43
43
 
44
44
  attr_reader :widget_proxy, :swt_font, :font_properties
45
45
 
46
+
46
47
  # Builds a new font proxy from passed in widget_proxy and font_properties hash,
47
48
  #
48
49
  # It begins with existing SWT widget font and amends it with font properties.
@@ -19,6 +19,9 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
+ require 'glimmer/swt/custom/drawable'
23
+ require 'glimmer/swt/properties'
24
+
22
25
  module Glimmer
23
26
  module SWT
24
27
  # Proxy for org.eclipse.swt.graphics.Image
@@ -27,6 +30,9 @@ module Glimmer
27
30
  #
28
31
  # Follows the Proxy Design Pattern
29
32
  class ImageProxy
33
+ include Custom::Drawable
34
+ include Properties
35
+
30
36
  class << self
31
37
  def create(*args)
32
38
  if args.size == 1 && args.first.is_a?(ImageProxy)
@@ -37,6 +43,7 @@ module Glimmer
37
43
  end
38
44
  end
39
45
 
46
+ include_package 'org.eclipse.swt.widgets'
40
47
  include_package 'org.eclipse.swt.graphics'
41
48
 
42
49
  attr_reader :file_path, :jar_file_path, :image_data, :swt_image
@@ -46,8 +53,13 @@ module Glimmer
46
53
  # Takes the same args as the SWT Image class
47
54
  # Alternatively, takes a file path string or a uri:classloader file path string (generated by JRuby when invoking `File.expand_path` inside a JAR file)
48
55
  # and returns an image object.
49
- def initialize(*args)
56
+ def initialize(*args, &content)
50
57
  @args = args
58
+ @parent_proxy = nil
59
+ if @args.first.is_a?(WidgetProxy)
60
+ @parent_proxy = @args.shift
61
+ @parent = @parent_proxy.swt_widget
62
+ end
51
63
  options = @args.last.is_a?(Hash) ? @args.delete_at(-1) : {}
52
64
  options[:swt_image] = @args.first if @args.size == 1 && @args.first.is_a?(Image)
53
65
  @file_path = @args.first if @args.size == 1 && @args.first.is_a?(String)
@@ -55,6 +67,9 @@ module Glimmer
55
67
  if options&.keys&.include?(:swt_image)
56
68
  @swt_image = options[:swt_image]
57
69
  @original_image_data = @image_data = @swt_image.image_data
70
+ elsif args.size == 1 && args.first.is_a?(ImageProxy)
71
+ @swt_image = @args.first.swt_image
72
+ @original_image_data = @image_data = @swt_image.image_data
58
73
  elsif @file_path
59
74
  @original_image_data = @image_data = ImageData.new(input_stream || @file_path)
60
75
  @swt_image = Image.new(DisplayProxy.instance.swt_display, @image_data)
@@ -64,9 +79,22 @@ module Glimmer
64
79
  width = (@image_data.width.to_f / @image_data.height.to_f)*height.to_f if !height.nil? && width.nil?
65
80
  scale_to(width, height) unless width.nil? || height.nil?
66
81
  else
82
+ @args.prepend(DisplayProxy.instance.swt_display) unless @args.first.is_a?(Display)
67
83
  @swt_image = Image.new(*@args)
68
84
  @original_image_data = @image_data = @swt_image.image_data
69
85
  end
86
+ @swt_image.singleton_class.alias_method(:dispose_without_glimmer, :dispose)
87
+ proxy = self
88
+ # TODO consider adding a get_data/set_data method to conform with other SWT widgets
89
+ @swt_image.singleton_class.define_method(:dispose) do
90
+ proxy.clear_shapes
91
+ dispose_without_glimmer
92
+ end
93
+ post_add_content if content.nil?
94
+ end
95
+
96
+ def post_add_content
97
+ @parent&.image = swt_image
70
98
  end
71
99
 
72
100
  def input_stream
@@ -91,6 +119,56 @@ module Glimmer
91
119
  self
92
120
  end
93
121
 
122
+ def gc
123
+ @gc ||= reset_gc
124
+ end
125
+
126
+ def reset_gc
127
+ @gc = org.eclipse.swt.graphics.GC.new(swt_image)
128
+ end
129
+
130
+ def disposed?
131
+ @swt_image.isDisposed
132
+ end
133
+
134
+ def has_attribute?(attribute_name, *args)
135
+ @swt_image.respond_to?(attribute_setter(attribute_name), args) || respond_to?(ruby_attribute_setter(attribute_name), args)
136
+ end
137
+
138
+ def set_attribute(attribute_name, *args)
139
+ # TODO consider refactoring/unifying this code with WidgetProxy and elsewhere
140
+ if args.count == 1
141
+ if args.first.is_a?(Symbol) || args.first.is_a?(String)
142
+ args[0] = ColorProxy.new(args.first).swt_color
143
+ end
144
+ if args.first.is_a?(ColorProxy)
145
+ args[0] = args.first.swt_color
146
+ end
147
+ end
148
+
149
+ if @swt_image.respond_to?(attribute_setter(attribute_name))
150
+ @swt_image.send(attribute_setter(attribute_name), *args) unless @swt_image.send(attribute_getter(attribute_name)) == args.first
151
+ elsif @swt_image.respond_to?(ruby_attribute_setter(attribute_name))
152
+ @swt_image.send(ruby_attribute_setter(attribute_name), args)
153
+ else
154
+ send(ruby_attribute_setter(attribute_name), args)
155
+ end
156
+ end
157
+
158
+ def get_attribute(attribute_name)
159
+ if @swt_image.respond_to?(attribute_getter(attribute_name))
160
+ @swt_image.send(attribute_getter(attribute_name))
161
+ elsif @swt_image.respond_to?(ruby_attribute_getter(attribute_name))
162
+ @swt_image.send(ruby_attribute_getter(attribute_name))
163
+ elsif @swt_image.respond_to?(attribute_name)
164
+ @swt_image.send(attribute_name)
165
+ elsif respond_to?(ruby_attribute_getter(attribute_name))
166
+ send(ruby_attribute_getter(attribute_name))
167
+ else
168
+ send(attribute_name)
169
+ end
170
+ end
171
+
94
172
  def method_missing(method, *args, &block)
95
173
  swt_image.send(method, *args, &block)
96
174
  rescue => e
@@ -72,7 +72,9 @@ module Glimmer
72
72
  @swt_layout.marginRight = 0 if @swt_layout.respond_to?(:marginRight)
73
73
  @swt_layout.marginBottom = 0 if @swt_layout.respond_to?(:marginBottom)
74
74
  @swt_layout.marginLeft = 0 if @swt_layout.respond_to?(:marginLeft)
75
+ old_layout = @widget_proxy.swt_widget.getLayout
75
76
  @widget_proxy.swt_widget.setLayout(@swt_layout)
77
+ @widget_proxy.swt_widget.layout if old_layout
76
78
  end
77
79
 
78
80
  def has_attribute?(attribute_name, *args)
@@ -83,7 +85,8 @@ module Glimmer
83
85
  apply_property_type_converters(attribute_name, args)
84
86
  if args.first != @swt_layout.send(attribute_getter(attribute_name))
85
87
  @swt_layout.send(attribute_setter(attribute_name), *args)
86
- @widget_proxy.swt_widget.getShell.pack
88
+ @widget_proxy.swt_widget.layout
89
+ @widget_proxy.swt_widget.getShell.layout
87
90
  end
88
91
  end
89
92
 
@@ -82,6 +82,9 @@ module Glimmer
82
82
  end
83
83
  end
84
84
  end
85
+ on_widget_disposed {
86
+ clear_shapes
87
+ }
85
88
  @display ||= @swt_widget.getDisplay
86
89
  end
87
90
 
@@ -124,8 +127,14 @@ module Glimmer
124
127
  end
125
128
  alias disposed disposed?
126
129
 
130
+ # Hides shell. Automatically checks if widget is disposed to avoid crashing.
127
131
  def hide
128
- @swt_widget.setVisible(false)
132
+ @swt_widget.setVisible(false) unless @swt_widget.isDisposed
133
+ end
134
+
135
+ # Closes shell. Automatically checks if widget is disposed to avoid crashing.
136
+ def close
137
+ @swt_widget.close unless @swt_widget.isDisposed
129
138
  end
130
139
 
131
140
  def visible?
@@ -273,6 +273,10 @@ module Glimmer
273
273
  swt_widget.data
274
274
  end
275
275
 
276
+ def table_items_binding
277
+ swt_widget.get_data('table_items_binding')
278
+ end
279
+
276
280
  def sort_block=(comparator)
277
281
  @sort_block = comparator
278
282
  end
@@ -357,9 +361,9 @@ module Glimmer
357
361
  @additional_sort_properties = args unless args.empty?
358
362
  end
359
363
 
360
- def sort!
364
+ def sort!(internal_sort: false)
361
365
  return unless sort_property && (sort_type || sort_block || sort_by_block)
362
- array = model_binding.evaluate_property
366
+ original_array = array = model_binding.evaluate_property
363
367
  array = array.sort_by(&:hash) # this ensures consistent subsequent sorting in case there are equivalent sorts to avoid an infinite loop
364
368
  # Converting value to_s first to handle nil cases. Should work with numeric, boolean, and date fields
365
369
  if sort_block
@@ -383,7 +387,12 @@ module Glimmer
383
387
  end
384
388
  end
385
389
  sorted_array = sorted_array.reverse if sort_direction == :descending
386
- model_binding.call(sorted_array)
390
+ if model_binding.binding_options.symbolize_keys[:read_only_sort]
391
+ table_items_binding.call(sorted_array, internal_sort: true) unless internal_sort
392
+ else
393
+ model_binding.call(sorted_array)
394
+ end
395
+ sorted_array
387
396
  end
388
397
 
389
398
  def editor=(args)
@@ -448,7 +457,7 @@ module Glimmer
448
457
  edit_table_item(swt_widget.getSelection.first, column_index, before_write: before_write, after_write: after_write, after_cancel: after_cancel)
449
458
  end
450
459
 
451
- def edit_table_item(table_item, column_index, before_write: nil, after_write: nil, after_cancel: nil)
460
+ def edit_table_item(table_item, column_index, before_write: nil, after_write: nil, after_cancel: nil, write_on_cancel: false)
452
461
  return if table_item.nil?
453
462
  model = table_item.data
454
463
  property = column_properties[column_index]
@@ -467,18 +476,22 @@ module Glimmer
467
476
  widget_value_property = TableProxy::editors.symbolize_keys[editor_widget][:widget_value_property]
468
477
 
469
478
  @cancel_edit = lambda do |event=nil|
470
- @cancel_in_progress = true
471
- @table_editor_widget_proxy&.swt_widget&.dispose
472
- @table_editor_widget_proxy = nil
473
- if after_cancel&.arity == 0
474
- after_cancel&.call
479
+ if write_on_cancel
480
+ @finish_edit.call(event)
475
481
  else
476
- after_cancel&.call(table_item)
482
+ @cancel_in_progress = true
483
+ @table_editor_widget_proxy&.swt_widget&.dispose
484
+ @table_editor_widget_proxy = nil
485
+ if after_cancel&.arity == 0
486
+ after_cancel&.call
487
+ else
488
+ after_cancel&.call(table_item)
489
+ end
490
+ @edit_in_progress = false
491
+ @cancel_in_progress = false
492
+ @cancel_edit = nil
493
+ @edit_mode = false
477
494
  end
478
- @edit_in_progress = false
479
- @cancel_in_progress = false
480
- @cancel_edit = nil
481
- @edit_mode = false
482
495
  end
483
496
 
484
497
  @finish_edit = lambda do |event=nil|