glimmer-dsl-swt 0.2.4 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -33,26 +33,24 @@ module Glimmer
33
33
 
34
34
  def initialize(*args)
35
35
  @swt_display = Display.new(*args)
36
+ @swt_display.set_data('proxy', self)
36
37
  end
37
38
 
38
- def dispose
39
- @swt_display.dispose
39
+ def method_missing(method, *args, &block)
40
+ swt_display.send(method, *args, &block)
41
+ rescue => e
42
+ Glimmer::Config.logger.debug {"Neither DisplayProxy nor #{swt_display.class.name} can handle the method ##{method}"}
43
+ super
40
44
  end
41
-
42
- # Executes code block asynchronously with respect to SWT UI thread
43
- def async_exec(&block)
44
- @swt_display.asyncExec(&block)
45
- end
46
-
47
- # Executes code block synchronously with respect to SWT UI thread
48
- def sync_exec(&block)
49
- @swt_display.syncExec(&block)
45
+
46
+ def respond_to?(method, *args, &block)
47
+ super || swt_display.respond_to?(method, *args, &block)
50
48
  end
51
49
 
52
50
  def can_handle_observation_request?(observation_request)
53
51
  observation_request = observation_request.to_s
54
- if observation_request.start_with?('on_event_')
55
- constant_name = observation_request.sub(/^on_event_/, '')
52
+ if observation_request.start_with?('on_swt_')
53
+ constant_name = observation_request.sub(/^on_swt_/, '')
56
54
  SWTProxy.has_constant?(constant_name)
57
55
  elsif observation_request.start_with?('on_')
58
56
  event_name = observation_request.sub(/^on_/, '')
@@ -63,8 +61,8 @@ module Glimmer
63
61
  end
64
62
 
65
63
  def handle_observation_request(observation_request, &block)
66
- if observation_request.start_with?('on_event_')
67
- constant_name = observation_request.sub(/^on_event_/, '')
64
+ if observation_request.start_with?('on_swt_')
65
+ constant_name = observation_request.sub(/^on_swt_/, '')
68
66
  add_swt_event_listener(constant_name, &block)
69
67
  elsif observation_request.start_with?('on_')
70
68
  event_name = observation_request.sub(/^on_/, '')
@@ -31,7 +31,7 @@ module Glimmer
31
31
  begin
32
32
  @swt_layout_data = swt_layout_data_class.new(*args)
33
33
  rescue => e
34
- Glimmer::Config.logger&.debug "#{e.message}\n#{e.backtrace.join("\n")}"
34
+ Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
35
35
  @swt_layout_data = args.first if args.count == 1
36
36
  end
37
37
  @widget_proxy.swt_widget.setLayoutData(@swt_layout_data)
@@ -34,8 +34,8 @@ module Glimmer
34
34
  end
35
35
  swt_layout_class
36
36
  rescue => e
37
- Glimmer::Config.logger&.debug e.message
38
- # Glimmer::Config.logger&.debug "#{e.message}\n#{e.backtrace.join("\n")}"
37
+ Glimmer::Config.logger.debug {e.message}
38
+ # Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
39
39
  raise e
40
40
  end
41
41
  end
@@ -15,11 +15,13 @@ module Glimmer
15
15
 
16
16
  def initialize(parent, style)
17
17
  parent = parent.swt_widget if parent.respond_to?(:swt_widget) && parent.swt_widget.is_a?(Shell)
18
+ @temporary_parent = parent = Glimmer::SWT::ShellProxy.new.swt_widget if parent.nil?
18
19
  @swt_widget = MessageBox.new(parent, style)
19
20
  end
20
21
 
21
22
  def open
22
23
  @swt_widget.open
24
+ @temporary_parent&.dispose
23
25
  end
24
26
 
25
27
  # TODO refactor the following methods to put in a JavaBean mixin or somethin (perhaps contribute to OSS project too)
@@ -40,9 +40,10 @@ module Glimmer
40
40
  end
41
41
  args = args.compact
42
42
  @swt_widget = Shell.new(*args)
43
+ @swt_widget.set_data('proxy', self)
43
44
  @swt_widget.setLayout(FillLayout.new)
