glimmer-dsl-opal 0.6.1 → 0.7.4

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +54 -0
  3. data/README.md +489 -16
  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 +31 -3
  8. data/lib/glimmer-dsl-opal/ext/date.rb +11 -0
  9. data/lib/glimmer-dsl-opal/ext/struct.rb +37 -0
  10. data/lib/glimmer-dsl-opal/samples/hello/hello_browser.rb +1 -1
  11. data/lib/glimmer-dsl-opal/samples/hello/hello_button.rb +46 -0
  12. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_shell.rb +7 -7
  13. data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +283 -0
  14. data/lib/glimmer-dsl-swt.rb +20 -35
  15. data/lib/glimmer/data_binding/table_items_binding.rb +32 -19
  16. data/lib/glimmer/dsl/opal/block_property_expression.rb +41 -0
  17. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +5 -0
  18. data/lib/glimmer/dsl/opal/dsl.rb +2 -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/display_proxy.rb +4 -0
  25. data/lib/glimmer/swt/font_proxy.rb +4 -4
  26. data/lib/glimmer/swt/grid_layout_proxy.rb +7 -8
  27. data/lib/glimmer/swt/label_proxy.rb +11 -3
  28. data/lib/glimmer/swt/layout_data_proxy.rb +9 -3
  29. data/lib/glimmer/swt/layout_proxy.rb +1 -1
  30. data/lib/glimmer/swt/message_box_proxy.rb +3 -10
  31. data/lib/glimmer/swt/property_owner.rb +2 -2
  32. data/lib/glimmer/swt/shell_proxy.rb +8 -0
  33. data/lib/glimmer/swt/table_column_proxy.rb +71 -12
  34. data/lib/glimmer/swt/table_editor.rb +65 -0
  35. data/lib/glimmer/swt/table_item_proxy.rb +44 -1
  36. data/lib/glimmer/swt/table_proxy.rb +579 -12
  37. data/lib/glimmer/swt/text_proxy.rb +49 -1
  38. data/lib/glimmer/swt/widget_proxy.rb +106 -17
  39. data/lib/glimmer/ui/custom_shell.rb +9 -7
  40. data/lib/net/http.rb +1 -5
  41. data/lib/os.rb +36 -0
  42. data/lib/uri.rb +3 -3
  43. metadata +31 -9
  44. data/lib/glimmer/data_binding/ext/observable_model.rb +0 -40
@@ -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,7 @@ 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.num_columns = @args.first || 1
22
21
  reapply
23
22
  end
24
23
 
@@ -30,7 +29,7 @@ module Glimmer
30
29
  end
31
30
 
32
31
  def make_columns_equal_width=(equal_width)
33
- @make_columns_equal_width = equal_width
32
+ @make_columns_equal_width = equal_width
34
33
  # @parent.add_css_class('make_columns_equal_width') if @make_columns_equal_width
35
34
  reapply
36
35
  end
@@ -68,15 +67,15 @@ module Glimmer
68
67
  grid-column-gap: #{@horizontal_spacing}px;
69
68
  CSS
70
69
  if @parent.css_classes.include?('grid-layout')
71
- layout_css.split(";").map(&:strip).map {|l| l.split(':').map(&:strip)}.each do |key, value|
70
+ layout_css.split(";").map(&:strip).map {|l| l.split(':').map(&:strip)}.each do |key, value|
72
71
  @parent.dom_element.css(key, value) unless key.nil?
73
72
  end
74
73
  else
75
- layout_css.split(";").map(&:strip).map {|l| l.split(':').map(&:strip)}.each do |key, value|
74
+ layout_css.split(";").map(&:strip).map {|l| l.split(':').map(&:strip)}.each do |key, value|
76
75
  @parent.dom_element.css(key, 'initial') unless key.nil?
77
- end
76
+ end
78
77
  end
79
- end
78
+ end
80
79
  end
81
80
  end
82
81
  end
@@ -4,11 +4,10 @@ require 'glimmer/swt/widget_proxy'
4
4
  module Glimmer
5
5
  module SWT
6
6
  class LabelProxy < WidgetProxy
7
- attr_reader :text, :background_image, :image, :alignment
7
+ attr_reader :text, :background_image, :image
8
8
 
9
9
  def initialize(parent, args, block)
10
10
  super(parent, args, block)
11
- self.alignment = [:left, :center, :right].detect {|align| args.detect { |arg| SWTProxy[align] == arg } }
12
11
  end
13
12
 
14
13
  def text=(value)
@@ -32,6 +31,15 @@ module Glimmer
32
31
  def element
