glimmer-dsl-opal 0.7.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -0
  3. data/README.md +745 -82
  4. data/VERSION +1 -1
  5. data/lib/glimmer-dsl-opal.rb +14 -1
  6. data/lib/glimmer-dsl-opal/ext/class.rb +10 -0
  7. data/lib/{file.rb → glimmer-dsl-opal/ext/file.rb} +0 -0
  8. data/lib/glimmer-dsl-opal/ext/glimmer/dsl/engine.rb +30 -0
  9. data/lib/glimmer-dsl-opal/samples/elaborate/contact_manager.rb +50 -23
  10. data/lib/glimmer-dsl-opal/samples/elaborate/login.rb +22 -5
  11. data/lib/glimmer-dsl-opal/samples/hello/hello_browser.rb +24 -1
  12. data/lib/glimmer-dsl-opal/samples/hello/hello_button.rb +46 -0
  13. data/lib/glimmer-dsl-opal/samples/hello/hello_computed.rb +27 -0
  14. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_shell.rb +7 -7
  15. data/lib/glimmer-dsl-opal/samples/hello/hello_list_multi_selection.rb +62 -32
  16. data/lib/glimmer-dsl-opal/samples/hello/hello_list_single_selection.rb +47 -22
  17. data/lib/glimmer-dsl-opal/samples/hello/hello_menu_bar.rb +241 -0
  18. data/lib/glimmer-dsl-opal/samples/hello/hello_message_box.rb +37 -0
  19. data/lib/glimmer-dsl-opal/samples/hello/hello_pop_up_context_menu.rb +84 -0
  20. data/lib/glimmer/data_binding/observable_element.rb +1 -1
  21. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +6 -0
  22. data/lib/glimmer/dsl/opal/dsl.rb +2 -0
  23. data/lib/glimmer/dsl/opal/menu_bar_expression.rb +54 -0
  24. data/lib/glimmer/dsl/opal/menu_expression.rb +61 -0
  25. data/lib/glimmer/dsl/opal/shell_expression.rb +0 -4
  26. data/lib/glimmer/dsl/opal/widget_expression.rb +3 -2
  27. data/lib/glimmer/dsl/opal/widget_listener_expression.rb +2 -2
  28. data/lib/glimmer/swt/custom/checkbox_group.rb +2 -2
  29. data/lib/glimmer/swt/custom/radio_group.rb +2 -2
  30. data/lib/glimmer/swt/date_time_proxy.rb +5 -4
  31. data/lib/glimmer/swt/display_proxy.rb +4 -4
  32. data/lib/glimmer/swt/event_listener_proxy.rb +14 -4
  33. data/lib/glimmer/swt/font_proxy.rb +4 -4
  34. data/lib/glimmer/swt/grid_layout_proxy.rb +21 -12
  35. data/lib/glimmer/swt/label_proxy.rb +17 -6
  36. data/lib/glimmer/swt/latest_message_box_proxy.rb +20 -0
  37. data/lib/glimmer/swt/layout_data_proxy.rb +6 -6
  38. data/lib/glimmer/swt/list_proxy.rb +15 -0
  39. data/lib/glimmer/swt/menu_item_proxy.rb +174 -0
  40. data/lib/glimmer/swt/menu_proxy.rb +273 -0
  41. data/lib/glimmer/swt/message_box_proxy.rb +57 -72
  42. data/lib/glimmer/swt/property_owner.rb +2 -0
  43. data/lib/glimmer/swt/radio_proxy.rb +1 -1
  44. data/lib/glimmer/swt/shell_proxy.rb +34 -189
  45. data/lib/glimmer/swt/tab_folder_proxy.rb +43 -0
  46. data/lib/glimmer/swt/table_column_proxy.rb +3 -2
  47. data/lib/glimmer/swt/table_editor.rb +1 -1
  48. data/lib/glimmer/swt/table_item_proxy.rb +7 -5
  49. data/lib/glimmer/swt/table_proxy.rb +14 -10
  50. data/lib/glimmer/swt/widget_proxy.rb +327 -33
  51. data/lib/glimmer/ui/custom_shell.rb +9 -7
  52. data/lib/glimmer/ui/custom_widget.rb +3 -3
  53. metadata +36 -4
