glimmer-dsl-swt 4.18.4.8 → 4.18.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +70 -0
  3. data/README.md +24 -11
  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 +353 -145
  8. data/docs/reference/GLIMMER_SAMPLES.md +207 -41
  9. data/glimmer-dsl-swt.gemspec +33 -15
  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 +18 -3
  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 +9 -5
  29. data/lib/glimmer/dsl/swt/shape_expression.rb +1 -1
  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 +50 -2
  37. data/lib/glimmer/swt/custom/radio_group.rb +2 -1
  38. data/lib/glimmer/swt/custom/shape.rb +166 -34
  39. data/lib/glimmer/{dsl/swt/directory_dialog_expression.rb → swt/custom/shape/arc.rb} +15 -20
  40. data/lib/glimmer/swt/custom/shape/focus.rb +43 -0
  41. data/lib/glimmer/swt/custom/shape/image.rb +86 -0
  42. data/lib/glimmer/swt/custom/shape/line.rb +58 -0
  43. data/lib/glimmer/swt/custom/shape/oval.rb +43 -0
  44. data/lib/glimmer/swt/custom/shape/point.rb +52 -0
  45. data/lib/glimmer/swt/custom/shape/polygon.rb +73 -0
  46. data/lib/glimmer/swt/custom/shape/polyline.rb +73 -0
  47. data/lib/glimmer/swt/custom/shape/rectangle.rb +87 -0
  48. data/lib/glimmer/swt/custom/shape/text.rb +73 -0
  49. data/lib/glimmer/swt/date_time_proxy.rb +9 -3
  50. data/lib/glimmer/swt/dialog_proxy.rb +92 -0
  51. data/lib/glimmer/swt/display_proxy.rb +62 -2
  52. data/lib/glimmer/swt/expand_item_proxy.rb +18 -12
  53. data/lib/glimmer/swt/font_proxy.rb +13 -7
  54. data/lib/glimmer/swt/image_proxy.rb +16 -5
  55. data/lib/glimmer/swt/layout_data_proxy.rb +21 -15
  56. data/lib/glimmer/swt/layout_proxy.rb +19 -15
  57. data/lib/glimmer/swt/menu_proxy.rb +2 -2
  58. data/lib/glimmer/swt/message_box_proxy.rb +21 -7
  59. data/lib/glimmer/swt/properties.rb +3 -0
  60. data/lib/glimmer/swt/proxy_properties.rb +145 -0
  61. data/lib/glimmer/swt/scrolled_composite_proxy.rb +20 -7
  62. data/lib/glimmer/swt/shell_proxy.rb +96 -80
  63. data/lib/glimmer/swt/swt_proxy.rb +17 -0
  64. data/lib/glimmer/swt/tab_item_proxy.rb +6 -3
  65. data/lib/glimmer/swt/table_proxy.rb +32 -11
  66. data/lib/glimmer/swt/transform_proxy.rb +26 -38
  67. data/lib/glimmer/swt/tree_proxy.rb +11 -16
  68. data/lib/glimmer/swt/widget_listener_proxy.rb +6 -2
  69. data/lib/glimmer/swt/widget_proxy.rb +194 -137
  70. data/lib/glimmer/ui.rb +5 -0
  71. data/lib/glimmer/ui/custom_shell.rb +13 -7
  72. data/lib/glimmer/ui/custom_widget.rb +4 -5
  73. data/samples/elaborate/contact_manager.rb +7 -7
  74. data/samples/elaborate/login.rb +25 -21
  75. data/samples/elaborate/mandelbrot_fractal.rb +346 -39
  76. data/samples/elaborate/meta_sample.rb +1 -1
  77. data/samples/elaborate/tetris.rb +1 -0
  78. data/samples/elaborate/tic_tac_toe.rb +16 -14
  79. data/samples/elaborate/tic_tac_toe/board.rb +5 -5
  80. data/samples/elaborate/tic_tac_toe/cell.rb +5 -5
  81. data/samples/hello/hello_button.rb +7 -7
  82. data/samples/hello/hello_canvas.rb +145 -41
  83. data/samples/hello/hello_checkbox.rb +16 -14
  84. data/samples/hello/hello_checkbox_group.rb +11 -9
  85. data/samples/hello/hello_color_dialog.rb +66 -0
  86. data/samples/hello/hello_combo.rb +14 -12
  87. data/samples/hello/hello_computed.rb +7 -7
  88. data/samples/hello/hello_cursor.rb +58 -0
  89. data/samples/hello/hello_custom_shell.rb +17 -21
  90. data/samples/hello/hello_custom_widget.rb +4 -6
  91. data/samples/hello/hello_date_time.rb +14 -12
  92. data/samples/hello/hello_directory_dialog.rb +7 -7
  93. data/samples/hello/hello_expand_bar.rb +8 -8
  94. data/samples/hello/hello_file_dialog.rb +7 -7
  95. data/samples/hello/hello_font_dialog.rb +82 -0
  96. data/samples/hello/hello_group.rb +18 -16
  97. data/samples/hello/hello_list_multi_selection.rb +13 -11
  98. data/samples/hello/hello_list_single_selection.rb +13 -11
  99. data/samples/hello/hello_progress_bar.rb +125 -0
  100. data/samples/hello/hello_radio.rb +18 -16
  101. data/samples/hello/hello_radio_group.rb +14 -12
  102. data/samples/hello/hello_spinner.rb +7 -7
  103. data/samples/hello/hello_tab.rb +5 -5
  104. data/samples/hello/hello_table.rb +10 -5
  105. data/samples/hello/hello_tree.rb +485 -0
  106. metadata +30 -23
  107. data/lib/glimmer/swt/directory_dialog_proxy.rb +0 -65
  108. data/lib/glimmer/swt/file_dialog_proxy.rb +0 -66
