glimmer-dsl-opal 0.10.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +84 -22
  4. data/VERSION +1 -1
  5. data/lib/display.rb +3 -0
  6. data/lib/glimmer-dsl-opal.rb +1 -1
  7. data/lib/glimmer-dsl-opal/ext/glimmer/dsl/engine.rb +5 -2
  8. data/lib/glimmer-dsl-opal/samples/elaborate/contact_manager.rb +17 -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/hello/hello_button.rb +1 -1
  12. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox.rb +4 -4
  13. data/lib/glimmer-dsl-opal/samples/hello/hello_combo.rb +1 -1
  14. data/lib/glimmer-dsl-opal/samples/hello/hello_computed.rb +5 -5
  15. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_shell.rb +1 -1
  16. data/lib/glimmer-dsl-opal/samples/hello/hello_date_time.rb +4 -4
  17. data/lib/glimmer-dsl-opal/samples/hello/hello_group.rb +6 -6
  18. data/lib/glimmer-dsl-opal/samples/hello/hello_list_multi_selection.rb +1 -1
  19. data/lib/glimmer-dsl-opal/samples/hello/hello_list_single_selection.rb +1 -1
  20. data/lib/glimmer-dsl-opal/samples/hello/hello_radio.rb +6 -6
  21. data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +2 -2
  22. data/lib/glimmer/data_binding/table_items_binding.rb +3 -2
  23. data/lib/glimmer/dsl/opal/bind_expression.rb +24 -25
  24. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +6 -6
  25. data/lib/glimmer/dsl/opal/dsl.rb +4 -0
  26. data/lib/glimmer/dsl/opal/menu_expression.rb +1 -1
  27. data/lib/glimmer/dsl/opal/message_box_expression.rb +1 -1
  28. data/lib/glimmer/dsl/opal/property_expression.rb +2 -1
  29. data/lib/glimmer/dsl/opal/shape_expression.rb +26 -0
  30. data/lib/glimmer/dsl/opal/shell_expression.rb +1 -1
  31. data/lib/glimmer/dsl/opal/shine_data_binding_expression.rb +49 -0
  32. data/lib/glimmer/dsl/opal/table_items_data_binding_expression.rb +2 -2
  33. data/lib/glimmer/dsl/opal/widget_expression.rb +1 -1
  34. data/lib/glimmer/swt/combo_proxy.rb +1 -0
  35. data/lib/glimmer/swt/composite_proxy.rb +18 -2
  36. data/lib/glimmer/swt/dialog_proxy.rb +47 -32
  37. data/lib/glimmer/swt/display_proxy.rb +128 -9
  38. data/lib/glimmer/swt/grid_layout_proxy.rb +28 -33
  39. data/lib/glimmer/swt/latest_dialog_proxy.rb +3 -1
  40. data/lib/glimmer/swt/latest_message_box_proxy.rb +3 -1
  41. data/lib/glimmer/swt/latest_shell_proxy.rb +6 -2
  42. data/lib/glimmer/swt/layout_proxy.rb +32 -9
  43. data/lib/glimmer/swt/message_box_proxy.rb +20 -10
  44. data/lib/glimmer/swt/row_layout_proxy.rb +13 -6
  45. data/lib/glimmer/swt/shell_proxy.rb +30 -8
  46. data/lib/glimmer/swt/table_proxy.rb +19 -3
  47. data/lib/glimmer/swt/widget_proxy.rb +50 -19
  48. data/lib/glimmer/ui/custom_shell.rb +23 -6
  49. data/lib/glimmer/util/proc_tracker.rb +16 -5
  50. metadata +11 -9
@@ -0,0 +1,49 @@
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
+
22
+ require 'glimmer/dsl/expression'
23
+ require 'glimmer/data_binding/model_binding'
24
+ require 'glimmer/swt/table_proxy'
25
+ require 'glimmer/data_binding/shine'
26
+
27
+ module Glimmer
28
+ module DSL
29
+ module Opal
30
+ class ShineDataBindingExpression < Expression
31
+ def can_interpret?(parent, keyword, *args, &block)
32
+ args.size == 0 and
33
+ block.nil? and
34
+ (
35
+ (parent.respond_to?(:set_attribute) and parent.respond_to?(keyword)) or
36
+ (parent.is_a?(Glimmer::SWT::TableProxy)) # TODO support tree element
37
+ )
38
+ # TODO support canvas elements
39
+ # and
40
+ # !(parent.respond_to?(:swt_widget) && parent.swt_widget.class == org.eclipse.swt.widgets.Canvas && keyword == 'image')
41
+ end
42
+
43
+ def interpret(parent, keyword, *args, &block)
44
+ Glimmer::DataBinding::Shine.new(parent, keyword)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -12,10 +12,10 @@ module Glimmer
12
12
  keyword == "items" and
