glimmer-dsl-opal 0.10.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +34 -27
  4. data/VERSION +1 -1
  5. data/lib/display.rb +3 -0
  6. data/lib/glimmer-dsl-opal.rb +1 -1
  7. data/lib/glimmer-dsl-opal/ext/glimmer/dsl/engine.rb +1 -1
  8. data/lib/glimmer-dsl-opal/samples/elaborate/contact_manager.rb +15 -13
  9. data/lib/glimmer-dsl-opal/samples/elaborate/login.rb +55 -28
  10. data/lib/glimmer-dsl-opal/samples/elaborate/tic_tac_toe.rb +2 -2
  11. data/lib/glimmer-dsl-opal/samples/hello/hello_button.rb +1 -1
  12. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox.rb +16 -14
  13. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox_group.rb +14 -9
  14. data/lib/glimmer-dsl-opal/samples/hello/hello_combo.rb +1 -1
  15. data/lib/glimmer-dsl-opal/samples/hello/hello_computed.rb +5 -5
  16. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_shell.rb +16 -12
  17. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_widget.rb +1 -1
  18. data/lib/glimmer-dsl-opal/samples/hello/hello_date_time.rb +4 -4
  19. data/lib/glimmer-dsl-opal/samples/hello/hello_group.rb +6 -6
  20. data/lib/glimmer-dsl-opal/samples/hello/hello_list_multi_selection.rb +1 -1
  21. data/lib/glimmer-dsl-opal/samples/hello/hello_list_single_selection.rb +1 -1
  22. data/lib/glimmer-dsl-opal/samples/hello/hello_radio.rb +18 -16
  23. data/lib/glimmer-dsl-opal/samples/hello/hello_radio_group.rb +17 -12
  24. data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +4 -4
  25. data/lib/glimmer/data_binding/table_items_binding.rb +3 -2
  26. data/lib/glimmer/dsl/opal/bind_expression.rb +24 -25
  27. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +8 -8
  28. data/lib/glimmer/dsl/opal/dsl.rb +4 -0
  29. data/lib/glimmer/dsl/opal/menu_expression.rb +1 -1
  30. data/lib/glimmer/dsl/opal/property_expression.rb +2 -1
  31. data/lib/glimmer/dsl/opal/shape_expression.rb +26 -0
  32. data/lib/glimmer/dsl/opal/shell_expression.rb +1 -1
  33. data/lib/glimmer/dsl/opal/shine_data_binding_expression.rb +49 -0
  34. data/lib/glimmer/dsl/opal/table_items_data_binding_expression.rb +2 -2
  35. data/lib/glimmer/dsl/opal/widget_expression.rb +1 -1
  36. data/lib/glimmer/swt/combo_proxy.rb +1 -0
  37. data/lib/glimmer/swt/composite_proxy.rb +2 -0
  38. data/lib/glimmer/swt/dialog_proxy.rb +2 -5
  39. data/lib/glimmer/swt/display_proxy.rb +104 -10
  40. data/lib/glimmer/swt/grid_layout_proxy.rb +17 -17
  41. data/lib/glimmer/swt/layout_proxy.rb +23 -3
  42. data/lib/glimmer/swt/message_box_proxy.rb +4 -4
  43. data/lib/glimmer/swt/row_layout_proxy.rb +12 -3
  44. data/lib/glimmer/swt/table_proxy.rb +19 -3
  45. data/lib/glimmer/swt/widget_proxy.rb +3 -4
  46. data/lib/glimmer/ui/custom_shell.rb +22 -5
  47. data/lib/glimmer/ui/custom_widget.rb +11 -2
  48. data/lib/glimmer/util/proc_tracker.rb +5 -3
  49. metadata +11 -9
@@ -19,12 +19,15 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
+ # This sample demonstrates the use of a `radio_group` in Glimmer, which provides terser syntax
23
+ # than HelloRadio for representing multiple radio buttons by relying on data-binding to
24
+ # automatically spawn the `radio` widgets based on available options on the model.
22
25
  class HelloRadioGroup
23
26
  class Person
24
27
  attr_accessor :gender, :age_group
25
28
 
26
29
  def initialize
