glimmer 0.5.8 → 0.5.9

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.
@@ -64,7 +64,13 @@ module Glimmer
64
64
  jruby_options_string = jruby_options.join(' ') + ' ' if jruby_options.any?
65
65
  env_vars_string = env_vars.map {|k,v| "#{k}=#{v}"}.join(' ')
66
66
  env_vars_string = [env_vars_string, glimmer_option_env_vars(glimmer_options)].join(' ')
67
- system "#{env_vars_string} jruby #{jruby_options_string}#{jruby_os_specific_options} -r #{glimmer_lib} -S #{application}"
67
+ the_glimmer_lib = glimmer_lib
68
+ devmode_require = nil
69
+ if the_glimmer_lib == GLIMMER_LIB_LOCAL
70
+ devmode_require = '-r puts_debuggerer '
71
+ end
72
+ puts "#{env_vars_string} jruby #{jruby_options_string}#{jruby_os_specific_options} -r #{the_glimmer_lib} #{devmode_require}-S \"#{application}\"" if jruby_options_string.to_s.include?('--debug')
73
+ system "#{env_vars_string} jruby #{jruby_options_string}#{jruby_os_specific_options} -r #{the_glimmer_lib} #{devmode_require}-S \"#{application}\""
68
74
  end
69
75
  end
70
76
 
@@ -87,7 +93,7 @@ module Glimmer
87
93
 
88
94
  def launch_application
89
95
  threads = @application_paths.map do |application_path|
90
- puts "Launching Glimmer Application: #{application_path}" unless application_path.to_s.include?('irb')
96
+ puts "Launching Glimmer Application: #{application_path}" unless application_path.to_s.match(/(irb)|(gladiator)/)
91
97
  Thread.new do