13
13
  block.nil? and
14
14
  parent.is_a?(Glimmer::SWT::TableProxy) and
15
- args.size == 2 and
15
+ args.size.between?(1, 2) and
16
16
  args[0].is_a?(DataBinding::ModelBinding) and
17
17
  args[0].evaluate_property.is_a?(Array) and
18
- args[1].is_a?(Array)
18
+ (args[1].nil? or args[1].is_a?(Array))
19
19
  end
20
20
 
21
21
  def interpret(parent, keyword, *args, &block)
@@ -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)
@@ -4,8 +4,6 @@ require 'glimmer/swt/widget_proxy'
4
4
  module Glimmer
5
5
  module SWT
6
6
  class CompositeProxy < WidgetProxy
7
- attr_reader :layout
8
-
9
7
  def initialize(parent, args, block)
10
8
  super(parent, args, block)
11
9
  @layout = default_layout
@@ -27,9 +25,27 @@ module Glimmer
27
25
  def layout=(the_layout)
28
26
  @layout = the_layout
29
27
  end
28
+ alias set_layout layout=
29
+ alias setLayout layout=
30
30
 
31
+ def get_layout
32
+ @layout
33
+ end
34
+ alias getLayout get_layout #TODO consider pregenerating these aliases with an easy method in the future
35
+
36
+ def pack(*args)
37
+ # No Op (just a shim) TODO consider if it should be implemented
38
+ end
39
+
40
+ def layout​(changed = nil, all = nil)
41
+ # TODO implement layout(changed = nil, all = nil) just as per SWT API
42
+ @layout&.layout(self, changed)
43
+ end
44
+
31
45
  end
32
46
 
47
+ CanvasProxy = CompositeProxy # TODO implement fully eventually
48
+
33
49
  end
34
50
 
35
51
  end
@@ -63,14 +63,12 @@ module Glimmer
63
63
  i = 0
64
64
  @parent = parent
65
65
  @parent = nil if parent.is_a?(LatestShellProxy)
66
- @parent ||= DisplayProxy.instance.shells.last || ShellProxy.new([])
66
+ @parent ||= DisplayProxy.instance.shells.detect(&:open?) || ShellProxy.new([])
67
67
  @args = args
68
68
  @block = block
69
69
  @children = Set.new
70
70
  @enabled = true
71
- # on_widget_selected {
72
- # hide
73
- # }
71
+ DisplayProxy.instance.opened_dialogs.last&.suspend_event_handling
74
72
  DisplayProxy.instance.dialogs << self
75
73
  @parent.post_initialize_child(self)
76
74
  end
@@ -84,35 +82,39 @@ module Glimmer
84
82
  end
85
83
  end
86
84
 
85
+ def open?
86
+ @open
87
+ end
88
+
87
89
  def open
88
- unless @init
89
- dom_element.remove_class('hide')
90
- dom_element.dialog({'auto_open' => false})
91
- @init = true
92
- dom_element.dialog('option', 'appendTo', parent.path)
93
- dom_element.dialog('option', 'modal', true) # NOTE: Not Working! Doing manually below by relying on overlay in ShellProxy.
94
- if DisplayProxy.instance.dialogs.size == 1 # only add for first dialog open
95
- Element['.dialog-overlay'].remove_class('hide')
96
- end
97
- dom_element.dialog('option', 'closeOnEscape', true)
98
- dom_element.dialog('option', 'draggable', true)
99
- dom_element.dialog('option', 'width', 'auto')
100
- dom_element.dialog('option', 'minHeight', 'none')
101
- dom_element.on('dialogclose') do
102
- unless @hiding
103
- close
104
- else
105
- @hiding = false
90
+ owned_proc = Glimmer::Util::ProcTracker.new(owner: self, invoked_from: :open) {
91
+ shell.open(async: false) unless shell.open?
92
+ unless @init
93
+ dom_element.remove_class('hide')
94
+ dom_element.dialog('auto_open' => false)
95
+ @init = true
96
+ dom_element.dialog('option', 'appendTo', parent.path)
97
+ dom_element.dialog('option', 'modal', true) # NOTE: Not Working! Doing manually below by relying on overlay in ShellProxy.
98
+ unless DisplayProxy.instance.dialogs.any?(&:open?) # only add for first dialog open
99
+ Element['.dialog-overlay'].remove_class('hide')
100
+ end
101
+ dom_element.dialog('option', 'closeOnEscape', true)
102
+ dom_element.dialog('option', 'draggable', true)
103
+ dom_element.dialog('option', 'width', 'auto')
104
+ dom_element.dialog('option', 'minHeight', 'none')
105
+ dom_element.on('dialogclose') do
106
+ unless @hiding
107
+ close
108
+ else
109
+ @hiding = false
110
+ end
106
111
  end
112
+ else
113
+ dom_element.dialog('open')
107
114
  end
108
- else
109
- dom_element.dialog('open')
110
- end
111
- @open = true
112
- end
113
-
114
- def open?
115
- @open
115
+ @open = true
116
+ }
117
+ DisplayProxy.instance.async_exec(owned_proc)
116
118
  end