33
32
  'label'
34
33
  end
34
+
35
+ def alignment
36
+ if @alignment.nil?
37
+ found_arg = nil
38
+ @alignment = [:left, :center, :right].detect {|align| found_arg = args.detect { |arg| SWTProxy[align] == SWTProxy[arg] } }
39
+ args.delete(found_arg)
40
+ end
41
+ @alignment
42
+ end
35
43
 
36
44
  def alignment=(value)
37
45
  # TODO consider storing swt value in the future instead
@@ -44,7 +52,7 @@ module Glimmer
44
52
  label_id = id
45
53
  label_class = name
46
54
  @dom ||= html {
47
- label(id: label_id, class: label_class) {
55
+ label(id: label_id, class: label_class, style: "text-align: #{alignment};") {
48
56
  label_text
49
57
  }
50
58
  }.to_s
@@ -3,6 +3,7 @@ require 'glimmer/swt/property_owner'
3
3
  module Glimmer
4
4
  module SWT
5
5
  class LayoutDataProxy
6
+ # TODO make this polymorphic as GridData or RowData subclasses
6
7
  include Glimmer::SWT::PropertyOwner
7
8
  attr_reader :parent,
8
9
  :args,
@@ -47,7 +48,10 @@ module Glimmer
47
48
  if @horizontal_alignment == 'fill'
48
49
  @parent.dom_element.css('width', '100%') if width_hint.nil?
49
50
  else
50
- @parent.dom_element.css('text-align', @horizontal_alignment)
51
+ @parent.dom_element.css('text-align', @horizontal_alignment.to_s)
52
+ @parent.dom_element.css('place-self', @horizontal_alignment.to_s)
53
+ @parent.dom_element.css('margin-left', 'auto') if ['right', 'center'].include?(@horizontal_alignment.to_s)
54
+ @parent.dom_element.css('margin-right', 'auto') if ['left', 'center'].include?(@horizontal_alignment.to_s)
51
55
  end
52
56
  # TODO
53
57
  # reapply
@@ -85,13 +89,15 @@ module Glimmer
85
89
 
86
90
  def grab_excess_horizontal_space=(grab_excess_horizontal_space)
87
91
  @grab_excess_horizontal_space = grab_excess_horizontal_space
88
- @parent.dom_element.css('width', "100%") if @grab_excess_horizontal_space && width_hint.nil?
92
+ @parent.dom_element.css('width', "100%") if @grab_excess_horizontal_space && @horizontal_alignment == 'fill' && width_hint.nil?
93
+ @parent.dom_element.css('justify-self', @horizontal_alignment) if @grab_excess_horizontal_space && @horizontal_alignment != 'fill' && width_hint.nil?
94
+ @parent.parent.dom_element.css('justify-content', "normal") if @grab_excess_horizontal_space
89
95
  # reapply
90
96
  end
91
97
 
92
98
  def grab_excess_vertical_space=(grab_excess_vertical_space)
93
99
  @grab_excess_vertical_space = grab_excess_vertical_space
94
- @parent.dom_element.css('height', "100%") if @grab_excess_vertical_space && height_hint.nil?
100
+ @parent.dom_element.css('height', "100%") if @grab_excess_vertical_space && @vertical_alignment == 'fill' && height_hint.nil?
95
101
  # TODO
96
102
  # reapply
97
103
  end
@@ -18,7 +18,7 @@ module Glimmer
18
18
  a_layout_class = Glimmer::SWT.const_get(class_name_main.to_sym) rescue Glimmer::SWT.const_get(class_name_alternative.to_sym)
19
19
  a_layout_class if a_layout_class.ancestors.include?(Glimmer::SWT::LayoutProxy)
20
20
  rescue => e
21
- puts "Layout #{keyword} was not found!"
21
+ Glimmer::Config.logger.debug "Layout #{keyword} was not found!"
22
22
  nil
23
23
  end
24
24
 
@@ -1,4 +1,5 @@
1
1
  require 'glimmer/swt/widget_proxy'
2
+ require 'glimmer/swt/display_proxy'
2
3
 
3
4
  module Glimmer
4
5
  module SWT
@@ -7,7 +8,7 @@ module Glimmer
7
8
 
8
9
  def initialize(parent, args, block)
9
10
  i = 0
10
- @parent = parent
11
+ @parent = parent || DisplayProxy.instance.shells.last
11
12
  @args = args
12
13
  @block = block
13
14
  @children = Set.new
@@ -29,16 +30,8 @@ module Glimmer
29
30
  dom_element.find('.modal-content .message').html(@text)
30
31
  end
31
32
 
32
- def document
33
- element = self
34
- begin
35
- element = element.parent
36
- end while(element.parent)
37
- element
38
- end
39
-
40
33
  def open
41
- document.post_initialize_child(self)
34
+ parent.post_initialize_child(self)
42
35
  end
43
36
 
44
37
  def hide
@@ -7,7 +7,7 @@ module Glimmer
7
7
  end
8
8
 
9
9
  def set_attribute(attribute_name, *args)
10
- send(attribute_setter(attribute_name), *args) unless send(attribute_getter(attribute_name)) == args.first
10
+ send(attribute_setter(attribute_name), *args) unless args.size == 1 && send(attribute_getter(attribute_name)) == args.first
11
11
  end
12
12
 
13
13
  def attribute_setter(attribute_name)
@@ -16,7 +16,7 @@ module Glimmer
16
16
 
17
17
  def attribute_getter(attribute_name)
18
18
  attribute_name.to_s.underscore
19
- end
19
+ end
20
20
  end
21
21
  end
22
22
  end
@@ -21,6 +21,7 @@ module Glimmer
21
21
  @layout.margin_width = 0
22
22
  @layout.margin_height = 0
23
23
  self.minimum_size = Point.new(WIDTH_MIN, HEIGHT_MIN)
24
+ DisplayProxy.instance.shells << self
24
25
  end
25
26
 
26
27
  def element
@@ -207,6 +208,7 @@ module Glimmer
207
208
  body_class = ([name] + css_classes.to_a).join(' ')
208
209
  @dom ||= html {
209
210
  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
210
212
  style(class: 'common-style') {
211
213
  style_dom_css
212
214
  }
@@ -246,6 +248,12 @@ module Glimmer
246
248
  style(class: 'scrolled-composite-style') {
247
249
  Glimmer::SWT::ScrolledCompositeProxy::STYLE
248
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
+ }
249
257
  }
250
258
  }.to_s