@@ -49,10 +49,27 @@ module Glimmer
49
49
  def extra_styles
50
50
  EXTRA_STYLES
51
51
  end
52
+
53
+ def cursor_styles
54
+ SWT.constants.select {|c| c.to_s.start_with?('CURSOR_')}
55
+ end
56
+
57
+ def color_styles
58
+ SWT.constants.select {|c| c.to_s.start_with?('COLOR_')}
59
+ end
60
+
61
+ def cursor_options
62
+ cursor_styles.map {|c| c.to_s.sub('CURSOR_', '').downcase}.map(&:to_sym)
63
+ end
64
+
65
+ def color_options
66
+ color_styles.map {|c| c.to_s.sub('COLOR_', '').downcase}.map(&:to_sym)
67
+ end
52
68
  end
53
69
 
54
70
  EXTRA_STYLES = {
55
71
  NO_RESIZE: self[:shell_trim, :resize!, :max!],
72
+ WINDOW_TRIM: self[:shell_trim],
56
73
  }
57
74
  end
58
75
  end
@@ -49,6 +49,7 @@ module Glimmer
49
49
  @widget_proxy = SWT::WidgetProxy.new('tab_item', parent, style)
50
50
  @swt_tab_item = @widget_proxy.swt_widget
51
51
  @swt_tab_item.control = swt_widget
52
+ # parent.pack # TODO auto fix the tab folder to avoid having to do this manually (including maintaining bounds and minimum size of shell)
52
53
  end
53
54
 
54
55
  def has_attribute?(attribute_name, *args)
@@ -82,9 +83,11 @@ module Glimmer
82
83
  end
83
84
 
84
85
  def dispose
85
- swt_tab_item.setControl(nil)
86
- swt_widget.dispose
87
- swt_tab_item.dispose
86
+ auto_exec do
87
+ swt_tab_item.setControl(nil)
88
+ swt_widget.dispose
89
+ swt_tab_item.dispose
90
+ end
88
91
  end
89
92
  end
90
93
  end
@@ -266,15 +266,21 @@ module Glimmer
266
266
  end
267
267
 
268
268
  def items
269
- swt_widget.get_items
269
+ auto_exec do
270
+ swt_widget.get_items
271
+ end
270
272
  end
271
273
 
272
274
  def model_binding
273
- swt_widget.data
275
+ auto_exec do
276
+ swt_widget.data
277
+ end
274
278
  end
275
279
 
276
280
  def table_items_binding
277
- swt_widget.get_data('table_items_binding')
281
+ auto_exec do
282
+ swt_widget.get_data('table_items_binding')
283
+ end
278
284
  end
279
285
 
280
286
  def sort_block=(comparator)
@@ -313,7 +319,10 @@ module Glimmer
313
319
 
314
320
  # Sorts by specified TableColumnProxy object. If nil, it uses the table default sort instead.
315
321
  def sort_by_column!(table_column_proxy=nil)
316
- index = swt_widget.columns.to_a.index(table_column_proxy.swt_widget) unless table_column_proxy.nil?
322
+ index = nil
323
+ auto_exec do
324
+ index = swt_widget.columns.to_a.index(table_column_proxy.swt_widget) unless table_column_proxy.nil?
325
+ end
317
326
  new_sort_property = table_column_proxy.nil? ? @sort_property : table_column_proxy.sort_property || [column_properties[index]]
318
327
  return if table_column_proxy.nil? && new_sort_property.nil? && @sort_block.nil? && @sort_by_block.nil?
319
328
  if new_sort_property && table_column_proxy.nil? && new_sort_property.size == 1 && (index = column_sort_properties.index(new_sort_property))
@@ -330,12 +339,16 @@ module Glimmer
330
339
  end
331
340
 
332
341
  @sort_direction = @sort_direction.nil? || @sort_property.first != new_sort_property.first || @sort_direction == :descending ? :ascending : :descending
333
- swt_widget.sort_direction = @sort_direction == :ascending ? SWTProxy[:up] : SWTProxy[:down]
342
+ auto_exec do
343
+ swt_widget.sort_direction = @sort_direction == :ascending ? SWTProxy[:up] : SWTProxy[:down]
344
+ end
334
345
 
335
346
  @sort_property = [new_sort_property].flatten.compact
336
347
  table_column_index = column_properties.index(new_sort_property.to_s.to_sym)
337
348
  table_column_proxy ||= table_column_proxies[table_column_index] if table_column_index
338
- swt_widget.sort_column = table_column_proxy.swt_widget if table_column_proxy
349
+ auto_exec do
350
+ swt_widget.sort_column = table_column_proxy.swt_widget if table_column_proxy
351
+ end
339
352
 
340
353
  if table_column_proxy
341
354
  @sort_by_block = nil
@@ -405,14 +418,18 @@ module Glimmer
405
418
 
406
419
  def cells
407
420
  column_count = @table.column_properties.size
408
- swt_widget.items.map {|item| column_count.times.map {|i| item.get_text(i)} }
421
+ auto_exec do
422
+ swt_widget.items.map {|item| column_count.times.map {|i| item.get_text(i)} }
423
+ end
409
424
  end
410
425
 
411
426
  # Performs a search for table items matching block condition
412
427
  # If no condition block is passed, returns all table items
413
428
  # Returns a Java TableItem array to easily set as selection on org.eclipse.swt.Table if needed
414
429
  def search(&condition)
415
- swt_widget.getItems.select {|item| condition.nil? || condition.call(item)}.to_java(TableItem)
430
+ auto_exec do
431
+ swt_widget.getItems.select {|item| condition.nil? || condition.call(item)}.to_java(TableItem)
432
+ end
416
433
  end
417
434
 
418
435
  # Returns all table items including descendants
@@ -454,7 +471,9 @@ module Glimmer
454
471
  end
455
472
 