44
45
  @swt_widget.setMinimumSize(WIDTH_MIN, HEIGHT_MIN)
45
- on_event_show do
46
+ on_swt_show do
46
47
  Thread.new do
47
48
  sleep(0.25)
48
49
  async_exec do
@@ -83,10 +84,6 @@ module Glimmer
83
84
  @swt_widget.setVisible(false)
84
85
  end
85
86
 
86
- def close
87
- @swt_widget.close
88
- end
89
-
90
87
  def visible?
91
88
  @swt_widget.isDisposed ? false : @swt_widget.isVisible
92
89
  end
@@ -130,7 +127,11 @@ module Glimmer
130
127
  # This method is not needed except in rare circumstances where there is a need to start the SWT Event Loop before opening the shell.
131
128
  def start_event_loop
132
129
  until @swt_widget.isDisposed
133
- @display.sleep unless @display.readAndDispatch
130
+ begin
131
+ @display.sleep unless @display.readAndDispatch
132
+ rescue => e
133
+ Glimmer::Config.logger.debug {e.full_message}
134
+ end
134
135
  end
135
136
  end
136
137
 
@@ -140,9 +141,9 @@ module Glimmer
140
141
  visibility_notifier = proc do
141
142
  observer.call(visible?)
142
143
  end
143
- on_event_show(&visibility_notifier)
144
- on_event_hide(&visibility_notifier)
145
- on_event_close(&visibility_notifier)
144
+ on_swt_show(&visibility_notifier)
145
+ on_swt_hide(&visibility_notifier)
146
+ on_swt_close(&visibility_notifier)
146
147
  else
147
148
  super
148
149
  end
@@ -61,12 +61,12 @@ module Glimmer
61
61
  negative ? ~bit_value : bit_value
62
62
  rescue => e
63
63
  begin
64
- # Glimmer::Config.logger&.debug(e.full_message)
64
+ # Glimmer::Config.logger.debug {e.full_message}
65
65
  alternative_swt_constant_symbol = constant_source_class.constants.find {|c| c.to_s.upcase == swt_constant_symbol.to_s.upcase}
66
66
  bit_value = constant_source_class.const_get(alternative_swt_constant_symbol)
67
67
  negative ? ~bit_value : bit_value
68
68
  rescue => e
69
- # Glimmer::Config.logger&.debug(e.full_message)
69
+ # Glimmer::Config.logger.debug {e.full_message}
70
70
  bit_value = extra_styles[swt_constant_symbol]
71
71
  if bit_value
72
72
  negative ? ~bit_value : bit_value
@@ -3,7 +3,7 @@ require 'glimmer/swt/widget_proxy'
3
3
  module Glimmer
4
4
  module SWT
5
5
  class TableColumnProxy < Glimmer::SWT::WidgetProxy
6
- attr_reader :no_sort, :sort_property
6
+ attr_reader :no_sort, :sort_property, :editor
7
7
  alias no_sort? no_sort
8
8
  attr_accessor :sort_block, :sort_by_block
9
9
 
@@ -16,7 +16,11 @@ module Glimmer
16
16
  end
17
17
 
18
18
  def sort_property=(args)
19
- @sort_property = args.to_a.first
19
+ @sort_property = args unless args.empty?
20
+ end
21
+
22
+ def editor=(args)
23
+ @editor = args
20
24
  end
21
25
 
22
26
  end
@@ -23,8 +23,8 @@ module Glimmer
23
23
  def find_table_item_and_column_index
24
24
  {}.tap do |result|
25
25
  if respond_to?(:x) && respond_to?(:y)
26
- result[:table_item] = widget.getItems.detect do |ti|
27
- result[:column_index] = widget.getColumnCount.times.to_a.detect do |ci|
26
+ result[:table_item] = widget.items.detect do |ti|
27
+ result[:column_index] = widget.column_count.times.to_a.detect do |ci|
28
28
  ti.getBounds(ci).contains(x, y)
29
29
  end
30
30
  end
@@ -33,7 +33,61 @@ module Glimmer
33
33
  end
34
34
  end
35
35
 