27
- reset
30
+ reset!
28
31
  end
29
32
 
30
33
  def gender_options
@@ -35,17 +38,19 @@ class HelloRadioGroup
35
38
  ['Child', 'Teen', 'Adult', 'Senior']
36
39
  end
37
40
 
38
- def reset
41
+ def reset!
39
42
  self.gender = nil
40
43
  self.age_group = 'Adult'
41
44
  end
42
45
  end
43
46
 
44
- include Glimmer
47
+ include Glimmer::UI::CustomShell
45
48
 
46
- def launch
47
- person = Person.new
48
-
49
+ before_body {
50
+ @person = Person.new
51
+ }
52
+
53
+ body {
49
54
  shell {
50
55
  text 'Hello, Radio Group!'
51
56
  row_layout :vertical
@@ -57,7 +62,7 @@ class HelloRadioGroup
57
62
 
58
63
  radio_group {
59
64
  row_layout :horizontal
60
- selection bind(person, :gender)
65
+ selection <=> [@person, :gender]
61
66
  }
62
67
 
63
68
  label {
@@ -67,18 +72,18 @@ class HelloRadioGroup
67
72
 
68
73
  radio_group {
69
74
  row_layout :horizontal
70
- selection bind(person, :age_group)
75
+ selection <=> [@person, :age_group]
71
76
  }
72
77
 
73
78
  button {
74
79
  text 'Reset'
75
80
 
76
81
  on_widget_selected do
77
- person.reset
82
+ @person.reset!
78
83
  end
79
84
  }
80
- }.open
81
- end
85
+ }
86
+ }
82
87
  end
83
88
 
84
- HelloRadioGroup.new.launch
89
+ HelloRadioGroup.launch
@@ -196,7 +196,7 @@ class HelloTable
196
196
 
197
197
  combo(:read_only) {
198
198
  layout_data :center, :center, true, false
199
- selection bind(BaseballGame, :playoff_type)
199
+ selection <=> [BaseballGame, :playoff_type]
200
200
  font height: 16
201
201
  }
202
202
 
@@ -237,10 +237,10 @@ class HelloTable
237
237
  }
238
238
 
239
239
  # Data-bind table items (rows) to a model collection property, specifying column properties ordering per nested model
240
- items bind(BaseballGame, :schedule), column_properties(:game_date, :game_time, :ballpark, :home_team, :away_team, :promotion)
240
+ items <=> [BaseballGame, :schedule, column_attributes: [:game_date, :game_time, :ballpark, :home_team, :away_team, :promotion]]
241
241
 
242
242
  # Data-bind table selection
243
- selection bind(BaseballGame, :selected_game)
243
+ selection <=> [BaseballGame, :selected_game]
244
244
 
245
245
  # Default initial sort property
246
246
  sort_property :date
@@ -263,7 +263,7 @@ class HelloTable
263
263
  text 'Book Selected Game'
264
264
  layout_data :center, :center, true, false
265
265
  font height: 16
266
- enabled bind(BaseballGame, :selected_game)
266
+ enabled <=> [BaseballGame, :selected_game]
267
267
 