251
259
  end
@@ -1,12 +1,51 @@
1
1
  require 'glimmer/swt/widget_proxy'
2
+ require 'glimmer/swt/swt_proxy'
2
3
 
3
4
  module Glimmer
4
5
  module SWT
5
6
  class TableColumnProxy < WidgetProxy
7
+ STYLE = <<~CSS
8
+ th.table-column {
9
+ background: rgb(246, 246, 246);
10
+ text-align: left;
11
+ padding: 5px;
12
+ }
13
+
14
+ th.table-column .sort-direction {
15
+ float: right;
16
+ }
17
+ CSS
6
18
  include Glimmer
7
19
 
8
- attr_reader :text, :width
20
+ attr_accessor :sort_block, :sort_by_block
21
+ attr_reader :text, :width,
22
+ :no_sort, :sort_property, :editor
23
+ alias no_sort? no_sort
24
+
25
+ def initialize(parent, args, block)
26
+ @no_sort = args.delete(:no_sort)
27
+ super(parent, args, block)
28
+ unless no_sort?
29
+ content {
30
+ on_widget_selected { |event|
31
+ parent.sort_by_column!(self)
32
+ }
33
+ }
34
+ end
35
+ end
9
36
 
37
+ def sort_property=(args)
38
+ @sort_property = args unless args.empty?
39
+ end
40
+
41
+ def sort_direction
42
+ parent.sort_direction if parent.sort_column == self
43
+ end
44
+
45
+ def redraw_sort_direction
46
+ sort_icon_dom_element.attr('class', sort_icon_class)
47
+ end
48
+
10
49
  def text=(value)
11
50
  @text = value
12
51
  redraw
@@ -21,36 +60,56 @@ module Glimmer
21
60
  parent.columns_path
22
61
  end
23
62
 
24
- def css
25
- <<~CSS
26
- width: #{width}px;
27
- CSS
28
- end
29
-
30
63
  def element
31
64
  'th'
32
65
  end
33
66
 
67
+ def sort_icon_class
68
+ @sort_icon_class = 'sort-direction'
69
+ @sort_icon_class += (sort_direction == SWTProxy[:up] ? ' ui-icon ui-icon-caret-1-n' : ' ui-icon ui-icon-caret-1-s') unless sort_direction.nil?
70
+ @sort_icon_class
71
+ end
72
+
73
+ def sort_icon_dom_element
74
+ dom_element.find('.sort-direction')
75
+ end
76
+
77
+ # Sets editor (e.g. combo)
78
+ def editor=(*args)
79
+ @editor = args
80
+ end
81
+
82
+ def editable?
83
+ !@editor&.include?(:none)
84
+ end
85
+
34
86
  def observation_request_to_event_mapping
