glimmer-dsl-opal 0.10.1 → 0.13.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 (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