cyberarm_engine 0.24.4 → 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 -404
  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,404 +1,404 @@
1
- module CyberarmEngine
2
- class Element
3
- class Container < Element
4
- include Common
5
-
6
- attr_accessor :stroke_color, :fill_color
7
- attr_reader :children, :gui_state, :scroll_position, :scroll_target_position
8
-
9
- def self.current_container
10
- @@current_container
11
- end
12
-
13
- def self.current_container=(container)
14
- raise ArgumentError, "Expected container to an an instance of CyberarmEngine::Element::Container, got #{container.class}" unless container.is_a?(CyberarmEngine::Element::Container)
15
-
16
- @@current_container = container
17
- end
18
-
19
- def initialize(options = {}, block = nil)
20
- @gui_state = options.delete(:gui_state)
21
- super
22
-
23
- @last_scroll_position = Vector.new(0, 0)
24
- @scroll_position = Vector.new(0, 0)
25
- @scroll_target_position = Vector.new(0, 0)
26
- @scroll_chunk = 120
27
- @scroll_speed = 40
28
-
29
- @text_color = options[:color]
30
-
31
- @children = []
32
-
33
- event(:window_size_changed)
34
- end
35
-
36
- def build
37
- @block.call(self) if @block
38
-
39
- root.gui_state.request_recalculate_for(self)
40
- end
41
-
42
- def add(element)
43
- @children << element
44
-
45
- root.gui_state.request_recalculate_for(self)
46
- end
47
-
48
- def remove(element)
49
- root.gui_state.request_recalculate_for(self) if @children.delete(element)
50
- end
51
-
52
- def append(&block)
53
- old_container = CyberarmEngine::Element::Container.current_container
54
-
55
- CyberarmEngine::Element::Container.current_container = self
56
- block.call(self) if block
57
-
58
- CyberarmEngine::Element::Container.current_container = old_container
59
-
60
- root.gui_state.request_recalculate_for(self)
61
- end
62
-
63
- def clear(&block)
64
- @children.clear
65
-
66
- old_container = CyberarmEngine::Element::Container.current_container
67
-
68
- CyberarmEngine::Element::Container.current_container = self
69
- block.call(self) if block
70
-
71
- CyberarmEngine::Element::Container.current_container = old_container
72
-
73
- root.gui_state.request_recalculate_for(self)
74
- end
75
-
76
- def render
77
- Gosu.clip_to(
78
- @x + @style.border_thickness_left + @style.padding_left,
79
- @y + @style.border_thickness_top + @style.padding_top,
80
- content_width + 1,
81
- content_height + 1
82
- ) do
83
- Gosu.translate(@scroll_position.x, @scroll_position.y) do
84
- @children.each(&:draw)
85
- end
86
- end
87
- end
88
-
89
- def debug_draw
90
- super
91
-
92
- @children.each do |child|
93
- child.debug_draw
94
- end
95
- end
96
-
97
- def update
98
- update_scroll if @style.scroll
99
- @children.each(&:update)
100
- end
101
-
102
- def hit_element?(x, y)
103
- return unless hit?(x, y)
104
-
105
- # Offset child hit point by scroll position/offset
106
- child_x = x - @scroll_position.x
107
- child_y = y - @scroll_position.y
108
-
109
- @children.reverse_each do |child|
110
- next unless child.visible?
111
-
112
- case child
113
- when Container
114
- if element = child.hit_element?(child_x, child_y)
115
- return element
116
- end
117
- else
118
- return child if child.hit?(child_x, child_y)
119
- end
120
- end
121
-
122
- self if hit?(x, y)
123
- end
124
-
125
- def update_child_element_visibity(child)
126
- child.element_visible = child.x >= (@x - @scroll_position.x) - child.width && child.x <= (@x - @scroll_position.x) + width &&
127
- child.y >= (@y - @scroll_position.y) - child.height && child.y <= (@y - @scroll_position.y) + height
128
- end
129
-
130
- def update_scroll
131
- dt = window.dt.clamp(0.000001, 0.025)
132
- @scroll_position.x += (((@scroll_target_position.x - @scroll_position.x) * (@scroll_speed / 4.0) * 0.98) * dt).round
133
- @scroll_position.y += (((@scroll_target_position.y - @scroll_position.y) * (@scroll_speed / 4.0) * 0.98) * dt).round
134
-
135
- # Scrolled PAST top
136
- if @scroll_position.y > 0
137
- @scroll_target_position.y = 0
138
-
139
- # Scrolled PAST bottom
140
- elsif @scroll_position.y < -max_scroll_height
141
- @scroll_target_position.y = -max_scroll_height
142
- end
143
-
144
- if @last_scroll_position != @scroll_position
145
- @children.each { |child| update_child_element_visibity(child) }
146
- root.gui_state.request_repaint
147
- end
148
-
149
- @last_scroll_position.x = @scroll_position.x
150
- @last_scroll_position.y = @scroll_position.y
151
- end
152
-
153
- def recalculate
154
- @current_position = Vector.new(@style.margin_left + @style.padding_left, @style.margin_top + @style.padding_top)
155
-
156
- return unless visible?
157
-
158
- Stats.frame&.increment(:gui_recalculations)
159
-
160
- # s = Gosu.milliseconds
161
-
162
- stylize
163
- layout
164
-
165
- # Old sizes MUST be determined AFTER call to layout
166
- old_width = width
167
- old_height = height
168
-
169
- @cached_scroll_width = nil
170
- @cached_scroll_height = nil
171
-
172
- if is_root?
173
- @width = @style.width = window.width
174
- @height = @style.height = window.height
175
- else
176
- @width = 0
177
- @height = 0
178
-
179
- _width = dimensional_size(@style.width, :width)
180
- _height = dimensional_size(@style.height, :height)
181
-
182
- @width = _width || (@children.map { |c| c.x + c.outer_width }.max || 0).floor
183
- @height = _height || (@children.map { |c| c.y + c.outer_height }.max || 0).floor
184
- end
185
-
186
- # FIXME: Correctly handle alignment when element has siblings
187
- # FIXME: Enable alignment for any element, not just containers
188
- if @style.v_align
189
- space = space_available_height
190
-
191
- case @style.v_align
192
- when :center
193
- @y = parent.height / 2 - height / 2
194
- when :bottom
195
- @y = parent.height - height
196
- end
197
- end
198
-
199
- if @style.h_align
200
- space = space_available_width
201
-
202
- case @style.h_align
203
- when :center
204
- @x = parent.width / 2 - width / 2
205
- when :right
206
- @x = parent.width - width
207
- end
208
- end
209
-
210
- # t = Gosu.milliseconds
211
- # Move children to parent after positioning
212
- @children.each do |child|
213
- child.x += (@x + @style.border_thickness_left) - style.margin_left
214
- child.y += (@y + @style.border_thickness_top) - style.margin_top
215
-
216
- child.stylize
217
- child.recalculate
218
- child.reposition # TODO: Implement top,bottom,left,center, and right positioning
219
-
220
- Stats.frame&.increment(:gui_recalculations)
221
-
222
- update_child_element_visibity(child)
223
- end
224
- # puts "TOOK: #{Gosu.milliseconds - t}ms to recalculate #{self.class}:0x#{self.object_id.to_s(16)}'s #{@children.count} children"
225
-
226
- update_background
227
-
228
- # Fixes resized container scrolled past bottom
229
- if old_height != @height
230
- self.scroll_top = -@scroll_position.y
231
- @scroll_target_position.y = @scroll_position.y
232
- end
233
-
234
- # Fixes resized container that is scrolled down from being stuck overscrolled when resized
235
- if scroll_height < height
236
- @scroll_target_position.y = 0
237
- end
238
-
239
- recalculate_if_size_changed
240
-
241
- # puts "TOOK: #{Gosu.milliseconds - s}ms to recalculate #{self.class}:0x#{self.object_id.to_s(16)}"
242
- end
243
-
244
- def layout
245
- raise "Not overridden"
246
- end
247
-
248
- def max_width
249
- # _width = dimensional_size(@style.width, :width)
250
- # if _width
251
- # outer_width
252
- # else
253
- # window.width - (@parent ? @parent.style.margin_right + @style.margin_right : @style.margin_right)
254
- # end
255
-
256
- outer_width
257
- end
258
-
259
- def fits_on_line?(element) # Flow
260
- @current_position.x + element.outer_width <= max_width &&
261
- @current_position.x + element.outer_width <= window.width
262
- end
263
-
264
- def position_on_current_line(element) # Flow
265
- element.x = element.style.margin_left + @current_position.x
266
- element.y = element.style.margin_top + @current_position.y
267
-
268
- @current_position.x += element.outer_width
269
- end
270
-
271
- def tallest_neighbor(querier, _y_position) # Flow
272
- response = querier
273
- @children.each do |child|
274
- response = child if child.outer_height > response.outer_height
275
- break if child == querier
276
- end
277
-
278
- response
279
- end
280
-
281
- def position_on_next_line(element) # Flow
282
- @current_position.x = @style.margin_left + @style.padding_left
283
- @current_position.y += tallest_neighbor(element, @current_position.y).outer_height
284
-
285
- element.x = element.style.margin_left + @current_position.x
286
- element.y = element.style.margin_top + @current_position.y
287
-
288
- @current_position.x += element.outer_width
289
- end
290
-
291
- def move_to_next_line(element) # Stack
292
- element.x = element.style.margin_left + @current_position.x
293
- element.y = element.style.margin_top + @current_position.y
294
-
295
- @current_position.y += element.outer_height
296
- end
297
-
298
- def mouse_wheel_up(sender, x, y)
299
- return unless @style.scroll
300
-
301
- # Allow overscrolling UP, only if one can scroll DOWN
302
- if height < scroll_height
303
- if @scroll_target_position.y > 0
304
- @scroll_target_position.y = @scroll_chunk
305
- else
306
- @scroll_target_position.y += @scroll_chunk
307
- end
308
-
309
- return :handled
310
- end
311
- end
312
-
313
- def mouse_wheel_down(sender, x, y)
314
- return unless @style.scroll
315
-
316
- return unless height < scroll_height
317
-
318
- if @scroll_target_position.y > 0
319
- @scroll_target_position.y = -@scroll_chunk
320
- else
321
- @scroll_target_position.y -= @scroll_chunk
322
- end
323
-
324
- return :handled
325
- end
326
-
327
- def scroll_jump_to_top(sender, x, y)
328
- return unless @style.scroll
329
-
330
- @scroll_position.y = 0
331
- @scroll_target_position.y = 0
332
-
333
- return :handled
334
- end
335
-
336
- def scroll_jump_to_end(sender, x, y)
337
- return unless @style.scroll
338
-
339
- @scroll_position.y = -max_scroll_height
340
- @scroll_target_position.y = -max_scroll_height
341
-
342
- return :handled
343
- end
344
-
345
- def scroll_page_up(sender, x, y)
346
- return unless @style.scroll
347
-
348
- @scroll_position.y += height
349
- @scroll_position.y = 0 if @scroll_position.y > 0
350
- @scroll_target_position.y = @scroll_position.y
351
-
352
- return :handled
353
- end
354
-
355
- def scroll_page_down(sender, x, y)
356
- return unless @style.scroll
357
-
358
- @scroll_position.y -= height
359
- @scroll_position.y = -max_scroll_height if @scroll_position.y < -max_scroll_height
360
- @scroll_target_position.y = @scroll_position.y
361
-
362
- return :handled
363
- end
364
-
365
- def scroll_top
366
- @scroll_position.y
367
- end
368
-
369
- def scroll_top=(n)
370
- n = 0 if n <= 0
371
- @scroll_position.y = -n
372
-
373
- if max_scroll_height.positive?
374
- @scroll_position.y = -max_scroll_height if @scroll_position.y.abs > max_scroll_height
375
- else
376
- @scroll_position.y = 0
377
- end
378
- end
379
-
380
- def value
381
- @children.map(&:class).join(", ")
382
- end
383
-
384
- def to_s
385
- "#{self.class} x=#{x} y=#{y} width=#{width} height=#{height} children=#{@children.size}"
386
- end
387
-
388
- def write_tree(indent = "", _index = 0)
389
- puts self
390
-
391
- indent += " "
392
- @children.each_with_index do |child, i|
393
- print "#{indent}#{i}: "
394
-
395
- if child.is_a?(Container)
396
- child.write_tree(indent)
397
- else
398
- puts child
399
- end
400
- end
401
- end
402
- end
403
- end
404
- end
1
+ module CyberarmEngine
2
+ class Element
3
+ class Container < Element
4
+ include Common
5
+
6
+ attr_accessor :stroke_color, :fill_color
7
+ attr_reader :children, :gui_state, :scroll_position, :scroll_target_position
8
+
9
+ def self.current_container
10
+ @@current_container
11
+ end
12
+
13
+ def self.current_container=(container)
14
+ raise ArgumentError, "Expected container to an an instance of CyberarmEngine::Element::Container, got #{container.class}" unless container.is_a?(CyberarmEngine::Element::Container)
15
+
16
+ @@current_container = container
17
+ end
18
+
19
+ def initialize(options = {}, block = nil)
20
+ @gui_state = options.delete(:gui_state)
21
+ super
22
+
23
+ @last_scroll_position = Vector.new(0, 0)
24
+ @scroll_position = Vector.new(0, 0)
25
+ @scroll_target_position = Vector.new(0, 0)
26
+ @scroll_chunk = 120
27
+ @scroll_speed = 40
28
+
29
+ @text_color = options[:color]
30
+
31
+ @children = []
32
+
33
+ event(:window_size_changed)
34
+ end
35
+
36
+ def build
37
+ @block.call(self) if @block
38
+
39
+ root.gui_state.request_recalculate_for(self)
40
+ end
41
+
42
+ def add(element)
43
+ @children << element
44
+
45
+ root.gui_state.request_recalculate_for(self)
46
+ end
47
+
48
+ def remove(element)
49
+ root.gui_state.request_recalculate_for(self) if @children.delete(element)
50
+ end
51
+
52
+ def append(&block)
53
+ old_container = CyberarmEngine::Element::Container.current_container
54
+
55
+ CyberarmEngine::Element::Container.current_container = self
56
+ block.call(self) if block
57
+
58
+ CyberarmEngine::Element::Container.current_container = old_container
59
+
60
+ root.gui_state.request_recalculate_for(self)
61
+ end
62
+
63
+ def clear(&block)
64
+ @children.clear
65
+
66
+ old_container = CyberarmEngine::Element::Container.current_container
67
+
68
+ CyberarmEngine::Element::Container.current_container = self
69
+ block.call(self) if block
70
+
71
+ CyberarmEngine::Element::Container.current_container = old_container
72
+
73
+ root.gui_state.request_recalculate_for(self)
74
+ end
75
+
76
+ def render
77
+ Gosu.clip_to(
78
+ @x + @style.border_thickness_left + @style.padding_left,
79
+ @y + @style.border_thickness_top + @style.padding_top,
80
+ content_width + 1,
81
+ content_height + 1
82
+ ) do
83
+ Gosu.translate(@scroll_position.x, @scroll_position.y) do
84
+ @children.each(&:draw)
85
+ end
86
+ end
87
+ end
88
+
89
+ def debug_draw
90
+ super
91
+
92
+ @children.each do |child|
93
+ child.debug_draw
94
+ end
95
+ end
96
+
97
+ def update
98
+ update_scroll if @style.scroll
99
+ @children.each(&:update)
100
+ end
101
+
102
+ def hit_element?(x, y)
103
+ return unless hit?(x, y)
104
+
105
+ # Offset child hit point by scroll position/offset
106
+ child_x = x - @scroll_position.x
107
+ child_y = y - @scroll_position.y
108
+
109
+ @children.reverse_each do |child|
110
+ next unless child.visible?
111
+
112
+ case child
113
+ when Container
114
+ if element = child.hit_element?(child_x, child_y)
115
+ return element
116
+ end
117
+ else
118
+ return child if child.hit?(child_x, child_y)
119
+ end
120
+ end
121
+
122
+ self if hit?(x, y)
123
+ end
124
+
125
+ def update_child_element_visibity(child)
126
+ child.element_visible = child.x >= (@x - @scroll_position.x) - child.width && child.x <= (@x - @scroll_position.x) + width &&
127
+ child.y >= (@y - @scroll_position.y) - child.height && child.y <= (@y - @scroll_position.y) + height
128
+ end
129
+
130
+ def update_scroll
131
+ dt = window.dt.clamp(0.000001, 0.025)
132
+ @scroll_position.x += (((@scroll_target_position.x - @scroll_position.x) * (@scroll_speed / 4.0) * 0.98) * dt).round
133
+ @scroll_position.y += (((@scroll_target_position.y - @scroll_position.y) * (@scroll_speed / 4.0) * 0.98) * dt).round
134
+
135
+ # Scrolled PAST top
136
+ if @scroll_position.y > 0
137
+ @scroll_target_position.y = 0
138
+
139
+ # Scrolled PAST bottom
140
+ elsif @scroll_position.y < -max_scroll_height
141
+ @scroll_target_position.y = -max_scroll_height
142
+ end
143
+
144
+ if @last_scroll_position != @scroll_position
145
+ @children.each { |child| update_child_element_visibity(child) }
146
+ root.gui_state.request_repaint
147
+ end
148
+
149
+ @last_scroll_position.x = @scroll_position.x
150
+ @last_scroll_position.y = @scroll_position.y
151
+ end
152
+
153
+ def recalculate
154
+ @current_position = Vector.new(@style.margin_left + @style.padding_left, @style.margin_top + @style.padding_top)
155
+
156
+ return unless visible?
157
+
158
+ Stats.frame&.increment(:gui_recalculations)
159
+
160
+ # s = Gosu.milliseconds
161
+
162
+ stylize
163
+ layout
164
+
165
+ # Old sizes MUST be determined AFTER call to layout
166
+ old_width = width
167
+ old_height = height
168
+
169
+ @cached_scroll_width = nil
170
+ @cached_scroll_height = nil
171
+
172
+ if is_root?
173
+ @width = @style.width = window.width
174
+ @height = @style.height = window.height
175
+ else
176
+ @width = 0
177
+ @height = 0
178
+
179
+ _width = dimensional_size(@style.width, :width)
180
+ _height = dimensional_size(@style.height, :height)
181
+
182
+ @width = _width || (@children.map { |c| c.x + c.outer_width }.max || 0).floor
183
+ @height = _height || (@children.map { |c| c.y + c.outer_height }.max || 0).floor
184
+ end
185
+
186
+ # FIXME: Correctly handle alignment when element has siblings
187
+ # FIXME: Enable alignment for any element, not just containers
188
+ if @style.v_align
189
+ space = space_available_height
190
+
191
+ case @style.v_align
192
+ when :center
193
+ @y = parent.height / 2 - height / 2
194
+ when :bottom
195
+ @y = parent.height - height
196
+ end
197
+ end
198
+
199
+ if @style.h_align
200
+ space = space_available_width
201
+
202
+ case @style.h_align
203
+ when :center
204
+ @x = parent.width / 2 - width / 2
205
+ when :right
206
+ @x = parent.width - width
207
+ end
208
+ end
209
+
210
+ # t = Gosu.milliseconds
211
+ # Move children to parent after positioning
212
+ @children.each do |child|
213
+ child.x += (@x + @style.border_thickness_left) - style.margin_left
214
+ child.y += (@y + @style.border_thickness_top) - style.margin_top
215
+
216
+ child.stylize
217
+ child.recalculate
218
+ child.reposition # TODO: Implement top,bottom,left,center, and right positioning
219
+
220
+ Stats.frame&.increment(:gui_recalculations)
221
+
222
+ update_child_element_visibity(child)
223
+ end
224
+ # puts "TOOK: #{Gosu.milliseconds - t}ms to recalculate #{self.class}:0x#{self.object_id.to_s(16)}'s #{@children.count} children"
225
+
226
+ update_background
227
+
228
+ # Fixes resized container scrolled past bottom
229
+ if old_height != @height
230
+ self.scroll_top = -@scroll_position.y
231
+ @scroll_target_position.y = @scroll_position.y
232
+ end
233
+
234
+ # Fixes resized container that is scrolled down from being stuck overscrolled when resized
235
+ if scroll_height < height
236
+ @scroll_target_position.y = 0
237
+ end
238
+
239
+ recalculate_if_size_changed
240
+
241
+ # puts "TOOK: #{Gosu.milliseconds - s}ms to recalculate #{self.class}:0x#{self.object_id.to_s(16)}"
242
+ end
243
+
244
+ def layout
245
+ raise "Not overridden"
246
+ end
247
+
248
+ def max_width
249
+ # _width = dimensional_size(@style.width, :width)
250
+ # if _width
251
+ # outer_width
252
+ # else
253
+ # window.width - (@parent ? @parent.style.margin_right + @style.margin_right : @style.margin_right)
254
+ # end
255
+
256
+ outer_width
257
+ end
258
+
259
+ def fits_on_line?(element) # Flow
260
+ @current_position.x + element.outer_width <= max_width &&
261
+ @current_position.x + element.outer_width <= window.width
262
+ end
263
+
264
+ def position_on_current_line(element) # Flow
265
+ element.x = element.style.margin_left + @current_position.x
266
+ element.y = element.style.margin_top + @current_position.y
267
+
268
+ @current_position.x += element.outer_width
269
+ end
270
+
271
+ def tallest_neighbor(querier, _y_position) # Flow
272
+ response = querier
273
+ @children.each do |child|
274
+ response = child if child.outer_height > response.outer_height
275
+ break if child == querier
276
+ end
277
+
278
+ response
279
+ end
280
+
281
+ def position_on_next_line(element) # Flow
282
+ @current_position.x = @style.margin_left + @style.padding_left
283
+ @current_position.y += tallest_neighbor(element, @current_position.y).outer_height
284
+
285
+ element.x = element.style.margin_left + @current_position.x
286
+ element.y = element.style.margin_top + @current_position.y
287
+
288
+ @current_position.x += element.outer_width
289
+ end
290
+
291
+ def move_to_next_line(element) # Stack
292
+ element.x = element.style.margin_left + @current_position.x
293
+ element.y = element.style.margin_top + @current_position.y
294
+
295
+ @current_position.y += element.outer_height
296
+ end
297
+
298
+ def mouse_wheel_up(sender, x, y)
299
+ return unless @style.scroll
300
+
301
+ # Allow overscrolling UP, only if one can scroll DOWN
302
+ if height < scroll_height
303
+ if @scroll_target_position.y > 0
304
+ @scroll_target_position.y = @scroll_chunk
305
+ else
306
+ @scroll_target_position.y += @scroll_chunk
307
+ end
308
+
309
+ return :handled
310
+ end
311
+ end
312
+
313
+ def mouse_wheel_down(sender, x, y)
314
+ return unless @style.scroll
315
+
316
+ return unless height < scroll_height
317
+
318
+ if @scroll_target_position.y > 0
319
+ @scroll_target_position.y = -@scroll_chunk
320
+ else
321
+ @scroll_target_position.y -= @scroll_chunk
322
+ end
323
+
324
+ return :handled
325
+ end
326
+
327
+ def scroll_jump_to_top(sender, x, y)
328
+ return unless @style.scroll
329
+
330
+ @scroll_position.y = 0
331
+ @scroll_target_position.y = 0
332
+
333
+ return :handled
334
+ end
335
+
336
+ def scroll_jump_to_end(sender, x, y)
337
+ return unless @style.scroll
338
+
339
+ @scroll_position.y = -max_scroll_height
340
+ @scroll_target_position.y = -max_scroll_height
341
+
342
+ return :handled
343
+ end
344
+
345
+ def scroll_page_up(sender, x, y)
346
+ return unless @style.scroll
347
+
348
+ @scroll_position.y += height
349
+ @scroll_position.y = 0 if @scroll_position.y > 0
350
+ @scroll_target_position.y = @scroll_position.y
351
+
352
+ return :handled
353
+ end
354
+
355
+ def scroll_page_down(sender, x, y)
356
+ return unless @style.scroll
357
+
358
+ @scroll_position.y -= height
359
+ @scroll_position.y = -max_scroll_height if @scroll_position.y < -max_scroll_height
360
+ @scroll_target_position.y = @scroll_position.y
361
+
362
+ return :handled
363
+ end
364
+
365
+ def scroll_top
366
+ @scroll_position.y
367
+ end
368
+
369
+ def scroll_top=(n)
370
+ n = 0 if n <= 0
371
+ @scroll_position.y = -n
372
+
373
+ if max_scroll_height.positive?
374
+ @scroll_position.y = -max_scroll_height if @scroll_position.y.abs > max_scroll_height
375
+ else
376
+ @scroll_position.y = 0
377
+ end
378
+ end
379
+
380
+ def value
381
+ @children.map(&:class).join(", ")
382
+ end
383
+
384
+ def to_s
385
+ "#{self.class} x=#{x} y=#{y} width=#{width} height=#{height} children=#{@children.size}"
386
+ end
387
+
388
+ def write_tree(indent = "", _index = 0)
389
+ puts self
390
+
391
+ indent += " "
392
+ @children.each_with_index do |child, i|
393
+ print "#{indent}#{i}: "
394
+
395
+ if child.is_a?(Container)
396
+ child.write_tree(indent)
397
+ else
398
+ puts child
399
+ end
400
+ end
401
+ end
402
+ end
403
+ end
404
+ end