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
@@ -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'
@@ -12,26 +12,32 @@ module Glimmer
12
12
  include DataBinding::Observer
13
13
 
14
14
  def initialize(parent, model_binding, column_properties)
15
- @last_model_collection = nil
15
+ @last_populated_model_collection = nil
16
16
  @table = parent
17
17
  @model_binding = model_binding
18
18
  @column_properties = column_properties
19
- if @table.respond_to?(:column_properties=)
20
- @table.column_properties = @column_properties
21
- ##else # assume custom widget
22
- ## @table.body_root.column_properties = @column_properties
23
- end
24
- call(@model_binding.evaluate_property)
25
- model = model_binding.base_model
26
- observe(model, model_binding.property_name_expression)
19
+ @table.data = @model_binding
27
20
  ##@table.on_widget_disposed do |dispose_event| # doesn't seem needed within Opal
28
21
  ## unregister_all_observables
29
22
  ##end
23
+ if @table.respond_to?(:column_properties=)
24
+ @table.column_properties = @column_properties
25
+ else # assume custom widget
26
+ @table.body_root.column_properties = @column_properties
27
+ end
28
+ @table_observer_registration = observe(model_binding)
29
+ call
30
30
  end
31
31
 
32
32
  def call(new_model_collection=nil)
33
+ new_model_collection = @model_binding.evaluate_property # this ensures applying converters (e.g. :on_read)
34
+ table_cells = @table.items.map {|item| @table.column_properties.size.times.map {|i| item.get_text(i)} }
35
+ model_cells = new_model_collection.to_a.map {|m| @table.cells_for(m)}
36
+ return if table_cells == model_cells
33
37
  if new_model_collection and new_model_collection.is_a?(Array)
34
- observe(new_model_collection, @column_properties)
38
+ @table_items_observer_registration&.unobserve
39
+ @table_items_observer_registration = observe(new_model_collection, @column_properties)
40
+ add_dependent(@table_observer_registration => @table_items_observer_registration)
35
41
  @model_collection = new_model_collection
36
42
  end
37
43
  populate_table(@model_collection, @table, @column_properties)
@@ -39,8 +45,10 @@ module Glimmer
39
45
  end
40
46
 
41
47
  def populate_table(model_collection, parent, column_properties)
42
- return if model_collection&.sort_by(&:hash) == @last_model_collection&.sort_by(&:hash)
43
- @last_model_collection = model_collection
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
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)}}
44
52
  # TODO improve performance
45
53
  selected_table_item_models = parent.selection.map(&:get_data)
46
54
  old_items = parent.items
@@ -54,17 +62,22 @@ module Glimmer
54
62
  table_item.set_data(model)
55
63
  table_item.id = old_item_ids_per_model[model.hash] if old_item_ids_per_model[model.hash]
56
64
  end
57
- selected_table_items = parent.search {|item| selected_table_item_models.include?(item.get_data) }
58
- selected_table_items = [parent.items.first] if selected_table_items.empty? && !parent.items.empty?
59
- parent.selection = selected_table_items unless selected_table_items.empty?
65
+ parent.selection = parent.search {|item| selected_table_item_models.include?(item.get_data) }
60
66
  parent.redraw
61
67
  end
62
68
 
63
69
  def sort_table(model_collection, parent, column_properties)
64
- return if model_collection == @last_model_collection
65
- parent.items = parent.items.sort_by { |item| model_collection.index(item.get_data) }
66
- @last_model_collection = model_collection
67
- end
70
+ return if model_collection == @last_sorted_model_collection
71
+ if model_collection == @last_populated_model_collection
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
74
+ parent.sort!
75
+ else
76
+ # The model collection was sorted by the model, but beyond sorting, it did not change from the last populated model collection.
77
+ parent.items = parent.items.sort_by { |item| model_collection.index(item.get_data) }
78
+ @last_sorted_model_collection = @last_populated_model_collection = model_collection
79
+ end
80
+ end
68
81
  end
69
82
  end
70
83
  end
@@ -0,0 +1,41 @@
1
+ # Copyright (c) 2007-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/dsl/expression'
23
+
24
+ module Glimmer
25
+ module DSL
26
+ module Opal
27
+ class BlockPropertyExpression < Expression
28
+ def can_interpret?(parent, keyword, *args, &block)
29
+ block_given? and
30
+ args.size == 0 and
31
+ parent.respond_to?("#{keyword}_block=")
32
+ end
33
+
34
+ def interpret(parent, keyword, *args, &block)
35
+ parent.send("#{keyword}_block=", block)
36
+ nil
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -42,6 +42,11 @@ module Glimmer
42
42
  end
43
43
 
44
44
  def interpret(parent, keyword, *args, &block)
45
+ begin
46
+ require `localStorage[#{keyword}]`
47
+ rescue => e
48
+ Glimmer::Config.logger.debug e.message
49
+ end
45
50
  custom_widget_class = UI::CustomWidget.for(keyword)
46
51
  # TODO clean code by extracting methods into CustomShell
47
52
  if !Glimmer::UI::CustomShell.requested? && custom_widget_class&.ancestors&.to_a.include?(Glimmer::UI::CustomShell)
@@ -25,6 +25,7 @@ require 'glimmer/dsl/opal/custom_widget_expression'
25
25
  require 'glimmer/dsl/opal/swt_expression'
26
26
  require 'glimmer/dsl/opal/radio_group_selection_data_binding_expression'
27
27
  require 'glimmer/dsl/opal/checkbox_group_selection_data_binding_expression'
28
+ require 'glimmer/dsl/opal/block_property_expression'
28
29
 
29
30
  module Glimmer
30
31
  module DSL
@@ -42,6 +43,7 @@ module Glimmer
42
43
  data_binding
43
44
  font
44
45
  layout
46
+ block_property
45
47
  property
46
48
  widget
47
49
  ]
@@ -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
 
@@ -24,6 +24,10 @@ module Glimmer
24
24
  Document
25
25
  end
26
26
 
27
+ def shells
28
+ @shells ||= []
29
+ end
30
+
27
31
  def render
28
32
  # No rendering as body is rendered as part of ShellProxy.. this class only serves as an SWT Display utility
29
33
  end
@@ -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)