36
- attr_reader :table_editor, :table_editor_text_proxy, :sort_property, :sort_direction, :sort_block, :sort_type, :sort_by_block
36
+ class << self
37
+ def editors
38
+ @editors ||= {
39
+ text: {
40
+ widget_value_property: :text,
41
+ editor_gui: lambda do |args, model, property, table_proxy|
42
+ table_proxy.table_editor.minimumHeight = 20
43
+ table_editor_widget_proxy = text(*args) {
44
+ text model.send(property)
45
+ focus true
46
+ on_focus_lost {
47
+ table_proxy.finish_edit!
48
+ }
49
+ on_key_pressed { |key_event|
50
+ if key_event.keyCode == swt(:cr)
51
+ table_proxy.finish_edit!
52
+ elsif key_event.keyCode == swt(:esc)
53
+ table_proxy.cancel_edit!
54
+ end
55
+ }
56
+ }
57
+ table_editor_widget_proxy.swt_widget.selectAll
58
+ table_editor_widget_proxy
59
+ end,
60
+ },
61
+ combo: {
62
+ widget_value_property: :text,
63
+ editor_gui: lambda do |args, model, property, table_proxy|
64
+ table_proxy.table_editor.minimumHeight = 25
65
+ table_editor_widget_proxy = combo(*args) {
66
+ items model.send("#{property}_options")
67
+ text model.send(property)
68
+ focus true
69
+ on_focus_lost {
70
+ table_proxy.finish_edit!
71
+ }
72
+ on_key_pressed { |key_event|
73
+ if key_event.keyCode == swt(:cr)
74
+ table_proxy.finish_edit!
75
+ elsif key_event.keyCode == swt(:esc)
76
+ table_proxy.cancel_edit!
77
+ end
78
+ }
79
+ on_widget_selected {
80
+ table_proxy.finish_edit!
81
+ }
82
+ }
83
+ table_editor_widget_proxy
84
+ end,
85
+ }
86
+ }
87
+ end
88
+ end
89
+
90
+ attr_reader :table_editor, :table_editor_widget_proxy, :sort_property, :sort_direction, :sort_block, :sort_type, :sort_by_block, :additional_sort_properties, :editor
37
91
  attr_accessor :column_properties
38
92
 
39
93
  def initialize(underscored_widget_name, parent, args)
@@ -46,13 +100,30 @@ module Glimmer
46
100
 
47
101
  def model_binding
48
102
  swt_widget.data
49
- end
103
+ end
50
104
 
51
105
  def sort_by_column(table_column_proxy)
52
106
  index = swt_widget.columns.to_a.index(table_column_proxy.swt_widget)
53
- new_sort_property = table_column_proxy.sort_property || column_properties[index]
54
- @sort_direction = @sort_direction.nil? || @sort_property != new_sort_property || @sort_direction == :descending ? :ascending : :descending
107
+ new_sort_property = table_column_proxy.sort_property || [column_properties[index]]
108
+ if new_sort_property.size == 1 && !additional_sort_properties.to_a.empty?
109
+ selected_additional_sort_properties = additional_sort_properties.clone
110
+ if selected_additional_sort_properties.include?(new_sort_property.first)
111
+ selected_additional_sort_properties.delete(new_sort_property.first)
112
+ new_sort_property += selected_additional_sort_properties
113
+ else
114
+ new_sort_property += additional_sort_properties
115
+ end
116
+ end
117
+
118
+ @sort_direction = @sort_direction.nil? || @sort_property != new_sort_property || @sort_direction == :descending ? :ascending : :descending
119
+ swt_widget.sort_direction = @sort_direction == :ascending ? SWTProxy[:up] : SWTProxy[:down]
120
+
55
121
  @sort_property = new_sort_property
122
+ swt_widget.sort_column = table_column_proxy.swt_widget
123
+
124
+ @sort_by_block = nil
125
+ @sort_block = nil
126
+ @sort_type = nil
56
127
  if table_column_proxy.sort_by_block
57
128
  @sort_by_block = table_column_proxy.sort_by_block
58
129
  elsif table_column_proxy.sort_block
@@ -64,43 +135,51 @@ module Glimmer
64
135
  end
65
136
 
66
137
  def detect_sort_type
