glimmer-dsl-opal 0.7.0 → 0.7.5

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -0
  3. data/README.md +290 -53
  4. data/VERSION +1 -1
  5. data/lib/display.rb +31 -0
  6. data/lib/file.rb +29 -0
  7. data/lib/glimmer-dsl-opal.rb +30 -1
  8. data/lib/glimmer-dsl-opal/ext/date.rb +1 -1
  9. data/lib/glimmer-dsl-opal/ext/struct.rb +37 -0
  10. data/lib/glimmer-dsl-opal/samples/elaborate/contact_manager.rb +50 -23
  11. data/lib/glimmer-dsl-opal/samples/elaborate/login.rb +22 -5
  12. data/lib/glimmer-dsl-opal/samples/hello/hello_browser.rb +1 -1
  13. data/lib/glimmer-dsl-opal/samples/hello/hello_button.rb +46 -0
  14. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_shell.rb +7 -7
  15. data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +5 -5
  16. data/lib/glimmer-dsl-swt.rb +20 -35
  17. data/lib/glimmer/data_binding/table_items_binding.rb +6 -4
  18. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +6 -0
  19. data/lib/glimmer/dsl/opal/widget_expression.rb +6 -2
  20. data/lib/glimmer/swt/combo_proxy.rb +40 -1
  21. data/lib/glimmer/swt/composite_proxy.rb +5 -1
  22. data/lib/glimmer/swt/control_editor.rb +54 -0
  23. data/lib/glimmer/swt/date_time_proxy.rb +66 -1
  24. data/lib/glimmer/swt/font_proxy.rb +4 -4
  25. data/lib/glimmer/swt/grid_layout_proxy.rb +20 -12
  26. data/lib/glimmer/swt/label_proxy.rb +11 -3
  27. data/lib/glimmer/swt/layout_data_proxy.rb +10 -7
  28. data/lib/glimmer/swt/layout_proxy.rb +1 -1
  29. data/lib/glimmer/swt/message_box_proxy.rb +2 -10
  30. data/lib/glimmer/swt/table_column_proxy.rb +9 -0
  31. data/lib/glimmer/swt/table_editor.rb +65 -0
  32. data/lib/glimmer/swt/table_item_proxy.rb +36 -0
  33. data/lib/glimmer/swt/table_proxy.rb +375 -17
  34. data/lib/glimmer/swt/text_proxy.rb +1 -1
  35. data/lib/glimmer/swt/widget_proxy.rb +99 -21
  36. data/lib/glimmer/ui/custom_shell.rb +9 -7
  37. data/lib/os.rb +36 -0
  38. metadata +25 -3
@@ -207,28 +207,28 @@ class HelloTable
207
207
  text 'Game Date'
208
208
  width 150
209
209
  sort_property :date # ensure sorting by real date value (not `game_date` string specified in items below)
210
- # editor :date_drop_down, property: :date_time
210
+ editor :date_drop_down, property: :date_time
211
211
  }
212
212
  table_column {
213
213
  text 'Game Time'
214
214
  width 150
215
215
  sort_property :time # ensure sorting by real time value (not `game_time` string specified in items below)
216
- # editor :time, property: :date_time
216
+ editor :time, property: :date_time
217
217
  }
218
218
  table_column {
219
219
  text 'Ballpark'
220
220
  width 180
221
- # editor :none
221
+ editor :none
222
222
  }
223
223
  table_column {
224
224
  text 'Home Team'
225
225
  width 150
226
- # editor :combo, :read_only # read_only is simply an SWT style passed to combo widget
226
+ editor :combo, :read_only # read_only is simply an SWT style passed to combo widget
227
227
  }
228
228
  table_column {
229
229
  text 'Away Team'
230
230
  width 150
231
- # editor :combo, :read_only # read_only is simply an SWT style passed to combo widget
231
+ editor :combo, :read_only # read_only is simply an SWT style passed to combo widget
232
232
  }
