glimmer-dsl-opal 0.10.0 → 0.12.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +84 -22
  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 +5 -2
  8. data/lib/glimmer-dsl-opal/samples/elaborate/contact_manager.rb +17 -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 +4 -4
  13. data/lib/glimmer-dsl-opal/samples/hello/hello_combo.rb +1 -1
  14. data/lib/glimmer-dsl-opal/samples/hello/hello_computed.rb +5 -5
  15. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_shell.rb +1 -1
  16. data/lib/glimmer-dsl-opal/samples/hello/hello_date_time.rb +4 -4
  17. data/lib/glimmer-dsl-opal/samples/hello/hello_group.rb +6 -6
  18. data/lib/glimmer-dsl-opal/samples/hello/hello_list_multi_selection.rb +1 -1
  19. data/lib/glimmer-dsl-opal/samples/hello/hello_list_single_selection.rb +1 -1
  20. data/lib/glimmer-dsl-opal/samples/hello/hello_radio.rb +6 -6
  21. data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +2 -2
  22. data/lib/glimmer/data_binding/table_items_binding.rb +3 -2
  23. data/lib/glimmer/dsl/opal/bind_expression.rb +24 -25
  24. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +6 -6
  25. data/lib/glimmer/dsl/opal/dsl.rb +4 -0
  26. data/lib/glimmer/dsl/opal/menu_expression.rb +1 -1
  27. data/lib/glimmer/dsl/opal/message_box_expression.rb +1 -1
  28. data/lib/glimmer/dsl/opal/property_expression.rb +2 -1
  29. data/lib/glimmer/dsl/opal/shape_expression.rb +26 -0
  30. data/lib/glimmer/dsl/opal/shell_expression.rb +1 -1
  31. data/lib/glimmer/dsl/opal/shine_data_binding_expression.rb +49 -0
  32. data/lib/glimmer/dsl/opal/table_items_data_binding_expression.rb +2 -2
  33. data/lib/glimmer/dsl/opal/widget_expression.rb +1 -1
  34. data/lib/glimmer/swt/combo_proxy.rb +1 -0
  35. data/lib/glimmer/swt/composite_proxy.rb +18 -2
  36. data/lib/glimmer/swt/dialog_proxy.rb +47 -32
  37. data/lib/glimmer/swt/display_proxy.rb +128 -9
  38. data/lib/glimmer/swt/grid_layout_proxy.rb +28 -33
  39. data/lib/glimmer/swt/latest_dialog_proxy.rb +3 -1
  40. data/lib/glimmer/swt/latest_message_box_proxy.rb +3 -1
  41. data/lib/glimmer/swt/latest_shell_proxy.rb +6 -2
  42. data/lib/glimmer/swt/layout_proxy.rb +32 -9
  43. data/lib/glimmer/swt/message_box_proxy.rb +20 -10
  44. data/lib/glimmer/swt/row_layout_proxy.rb +13 -6
  45. data/lib/glimmer/swt/shell_proxy.rb +30 -8
  46. data/lib/glimmer/swt/table_proxy.rb +19 -3
  47. data/lib/glimmer/swt/widget_proxy.rb +50 -19
  48. data/lib/glimmer/ui/custom_shell.rb +23 -6
  49. data/lib/glimmer/util/proc_tracker.rb +16 -5
  50. metadata +11 -9
@@ -31,7 +31,9 @@ module Glimmer
31
31
 
32
32
  def open
33
33
  Document.ready? do
34
- DisplayProxy.instance.dialogs.last&.open
34
+ DisplayProxy.instance.async_exec {
35
+ DisplayProxy.instance.dialogs.last&.open
36
+ }
35
37
  end
36
38
  end
37
39
 
@@ -31,7 +31,9 @@ module Glimmer
31
31
 
32
32
  def open
33
33
  Document.ready? do
34
- DisplayProxy.instance.message_boxes.last&.open
34
+ DisplayProxy.instance.async_exec {
35
+ DisplayProxy.instance.message_boxes.last&.open
36
+ }
35
37
  end
36
38
  end
37
39
 
@@ -19,9 +19,11 @@
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
+ require 'glimmer/swt/shell_proxy'
23
+
22
24
  module Glimmer
23
25
  module SWT
