glimmer-dsl-opal 0.7.3 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +847 -158
  4. data/VERSION +1 -1
  5. data/lib/glimmer-dsl-opal.rb +15 -3
  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 +25 -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-dsl-opal/samples/hello/hello_world.rb +3 -3
  21. data/lib/glimmer/config/opal_logger.rb +16 -0
  22. data/lib/glimmer/data_binding/observable_element.rb +1 -1
  23. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +6 -0
  24. data/lib/glimmer/dsl/opal/dsl.rb +2 -0
  25. data/lib/glimmer/dsl/opal/menu_bar_expression.rb +54 -0
  26. data/lib/glimmer/dsl/opal/menu_expression.rb +61 -0
  27. data/lib/glimmer/dsl/opal/shell_expression.rb +0 -4
  28. data/lib/glimmer/dsl/opal/widget_expression.rb +3 -2
  29. data/lib/glimmer/dsl/opal/widget_listener_expression.rb +2 -2
  30. data/lib/glimmer/swt/custom/checkbox_group.rb +2 -2
  31. data/lib/glimmer/swt/custom/radio_group.rb +2 -2
  32. data/lib/glimmer/swt/date_time_proxy.rb +1 -1
  33. data/lib/glimmer/swt/display_proxy.rb +5 -4
  34. data/lib/glimmer/swt/event_listener_proxy.rb +14 -4
  35. data/lib/glimmer/swt/grid_layout_proxy.rb +21 -12
  36. data/lib/glimmer/swt/label_proxy.rb +17 -6
  37. data/lib/glimmer/swt/latest_message_box_proxy.rb +20 -0
  38. data/lib/glimmer/swt/latest_shell_proxy.rb +20 -0
  39. data/lib/glimmer/swt/layout_data_proxy.rb +6 -6
  40. data/lib/glimmer/swt/list_proxy.rb +15 -0
  41. data/lib/glimmer/swt/menu_item_proxy.rb +174 -0
  42. data/lib/glimmer/swt/menu_proxy.rb +273 -0
  43. data/lib/glimmer/swt/message_box_proxy.rb +57 -72
  44. data/lib/glimmer/swt/property_owner.rb +2 -0
  45. data/lib/glimmer/swt/radio_proxy.rb +1 -1
  46. data/lib/glimmer/swt/shell_proxy.rb +34 -189
  47. data/lib/glimmer/swt/tab_folder_proxy.rb +43 -0
  48. data/lib/glimmer/swt/table_column_proxy.rb +3 -2
  49. data/lib/glimmer/swt/table_editor.rb +1 -1
  50. data/lib/glimmer/swt/table_item_proxy.rb +7 -5
  51. data/lib/glimmer/swt/table_proxy.rb +10 -0
  52. data/lib/glimmer/swt/widget_proxy.rb +325 -31
  53. data/lib/glimmer/ui/custom_shell.rb +9 -7
  54. data/lib/glimmer/ui/custom_widget.rb +3 -3
  55. metadata +26 -6
@@ -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
@@ -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,6 +164,7 @@ 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
 
@@ -160,6 +177,7 @@ module Glimmer
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
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'