cyberarm_engine 0.24.3 → 0.24.5

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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -8
  3. data/.rubocop.yml +7 -7
  4. data/.travis.yml +5 -5
  5. data/Gemfile +6 -6
  6. data/Gemfile.lock +24 -0
  7. data/LICENSE.txt +21 -21
  8. data/README.md +74 -74
  9. data/Rakefile +10 -10
  10. data/assets/shaders/fragment/g_buffer.glsl +30 -30
  11. data/assets/shaders/fragment/lighting.glsl +115 -69
  12. data/assets/shaders/include/light_struct.glsl +11 -11
  13. data/assets/shaders/include/material_struct.glsl +16 -16
  14. data/assets/shaders/vertex/g_buffer.glsl +28 -28
  15. data/assets/shaders/vertex/lighting.glsl +24 -24
  16. data/bin/console +14 -14
  17. data/bin/setup +8 -8
  18. data/cyberarm_engine.gemspec +36 -36
  19. data/lib/cyberarm_engine/animator.rb +219 -219
  20. data/lib/cyberarm_engine/background.rb +180 -179
  21. data/lib/cyberarm_engine/background_image.rb +93 -93
  22. data/lib/cyberarm_engine/background_nine_slice.rb +142 -142
  23. data/lib/cyberarm_engine/bounding_box.rb +150 -150
  24. data/lib/cyberarm_engine/builtin/intro_state.rb +130 -130
  25. data/lib/cyberarm_engine/cache/download_manager.rb +123 -123
  26. data/lib/cyberarm_engine/cache.rb +4 -4
  27. data/lib/cyberarm_engine/common.rb +128 -128
  28. data/lib/cyberarm_engine/config_file.rb +46 -46
  29. data/lib/cyberarm_engine/console/command.rb +157 -157
  30. data/lib/cyberarm_engine/console/commands/help_command.rb +43 -43
  31. data/lib/cyberarm_engine/console/subcommand.rb +99 -99
  32. data/lib/cyberarm_engine/console.rb +248 -248
  33. data/lib/cyberarm_engine/game_object.rb +244 -244
  34. data/lib/cyberarm_engine/game_state.rb +124 -124
  35. data/lib/cyberarm_engine/gosu_ext/draw_arc.rb +97 -97
  36. data/lib/cyberarm_engine/gosu_ext/draw_circle.rb +30 -30
  37. data/lib/cyberarm_engine/gosu_ext/draw_path.rb +17 -17
  38. data/lib/cyberarm_engine/model/material.rb +21 -21
  39. data/lib/cyberarm_engine/model/{model_object.rb → mesh.rb} +131 -131
  40. data/lib/cyberarm_engine/model/parser.rb +74 -74
  41. data/lib/cyberarm_engine/model/parsers/collada_parser.rb +138 -138
  42. data/lib/cyberarm_engine/model/parsers/wavefront_parser.rb +154 -154
  43. data/lib/cyberarm_engine/model.rb +216 -213
  44. data/lib/cyberarm_engine/model_cache.rb +31 -31
  45. data/lib/cyberarm_engine/notification.rb +82 -82
  46. data/lib/cyberarm_engine/notification_manager.rb +241 -241
  47. data/lib/cyberarm_engine/opengl/light.rb +52 -50
  48. data/lib/cyberarm_engine/opengl/orthographic_camera.rb +46 -46
  49. data/lib/cyberarm_engine/opengl/perspective_camera.rb +41 -38
  50. data/lib/cyberarm_engine/opengl/renderer/bounding_box_renderer.rb +249 -249
  51. data/lib/cyberarm_engine/opengl/renderer/g_buffer.rb +167 -165
  52. data/lib/cyberarm_engine/opengl/renderer/opengl_renderer.rb +307 -304
  53. data/lib/cyberarm_engine/opengl/renderer/renderer.rb +33 -33
  54. data/lib/cyberarm_engine/opengl/shader.rb +408 -406
  55. data/lib/cyberarm_engine/opengl/texture.rb +69 -69
  56. data/lib/cyberarm_engine/opengl.rb +53 -40
  57. data/lib/cyberarm_engine/ray.rb +56 -56
  58. data/lib/cyberarm_engine/stats.rb +200 -200
  59. data/lib/cyberarm_engine/text.rb +260 -260
  60. data/lib/cyberarm_engine/timer.rb +23 -23
  61. data/lib/cyberarm_engine/transform.rb +296 -296
  62. data/lib/cyberarm_engine/trees/aabb_node.rb +126 -0
  63. data/lib/cyberarm_engine/trees/aabb_tree.rb +55 -0
  64. data/lib/cyberarm_engine/trees/aabb_tree_debug.rb +29 -0
  65. data/lib/cyberarm_engine/ui/border_canvas.rb +102 -102
  66. data/lib/cyberarm_engine/ui/dsl.rb +142 -142
  67. data/lib/cyberarm_engine/ui/element.rb +662 -662
  68. data/lib/cyberarm_engine/ui/elements/button.rb +100 -100
  69. data/lib/cyberarm_engine/ui/elements/check_box.rb +54 -54
  70. data/lib/cyberarm_engine/ui/elements/container.rb +404 -410
  71. data/lib/cyberarm_engine/ui/elements/edit_box.rb +179 -179
  72. data/lib/cyberarm_engine/ui/elements/edit_line.rb +297 -297
  73. data/lib/cyberarm_engine/ui/elements/flow.rb +15 -15
  74. data/lib/cyberarm_engine/ui/elements/image.rb +72 -72
  75. data/lib/cyberarm_engine/ui/elements/list_box.rb +79 -79
  76. data/lib/cyberarm_engine/ui/elements/menu.rb +27 -27
  77. data/lib/cyberarm_engine/ui/elements/menu_item.rb +6 -6
  78. data/lib/cyberarm_engine/ui/elements/progress.rb +93 -93
  79. data/lib/cyberarm_engine/ui/elements/radio.rb +6 -6
  80. data/lib/cyberarm_engine/ui/elements/slider.rb +107 -107
  81. data/lib/cyberarm_engine/ui/elements/stack.rb +11 -11
  82. data/lib/cyberarm_engine/ui/elements/text_block.rb +216 -216
  83. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +67 -67
  84. data/lib/cyberarm_engine/ui/event.rb +54 -54
  85. data/lib/cyberarm_engine/ui/gui_state.rb +321 -321
  86. data/lib/cyberarm_engine/ui/style.rb +50 -50
  87. data/lib/cyberarm_engine/ui/theme.rb +225 -225
  88. data/lib/cyberarm_engine/vector.rb +312 -312
  89. data/lib/cyberarm_engine/version.rb +4 -4
  90. data/lib/cyberarm_engine/window.rb +195 -195
  91. data/lib/cyberarm_engine.rb +70 -78
  92. data/mrbgem.rake +29 -29
  93. metadata +8 -7