268
268
  on_widget_selected {
269
269
  book_selected_game
@@ -11,11 +11,12 @@ module Glimmer
11
11
  include DataBinding::Observable
12
12
  include DataBinding::Observer
13
13
 
14
- def initialize(parent, model_binding, column_properties)
14
+ def initialize(parent, model_binding, column_properties = nil)
15
15
  @last_populated_model_collection = nil
16
16
  @table = parent
17
17
  @model_binding = model_binding
18
- @column_properties = column_properties
18
+ @column_properties = model_binding.binding_options[:column_attributes] || model_binding.binding_options[:column_properties] || column_properties # TODO
19
+ @table.editable = false if model_binding.binding_options[:read_only]
19
20
  @table.data = @model_binding
20
21
  ##@table.on_widget_disposed do |dispose_event| # doesn't seem needed within Opal
21
22
  ## unregister_all_observables
@@ -1,36 +1,35 @@
1
+ # Copyright (c) 2020-2021 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/dsl/static_expression'
23
+ require 'glimmer/dsl/bind_expression'
2
24
  require 'glimmer/data_binding/model_binding'
3
25
 
4
26
  module Glimmer
5
27
  module DSL
6
28
  module Opal
7
29
  # Responsible for setting up the return value of the bind keyword (command symbol)
8
- # as a ModelBinding. It is then used by another command handler like
9
- # DataBindingCommandHandler for text and selection properties on Text and Spinner
10
- # or TableItemsDataBindingCommandHandler for items in a Table
30
+ # as a ModelBinding. It is then used by other data-binding expressions
11
31
  class BindExpression < StaticExpression
12
- def can_interpret?(parent, keyword, *args, &block)
13
- (
14
- keyword == 'bind' and
15
- (
16
- (
17
- (args.size == 2) and
18
- textual?(args[1])
19
- ) ||
20
- (
21
- (args.size == 3) and
22
- textual?(args[1]) and
23
- (args[2].is_a?(Hash))
24
- )
25
- )
26
- )
27
- end
28
-
29
- def interpret(parent, keyword, *args, &block)
30
- binding_options = args[2] || {}
31
- binding_options[:on_read] = binding_options.delete(:on_read) || binding_options.delete('on_read') || block
32
- DataBinding::ModelBinding.new(args[0], args[1].to_s, binding_options)
33
- end
32
+ include Glimmer::DSL::BindExpression
34
33
  end
35
34
  end
36
35
  end
@@ -51,7 +51,7 @@ module Glimmer
51
51
  custom_widget_class = UI::CustomWidget.for(keyword)
52
52
  # TODO clean code by extracting methods into CustomShell
53
53
  if !Glimmer::UI::CustomShell.requested? && custom_widget_class&.ancestors&.to_a.include?(Glimmer::UI::CustomShell)
54
- if Glimmer::SWT::DisplayProxy.instance.shells.empty?
54
+ if Glimmer::SWT::DisplayProxy.instance.shells.empty? || Glimmer::SWT::DisplayProxy.open_custom_shells_in_current_window?
55
55
  custom_widget_class.new(parent, *args, {}, &block)
56
56
  else
57
57
  options = args.last.is_a?(Hash) ? args.pop : {}
@@ -80,15 +80,15 @@ module Glimmer
80
80
  end
81
81
  end
82
82
 
83
- def add_content(parent, &content)
84
- content.call(parent) if parent.is_a?(Glimmer::SWT::ShellProxy) || parent.is_a?(Glimmer::UI::CustomShell)
85
- end
86
-
83
+ # TODO delete if no longer needed
84
+ # def add_content(parent, &content)
85
+ # content.call(parent) if parent.is_a?(Glimmer::SWT::ShellProxy) || parent.is_a?(Glimmer::UI::CustomShell)
86
+ # end
87
87
 
88
- def add_content(parent, &block)
88
+ def add_content(parent, keyword, *args, &block)
89
89
  return unless parent.is_a?(Glimmer::UI::CustomWidget)
90
- # TODO consider avoiding source_location
91
- if block.source_location == parent.content&.__getobj__.source_location
90
+ # TODO consider avoiding source_location since it does not work in Opal
91
+ if block.source_location && (block.source_location == parent.content&.__getobj__&.source_location)
92
92
  parent.content.call(parent) unless parent.content.called?
93
93
  else
94
94
  super
@@ -28,6 +28,8 @@ require 'glimmer/dsl/opal/checkbox_group_selection_data_binding_expression'
28
28
  require 'glimmer/dsl/opal/block_property_expression'
29
29
  require 'glimmer/dsl/opal/menu_expression'
30
30
  require 'glimmer/dsl/opal/dialog_expression'
31
+ require 'glimmer/dsl/opal/shape_expression'
32
+ require 'glimmer/dsl/opal/shine_data_binding_expression'
31
33
 
32
34
  module Glimmer
33
35
  module DSL
@@ -47,6 +49,8 @@ module Glimmer
47
49
  layout
48
50
  block_property
49
51
  property
52
+ shine_data_binding
53
+ shape
50
54
  widget
51
55
  ]
52
56
  )
@@ -47,7 +47,7 @@ module Glimmer
47
47
  Glimmer::SWT::MenuProxy.new(parent, args)
