glimmer-dsl-tk 0.0.25 → 0.0.29

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.
@@ -34,7 +34,9 @@ module Glimmer
34
34
  begin
35
35
  class_name = "#{keyword.camelcase(:upper)}Proxy".to_sym
36
36
  Glimmer::Tk.const_get(class_name)
37
- rescue
37
+ rescue => e
38
+ Glimmer::Config.logger.debug {"Unable to instantiate custom class name for #{keyword} ... defaulting to Glimmer::Tk::WidgetProxy"}
39
+ Glimmer::Config.logger.debug {e.full_message}
38
40
  Glimmer::Tk::WidgetProxy
39
41
  end
40
42
  end
@@ -54,13 +56,15 @@ module Glimmer
54
56
  tk_widget_class = eval(tk_widget_name)
55
57
  break
56
58
  rescue RuntimeError, SyntaxError, NameError => e
57
- Glimmer::Config.logger.debug e.full_message
59
+ Glimmer::Config.logger.debug {e.full_message}
58
60
  end
59
61
  end
60
- tk_widget_class
62
+ tk_widget_class if tk_widget_class.respond_to?(:new)
61
63
  end
62
64
  end
63
65
 
66
+ FONTS_PREDEFINED = %w[default text fixed menu heading caption small_caption icon tooltip]
67
+
64
68
  attr_reader :parent_proxy, :tk, :args, :keyword, :children
65
69
 
66
70
  # Initializes a new Tk Widget
@@ -104,6 +108,8 @@ module Glimmer
104
108
  @tk.send(attribute_setter(attribute), @tk.send(attribute))
105
109
  result = true
106
110
  rescue => e
111
+ Glimmer::Config.logger.debug { "No tk attribute setter for #{attribute}" }
112
+ Glimmer::Config.logger.debug { e.full_message }
107
113
  result = false
108
114
  end
109
115
  result
@@ -114,7 +120,8 @@ module Glimmer
114
120
  # TK Widget currently doesn't support respond_to? properly, so I have to resort to this trick for now
115
121
  @tk.send(attribute)
116
122
  true
117
- rescue
123
+ rescue => e
124
+ Glimmer::Config.logger.debug { "No tk attribute getter setter for #{attribute}" }
118
125
  false
119
126
  end
120
127
  end
@@ -125,7 +132,8 @@ module Glimmer
125
132
  begin
126
133
  @tk.tile_instate(attribute)
127
134
  true
128
- rescue
135
+ rescue => e
136
+ Glimmer::Config.logger.debug { "No tk state for #{attribute}" }
129
137
  false
130
138
  end
131
139
  else
@@ -144,45 +152,55 @@ module Glimmer
144
152
  tk_widget_has_attribute_getter_setter?(attribute) or
145
153
  has_state?(attribute) or
146
154
  has_attributes_attribute?(attribute) or
147
- respond_to?(attribute_setter(attribute), args)
155
+ respond_to?(attribute_setter(attribute), args) or
156
+ respond_to?(attribute_setter(attribute), *args, super_only: true) or
157
+ respond_to?(attribute, *args, super_only: true)
148
158
  end
149
159
 
150
160
  def set_attribute(attribute, *args)
151
- widget_custom_attribute = widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][attribute.to_s]
152
- if respond_to?(attribute, super_only: true)
153
- send(attribute, *args)
154
- elsif respond_to?(attribute_setter(attribute), super_only: true)
155
- send(attribute_setter(attribute), *args)
156
- elsif widget_custom_attribute
157
- widget_custom_attribute[:setter][:invoker].call(@tk, args)
158
- elsif tk_widget_has_attribute_setter?(attribute)
159
- unless args.size == 1 && @tk.send(attribute) == args.first
160
- if args.size == 1
161
- @tk.send(attribute_setter(attribute), *args)
161
+ begin
162
+ widget_custom_attribute = widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][attribute.to_s]
163
+ if respond_to?(attribute_setter(attribute), super_only: true)
164
+ send(attribute_setter(attribute), *args)
165
+ elsif respond_to?(attribute, super_only: true) && self.class.instance_method(attribute).parameters.size > 0
166
+ send(attribute, *args)
167
+ elsif widget_custom_attribute
168
+ widget_custom_attribute[:setter][:invoker].call(@tk, args)
169
+ elsif tk_widget_has_attribute_setter?(attribute)
170
+ unless args.size == 1 && @tk.send(attribute) == args.first
171
+ if args.size == 1
172
+ @tk.send(attribute_setter(attribute), *args)
173
+ else
174
+ @tk.send(attribute_setter(attribute), args)
175
+ end
176
+ end
177
+ elsif tk_widget_has_attribute_getter_setter?(attribute)
178
+ @tk.send(attribute, *args)
179
+ elsif has_state?(attribute)
180
+ attribute = attribute.sub(/=$/, '')
181
+ if !!args.first
182
+ @tk.tile_state(attribute)
162
183
  else