92
98
  self.class.launch(
93
99
  application_path,
@@ -1,3 +1,5 @@
1
+ require 'glimmer/swt/widget_listener_proxy'
2
+
1
3
  module Glimmer
2
4
  module SWT
3
5
  # Proxy for org.eclipse.swt.widgets.Display
@@ -44,6 +46,29 @@ module Glimmer
44
46
  def sync_exec(&block)
45
47
  @swt_display.syncExec(&block)
46
48
  end
49
+
50
+ def can_handle_observation_request?(observation_request)
51
+ observation_request = observation_request.to_s
52
+ if observation_request.start_with?('on_event_')
53
+ constant_name = observation_request.sub(/^on_event_/, '')
54
+ SWTProxy.has_constant?(constant_name)
55
+ else
56
+ false
57
+ end
58
+ end
59
+
60
+ def handle_observation_request(observation_request, &block)
61
+ if observation_request.start_with?('on_event_')
62
+ constant_name = observation_request.sub(/^on_event_/, '')
63
+ add_swt_event_listener(constant_name, &block)
64
+ end
65
+ end
66
+
67
+ def add_swt_event_listener(swt_constant, &block)
68
+ event_type = SWTProxy[swt_constant]
69
+ @swt_display.addFilter(event_type, &block)
70
+ #WidgetListenerProxy.new(@swt_display.getListeners(event_type).last)
71
+ end
47
72
  end
48
73
  end
49
74
  end
@@ -57,7 +57,6 @@ module Glimmer
57
57
  if args.first != @swt_layout.send(attribute_getter(attribute_name))
58
58
  @swt_layout.send(attribute_setter(attribute_name), *args)
59
59
  @widget_proxy.swt_widget.getShell.pack
60
- # TODO see if pack(true) is needed
61
60
  end
62
61
  end
63
62
 
@@ -84,6 +84,23 @@ module Glimmer
84
84
  visibility ? show : hide
85
85
  end
86
86
 
87
+ def pack
88
+ @swt_widget.pack
89
+ end
90
+
91
+ def pack_same_size
92
+ minimum_size = @swt_widget.getMinimumSize
93
+ bounds = @swt_widget.getBounds
94
+ @swt_widget.setMinimumSize(bounds.width, bounds.height)
95
+ listener = on_control_resized {
96
+ @swt_widget.setSize(bounds.width, bounds.height)
97
+ @swt_widget.setLocation(bounds.x, bounds.y)
98
+ }
99
+ @swt_widget.pack
100
+ @swt_widget.removeControlListener(listener.swt_listener)
101
+ @swt_widget.setMinimumSize(minimum_size)
102
+ end
103
+
87
104
  def content(&block)
88
105
  Glimmer::DSL::Engine.add_content(self, DSL::ShellExpression.new, &block)
89
106
  end
@@ -62,6 +62,10 @@ module Glimmer
62
62
  is_found ? found += [c] : found
63
63
  end
64
64
  end
65
+
66
+ def include?(swt_constant, *symbols)
67
+ swt_constant & self[symbols] == self[symbols]
68
+ end
65
69
  end
66
70
 
67
71
  EXTRA_STYLES = {
@@ -1,391 +1,444 @@
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
- "menu_item" => [:push],
31
- }
32
-
33
- DEFAULT_INITIALIZERS = {
34
- "composite" => proc do |composite|
35
- composite.setLayout(GridLayout.new)
36
- end,
37
- "table" => proc do |table|
38
- table.setHeaderVisible(true)
39
- table.setLinesVisible(true)
40
- end,
41
- "table_column" => proc do |table_column|
42
- table_column.setWidth(80)
43
- end,
44
- "group" => proc do |group|
45
- group.setLayout(GridLayout.new)
46
- end,
47
- }
48
-
49
- attr_reader :swt_widget
50
-
51
- # Initializes a new SWT Widget
52
- #
53
- # Styles is a comma separate list of symbols representing SWT styles in lower case
54
- def initialize(underscored_widget_name, parent, args)
55
- styles, extra_options = extract_args(underscored_widget_name, args)
56
- swt_widget_class = self.class.swt_widget_class_for(underscored_widget_name)
57
- @swt_widget = swt_widget_class.new(parent.swt_widget, style(underscored_widget_name, styles), *extra_options)
58
- DEFAULT_INITIALIZERS[underscored_widget_name]&.call(@swt_widget)
59
- end
60
-
61
- def extract_args(underscored_widget_name, args)
62
- @arg_extractor_mapping ||= {
63
- 'menu_item' => lambda do |args|
64
- index = args.delete(args.last) if args.last.is_a?(Numeric)
65
- extra_options = [index].compact
66
- styles = args
67
- [styles, extra_options]
68
- end,
69
- }
70
- if @arg_extractor_mapping[underscored_widget_name]
71
- @arg_extractor_mapping[underscored_widget_name].call(args)
72
- else
73
- [args, []]
74
- end
75
- end
76
-
77
- def has_attribute?(attribute_name, *args)
78
- widget_custom_attribute = widget_custom_attribute_mapping[attribute_name.to_s]
79
- if widget_custom_attribute
80
- @swt_widget.respond_to?(widget_custom_attribute[:setter][:name])
81
- else
82
- @swt_widget.respond_to?(attribute_setter(attribute_name), args)
83
- end
84
- end
85
-
86
- def set_attribute(attribute_name, *args)
87
- widget_custom_attribute = widget_custom_attribute_mapping[attribute_name.to_s]
88
- if widget_custom_attribute
89
- widget_custom_attribute[:setter][:invoker].call(@swt_widget, args)
90
- else
91
- apply_property_type_converters(attribute_name, args)
92
- @swt_widget.send(attribute_setter(attribute_name), *args) unless @swt_widget.send(attribute_getter(attribute_name)) == args.first
93
- end
94
- end
95
-
96
- def get_attribute(attribute_name)
97
- widget_custom_attribute = widget_custom_attribute_mapping[attribute_name.to_s]
98
- if widget_custom_attribute
99
- @swt_widget.send(widget_custom_attribute[:getter][:name])
100
- else
101
- @swt_widget.send(attribute_getter(attribute_name))
102
- end
103
- end
104
-
105
- def widget_property_listener_installers
106
- @swt_widget_property_listener_installers ||= {
107
- Java::OrgEclipseSwtWidgets::Control => {
108
- :focus => proc do |observer|
109
- on_focus_gained { |focus_event|
110
- observer.call(true)
111
- }
112
- on_focus_lost { |focus_event|
113
- observer.call(false)
114
- }
115
- end,
116
- },
117
- Java::OrgEclipseSwtWidgets::Text => {
118
- :text => proc do |observer|
119
- on_modify_text { |modify_event|
120
- observer.call(@swt_widget.getText)
121
- }
122
- end,
123
- },
124
- Java::OrgEclipseSwtWidgets::Button => {
125
- :selection => proc do |observer|
126
- on_widget_selected { |selection_event|
127
- observer.call(@swt_widget.getSelection)
128
- }
129
- end
130
- },
131
- Java::OrgEclipseSwtWidgets::Spinner => {
132
- :selection => proc do |observer|
133
- on_widget_selected { |selection_event|
134
- observer.call(@swt_widget.getSelection)
135
- }
136
- end
137
- }
138
- }
139
- end
140
-
141
- def self.widget_exists?(underscored_widget_name)
142
- !!swt_widget_class_for(underscored_widget_name)
143
- end
144
-
145
- # This supports widgets in and out of basic SWT
146
- def self.swt_widget_class_for(underscored_widget_name)
147
- swt_widget_name = underscored_widget_name.camelcase(:upper)
148
- swt_widget_class = eval(swt_widget_name)
149
- unless swt_widget_class.ancestors.include?(org.eclipse.swt.widgets.Widget)
150
- Glimmer.logger&.debug("Class #{swt_widget_class} matching #{underscored_widget_name} is not a subclass of org.eclipse.swt.widgets.Widget")
151
- return nil
152
- end
153
- swt_widget_class
154
- rescue NameError => e
155
- Glimmer.logger&.debug e.message
156
- # Glimmer.logger&.debug("#{e.message}\n#{e.backtrace.join("\n")}")
157
- nil
158
- rescue => e
159
- Glimmer.logger&.debug e.message
160
- # Glimmer.logger&.debug("#{e.message}\n#{e.backtrace.join("\n")}")
161
- nil
162
- end
163
-
164
- def async_exec(&block)
165
- DisplayProxy.instance.async_exec(&block)
166
- end
167
-
168
- def sync_exec(&block)
169
- DisplayProxy.instance.sync_exec(&block)
170
- end
171
-
172
- def has_style?(style)
173
- (@swt_widget.style & SWTProxy[style]) == SWTProxy[style]
174
- end
175
-
176
- def dispose
177
- @swt_widget.dispose
178
- end
179
-
180
- # TODO Consider renaming these methods as they are mainly used for data-binding
181
-
182
- def can_add_observer?(property_name)
183
- @swt_widget.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact.map(&:keys).flatten.map(&:to_s).include?(property_name.to_s)
184
- end
185
-
186
- # Used for data-binding only. Consider renaming or improving to avoid the confusion it causes
187
- def add_observer(observer, property_name)
188
- property_listener_installers = @swt_widget.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact
189
- widget_listener_installers = property_listener_installers.map{|installer| installer[property_name.to_s.to_sym]}.compact if !property_listener_installers.empty?
190
- widget_listener_installers.each do |widget_listener_installer|
191
- widget_listener_installer.call(observer)
192
- end
193
- end
194
-
195
- def remove_observer(observer, property_name)
196
- # TODO consider implementing if remove_observer is needed (consumers can remove listener via SWT API)
197
- end
198
-
199
- # TODO eliminate duplication in the following methods perhaps by relying on exceptions
200
-
201
- def can_handle_observation_request?(observation_request)
202
- observation_request = observation_request.to_s
203
- if observation_request.start_with?('on_event_')
204
- constant_name = observation_request.sub(/^on_event_/, '')
205
- SWTProxy.has_constant?(constant_name)
206
- elsif observation_request.start_with?('on_')
207
- event = observation_request.sub(/^on_/, '')
208
- can_add_listener?(event)
209
- else
210
- false
211
- end
212
- end
213
-
214
- def handle_observation_request(observation_request, &block)
215
- if observation_request.start_with?('on_event_')
216
- constant_name = observation_request.sub(/^on_event_/, '')
217
- add_swt_event_listener(constant_name, &block)
218
- elsif observation_request.start_with?('on_')
219
- event = observation_request.sub(/^on_/, '')
220
- add_listener(event, &block)
221
- end
222
- end
223
-
224
- def content(&block)
225
- Glimmer::DSL::Engine.add_content(self, DSL::WidgetExpression.new, &block)
226
- end
227
-
228
- private
229
-
230
- def style(underscored_widget_name, styles)
231
- styles = [styles].flatten.compact
232
- styles.empty? ? default_style(underscored_widget_name) : SWTProxy[*styles]
233
- end
234
-
235
- def default_style(underscored_widget_name)
236
- styles = DEFAULT_STYLES[underscored_widget_name] || [:none]
237
- SWTProxy[styles]
238
- end
239
-
240
- def attribute_setter(attribute_name)
241
- "set#{attribute_name.to_s.camelcase(:upper)}"
242
- end
243
-
244
- def attribute_getter(attribute_name)
245
- "get#{attribute_name.to_s.camelcase(:upper)}"
246
- end
247
-
248
- # TODO refactor following methods to eliminate duplication
249
- # perhaps consider relying on raising an exception to avoid checking first
250
- # unless that gives obscure SWT errors
251
- # Otherwise, consider caching results from can_add_lsitener and using them in
252
- # add_listener knowing it will be called for sure afterwards
253
-
254
- def can_add_listener?(underscored_listener_name)
255
- !self.class.find_listener(@swt_widget.getClass, underscored_listener_name).empty?
256
- end
257
-
258
- def add_listener(underscored_listener_name, &block)
259
- widget_add_listener_method, listener_class, listener_method = self.class.find_listener(@swt_widget.getClass, underscored_listener_name)
260
- listener = listener_class.new(listener_method => block)
261
- @swt_widget.send(widget_add_listener_method, listener)
262
- WidgetListenerProxy.new(listener)
263
- end
264
-
265
- # Looks through SWT class add***Listener methods till it finds one for which
266
- # the argument is a listener class that has an event method matching
267
- # underscored_listener_name
268
- def self.find_listener(swt_widget_class, underscored_listener_name)
269
- @listeners ||= {}
270
- listener_key = [swt_widget_class.name, underscored_listener_name]
271
- unless @listeners.has_key?(listener_key)
272
- listener_method_name = underscored_listener_name.camelcase(:lower)
273
- swt_widget_class.getMethods.each do |widget_add_listener_method|
274
- if widget_add_listener_method.getName.match(/add.*Listener/)
275
- widget_add_listener_method.getParameterTypes.each do |listener_type|
276
- listener_type.getMethods.each do |listener_method|
277
- if (listener_method.getName == listener_method_name)
278
- @listeners[listener_key] = [widget_add_listener_method.getName, listener_class(listener_type), listener_method.getName]
279
- return @listeners[listener_key]
280
- end
281
- end
282
- end
283
- end
284
- end
285
- @listeners[listener_key] = []
286
- end
287
- @listeners[listener_key]
288
- end
289
-
290
- # Returns a Ruby class that implements listener type Java interface with ability to easily
291
- # install a block that gets called upon calling a listener event method
292
- def self.listener_class(listener_type)
293
- @listener_classes ||= {}
294
- listener_class_key = listener_type.name
295
- unless @listener_classes.has_key?(listener_class_key)
296
- @listener_classes[listener_class_key] = Class.new(Object).tap do |listener_class|
297
- listener_class.send :include, (eval listener_type.name.sub("interface", ""))
298
- listener_class.define_method('initialize') do |event_method_block_mapping|
299
- @event_method_block_mapping = event_method_block_mapping
300
- end
301
- listener_type.getMethods.each do |event_method|
302
- listener_class.define_method(event_method.getName) do |event|
303
- @event_method_block_mapping[event_method.getName]&.call(event)
304
- end
305
- end
306
- end
307
- end
308
- @listener_classes[listener_class_key]
309
- end
310
-
311
- def add_swt_event_listener(swt_constant, &block)
312
- event_type = SWTProxy[swt_constant]
313
- @swt_widget.addListener(event_type, &block)
314
- WidgetListenerProxy.new(@swt_widget.getListeners(event_type).last)
315
- end
316
-
317
- def widget_custom_attribute_mapping
318
- @swt_widget_custom_attribute_mapping ||= {
319
- 'focus' => {
320
- getter: {name: 'isFocusControl'},
321
- setter: {name: 'setFocus', invoker: lambda { |widget, args| @swt_widget.setFocus if args.first }},
322
- }
323
- }
324
- end
325
-
326
- def apply_property_type_converters(attribute_name, args)
327
- if args.count == 1
328
- value = args.first
329
- converter = property_type_converters[attribute_name.to_sym]
330
- args[0] = converter.call(value) if converter
331
- end
332
- if args.count == 1 && args.first.is_a?(ColorProxy)
333
- g_color = args.first
334
- args[0] = g_color.swt_color
335
- end
336
- end
337
-
338
- def property_type_converters
339
- color_converter = proc do |value|
340
- if value.is_a?(Symbol) || value.is_a?(String)
341
- ColorProxy.new(value).swt_color
342
- else
343
- value
344
- end
345
- end
346
- # TODO consider detecting type on widget method and automatically invoking right converter (e.g. :to_s for String, :to_i for Integer)
347
- @property_type_converters ||= {
348
- :background => color_converter,
349
- :background_image => proc do |value|
350
- if value.is_a?(String)
351
- if value.start_with?('uri:classloader')
352
- value = value.sub(/^uri\:classloader\:\//, '')
353
- object = java.lang.Object.new
354
- value = object.java_class.resource_as_stream(value)
355
- value = java.io.BufferedInputStream.new(value)
356
- end
357
- image_data = ImageData.new(value)
358
- # TODO in the future, look into unregistering this listener when no longer needed
359
- on_event_Resize do |resize_event|
360
- new_image_data = image_data.scaledTo(@swt_widget.getSize.x, @swt_widget.getSize.y)
361
- @swt_widget.getBackgroundImage&.dispose
362
- @swt_widget.setBackgroundImage(Image.new(@swt_widget.getDisplay, new_image_data))
363
- end
364
- Image.new(@swt_widget.getDisplay, image_data)
365
- else
366
- value
367
- end
368
- end,
369
- :foreground => color_converter,
370
- :font => proc do |value|
371
- if value.is_a?(Hash)
372
- font_properties = value
373
- FontProxy.new(self, font_properties).swt_font
374
- else
375
- value
376
- end
377
- end,
378
- :items => proc do |value|
379
- value.to_java :string
380
- end,
381
- :text => proc do |value|
382
- value.to_s
383
- end,
384
- :visible => proc do |value|
385
- !!value
386
- end,
387
- }
388
- end
389
- end
390
- end
391
- end
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
+ "styled_text" => [:border],
29
+ "list" => [:border, :v_scroll],
30
+ "button" => [:push],
31
+ "menu_item" => [:push],
32
+ }
33
+
34
+ DEFAULT_INITIALIZERS = {
35
+ "composite" => proc do |composite|
36
+ composite.setLayout(GridLayout.new)
37
+ end,
38
+ "table" => proc do |table|
39
+ table.setHeaderVisible(true)
40
+ table.setLinesVisible(true)
41
+ end,
42
+ "table_column" => proc do |table_column|
43
+ table_column.setWidth(80)
44
+ end,
45
+ "group" => proc do |group|
46
+ group.setLayout(GridLayout.new)
47
+ end,
48
+ }
49
+
50
+ attr_reader :swt_widget
51
+
52
+ # Initializes a new SWT Widget
53
+ #
54
+ # Styles is a comma separate list of symbols representing SWT styles in lower case
55
+ def initialize(underscored_widget_name, parent, args)
56
+ styles, extra_options = extract_args(underscored_widget_name, args)
57
+ swt_widget_class = self.class.swt_widget_class_for(underscored_widget_name)
58
+ @swt_widget = swt_widget_class.new(parent.swt_widget, style(underscored_widget_name, styles), *extra_options)
59
+ DEFAULT_INITIALIZERS[underscored_widget_name]&.call(@swt_widget)
60
+ end
61
+
62
+ def extract_args(underscored_widget_name, args)
63
+ @arg_extractor_mapping ||= {
64
+ 'menu_item' => lambda do |args|
65
+ index = args.delete(args.last) if args.last.is_a?(Numeric)
66
+ extra_options = [index].compact
67
+ styles = args
68
+ [styles, extra_options]
69
+ end,
70
+ }
71
+ if @arg_extractor_mapping[underscored_widget_name]
72
+ @arg_extractor_mapping[underscored_widget_name].call(args)
73
+ else
74
+ [args, []]
75
+ end
76
+ end
77
+
78
+ def has_attribute?(attribute_name, *args)
79
+ widget_custom_attribute = widget_custom_attribute_mapping[attribute_name.to_s]
80
+ if widget_custom_attribute
81
+ @swt_widget.respond_to?(widget_custom_attribute[:setter][:name])
82
+ else
83
+ @swt_widget.respond_to?(attribute_setter(attribute_name), args)
84
+ end
85
+ end
86
+
87
+ def set_attribute(attribute_name, *args)
88
+ widget_custom_attribute = widget_custom_attribute_mapping[attribute_name.to_s]
89
+ if widget_custom_attribute
90
+ widget_custom_attribute[:setter][:invoker].call(@swt_widget, args)
91
+ else
92
+ apply_property_type_converters(attribute_name, args)
93
+ @swt_widget.send(attribute_setter(attribute_name), *args) unless @swt_widget.send(attribute_getter(attribute_name)) == args.first
94
+ end
95
+ end
96
+
97
+ def get_attribute(attribute_name)
98
+ widget_custom_attribute = widget_custom_attribute_mapping[attribute_name.to_s]
99
+ if widget_custom_attribute
100
+ @swt_widget.send(widget_custom_attribute[:getter][:name])
101
+ else
102
+ @swt_widget.send(attribute_getter(attribute_name))
103
+ end
104
+ end
105
+
106
+ def widget_property_listener_installers
107
+ @swt_widget_property_listener_installers ||= {
108
+ Java::OrgEclipseSwtWidgets::Control => {
109
+ :focus => proc do |observer|
110
+ on_focus_gained { |focus_event|
111
+ observer.call(true)
112
+ }
113
+ on_focus_lost { |focus_event|
114
+ observer.call(false)
115
+ }
116
+ end,
117
+ },
118
+ Java::OrgEclipseSwtWidgets::Text => {
119
+ :text => proc do |observer|
120
+ on_modify_text { |modify_event|
121
+ observer.call(@swt_widget.getText)
122
+ }
123
+ end,
124
+ :caret_position => proc do |observer|
125
+ on_event_keydown { |event|
126
+ observer.call(@swt_widget.getCaretPosition)
127
+ }
128
+ on_event_keyup { |event|
129
+ observer.call(@swt_widget.getCaretPosition)
130
+ }
131
+ on_event_mousedown { |event|
132
+ observer.call(@swt_widget.getCaretPosition)
133
+ }
134
+ on_event_mouseup { |event|
135
+ observer.call(@swt_widget.getCaretPosition)
136
+ }
137
+ end,
138
+ :selection_count => proc do |observer|
139
+ on_event_keydown { |event|
140
+ observer.call(@swt_widget.getSelectionCount)
141
+ }
142
+ on_event_keyup { |event|
143
+ observer.call(@swt_widget.getSelectionCount)
144
+ }
145
+ on_event_mousedown { |event|
146
+ observer.call(@swt_widget.getSelectionCount)
147
+ }
148
+ on_event_mouseup { |event|
149
+ observer.call(@swt_widget.getSelectionCount)
150
+ }
151
+ end,
152
+ :top_index => proc do |observer|
153
+ @last_top_index = @swt_widget.getTopIndex
154
+ on_paint_control { |event|
155
+ if @swt_widget.getTopIndex != @last_top_index
156
+ @last_top_index = @swt_widget.getTopIndex
157
+ observer.call(@last_top_index)
158
+ end
159
+ }
160
+ end,
161
+ },
162
+ Java::OrgEclipseSwtCustom::StyledText => {
163
+ :text => proc do |observer|
164
+ on_modify_text { |modify_event|
165
+ observer.call(@swt_widget.getText)
166
+ }
167
+ end,
168
+ },
169
+ Java::OrgEclipseSwtWidgets::Button => {
170
+ :selection => proc do |observer|
171
+ on_widget_selected { |selection_event|
172
+ observer.call(@swt_widget.getSelection)
173
+ }
174
+ end
175
+ },
176
+ Java::OrgEclipseSwtWidgets::Spinner => {
177
+ :selection => proc do |observer|
178
+ on_widget_selected { |selection_event|
179
+ observer.call(@swt_widget.getSelection)
180
+ }
181
+ end
182
+ }
183
+ }
184
+ end
185
+
186
+ def self.widget_exists?(underscored_widget_name)
187
+ !!swt_widget_class_for(underscored_widget_name)
188
+ end
189
+
190
+ # This supports widgets in and out of basic SWT
191
+ def self.swt_widget_class_for(underscored_widget_name)
192
+ swt_widget_name = underscored_widget_name.camelcase(:upper)
193
+ swt_widget_class = eval(swt_widget_name)
194
+ unless swt_widget_class.ancestors.include?(org.eclipse.swt.widgets.Widget)
195
+ Glimmer.logger&.debug("Class #{swt_widget_class} matching #{underscored_widget_name} is not a subclass of org.eclipse.swt.widgets.Widget")
196
+ return nil
197
+ end
198
+ swt_widget_class
199
+ rescue NameError => e
200
+ Glimmer.logger&.debug e.message
201
+ # Glimmer.logger&.debug("#{e.message}\n#{e.backtrace.join("\n")}")
202
+ nil
203
+ rescue => e
204
+ Glimmer.logger&.debug e.message
205
+ # Glimmer.logger&.debug("#{e.message}\n#{e.backtrace.join("\n")}")
206
+ nil
207
+ end
208
+
209
+ def async_exec(&block)
210
+ DisplayProxy.instance.async_exec(&block)
211
+ end
212
+
213
+ def sync_exec(&block)
214
+ DisplayProxy.instance.sync_exec(&block)
215
+ end
216
+
217
+ def has_style?(style)
218
+ (@swt_widget.style & SWTProxy[style]) == SWTProxy[style]
219
+ end
220
+
221
+ def dispose
222
+ @swt_widget.dispose
223
+ end
224
+
225
+ # TODO Consider renaming these methods as they are mainly used for data-binding
226
+
227
+ def can_add_observer?(property_name)
228
+ @swt_widget.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact.map(&:keys).flatten.map(&:to_s).include?(property_name.to_s)
229
+ end
230
+
231
+ # Used for data-binding only. Consider renaming or improving to avoid the confusion it causes
232
+ def add_observer(observer, property_name)
233
+ property_listener_installers = @swt_widget.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact
234
+ widget_listener_installers = property_listener_installers.map{|installer| installer[property_name.to_s.to_sym]}.compact if !property_listener_installers.empty?
235
+ widget_listener_installers.each do |widget_listener_installer|
236
+ widget_listener_installer.call(observer)
237
+ end
238
+ end
239
+
240
+ def remove_observer(observer, property_name)
241
+ # TODO consider implementing if remove_observer is needed (consumers can remove listener via SWT API)
242
+ end
243
+
244
+ # TODO eliminate duplication in the following methods perhaps by relying on exceptions
245
+
246
+ def can_handle_observation_request?(observation_request)
247
+ observation_request = observation_request.to_s
248
+ if observation_request.start_with?('on_event_')
249
+ constant_name = observation_request.sub(/^on_event_/, '')
250
+ SWTProxy.has_constant?(constant_name)
251
+ elsif observation_request.start_with?('on_')
252
+ event = observation_request.sub(/^on_/, '')
253
+ can_add_listener?(event)
254
+ else
255
+ false
256
+ end
257
+ end
258
+
259
+ def handle_observation_request(observation_request, &block)
260
+ if observation_request.start_with?('on_event_')
261
+ constant_name = observation_request.sub(/^on_event_/, '')
262
+ add_swt_event_listener(constant_name, &block)
263
+ elsif observation_request.start_with?('on_')
264
+ event = observation_request.sub(/^on_/, '')
265
+ add_listener(event, &block)
266
+ end
267
+ end
268
+
269
+ def content(&block)
270
+ Glimmer::DSL::Engine.add_content(self, DSL::WidgetExpression.new, &block)
271
+ end
272
+
273
+ private
274
+
275
+ def style(underscored_widget_name, styles)
276
+ styles = [styles].flatten.compact
277
+ styles.empty? ? default_style(underscored_widget_name) : SWTProxy[*styles]
278
+ end
279
+
280
+ def default_style(underscored_widget_name)
281
+ styles = DEFAULT_STYLES[underscored_widget_name] || [:none]
282
+ SWTProxy[styles]
283
+ end
284
+
285
+ def attribute_setter(attribute_name)
286
+ "set#{attribute_name.to_s.camelcase(:upper)}"
287
+ end
288
+
289
+ def attribute_getter(attribute_name)
290
+ "get#{attribute_name.to_s.camelcase(:upper)}"
291
+ end
292
+
293
+ # TODO refactor following methods to eliminate duplication
294
+ # perhaps consider relying on raising an exception to avoid checking first
295
+ # unless that gives obscure SWT errors
296
+ # Otherwise, consider caching results from can_add_lsitener and using them in
297
+ # add_listener knowing it will be called for sure afterwards
298
+
299
+ def can_add_listener?(underscored_listener_name)
300
+ !self.class.find_listener(@swt_widget.getClass, underscored_listener_name).empty?
301
+ end
302
+
303
+ def add_listener(underscored_listener_name, &block)
304
+ widget_add_listener_method, listener_class, listener_method = self.class.find_listener(@swt_widget.getClass, underscored_listener_name)
305
+ listener = listener_class.new(listener_method => block)
306
+ @swt_widget.send(widget_add_listener_method, listener)
307
+ WidgetListenerProxy.new(listener)
308
+ end
309
+
310
+ # Looks through SWT class add***Listener methods till it finds one for which
311
+ # the argument is a listener class that has an event method matching
312
+ # underscored_listener_name
313
+ def self.find_listener(swt_widget_class, underscored_listener_name)
314
+ @listeners ||= {}
315
+ listener_key = [swt_widget_class.name, underscored_listener_name]
316
+ unless @listeners.has_key?(listener_key)
317
+ listener_method_name = underscored_listener_name.camelcase(:lower)
318
+ swt_widget_class.getMethods.each do |widget_add_listener_method|
319
+ if widget_add_listener_method.getName.match(/add.*Listener/)
320
+ widget_add_listener_method.getParameterTypes.each do |listener_type|
321
+ listener_type.getMethods.each do |listener_method|
322
+ if (listener_method.getName == listener_method_name)
323
+ @listeners[listener_key] = [widget_add_listener_method.getName, listener_class(listener_type), listener_method.getName]
324
+ return @listeners[listener_key]
325
+ end
326
+ end
327
+ end
328
+ end
329
+ end
330
+ @listeners[listener_key] = []
331
+ end
332
+ @listeners[listener_key]
333
+ end
334
+
335
+ # Returns a Ruby class that implements listener type Java interface with ability to easily
336
+ # install a block that gets called upon calling a listener event method
337
+ def self.listener_class(listener_type)
338
+ @listener_classes ||= {}
339
+ listener_class_key = listener_type.name
340
+ unless @listener_classes.has_key?(listener_class_key)
341
+ @listener_classes[listener_class_key] = Class.new(Object).tap do |listener_class|
342
+ listener_class.send :include, (eval listener_type.name.sub("interface", ""))
343
+ listener_class.define_method('initialize') do |event_method_block_mapping|
344
+ @event_method_block_mapping = event_method_block_mapping
345
+ end
346
+ listener_type.getMethods.each do |event_method|
347
+ listener_class.define_method(event_method.getName) do |event|
348
+ @event_method_block_mapping[event_method.getName]&.call(event)
349
+ end
350
+ end
351
+ end
352
+ end
353
+ @listener_classes[listener_class_key]
354
+ end
355
+
356
+ def add_swt_event_listener(swt_constant, &block)
357
+ event_type = SWTProxy[swt_constant]
358
+ @swt_widget.addListener(event_type, &block)
359
+ WidgetListenerProxy.new(@swt_widget.getListeners(event_type).last)
360
+ end
361
+
362
+ def widget_custom_attribute_mapping
363
+ @swt_widget_custom_attribute_mapping ||= {
364
+ 'focus' => {
365
+ getter: {name: 'isFocusControl'},
366
+ setter: {name: 'setFocus', invoker: lambda { |widget, args| @swt_widget.setFocus if args.first }},
367
+ },
368
+ 'caret_position' => {
369
+ getter: {name: 'getCaretPosition'},
370
+ setter: {name: 'setSelection', invoker: lambda { |widget, args| @swt_widget.setSelection(args.first) if args.first }},
371
+ },
372
+ 'selection_count' => {
373
+ getter: {name: 'getSelectionCount'},
374
+ setter: {name: 'setSelection', invoker: lambda { |widget, args| @swt_widget.setSelection(@swt_widget.getCaretPosition, @swt_widget.getCaretPosition + args.first) if args.first }},
375
+ },
376
+ }
377
+ end
378
+
379
+ def apply_property_type_converters(attribute_name, args)
380
+ if args.count == 1
381
+ value = args.first
382
+ converter = property_type_converters[attribute_name.to_sym]
383
+ args[0] = converter.call(value) if converter
384
+ end
385
+ if args.count == 1 && args.first.is_a?(ColorProxy)
386
+ g_color = args.first
387
+ args[0] = g_color.swt_color
388
+ end
389
+ end
390
+
391
+ def property_type_converters
392
+ color_converter = proc do |value|
393
+ if value.is_a?(Symbol) || value.is_a?(String)
394
+ ColorProxy.new(value).swt_color
395
+ else
396
+ value
397
+ end
398
+ end
399
+ # TODO consider detecting type on widget method and automatically invoking right converter (e.g. :to_s for String, :to_i for Integer)
400
+ @property_type_converters ||= {
401
+ :background => color_converter,
402
+ :background_image => proc do |value|
403
+ if value.is_a?(String)
404
+ if value.start_with?('uri:classloader')
405
+ value = value.sub(/^uri\:classloader\:\//, '')
406
+ object = java.lang.Object.new
407
+ value = object.java_class.resource_as_stream(value)
408
+ value = java.io.BufferedInputStream.new(value)
409
+ end
410
+ image_data = ImageData.new(value)
411
+ # TODO in the future, look into unregistering this listener when no longer needed
412
+ on_event_Resize do |resize_event|
413
+ new_image_data = image_data.scaledTo(@swt_widget.getSize.x, @swt_widget.getSize.y)
414
+ @swt_widget.getBackgroundImage&.dispose
415
+ @swt_widget.setBackgroundImage(Image.new(@swt_widget.getDisplay, new_image_data))
416
+ end
417
+ Image.new(@swt_widget.getDisplay, image_data)
418
+ else
419
+ value
420
+ end
421
+ end,
422
+ :foreground => color_converter,
423
+ :font => proc do |value|
424
+ if value.is_a?(Hash)
425
+ font_properties = value
426
+ FontProxy.new(self, font_properties).swt_font
427
+ else
428
+ value
429
+ end
430
+ end,
431
+ :items => proc do |value|
432
+ value.to_java :string
433
+ end,
434
+ :text => proc do |value|
435
+ value.to_s
436
+ end,
437
+ :visible => proc do |value|
438
+ !!value
439
+ end,
440
+ }
441
+ end
442
+ end
443
+ end
444
+ end