48
48
  end
49
49
 
50
- def add_content(parent, &block)
50
+ def add_content(parent, keyword, *args, &block)
51
51
  super(parent, &block)
52
52
  parent.post_add_content
53
53
  end
@@ -6,8 +6,9 @@ module Glimmer
6
6
  class PropertyExpression < StaticExpression
7
7
  include TopLevelExpression
8
8
 
9
- def can_interpret?(parent, keyword, *args, &block)
9
+ def can_interpret?(parent, keyword, *args, &block)
10
10
  parent and
11
+ (!args.empty?) and
11
12
  parent.respond_to?(:set_attribute) and
12
13
  parent.respond_to?(keyword, *args) and
13
14
  keyword and
@@ -0,0 +1,26 @@
1
+ require 'glimmer/dsl/expression'
2
+ require 'glimmer/dsl/parent_expression'
3
+
4
+ module Glimmer
5
+ module DSL
6
+ module Opal
7
+ class ShapeExpression < Expression
8
+ include ParentExpression
9
+
10
+ INCLUDED_KEYWORDS = %w[rectangle polygon]
11
+
12
+ def can_interpret?(parent, keyword, *args, &block)
13
+ INCLUDED_KEYWORDS.include?(keyword)
14
+ end
15
+
16
+ def interpret(parent, keyword, *args, &block)
17
+ # TODO
18
+ end
19
+
20
+ def add_content(parent, keyword, *args, &block)
21
+ # TODO
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -27,7 +27,7 @@ module Glimmer
27
27
  end
28
28
  end
29
29
 
30
- def add_content(parent, &block)
30
+ def add_content(parent, keyword, *args, &block)
31
31
  super(parent, &block)
32
32
  parent.post_add_content
33
33
  end
@@ -0,0 +1,49 @@
1
+ # Copyright (c) 2020-2021 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
+
22
+ require 'glimmer/dsl/expression'
23
+ require 'glimmer/data_binding/model_binding'
24
+ require 'glimmer/swt/table_proxy'
25
+ require 'glimmer/data_binding/shine'
26
+
27
+ module Glimmer
28
+ module DSL
29
+ module Opal
30
+ class ShineDataBindingExpression < Expression
31
+ def can_interpret?(parent, keyword, *args, &block)
32
+ args.size == 0 and
33
+ block.nil? and
34
+ (
35
+ (parent.respond_to?(:set_attribute) and parent.respond_to?(keyword)) or
36
+ (parent.is_a?(Glimmer::SWT::TableProxy)) # TODO support tree element
37
+ )
38
+ # TODO support canvas elements
39
+ # and
40
+ # !(parent.respond_to?(:swt_widget) && parent.swt_widget.class == org.eclipse.swt.widgets.Canvas && keyword == 'image')
41
+ end
42
+
43
+ def interpret(parent, keyword, *args, &block)
44
+ Glimmer::DataBinding::Shine.new(parent, keyword)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -12,10 +12,10 @@ module Glimmer
12
12
  keyword == "items" and
13
13
  block.nil? and
14
14
  parent.is_a?(Glimmer::SWT::TableProxy) and
15
- args.size == 2 and
15
+ args.size.between?(1, 2) and
16
16
  args[0].is_a?(DataBinding::ModelBinding) and
17
17
  args[0].evaluate_property.is_a?(Array) and
18
- args[1].is_a?(Array)
18
+ (args[1].nil? or args[1].is_a?(Array))
19
19
  end
20
20
 
21
21
  def interpret(parent, keyword, *args, &block)
@@ -20,7 +20,7 @@ module Glimmer
20
20
  Glimmer::SWT::WidgetProxy.for(keyword, parent, args, block)
21
21
  end
22
22
 
23
- def add_content(parent, &block)
23
+ def add_content(parent, keyword, *args, &block)
24
24
  if parent.rendered? || parent.skip_content_on_render_blocks?
25
25
  super(parent, &block)
26
26
  parent.post_add_content
@@ -6,6 +6,7 @@ module Glimmer
6
6
  class ComboProxy < WidgetProxy
7
7
  include Glimmer::DataBinding::ObservableElement
8
8
  attr_reader :text, :items