163
- @tk.send(attribute_setter(attribute), args)
184
+ @tk.tile_state("!#{attribute}")
164
185
  end
165
- end
166
- elsif tk_widget_has_attribute_getter_setter?(attribute)
167
- @tk.send(attribute, *args)
168
- elsif has_state?(attribute)
169
- attribute = attribute.sub(/=$/, '')
170
- if !!args.first
171
- @tk.tile_state(attribute)
186
+ elsif has_attributes_attribute?(attribute)
187
+ attribute = attribute.sub(/=$/, '')
188
+ @tk.attributes(attribute, args.first)
172
189
  else
173
- @tk.tile_state("!#{attribute}")
190
+ raise "#{self} cannot handle attribute #{attribute} with args #{args.inspect}"
174
191
  end
175
- elsif has_attributes_attribute?(attribute)
176
- attribute = attribute.sub(/=$/, '')
177
- @tk.attributes(attribute, args.first)
178
- else
179
- send(attribute_setter(attribute), args)
192
+ rescue => e
193
+ Glimmer::Config.logger.debug {"Failed to set attribute #{attribute} with args #{args.inspect}. Attempting to set through style instead..."}
194
+ Glimmer::Config.logger.debug {e.full_message}
195
+ apply_style(attribute => args.first)
180
196
  end
181
197
  end
182
198
 
183
199
  def get_attribute(attribute)
184
200
  widget_custom_attribute = widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][attribute.to_s]
185
- if widget_custom_attribute
201
+ if respond_to?(attribute, super_only: true)
202
+ send(attribute)
203
+ elsif widget_custom_attribute
186
204
  widget_custom_attribute[:getter][:invoker].call(@tk, args)
187
205
  elsif tk_widget_has_attribute_getter_setter?(attribute)
188
206
  @tk.send(attribute)
@@ -201,9 +219,17 @@ module Glimmer
201
219
  "#{attribute}="
202
220
  end
203
221
 
222
+ def style=(styles)
223
+ styles.each do |attribute, value|
224
+ apply_style(attribute => value)
225
+ end
226
+ end
227
+
204
228
  def grid(options = {})
205
229
  options = options.stringify_keys
206
230
  index_in_parent = @parent_proxy.children.index(self)
231
+ options['rowspan'] = options.delete('row_span') if options.keys.include?('row_span')
232
+ options['columnspan'] = options.delete('column_span') if options.keys.include?('column_span')
207
233
  options['rowweight'] = options.delete('row_weight') if options.keys.include?('row_weight')
208
234
  options['columnweight'] = options.delete('column_weight') if options.keys.include?('column_weight')
209
235
  options['columnweight'] = options['rowweight'] = options.delete('weight') if options.keys.include?('weight')
@@ -221,6 +247,25 @@ module Glimmer
221
247
  @tk.grid(options)
222
248
  end
223
249
 
250
+ def font=(value)
251
+ if (value.is_a?(Symbol) || value.is_a?(String)) && FONTS_PREDEFINED.include?(value.to_s.downcase)
252
+ @tk.font = "tk_#{value}_font".camelcase(:upper)
253
+ else
254
+ @tk.font = value.is_a?(TkFont) ? value : TkFont.new(value)
255
+ end
256
+ rescue => e
257
+ Glimmer::Config.logger.debug {"Failed to set attribute #{attribute} with args #{args.inspect}. Attempting to set through style instead..."}
258
+ Glimmer::Config.logger.debug {e.full_message}
259
+ apply_style({"font" => value})
260
+ end
261
+
262
+ def apply_style(options)
263
+ @@style_number = 0 unless defined?(@@style_number)
264
+ style = "style#{@@style_number += 1}.#{@tk.class.name.split('::').last}"
265
+ ::Tk::Tile::Style.configure(style, options)
266
+ @tk.style = style
267
+ end
268
+
224
269
  def widget_custom_attribute_mapping
