glimmer-dsl-opal 0.7.1 → 0.8.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/README.md +453 -48
  4. data/VERSION +1 -1
  5. data/lib/display.rb +31 -0
  6. data/lib/glimmer-dsl-opal.rb +15 -36
  7. data/lib/glimmer-dsl-opal/ext/class.rb +10 -0
  8. data/lib/glimmer-dsl-opal/ext/file.rb +29 -0
  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 +24 -1
  13. data/lib/glimmer-dsl-opal/samples/hello/hello_button.rb +46 -0
  14. data/lib/glimmer-dsl-opal/samples/hello/hello_computed.rb +27 -0
  15. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_shell.rb +7 -7
  16. data/lib/glimmer-dsl-opal/samples/hello/hello_list_multi_selection.rb +62 -32
  17. data/lib/glimmer-dsl-opal/samples/hello/hello_list_single_selection.rb +47 -22
  18. data/lib/glimmer-dsl-opal/samples/hello/hello_message_box.rb +37 -0
  19. data/lib/glimmer-dsl-opal/samples/hello/hello_pop_up_context_menu.rb +84 -0
  20. data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +2 -2
  21. data/lib/glimmer/data_binding/observable_element.rb +1 -1
  22. data/lib/glimmer/data_binding/table_items_binding.rb +3 -3
  23. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +6 -0
  24. data/lib/glimmer/dsl/opal/dsl.rb +2 -0
  25. data/lib/glimmer/dsl/opal/menu_bar_expression.rb +54 -0
  26. data/lib/glimmer/dsl/opal/menu_expression.rb +61 -0
  27. data/lib/glimmer/dsl/opal/widget_expression.rb +3 -2
  28. data/lib/glimmer/dsl/opal/widget_listener_expression.rb +2 -2
  29. data/lib/glimmer/swt/combo_proxy.rb +40 -1
  30. data/lib/glimmer/swt/control_editor.rb +2 -1
  31. data/lib/glimmer/swt/custom/checkbox_group.rb +2 -2
  32. data/lib/glimmer/swt/custom/radio_group.rb +2 -2
  33. data/lib/glimmer/swt/date_time_proxy.rb +66 -1
  34. data/lib/glimmer/swt/event_listener_proxy.rb +14 -4
  35. data/lib/glimmer/swt/font_proxy.rb +4 -4
  36. data/lib/glimmer/swt/grid_layout_proxy.rb +21 -12
  37. data/lib/glimmer/swt/label_proxy.rb +17 -6
  38. data/lib/glimmer/swt/layout_data_proxy.rb +10 -7
  39. data/lib/glimmer/swt/list_proxy.rb +33 -0
  40. data/lib/glimmer/swt/menu_item_proxy.rb +87 -0
  41. data/lib/glimmer/swt/menu_proxy.rb +162 -0
  42. data/lib/glimmer/swt/message_box_proxy.rb +53 -67
  43. data/lib/glimmer/swt/property_owner.rb +2 -0
  44. data/lib/glimmer/swt/radio_proxy.rb +1 -1
  45. data/lib/glimmer/swt/shell_proxy.rb +32 -187
  46. data/lib/glimmer/swt/tab_folder_proxy.rb +43 -0
  47. data/lib/glimmer/swt/table_column_proxy.rb +4 -3
  48. data/lib/glimmer/swt/table_editor.rb +2 -2
  49. data/lib/glimmer/swt/table_item_proxy.rb +15 -5
  50. data/lib/glimmer/swt/table_proxy.rb +34 -12
  51. data/lib/glimmer/swt/text_proxy.rb +1 -1
  52. data/lib/glimmer/swt/widget_proxy.rb +335 -38
  53. data/lib/glimmer/ui/custom_shell.rb +9 -7
  54. data/lib/glimmer/ui/custom_widget.rb +3 -3
  55. data/lib/os.rb +36 -0
  56. metadata +36 -3
@@ -2,6 +2,8 @@ module Glimmer
2
2
  module SWT
3
3
  # Adapts Glimmer UI classes to SWT JavaBean property owner classes (which are now adapted to Opal)
4
4
  module PropertyOwner
5
+ # TODO consider adding has_attribute?
6
+
5
7
  def get_attribute(attribute_name)
6
8
  send(attribute_getter(attribute_name))
7
9
  end
@@ -4,7 +4,7 @@ module Glimmer
4
4
  module SWT
5
5
  class RadioProxy < WidgetProxy
6
6
  # TODO add a create method that ensures passing :radio style in if not there
7
- STYLE=<<~CSS
7
+ STYLE = <<~CSS
8
8
  .radio {
9
9
  display: inline;
10
10
  }