35
87
  {
36
88
  'on_widget_selected' => {
37
- event: 'click'
89
+ event: 'click',
90
+ event_handler: -> (event_listener) {
91
+ -> (event) {
92
+ event_listener.call(event)
93
+ redraw_sort_direction
94
+ }
95
+ }
38
96
  },
39
97
  }
40
- end
98
+ end
41
99
 
42
100
  def dom
43
101
  table_column_text = text
44
102
  table_column_id = id
45
- table_column_id_style = css
103
+ table_column_id_style = "width: #{width}px;"
46
104
  table_column_css_classes = css_classes
47
105
  table_column_css_classes << name
48
106
  @dom ||= html {
49
107
  th(id: table_column_id, style: table_column_id_style, class: table_column_css_classes.to_a.join(' ')) {
50
- table_column_text
108
+ span {table_column_text}
109
+ span(class: sort_icon_class)
51
110
  }
52
111
  }.to_s
53
- end
112
+ end
54
113
  end
55
114
  end
56
115
  end
@@ -0,0 +1,65 @@
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
+ require 'glimmer/swt/control_editor'
23
+
24
+ module Glimmer
25
+ module SWT
26
+ # Emulates SWT's native org.eclipse.swt.custom.TableEditor
27
+ class TableEditor < ControlEditor
28
+ alias table composite
29
+
30
+ def editor=(editor_widget, table_item, table_column_index)
31
+ # TODO consider making editor not gain an ID or gain a separate set of IDs to avoid clashing with standard widget predictability of ID
32
+ @table_item = table_item
33
+ @table_column_index = table_column_index
34
+ @editor_widget = editor_widget
35
+ @old_value = table_item.cell_dom_element(table_column_index).html
36
+ table_item.cell_dom_element(table_column_index).html('')
37
+ editor_widget.render(table_item.cell_dom_element(table_column_index))
38
+ # TODO tweak the width perfectly so it doesn't expand the table cell
39
+ # editor_widget.dom_element.css('width', 'calc(100% - 20px)')
40
+ editor_widget.dom_element.css('width', "#{minimumWidth}%") # TODO implement property with pixels (and perhaps derive percentage separately from pixels)
41
+ editor_widget.dom_element.css('height', "#{minimumHeight}px")
42
+ editor_widget.dom_element.add_class('table-editor')
43
+ # TODO consider relying on autofocus instead
44
+ editor_widget.dom_element.focus
45
+ # TODO consider doing the following line only for :text editor
46
+ editor_widget.dom_element.select
47
+ end
48
+ alias set_editor editor=
49
+ alias setEditor editor=
50
+
51
+ def cancel!
52
+ done!
53
+ end
54
+
55
+ def save!
56
+ done!
57
+ end
58
+
59
+ def done!
60
+ @table_item.cell_dom_element(@table_column_index).html(@old_value) unless @old_value.nil?
61
+ @old_value = nil
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,12 +1,43 @@
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 TableItemProxy < WidgetProxy
27
+ STYLE = <<~CSS
28
+ tr.table-item td {
29
+ padding-bottom: 0;
30
+ }
31
+ tr.table-item:nth-child(even):not(.selected) {
32
+ background: rgb(243, 244, 246);
33
+ }
34
+ CSS
35
+
6
36
  attr_reader :data
7
37
 
8
38
  def initialize(parent, args, block)
9
39
  super(parent, args, block)
40
+ # TODO check if there is a need to remove this observer when removing widget from table upon items update
10
41
  on_widget_selected { |event|
11
42
  parent.select(parent.index_of(self), event.meta?)
12
43
  }
@@ -53,8 +84,12 @@ module Glimmer
53
84
  'tr'
54
85
  end
55
86
 
87
+ def cell_dom_element(column_index)
88
+ dom_element.find("td:nth-child(#{column_index + 1})")
89
+ end
90
+
56
91
  def redraw
57
- super() #TODO re-enalbe and remove below lines
92
+ super() #TODO re-enable and remove below lines
58
93
 
59
94
  # TODO perhaps turn the following lambdas into methods
60
95
  table_item_edit_handler = lambda do |event, cancel = false|
@@ -104,6 +139,14 @@ module Glimmer
104
139
  redraw
105
140
  end
106
141
 
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
+
107
150
  def on_widget_selected(&block)
108
151
  event = 'click'
109
152
  delegate = $document.on(event, selector, &block)