225
270
  # TODO consider extracting to modules/subclasses
226
271
  @widget_custom_attribute_mapping ||= {
@@ -325,12 +370,20 @@ module Glimmer
325
370
  @tk.command {
326
371
  observer.call(@tk.textvariable&.value)
327
372
  }
373
+ @tk.validate('key')
374
+ @tk.validatecommand { |validate_args|
375
+ observer.call(validate_args.value)
376
+ new_icursor = validate_args.index
377
+ new_icursor += validate_args.string.size if validate_args.action == 1
378
+ @tk.icursor = new_icursor
379
+ true
380
+ }
328
381
  end,
329
382
  },
330
383
  ::Tk::Text => {
331
- 'text' => lambda do |observer|
384
+ 'value' => lambda do |observer|
332
385
  handle_listener('modified') do
333
- observer.call(text)
386
+ observer.call(value)
334
387
  end
335
388
  end,
336
389
  },
@@ -371,6 +424,7 @@ module Glimmer
371
424
  begin
372
425
  @tk.bind(listener_name, &listener)
373
426
  rescue => e
427
+ Glimmer::Config.logger.debug {"Unable to bind to #{listener_name} .. attempting to surround with <>"}
374
428
  Glimmer::Config.logger.debug {e.full_message}
375
429
  listener_name = "<#{listener_name}" if !listener_name.start_with?('<')
376
430
  listener_name = "#{listener_name}>" if !listener_name.end_with?('>')
@@ -74,8 +74,8 @@ class MetaSample
74
74
  def launch
75
75
  @root = root {
76
76
  title 'Glimmer Meta-Sample'
77
- width 700
78
- height 500
77
+ width 1280
78
+ height 720
79
79
 
80
80
  frame {
81
81
  grid row: 0, column: 0, column_weight: 0, row_weight: 1
@@ -87,7 +87,7 @@ class MetaSample
87
87
 
88
88
  on('command') do
89
89
  @selected_sample_index = index
90
- @code_text.text = File.read(file_path_for(selected_sample))
90
+ @code_text.value = File.read(file_path_for(selected_sample))
91
91
  end
92
92
  }
93
93
  end
@@ -102,7 +102,7 @@ class MetaSample
102
102
  parent_dir = File.join(Dir.home, '.glimmer-dsl-tk', 'samples', 'hello')
103
103
  FileUtils.mkdir_p(parent_dir)
104
104
  sample_file = File.join(parent_dir, "#{selected_sample.underscore}.rb")
105
- File.write(sample_file, @code_text.text)
105
+ File.write(sample_file, @code_text.value)
106
106
  FileUtils.cp_r(File.expand_path('../../icons', __dir__), File.dirname(File.dirname(parent_dir)))
107
107
  FileUtils.cp_r(File.expand_path('../hello/images', __dir__), parent_dir)
108
108
  sample_namespace_directory = File.expand_path("../hello/#{selected_sample.underscore}", __dir__)
@@ -120,7 +120,7 @@ class MetaSample
120
120
  text 'Reset'
121
121
 
122
122
  on('command') do
123
- @code_text.text = File.read(file_path_for(selected_sample))
123
+ @code_text.value = File.read(file_path_for(selected_sample))
124
124
  end
125
125
  }
126
126
  }
@@ -128,7 +128,7 @@ class MetaSample
128
128
 
129
129
  @code_text = text {
130
130
  grid row: 0, column: 1, column_weight: 1
131
- text File.read(file_path_for(selected_sample))
131
+ value File.read(file_path_for(selected_sample))
132
132
  }
133
133
  }
134
134
  @root.open