@@ -1,8 +1,51 @@
1
+ # Copyright (c) 2020 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
1
22
  require 'glimmer/swt/widget_proxy'
2
23
 
3
24
  module Glimmer
4
25
  module SWT
5
26
  class TabFolderProxy < WidgetProxy
27
+ STYLE = <<~CSS
28
+ .tabs .tab.selected {
29
+ background: rgb(80, 116, 211);
30
+ color: white;
31
+ }
32
+ .tabs .tab {
33
+ background-color: inherit;
34
+ float: left;
35
+ border: none;
36
+ outline: none;
37
+ cursor: pointer;
38
+ padding: 14px 16px;
39
+ transition: 0.3s;
40
+ font-size: 17px;
41
+ }
42
+ .tabs {
43
+ overflow: hidden;
44
+ border: 1px solid #ccc;
45
+ background-color: #f1f1f1;
46
+ }
47
+ CSS
48
+
6
49
  attr_reader :tabs
7
50
 
8
51
  def initialize(parent, args, block)
@@ -4,6 +4,8 @@ require 'glimmer/swt/swt_proxy'
4
4
  module Glimmer
5
5
  module SWT
6
6
  class TableColumnProxy < WidgetProxy
7
+ include Glimmer
8
+
7
9
  STYLE = <<~CSS
8
10
  th.table-column {
9
11
  background: rgb(246, 246, 246);
@@ -15,8 +17,7 @@ module Glimmer
15
17
  float: right;
16
18
  }
17
19
  CSS
18
- include Glimmer
19
-
20
+
20
21
  attr_accessor :sort_block, :sort_by_block
21
22
  attr_reader :text, :width,
22
23
  :no_sort, :sort_property, :editor
@@ -34,7 +34,7 @@ module Glimmer
34
34
  @editor_widget = editor_widget
35
35
  @old_value = table_item.cell_dom_element(table_column_index).html
36
36
  table_item.cell_dom_element(table_column_index).html('')
37
- editor_widget.render(table_item.cell_dom_element(table_column_index))
37
+ editor_widget.render(custom_parent_dom_element: table_item.cell_dom_element(table_column_index))
38
38
  # TODO tweak the width perfectly so it doesn't expand the table cell
39
39
  # editor_widget.dom_element.css('width', 'calc(100% - 20px)')
40
40
  editor_widget.dom_element.css('width', "#{minimumWidth}%") # TODO implement property with pixels (and perhaps derive percentage separately from pixels)
@@ -39,7 +39,7 @@ module Glimmer
39
39
  super(parent, args, block)
40
40
  # TODO check if there is a need to remove this observer when removing widget from table upon items update
41
41
  on_widget_selected { |event|
42
- parent.select(parent.index_of(self), event.meta?)
42
+ parent.select(parent.index_of(self), (event.meta? if event.respond_to?(:meta?)))
43
43
  }
44
44
  end
45
45
 
@@ -147,10 +147,12 @@ module Glimmer
147
147
  end
148
148
  end
149
149
 
150
- def on_widget_selected(&block)
151
- event = 'click'
152
- delegate = $document.on(event, selector, &block)
153
- EventListenerProxy.new(element_proxy: self, event: event, selector: selector, delegate: delegate)
150
+ def observation_request_to_event_mapping
151
+ {
152
+ 'on_widget_selected' => {
153
+ event: 'mouseup',
154
+ }
155
+ }
154
156
  end
155
157
 
156
158
  def max_column_width(column_index)
@@ -27,6 +27,16 @@ require 'glimmer/swt/table_editor'
27
27
  module Glimmer
28
28
  module SWT
29
29
  class TableProxy < CompositeProxy
30
+ STYLE = <<~CSS
31
+ table {
32
+ border-spacing: 0;
33
+ }
34
+
35
+ table tr th,td {
36
+ cursor: default;
37
+ }
38
+ CSS
39
+
30
40
  attr_reader :columns, :selection,
31
41
  :sort_type, :sort_column, :sort_property, :sort_block, :sort_by_block, :additional_sort_properties,
32
42
  :editor, :table_editor
@@ -128,9 +138,6 @@ module Glimmer
128
138
  on_widget_selected {
129
139
  table_proxy.finish_edit!
130
140
  }
