glimmer 0.4.9 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/README.markdown +324 -59
  3. data/lib/glimmer.rb +54 -63
  4. data/lib/glimmer/data_binding/list_selection_binding.rb +52 -0
  5. data/lib/glimmer/{swt → data_binding}/model_binding.rb +33 -5
  6. data/lib/glimmer/{swt → data_binding}/observable.rb +6 -3
  7. data/lib/glimmer/{swt → data_binding}/observable_array.rb +3 -1
  8. data/lib/glimmer/{swt → data_binding}/observable_model.rb +3 -2
  9. data/lib/glimmer/data_binding/observable_widget.rb +17 -0
  10. data/lib/glimmer/{swt → data_binding}/observer.rb +3 -3
  11. data/lib/glimmer/{shine.rb → data_binding/shine.rb} +2 -2
  12. data/lib/glimmer/{swt → data_binding}/table_items_binding.rb +12 -11
  13. data/lib/glimmer/{swt → data_binding}/tree_items_binding.rb +15 -12
  14. data/lib/glimmer/{swt → data_binding}/widget_binding.rb +2 -1
  15. data/lib/glimmer/dsl.rb +26 -0
  16. data/lib/glimmer/dsl/async_exec_expression.rb +12 -0
  17. data/lib/glimmer/dsl/bind_expression.rb +44 -0
  18. data/lib/glimmer/dsl/color_expression.rb +28 -0
  19. data/lib/glimmer/dsl/column_properties_expression.rb +22 -0
  20. data/lib/glimmer/dsl/combo_selection_data_binding_expression.rb +40 -0
  21. data/lib/glimmer/dsl/custom_widget_expression.rb +35 -0
  22. data/lib/glimmer/dsl/data_binding_expression.rb +33 -0
  23. data/lib/glimmer/dsl/display_expression.rb +12 -0
  24. data/lib/glimmer/dsl/engine.rb +80 -0
  25. data/lib/glimmer/dsl/exec_expression.rb +23 -0
  26. data/lib/glimmer/dsl/expression.rb +44 -0
  27. data/lib/glimmer/dsl/expression_handler.rb +48 -0
  28. data/lib/glimmer/dsl/layout_data_expression.rb +24 -0
  29. data/lib/glimmer/dsl/layout_expression.rb +26 -0
  30. data/lib/glimmer/dsl/list_selection_data_binding_expression.rb +42 -0
  31. data/lib/glimmer/dsl/observe_expression.rb +27 -0
  32. data/lib/glimmer/dsl/parent_expression.rb +12 -0
  33. data/lib/glimmer/dsl/property_expression.rb +20 -0
  34. data/lib/glimmer/dsl/shell_expression.rb +15 -0
  35. data/lib/glimmer/dsl/static_expression.rb +38 -0
  36. data/lib/glimmer/dsl/swt_expression.rb +23 -0
  37. data/lib/glimmer/dsl/sync_exec_expression.rb +13 -0
  38. data/lib/glimmer/dsl/tab_item_expression.rb +31 -0
  39. data/lib/glimmer/dsl/table_items_data_binding_expression.rb +29 -0
  40. data/lib/glimmer/dsl/tree_items_data_binding_expression.rb +30 -0
  41. data/lib/glimmer/dsl/tree_properties_expression.rb +24 -0
  42. data/lib/glimmer/dsl/widget_expression.rb +26 -0
  43. data/lib/glimmer/dsl/widget_listener_expression.rb +27 -0
  44. data/lib/glimmer/error.rb +6 -0
  45. data/lib/glimmer/invalid_keyword_error.rb +6 -0
  46. data/lib/glimmer/launcher.rb +39 -23
  47. data/lib/glimmer/swt/color_proxy.rb +48 -0
  48. data/lib/glimmer/swt/display_proxy.rb +49 -0
  49. data/lib/glimmer/swt/font_proxy.rb +72 -0
  50. data/lib/glimmer/swt/layout_data_proxy.rb +73 -0
  51. data/lib/glimmer/swt/{g_layout.rb → layout_proxy.rb} +24 -25
  52. data/lib/glimmer/swt/packages.rb +13 -0
  53. data/lib/glimmer/swt/shell_proxy.rb +108 -0
  54. data/lib/glimmer/swt/{g_swt.rb → swt_proxy.rb} +12 -5
  55. data/lib/glimmer/swt/tab_item_proxy.rb +59 -0
  56. data/lib/glimmer/swt/widget_listener_proxy.rb +17 -0
  57. data/lib/glimmer/swt/widget_proxy.rb +366 -0
  58. data/lib/glimmer/{swt → ui}/custom_shell.rb +4 -4
  59. data/lib/glimmer/{swt → ui}/custom_widget.rb +67 -51
  60. data/lib/glimmer/{swt → ui}/video.rb +13 -13
  61. data/lib/glimmer/{swt → util}/proc_tracker.rb +1 -1
  62. data/vendor/swt/linux/swt.jar +0 -0
  63. data/vendor/swt/mac/swt.jar +0 -0
  64. data/vendor/swt/windows/swt.jar +0 -0
  65. metadata +66 -70
  66. data/lib/glimmer/command_handler.rb +0 -15
  67. data/lib/glimmer/command_handler_chain_factory.rb +0 -32
  68. data/lib/glimmer/command_handler_chain_link.rb +0 -25
  69. data/lib/glimmer/command_handlers.rb +0 -46
  70. data/lib/glimmer/ext/module.rb +0 -20
  71. data/lib/glimmer/parent.rb +0 -7
  72. data/lib/glimmer/swt/command_handlers/bind_command_handler.rb +0 -56
  73. data/lib/glimmer/swt/command_handlers/color_command_handler.rb +0 -30
  74. data/lib/glimmer/swt/command_handlers/combo_selection_data_binding_command_handler.rb +0 -44
  75. data/lib/glimmer/swt/command_handlers/custom_widget_command_handler.rb +0 -26
  76. data/lib/glimmer/swt/command_handlers/data_binding_command_handler.rb +0 -40
  77. data/lib/glimmer/swt/command_handlers/display_command_handler.rb +0 -20
  78. data/lib/glimmer/swt/command_handlers/layout_command_handler.rb +0 -27
  79. data/lib/glimmer/swt/command_handlers/layout_data_command_handler.rb +0 -27
  80. data/lib/glimmer/swt/command_handlers/list_selection_data_binding_command_handler.rb +0 -49
  81. data/lib/glimmer/swt/command_handlers/observe_command_handler.rb +0 -35
  82. data/lib/glimmer/swt/command_handlers/property_command_handler.rb +0 -24
  83. data/lib/glimmer/swt/command_handlers/shell_command_handler.rb +0 -20
  84. data/lib/glimmer/swt/command_handlers/tab_item_command_handler.rb +0 -34
  85. data/lib/glimmer/swt/command_handlers/table_column_properties_data_binding_command_handler.rb +0 -29
  86. data/lib/glimmer/swt/command_handlers/table_items_data_binding_command_handler.rb +0 -34
  87. data/lib/glimmer/swt/command_handlers/tree_items_data_binding_command_handler.rb +0 -33
  88. data/lib/glimmer/swt/command_handlers/tree_properties_data_binding_command_handler.rb +0 -29
  89. data/lib/glimmer/swt/command_handlers/widget_command_handler.rb +0 -27
  90. data/lib/glimmer/swt/command_handlers/widget_listener_command_handler.rb +0 -48
  91. data/lib/glimmer/swt/g_color.rb +0 -41
  92. data/lib/glimmer/swt/g_display.rb +0 -36
  93. data/lib/glimmer/swt/g_font.rb +0 -71
  94. data/lib/glimmer/swt/g_layout_data.rb +0 -56
  95. data/lib/glimmer/swt/g_runnable.rb +0 -15
  96. data/lib/glimmer/swt/g_shell.rb +0 -106
  97. data/lib/glimmer/swt/g_tab_item_composite.rb +0 -41
  98. data/lib/glimmer/swt/g_widget.rb +0 -349
  99. data/lib/glimmer/swt/g_widget_listener.rb +0 -12
  100. data/lib/glimmer/swt/list_selection_binding.rb +0 -47
  101. data/lib/glimmer/swt_packages.rb +0 -15
  102. data/lib/glimmer/xml/command_handlers/html_command_handler.rb +0 -50
  103. data/lib/glimmer/xml/command_handlers/xml_command_handler.rb +0 -23
  104. data/lib/glimmer/xml/command_handlers/xml_name_space_command_handler.rb +0 -36
  105. data/lib/glimmer/xml/command_handlers/xml_tag_command_handler.rb +0 -28
  106. data/lib/glimmer/xml/command_handlers/xml_text_command_handler.rb +0 -24
  107. data/lib/glimmer/xml/depth_first_search_iterator.rb +0 -20
  108. data/lib/glimmer/xml/name_space_visitor.rb +0 -21
  109. data/lib/glimmer/xml/node.rb +0 -84
  110. data/lib/glimmer/xml/node_visitor.rb +0 -13
  111. data/lib/glimmer/xml/xml_visitor.rb +0 -63
  112. data/lib/glimmer/xml_command_handlers.rb +0 -18