@@ -0,0 +1,68 @@
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-tk'
23
+
24
+ include Glimmer
25
+
26
+ root { |w|
27
+ title 'Hello, Built-in Dialog!'
28
+ width 400
29
+ height 400
30
+ x 150
31
+ y 150
32
+
33
+ frame {
34
+ %w[get_open_file get_multiple_open_file get_save_file choose_directory choose_color].each do |dialog|
35
+ button {
36
+ text dialog.split('_').map(&:capitalize).join(' ')
37
+
38
+ on('command') do
39
+ result = send(dialog, parent: w)
40
+ @result_label.text = [result].flatten.join("\n")
41
+ end
42
+ }
43
+ end
44
+
45
+ button {
46
+ text 'Choose Font'
47
+
48
+ on('command') do
49
+ choose_font(family: 'Courier New', size: '30', weight: 'bold') do |chosen_font|
50
+ @result_label.text = chosen_font(parent: w)
51
+ end
52
+ end
53
+ }
54
+ }
55
+
56
+ frame {
57
+ grid sticky: 'nsew', padx: 15, pady: 15
58
+
59
+ label {
60
+ grid row: 0, column: 0
61
+ text 'Result:'
62
+ }
63
+
64
+ @result_label = label {
65
+ grid row: 0, column: 1
66
+ }
67
+ }
68
+ }.open
@@ -1,3 +1,4 @@
1
+
1
2
  # Copyright (c) 2020-2021 Andy Maleh
2
3
  #
3
4
  # Permission is hereby granted, free of charge, to any person obtaining
@@ -44,7 +45,7 @@ class HelloCombobox
44
45
  title 'Hello, Combobox!'
45
46
 
46
47
  combobox {
47
- state 'readonly'
48
+ readonly true # this applies to text editing only (item selection still triggers a write to model)
48
49
  text <=> [person, :country]
49
50
  }
50
51
 
@@ -0,0 +1,159 @@
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-tk"
23
+ require "glimmer/tk/drag_and_drop_extension"
24
+
25
+ include Glimmer
26
+
27
+ root {
28
+ title "Hello, Drag and Drop!"
29
+ frame {
30
+ padding 5
31
+ labelframe {
32
+ text "Drag sources"
33
+ padding 5
34
+ label {
35
+ text "Entry"
36
+ grid :row => 0, :column => 0
37
+ }
38
+ entry {
39
+ text "Drag entry text"
40
+ width 30
41
+ grid :row => 0, :column => 1, :pady => 5, :sticky => "e"
42
+ on_drag_start { |event|
43
+ event.data = event.source.textvariable&.value
44
+ event.source.configure(:cursor => "hand2")
45
+ TkLabel.new(event.tooltip) {
46
+ text event.data + " "
47
+ bg "yellow"
48
+ bitmap "warning"
49
+ compound "right"
50
+ }.pack
51
+ }
52
+ on_drag_motion { |event|
53
+ if event.drop_accepted
54
+ event.source.configure(:cursor => "hand1")
55
+ else
56
+ event.source.configure(:cursor => "hand2")
57
+ end
58
+ event.tooltip.geometry("+#{event.x_root + 10}+#{event.y_root - 4}")
59
+ }
60
+ }
61
+ label {
62
+ text "Label"
63
+ grid :row => 1, :column => 0
64
+ }
65
+ label {
66
+ text "Drag label text"
67
+ width 30
68
+ grid :row => 1, :column => 1, :pady => 10, :sticky => "e"
69
+ drag_source true
70
+ }
71
+ label {
72
+ text "Combobox"
73
+ grid :row => 2, :column => 0
74
+ }
75
+ combobox {
76
+ text "Spain"
77
+ values %w[USA Canada Mexico Columbia UK Australia Germany Italy Spain]
78
+ width 27
79
+ grid :row => 2, :column => 1, :pady => 5, :sticky => "e"
80
+ on_drag_start { |event|
81
+ event.data = event.source.textvariable&.value
82
+ }
83
+ }
84
+ label {
85
+ text "Button"
86
+ grid :row => 3, :column => 0
87
+ }
88
+ button {
89
+ text "Drag it"
90
+ grid :row => 3, :column => 1, :pady => 5, :sticky => "w"
91
+ drag_source true
92
+ }
93
+ }
94
+
95
+ labelframe {
96
+ text "Drop targets"
97
+ grid :sticky => "nsew", :pady => 15
98
+ padding 5
99
+ label {
100
+ text "Entry"
101
+ grid :row => 0, :column => 0
102
+ }
103
+ entry {
104
+ width 30
105
+ grid :row => 0, :column => 1, :pady => 5, :sticky => "e"
106
+ on_drop { |event|
107
+ event.target.textvariable.value = event.data
108
+ }
109
+ }
110
+ label {
111
+ text "Label"
112
+ grid :row => 1, :column => 0
113
+ }
114
+ label {
115
+ width 30
116
+ grid :row => 1, :column => 1, :pady => 10, :sticky => "e"
117
+ borderwidth 2
118
+ relief "solid"
119
+ on_drop { |event|
120
+ event.target.textvariable.value = event.data
121
+ }
122
+ }
123
+ label {
124
+ text "Combobox"
125
+ grid :row => 2, :column => 0
126
+ }
127
+ combobox {
128
+ width 27
129
+ grid :row => 2, :column => 1, :pady => 5, :sticky => "e"
130
+ on_drop { |event|
131
+ event.target.textvariable.value = event.data
132
+ }
133
+ }
134
+ label {
135
+ text "Button"
136
+ grid :row => 3, :column => 0
137
+ }
138
+ button {
139
+ text "Drop here"
140
+ grid :row => 3, :column => 1, :pady => 5, :sticky => "w"
141
+ on_drop { |event|
142
+ event.target.text = event.data
143
+ }
144
+ }
145
+ label {
146
+ text "Checkbutton"
147
+ grid :row => 4, :column => 0
148
+ }
149
+ checkbutton {
150
+ text "Drop here to destroy a widget\n(except button)"
151
+ grid :row => 4, :column => 1, :pady => 5, :sticky => "w"
152
+ on_drop { |event|
153
+ event.target.text = event.data
154
+ event.source.destroy unless event.source.is_a? Tk::Button
155
+ }
156
+ }
157
+ }
158
+ }
159
+ }.open
@@ -38,7 +38,7 @@ class HelloEntry
38
38
  title 'Hello, Entry!'
