glimmer 0.5.8 → 0.5.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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