233
233
  table_column {
234
234
  text 'Promotion'
@@ -1,37 +1,22 @@
1
- class OS
2
- class << self
3
- def windows?
4
- # No Op in Opal
5
- end
6
-
7
- def mac?
8
- # No Op in Opal
9
- end
10
-
11
- def linux?
12
- # No Op in Opal
13
- end
14
- end
15
- end
16
-
17
- class File
18
- class << self
19
- def read(*args, &block)
20
- # TODO implement via asset downloads in the future
21
- # No Op in Opal
22
- end
23
- end
24
- end
25
-
26
- class Display
27
- class << self
28
- def setAppName(app_name)
29
- # No Op in Opal
30
- end
31
- def setAppVersion(version)
32
- # No Op in Opal
33
- end
34
- end
35
- end
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.
36
21
 
37
22
  require 'glimmer-dsl-opal'
@@ -35,7 +35,7 @@ module Glimmer
35
35
  model_cells = new_model_collection.to_a.map {|m| @table.cells_for(m)}
36
36
  return if table_cells == model_cells
37
37
  if new_model_collection and new_model_collection.is_a?(Array)
38
- # @table_items_observer_registration&.unobserve
38
+ @table_items_observer_registration&.unobserve
39
39
  @table_items_observer_registration = observe(new_model_collection, @column_properties)
40
40
  add_dependent(@table_observer_registration => @table_items_observer_registration)
41
41
  @model_collection = new_model_collection
@@ -45,8 +45,10 @@ module Glimmer
45
45
  end
46
46
 
47
47
  def populate_table(model_collection, parent, column_properties)
48
- return if model_collection&.sort_by(&:hash) == @last_populated_model_collection&.sort_by(&:hash)
48
+ @skip_populate_table = model_collection&.sort_by(&:hash).map {|m| @table.column_properties.map {|p| m.send(p)}} == @last_populated_model_collection_properties
49
+ return if @skip_populate_table
49
50
  @last_populated_model_collection = model_collection
51
+ @last_populated_model_collection_properties = model_collection&.sort_by(&:hash).map {|m| @table.column_properties.map {|p| m.send(p)}}
50
52
  # TODO improve performance
51
53
  selected_table_item_models = parent.selection.map(&:get_data)
52
54
  old_items = parent.items
@@ -60,8 +62,7 @@ module Glimmer
60
62
  table_item.set_data(model)
61
63
  table_item.id = old_item_ids_per_model[model.hash] if old_item_ids_per_model[model.hash]
62
64
  end
63
- selected_table_items = parent.search {|item| selected_table_item_models.include?(item.get_data) }
64
- parent.selection = selected_table_items
65
+ parent.selection = parent.search {|item| selected_table_item_models.include?(item.get_data) }
65
66
  parent.redraw
66
67
  end
67
68
 
@@ -69,6 +70,7 @@ module Glimmer
69
70
  return if model_collection == @last_sorted_model_collection
70
71
  if model_collection == @last_populated_model_collection
71
72
  # Reapply the last table sort. The model collection has just been populated since it diverged from what it was before
73
+ # TODO optimize in the future by sorting elements in DOM directly
72
74
  parent.sort!
73
75
  else
74
76
  # The model collection was sorted by the model, but beyond sorting, it did not change from the last populated model collection.
@@ -42,6 +42,12 @@ module Glimmer
42
42
  end
43
43
 
44
44
  def interpret(parent, keyword, *args, &block)
45
+ begin
46
+ require_path = `localStorage[#{keyword}]`
47
+ require(require_path) if require_path
48
+ rescue => e
49
+ Glimmer::Config.logger.debug e.message
50
+ end
45
51
  custom_widget_class = UI::CustomWidget.for(keyword)
46
52
  # TODO clean code by extracting methods into CustomShell
47
53
  if !Glimmer::UI::CustomShell.requested? && custom_widget_class&.ancestors&.to_a.include?(Glimmer::UI::CustomShell)
@@ -20,8 +20,12 @@ module Glimmer
20
20
  end
21
21
 
22
22
  def add_content(parent, &block)
23
- super(parent, &block)
24
- parent.post_add_content
23
+ if parent.rendered?
24
+ super(parent, &block)
25
+ parent.post_add_content
26
+ else
27
+ parent.add_content_on_render(&block)
28
+ end
25
29
  end
26
30
  end
27
31
  end
@@ -45,7 +45,46 @@ module Glimmer
45
45
  event_listener.call(event)
46
46
  }
47
47
  }
48
- }
48
+ },
49
+ 'on_key_pressed' => {
50
+ event: 'keydown',
51
+ event_handler: -> (event_listener) {
52
+ -> (event) {
53
+ @last_key_pressed_event = event
54
+ @text = event.target.value
55
+ # TODO generalize this solution to all widgets that support key presses
56
+ # TODO support event.location once DOM3 is supported by opal-jquery
57
+ event.define_singleton_method(:keyCode) {event.which}
58
+ event.define_singleton_method(:key_code, &event.method(:keyCode))
59
+ event.define_singleton_method(:character) {event.which.chr}
60
+ event.define_singleton_method(:stateMask) do
61
+ state_mask = 0
62
+ state_mask |= SWTProxy[:alt] if event.alt_key
63
+ state_mask |= SWTProxy[:ctrl] if event.ctrl_key
64
+ state_mask |= SWTProxy[:shift] if event.shift_key
65
+ state_mask |= SWTProxy[:command] if event.meta_key
66
+ state_mask
67
+ end
68
+ event.define_singleton_method(:state_mask, &event.method(:stateMask))
69
+ doit = true
70
+ event.define_singleton_method(:doit=) do |value|
71
+ doit = value
72
+ end
73
+ event.define_singleton_method(:doit) { doit }
74
+ event_listener.call(event)
75
+
76
+ # TODO Fix doit false, it's not stopping input
77
+ unless doit
78
+ event.prevent
79
+ event.prevent_default
80
+ event.stop_propagation
81
+ event.stop_immediate_propagation
82
+ end
83
+
84
+ doit
85
+ }
86
+ }
87
+ },
49
88
  }