117
119
 
118
120
  def hide
@@ -128,12 +130,15 @@ module Glimmer
128
130
  @open = false
129
131
  @init = false
130
132
  Element['.dialog-overlay'].add_class('hide') unless DisplayProxy.instance.dialogs.any?(&:open?)
133
+ parent.children.delete(self)
134
+ shell.close if shell.children.empty?
131
135
  DisplayProxy.instance.dialogs.delete(self)
136
+ DisplayProxy.instance.opened_dialogs.last&.resume_event_handling
132
137
  end
133
138
 
134
139
 
135
140
  def content(&block)
136
- 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)
137
142
  end
138
143
 
139
144
  def path
@@ -144,6 +149,16 @@ module Glimmer
144
149
  end
145
150
  end
146
151
 
152
+ def suspend_event_handling
153
+ super
154
+ Element["[aria-describedby=#{id}]"].css('z-index', 9)
155
+ end
156
+
157
+ def resume_event_handling
158
+ super
159
+ Element["[aria-describedby=#{id}]"].css('z-index', 100)
160
+ end
161
+
147
162
  # def selector
148
163
  # super + ' .close'
149
164
  # end
@@ -162,7 +177,7 @@ module Glimmer
162
177
 
163
178
  def dom
164
179
  @dom ||= html {
165
- div(id: id, class: "#{name} hide", title: text) {
180
+ div(id: id, class: "#{name} modal hide", title: text) {
166
181
  }
167
182
  }.to_s
168
183
  end
@@ -1,3 +1,5 @@
1
+ require 'glimmer/swt/widget_proxy'
2
+
1
3
  module Glimmer
2
4
  module SWT
3
5
  class DisplayProxy < WidgetProxy
@@ -5,6 +7,9 @@ module Glimmer
5
7
  def instance
6
8
  @instance ||= new
7
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
8
13
  end
9
14
 
10
15
  def initialize
@@ -36,20 +41,46 @@ module Glimmer
36
41
  @dialogs ||= []
37
42
  end
38
43
 
44
+ def modals
45
+ message_boxes + dialogs
46
+ end
47
+
48
+ def message_box_open?
49
+ message_boxes.any?(&:open?)
50
+ end
51
+
52
+ def dialog_open?
53
+ dialogs.any?(&:open?)
54
+ end
55
+
56
+ def opened_dialogs
57
+ dialogs.select(&:open?)
58
+ end
59
+
60
+ def modal_open?
61
+ message_box_open? or dialog_open?
62
+ end
63
+
39
64
  def render
40
65
  # No rendering as body is rendered as part of ShellProxy.. this class only serves as an SWT Display utility
41
66
  end
42
67
 
43
- def async_exec(&block)
44
- executer = lambda do
45
- if Document.find('.modal').to_a.empty?
46
- block.call
47
- else
48
- sleep(0.05)
49
- Async::Task.new(&executer)
50
- end
68
+ def beep
69
+ # TODO (simulate beep from SWT display flashing the screen and making a noise if possible)
70
+ end
71
+
72
+ def async_exec(proc_tracker = nil, &block)
73
+ block = proc_tracker unless proc_tracker.nil?
74
+ queue = nil # general queue
75
+ if !proc_tracker.nil? && proc_tracker.invoked_from.to_s == 'open' && modal_open? &&
76
+ (
77
+ proc_tracker.owner.is_a?(MessageBoxProxy) ||
78
+ (dialog_open? && proc_tracker.owner.is_a?(DialogProxy) && opened_dialogs.last == WidgetProxy.widget_handling_listener&.dialog_ancestor)
79
+ )
80
+ queue = WidgetProxy.widget_handling_listener
51
81
  end
52
- Async::Task.new(&executer)
82
+ return block.call if !modal_open?
83
+ schedule_async_exec(block, queue)
53
84
  end
54
85
  # sync_exec kept for API compatibility reasons
55
86
  alias sync_exec async_exec
@@ -64,7 +95,34 @@ module Glimmer
64
95
  event.singleton_class.define_method(:character) do
65
96
  which || key_code
66
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 }
67
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
68
126
  }
69
127
  }
70
128
  },
@@ -75,13 +133,74 @@ module Glimmer
75
133
  event.singleton_class.define_method(:character) do