39
39
 
40
40
  label {
41
- grid sticky: 'ew', column_weight: 1
41
+ grid sticky: 'ew'
42
42
  text 'default entry'
43
43
  }
44
44
  entry {
@@ -72,7 +72,7 @@ class HelloEntry
72
72
 
73
73
  ## this event kicks in just after the text variable is validated and before it is modified
74
74
  on('invalid') do |validate_args|
75
- @validated_entry_label.text = "#{validate_args.string} is not valid!"
75
+ @validated_entry_label.text = "#{validate_args.value} is not valid!"
76
76
  @validated_entry_label.foreground = 'red'
77
77
  end
78
78
 
@@ -27,8 +27,6 @@ root { |r|
27
27
  title 'Hello, Message Box!'
28
28
 
29
29
  frame {
30
- grid sticky: 'nsew', padx: 15, pady: 15
31
-
32
30
  button {
33
31
  text 'Please Click To Win a Surprise'
34
32
 
@@ -80,8 +78,6 @@ root { |r|
80
78
  }
81
79
 
82
80
  frame {
83
- grid sticky: 'nsew', padx: 15, pady: 15
84
-
85
81
  label {
86
82
  grid row: 0, column: 0
87
83
  text 'Result:'
@@ -0,0 +1,69 @@
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-tk'
23
+
24
+ include Glimmer
25
+
26
+ root {
27
+ title 'Hello, Separator!'
28
+ width 200
29
+ height 200
30
+
31
+ label {
32
+ grid row: 0, column: 0, min_width: 100, min_height: 100, column_weight: 0, sticky: 'nsew'
33
+ text 'Label 1'
34
+ anchor 'center'
35
+ }
36
+
37
+ separator {
38
+ grid row: 0, column: 1
39
+ orient 'vertical'
40
+ }
41
+
42
+ label {
43
+ grid row: 0, column: 2, min_width: 100, min_height: 100, sticky: 'nsew'
44
+ text 'Label 2'
45
+ anchor 'center'
46
+ }
47
+
48
+ separator {
49
+ grid row: 1, column: 0, column_span: 3
50
+ orient 'horizontal'
51
+ }
52
+
53
+ label {
54
+ grid row: 2, column: 0, min_width: 100, min_height: 100, sticky: 'nsew'
55
+ text 'Label 3'
56
+ anchor 'center'
57
+ }
58
+
59
+ separator {
60
+ grid row: 2, column: 1
61
+ orient 'vertical'
62
+ }
63
+
64
+ label {
65
+ grid row: 2, column: 2, min_width: 100, min_height: 100, sticky: 'nsew'
66
+ text 'Label 4'
67
+ anchor 'center'
68
+ }
69
+ }.open