50
89
  end
51
90
 
@@ -8,7 +8,11 @@ module Glimmer
8
8
 
9
9
  def initialize(parent, args, block)
10
10
  super(parent, args, block)
11
- @layout = GridLayoutProxy.new(self, [])
11
+ @layout = default_layout
12
+ end
13
+
14
+ def default_layout
15
+ GridLayoutProxy.new(self, [])
12
16
  end
13
17
 
14
18
  def dom
@@ -0,0 +1,54 @@
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
+
22
+ module Glimmer
23
+ module SWT
24
+ # Emulates SWT's native org.eclipse.swt.custom.ControlEditor
25
+ class ControlEditor
26
+ # TODO implement behavior for all these attributes
27
+ ATTRIBUTES = [:grabHorizontal, :grabVertical, :horizontalAlignment, :verticalAlignment, :minimumWidth, :minimumHeight]
28
+ attr_accessor(*ATTRIBUTES)
29
+ ATTRIBUTES.each do |attribute|
30
+ alias_method attribute.underscore, attribute
31
+ alias_method "#{attribute.underscore}=", "#{attribute}="
32
+ end
33
+
34
+ # TODO consider supporting a java_attr_accessor to get around having to generate all the aliases manually
35
+ attr_accessor :editor
36
+ alias getEditor editor
37
+ alias get_editor editor
38
+ alias setEditor editor=
39
+ alias set_editor editor=
40
+
41
+ # TODO implement `#layout` method if needed
42
+
43
+ attr_reader :composite
44
+
45
+ def initialize(composite)
46
+ @composite = composite
47
+ end
48
+
49
+ # TODO implement showing editor for composite or canvas
50
+ # def editor=(widget)
51
+ # end
52
+ end
53
+ end
54
+ end
@@ -31,7 +31,13 @@ module Glimmer
31
31
  showPeriod: true,
32
32
  showLeadingZero: true,
33
33
  showOn: 'both',
34
+ showNowButton: true,
35
+ showCloseButton: true,
34
36
  button: "##{time_button_id}",
37
+ onClose: ->(v) {
38
+ @timepicker_done = true
39
+ dom_element.trigger('change')
40
+ },
35
41
  })
36
42
  else
37
43
  options = {}
@@ -105,9 +111,68 @@ module Glimmer
105
111
  {
106
112
  'on_widget_selected' => [
107
113
  {
108
- event: 'change'
114
+ event: 'change',
115
+ event_handler: -> (event_listener) {
116
+ -> (event) {
117
+ if calendar? || date? || (time? && @timepicker_done)
118
+ @timepicker_done = false if time?
119
+ event_listener.call(event)
120
+ end
121
+ }
122
+ }
109
123
  },
110
124
  ],
125
+ 'on_focus_lost' => [
126
+ {
127
+ event: 'blur',
128
+ event_handler: -> (event_listener) {
129
+ -> (event) {
130
+ # TODO support blur event for date?
131
+ if time? && @timepicker_done
132
+ @timepicker_done = false if time?
133
+ event_listener.call(event)
134
+ end
135
+ }
136
+ }
137
+ },
138
+ ],
139
+ 'on_key_pressed' => {
140
+ event: 'keydown',
141
+ event_handler: -> (event_listener) {
142
+ -> (event) {
143
+ # TODO generalize this solution to all widgets that support key presses
144
+ # TODO support event.location once DOM3 is supported by opal-jquery
145
+ event.define_singleton_method(:keyCode) {event.which}
146
+ event.define_singleton_method(:key_code, &event.method(:keyCode))
147
+ event.define_singleton_method(:character) {event.which.chr}
148
+ event.define_singleton_method(:stateMask) do
149
+ state_mask = 0
150
+ state_mask |= SWTProxy[:alt] if event.alt_key
151
+ state_mask |= SWTProxy[:ctrl] if event.ctrl_key
152
+ state_mask |= SWTProxy[:shift] if event.shift_key
153
+ state_mask |= SWTProxy[:command] if event.meta_key
154
+ state_mask
155
+ end
156
+ event.define_singleton_method(:state_mask, &event.method(:stateMask))
157
+ doit = true
158
+ event.define_singleton_method(:doit=) do |value|
159
+ doit = value
160
+ end
161
+ event.define_singleton_method(:doit) { doit }
162
+ event_listener.call(event)
163
+
164
+ # TODO Fix doit false, it's not stopping input
165
+ unless doit
166
+ event.prevent
167
+ event.prevent_default
168
+ event.stop_propagation
169
+ event.stop_immediate_propagation
170
+ end
171
+
172
+ doit
173
+ }
174
+ }
175
+ },
111
176
  }