@@ -0,0 +1,13 @@
1
+ module Glimmer
2
+ module SWT
3
+ # This contains Java imports of SWT Java packages
4
+ module Packages
5
+ include_package 'org.eclipse.swt'
6
+ include_package 'org.eclipse.swt.widgets'
7
+ include_package 'org.eclipse.swt.layout'
8
+ include_package 'org.eclipse.swt.graphics'
9
+ include_package 'org.eclipse.swt.browser'
10
+ include_package 'org.eclipse.swt.custom'
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,108 @@
1
+ require 'glimmer/swt/swt_proxy'
2
+ require 'glimmer/swt/widget_proxy'
3
+ require 'glimmer/swt/display_proxy'
4
+ require 'glimmer/dsl/shell_expression'
5
+
6
+ module Glimmer
7
+ module SWT
8
+ # Proxy for org.eclipse.swt.widgets.Shell
9
+ #
10
+ # Follows the Proxy Design Pattern
11
+ class ShellProxy < WidgetProxy
12
+ include_package 'org.eclipse.swt.widgets'
13
+ include_package 'org.eclipse.swt.layout'
14
+
15
+ WIDTH_MIN = 130
16
+ HEIGHT_MIN = 0
17
+
18
+ attr_reader :opened_before
19
+ alias opened_before? opened_before
20
+
21
+ # Instantiates ShellProxy with same arguments expected by SWT Shell
22
+ def initialize(*args)
23
+ if args.first.nil? || !args.first.is_a?(Display) && !args.first.is_a?(Shell)
24
+ @display = DisplayProxy.instance.swt_display
25
+ args = [@display] + args
26
+ end
27
+ args = SWTProxy.constantify_args(args).compact
28
+ @swt_widget = Shell.new(*args)
29
+ @display ||= @swt_widget.getDisplay
30
+ @swt_widget.setLayout(FillLayout.new)
31
+ @swt_widget.setMinimumSize(WIDTH_MIN, HEIGHT_MIN)
32
+ end
33
+
34
+ # Centers shell within monitor it is in
35
+ def center
36
+ primary_monitor = @display.getPrimaryMonitor()
37
+ monitor_bounds = primary_monitor.getBounds()
38
+ shell_bounds = @swt_widget.getBounds()
39
+ location_x = monitor_bounds.x + (monitor_bounds.width - shell_bounds.width) / 2
40
+ location_y = monitor_bounds.y + (monitor_bounds.height - shell_bounds.height) / 2
41
+ @swt_widget.setLocation(location_x, location_y)
42
+ end
43
+
44
+ # Opens shell and starts SWT's UI thread event loop
45
+ def open
46
+ if @opened_before
47
+ @swt_widget.setVisible(true)
48
+ # notify_observers('visible')
49
+ else
50
+ @opened_before = true
51
+ @swt_widget.pack
52
+ center
53
+ @swt_widget.open
54
+ start_event_loop
55
+ @display.dispose # TODO consider if it's more performant to reuse instead of disposing
56
+ end
57
+ end
58
+ alias show open
59
+
60
+ def hide
61
+ @swt_widget.setVisible(false)
62
+ end
63
+
64
+ def close
65
+ @swt_widget.close
66
+ end
67
+
68
+ def visible?
69
+ @swt_widget.isDisposed ? false : @swt_widget.isVisible
70
+ end
71
+
72
+ # Setting to true opens/shows shell. Setting to false hides the shell.
73
+ def visible=(visibility)
74
+ visibility ? show : hide
75
+ end
76
+
77
+ def content(&block)
78
+ Glimmer::DSL::Engine.add_content(self, DSL::ShellExpression.new, &block)
79
+ end
80
+
81
+ # (happens as part of `#open`)
82
+ # Starts SWT Event Loop.
83
+ #
84
+ # You may learn more about the SWT Event Loop here:
85
+ # https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/widgets/Display.html
86
+ # This method is not needed except in rare circumstances where there is a need to start the SWT Event Loop before opening the shell.
87
+ def start_event_loop
88
+ until @swt_widget.isDisposed
89
+ @display.sleep unless @display.readAndDispatch
90
+ end
91
+ end
92
+
93
+ def add_observer(observer, property_name)
94
+ case property_name.to_s
95
+ when 'visible?'
96
+ visibility_notifier = proc do
97
+ observer.call(visible?)
98
+ end
99
+ on_event_show(&visibility_notifier)
100
+ on_event_hide(&visibility_notifier)
101
+ on_event_close(&visibility_notifier)
102
+ else
103
+ super
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -1,19 +1,26 @@
1
+ require 'glimmer/error'
2
+
1
3
  module Glimmer