24
- class LatestShellProxy #< ShellProxy
26
+ class LatestShellProxy < ShellProxy
25
27
  def initialize(parent, args, block)
26
28
  # No Op
27
29
  end
@@ -40,7 +42,9 @@ module Glimmer
40
42
 
41
43
  def open
42
44
  Document.ready? do
43
- latest_shell&.open
45
+ DisplayProxy.instance.async_exec {
46
+ latest_shell&.open
47
+ }
44
48
  end
45
49
  end
46
50
 
@@ -1,3 +1,24 @@
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/swt/property_owner'
2
23
 
3
24
  module Glimmer
@@ -13,9 +34,8 @@ module Glimmer
13
34
  end
14
35
 
15
36
  def layout_class(keyword)
16
- class_name_alternative = keyword.camelcase(:upper)
17
- class_name_main = "#{class_name_alternative}Proxy"
18
- a_layout_class = Glimmer::SWT.const_get(class_name_main.to_sym) rescue Glimmer::SWT.const_get(class_name_alternative.to_sym)
37
+ class_name_main = "#{keyword.camelcase(:upper)}Proxy"
38
+ a_layout_class = Glimmer::SWT.const_get(class_name_main.to_sym)
19
39
  a_layout_class if a_layout_class.ancestors.include?(Glimmer::SWT::LayoutProxy)
20
40
  rescue => e
21
41
  Glimmer::Config.logger.debug "Layout #{keyword} was not found!"
@@ -31,29 +51,32 @@ module Glimmer
31
51
 
32
52
  def initialize(parent, args)
33
53
  @parent = parent
54
+ @args = args
34
55
  @parent = parent.body_root if @parent.is_a?(Glimmer::UI::CustomWidget)
35
56
  @parent.css_classes.each do |css_class|
36
57
  @parent.remove_css_class(css_class) if css_class.include?('layout')
37
58
  end
38
- @args = args
39
59
  @parent.add_css_class(css_class)
40
60
  @parent.layout = self
41
61
  self.margin_width = 15 if respond_to?(:margin_width=)
42
62
  self.margin_height = 15 if respond_to?(:margin_height=)
43
63
  end
44
-
45
- def css_class
46
- self.class.name.split('::').last.underscore.sub(/_proxy$/, '').gsub('_', '-')
64
+
65
+ def layout​(composite = nil, flush_cache = false)
66
+ # TODO implement def layout​(composite = nil, flush_cache = false) as per SWT API
67
+ composite ||= @parent
68
+ initialize(composite, @args)
47
69
  end
48
70
 
49
- def reapply
50
- # subclasses can override this
71
+ def css_class
72
+ self.class.name.split('::').last.underscore.sub(/_proxy$/, '').gsub('_', '-')
51
73
  end
52
74
 
53
75
  # Decorates widget dom. Subclasses may override. Returns widget dom by default.
54
76
  def dom(widget_dom)
55
77
  widget_dom
56
78
  end
79
+
57
80
  end
58
81
  end
59
82
  end
@@ -26,7 +26,7 @@ module Glimmer
26
26
  module SWT
27
27
  class MessageBoxProxy < WidgetProxy
28
28
  STYLE = <<~CSS