112
177
  end
113
178
 
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2020 Andy Maleh
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -68,7 +68,7 @@ module Glimmer
68
68
  private
69
69
 
70
70
  def detect_invalid_font_property(font_properties)
71
- [font_properties[:style]].flatten.select do |style|
71
+ font_properties[:style].to_collection(false).select do |style|
72
72
  style.is_a?(Symbol) || style.is_a?(String)
73
73
  end.each do |style|
74
74
  raise Error, style.to_s + ERROR_INVALID_FONT_STYLE if !FONT_STYLES.include?(style.to_sym)
@@ -7,9 +7,8 @@ module Glimmer
7
7
  .grid-layout {
8
8
  display: grid;
9
9
  grid-template-rows: min-content;
10
- justify-content: start;
11
10
  place-content: start;
12
- align-items: stretch;
11
+ align-items: stretch;
13
12
  }
14
13
  CSS
15
14
  attr_reader :num_columns, :make_columns_equal_width, :horizontal_spacing, :vertical_spacing, :margin_width, :margin_height
@@ -18,7 +17,9 @@ module Glimmer
18
17
  super(parent, args)
19
18
  self.horizontal_spacing = 10
20
19
  self.vertical_spacing = 10
21
- self.num_columns = @args.first || 1
20
+ self.margin_width = 15
21
+ self.margin_height = 15
22
+ self.num_columns = @args.first || 1
22
23
  reapply
23
24
  end
24
25
 
@@ -30,7 +31,7 @@ module Glimmer
30
31
  end
31
32
 
32
33
  def make_columns_equal_width=(equal_width)
33
- @make_columns_equal_width = equal_width
34
+ @make_columns_equal_width = equal_width
34
35
  # @parent.add_css_class('make_columns_equal_width') if @make_columns_equal_width
35
36
  reapply
36
37
  end
@@ -50,14 +51,18 @@ module Glimmer
50
51
  def margin_width=(pixels)
51
52
  @margin_width = pixels
52
53
  # Using padding for width since margin-right isn't getting respected with width 100%
53
- @parent.dom_element.css('padding-left', @margin_width)
54
- @parent.dom_element.css('padding-right', @margin_width)
54
+ effective_margin_width = @margin_width
55
+ effective_margin_width += 6 if @parent.is_a?(GroupProxy)
56
+ @parent.dom_element.css('padding-left', effective_margin_width)
57
+ @parent.dom_element.css('padding-right', effective_margin_width)
55
58
  end
56
59
 
57
60
  def margin_height=(pixels)
58
61
  @margin_height = pixels
59
- @parent.dom_element.css('padding-top', @margin_height)
60
- @parent.dom_element.css('padding-bottom', @margin_height)
62
+ effective_margin_height = @margin_height
63
+ effective_margin_height += 9 if @parent.is_a?(GroupProxy)
64
+ @parent.dom_element.css('padding-top', effective_margin_height)
65
+ @parent.dom_element.css('padding-bottom', effective_margin_height)
61
66
  end
62
67
 
63
68
  def reapply
@@ -68,15 +73,18 @@ module Glimmer
68
73
  grid-column-gap: #{@horizontal_spacing}px;
69
74
  CSS
70
75
  if @parent.css_classes.include?('grid-layout')
71
- layout_css.split(";").map(&:strip).map {|l| l.split(':').map(&:strip)}.each do |key, value|
76
+ layout_css.split(";").map(&:strip).map {|l| l.split(':').map(&:strip)}.each do |key, value|
72
77
  @parent.dom_element.css(key, value) unless key.nil?
73
78
  end
79
+ if @parent.is_a?(GroupProxy)
80
+ @parent.dom_element.find('legend').css('grid-column-start', "span #{@num_columns.to_i}")
81
+ end
74
82
  else
75
- layout_css.split(";").map(&:strip).map {|l| l.split(':').map(&:strip)}.each do |key, value|
83
+ layout_css.split(";").map(&:strip).map {|l| l.split(':').map(&:strip)}.each do |key, value|
76
84
  @parent.dom_element.css(key, 'initial') unless key.nil?
77
- end
85
+ end
78
86
  end
79
- end
87
+ end
80
88
  end
81
89
  end
82
90
  end