2
- module SWT #TODO refactor so GSWT and SWT are one and the same
3
- class GSWT
4
+ module SWT # TODO Consider making this the class below to ease calling it
5
+ # Proxy for org.eclipse.swt.SWT
6
+ #
7
+ # Follows the Proxy Design Pattern
8
+ class SWTProxy
4
9
  class << self
5
- ERROR_INVALID_STYLE = " is an invalid SWT style! Please choose a style from org.eclipse.swt.SWT class constants."
6
10
  java_import 'org.eclipse.swt.SWT'
7
11
 
12
+ ERROR_INVALID_STYLE = " is an invalid SWT style! Please choose a style from org.eclipse.swt.SWT class constants."
13
+
8
14
  # Gets SWT constants as if calling SWT::CONSTANT where constant is
9
15
  # passed in as a lower case symbol
10
16
  def [](*symbols)
17
+ symbols = symbols.first if symbols.size == 1 && symbols.first.is_a?(Array)
11
18
  symbols.compact.reduce(0) do |output, symbol|
12
19
  constant_value = constant(symbol)
13
20
  if constant_value.is_a?(Integer)
14
21
  output | constant(symbol)
15
22
  else
16
- raise symbol.to_s + ERROR_INVALID_STYLE
23
+ raise Error, symbol.to_s + ERROR_INVALID_STYLE
17
24
  end
