glimmer-dsl-opal 0.10.2 → 0.14.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 +252 -0
  3. data/README.md +187 -1301
  4. data/VERSION +1 -1
  5. data/lib/display.rb +3 -0
  6. data/lib/glimmer-dsl-opal.rb +2 -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/elaborate/weather.rb +157 -0
  12. data/lib/glimmer-dsl-opal/samples/hello/hello_button.rb +8 -8
  13. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox.rb +16 -14
  14. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox_group.rb +14 -9
  15. data/lib/glimmer-dsl-opal/samples/hello/hello_combo.rb +24 -22
  16. data/lib/glimmer-dsl-opal/samples/hello/hello_computed.rb +32 -14
  17. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_shell.rb +16 -12
  18. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_widget.rb +1 -1
  19. data/lib/glimmer-dsl-opal/samples/hello/hello_date_time.rb +4 -4
  20. data/lib/glimmer-dsl-opal/samples/hello/hello_group.rb +6 -6
  21. data/lib/glimmer-dsl-opal/samples/hello/hello_list_multi_selection.rb +1 -1
  22. data/lib/glimmer-dsl-opal/samples/hello/hello_list_single_selection.rb +1 -1
  23. data/lib/glimmer-dsl-opal/samples/hello/hello_radio.rb +18 -16
  24. data/lib/glimmer-dsl-opal/samples/hello/hello_radio_group.rb +17 -12
  25. data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +4 -4
  26. data/lib/glimmer/data_binding/table_items_binding.rb +3 -2
  27. data/lib/glimmer/dsl/opal/bind_expression.rb +24 -25
  28. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +8 -8
  29. data/lib/glimmer/dsl/opal/dsl.rb +4 -0
  30. data/lib/glimmer/dsl/opal/menu_expression.rb +1 -1
  31. data/lib/glimmer/dsl/opal/property_expression.rb +2 -1
  32. data/lib/glimmer/dsl/opal/shape_expression.rb +26 -0
  33. data/lib/glimmer/dsl/opal/shell_expression.rb +1 -1
  34. data/lib/{glimmer-dsl-opal/samples/hello/hello_computed/contact.rb → glimmer/dsl/opal/shine_data_binding_expression.rb} +24 -17
  35. data/lib/glimmer/dsl/opal/table_items_data_binding_expression.rb +2 -2
  36. data/lib/glimmer/dsl/opal/widget_expression.rb +1 -1
  37. data/lib/glimmer/swt/combo_proxy.rb +1 -0
  38. data/lib/glimmer/swt/composite_proxy.rb +2 -0
  39. data/lib/glimmer/swt/dialog_proxy.rb +1 -1
  40. data/lib/glimmer/swt/display_proxy.rb +63 -1
  41. data/lib/glimmer/swt/grid_layout_proxy.rb +17 -17
  42. data/lib/glimmer/swt/layout_proxy.rb +23 -3
  43. data/lib/glimmer/swt/message_box_proxy.rb +1 -1
  44. data/lib/glimmer/swt/row_layout_proxy.rb +12 -3
  45. data/lib/glimmer/swt/table_proxy.rb +19 -3
  46. data/lib/glimmer/swt/widget_proxy.rb +3 -4
  47. data/lib/glimmer/ui/custom_shell.rb +22 -5
  48. data/lib/glimmer/ui/custom_widget.rb +11 -2
  49. data/lib/net/http.rb +14 -6
  50. metadata +14 -10
@@ -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
@@ -138,7 +138,7 @@ module Glimmer
138
138
 
139
139
 
140
140
  def content(&block)
141
- 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)
142
142
  end
143
143
 
144
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,6 +65,10 @@ 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
74
  queue = nil # general queue
@@ -72,6 +79,7 @@ module Glimmer
72
79
  )
73
80
  queue = WidgetProxy.widget_handling_listener
74
81
  end
82
+ return block.call if !modal_open?
75
83
  schedule_async_exec(block, queue)
76
84
  end
77
85
  # sync_exec kept for API compatibility reasons
@@ -87,7 +95,34 @@ module Glimmer
87
95
  event.singleton_class.define_method(:character) do
88
96
  which || key_code
89
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 }
90
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
91
126
  }
92
127
  }
93
128
  },
@@ -98,7 +133,34 @@ module Glimmer
98
133
  event.singleton_class.define_method(:character) do
99
134
  which || key_code