456
473
  def edit_selected_table_item(column_index, before_write: nil, after_write: nil, after_cancel: nil)
457
- edit_table_item(swt_widget.getSelection.first, column_index, before_write: before_write, after_write: after_write, after_cancel: after_cancel)
474
+ auto_exec do
475
+ edit_table_item(swt_widget.getSelection.first, column_index, before_write: before_write, after_write: after_write, after_cancel: after_cancel)
476
+ end
458
477
  end
459
478
 
460
479
  def edit_table_item(table_item, column_index, before_write: nil, after_write: nil, after_cancel: nil, write_on_cancel: false)
@@ -512,7 +531,9 @@ module Glimmer
512
531
  model.send("#{model_editing_property}=", new_value) # makes table update itself, so must search for selected table item again
513
532
  # Table refresh happens here because of model update triggering observers, so must retrieve table item again
514
533
  edited_table_item = search { |ti| ti.getData == model }.first
515
- swt_widget.showItem(edited_table_item)
534
+ auto_exec do
535
+ swt_widget.showItem(edited_table_item)
536
+ end
516
537
  @table_editor_widget_proxy&.swt_widget&.dispose
517
538
  @table_editor_widget_proxy = nil
518
539
  if after_write&.arity == 0
@@ -524,7 +545,7 @@ module Glimmer
524
545
  end
525
546
  end
526
547
  end
527
-
548
+
528
549
  content {
529
550
  @table_editor_widget_proxy = TableProxy::editors.symbolize_keys[editor_widget][:editor_gui].call(editor_widget_args, model, model_editing_property, self)
530
551
  }
@@ -37,32 +37,36 @@ module Glimmer
37
37
  attr_reader :swt_transform, :parent
38
38
 
39
39
  def initialize(parent, *args, swt_transform: nil, multiply: false)
40
- @parent = parent
41
- @multiply = multiply
42
- if swt_transform.nil?
43
- if !args.first.is_a?(Display) && !args.first.is_a?(DisplayProxy)
44
- args.prepend DisplayProxy.instance.swt_display
45
- end
46
- if args.first.is_a?(DisplayProxy)
47
- args[0] = args[0].swt_display
48
- end
49
- if args.last.is_a?(TransformProxy)
50
- args[-1] = args[-1].swt_transform
51
- end
52
- if args.last.nil? || args.last.is_a?(Transform)
53
- @swt_transform = args.last
54
- @parent&.set_attribute('transform', self)
40
+ Glimmer::SWT::DisplayProxy.instance.auto_exec do
41
+ @parent = parent
42
+ @multiply = multiply
43
+ if swt_transform.nil?
44
+ if !args.first.is_a?(Display) && !args.first.is_a?(DisplayProxy)
45
+ args.prepend DisplayProxy.instance.swt_display
46
+ end
47
+ if args.first.is_a?(DisplayProxy)
48
+ args[0] = args[0].swt_display
49
+ end
50
+ if args.last.is_a?(TransformProxy)
51
+ args[-1] = args[-1].swt_transform
52
+ end
53
+ if args.last.nil? || args.last.is_a?(Transform)
54
+ @swt_transform = args.last
55
+ @parent&.set_attribute('transform', self)
56
+ else
57
+ @swt_transform = Transform.new(*args)
58
+ end
55
59
  else
56
- @swt_transform = Transform.new(*args)
60
+ @swt_transform = swt_transform
57
61
  end
58
- else
59
- @swt_transform = swt_transform
60
62
  end
61
63
  end
62
64
 
63
65
  def post_add_content
64
66
  if @multiply
65
- @parent.multiply(@swt_transform)
67
+ Glimmer::SWT::DisplayProxy.instance.auto_exec {
68
+ @parent.multiply(@swt_transform)
69
+ }
66
70
  else
67
71
  @parent&.set_attribute('transform', self)
68
72
  end
@@ -72,28 +76,12 @@ module Glimmer
72
76
  Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::SWT::TransformExpression.new, &block)
73
77
  end
74
78
 
75
- def has_attribute?(attribute_name, *args)
76
- @swt_transform.respond_to?(attribute_name) || @swt_transform.respond_to?(attribute_setter(attribute_name))
77
- end
78
-
79
- def set_attribute(attribute_name, *args)
80
- if @swt_transform.respond_to?(attribute_name)
81
- @swt_transform.send(attribute_name, *args)
82
- elsif @swt_transform.respond_to?(attribute_setter(attribute_name))
83
- @swt_transform.send(attribute_setter(attribute_name), *args)
84
- end
85
- end
86
-
87
- def get_attribute(attribute_name)
88
- if @swt_transform.respond_to?(attribute_getter(attribute_name))
89
- @swt_transform.send(attribute_getter(attribute_name))
90
- else
91
- @swt_transform.send(attribute_name)
92
- end
79
+ def proxy_source_object
80
+ @swt_transform
93
81
  end
94
82
 
95
83
  def method_missing(method_name, *args, &block)
96
- result = @swt_transform.send(method_name, *args, &block)
84
+ result = Glimmer::SWT::DisplayProxy.instance.auto_exec { @swt_transform.send(method_name, *args, &block) }
97
85
  result.nil? ? self : result
98
86
  rescue => e
99
87
  Glimmer::Config.logger.debug {"Neither MessageBoxProxy nor #{@swt_transform.class.name} can handle the method ##{method}"}
@@ -42,7 +42,8 @@ module Glimmer
42
42
  # Returns a Java TreeItem array to easily set as selection on org.eclipse.swt.Tree if needed
43
43
  def depth_first_search(&condition)
44
44
  found = []
45
- recursive_depth_first_search(swt_widget.getItems.first, found, &condition)
45
+ first_item = DisplayProxy.instance.auto_exec { swt_widget.getItems.first }
46
+ recursive_depth_first_search(first_item, found, &condition)
46
47
  found.to_java(TreeItem)