18
25
  end
19
26
  end
@@ -58,7 +65,7 @@ module Glimmer
58
65
  end
59
66
 
60
67
  EXTRA_STYLES = {
61
- NO_RESIZE: GSWT[:shell_trim] & (~GSWT[:resize]) & (~GSWT[:max])
68
+ NO_RESIZE: SWTProxy[:shell_trim] & (~SWTProxy[:resize]) & (~SWTProxy[:max])
62
69
  }
63
70
  end
64
71
  end
@@ -0,0 +1,59 @@
1
+ require 'glimmer/swt/widget_proxy'
2
+
3
+ module Glimmer
4
+ module SWT
5
+ # Proxy for org.eclipse.swt.widgets.TabItem
6
+ #
7
+ # Functions differently from other widget proxies.
8
+ #
9
+ # Glimmer instantiates an SWT Composite alongside the SWT TabItem
10
+ # and returns it for `#swt_widget` to allow adding widgets into it.
11
+ #
12
+ # In order to get the SWT TabItem object, one must call `#swt_tab_item`.
13
+ #
14
+ # Behinds the scenes, this creates a tab item widget proxy separately from a composite that
15
+ # is set as the control of the tab item and `#swt_widget`.
16
+ #
17
+ # In order to retrieve the tab item widget proxy, one must call `#widget_proxy`
18
+ #
19
+ # Follows the Proxy Design Pattern
20
+ class TabItemProxy < WidgetProxy
21
+ include_package 'org.eclipse.swt.widgets'
22
+
23
+ attr_reader :widget_proxy, :swt_tab_item
24
+
25
+ def initialize(parent, style, &contents)
26
+ super("composite", parent, style, &contents)
27
+ @widget_proxy = SWT::WidgetProxy.new('tab_item', parent, style)
28
+ @swt_tab_item = @widget_proxy.swt_widget
29
+ @widget_proxy.swt_widget.control = self.swt_widget
30
+ end
31
+
32
+ def has_attribute?(attribute_name, *args)
33
+ if attribute_name.to_s == "text"
34
+ true
35
+ else
36
+ super(attribute_name, *args)
37
+ end
38
+ end
39
+
40
+ def set_attribute(attribute_name, *args)
41
+ attribute_name
42
+ if attribute_name.to_s == "text"
43
+ text_value = args[0]
44
+ @swt_tab_item.setText text_value
45
+ else
46
+ super(attribute_name, *args)
47
+ end
48
+ end
49
+
50
+ def get_attribute(attribute_name)
51
+ if attribute_name.to_s == "text"
52
+ @swt_tab_item.getText
53
+ else
54
+ super(attribute_name)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,17 @@
1
+ module Glimmer
2
+ module SWT
3
+ # Proxy for widget listeners
4
+ #
5
+ # Follows the Proxy Design Pattern
6
+ class WidgetListenerProxy
7
+
8
+ attr_reader :swt_listener
9
+
10
+ # TODO capture its widget and support unregistering
11
+
12
+ def initialize(swt_listener)
13
+ @swt_listener = swt_listener
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,366 @@
1
+ require 'glimmer'
2
+ require 'glimmer/swt/widget_listener_proxy'
3
+ require 'glimmer/swt/color_proxy'
4
+ require 'glimmer/swt/font_proxy'
5
+ require 'glimmer/swt/swt_proxy'
6
+ require 'glimmer/data_binding/observable_widget'
7
+ require 'glimmer/dsl/widget_expression'
8
+
9
+ module Glimmer
10
+ module SWT
11
+ # Proxy for SWT Widget objects
12
+ #
13
+ # Sets default SWT styles to widgets upon inititalizing as
14
+ # per DEFAULT_STYLES
15
+ #
16
+ # Also, auto-initializes widgets as per initializer blocks
17
+ # in DEFAULT_INITIALIZERS (e.g. setting Composite default layout)
18
+ #
19
+ # Follows the Proxy Design Pattern
20
+ class WidgetProxy
21
+ include Packages
22
+ include DataBinding::ObservableWidget
23
+
24
+ DEFAULT_STYLES = {
25
+ "text" => [:border],
26
+ "table" => [:border],
27
+ "spinner" => [:border],
28
+ "list" => [:border, :v_scroll],
29
+ "button" => [:push],
30
+ }
31
+
32
+ DEFAULT_INITIALIZERS = {
33
+ "composite" => proc do |composite|
34
+ composite.setLayout(GridLayout.new)
35
+ end,
36
+ "table" => proc do |table|
37
+ table.setHeaderVisible(true)
38
+ table.setLinesVisible(true)
39
+ end,
40
+ "table_column" => proc do |table_column|
41
+ table_column.setWidth(80)
42
+ end,
43
+ "group" => proc do |group|
44
+ group.setLayout(GridLayout.new)
45
+ end,
46
+ }
47
+
48
+ attr_reader :swt_widget
49
+
50
+ # Initializes a new SWT Widget
51
+ #
52
+ # Styles is a comma separate list of symbols representing SWT styles in lower case
53
+ def initialize(underscored_widget_name, parent, styles, &contents)
54
+ swt_widget_class = self.class.swt_widget_class_for(underscored_widget_name)
55
+ @swt_widget = swt_widget_class.new(parent.swt_widget, style(underscored_widget_name, styles))
56
+ DEFAULT_INITIALIZERS[underscored_widget_name]&.call(@swt_widget)
57
+ end
58
+
59
+ def has_attribute?(attribute_name, *args)
60
+ widget_custom_attribute = widget_custom_attribute_mapping[attribute_name.to_s]
61
+ if widget_custom_attribute
62
+ @swt_widget.respond_to?(widget_custom_attribute[:setter][:name])
63
+ else
64
+ @swt_widget.respond_to?(attribute_setter(attribute_name), args)
65
+ end
66
+ end
67
+
68
+ def set_attribute(attribute_name, *args)
69
+ widget_custom_attribute = widget_custom_attribute_mapping[attribute_name.to_s]
70
+ if widget_custom_attribute
71
+ widget_custom_attribute[:setter][:invoker].call(@swt_widget, args)
72
+ else
73
+ apply_property_type_converters(attribute_name, args)
74
+ @swt_widget.send(attribute_setter(attribute_name), *args)
75
+ end
76
+ end
77
+
78
+ def get_attribute(attribute_name)
79
+ widget_custom_attribute = widget_custom_attribute_mapping[attribute_name.to_s]
80
+ if widget_custom_attribute
81
+ @swt_widget.send(widget_custom_attribute[:getter][:name])
82
+ else
83
+ @swt_widget.send(attribute_getter(attribute_name))
84
+ end
85
+ end
86
+
87
+ def widget_property_listener_installers
88
+ @swt_widget_property_listener_installers ||= {
89
+ Java::OrgEclipseSwtWidgets::Control => {
90
+ :focus => proc do |observer|
91
+ on_focus_gained { |focus_event|
92
+ observer.call(true)
93
+ }
94
+ on_focus_lost { |focus_event|
95
+ observer.call(false)
96
+ }
97
+ end,
98
+ },
99
+ Java::OrgEclipseSwtWidgets::Text => {
100
+ :text => proc do |observer|
101
+ on_modify_text { |modify_event|
102
+ observer.call(@swt_widget.getText)
103
+ }
104
+ end,
105
+ },
106
+ Java::OrgEclipseSwtWidgets::Button => {
107
+ :selection => proc do |observer|
108
+ on_widget_selected { |selection_event|
109
+ observer.call(@swt_widget.getSelection)
110
+ }
111
+ end
112
+ },
113
+ Java::OrgEclipseSwtWidgets::Spinner => {
114
+ :selection => proc do |observer|
115
+ on_widget_selected { |selection_event|
116
+ observer.call(@swt_widget.getSelection)
117
+ }
118
+ end
119
+ }
120
+ }
121
+ end
122
+
123
+ def self.widget_exists?(underscored_widget_name)
124
+ !!swt_widget_class_for(underscored_widget_name)
125
+ end
126
+
127
+ # This supports widgets in and out of basic SWT
128
+ def self.swt_widget_class_for(underscored_widget_name)
129
+ swt_widget_name = underscored_widget_name.camelcase(:upper)
130
+ swt_widget_class = eval(swt_widget_name)
131
+ unless swt_widget_class.ancestors.include?(org.eclipse.swt.widgets.Widget)
132
+ Glimmer.logger.debug("Class #{swt_widget_class} matching #{underscored_widget_name} is not a subclass of org.eclipse.swt.widgets.Widget")
133
+ return nil
134
+ end
135
+ swt_widget_class
136
+ rescue NameError => e
137
+ Glimmer.logger.debug e.message
138
+ # Glimmer.logger.debug("#{e.message}\n#{e.backtrace.join("\n")}")
139
+ nil
140
+ rescue => e
141
+ Glimmer.logger.debug e.message
142
+ # Glimmer.logger.debug("#{e.message}\n#{e.backtrace.join("\n")}")
143
+ nil
144
+ end
145
+
146
+ def async_exec(&block)
147
+ DisplayProxy.instance.async_exec(&block)
148
+ end
149
+
150
+ def sync_exec(&block)
151
+ DisplayProxy.instance.sync_exec(&block)
152
+ end
153
+
154
+ def has_style?(style)
155
+ (@swt_widget.style & SWTProxy[style]) == SWTProxy[style]
156
+ end
157
+
158
+ def dispose
159
+ @swt_widget.dispose
160
+ end
161
+
162
+ # TODO Consider renaming these methods as they are mainly used for data-binding
163
+
164
+ def can_add_observer?(property_name)
165
+ @swt_widget.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact.map(&:keys).flatten.map(&:to_s).include?(property_name.to_s)
166
+ end
167
+
168
+ # Used for data-binding only. Consider renaming or improving to avoid the confusion it causes
169
+ def add_observer(observer, property_name)
170
+ property_listener_installers = @swt_widget.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact
171
+ widget_listener_installers = property_listener_installers.map{|installer| installer[property_name.to_s.to_sym]}.compact if !property_listener_installers.empty?
172
+ widget_listener_installers.each do |widget_listener_installer|
173
+ widget_listener_installer.call(observer)
174
+ end
175
+ end
176
+
177
+ def remove_observer(observer, property_name)
178
+ # TODO consider implementing if remove_observer is needed (consumers can remove listener via SWT API)
179
+ end
180
+
181
+ # TODO eliminate duplication in the following methods perhaps by relying on exceptions
182
+
183
+ def can_handle_observation_request?(observation_request)
184
+ observation_request = observation_request.to_s
185
+ if observation_request.start_with?('on_event_')
186
+ constant_name = observation_request.sub(/^on_event_/, '')
187
+ SWTProxy.has_constant?(constant_name)
188
+ elsif observation_request.start_with?('on_')
189
+ event = observation_request.sub(/^on_/, '')
190
+ can_add_listener?(event)
191
+ else
192
+ false
193
+ end
194
+ end
195
+
196
+ def handle_observation_request(observation_request, &block)
197
+ if observation_request.start_with?('on_event_')
198
+ constant_name = observation_request.sub(/^on_event_/, '')
199
+ add_swt_event_listener(constant_name, &block)
200
+ elsif observation_request.start_with?('on_')
201
+ event = observation_request.sub(/^on_/, '')
202
+ add_listener(event, &block)
203
+ end
204
+ end
205
+
206
+ def content(&block)
207
+ Glimmer::DSL::Engine.add_content(self, DSL::WidgetExpression.new, &block)
208
+ end
209
+
210
+ private
211
+
212
+ def style(underscored_widget_name, styles)
213
+ styles = [styles].flatten.compact
214
+ styles.empty? ? default_style(underscored_widget_name) : SWTProxy[*styles]
215
+ end
216
+
217
+ def default_style(underscored_widget_name)
218
+ styles = DEFAULT_STYLES[underscored_widget_name] || [:none]
219
+ SWTProxy[styles]
220
+ end
221
+
222
+ def attribute_setter(attribute_name)
223
+ "set#{attribute_name.to_s.camelcase(:upper)}"
224
+ end
225
+
226
+ def attribute_getter(attribute_name)
227
+ "get#{attribute_name.to_s.camelcase(:upper)}"
228
+ end
229
+
230
+ # TODO refactor following methods to eliminate duplication
231
+ # perhaps consider relying on raising an exception to avoid checking first
232
+ # unless that gives obscure SWT errors
233
+ # Otherwise, consider caching results from can_add_lsitener and using them in
234
+ # add_listener knowing it will be called for sure afterwards
235
+
236
+ def can_add_listener?(underscored_listener_name)
237
+ !self.class.find_listener(@swt_widget.getClass, underscored_listener_name).empty?
238
+ end
239
+
240
+ def add_listener(underscored_listener_name, &block)
241
+ widget_add_listener_method, listener_class, listener_method = self.class.find_listener(@swt_widget.getClass, underscored_listener_name)
242
+ listener = listener_class.new(listener_method.getName => block)
243
+ @swt_widget.send(widget_add_listener_method.getName, listener)
244
+ WidgetListenerProxy.new(listener)
245
+ end
246
+
247
+ # Looks through SWT class add***Listener methods till it finds one for which
248
+ # the argument is a listener class that has an event method matching
249
+ # underscored_listener_name
250
+ def self.find_listener(swt_widget_class, underscored_listener_name)
251
+ @listeners ||= {}
252
+ listener_key = [swt_widget_class.name, underscored_listener_name]
253
+ unless @listeners.has_key?(listener_key)
254
+ listener_method_name = underscored_listener_name.camelcase(:lower)
255
+ swt_widget_class.getMethods.each do |widget_add_listener_method|
256
+ if widget_add_listener_method.getName.match(/add.*Listener/)
257
+ widget_add_listener_method.getParameterTypes.each do |listener_type|
258
+ listener_type.getMethods.each do |listener_method|
259
+ if (listener_method.getName == listener_method_name)
260
+ @listeners[listener_key] = [widget_add_listener_method, listener_class(listener_type), listener_method]
261
+ return @listeners[listener_key]
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
267
+ @listeners[listener_key] = []
268
+ end
269
+ @listeners[listener_key]
270
+ end
271
+
272
+ # Returns a Ruby class that implements listener type Java interface with ability to easily
273
+ # install a block that gets called upon calling a listener event method
274
+ def self.listener_class(listener_type)
275
+ @listener_classes ||= {}
276
+ listener_class_key = listener_type.name
277
+ unless @listener_classes.has_key?(listener_class_key)
278
+ @listener_classes[listener_class_key] = Class.new(Object).tap do |listener_class|
279
+ listener_class.send :include, (eval listener_type.name.sub("interface", ""))
280
+ listener_class.define_method('initialize') do |event_method_block_mapping|
281
+ @event_method_block_mapping = event_method_block_mapping
282
+ end
283
+ listener_type.getMethods.each do |event_method|
284
+ listener_class.define_method(event_method.getName) do |event|
285
+ @event_method_block_mapping[event_method.getName]&.call(event)
286
+ end
287
+ end
288
+ end
289
+ end
290
+ @listener_classes[listener_class_key]
291
+ end
292
+
293
+ def add_swt_event_listener(swt_constant, &block)
294
+ event_type = SWTProxy[swt_constant]
295
+ @swt_widget.addListener(event_type, &block)
296
+ WidgetListenerProxy.new(@swt_widget.getListeners(event_type).last)
297
+ end
298
+
299
+ def widget_custom_attribute_mapping
300
+ @swt_widget_custom_attribute_mapping ||= {
301
+ 'focus' => {
302
+ getter: {name: 'isFocusControl'},
303
+ setter: {name: 'setFocus', invoker: lambda { |widget, args| @swt_widget.setFocus if args.first }},
304
+ }
305
+ }
306
+ end
307
+
308
+ def apply_property_type_converters(attribute_name, args)
309
+ if args.count == 1
310
+ value = args.first
311
+ converter = property_type_converters[attribute_name.to_sym]
312
+ args[0] = converter.call(value) if converter
313
+ end
314
+ if args.count == 1 && args.first.is_a?(ColorProxy)
315
+ g_color = args.first
316
+ args[0] = g_color.swt_color
317
+ end
318
+ end
319
+
320
+ def property_type_converters
321
+ color_converter = proc do |value|
322
+ if value.is_a?(Symbol) || value.is_a?(String)
323
+ ColorProxy.new(value).swt_color
324
+ else
325
+ value
326
+ end
327
+ end
328
+ @property_type_converters ||= {
329
+ :background => color_converter,
330
+ :background_image => proc do |value|
331
+ if value.is_a?(String)
332
+ image_data = ImageData.new(value)
333
+ # TODO in the future, look into unregistering this listener when no longer needed
334
+ on_event_Resize do |resize_event|
335
+ new_image_data = image_data.scaledTo(@swt_widget.getSize.x, @swt_widget.getSize.y)
336
+ @swt_widget.getBackgroundImage&.dispose
337
+ @swt_widget.setBackgroundImage(Image.new(@swt_widget.getDisplay, new_image_data))
338
+ end
339
+ Image.new(@swt_widget.getDisplay, image_data)
340
+ else
341
+ value
342
+ end
343
+ end,
344
+ :foreground => color_converter,
345
+ :font => proc do |value|
346
+ if value.is_a?(Hash)
347
+ font_properties = value
348
+ FontProxy.new(self, font_properties).swt_font
349
+ else
350
+ value
351
+ end
352
+ end,
353
+ :items => proc do |value|
354
+ value.to_java :string
355
+ end,
356
+ :text => proc do |value|
357
+ value.to_s
358
+ end,
359
+ :visible => proc do |value|
360
+ !!value
361
+ end,
362
+ }
363
+ end
364
+ end
365
+ end
366
+ end