29
- .modal {
29
+ .message-box {
30
30
  position: fixed;
31
31
  z-index: 1000;
32
32
  padding-top: 100px;
@@ -39,15 +39,15 @@ module Glimmer
39
39
  background-color: rgba(0,0,0,0.4);
40
40
  text-align: center;
41
41
  }
42
- .modal-content .text {
42
+ .message-box-content .text {
43
43
  background: rgb(80, 116, 211);
44
44
  color: white;
45
45
  padding: 5px;
46
46
  }
47
- .modal-content .message {
47
+ .message-box-content .message {
48
48
  padding: 20px;
49
49
  }
50
- .modal-content {
50
+ .message-box-content {
51
51
  background-color: #fefefe;
52
52
  padding-bottom: 15px;
53
53
  border: 1px solid #888;
@@ -74,7 +74,7 @@ module Glimmer
74
74
  i = 0
75
75
  @parent = parent
76
76
  @parent = nil if parent.is_a?(LatestShellProxy)
77
- @parent ||= DisplayProxy.instance.shells.last || ShellProxy.new([])
77
+ @parent ||= DisplayProxy.instance.shells.detect(&:open?) || ShellProxy.new([])
78
78
  @args = args
79
79
  @block = block
80
80
  @children = Set.new
@@ -87,7 +87,7 @@ module Glimmer
87
87
 
88
88
  def text=(txt)
89
89
  @text = txt
90
- dom_element.find('.modal-content .text').html(@text)
90
+ dom_element.find('.message-box-content .text').html(@text)
91
91
  end
92
92
 
93
93
  def html_message
@@ -96,19 +96,29 @@ module Glimmer
96
96
 
97
97
  def message=(msg)
98
98
  @message = msg
99
- dom_element.find('.modal-content .message').html(html_message)
99
+ dom_element.find('.message-box-content .message').html(html_message)
100
+ end
101
+
102
+ def open?
103
+ @open
100
104
  end
101
105
 
102
106
  def open
103
- parent.post_initialize_child(self)
107
+ shell.open(async: false) unless shell.open?
108
+ owned_proc = Glimmer::Util::ProcTracker.new(owner: self, invoked_from: :open) {
109
+ parent.post_initialize_child(self)
110
+ @open = true
111
+ }
112
+ DisplayProxy.instance.async_exec(owned_proc)
104
113
  end
105
114
 
106
115
  def hide
107
116
  dom_element.remove
117
+ @open = false
108
118
  end
109
119
 
110
120
  def content(&block)
111
- Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Opal::MessageBoxExpression.new, &block)
121
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Opal::MessageBoxExpression.new, 'message_box', *@args, &block)
112
122
  end
113
123
 
114
124
  def selector
@@ -130,7 +140,7 @@ module Glimmer
130
140
  def dom
131
141
  @dom ||= html {
132
142
  div(id: id, class: "modal #{name}") {
133
- div(class: 'modal-content') {
143
+ div(class: 'message-box-content') {
134
144
  header(class: 'text') {
135
145
  "#{text}&nbsp;" # ensure title area occuppied when there is no text by adding non-breaking space (&nbsp;)
136
146
  }
@@ -42,15 +42,17 @@ module Glimmer
42
42
  }
43
43
  CSS
44
44
 
45
- attr_reader :type, :margin_width, :margin_height, :margin_top, :margin_right, :margin_bottom, :margin_left, :spacing, :pack, :center
46
-
45
+ attr_reader :type, :fill, :margin_width, :margin_height, :margin_top, :margin_right, :margin_bottom, :margin_left, :spacing, :pack, :center
46
+
47
47
  def initialize(parent, args)
48
48
  super(parent, args)
49
- @type = @args.first || :horizontal
50
- @marign_width = 15
51
- @margin_height = 15
52
- self.pack = true
53
49
  @parent.dom_element.add_class('row-layout')
50
+ self.type = args.first || :horizontal
51
+ self.pack = true
52
+ end
53
+
54
+ def type=(value)
55
+ @type = value
54
56
  @parent.dom_element.add_class(horizontal? ? 'row-layout-horizontal' : 'row-layout-vertical')
55
57
  end
56
58
 
@@ -77,6 +79,11 @@ module Glimmer
77
79
  end
78
80
  end
79
81
 
82
+ def fill=(value)
83
+ # TODO verify this is a correct implementation and interpretation of RowLayout in SWT
84
+ self.pack(!value)
85
+ end
86
+
80
87
  def center=(center_value)
81
88
  @center = center_value
82
89
  # Using padding for width since margin-right isn't getting respected with width 100%
@@ -47,9 +47,7 @@ module Glimmer
47
47
  def initialize(args)
48
48
  @args = args
49
49
  @children = []
50
- # TODO consider the implication of emptying the body
51
- Document.find('body').empty unless ENV['RUBY_ENV'] == 'test'
52
- render
50
+ render # TODO attach to specific element
53
51
  @layout = FillLayoutProxy.new(self, [])
54
52
  @layout.margin_width = 0
55
53
  @layout.margin_height = 0
@@ -95,7 +93,7 @@ module Glimmer
95
93
  def dom
96
94
  i = 0
97
95
  body_id = id
98
- body_class = ([name] + css_classes.to_a).join(' ')
96
+ body_class = ([name, 'hide'] + css_classes.to_a).join(' ')
99
97
  @dom ||= html {
100
98
  div(id: body_id, class: body_class) {
101
99
  # TODO consider supporting the idea of dynamic CSS building on close of shell that adds only as much CSS as needed for widgets that were mentioned
@@ -115,10 +113,34 @@ module Glimmer
115
113
  }.to_s
116
114
  end
117
115
 
118
- def open
119
- # TODO consider the idea of delaying rendering till the open method
120
- # TODO make it start as hidden and show shell upon open
121
- # DisplayProxy.instance.shells << self
116
+ def open(async: true)
117
+ work = lambda do
118
+ unless @open
119
+ DisplayProxy.instance.shells.select(&:open?).reject {|s| s == self}.map(&:hide)
120
+ dom_element.remove_class('hide')
121
+ @open = true
122
+ end
123
+ end
124
+ if async
125
+ DisplayProxy.instance.async_exec(&work)
126
+ else
127
+ work.call
128
+ end
129
+ end
130
+
131
+ def hide
132
+ dom_element.add_class('hide')
133
+ @open = false
134
+ end
135
+
136
+ def close
137
+ DisplayProxy.instance.shells.delete(self)
138
+ dom_element.remove
139
+ @open = false
140
+ end
141
+
142
+ def open?
143
+ @open
122
144
  end
123
145
  end
124
146
  end
@@ -257,9 +257,7 @@ module Glimmer
257
257
  @table_editor.minimumWidth = 90
258
258
  @table_editor.minimumHeight = 20
259
259
  if editable?
260
- on_mouse_up { |event|
261
- edit_table_item(event.table_item, event.column_index)
262
- }
260
+ add_editable_event_listener
263
261
  end
264
262
  end
265
263
 
@@ -311,6 +309,24 @@ module Glimmer
311
309
  end
312
310
  alias editable editable?
313
311
 
312
+ def editable=(value)
313
+ if value
314
+ args.push(:editable)
315
+ dom_element.addClass('editable')
316
+ add_editable_event_listener
317
+ else
318
+ args.delete(:editable)
319
+ dom_element.removeClass('editable')
320
+ @editable_on_mouse_up_event_listener.deregister # TODO see why table event listener deregistration is not working
321
+ end
322
+ end
323
+
324
+ def add_editable_event_listener
325
+ @editable_on_mouse_up_event_listener = on_mouse_up { |event|
326
+ edit_table_item(event.table_item, event.column_index) if editable?
327
+ }
328
+ end
329
+
314
330
  def selection
315
331
  @selection.to_a
316
332
  end
@@ -46,9 +46,8 @@ module Glimmer
46
46
  end
47
47
 
48
48
  def widget_class(keyword)
49
- class_name_alternative = keyword.camelcase(:upper)
50
- class_name_main = "#{class_name_alternative}Proxy"
51
- Glimmer::SWT.const_get(class_name_main.to_sym) rescue Glimmer::SWT.const_get(class_name_alternative.to_sym)
49
+ class_name_main = "#{keyword.camelcase(:upper)}Proxy"
50
+ Glimmer::SWT.const_get(class_name_main.to_sym)
52
51
  rescue => e
53
52
  puts "Widget #{keyword} was not found!"
54
53
  nil
@@ -77,15 +76,20 @@ module Glimmer
77
76
  def underscored_widget_name(widget_proxy)
78
77
  widget_proxy.class.name.split(/::|\./).last.sub(/Proxy$/, '').underscore
79
78
  end
79
+
80
+ def widget_handling_listener
81
+ @@widget_handling_listener
82
+ end
80
83
  end
81
84
 
82
85
  DEFAULT_INITIALIZERS = {
86
+ # TODO remove if built in class initializer is taking care of this
83
87
  composite: lambda do |composite_proxy|
84
- if composite_proxy.layout.nil?
85
- layout = GridLayoutProxy.new(composite_proxy, [])
86
- composite_proxy.layout = layout
87
- layout.margin_width = 15
88
- layout.margin_height = 15
88
+ if composite_proxy.get_layout.nil?
89
+ the_layout = GridLayoutProxy.new(composite_proxy, [])
90
+ composite_proxy.layout = the_layout
91
+ the_layout.margin_width = 15
92
+ the_layout.margin_height = 15
89
93
  end
90
94
  end,
91
95
  # scrolled_composite: lambda do |scrolled_composite|
@@ -100,7 +104,7 @@ module Glimmer
100
104
  table_column_proxy.width = 80
101
105
  end,
102
106
  # group: lambda do |group_proxy|
103
- # group_proxy.layout = GridLayoutProxy.new(group_proxy, []) if group.layout.nil?
107
+ # group_proxy.layout = GridLayoutProxy.new(group_proxy, []) if group.get_layout.nil?
104
108
  # end,
105
109
  }
106
110
 
@@ -135,8 +139,8 @@ module Glimmer
135
139
  dom_element.css('position', 'relative')
136
140
  menu&.render
137
141
  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)
142
+ menu.dom_element.css('left', mouse_event.x - parent.get_layout&.margin_width.to_i) # TODO - parent.get_layout&.margin_left.to_i)
143
+ menu.dom_element.css('top', mouse_event.y - parent.get_layout&.margin_height.to_i - 5) # TODO - parent.get_layout&.margin_top.to_i)
140
144
  @menu_requested = false
141
145
  end
142
146
  }
@@ -193,12 +197,24 @@ module Glimmer
193
197
  'div'
194
198
  end
195
199
 
196
- def pack(*args)
197
- # No Op (just a shim) TODO consider if it should be implemented
200
+ def shell
201
+ current_widget = self
202
+ current_widget = current_widget.parent until current_widget.parent.nil?
203
+ current_widget
204
+ end
205
+
206
+ def parents
207
+ parents_array = []
208
+ current_widget = self
209
+ until current_widget.parent.nil?
210
+ current_widget = current_widget.parent
211
+ parents_array << current_widget
212
+ end
213
+ parents_array
198
214
  end
199
215
 
200
- def layout(*args)
201
- # No Op (just a shim) TODO consider if it should be implemented
216
+ def dialog_ancestor
217
+ parents.detect {|p| p.is_a?(DialogProxy)}
202
218
  end
203
219
 
204
220
  def enabled=(value)
@@ -237,7 +253,7 @@ module Glimmer
237
253
  alias setFocus set_focus
238
254
 
239
255
  def parent_path
240
- @parent.path
256
+ @parent&.path
241
257
  end
242
258
 
243
259
  def parent_dom_element
@@ -287,12 +303,12 @@ module Glimmer
287
303
  # TODO consider passing parent element instead and having table item include a table cell widget only for opal
288
304
  @dom = nil
289
305
  @dom = dom
290
- @dom = @parent.layout.dom(@dom) if @parent.respond_to?(:layout) && @parent.layout
306
+ @dom = @parent.get_layout.dom(@dom) if @parent.respond_to?(:layout) && @parent.get_layout
291
307
  @dom
292
308
  end
293
309
 
294
310
  def content(&block)
295
- Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Opal::WidgetExpression.new, &block)
311
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Opal::WidgetExpression.new, self.class.underscored_widget_name(self), &block)
296
312
  end
297
313
 
298
314
  # Subclasses must override with their own mappings
@@ -633,6 +649,18 @@ module Glimmer
633
649
  @event_listener_proxies ||= []
634
650
  end
635
651
 
652
+ def suspend_event_handling
653
+ @event_handling_suspended = true
654
+ end
655
+
656
+ def resume_event_handling
657
+ @event_handling_suspended = false
658
+ end
659
+
660
+ def event_handling_suspended?
661
+ @event_handling_suspended
662
+ end
663
+
636
664
  def can_handle_observation_request?(observation_request)
637
665
  # TODO sort this out for Opal
638
666
  observation_request = observation_request.to_s
@@ -662,7 +690,10 @@ module Glimmer
662
690
  # 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
691
  # maybe consider pushing inside the widget classes instead where needed only or implement universal doit support correctly to bypass this issue
664
692
  # Async::Task.new do
665
- event_listener.call(event)
693
+ @@widget_handling_listener = self
694
+ # TODO also make sure to disable all widgets for suspension
695
+ event_listener.call(event) unless dialog_ancestor&.event_handling_suspended?
696
+ @widget_handling_listener = nil
666
697
  # end
667
698
  end
668
699
  the_listener_dom_element = event_element_css_selector ? Element[event_element_css_selector] : listener_dom_element