67
- @sort_type = String
138
+ @sort_type = sort_property.size.times.map { String }
68
139
  array = model_binding.evaluate_property
69
- values = array.map { |object| object.send(sort_property) }
70
- value_classes = values.map(&:class).uniq
71
- if value_classes.size == 1
72
- @sort_type = value_classes.first
73
- elsif value_classes.include?(Integer)
74
- @sort_type = Integer
75
- elsif value_classes.include?(Float)
76
- @sort_type = Float
140
+ sort_property.each_with_index do |a_sort_property, i|
141
+ values = array.map { |object| object.send(a_sort_property) }
142
+ value_classes = values.map(&:class).uniq
143
+ if value_classes.size == 1
144
+ @sort_type[i] = value_classes.first
145
+ elsif value_classes.include?(Integer)
146
+ @sort_type[i] = Integer
147
+ elsif value_classes.include?(Float)
148
+ @sort_type[i] = Float
149
+ end
77
150
  end
78
151
  end
79
152
 
153
+ def additional_sort_properties=(args)
154
+ @additional_sort_properties = args unless args.empty?
155
+ end
156
+
157
+ def editor=(args)
158
+ @editor = args
159
+ end
160
+
80
161
  def sort
81
162
  return unless sort_property && (sort_type || sort_block || sort_by_block)
82
163
  array = model_binding.evaluate_property
83
164
  # Converting value to_s first to handle nil cases. Should work with numeric, boolean, and date fields
84
165
  if sort_block
85
- sorted_array = array.sort do |object1, object2|
86
- value1 = object1.send(sort_property)
87
- value2 = object2.send(sort_property)
88
- sort_block.call(value1, value2)
89
- end
166
+ sorted_array = array.sort(&sort_block)
167
+ elsif sort_by_block
168
+ sorted_array = array.sort_by(&sort_by_block)
90
169
  else
91
170
  sorted_array = array.sort_by do |object|
92
- value = object.send(sort_property)
93
- # handle nil and difficult to compare types gracefully
94
- if sort_by_block
95
- value = sort_by_block.call(value)
96
- elsif sort_type == Integer
97
- value = value.to_i
98
- elsif sort_type == Float
99
- value = value.to_f
100
- elsif sort_type == String
101
- value = value.to_s
171
+ sort_property.each_with_index.map do |a_sort_property, i|
172
+ value = object.send(a_sort_property)
173
+ # handle nil and difficult to compare types gracefully
174
+ if sort_type[i] == Integer
175
+ value = value.to_i
176
+ elsif sort_type[i] == Float
177
+ value = value.to_f
178
+ elsif sort_type[i] == String
179
+ value = value.to_s
180
+ end
181
+ value
102
182
  end
103
- value
104
183
  end
105
184
  end
106
185
  sorted_array = sorted_array.reverse if sort_direction == :descending
@@ -131,6 +210,14 @@ module Glimmer
131
210
  })
132
211
  end
133
212
 
213
+ def post_initialize_child(table_column_proxy)
214
+ table_column_proxies << table_column_proxy
215
+ end
216
+
217
+ def table_column_proxies
218
+ @table_column_proxies ||= []
219
+ end
220
+
134
221
  # Indicates if table is in edit mode, thus displaying a text widget for a table item cell
135
222
  def edit_mode?
136
223
  !!@edit_mode
@@ -156,58 +243,62 @@ module Glimmer
156
243
 
157
244
  def edit_table_item(table_item, column_index, before_write: nil, after_write: nil, after_cancel: nil)
158
245
  return if table_item.nil?
246
+ model = table_item.data
247
+ property = column_properties[column_index]
159
248
  @cancel_edit&.call if @edit_mode
249
+ action_taken = false
160
250
  @edit_mode = true