131
- on_focus_lost {
132
- table_proxy.finish_edit!
133
- }
134
141
  on_key_pressed { |key_event|
135
142
  if key_event.keyCode == swt(:cr)
136
143
  table_proxy.finish_edit!
@@ -153,9 +160,6 @@ module Glimmer
153
160
  on_widget_selected {
154
161
  table_proxy.finish_edit!
155
162
  }
156
- on_focus_lost {
157
- table_proxy.finish_edit!
158
- }
159
163
  on_key_pressed { |key_event|
160
164
  if key_event.keyCode == swt(:cr)
161
165
  table_proxy.finish_edit!
@@ -362,7 +366,7 @@ module Glimmer
362
366
  end
363
367
 
364
368
  def sort_property=(new_sort_property)
365
- @sort_property = [new_sort_property].flatten.compact
369
+ @sort_property = new_sort_property.to_collection
366
370
  end
367
371
 
368
372
  def detect_sort_type
@@ -383,7 +387,7 @@ module Glimmer
383
387
 
384
388
  def column_sort_properties
385
389
  column_properties.zip(columns.map(&:sort_property)).map do |pair|
386
- [pair.compact.last].flatten.compact
390
+ pair.compact.last.to_collection
387
391
  end
388
392
  end
389
393
 
@@ -414,7 +418,7 @@ module Glimmer
414
418
  end
415
419
  end
416
420
 
417
- new_sort_property = [new_sort_property].flatten.compact unless new_sort_property.is_a?(Array)
421
+ new_sort_property = new_sort_property.to_collection unless new_sort_property.is_a?(Array)
418
422
  @sort_direction = @sort_direction.nil? || @sort_property.first != new_sort_property.first || @sort_direction == :descending ? :ascending : :descending
419
423
 
420
424
  @sort_property = new_sort_property
@@ -520,7 +524,7 @@ module Glimmer
520
524
  @edit_mode = true
521
525
 
522
526
  editor_config = columns[column_index].editor || editor
523
- editor_config = [editor_config].flatten.compact
527
+ editor_config = editor_config.to_collection
524
528
  editor_widget_options = editor_config.last.is_a?(Hash) ? editor_config.last : {}
525
529
  editor_widget_arg_last_index = editor_config.last.is_a?(Hash) ? -2 : -1
526
530
  editor_widget = (editor_config[0] || :text).to_sym
@@ -23,16 +23,20 @@ require 'glimmer/swt/event_listener_proxy'
23
23
  require 'glimmer/swt/property_owner'
24
24
  require 'glimmer/swt/swt_proxy'
25
25
 
26
+ # TODO implement menu (which delays building it till render using add_content_on_render)
27
+
26
28
  module Glimmer
27
29
  module SWT
28
30
  class WidgetProxy
29
31
  include Glimmer
30
32
  include PropertyOwner
31
33
 
32
- attr_reader :parent, :args, :path, :children, :enabled, :foreground, :background, :font, :focus, :disposed?, :rendered
34
+ attr_reader :parent, :args, :path, :children, :enabled, :foreground, :background, :font, :focus, :disposed?, :rendered, :menu_requested
35
+ attr_accessor :menu
33
36
  alias isDisposed disposed?
34
37
  alias is_disposed disposed?
35
38
  alias rendered? rendered
39
+ alias menu_requested? menu_requested
36
40
 
37
41
  class << self
38
42
  # Factory Method that translates a Glimmer DSL keyword into a WidgetProxy object
@@ -76,7 +80,7 @@ module Glimmer
76
80
  end
77
81
 
78
82
  DEFAULT_INITIALIZERS = {
79
- "composite" => lambda do |composite_proxy|
83
+ composite: lambda do |composite_proxy|
80
84
  if composite_proxy.layout.nil?
81
85
  layout = GridLayoutProxy.new(composite_proxy, [])
82
86
  composite_proxy.layout = layout
@@ -84,18 +88,18 @@ module Glimmer
84
88
  layout.margin_height = 15
85
89
  end
86
90
  end,
87
- # "scrolled_composite" => lambda do |scrolled_composite|
91
+ # scrolled_composite: lambda do |scrolled_composite|
88
92
  # scrolled_composite.expand_horizontal = true
89
93
  # scrolled_composite.expand_vertical = true
90
94
  # end,
91
- # "table" => lambda do |table|
95
+ # table: lambda do |table|
92
96
  # table.setHeaderVisible(true)
93
97
  # table.setLinesVisible(true)
94
98
  # end,
95
- "table_column" => lambda do |table_column_proxy|
99
+ table_column: lambda do |table_column_proxy|
96
100
  table_column_proxy.width = 80
97
101
  end,
98
- # "group" => lambda do |group_proxy|
102
+ # group: lambda do |group_proxy|
99
103
  # group_proxy.layout = GridLayoutProxy.new(group_proxy, []) if group.layout.nil?
100
104
  # end,
101
105
  }
@@ -107,7 +111,7 @@ module Glimmer
107
111
  @children = Set.new # TODO consider moving to composite
108
112
  @enabled = true
109
113
  @data = {}
110
- DEFAULT_INITIALIZERS[self.class.underscored_widget_name(self)]&.call(self)
114
+ DEFAULT_INITIALIZERS[self.class.underscored_widget_name(self).to_s.to_sym]&.call(self)
111
115
  @parent.post_initialize_child(self) # TODO rename to post_initialize_child to be closer to glimmer-dsl-swt terminology
112
116
  end
113
117
 
@@ -124,7 +128,19 @@ module Glimmer
124
128
 
125
129
  # Executes at the closing of a parent widget curly braces after all children/properties have been added/set
126
130
  def post_add_content
127
- # No Op by default
131
+ if !menu.nil? && !is_a?(MenuProxy) && !is_a?(MenuItemProxy)
132
+ on_mouse_down { |mouse_event|
133
+ if mouse_event.button == 3 # right-click
134
+ @menu_requested = true
135
+ dom_element.css('position', 'relative')
136
+ menu&.render
137
+ menu.dom_element.css('position', 'absolute')
138
+ menu.dom_element.css('left', mouse_event.x - parent.layout&.margin_width.to_i) # TODO - parent.layout&.margin_left.to_i)
139
+ menu.dom_element.css('top', mouse_event.y - parent.layout&.margin_height.to_i - 5) # TODO - parent.layout&.margin_top.to_i)
140
+ @menu_requested = false
141
+ end
142
+ }
143
+ end
128
144
  end
129
145
 
130
146
  def set_data(key=nil, value)
@@ -148,18 +164,20 @@ module Glimmer
148
164
  Document.find(path).remove
149
165
  parent&.post_dispose_child(self)
150
166
  # TODO fire on_widget_disposed listener
167
+ # children.each(:dispose) # TODO enable this safely
151
168
  @disposed = true
152
169
  end
153
170
 
154
171
  def remove_all_listeners
155
172
  effective_observation_request_to_event_mapping.keys.each do |keyword|
156
- [effective_observation_request_to_event_mapping[keyword]].flatten.each do |mapping|
173
+ effective_observation_request_to_event_mapping[keyword].to_collection.each do |mapping|
157
174
  observation_requests[keyword].to_a.each do |event_listener|
158
175
  event = mapping[:event]
159
176
  event_handler = mapping[:event_handler]
160
177
  event_element_css_selector = mapping[:event_element_css_selector]
161
178
  the_listener_dom_element = event_element_css_selector ? Element[event_element_css_selector] : listener_dom_element
162
179
  the_listener_dom_element.off(event)
180
+ # TODO improve to precisely remove the listeners that were added, no more no less. (or use the event_listener_proxies method instead or in collaboration)
163
181
  end
164
182
  end
165
183
  end
@@ -173,6 +191,14 @@ module Glimmer
173
191
  def element
174
192
  'div'
175
193
  end
194
+
195
+ def pack(*args)
196
+ # No Op (just a shim) TODO consider if it should be implemented
197
+ end
198
+
199
+ def layout(*args)
200
+ # No Op (just a shim) TODO consider if it should be implemented
201
+ end
176
202
 
177
203
  def enabled=(value)
178
204
  @enabled = value
@@ -180,11 +206,13 @@ module Glimmer
180
206
  end
181
207
 
182
208
  def foreground=(value)
209
+ value = ColorProxy.new(value) if value.is_a?(String)
183
210
  @foreground = value
184
211
  dom_element.css('color', foreground.to_css) unless foreground.nil?
185
212
  end
186
213
 
187
214
  def background=(value)
215
+ value = ColorProxy.new(value) if value.is_a?(String)
188
216
  @background = value
189
217
  dom_element.css('background-color', background.to_css) unless background.nil?
190
218
  end
@@ -215,27 +243,27 @@ module Glimmer
215
243
  Document.find(parent_path)
216
244
  end
217
245
 
218
- def render(custom_parent_dom_element = nil)
246
+ def render(custom_parent_dom_element: nil, brand_new: false)
219
247
  the_parent_dom_element = custom_parent_dom_element || parent_dom_element
220
248
  old_element = dom_element
221
- brand_new = @dom.nil? || old_element.empty?
222
- build_dom(!custom_parent_dom_element) # TODO handle custom parent layout by passing parent instead of parent dom element
249
+ brand_new = @dom.nil? || old_element.empty? || brand_new
250
+ build_dom(layout: !custom_parent_dom_element) # TODO handle custom parent layout by passing parent instead of parent dom element
223
251
  if brand_new
224
- the_parent_dom_element.append(@dom)
252
+ the_parent_dom_element.append(@dom) # TODO make a method attach to allow subclasses to override if needed
225
253
  else
226
254
  old_element.replace_with(@dom)
255
+ old_element.replace_with(@dom)
227
256
  end
228
- observation_requests&.clone&.each do |keyword, event_listener_set|
257
+ observation_requests&.each do |keyword, event_listener_set|
229
258
  event_listener_set.each do |event_listener|
230
- observation_requests[keyword].delete(event_listener) # TODO look into the implications of this and if it's needed.
231
- handle_observation_request(keyword, &event_listener)
259
+ handle_observation_request(keyword, event_listener)
232
260
  end
233
261
  end
234
262
  children.each do |child|
235
263
  child.render
236
264
  end
237
265
  @rendered = true
238
- content_on_render_blocks.each { |content_block| content(&content_block) }
266
+ content_on_render_blocks.each { |content_block| content(&content_block) } unless skip_content_on_render_blocks?
239
267
  end
240
268
  alias redraw render
241
269
 
@@ -243,6 +271,10 @@ module Glimmer
243
271
  @content_on_render_blocks ||= []
244
272
  end
245
273
 
274
+ def skip_content_on_render_blocks?
275
+ false
276
+ end
277
+
246
278
  def add_content_on_render(&content_block)
247
279
  if rendered?
248
280
  content_block.call
@@ -251,7 +283,7 @@ module Glimmer
251
283
  end
252
284
  end
253
285
 
254
- def build_dom(layout=true)
286
+ def build_dom(layout: true)
255
287
  # TODO consider passing parent element instead and having table item include a table cell widget only for opal
256
288
  @dom = nil
257
289
  @dom = dom
@@ -273,6 +305,60 @@ module Glimmer
273
305
  end
274
306
 
275
307
  def default_observation_request_to_event_mapping
308
+ mouse_event_handler = -> (event_listener) {
309
+ -> (event) {
310
+ # TODO generalize this solution to all widgets that support key presses
311
+ # TODO support event.location once DOM3 is supported by opal-jquery
312
+ event.define_singleton_method(:button, &event.method(:which))
313
+ event.define_singleton_method(:count) {1} # TODO support double-click count of 2 in the future by using ondblclick
314
+ event.define_singleton_method(:x, &event.method(:page_x))
315
+ event.define_singleton_method(:y, &event.method(:page_y))
316
+ doit = true
317
+ event.define_singleton_method(:doit=) do |value|
318
+ doit = value
319
+ end
320
+ event.define_singleton_method(:doit) { doit }
321
+
322
+ if event.which == 1
323
+ # event.prevent # TODO consider if this is needed
324
+ event_listener.call(event)
325
+ end
326
+
327
+ # TODO Imlement doit properly for all different kinds of events
328
+ # unless doit
329
+ # event.prevent
330
+ # event.stop
331
+ # event.stop_immediate
332
+ # end
333
+ }
334
+ }
335
+ context_menu_handler = -> (event_listener) {
336
+ -> (event) {
337
+ # TODO generalize this solution to all widgets that support key presses
338
+ # TODO support event.location once DOM3 is supported by opal-jquery
339
+ event.define_singleton_method(:button, &event.method(:which))
340
+ event.define_singleton_method(:count) {1} # TODO support double-click count of 2 in the future by using ondblclick
341
+ event.define_singleton_method(:x, &event.method(:page_x))
342
+ event.define_singleton_method(:y, &event.method(:page_y))
343
+ doit = true
344
+ event.define_singleton_method(:doit=) do |value|
345
+ doit = value
346
+ end
347
+ event.define_singleton_method(:doit) { doit }
348
+
349
+ if event.which == 3
350
+ event.prevent
351
+ event_listener.call(event)
352
+ end
353
+
354
+ # TODO Imlement doit properly for all different kinds of events
355
+ # unless doit
356
+ # event.prevent
357
+ # event.stop
358
+ # event.stop_immediate
359
+ # end
360
+ }
361
+ }
276
362
  {
277
363
  'on_focus_gained' => {
278
364
  event: 'focus',
@@ -280,6 +366,191 @@ module Glimmer
280
366
  'on_focus_lost' => {
281
367
  event: 'blur',
282
368
  },
369
+ 'on_mouse_up' => [
370
+ {
371
+ event: 'mouseup',
372
+ event_handler: mouse_event_handler,
373
+ },
374
+ {
375
+ event: 'contextmenu',
376
+ event_handler: context_menu_handler,
377
+ },
378
+ ],
379
+ 'on_mouse_down' => [
380
+ {
381
+ event: 'mousedown',
382
+ event_handler: mouse_event_handler,
383
+ },
384
+ {
385
+ event: 'contextmenu',
386
+ event_handler: context_menu_handler,
387
+ },
388
+ ],
389
+ 'on_swt_mouseup' => [
390
+ {
391
+ event: 'mouseup',
392
+ event_handler: mouse_event_handler,
393
+ },
394
+ {
395
+ event: 'contextmenu',
396
+ event_handler: context_menu_handler,
397
+ },
398
+ ],
399
+ 'on_swt_mousedown' => [
400
+ {
401
+ event: 'mousedown',
402
+ event_handler: mouse_event_handler,
403
+ },
404
+ {
405
+ event: 'contextmenu',
406
+ event_handler: context_menu_handler,
407
+ },
408
+ ],
409
+ 'on_key_pressed' => {
410
+ event: 'keypress',
411
+ event_handler: -> (event_listener) {
412
+ -> (event) {
413
+ # TODO generalize this solution to all widgets that support key presses
414
+ # TODO support event.location once DOM3 is supported by opal-jquery
415
+ event.define_singleton_method(:keyCode) {event.which}
416
+ event.define_singleton_method(:key_code, &event.method(:keyCode))
417
+ event.define_singleton_method(:character) {event.which.chr}
418
+ event.define_singleton_method(:stateMask) do
419
+ state_mask = 0
420
+ state_mask |= SWTProxy[:alt] if event.alt_key
421
+ state_mask |= SWTProxy[:ctrl] if event.ctrl_key
422
+ state_mask |= SWTProxy[:shift] if event.shift_key
423
+ state_mask |= SWTProxy[:command] if event.meta_key
424
+ state_mask
425
+ end
426
+ event.define_singleton_method(:state_mask, &event.method(:stateMask))
427
+ doit = true
428
+ event.define_singleton_method(:doit=) do |value|
429
+ doit = value
430
+ end
431
+ event.define_singleton_method(:doit) { doit }
432
+ event_listener.call(event)
433
+
434
+ # TODO Fix doit false, it's not stopping input
435
+ unless doit
436
+ event.prevent
437
+ event.prevent_default
438
+ event.stop_propagation
439
+ event.stop_immediate_propagation
440
+ end
441
+
442
+ doit
443
+ }
444
+ } },
445
+ 'on_key_released' => {
446
+ event: 'keydown',
447
+ event_handler: -> (event_listener) {
448
+ -> (event) {
449
+ # TODO generalize this solution to all widgets that support key presses
450
+ # TODO support event.location once DOM3 is supported by opal-jquery
451
+ event.define_singleton_method(:keyCode) {event.which}
452
+ event.define_singleton_method(:key_code, &event.method(:keyCode))
453
+ event.define_singleton_method(:character) {event.which.chr}
454
+ event.define_singleton_method(:stateMask) do
455
+ state_mask = 0
456
+ state_mask |= SWTProxy[:alt] if event.alt_key
457
+ state_mask |= SWTProxy[:ctrl] if event.ctrl_key
458
+ state_mask |= SWTProxy[:shift] if event.shift_key
459
+ state_mask |= SWTProxy[:command] if event.meta_key
460
+ state_mask
461
+ end
462
+ event.define_singleton_method(:state_mask, &event.method(:stateMask))
463
+ doit = true
464
+ event.define_singleton_method(:doit=) do |value|
465
+ doit = value
466
+ end
467
+ event.define_singleton_method(:doit) { doit }
468
+ event_listener.call(event)
469
+
470
+ # TODO Fix doit false, it's not stopping input
471
+ unless doit
472
+ event.prevent
473
+ event.prevent_default
474
+ event.stop_propagation
475
+ event.stop_immediate_propagation
476
+ end
477
+
478
+ doit
479
+ }
480
+ } },
481
+ 'on_swt_keydown' => {
482
+ event: 'keypress',
483
+ event_handler: -> (event_listener) {
484
+ -> (event) {
485
+ # TODO generalize this solution to all widgets that support key presses
486
+ # TODO support event.location once DOM3 is supported by opal-jquery
487
+ event.define_singleton_method(:keyCode) {event.which}
488
+ event.define_singleton_method(:key_code, &event.method(:keyCode))
489
+ event.define_singleton_method(:character) {event.which.chr}
490
+ event.define_singleton_method(:stateMask) do
491
+ state_mask = 0
492
+ state_mask |= SWTProxy[:alt] if event.alt_key
493
+ state_mask |= SWTProxy[:ctrl] if event.ctrl_key
494
+ state_mask |= SWTProxy[:shift] if event.shift_key
495
+ state_mask |= SWTProxy[:command] if event.meta_key
496
+ state_mask
497
+ end
498
+ event.define_singleton_method(:state_mask, &event.method(:stateMask))
499
+ doit = true
500
+ event.define_singleton_method(:doit=) do |value|
501
+ doit = value
502
+ end
503
+ event.define_singleton_method(:doit) { doit }
504
+ event_listener.call(event)
505
+
506
+ # TODO Fix doit false, it's not stopping input
507
+ unless doit
508
+ event.prevent
509
+ event.prevent_default
510
+ event.stop_propagation
511
+ event.stop_immediate_propagation
512
+ end
513
+
514
+ doit
515
+ }
516
+ } },
517
+ 'on_swt_keyup' => {
518
+ event: 'keydown',
519
+ event_handler: -> (event_listener) {
520
+ -> (event) {
521
+ # TODO generalize this solution to all widgets that support key presses
522
+ # TODO support event.location once DOM3 is supported by opal-jquery
523
+ event.define_singleton_method(:keyCode) {event.which}
524
+ event.define_singleton_method(:key_code, &event.method(:keyCode))
525
+ event.define_singleton_method(:character) {event.which.chr}
526
+ event.define_singleton_method(:stateMask) do
527
+ state_mask = 0
528
+ state_mask |= SWTProxy[:alt] if event.alt_key
529
+ state_mask |= SWTProxy[:ctrl] if event.ctrl_key
530
+ state_mask |= SWTProxy[:shift] if event.shift_key
531
+ state_mask |= SWTProxy[:command] if event.meta_key
532
+ state_mask
533
+ end
534
+ event.define_singleton_method(:state_mask, &event.method(:stateMask))
535
+ doit = true
536
+ event.define_singleton_method(:doit=) do |value|
537
+ doit = value
538
+ end
539
+ event.define_singleton_method(:doit) { doit }
540
+ event_listener.call(event)
541
+
542
+ # TODO Fix doit false, it's not stopping input
543
+ unless doit
544
+ event.prevent
545
+ event.prevent_default
546
+ event.stop_propagation
547
+ event.stop_immediate_propagation
548
+ end
549
+
550
+ doit
551
+ }
552
+ }
553
+ },
283
554
  }
284
555
  end
285
556
 
@@ -354,6 +625,14 @@ module Glimmer
354
625
  Document.find(listener_path)
355
626
  end
356
627
 
628
+ def observation_requests
629
+ @observation_requests ||= {}
630
+ end
631
+
632
+ def event_listener_proxies
633
+ @event_listener_proxies ||= []
634
+ end
635
+
357
636
  def can_handle_observation_request?(observation_request)
358
637
  # TODO sort this out for Opal
359
638
  observation_request = observation_request.to_s
@@ -367,32 +646,40 @@ module Glimmer
367
646
  end
368
647
  end
369
648
 
370
- def observation_requests
371
- @observation_requests ||= {}
372
- end
373
-
374
- def handle_observation_request(keyword, &event_listener)
649
+ def handle_observation_request(keyword, original_event_listener)
375
650
  return unless effective_observation_request_to_event_mapping.keys.include?(keyword)
376
651
  event = nil
377
652
  delegate = nil
378
- [effective_observation_request_to_event_mapping[keyword]].flatten.each do |mapping|
653
+ effective_observation_request_to_event_mapping[keyword].to_collection.each do |mapping|
379
654
  observation_requests[keyword] ||= Set.new
380
- observation_requests[keyword] << event_listener
655
+ observation_requests[keyword] << original_event_listener
381
656
  event = mapping[:event]
382
657
  event_handler = mapping[:event_handler]
383
658
  event_element_css_selector = mapping[:event_element_css_selector]
384
- potential_event_listener = event_handler&.call(event_listener)
385
- event_listener = potential_event_listener || event_listener
659
+ potential_event_listener = event_handler&.call(original_event_listener)
660
+ event_listener = potential_event_listener || original_event_listener
386
661
  async_event_listener = lambda do |event|
387
- Async::Task.new do
662
+ # TODO look into the issue with using async::task.new here. maybe put it in event listener (like not being able to call preventDefault or return false successfully )
663
+ # maybe consider pushing inside the widget classes instead where needed only or implement universal doit support correctly to bypass this issue
664
+ # Async::Task.new do
388
665
  event_listener.call(event)
389
- end
666
+ # end
390
667
  end
391
668
  the_listener_dom_element = event_element_css_selector ? Element[event_element_css_selector] : listener_dom_element
392
- delegate = the_listener_dom_element.on(event, &async_event_listener)
669
+ unless the_listener_dom_element.empty?
670
+ the_listener_dom_element.on(event, &async_event_listener)
671
+ # TODO ensure uniqueness of insertion (perhaps adding equals/hash method to event listener proxy)
672
+
673
+ event_listener_proxies << EventListenerProxy.new(element_proxy: self, selector: selector, dom_element: the_listener_dom_element, event: event, listener: async_event_listener, original_event_listener: original_event_listener)
674
+ end
675
+ end
676
+ end
677
+
678
+ def remove_event_listener_proxies
679
+ event_listener_proxies.each do |event_listener_proxy|
680
+ event_listener_proxy.unregister
393
681
  end
394
- # TODO update code below for new WidgetProxy API
395
- EventListenerProxy.new(element_proxy: self, event: event, selector: selector, delegate: delegate)
682
+ event_listener_proxies.clear
396
683
  end
397
684
 
398
685
  def add_observer(observer, property_name)
@@ -410,12 +697,17 @@ module Glimmer
410
697
 
411
698
  def method_missing(method, *args, &block)
412
699
  if method.to_s.start_with?('on_')
413
- handle_observation_request(method, &block)
700
+ handle_observation_request(method, block)
414
701
  else
415
702
  super(method, *args, &block)
416
703
  end
417
704
  end
418
705
 
706
+ def swt_widget
707
+ # only added for compatibility/adaptibility with Glimmer DSL for SWT
708
+ self
709
+ end
710
+
419
711
  def apply_property_type_converters(attribute_name, args)
420
712
  if args.count == 1
421
713
  value = args.first
@@ -608,6 +900,8 @@ require 'glimmer/swt/date_time_proxy'
608
900
  require 'glimmer/swt/group_proxy'
609
901
  require 'glimmer/swt/label_proxy'
610
902
  require 'glimmer/swt/list_proxy'
903
+ require 'glimmer/swt/menu_item_proxy'
904
+ require 'glimmer/swt/menu_proxy'
611
905
  require 'glimmer/swt/radio_proxy'
612
906
  require 'glimmer/swt/tab_folder_proxy'
613
907
  require 'glimmer/swt/tab_item_proxy'