@@ -1,12 +1,34 @@
1
1
  require 'glimmer/swt/widget_proxy'
2
+ require 'glimmer/swt/layout_proxy'
2
3
  require 'glimmer/swt/display_proxy'
3
4
  require 'glimmer/swt/point'
4
5
 
5
6
  module Glimmer
6
7
  module SWT
7
8
  class ShellProxy < CompositeProxy
9
+ STYLE = <<~CSS
10
+ html {
11
+ width: 100%;
12
+ height: 100%;
13
+ }
14
+ body {
15
+ width: 100%;
16
+ height: 100%;
17
+ margin: 0;
18
+ }
19
+ .shell {
20
+ height: 100%;
21
+ margin: 0;
22
+ }
23
+ .shell iframe {
24
+ width: 100%;
25
+ height: 100%;
26
+ }
27
+ CSS
28
+
8
29
  # TODO consider renaming to ShellProxy to match SWT API
9
30
  attr_reader :minimum_size
31
+ attr_accessor :menu_bar # TODO implement menu bar rendering
10
32
 
11
33
  WIDTH_MIN = 130
12
34
  HEIGHT_MIN = 0
@@ -52,208 +74,31 @@ module Glimmer
52
74
  .hide {
53
75
  display: none !important;
54
76
  }
55
- .selected, .tabs .tab.selected {
77
+ .selected {
56
78
  background: rgb(80, 116, 211);
57
79
  color: white;
58
80
  }
59
81
  CSS
60
82
  end
61
83
 
62
- def style_dom_shell_css
63
- <<~CSS
64
- html {
65
- width: 100%;
66
- height: 100%;
67
- }
68
- body {
69
- width: 100%;
70
- height: 100%;
71
- margin: 0;
72
- }
73
- .shell {
74
- height: 100%;
75
- margin: 0;
76
- }
77
- .shell iframe {
78
- width: 100%;
79
- height: 100%;
80
- }
81
- CSS
82
- end
83
-
84
- def style_dom_list_css
85
- <<~CSS
86
- ul {
87
- list-style: none;
88
- padding: 0;
89
- }
90
- li {
91
- cursor: default;
92
- padding-left: 10px;
93
- padding-right: 10px;
94
- }
95
- li.empty-list-item {
96
- color: transparent;
97
- }
98
- CSS
99
- end
100
-
101
- def style_dom_tab_css
102
- <<~CSS
103
- .tabs .tab {
104
- background-color: inherit;
105
- float: left;
106
- border: none;
107
- outline: none;
108
- cursor: pointer;
109
- padding: 14px 16px;
110
- transition: 0.3s;
111
- font-size: 17px;
112
- }
113
- .tabs {
114
- overflow: hidden;
115
- border: 1px solid #ccc;
116
- background-color: #f1f1f1;
117
- }
118
- CSS
119
- end
120
-
121
- def style_dom_tab_item_css
122
- <<~CSS
123
- /* Create an selected/current tablink class */
124
- .tabs .tab.selected {
125
- background-color: #ccc;
126
- }
127
- /* Change background color of buttons on hover */
128
- .tabs .tab:hover {
129
- background-color: #ddd;
130
- }
131
- /* Style the tab content */
132
- .tab-item {
133
- padding: 6px 12px;
134
- border: 1px solid #ccc;
135
- border-top: none;
136
- }
137
- CSS
138
- end
139
-
140
- def style_dom_modal_css
141
- <<~CSS
142
- /* The Modal (background) */
143
- .modal {
144
- position: fixed; /* Stay in place */
145
- z-index: 1; /* Sit on top */
146
- padding-top: 100px; /* Location of the box */
147
- left: 0;
148
- top: 0;
149
- width: 100%; /* Full width */
150
- height: 100%; /* Full height */
151
- overflow: auto; /* Enable scroll if needed */
152
- background-color: rgb(0,0,0); /* Fallback color */
153
- background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
154
- text-align: center;
155
- }
156
-
157
- /* Modal Content */
158
- .modal-content {
159
- background-color: #fefefe;
160
- margin: auto;
161
- border: 1px solid #888;
162
- display: inline-block;
163
- min-width: 200px;
164
- }
165
-
166
- .modal-content .text {
167
- background: rgb(80, 116, 211);
168
- color: white;
169
- padding: 5px;
170
- }
171
-
172
- .modal-content .message {
173
- padding: 20px;
174
- }
175
-
176
- /* The Close Button */
177
- .close {
178
- color: #aaaaaa;
179
- float: right;
180
- font-weight: bold;
181
- margin: 5px;
182
- }
183
-
184
- .close:hover,
185
- .close:focus {
186
- color: #000;
187
- text-decoration: none;
188
- cursor: pointer;
189
- }
190
- CSS
191
- end
192
-
193
- def style_dom_table_css
194
- <<~CSS
195
- table {
196
- border-spacing: 0;
197
- }
198
-
199
- table tr th,td {
200
- cursor: default;
201
- }
202
- CSS
203
- end
204
-
205
84
  def dom
206
85
  i = 0
207
86
  body_id = id
208
87
  body_class = ([name] + css_classes.to_a).join(' ')
209
88
  @dom ||= html {
210
89
  div(id: body_id, class: body_class) {
211
- # TODO support the idea of dynamic CSS building on close of shell that adds only as much CSS as needed for widgets that were mentioned
90
+ # 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
212
91
  style(class: 'common-style') {
213
92
  style_dom_css
214
93
  }
215
- style(class: 'shell-style') {
216
- style_dom_shell_css
217
- }
218
- style(class: 'list-style') {
219
- style_dom_list_css
220
- }
221
- style(class: 'tab-style') {
222
- style_dom_tab_css
223
- }
224
- # style(class: 'tab-item-style') {
225
- # style_dom_tab_item_css
226
- # }
227
- # style(class: 'modal-style') {
228
- # style_dom_modal_css
229
- # }
230
- style(class: 'table-style') {
231
- style_dom_table_css
232
- }
233
- style(class: 'fill-layout-style') {
234
- Glimmer::SWT::FillLayoutProxy::STYLE
235
- }
236
- style(class: 'row-layout-style') {
237
- Glimmer::SWT::RowLayoutProxy::STYLE
238
- }
239
- style(class: 'grid-layout-style') {
240
- Glimmer::SWT::GridLayoutProxy::STYLE
241
- }
242
- style(class: 'checkbox-style') {
243
- Glimmer::SWT::CheckboxProxy::STYLE
244
- }
245
- style(class: 'radio-style') {
246
- Glimmer::SWT::RadioProxy::STYLE
247
- }
248
- style(class: 'scrolled-composite-style') {
249
- Glimmer::SWT::ScrolledCompositeProxy::STYLE
250
- }
251
- style(class: 'table-item-style') {
252
- Glimmer::SWT::TableItemProxy::STYLE
253
- }
254
- style(class: 'table-column-style') {
255
- Glimmer::SWT::TableColumnProxy::STYLE
256
- }
94
+ [LayoutProxy, WidgetProxy].map(&:descendants).reduce(:+).each do |style_class|
95
+ if style_class.constants.include?('STYLE')
96
+ style(class: "#{style_class.name.split(':').last.underscore.gsub('_', '-').sub(/-proxy$/, '')}-style") {
97
+ style_class::STYLE
98
+ }
99
+ end
100
+ end
101
+ ''
257
102
  }
258
103
  }.to_s
259
104
  end
@@ -1,8 +1,51 @@
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
+
1
22
  require 'glimmer/swt/widget_proxy'
2
23
 
3
24
  module Glimmer
4
25
  module SWT
5
26
  class TabFolderProxy < WidgetProxy
27
+ STYLE = <<~CSS
28
+ .tabs .tab.selected {
29
+ background: rgb(80, 116, 211);
30
+ color: white;
31
+ }
32
+ .tabs .tab {
33
+ background-color: inherit;
34
+ float: left;
35
+ border: none;
36
+ outline: none;
37
+ cursor: pointer;
38
+ padding: 14px 16px;
39
+ transition: 0.3s;
40
+ font-size: 17px;
41
+ }
42
+ .tabs {
43
+ overflow: hidden;
44
+ border: 1px solid #ccc;
45
+ background-color: #f1f1f1;
46
+ }
47
+ CSS
48
+
6
49
  attr_reader :tabs
7
50
 
8
51
  def initialize(parent, args, block)
@@ -4,6 +4,8 @@ require 'glimmer/swt/swt_proxy'
4
4
  module Glimmer
5
5
  module SWT
6
6
  class TableColumnProxy < WidgetProxy
7
+ include Glimmer
8
+
7
9
  STYLE = <<~CSS
8
10
  th.table-column {
9
11
  background: rgb(246, 246, 246);
@@ -15,8 +17,7 @@ module Glimmer
15
17
  float: right;
16
18
  }
17
19
  CSS
18
- include Glimmer
19
-
20
+
20
21
  attr_accessor :sort_block, :sort_by_block
21
22
  attr_reader :text, :width,
22
23
  :no_sort, :sort_property, :editor
@@ -75,7 +76,7 @@ module Glimmer
75
76
  end
76
77
 
77
78
  # Sets editor (e.g. combo)
78
- def editor=(args)
79
+ def editor=(*args)
79
80
  @editor = args
80
81
  end
81
82
 
@@ -34,10 +34,10 @@ module Glimmer
34
34
  @editor_widget = editor_widget
35
35
  @old_value = table_item.cell_dom_element(table_column_index).html
36
36
  table_item.cell_dom_element(table_column_index).html('')
37
- editor_widget.render(table_item.cell_dom_element(table_column_index))
37
+ editor_widget.render(custom_parent_dom_element: table_item.cell_dom_element(table_column_index))
38
38
  # TODO tweak the width perfectly so it doesn't expand the table cell
39
39
  # editor_widget.dom_element.css('width', 'calc(100% - 20px)')
40
- editor_widget.dom_element.css('width', '90%') # just a good enough approximation
40
+ editor_widget.dom_element.css('width', "#{minimumWidth}%") # TODO implement property with pixels (and perhaps derive percentage separately from pixels)
41
41
  editor_widget.dom_element.css('height', "#{minimumHeight}px")
42
42
  editor_widget.dom_element.add_class('table-editor')
43
43
  # TODO consider relying on autofocus instead
@@ -39,7 +39,7 @@ module Glimmer
39
39
  super(parent, args, block)
40
40
  # TODO check if there is a need to remove this observer when removing widget from table upon items update
41
41
  on_widget_selected { |event|
42
- parent.select(parent.index_of(self), event.meta?)
42
+ parent.select(parent.index_of(self), (event.meta? if event.respond_to?(:meta?)))
43
43
  }
44
44
  end
45
45
 
@@ -139,10 +139,20 @@ module Glimmer
139
139
  redraw
140
140
  end
141
141
 
142
- def on_widget_selected(&block)
143
- event = 'click'
144
- delegate = $document.on(event, selector, &block)
145
- EventListenerProxy.new(element_proxy: self, event: event, selector: selector, delegate: delegate)
142
+ def redraw_selection
143
+ if parent.selection.include?(self)
144
+ dom_element.add_class('selected')
145
+ else
146
+ dom_element.remove_class('selected')
147
+ end
148
+ end
149
+
150
+ def observation_request_to_event_mapping
151
+ {
152
+ 'on_widget_selected' => {
153
+ event: 'mouseup',
154
+ }
155
+ }
146
156
  end
147
157
 
148
158
  def max_column_width(column_index)
@@ -27,6 +27,16 @@ require 'glimmer/swt/table_editor'
27
27
  module Glimmer
28
28
  module SWT
29
29
  class TableProxy < CompositeProxy
30
+ STYLE = <<~CSS
31
+ table {
32
+ border-spacing: 0;
33
+ }
34
+
35
+ table tr th,td {
36
+ cursor: default;
37
+ }
38
+ CSS
39
+
30
40
  attr_reader :columns, :selection,
31
41
  :sort_type, :sort_column, :sort_property, :sort_block, :sort_by_block, :additional_sort_properties,
32
42
  :editor, :table_editor
@@ -43,6 +53,7 @@ module Glimmer
43
53
  text: {
44
54
  widget_value_property: :text,
45
55
  editor_gui: lambda do |args, model, property, table_proxy|
56
+ table_proxy.table_editor.minimumWidth = 90
46
57
  table_proxy.table_editor.minimumHeight = 10
47
58
  table_editor_widget_proxy = text(*args) {
48
59
  text model.send(property)
@@ -66,6 +77,7 @@ module Glimmer
66
77
  widget_value_property: :text,
67
78
  editor_gui: lambda do |args, model, property, table_proxy|
68
79
  first_time = true
80
+ table_proxy.table_editor.minimumWidth = 90
69
81
  table_proxy.table_editor.minimumHeight = 18
70
82
  table_editor_widget_proxy = combo(*args) {
71
83
  items model.send("#{property}_options")
@@ -118,11 +130,12 @@ module Glimmer
118
130
  widget_value_property: :date_time,
119
131
  editor_gui: lambda do |args, model, property, table_proxy|
120
132
  first_time = true
121
- table_proxy.table_editor.minimumHeight = 25
133
+ table_proxy.table_editor.minimumWidth = 90
134
+ table_proxy.table_editor.minimumHeight = 15
122
135
  date(*args) {
123
136
  date_time model.send(property)
124
137
  focus true
125
- on_focus_lost {
138
+ on_widget_selected {
126
139
  table_proxy.finish_edit!
127
140
  }
128
141
  on_key_pressed { |key_event|
@@ -139,11 +152,12 @@ module Glimmer
139
152
  widget_value_property: :date_time,
140
153
  editor_gui: lambda do |args, model, property, table_proxy|
141
154
  first_time = true
142
- table_proxy.table_editor.minimumHeight = 25
155
+ table_proxy.table_editor.minimumWidth = 80
156
+ table_proxy.table_editor.minimumHeight = 15
143
157
  date_drop_down(*args) {
144
158
  date_time model.send(property)
145
159
  focus true
146
- on_focus_lost {
160
+ on_widget_selected {
147
161
  table_proxy.finish_edit!
148
162
  }
149
163
  on_key_pressed { |key_event|
@@ -160,10 +174,14 @@ module Glimmer
160
174
  widget_value_property: :date_time,
161
175
  editor_gui: lambda do |args, model, property, table_proxy|
162
176
  first_time = true
163
- table_proxy.table_editor.minimumHeight = 25
177
+ table_proxy.table_editor.minimumWidth = 80
178
+ table_proxy.table_editor.minimumHeight = 15
164
179
  time(*args) {
165
180
  date_time model.send(property)
166
181
  focus true
182
+ on_widget_selected {
183
+ table_proxy.finish_edit!
184
+ }
167
185
  on_focus_lost {
168
186
  table_proxy.finish_edit!
169
187
  }
@@ -236,6 +254,7 @@ module Glimmer
236
254
  @table_editor = TableEditor.new(self)
237
255
  @table_editor.horizontalAlignment = SWTProxy[:left]
238
256
  @table_editor.grabHorizontal = true
257
+ @table_editor.minimumWidth = 90
239
258
  @table_editor.minimumHeight = 20
240
259
  if editable?
241
260
  on_mouse_up { |event|
@@ -300,11 +319,12 @@ module Glimmer
300
319
  new_selection = new_selection.to_a
301
320
  changed = (selection + new_selection) - (selection & new_selection)
302
321
  @selection = new_selection
303
- changed.each(&:redraw)
322
+ changed.each(&:redraw_selection)
304
323
  end
305
324
 
306
325
  def items=(new_items)
307
326
  @children = new_items
327
+ # TODO optimize in the future by sorting elements in DOM directly when no change to elements occur other than sort
308
328
  redraw
309
329
  end
310
330
 
@@ -346,7 +366,7 @@ module Glimmer
346
366
  end
347
367
 
348
368
  def sort_property=(new_sort_property)
349
- @sort_property = [new_sort_property].flatten.compact
369
+ @sort_property = new_sort_property.to_collection
350
370
  end
351
371
 
352
372
  def detect_sort_type
@@ -367,7 +387,7 @@ module Glimmer
367
387
 
368
388
  def column_sort_properties
369
389
  column_properties.zip(columns.map(&:sort_property)).map do |pair|
370
- [pair.compact.last].flatten.compact
390
+ pair.compact.last.to_collection
371
391
  end
372
392
  end
373
393
 
@@ -398,7 +418,7 @@ module Glimmer
398
418
  end
399
419
  end
400
420
 
401
- new_sort_property = [new_sort_property].flatten.compact unless new_sort_property.is_a?(Array)
421
+ new_sort_property = new_sort_property.to_collection unless new_sort_property.is_a?(Array)
402
422
  @sort_direction = @sort_direction.nil? || @sort_property.first != new_sort_property.first || @sort_direction == :descending ? :ascending : :descending
403
423
 
404
424
  @sort_property = new_sort_property
@@ -504,7 +524,7 @@ module Glimmer
504
524
  @edit_mode = true
505
525
 
506
526
  editor_config = columns[column_index].editor || editor
507
- editor_config = [editor_config].flatten.compact
527
+ editor_config = editor_config.to_collection
508
528
  editor_widget_options = editor_config.last.is_a?(Hash) ? editor_config.last : {}
509
529
  editor_widget_arg_last_index = editor_config.last.is_a?(Hash) ? -2 : -1
510
530
  editor_widget = (editor_config[0] || :text).to_sym
@@ -604,7 +624,8 @@ module Glimmer
604
624
  event.singleton_class.send(:define_method, :column_index) do
605
625
  (table_data || event.target).attr('data-column-index')
606
626
  end
607
- event_listener.call(event)
627
+
628
+ event_listener.call(event) unless event.table_item.nil? && event.column_index.nil?
608
629
  }
609
630
  }
610
631
 
@@ -619,7 +640,8 @@ module Glimmer
619
640
  },
620
641
  'on_widget_selected' => {
621
642
  event: 'mouseup',
622
- }
643
+ event_handler: mouse_handler,
644
+ },
623
645
  }
624
646
  end
625
647