76
134
  which || key_code
77
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 }
78
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
79
164
  }
80
165
  }
81
166
  }
82
167
  ]
83
168
  }
84
169
  end
170
+
171
+ private
172
+
173
+ def async_exec_queues
174
+ @async_exec_queues ||= {}
175
+ end
176
+
177
+ def async_exec_queue(widget_handling_listener = nil)
178
+ async_exec_queues[widget_handling_listener] ||= []
179
+ end
180
+
181
+ def no_widget_handling_listener_work?
182
+ async_exec_queues.reject {|key, value| key.nil?}.values.reduce(:+).to_a.empty?
183
+ end
184
+
185
+ def schedule_async_exec(block, queue)
186
+ async_exec_queue(queue).unshift(block)
187
+
188
+ # TODO consider the need for locking to avoid race conditions (rare or impossible case)
189
+ if async_exec_queue(queue).size == 1
190
+ executer = lambda do
191
+ # queue could be a widget handling listener queue
192
+ # TODO see if there are more intricate cases of opening a dialog from a widget listener handler
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?)
194
+ block = async_exec_queue(queue).pop
195
+ block&.call
196
+ Async::Task.new(delay: 1, &executer) if async_exec_queue(queue).any?
197
+ else
198
+ Async::Task.new(delay: 100, &executer)
199
+ end
200
+ end
201
+ Async::Task.new(delay: 1, &executer)
202
+ end
203
+ end
85
204
  end
86
205
  end
87
206
  end
@@ -14,39 +14,46 @@ module Glimmer
14
14
 
15
15
  attr_reader :num_columns, :make_columns_equal_width, :horizontal_spacing, :vertical_spacing, :margin_width, :margin_height
16
16
 
17
- def initialize(parent, args)
18
- super(parent, args)
19
- self.horizontal_spacing = 10
20
- self.vertical_spacing = 10
21
- self.margin_width = 15
22
- self.margin_height = 15
23
- self.num_columns = @args.first || 1
24
- reapply
25
- end
26
-
27
17
  def num_columns=(columns)
28
18
  @num_columns = columns
29
19
  # TODO do the following instead of reapply
30
20
  # @parent.add_css_class("num-columns-#{@num_columns}")
31
- reapply
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
32
39
  end
33
40
 
34
41
  def make_columns_equal_width=(equal_width)
35
42
  @make_columns_equal_width = equal_width
36
43
  # @parent.add_css_class('make_columns_equal_width') if @make_columns_equal_width
37
- reapply
44
+ # reinitialize # TODO reimplement without using reinitialize
38
45
  end
39
46
 
40
47
  def horizontal_spacing=(spacing)
41
48
  @horizontal_spacing = spacing
42
49
  # @parent.add_css_class("horizontal-spacing-#{@horizontal_spacing}")
43
- reapply
50
+ # reinitialize # TODO reimplement without using reinitialize
44
51
  end
45
52
 
46
53
  def vertical_spacing=(spacing)
47
54
  @vertical_spacing = spacing
48
55
  # @parent.add_css_class("vertical-spacing-#{@vertical_spacing}")
49
- reapply
56
+ # reinitialize # TODO reimplement without using reinitialize
50
57
  end
51
58
 
52
59
  def margin_width=(pixels)
@@ -66,25 +73,13 @@ module Glimmer
66
73
  @parent.dom_element.css('padding-bottom', effective_margin_height)
67
74
  end
68
75
 
69
- def reapply
70
- # TODO get rid of this method
71
- layout_css = <<~CSS
72
- grid-template-columns: #{'auto ' * @num_columns.to_i};
73
- grid-row-gap: #{@vertical_spacing}px;
74
- grid-column-gap: #{@horizontal_spacing}px;
75
- CSS
76
- if @parent.css_classes.include?('grid-layout')
77
- layout_css.split(";").map(&:strip).map {|l| l.split(':').map(&:strip)}.each do |key, value|
78
- @parent.dom_element.css(key, value) unless key.nil?
79
- end
80
- if @parent.is_a?(GroupProxy)
81
- @parent.dom_element.find('legend').css('grid-column-start', "span #{@num_columns.to_i}")
82
- end
83
- else
84
- layout_css.split(";").map(&:strip).map {|l| l.split(':').map(&:strip)}.each do |key, value|
85
- @parent.dom_element.css(key, 'initial') unless key.nil?
86
- end
87
- end
76
+ def initialize(parent, args)
77
+ super(parent, args)
78
+ self.horizontal_spacing = 10
79
+ self.vertical_spacing = 10
80
+ self.margin_width = 15
81
+ self.margin_height = 15
82
+ self.num_columns = @args.first || 1
88
83
  end
89
84
  end
90
85
  end