100
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 }
101
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
102
164
  }
103
165
  }
104
166
  }
@@ -130,7 +192,7 @@ module Glimmer
130
192
  # TODO see if there are more intricate cases of opening a dialog from a widget listener handler
131
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?)
132
194
  block = async_exec_queue(queue).pop
133
- block.call
195
+ block&.call
134
196
  Async::Task.new(delay: 1, &executer) if async_exec_queue(queue).any?
135
197
  else
136
198
  Async::Task.new(delay: 100, &executer)
@@ -19,6 +19,23 @@ module Glimmer
19
19
  # TODO do the following instead of reapply
20
20
  # @parent.add_css_class("num-columns-#{@num_columns}")
21
21
  # reinitialize # TODO reimplement without using reinitialize
22
+ layout_css = <<~CSS
23
+ grid-template-columns: #{'auto ' * @num_columns.to_i};
24
+ grid-row-gap: #{@vertical_spacing}px;
25
+ grid-column-gap: #{@horizontal_spacing}px;
26
+ CSS
27
+ if @parent.css_classes.include?('grid-layout')
28
+ layout_css.split(";").map(&:strip).map {|l| l.split(':').map(&:strip)}.each do |key, value|
29
+ @parent.dom_element.css(key, value) unless key.nil?
30
+ end
31
+ if @parent.is_a?(GroupProxy)
32
+ @parent.dom_element.find('legend').css('grid-column-start', "span #{@num_columns.to_i}")
33
+ end
34
+ else
35
+ layout_css.split(";").map(&:strip).map {|l| l.split(':').map(&:strip)}.each do |key, value|
36
+ @parent.dom_element.css(key, 'initial') unless key.nil?
37
+ end
38
+ end
22
39
  end
23
40
 
24
41
  def make_columns_equal_width=(equal_width)
@@ -63,23 +80,6 @@ module Glimmer
63
80
  self.margin_width = 15
64
81
  self.margin_height = 15
65
82
  self.num_columns = @args.first || 1
66
- layout_css = <<~CSS
67
- grid-template-columns: #{'auto ' * @num_columns.to_i};
68
- grid-row-gap: #{@vertical_spacing}px;
69
- grid-column-gap: #{@horizontal_spacing}px;
70
- CSS
71
- if @parent.css_classes.include?('grid-layout')
72
- layout_css.split(";").map(&:strip).map {|l| l.split(':').map(&:strip)}.each do |key, value|
73
- @parent.dom_element.css(key, value) unless key.nil?
74
- end
75
- if @parent.is_a?(GroupProxy)
76
- @parent.dom_element.find('legend').css('grid-column-start', "span #{@num_columns.to_i}")
77
- end
78
- else
79
- layout_css.split(";").map(&:strip).map {|l| l.split(':').map(&:strip)}.each do |key, value|
80
- @parent.dom_element.css(key, 'initial') unless key.nil?
81
- end
82
- end
83
83
  end
84
84
  end
85
85
  end
@@ -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!"
@@ -118,7 +118,7 @@ module Glimmer
118
118
  end
119
119
 
120
120
  def content(&block)
121
- 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)
122
122
  end
123
123
 
124
124
  def selector
@@ -42,13 +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
45
+ attr_reader :type, :fill, :margin_width, :margin_height, :margin_top, :margin_right, :margin_bottom, :margin_left, :spacing, :pack, :center
46
46
 
47
47
  def initialize(parent, args)
48
48
  super(parent, args)
49
- @type = args.first || :horizontal
50
- self.pack = true
51
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
52
56
  @parent.dom_element.add_class(horizontal? ? 'row-layout-horizontal' : 'row-layout-vertical')
53
57
  end
54
58
 
@@ -75,6 +79,11 @@ module Glimmer
75
79
  end
76
80
  end
77
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
+
78
87
  def center=(center_value)
79
88
  @center = center_value
80
89
  # Using padding for width since margin-right isn't getting respected with width 100%
@@ -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
@@ -309,7 +308,7 @@ module Glimmer
309
308
  end
310
309
 
311
310
  def content(&block)
312
- 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)
313
312
  end
314
313
 
315
314
  # Subclasses must override with their own mappings
@@ -28,15 +28,26 @@ module Glimmer
28
28
  module CustomShell
29
29
  include Glimmer::UI::CustomWidget
30
30
 