9
+ attr_accessor :selection # virtual attribute just to pass the shine data-binding test (TODO THINK OF A BETTER WAY OF HANDLING THIS)
9
10
 
10
11
  def initialize(parent, args, block)
11
12
  super(parent, args, block)
@@ -44,6 +44,8 @@ module Glimmer
44
44
 
45
45
  end
46
46
 
47
+ CanvasProxy = CompositeProxy # TODO implement fully eventually
48
+
47
49
  end
48
50
 
49
51
  end
@@ -68,9 +68,6 @@ module Glimmer
68
68
  @block = block
69
69
  @children = Set.new
70
70
  @enabled = true
71
- # on_widget_selected {
72
- # hide
73
- # }
74
71
  DisplayProxy.instance.opened_dialogs.last&.suspend_event_handling
75
72
  DisplayProxy.instance.dialogs << self
76
73
  @parent.post_initialize_child(self)
@@ -90,7 +87,7 @@ module Glimmer
90
87
  end
91
88
 
92
89
  def open
93
- owned_proc = Glimmer::Util::ProcTracker.new(owner: self) {
90
+ owned_proc = Glimmer::Util::ProcTracker.new(owner: self, invoked_from: :open) {
94
91
  shell.open(async: false) unless shell.open?
95
92
  unless @init
96
93
  dom_element.remove_class('hide')
@@ -141,7 +138,7 @@ module Glimmer
141
138
 
142
139
 
143
140
  def content(&block)
144
- Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Opal::DialogExpression.new, &block)
141
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Opal::DialogExpression.new, 'dialog', &block)
145
142
  end
146
143
 
147
144
  def path
@@ -7,6 +7,9 @@ module Glimmer
7
7
  def instance
8
8
  @instance ||= new
9
9
  end
10
+
11
+ attr_accessor :open_custom_shells_in_current_window
12
+ alias open_custom_shells_in_current_window? open_custom_shells_in_current_window
10
13
  end
11
14
 
12
15
  def initialize
@@ -62,19 +65,22 @@ module Glimmer
62
65
  # No rendering as body is rendered as part of ShellProxy.. this class only serves as an SWT Display utility
63
66
  end
64
67
 
68
+ def beep
69
+ # TODO (simulate beep from SWT display flashing the screen and making a noise if possible)
70
+ end
71
+
65
72
  def async_exec(proc_tracker = nil, &block)
66
73
  block = proc_tracker unless proc_tracker.nil?
67
- can_open_modal = !modal_open?
68
- return block.call if !can_open_modal && dialog_open? && block.respond_to?(:owner) && !block.owner.nil? && block.owner.is_a?(DialogProxy) && opened_dialogs.last == WidgetProxy.widget_handling_listener&.dialog_ancestor
69
- return block.call if !can_open_modal && block.respond_to?(:owner) && !block.owner.nil? && block.owner.is_a?(MessageBoxProxy)
70
- executer = lambda do
71
- if !modal_open?
72
- block.call
73
- else
74
- Async::Task.new(delay: 100, &executer)
75
- end
74
+ queue = nil # general queue
75
+ if !proc_tracker.nil? && proc_tracker.invoked_from.to_s == 'open' && modal_open? &&
76
+ (
77
+ proc_tracker.owner.is_a?(MessageBoxProxy) ||
78
+ (dialog_open? && proc_tracker.owner.is_a?(DialogProxy) && opened_dialogs.last == WidgetProxy.widget_handling_listener&.dialog_ancestor)
79
+ )
80
+ queue = WidgetProxy.widget_handling_listener
76
81
  end
77
- Async::Task.new(delay: 1, &executer)
82
+ return block.call if !modal_open?
83
+ schedule_async_exec(block, queue)
78
84
  end
79
85
  # sync_exec kept for API compatibility reasons
80
86
  alias sync_exec async_exec
@@ -89,7 +95,34 @@ module Glimmer
89
95
  event.singleton_class.define_method(:character) do
90
96
  which || key_code
91
97
  end