@@ -1,297 +1,297 @@
1
- module CyberarmEngine
2
- class Element
3
- class EditLine < Button
4
- class TextInput < Gosu::TextInput
5
- def filter=(filter)
6
- @filter = filter
7
- end
8
-
9
- def filter(text_in)
10
- if @filter
11
- @filter.call(text_in)
12
- else
13
- text_in
14
- end
15
- end
16
- end
17
-
18
- def initialize(text, options = {}, block = nil)
19
- @filter = options.delete(:filter)
20
- super(text, options, block)
21
-
22
- @type = default(:type)
23
-
24
- @caret_width = default(:caret_width)
25
- @caret_height = @text.textobject.height
26
- @caret_color = default(:caret_color)
27
- @caret_interval = default(:caret_interval)
28
- @caret_last_interval = Gosu.milliseconds
29
- @show_caret = true
30
-
31
- @text_input = TextInput.new
32
- @text_input.filter = @filter
33
- @text_input.text = text
34
- @last_text_value = text
35
- @last_caret_position = @text_input.caret_pos
36
-
37
- @offset_x = 0
38
- @offset_y = 0
39
-
40
- event(:begin_drag)
41
- event(:drag_update)
42
- event(:end_drag)
43
- end
44
-
45
- def render
46
- Gosu.clip_to(@text.x, @text.y, @width, @height) do
47
- Gosu.translate(-@offset_x, -@offset_y) do
48
- draw_selection
49
- draw_caret if @focus && @show_caret
50
- draw_text
51
- end
52
- end
53
- end
54
-
55
- def draw_text
56
- @text.draw(:draw_text)
57
- end
58
-
59
- def draw_caret
60
- Gosu.draw_rect(caret_position, @text.y, @caret_width, @caret_height, @caret_color, @z)
61
- end
62
-
63
- def draw_selection
64
- selection_width = caret_position - selection_start_position
65
-
66
- Gosu.draw_rect(selection_start_position, @text.y, selection_width, @text.height, default(:selection_color), @z)
67
- end
68
-
69
- def update
70
- @style_event = :active if @focus
71
-
72
- @text.text = if @type == :password
73
- default(:password_character) * @text_input.text.length
74
- else
75
- @text_input.text
76
- end
77
-
78
- if @last_text_value != value
79
- @last_text_value = value
80
- @show_caret = true
81
- @caret_last_interval = Gosu.milliseconds
82
-
83
- root.gui_state.request_repaint
84
-
85
- publish(:changed, value)
86
- end
87
-
88
- if @last_caret_position != @text_input.caret_pos
89
- @last_caret_position = @text_input.caret_pos
90
- root.gui_state.request_repaint
91
-
92
- @show_caret = true
93
- @caret_last_interval = Gosu.milliseconds
94
- end
95
-
96
- if Gosu.milliseconds >= @caret_last_interval + @caret_interval
97
- root.gui_state.request_repaint
98
-
99
- @caret_last_interval = Gosu.milliseconds
100
-
101
- @show_caret = !@show_caret
102
- end
103
-
104
- keep_caret_visible
105
- end
106
-
107
- def button_down(id)
108
- handle_keyboard_shortcuts(id)
109
- end
110
-
111
- def handle_keyboard_shortcuts(id)
112
- return unless @focus && @enabled
113
-
114
- if Gosu.button_down?(Gosu::KB_LEFT_CONTROL) || Gosu.button_down?(Gosu::KB_RIGHT_CONTROL)
115
- case id
116
- when Gosu::KB_A
117
- @text_input.selection_start = 0
118
- @text_input.caret_pos = @text_input.text.length
119
-
120
- when Gosu::KB_C
121
- Gosu.clipboard = if @text_input.selection_start < @text_input.caret_pos
122
- @text_input.text[@text_input.selection_start...@text_input.caret_pos]
123
- else
124
- @text_input.text[@text_input.caret_pos...@text_input.selection_start]
125
- end
126
-
127
- when Gosu::KB_X
128
- chars = @text_input.text.chars
129
-
130
- if @text_input.selection_start < @text_input.caret_pos
131
- Gosu.clipboard = @text_input.text[@text_input.selection_start...@text_input.caret_pos]
132
- chars.slice!(@text_input.selection_start, @text_input.caret_pos)
133
- else
134
- Gosu.clipboard = @text_input.text[@text_input.caret_pos...@text_input.selection_start]
135
- chars.slice!(@text_input.caret_pos, @text_input.selection_start)
136
- end
137
-
138
- @text_input.text = chars.join
139
-
140
- when Gosu::KB_V
141
- if instance_of?(EditLine) # EditLine assumes a single line of text
142
- @text_input.insert_text(Gosu.clipboard.gsub("\n", ""))
143
- else
144
- @text_input.insert_text(Gosu.clipboard)
145
- end
146
- end
147
- end
148
- end
149
-
150
- def caret_position_under_mouse(mouse_x)
151
- 1.upto(@text.text.length) do |i|
152
- return i - 1 if mouse_x < @text.x - @offset_x + @text.width(@text.text[0...i])
153
- end
154
-
155
- @text_input.text.length
156
- end
157
-
158
- def move_caret_to_mouse(mouse_x, _mouse_y)
159
- @text_input.caret_pos = @text_input.selection_start = caret_position_under_mouse(mouse_x)
160
- end
161
-
162
- def keep_caret_visible
163
- caret_pos = (caret_position - @text.x) + @caret_width
164
-
165
- @last_text ||= "/\\"
166
- @last_pos ||= -1
167
-
168
- @last_text = @text.text
169
- @last_pos = caret_pos
170
-
171
- if caret_pos.between?(@offset_x, @width + @offset_x)
172
- # Do nothing
173
-
174
- elsif caret_pos < @offset_x
175
- @offset_x = if caret_pos > @width
176
- caret_pos + @width
177
- else
178
- 0
179
- end
180
-
181
- elsif caret_pos > @width
182
- @offset_x = caret_pos - @width
183
-
184
- else
185
- # Reset to Zero
186
- @offset_x = 0
187
- end
188
- end
189
-
190
- def caret_position
191
- text_input_position_for(:caret_pos)
192
- end
193
-
194
- def selection_start_position
195
- text_input_position_for(:selection_start)
196
- end
197
-
198
- def text_input_position_for(method)
199
- if @type == :password
200
- @text.x + @text.width(default(:password_character) * @text_input.text[0...@text_input.send(method)].length)
201
- else
202
- @text.x + @text.width(@text_input.text[0...@text_input.send(method)]) - @style.border_thickness_left
203
- end
204
- end
205
-
206
- def left_mouse_button(sender, x, y)
207
- super
208
- window.text_input = @text_input
209
-
210
- @caret_last_interval = Gosu.milliseconds
211
- @show_caret = true
212
-
213
- move_caret_to_mouse(x, y)
214
-
215
- :handled
216
- end
217
-
218
- def focus(sender)
219
- @focus = true
220
- window.text_input = @text_input
221
- @text_input.caret_pos = @text_input.selection_start = @text_input.text.length
222
-
223
- update_styles(:active)
224
-
225
- :handled
226
- end
227
-
228
- def enter(sender)
229
- if @enabled && @focus
230
- update_styles(:active)
231
- elsif @enabled && !@focus
232
- update_styles(:hover)
233
- else
234
- update_styles(:disabled)
235
- end
236
-
237
- :handled
238
- end
239
-
240
- def leave(sender)
241
- if @enabled && @focus
242
- update_styles(:active)
243
- elsif @enabled && !@focus
244
- update_styles
245
- else
246
- update_styles(:disabled)
247
- end
248
-
249
- :handled
250
- end
251
-
252
- def blur(_sender)
253
- super
254
- window.text_input = nil
255
-
256
- :handled
257
- end
258
-
259
- def draggable?(button)
260
- button == :left
261
- end
262
-
263
- def begin_drag(_sender, x, _y, _button)
264
- @drag_start = x
265
- @offset_drag_start = @offset_x
266
- @drag_caret_position = @text_input.caret_pos
267
-
268
- :handled
269
- end
270
-
271
- def drag_update(_sender, x, _y, _button)
272
- @text_input.caret_pos = caret_position_under_mouse(x)
273
-
274
- :handled
275
- end
276
-
277
- def end_drag(_sender, _x, _y, _button)
278
- :handled
279
- end
280
-
281
- def layout
282
- super
283
-
284
- @width = dimensional_size(@style.width, :width) || default(:width)
285
- update_background
286
- end
287
-
288
- def value
289
- @text_input.text
290
- end
291
-
292
- def value=(string)
293
- @text_input.text = string
294
- end
295
- end
296
- end
297
- end
1
+ module CyberarmEngine
2
+ class Element
3
+ class EditLine < Button
4
+ class TextInput < Gosu::TextInput
5
+ def filter=(filter)
6
+ @filter = filter
7
+ end
8
+
9
+ def filter(text_in)
10
+ if @filter
11
+ @filter.call(text_in)
12
+ else
13
+ text_in
14
+ end
15
+ end
16
+ end
17
+
18
+ def initialize(text, options = {}, block = nil)
19
+ @filter = options.delete(:filter)
20
+ super(text, options, block)
21
+
22
+ @type = default(:type)
23
+
24
+ @caret_width = default(:caret_width)
25
+ @caret_height = @text.textobject.height
26
+ @caret_color = default(:caret_color)
27
+ @caret_interval = default(:caret_interval)
28
+ @caret_last_interval = Gosu.milliseconds
29
+ @show_caret = true
30
+
31
+ @text_input = TextInput.new
32
+ @text_input.filter = @filter
33
+ @text_input.text = text
34
+ @last_text_value = text
35
+ @last_caret_position = @text_input.caret_pos
36
+
37
+ @offset_x = 0
38
+ @offset_y = 0
39
+
40
+ event(:begin_drag)
41
+ event(:drag_update)
42
+ event(:end_drag)
43
+ end
44
+
45
+ def render
46
+ Gosu.clip_to(@text.x, @text.y, @width, @height) do
47
+ Gosu.translate(-@offset_x, -@offset_y) do
48
+ draw_selection
49
+ draw_caret if @focus && @show_caret
50
+ draw_text
51
+ end
52
+ end
53
+ end
54
+
55
+ def draw_text
56
+ @text.draw(:draw_text)
57
+ end
58
+
59
+ def draw_caret
60
+ Gosu.draw_rect(caret_position, @text.y, @caret_width, @caret_height, @caret_color, @z)
61
+ end
62
+
63
+ def draw_selection
64
+ selection_width = caret_position - selection_start_position
65
+
66
+ Gosu.draw_rect(selection_start_position, @text.y, selection_width, @text.height, default(:selection_color), @z)
67
+ end
68
+
69
+ def update
70
+ @style_event = :active if @focus
71
+
72
+ @text.text = if @type == :password
73
+ default(:password_character) * @text_input.text.length
74
+ else
75
+ @text_input.text
76
+ end
77
+
78
+ if @last_text_value != value
79
+ @last_text_value = value
80
+ @show_caret = true
81
+ @caret_last_interval = Gosu.milliseconds
82
+
83
+ root.gui_state.request_repaint
84
+
85
+ publish(:changed, value)
86
+ end
87
+
88
+ if @last_caret_position != @text_input.caret_pos
89
+ @last_caret_position = @text_input.caret_pos
90
+ root.gui_state.request_repaint
91
+
92
+ @show_caret = true
93
+ @caret_last_interval = Gosu.milliseconds
94
+ end
95
+
96
+ if Gosu.milliseconds >= @caret_last_interval + @caret_interval
97
+ root.gui_state.request_repaint
98
+
99
+ @caret_last_interval = Gosu.milliseconds
100
+
101
+ @show_caret = !@show_caret
102
+ end
103
+
104
+ keep_caret_visible
105
+ end
106
+
107
+ def button_down(id)
108
+ handle_keyboard_shortcuts(id)
109
+ end
110
+
111
+ def handle_keyboard_shortcuts(id)
112
+ return unless @focus && @enabled
113
+
114
+ if Gosu.button_down?(Gosu::KB_LEFT_CONTROL) || Gosu.button_down?(Gosu::KB_RIGHT_CONTROL)
115
+ case id
116
+ when Gosu::KB_A
117
+ @text_input.selection_start = 0
118
+ @text_input.caret_pos = @text_input.text.length
119
+
120
+ when Gosu::KB_C
121
+ Gosu.clipboard = if @text_input.selection_start < @text_input.caret_pos
122
+ @text_input.text[@text_input.selection_start...@text_input.caret_pos]
123
+ else
124
+ @text_input.text[@text_input.caret_pos...@text_input.selection_start]
125
+ end
126
+
127
+ when Gosu::KB_X
128
+ chars = @text_input.text.chars
129
+
130
+ if @text_input.selection_start < @text_input.caret_pos
131
+ Gosu.clipboard = @text_input.text[@text_input.selection_start...@text_input.caret_pos]
132
+ chars.slice!(@text_input.selection_start, @text_input.caret_pos)
133
+ else
134
+ Gosu.clipboard = @text_input.text[@text_input.caret_pos...@text_input.selection_start]
135
+ chars.slice!(@text_input.caret_pos, @text_input.selection_start)
136
+ end
137
+
138
+ @text_input.text = chars.join
139
+
140
+ when Gosu::KB_V
141
+ if instance_of?(EditLine) # EditLine assumes a single line of text
142
+ @text_input.insert_text(Gosu.clipboard.gsub("\n", ""))
143
+ else
144
+ @text_input.insert_text(Gosu.clipboard)
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ def caret_position_under_mouse(mouse_x)
151
+ 1.upto(@text.text.length) do |i|
152
+ return i - 1 if mouse_x < @text.x - @offset_x + @text.width(@text.text[0...i])
153
+ end
154
+
155
+ @text_input.text.length
156
+ end
157
+
158
+ def move_caret_to_mouse(mouse_x, _mouse_y)
159
+ @text_input.caret_pos = @text_input.selection_start = caret_position_under_mouse(mouse_x)
160
+ end
161
+
162
+ def keep_caret_visible
163
+ caret_pos = (caret_position - @text.x) + @caret_width
164
+
165
+ @last_text ||= "/\\"
166
+ @last_pos ||= -1
167
+
168
+ @last_text = @text.text
169
+ @last_pos = caret_pos
170
+
171
+ if caret_pos.between?(@offset_x + 1, @width + @offset_x)
172
+ # Do nothing
173
+
174
+ elsif caret_pos < @offset_x
175
+ @offset_x = if caret_pos > @width
176
+ caret_pos + @width
177
+ else
178
+ 0
179
+ end
180
+
181
+ elsif caret_pos > @width
182
+ @offset_x = caret_pos - @width
183
+
184
+ else
185
+ # Reset to Zero
186
+ @offset_x = 0
187
+ end
188
+ end
189
+
190
+ def caret_position
191
+ text_input_position_for(:caret_pos)
192
+ end
193
+
194
+ def selection_start_position
195
+ text_input_position_for(:selection_start)
196
+ end
197
+
198
+ def text_input_position_for(method)
199
+ if @type == :password
200
+ @text.x + @text.width(default(:password_character) * @text_input.text[0...@text_input.send(method)].length) - @style.border_thickness_left
201
+ else
202
+ @text.x + @text.width(@text_input.text[0...@text_input.send(method)]) - @style.border_thickness_left
203
+ end
204
+ end
205
+
206
+ def left_mouse_button(sender, x, y)
207
+ super
208
+ window.text_input = @text_input
209
+
210
+ @caret_last_interval = Gosu.milliseconds
211
+ @show_caret = true
212
+
213
+ move_caret_to_mouse(x, y)
214
+
215
+ :handled
216
+ end
217
+
218
+ def focus(sender)
219
+ @focus = true
220
+ window.text_input = @text_input
221
+ @text_input.caret_pos = @text_input.selection_start = @text_input.text.length
222
+
223
+ update_styles(:active)
224
+
225
+ :handled
226
+ end
227
+
228
+ def enter(sender)
229
+ if @enabled && @focus
230
+ update_styles(:active)
231
+ elsif @enabled && !@focus
232
+ update_styles(:hover)
233
+ else
234
+ update_styles(:disabled)
235
+ end
236
+
237
+ :handled
238
+ end
239
+
240
+ def leave(sender)
241
+ if @enabled && @focus
242
+ update_styles(:active)
243
+ elsif @enabled && !@focus
244
+ update_styles
245
+ else
246
+ update_styles(:disabled)
247
+ end
248
+
249
+ :handled
250
+ end
251
+
252
+ def blur(_sender)
253
+ super
254
+ window.text_input = nil
255
+
256
+ :handled
257
+ end
258
+
259
+ def draggable?(button)
260
+ button == :left
261
+ end
262
+
263
+ def begin_drag(_sender, x, _y, _button)
264
+ @drag_start = x
265
+ @offset_drag_start = @offset_x
266
+ @drag_caret_position = @text_input.caret_pos
267
+
268
+ :handled
269
+ end
270
+
271
+ def drag_update(_sender, x, _y, _button)
272
+ @text_input.caret_pos = caret_position_under_mouse(x)
273
+
274
+ :handled
275
+ end
276
+
277
+ def end_drag(_sender, _x, _y, _button)
278
+ :handled
279
+ end
280
+
281
+ def layout
282
+ super
283
+
284
+ @width = dimensional_size(@style.width, :width) || default(:width)
285
+ update_background
286
+ end
287
+
288
+ def value
289
+ @text_input.text
290
+ end
291
+
292
+ def value=(string)
293
+ @text_input.text = string
294
+ end
295
+ end
296
+ end
297
+ end
@@ -1,15 +1,15 @@
1
- module CyberarmEngine
2
- class Element
3
- class Flow < Container
4
- def layout
5
- @children.each do |child|
6
- if fits_on_line?(child)
7
- position_on_current_line(child)
8
- else
9
- position_on_next_line(child)
10
- end
11
- end
12
- end
13
- end
14
- end
15
- end
1
+ module CyberarmEngine
2
+ class Element
3
+ class Flow < Container
4
+ def layout
5
+ @children.each do |child|
6
+ if fits_on_line?(child)
7
+ position_on_current_line(child)
8
+ else
9
+ position_on_next_line(child)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end