161
- content {
162
- @table_editor_text_proxy = text {
163
- focus true
164
- text table_item.getText(column_index)
165
- action_taken = false
166
- @cancel_edit = lambda do
167
- @cancel_in_progress = true
168
- @table_editor_text_proxy&.swt_widget&.dispose
169
- @table_editor_text_proxy = nil
170
- after_cancel&.call
251
+
252
+ editor_config = table_column_proxies[column_index].editor || editor
253
+ editor_config = editor_config.to_a
254
+ editor_widget_options = editor_config.last.is_a?(Hash) ? editor_config.last : {}
255
+ editor_widget_arg_last_index = editor_config.last.is_a?(Hash) ? -2 : -1
256
+ editor_widget = editor_config[0] || :text
257
+ editor_widget_args = editor_config[1..editor_widget_arg_last_index]
258
+ model_editing_property = editor_widget_options[:property] || property
259
+ widget_value_property = TableProxy::editors[editor_widget][:widget_value_property]
260
+
261
+ @cancel_edit = lambda do |event=nil|
262
+ @cancel_in_progress = true
263
+ @table_editor_widget_proxy&.swt_widget&.dispose
264
+ @table_editor_widget_proxy = nil
265
+ after_cancel&.call
266
+ @edit_in_progress = false
267
+ @cancel_in_progress = false
268
+ @cancel_edit = nil
269
+ @edit_mode = false
270
+ end
271
+
272
+ @finish_edit = lambda do |event=nil|
273
+ new_value = @table_editor_widget_proxy&.swt_widget&.send(widget_value_property)
274
+ if table_item.isDisposed
275
+ @cancel_edit.call
276
+ elsif new_value && !action_taken && !@edit_in_progress && !@cancel_in_progress
277
+ action_taken = true
278
+ @edit_in_progress = true
279
+ if new_value == model.send(model_editing_property)
280
+ @cancel_edit.call
281
+ else
282
+ before_write&.call
283
+ model.send("#{model_editing_property}=", new_value) # makes table update itself, so must search for selected table item again
284
+ # Table refresh happens here because of model update triggering observers, so must retrieve table item again
285
+ edited_table_item = search { |ti| ti.getData == model }.first
286
+ swt_widget.showItem(edited_table_item)
287
+ @table_editor_widget_proxy&.swt_widget&.dispose
288
+ @table_editor_widget_proxy = nil
289
+ after_write&.call(edited_table_item)
171
290
  @edit_in_progress = false
172
- @cancel_in_progress = false
173
- @cancel_edit = nil
174
- @edit_mode = false
175
291
  end
176
- @finish_edit = lambda do |event=nil|
177
- if table_item.isDisposed
178
- @cancel_edit.call
179
- elsif !action_taken && !@edit_in_progress && !@cancel_in_progress
180
- action_taken = true
181
- @edit_in_progress = true
182
- new_text = @table_editor_text_proxy.swt_widget.getText
183
- if new_text == table_item.getText(column_index)
184
- @cancel_edit.call
185
- else
186
- before_write&.call
187
- table_item.setText(column_index, new_text)
188
- model = table_item.getData
189
- model.send("#{column_properties[column_index]}=", new_text) # makes table update itself, so must search for selected table item again
190
- edited_table_item = search { |ti| ti.getData == model }.first
191
- swt_widget.showItem(edited_table_item)
192
- @table_editor_text_proxy&.swt_widget&.dispose
193
- @table_editor_text_proxy = nil
194
- after_write&.call(edited_table_item)
195
- @edit_in_progress = false
196
- end
197
- end
198
- end
199
- on_focus_lost(&@finish_edit)
200
- on_key_pressed { |key_event|
201
- if key_event.keyCode == swt(:cr)
202
- @finish_edit.call(key_event)
203
- elsif key_event.keyCode == swt(:esc)
204
- @cancel_edit.call
205
- end
206
- }
207
- }
208
- @table_editor_text_proxy.swt_widget.selectAll
292
+ end
293
+ end
294
+
295
+ content {
296
+ @table_editor_widget_proxy = TableProxy::editors[editor_widget][:editor_gui].call(editor_widget_args, model, model_editing_property, self)
209
297
  }
210
- @table_editor.setEditor(@table_editor_text_proxy.swt_widget, table_item, column_index)
298
+ @table_editor.setEditor(@table_editor_widget_proxy.swt_widget, table_item, column_index)
299
+ rescue => e
300
+ Glimmer::Config.logger.error {e.full_message}
301
+ raise e
211
302
  end
212
303
 
213
304
  def add_listener(underscored_listener_name, &block)