98
+ event.define_singleton_method(:keyCode) {event.which}
99
+ event.define_singleton_method(:key_code, &event.method(:keyCode))
100
+ event.define_singleton_method(:character) {event.which.chr}
101
+ event.define_singleton_method(:stateMask) do
102
+ state_mask = 0
103
+ state_mask |= SWTProxy[:alt] if event.alt_key
104
+ state_mask |= SWTProxy[:ctrl] if event.ctrl_key
105
+ state_mask |= SWTProxy[:shift] if event.shift_key
106
+ state_mask |= SWTProxy[:command] if event.meta_key
107
+ state_mask
108
+ end
109
+ event.define_singleton_method(:state_mask, &event.method(:stateMask))
110
+ doit = true
111
+ event.define_singleton_method(:doit=) do |value|
112
+ doit = value
113
+ end
114
+ event.define_singleton_method(:doit) { doit }
92
115
  event_listener.call(event)
116
+
117
+ # TODO Fix doit false, it's not stopping input
118
+ unless doit
119
+ event.prevent
120
+ event.prevent_default
121
+ event.stop_propagation
122
+ event.stop_immediate_propagation
123
+ end
124
+
125
+ doit
93
126
  }
94
127
  }
95
128
  },
@@ -100,13 +133,74 @@ module Glimmer
100
133
  event.singleton_class.define_method(:character) do
101
134
  which || key_code
102
135
  end
136
+ event.define_singleton_method(:keyCode) {event.which}
137
+ event.define_singleton_method(:key_code, &event.method(:keyCode))
138
+ event.define_singleton_method(:character) {event.which.chr}
139
+ event.define_singleton_method(:stateMask) do
140
+ state_mask = 0
141
+ state_mask |= SWTProxy[:alt] if event.alt_key
142
+ state_mask |= SWTProxy[:ctrl] if event.ctrl_key
143
+ state_mask |= SWTProxy[:shift] if event.shift_key
144
+ state_mask |= SWTProxy[:command] if event.meta_key
145
+ state_mask
146
+ end
147
+ event.define_singleton_method(:state_mask, &event.method(:stateMask))
148
+ doit = true
149
+ event.define_singleton_method(:doit=) do |value|
150
+ doit = value
151
+ end
152
+ event.define_singleton_method(:doit) { doit }
103
153
  event_listener.call(event) if event.key_code != 13 && (event.key_code == 127 || event.key_code <= 31)
154
+
155
+ # TODO Fix doit false, it's not stopping input
156
+ unless doit
157
+ event.prevent
158
+ event.prevent_default
159
+ event.stop_propagation
160
+ event.stop_immediate_propagation
161
+ end
162
+
163
+ doit
104
164
  }
105
165
  }
106
166
  }
107
167
  ]
108
168
  }
109
169
  end
170
+
171
+ private
172
+
173
+ def async_exec_queues
174
+ @async_exec_queues ||= {}
175
+ end
176
+
177
+ def async_exec_queue(widget_handling_listener = nil)
178
+ async_exec_queues[widget_handling_listener] ||= []
179
+ end
180
+
181
+ def no_widget_handling_listener_work?
182
+ async_exec_queues.reject {|key, value| key.nil?}.values.reduce(:+).to_a.empty?
183
+ end
184
+
185
+ def schedule_async_exec(block, queue)
186
+ async_exec_queue(queue).unshift(block)
187
+
188
+ # TODO consider the need for locking to avoid race conditions (rare or impossible case)
189
+ if async_exec_queue(queue).size == 1
190
+ executer = lambda do
191
+ # queue could be a widget handling listener queue
192
+ # TODO see if there are more intricate cases of opening a dialog from a widget listener handler
193
+ if !message_box_open? && (!dialog_open? || queue&.dialog_ancestor == opened_dialogs.last) && ((!queue.nil? && async_exec_queues.keys.last == queue) || no_widget_handling_listener_work?)
194
+ block = async_exec_queue(queue).pop
195
+ block&.call
196
+ Async::Task.new(delay: 1, &executer) if async_exec_queue(queue).any?
197
+ else
198
+ Async::Task.new(delay: 100, &executer)
199
+ end
200
+ end
201
+ Async::Task.new(delay: 1, &executer)
202
+ end
203
+ end
110
204
  end
111
205
  end
112
206
  end