31
+ module ClassMethods
32
+ include Glimmer
33
+ attr_reader :custom_shell
34
+
35
+ def launch
36
+ custom_shell = send(self.name.underscore.gsub('::', '__'))
37
+ custom_shell.open
38
+ end
39
+ end
40
+
31
41
  class << self
32
42
  def included(klass)
33
43
  klass.extend(CustomWidget::ClassMethods)
44
+ klass.extend(CustomShell::ClassMethods)
34
45
  klass.include(Glimmer)
35
46
  Glimmer::UI::CustomWidget.add_custom_widget_namespaces_for(klass)
36
47
  keyword = klass.name.split(':').last.underscore
37
48
  LocalStorage[keyword] = $LOADED_FEATURES.last
38
49
  end
39
-
50
+
40
51
  def request_parameter_string
41
52
  URI.decode_www_form_component(`document.location.href`.match(/\?(.*)$/).to_a[1].to_s)
42
53
  end
@@ -59,11 +70,17 @@ module Glimmer
59
70
  raise Error, 'Invalid custom shell body root! Must be a shell or another custom shell.' unless body_root.is_a?(Glimmer::SWT::ShellProxy) || body_root.is_a?(Glimmer::UI::CustomShell)
60
71
  end
61
72
 
62
- # Classes may override
63
- def open
64
- # TODO consider the idea of delaying rendering till the open method
65
- body_root.open
73
+ def open(async: true)
74
+ work = lambda do
75
+ body_root.open
76
+ end
77
+ if async
78
+ Glimmer::SWT::DisplayProxy.instance.async_exec(&work)
79
+ else
80
+ work.call
81
+ end
66
82
  end
83
+
67
84
 
68
85
  # DO NOT OVERRIDE. JUST AN ALIAS FOR `#open`. OVERRIDE `#open` INSTEAD.
69
86
  def show
@@ -83,6 +83,16 @@ module Glimmer
83
83
  @after_body_blocks ||= []
84
84
  @after_body_blocks << block
85
85
  end
86
+
87
+ def keyword
88
+ self.name.underscore.gsub('::', '__')
89
+ end
90
+
91
+ # Returns shortcut keyword to use for this custom widget (keyword minus namespace)
92
+ def shortcut_keyword
93
+ self.name.underscore.gsub('::', '__').split('__').last
94
+ end
95
+
86
96
  end
87
97
 
88
98
  class << self
@@ -143,7 +153,6 @@ module Glimmer
143
153
  def reset_custom_widget_namespaces
144
154
  @custom_widget_namespaces = Set[Object, Glimmer::UI]
145
155
  end
146
-
147
156
  end
148
157
  # <- end of class methods
149
158
 
@@ -259,7 +268,7 @@ module Glimmer
259
268
  # Otherwise, if a block is passed, it adds it as content to this custom widget
260
269
  def content(&block)
261
270
  if block_given?
262
- body_root.content(&block)
271
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Opal::CustomWidgetExpression.new, self.class.keyword, &block)
263
272
  else
264
273
  @content
265
274
  end
data/lib/net/http.rb CHANGED
@@ -19,8 +19,6 @@
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
- # Missing Net module class methods TODO implement
23
-
24
22
  require_relative '../uri'
25
23
 
26
24
  module Net
@@ -30,11 +28,21 @@ module Net
30
28
  class HTTP
31
29
  class << self
32
30
  def post_form(uri, params)
33
- response_body = nil
34
- result = ::HTTP.post(uri, payload: params) do |response|
35
- response_body = response.body
31
+ uri = "#{`window.location.protocol`}//#{File.join(uri)}" unless uri.start_with?('http:') || uri.start_with?('https:') # TODO refactor repetitive code
32
+ result = nil
33
+ ::HTTP.post(uri, {async: false, dataType: 'text', data: params}) do |response|
34
+ result = response.body if response.ok?
35
+ end
36
+ result
37
+ end
38
+
39
+ def get(uri, path_and_params)
40
+ uri = "#{`window.location.protocol`}//#{File.join(uri, path_and_params)}" unless uri.start_with?('http:') || uri.start_with?('https:') # TODO refactor repetitive code
41
+ result = nil
42
+ ::HTTP.get(uri, {async: false, dataType: 'text'}) do |response|
43
+ result = response.body if response.ok?
36
44
  end
37
- response_body
45
+ result
38
46
  end
39
47
  end
40
48
  end