47
48
  end
48
49
 
@@ -51,18 +52,6 @@ module Glimmer
51
52
  depth_first_search
52
53
  end
53
54
 
54
- def widget_property_listener_installers
55
- super.merge({
56
- Java::OrgEclipseSwtWidgets::Tree => {
57
- selection: lambda do |observer|
58
- on_widget_selected { |selection_event|
59
- observer.call(@swt_widget.getSelection)
60
- }
61
- end
62
- },
63
- })
64
- end
65
-
66
55
  def edit_in_progress?
67
56
  !!@edit_in_progress
68
57
  end
@@ -127,8 +116,10 @@ module Glimmer
127
116
 
128
117
  def recursive_depth_first_search(tree_item, found, &condition)
129
118
  return if tree_item.nil?
130
- found << tree_item if condition.nil? || condition.call(tree_item)
131
- tree_item.getItems.each do |child_tree_item|
119
+ found << tree_item if condition.nil? || DisplayProxy.instance.auto_exec {condition.call(tree_item)}
120
+ # return if found.any? && !has_style?(:multi) # TODO inspect if this is a good optimization when needed
121
+ tree_items = DisplayProxy.instance.auto_exec {tree_item.getItems}
122
+ tree_items.each do |child_tree_item|
132
123
  recursive_depth_first_search(child_tree_item, found, &condition)
133
124
  end
134
125
  end
@@ -136,7 +127,11 @@ module Glimmer
136
127
  def property_type_converters
137
128
  super.merge({
138
129
  selection: lambda do |value|
139
- depth_first_search {|ti| ti.getData == value}
130
+ if value.is_a?(Array)
131
+ depth_first_search {|ti| value.include?(ti.getData) }
132
+ else
133
+ depth_first_search {|ti| ti.getData == value}
134
+ end
140
135
  end,
141
136
  })
142
137
  end
@@ -53,9 +53,13 @@ module Glimmer
53
53
  @swt_display.removeListener(@event_type, @swt_listener)
54
54
  end
55
55
  elsif @event_type
56
- @swt_widget.removeListener(@event_type, @swt_listener)
56
+ DisplayProxy.instance.auto_exec do
57
+ @swt_widget.removeListener(@event_type, @swt_listener)
58
+ end
57
59
  else
58
- @swt_widget.send(widget_remove_listener_method, @swt_listener)
60
+ DisplayProxy.instance.auto_exec do
61
+ @swt_widget.send(widget_remove_listener_method, @swt_listener)
62
+ end
59
63
  end
60
64
  end
61
65
  alias unregister deregister # TODO consider dropping unregister (and in Observer too)
@@ -26,7 +26,7 @@ require 'glimmer/swt/swt_proxy'
26
26
  require 'glimmer/swt/display_proxy'
27
27
  require 'glimmer/swt/dnd_proxy'
28
28
  require 'glimmer/swt/image_proxy'
29
- require 'glimmer/swt/properties'
29
+ require 'glimmer/swt/proxy_properties'
30
30
  require 'glimmer/swt/custom/drawable'
31
31
 
32
32
  # TODO refactor to make file smaller and extract sub-widget-proxies out of this
@@ -44,13 +44,13 @@ module Glimmer
44
44
  # Follows the Proxy Design Pattern
45
45
  class WidgetProxy
46
46
  include Packages
47
- include Properties
47
+ include ProxyProperties
48
48
  include Custom::Drawable
49
49
 
50
50
  DEFAULT_STYLES = {
51
51
  'arrow' => [:arrow],
52
52
  'button' => [:push],
53
- 'canvas' => [:double_buffered],
53
+ 'canvas' => ([:double_buffered] if OS.windows?),
54
54
  'checkbox' => [:check],
55
55
  'check' => [:check],
56
56
  'drag_source' => [:drop_copy],
@@ -112,13 +112,15 @@ module Glimmer
112
112
  # Instantiates the right WidgetProxy subclass for passed in keyword
113
113
  # Args are: keyword, parent, swt_widget_args (including styles)
114
114
  def create(*init_args, swt_widget: nil)
115
- return swt_widget.get_data('proxy') if swt_widget&.get_data('proxy')
116
- keyword, parent, args = init_args
117
- selected_widget_proxy_class = widget_proxy_class(keyword || underscored_widget_name(swt_widget))
118
- if init_args.empty?
119
- selected_widget_proxy_class.new(swt_widget: swt_widget)
120
- else
121
- selected_widget_proxy_class.new(*init_args)
115
+ DisplayProxy.instance.auto_exec do
116
+ return swt_widget.get_data('proxy') if swt_widget&.get_data('proxy')
117
+ keyword, parent, args = init_args
118
+ selected_widget_proxy_class = widget_proxy_class(keyword || underscored_widget_name(swt_widget))
119
+ if init_args.empty?
120
+ selected_widget_proxy_class.new(swt_widget: swt_widget)
121
+ else
122
+ selected_widget_proxy_class.new(*init_args)
123
+ end
122
124
  end
123
125
  end
124
126
 
@@ -149,31 +151,33 @@ module Glimmer
149
151
  #
150
152
  # Styles is a comma separate list of symbols representing SWT styles in lower case
151
153
  def initialize(*init_args, swt_widget: nil)
152
- @image_double_buffered = !!(init_args&.last&.include?(:image_double_buffered) && init_args&.last&.delete(:image_double_buffered))
153
- if swt_widget.nil?
154
- underscored_widget_name, parent, args = init_args
155
- @parent_proxy = parent
156
- styles, extra_options = extract_args(underscored_widget_name, args)
157
- swt_widget_class = self.class.swt_widget_class_for(underscored_widget_name)
158
- @swt_widget = swt_widget_class.new(@parent_proxy.swt_widget, style(underscored_widget_name, styles), *extra_options)
159
- else
160
- @swt_widget = swt_widget
161
- underscored_widget_name = self.class.underscored_widget_name(@swt_widget)
162
- parent_proxy_class = self.class.widget_proxy_class(self.class.underscored_widget_name(@swt_widget.parent))
163
- parent = swt_widget.parent
164
- @parent_proxy = parent&.get_data('proxy') || parent_proxy_class.new(swt_widget: parent)
165
- end
166
- if @swt_widget&.get_data('proxy').nil?
167
- @swt_widget.set_data('proxy', self)
168
- DEFAULT_INITIALIZERS[underscored_widget_name.to_s.to_sym]&.call(@swt_widget)
169
- @parent_proxy.post_initialize_child(self)
170
- end
171
- @keyword = underscored_widget_name.to_s
172
- if respond_to?(:on_widget_disposed)
173
- on_widget_disposed {
174
- clear_shapes
175
- deregister_shape_painting
176
- }
154
+ auto_exec do
155
+ @image_double_buffered = !!(init_args&.last&.include?(:image_double_buffered) && init_args&.last&.delete(:image_double_buffered))
156
+ if swt_widget.nil?
157
+ underscored_widget_name, parent, args = init_args
158
+ @parent_proxy = parent
159
+ styles, extra_options = extract_args(underscored_widget_name, args)
160
+ swt_widget_class = self.class.swt_widget_class_for(underscored_widget_name)
161
+ @swt_widget = swt_widget_class.new(@parent_proxy.swt_widget, style(underscored_widget_name, styles), *extra_options)
162
+ else
163
+ @swt_widget = swt_widget
164
+ underscored_widget_name = self.class.underscored_widget_name(@swt_widget)
165
+ parent_proxy_class = self.class.widget_proxy_class(self.class.underscored_widget_name(@swt_widget.parent))
166
+ parent = swt_widget.parent
167
+ @parent_proxy = parent&.get_data('proxy') || parent_proxy_class.new(swt_widget: parent)
168
+ end
169
+ if @swt_widget&.get_data('proxy').nil?
170
+ @swt_widget.set_data('proxy', self)
171
+ DEFAULT_INITIALIZERS[underscored_widget_name.to_s.to_sym]&.call(@swt_widget)
172
+ @parent_proxy.post_initialize_child(self)
173
+ end
174
+ @keyword = underscored_widget_name.to_s
175
+ if respond_to?(:on_widget_disposed)
176
+ on_widget_disposed {
177
+ clear_shapes
178
+ deregister_shape_painting
179
+ }
180
+ end
177
181
  end
178
182
  end
179
183
 
@@ -219,81 +223,77 @@ module Glimmer
219
223
  [args, extra_options]
220
224
  end
221
225
  end
222
-
223
- def has_attribute_getter?(attribute_getter_name, *args)
224
- attribute_getter_name = attribute_getter_name.to_s.underscore
225
- return false unless !attribute_getter_name.end_with?('=') && !attribute_getter_name.start_with?('set_')
226
- args.empty? && swt_widget.respond_to?(attribute_getter_name)
227
- end
228
226
 
229
- def has_attribute_setter?(attribute_setter_name, *args)
230
- attribute_setter_name = attribute_setter_name.to_s
231
- underscored_attribute_setter_name = attribute_setter_name.underscore
232
- return false unless attribute_setter_name.end_with?('=') || (attribute_setter_name.start_with?('set_') && !args.empty?)
233
- attribute_name = underscored_attribute_setter_name.sub(/^set_/, '').sub(/=$/, '')
234
- has_attribute?(attribute_name, *args)
227
+ def proxy_source_object
228
+ @swt_widget
235
229
  end
236
-
230
+
237
231
  def has_attribute?(attribute_name, *args)
238
232
  # TODO test that attribute getter responds too
239
233
  widget_custom_attribute = widget_custom_attribute_mapping[attribute_name.to_s]
240
- if widget_custom_attribute
241
- @swt_widget.respond_to?(widget_custom_attribute[:setter][:name])
242
- else
243
- @swt_widget.respond_to?(attribute_setter(attribute_name), args) || respond_to?(ruby_attribute_setter(attribute_name), args)
234
+ auto_exec do
235
+ if widget_custom_attribute
236
+ @swt_widget.respond_to?(widget_custom_attribute[:setter][:name])
237
+ else
238
+ super
239
+ end
244
240
  end
245
241
  end
246
242
 
247
243
  def set_attribute(attribute_name, *args)
248
244
  # TODO Think about widget subclasses overriding set_attribute to add more attributes vs adding as Ruby attributes directly
249
245
  widget_custom_attribute = widget_custom_attribute_mapping[attribute_name.to_s]
250
- apply_property_type_converters(normalized_attribute(attribute_name), args)
251
- if widget_custom_attribute
252
- widget_custom_attribute[:setter][:invoker].call(@swt_widget, args)
253
- elsif @swt_widget.respond_to?(attribute_setter(attribute_name))
254
- @swt_widget.send(attribute_setter(attribute_name), *args) unless @swt_widget.send(attribute_getter(attribute_name)) == args.first
255
- elsif @swt_widget.respond_to?(ruby_attribute_setter(attribute_name))
256
- @swt_widget.send(ruby_attribute_setter(attribute_name), args)
257
- else
258
- send(ruby_attribute_setter(attribute_name), args)
246
+ auto_exec do
247
+ apply_property_type_converters(normalized_attribute(attribute_name), args)
259
248
  end
249
+ swt_widget_operation = false
250
+ result = nil
251
+ auto_exec do
252
+ result = if widget_custom_attribute
253
+ swt_widget_operation = true
254
+ widget_custom_attribute[:setter][:invoker].call(@swt_widget, args)
255
+ end
256
+ end
257
+ result = super unless swt_widget_operation
258
+ result
260
259
  end
261
260
 
262
261
  def get_attribute(attribute_name)
263
262
  widget_custom_attribute = widget_custom_attribute_mapping[attribute_name.to_s]
264
- if widget_custom_attribute
265
- if widget_custom_attribute[:getter][:invoker]
266
- widget_custom_attribute[:getter][:invoker].call(@swt_widget, [])
267
- else
268
- @swt_widget.send(widget_custom_attribute[:getter][:name])
263
+ swt_widget_operation = false
264
+ result = nil
265
+ auto_exec do
266
+ result = if widget_custom_attribute
267
+ swt_widget_operation = true
268
+ if widget_custom_attribute[:getter][:invoker]
269
+ widget_custom_attribute[:getter][:invoker].call(@swt_widget, [])
270
+ else
271
+ @swt_widget.send(widget_custom_attribute[:getter][:name])
272
+ end
269
273
  end
270
- elsif @swt_widget.respond_to?(attribute_getter(attribute_name))
271
- @swt_widget.send(attribute_getter(attribute_name))
272
- elsif @swt_widget.respond_to?(ruby_attribute_getter(attribute_name))
273
- @swt_widget.send(ruby_attribute_getter(attribute_name))
274
- elsif @swt_widget.respond_to?(attribute_name)
275
- @swt_widget.send(attribute_name)
276
- elsif respond_to?(ruby_attribute_getter(attribute_name))
277
- send(ruby_attribute_getter(attribute_name))
278
- else
279
- send(attribute_name)
280
274
  end
275
+ result = super unless swt_widget_operation
276
+ result
281
277
  end
282
278
 
283
279
  def pack_same_size
284
- bounds = @swt_widget.getBounds
285
- listener = on_control_resized {
286
- @swt_widget.setSize(bounds.width, bounds.height)
287
- @swt_widget.setLocation(bounds.x, bounds.y)
288
- }
289
- if @swt_widget.is_a?(Composite)
290
- @swt_widget.layout(true, true)
291
- else
292
- @swt_widget.pack(true)
280
+ auto_exec do
281
+ bounds = @swt_widget.getBounds
282
+ listener = on_control_resized {
283
+ @swt_widget.setSize(bounds.width, bounds.height)
284
+ @swt_widget.setLocation(bounds.x, bounds.y)
285
+ }
286
+ if @swt_widget.is_a?(Composite)
287
+ @swt_widget.layout(true, true)
288
+ else
289
+ @swt_widget.pack(true)
290
+ end
291
+ @swt_widget.removeControlListener(listener.swt_listener)
293
292
  end
294
- @swt_widget.removeControlListener(listener.swt_listener)
295
293
  end
296
294
 
295
+ # these work in tandem with the property_type_converters
296
+ # sometimes, they are specified in subclasses instead
297
297
  def widget_property_listener_installers
298
298
  @swt_widget_property_listener_installers ||= {
299
299
  Java::OrgEclipseSwtWidgets::Control => {
@@ -334,6 +334,17 @@ module Glimmer
334
334
  }
335
335
  end,
336
336
  },
337
+ Java::OrgEclipseSwtWidgets::Tree => {
338
+ :selection => lambda do |observer|
339
+ on_widget_selected { |selection_event|
340
+ if has_style?(:multi)
341
+ observer.call(@swt_widget.getSelection.map(&:get_data))
342
+ else
343
+ observer.call(@swt_widget.getSelection.first&.get_data)
344
+ end
345
+ }
346
+ end,
347
+ },
337
348
  Java::OrgEclipseSwtWidgets::Text => {
338
349
  :text => lambda do |observer|
339
350
  on_modify_text { |modify_event|
@@ -546,25 +557,37 @@ module Glimmer
546
557
  @flyweight_swt_widget_classes ||= {}
547
558
  end
548
559
 
560
+ # delegates to DisplayProxy
549
561
  def async_exec(&block)
550
562
  DisplayProxy.instance.async_exec(&block)
551
563
  end
552
564
 
565
+ # delegates to DisplayProxy
553
566
  def sync_exec(&block)
554
567
  DisplayProxy.instance.sync_exec(&block)
555
568
  end
556
569
 
570
+ # delegates to DisplayProxy
557
571
  def timer_exec(delay_in_millis, &block)
558
572
  DisplayProxy.instance.timer_exec(delay_in_millis, &block)
559
573
  end
560
574
 
575
+ # delegates to DisplayProxy
576
+ def auto_exec(override_sync_exec: nil, override_async_exec: nil, &block)
577
+ DisplayProxy.instance.auto_exec(override_sync_exec: override_sync_exec, override_async_exec: override_async_exec, &block)
578
+ end
579
+
561
580
  def has_style?(style)
562
581
  comparison = interpret_style(style)
563
- (@swt_widget.style & comparison) == comparison
582
+ auto_exec do
583
+ (@swt_widget.style & comparison) == comparison
584
+ end
564
585
  end
565
586
 
566
587
  def dispose
567
- @swt_widget.dispose
588
+ auto_exec do
589
+ @swt_widget.dispose
590
+ end
568
591
  end
569
592
 
570
593
  def disposed?
@@ -574,15 +597,19 @@ module Glimmer
574
597
  # TODO Consider renaming these methods as they are mainly used for data-binding
575
598
 
576
599
  def can_add_observer?(property_name)
577
- @swt_widget.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact.map(&:keys).flatten.map(&:to_s).include?(property_name.to_s)
600
+ auto_exec do
601
+ @swt_widget.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact.map(&:keys).flatten.map(&:to_s).include?(property_name.to_s)
602
+ end
578
603
  end
579
604
 
580
605
  # Used for data-binding only. Consider renaming or improving to avoid the confusion it causes
581
606
  def add_observer(observer, property_name)
582
607
  if !observer.respond_to?(:binding_options) || !observer.binding_options[:read_only]
583
- property_listener_installers = @swt_widget.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact
584
- widget_listener_installers = property_listener_installers.map{|installer| installer[property_name.to_s.to_sym]}.compact if !property_listener_installers.empty?
585
- widget_listener_installers.to_a.first&.call(observer)
608
+ auto_exec do
609
+ property_listener_installers = @swt_widget.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact
610
+ widget_listener_installers = property_listener_installers.map{|installer| installer[property_name.to_s.to_sym]}.compact if !property_listener_installers.empty?
611
+ widget_listener_installers.to_a.first&.call(observer)
612
+ end
586
613
  end
587
614
  end
588
615
 
@@ -597,7 +624,7 @@ module Glimmer
597
624
  end
598
625
 
599
626
  def ensure_drop_target_proxy(style=[])
600
- @drop_target_proxy ||= self.class.new('drop_target', self, style).tap do |proxy|
627
+ @drop_target_proxy ||= WidgetProxy.new('drop_target', self, style).tap do |proxy|
601
628
  proxy.set_attribute(:transfer, :text)
602
629
  proxy.on_drag_enter { |event|
603
630
  event.detail = DNDProxy[:drop_copy]
@@ -608,7 +635,7 @@ module Glimmer
608
635
  # TODO eliminate duplication in the following methods perhaps by relying on exceptions
609
636
 
610
637
  def can_handle_observation_request?(observation_request)
611
- observation_request = observation_request.to_s
638
+ observation_request = normalize_observation_request(observation_request)
612
639
  if observation_request.start_with?('on_swt_')
613
640
  constant_name = observation_request.sub(/^on_swt_/, '')
614
641
  SWTProxy.has_constant?(constant_name)
@@ -619,13 +646,15 @@ module Glimmer
619
646
  end
620
647
 
621
648
  def can_handle_drag_observation_request?(observation_request)
622
- return false unless swt_widget.is_a?(Control)
623
- potential_drag_source = @drag_source_proxy.nil?
624
- ensure_drag_source_proxy
625
- @drag_source_proxy.can_handle_observation_request?(observation_request).tap do |result|
626
- if potential_drag_source && !result
627
- @drag_source_proxy.swt_widget.dispose
628
- @drag_source_proxy = nil
649
+ auto_exec do
650
+ return false unless swt_widget.is_a?(Control)
651
+ potential_drag_source = @drag_source_proxy.nil?
652
+ ensure_drag_source_proxy
653
+ @drag_source_proxy.can_handle_observation_request?(observation_request).tap do |result|
654
+ if potential_drag_source && !result
655
+ @drag_source_proxy.swt_widget.dispose
656
+ @drag_source_proxy = nil
657
+ end
629
658
  end
630
659
  end
631
660
  rescue => e
@@ -634,19 +663,21 @@ module Glimmer
634
663
  end
635
664
 
636
665
  def can_handle_drop_observation_request?(observation_request)
637
- return false unless swt_widget.is_a?(Control)
638
- potential_drop_target = @drop_target_proxy.nil?
639
- ensure_drop_target_proxy
640
- @drop_target_proxy.can_handle_observation_request?(observation_request).tap do |result|
641
- if potential_drop_target && !result
642
- @drop_target_proxy.swt_widget.dispose
643
- @drop_target_proxy = nil
666
+ auto_exec do
667
+ return false unless swt_widget.is_a?(Control)
668
+ potential_drop_target = @drop_target_proxy.nil?
669
+ ensure_drop_target_proxy
670
+ @drop_target_proxy.can_handle_observation_request?(observation_request).tap do |result|
671
+ if potential_drop_target && !result
672
+ @drop_target_proxy.swt_widget.dispose
673
+ @drop_target_proxy = nil
674
+ end
644
675
  end
645
676
  end
646
677
  end
647
678
 
648
679
  def handle_observation_request(observation_request, &block)
649
- observation_request = observation_request.to_s
680
+ observation_request = normalize_observation_request(observation_request)
650
681
  if observation_request.start_with?('on_swt_')
651
682
  constant_name = observation_request.sub(/^on_swt_/, '')
652
683
  add_swt_event_listener(constant_name, &block)
@@ -664,30 +695,26 @@ module Glimmer
664
695
  end
665
696
 
666
697
  def content(&block)
667
- Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::SWT::WidgetExpression.new, &block)
698
+ auto_exec do
699
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::SWT::WidgetExpression.new, &block)
700
+ end
668
701
  end
669
702
 
670
703
  def method_missing(method, *args, &block)
704
+ # TODO push most of this logic down to Properties (and perhaps create Listeners module as well)
671
705
  if can_handle_observation_request?(method)
672
706
  handle_observation_request(method, &block)
673
- elsif has_attribute_setter?(method, *args)
674
- set_attribute(method, *args)
675
- elsif has_attribute_getter?(method, *args)
676
- get_attribute(method, *args)
677
707
  else
678
- swt_widget.send(method, *args, &block)
708
+ super
679
709
  end
680
- rescue => e
681
- Glimmer::Config.logger.debug { "Neither WidgetProxy nor #{swt_widget.class.name} can handle the method ##{method}" }
682
- Glimmer::Config.logger.debug { e.full_message }
683
- super
684
- # TODO consider get_attribute too
685
710
  end
686
711
 
687
712
  def respond_to?(method, *args, &block)
688
- super ||
689
- can_handle_observation_request?(method) ||
690
- swt_widget.respond_to?(method, *args, &block)
713
+ result = super
714
+ return true if result
715
+ auto_exec do
716
+ can_handle_observation_request?(method)
717
+ end
691
718
  end
692
719
 
693
720
  private
@@ -713,16 +740,26 @@ module Glimmer
713
740
  # add_listener knowing it will be called for sure afterwards
714
741
 
715
742
  def can_add_listener?(underscored_listener_name)
716
- !self.class.find_listener(@swt_widget.getClass, underscored_listener_name).empty?
743
+ auto_exec do
744
+ !self.class.find_listener(@swt_widget.getClass, underscored_listener_name).empty?
745
+ end
717
746
  end
718
747
 
719
748
  def add_listener(underscored_listener_name, &block)
720
- widget_add_listener_method, listener_class, listener_method = self.class.find_listener(@swt_widget.getClass, underscored_listener_name)
721
- widget_listener_proxy = nil
722
- safe_block = lambda { |*args| block.call(*args) unless @swt_widget.isDisposed }
723
- listener = listener_class.new(listener_method => safe_block)
724
- @swt_widget.send(widget_add_listener_method, listener)
725
- WidgetListenerProxy.new(swt_widget: @swt_widget, swt_listener: listener, widget_add_listener_method: widget_add_listener_method, swt_listener_class: listener_class, swt_listener_method: listener_method)
749
+ auto_exec do
750
+ widget_add_listener_method, listener_class, listener_method = self.class.find_listener(@swt_widget.getClass, underscored_listener_name)
751
+ widget_listener_proxy = nil
752
+ safe_block = lambda do |*args|
753
+ begin
754
+ block.call(*args) unless @swt_widget.isDisposed
755
+ rescue => e
756
+ Glimmer::Config.logger.error {e}
757
+ end
758
+ end
759
+ listener = listener_class.new(listener_method => safe_block)
760
+ @swt_widget.send(widget_add_listener_method, listener)
761
+ WidgetListenerProxy.new(swt_widget: @swt_widget, swt_listener: listener, widget_add_listener_method: widget_add_listener_method, swt_listener_class: listener_class, swt_listener_method: listener_method)
762
+ end
726
763
  end
727
764
 
728
765
  # Looks through SWT class add***Listener methods till it finds one for which
@@ -772,16 +809,22 @@ module Glimmer
772
809
  end
773
810
 
774
811
  def add_swt_event_listener(swt_constant, &block)
775
- event_type = SWTProxy[swt_constant]
776
- widget_listener_proxy = nil
777
- safe_block = lambda { |*args| block.call(*args) unless @swt_widget.isDisposed }
778
- @swt_widget.addListener(event_type, &safe_block)
779
- widget_listener_proxy = WidgetListenerProxy.new(swt_widget: @swt_widget, swt_listener: @swt_widget.getListeners(event_type).last, event_type: event_type, swt_constant: swt_constant)
812
+ auto_exec do
813
+ event_type = SWTProxy[swt_constant]
814
+ widget_listener_proxy = nil
815
+ safe_block = lambda { |*args| block.call(*args) unless @swt_widget.isDisposed }
816
+ @swt_widget.addListener(event_type, &safe_block)
817
+ widget_listener_proxy = WidgetListenerProxy.new(swt_widget: @swt_widget, swt_listener: @swt_widget.getListeners(event_type).last, event_type: event_type, swt_constant: swt_constant)
818
+ end
780
819
  end
781
820
 
782
821
  def widget_custom_attribute_mapping
783
822
  # TODO scope per widget class type just like other mappings
784
823
  @swt_widget_custom_attribute_mapping ||= {
824
+ 'window' => {
825
+ getter: {name: 'getShell'},
826
+ setter: {name: 'getShell', invoker: lambda { |widget, args| @swt_widget.getShell }}, # No Op
827
+ },
785
828
  'focus' => {
786
829
  getter: {name: 'isFocusControl'},
787
830
  setter: {name: 'setFocus', invoker: lambda { |widget, args| @swt_widget.setFocus if args.first }},
@@ -834,6 +877,14 @@ module Glimmer
834
877
  ensure_drop_target_proxy
835
878
  @drop_target_proxy.set_attribute(:drop_target_effect, args)
836
879
  end
880
+
881
+ def normalize_observation_request(observation_request)
882
+ observation_request = observation_request.to_s
883
+ if @swt_widget.is_a?(Shell) && observation_request.downcase.include?('window')
884
+ observation_request = observation_request.sub('window', 'shell').sub('Window', 'Shell')
885
+ end
886
+ observation_request
887
+ end
837
888
 
838
889
  def apply_property_type_converters(attribute_name, args)
839
890
  value = args
@@ -849,6 +900,8 @@ module Glimmer
849
900
  color_converter = lambda do |value|
850
901
  if value.is_a?(Symbol) || value.is_a?(String)
851
902
  ColorProxy.new(value).swt_color
903
+ elsif value.is_a?(RGB)
904
+ ColorProxy.new(value.red, value.green, value.blue).swt_color
852
905
  else
853
906
  value
854
907
  end
@@ -887,7 +940,7 @@ module Glimmer
887
940
  while last_image_number != image_number
888
941
  last_image_number = image_number
889
942
  sync_exec {
890
- redraw
943
+ redraw unless disposed?
891
944
  }
892
945
  delayTime = loader.data[image_number].delayTime.to_f / 100.0
893
946
  sleep(delayTime)
@@ -918,7 +971,7 @@ module Glimmer
918
971
  foreground: color_converter,
919
972
  link_foreground: color_converter,
920
973
  font: lambda do |value|
921
- if value.is_a?(Hash)
974
+ if value.is_a?(Hash) || value.is_a?(FontData)
922
975
  font_properties = value
923
976
  FontProxy.new(self, font_properties).swt_font
924
977
  else
@@ -936,6 +989,10 @@ module Glimmer
936
989
  items: lambda do |value|
937
990
  value.to_java :string
938
991
  end,
992
+ selection: lambda do |value|
993
+ value = value.to_f if swt_widget.is_a?(Spinner)
994
+ value
995
+ end,
939
996
  text: lambda do |value|
940
997
  value.to_s
941
998
  end,