cyberarm_engine 0.13.0 → 0.17.0

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -8
  3. data/.rubocop.yml +8 -0
  4. data/.travis.yml +5 -5
  5. data/Gemfile +6 -6
  6. data/LICENSE.txt +21 -21
  7. data/README.md +73 -43
  8. data/Rakefile +10 -10
  9. data/assets/textures/default.png +0 -0
  10. data/bin/console +14 -14
  11. data/bin/setup +8 -8
  12. data/cyberarm_engine.gemspec +39 -36
  13. data/lib/cyberarm_engine.rb +64 -47
  14. data/lib/cyberarm_engine/animator.rb +56 -54
  15. data/lib/cyberarm_engine/background.rb +179 -175
  16. data/lib/cyberarm_engine/background_nine_slice.rb +125 -0
  17. data/lib/cyberarm_engine/bounding_box.rb +150 -150
  18. data/lib/cyberarm_engine/cache.rb +4 -0
  19. data/lib/cyberarm_engine/cache/download_manager.rb +121 -0
  20. data/lib/cyberarm_engine/common.rb +96 -96
  21. data/lib/cyberarm_engine/config_file.rb +46 -0
  22. data/lib/cyberarm_engine/game_object.rb +248 -257
  23. data/lib/cyberarm_engine/game_state.rb +92 -89
  24. data/lib/cyberarm_engine/model.rb +207 -0
  25. data/lib/cyberarm_engine/model/material.rb +21 -0
  26. data/lib/cyberarm_engine/model/model_object.rb +131 -0
  27. data/lib/cyberarm_engine/model/parser.rb +74 -0
  28. data/lib/cyberarm_engine/model/parsers/collada_parser.rb +138 -0
  29. data/lib/cyberarm_engine/model/parsers/wavefront_parser.rb +154 -0
  30. data/lib/cyberarm_engine/model_cache.rb +31 -0
  31. data/lib/cyberarm_engine/opengl.rb +28 -0
  32. data/lib/cyberarm_engine/opengl/light.rb +50 -0
  33. data/lib/cyberarm_engine/opengl/orthographic_camera.rb +46 -0
  34. data/lib/cyberarm_engine/opengl/perspective_camera.rb +38 -0
  35. data/lib/cyberarm_engine/opengl/renderer/bounding_box_renderer.rb +249 -0
  36. data/lib/cyberarm_engine/opengl/renderer/g_buffer.rb +164 -0
  37. data/lib/cyberarm_engine/opengl/renderer/opengl_renderer.rb +289 -0
  38. data/lib/cyberarm_engine/opengl/renderer/renderer.rb +22 -0
  39. data/lib/cyberarm_engine/opengl/shader.rb +406 -0
  40. data/lib/cyberarm_engine/opengl/texture.rb +69 -0
  41. data/lib/cyberarm_engine/ray.rb +56 -56
  42. data/lib/cyberarm_engine/stats.rb +21 -0
  43. data/lib/cyberarm_engine/text.rb +160 -146
  44. data/lib/cyberarm_engine/timer.rb +23 -23
  45. data/lib/cyberarm_engine/transform.rb +296 -273
  46. data/lib/cyberarm_engine/ui/border_canvas.rb +102 -101
  47. data/lib/cyberarm_engine/ui/dsl.rb +138 -99
  48. data/lib/cyberarm_engine/ui/element.rb +315 -276
  49. data/lib/cyberarm_engine/ui/elements/button.rb +160 -67
  50. data/lib/cyberarm_engine/ui/elements/check_box.rb +51 -59
  51. data/lib/cyberarm_engine/ui/elements/container.rb +256 -176
  52. data/lib/cyberarm_engine/ui/elements/edit_box.rb +179 -0
  53. data/lib/cyberarm_engine/ui/elements/edit_line.rb +262 -172
  54. data/lib/cyberarm_engine/ui/elements/flow.rb +15 -17
  55. data/lib/cyberarm_engine/ui/elements/image.rb +72 -52
  56. data/lib/cyberarm_engine/ui/elements/label.rb +156 -50
  57. data/lib/cyberarm_engine/ui/elements/list_box.rb +82 -0
  58. data/lib/cyberarm_engine/ui/elements/progress.rb +51 -50
  59. data/lib/cyberarm_engine/ui/elements/radio.rb +6 -0
  60. data/lib/cyberarm_engine/ui/elements/slider.rb +104 -0
  61. data/lib/cyberarm_engine/ui/elements/stack.rb +11 -13
  62. data/lib/cyberarm_engine/ui/elements/text_block.rb +156 -0
  63. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +65 -56
  64. data/lib/cyberarm_engine/ui/event.rb +47 -47
  65. data/lib/cyberarm_engine/ui/gui_state.rb +226 -135
  66. data/lib/cyberarm_engine/ui/style.rb +38 -37
  67. data/lib/cyberarm_engine/ui/theme.rb +182 -120
  68. data/lib/cyberarm_engine/vector.rb +293 -203
  69. data/lib/cyberarm_engine/version.rb +4 -4
  70. data/lib/cyberarm_engine/{engine.rb → window.rb} +114 -101
  71. metadata +88 -18
  72. data/lib/cyberarm_engine/gosu_ext/circle.rb +0 -9
  73. data/lib/cyberarm_engine/shader.rb +0 -262
@@ -1,67 +1,160 @@
1
- module CyberarmEngine
2
- class Element
3
- class Button < Label
4
- def initialize(text, options = {}, block = nil)
5
- super(text, options, block)
6
-
7
- @style.background_canvas.background = default(:background)
8
- end
9
-
10
- def render
11
- draw_text
12
- end
13
-
14
- def draw_text
15
- @text.draw
16
- end
17
-
18
- def enter(sender)
19
- @focus = false unless window.button_down?(Gosu::MsLeft)
20
-
21
- if @focus
22
- @style.background_canvas.background = default(:active, :background)
23
- @text.color = default(:active, :color)
24
- else
25
- @style.background_canvas.background = default(:hover, :background)
26
- @text.color = default(:hover, :color)
27
- end
28
-
29
- return :handled
30
- end
31
-
32
- def left_mouse_button(sender, x, y)
33
- @focus = true
34
- @style.background_canvas.background = default(:active, :background)
35
- window.current_state.focus = self
36
- @text.color = default(:active, :color)
37
-
38
- return :handled
39
- end
40
-
41
- def released_left_mouse_button(sender,x, y)
42
- enter(sender)
43
-
44
- return :handled
45
- end
46
-
47
- def clicked_left_mouse_button(sender, x, y)
48
- @block.call(self) if @block
49
-
50
- return :handled
51
- end
52
-
53
- def leave(sender)
54
- @style.background_canvas.background = default(:background)
55
- @text.color = default(:color)
56
-
57
- return :handled
58
- end
59
-
60
- def blur(sender)
61
- @focus = false
62
-
63
- return :handled
64
- end
65
- end
66
- end
67
- end
1
+ module CyberarmEngine
2
+ class Element
3
+ class Button < TextBlock
4
+ def initialize(text_or_image, options = {}, block = nil)
5
+ @image = nil
6
+ @scale_x = 1
7
+ @scale_y = 1
8
+
9
+ @image = text_or_image if text_or_image.is_a?(Gosu::Image)
10
+
11
+ super(text_or_image, options, block)
12
+
13
+ @style.background_canvas.background = @style.background
14
+ end
15
+
16
+ def render
17
+ if @image
18
+ draw_image
19
+ else
20
+ draw_text
21
+ end
22
+ end
23
+
24
+ def draw_image
25
+ @image.draw(
26
+ @style.border_thickness_left + @style.padding_left + @x,
27
+ @style.border_thickness_top + @style.padding_top + @y,
28
+ @z + 2,
29
+ @scale_x, @scale_y, @text.color
30
+ )
31
+ end
32
+
33
+ def draw_text
34
+ @text.draw
35
+ end
36
+
37
+ def enter(_sender)
38
+ @focus = false unless window.button_down?(Gosu::MsLeft)
39
+
40
+ if !@enabled
41
+ @style.background_canvas.background = @style.disabled[:background]
42
+ @text.color = @style.disabled[:color]
43
+ elsif @focus
44
+ @style.background_canvas.background = @style.active[:background]
45
+ @text.color = @style.active[:color]
46
+ else
47
+ @style.background_canvas.background = @style.hover[:background]
48
+ @text.color = @style.hover[:color]
49
+ end
50
+
51
+ :handled
52
+ end
53
+
54
+ def left_mouse_button(_sender, _x, _y)
55
+ @focus = true
56
+
57
+ unless @enabled
58
+ @style.background_canvas.background = @style.disabled[:background]
59
+ @text.color = @style.disabled[:color]
60
+ else
61
+ @style.background_canvas.background = @style.active[:background]
62
+ @text.color = @style.active[:color]
63
+ end
64
+
65
+ window.current_state.focus = self
66
+
67
+ :handled
68
+ end
69
+
70
+ def released_left_mouse_button(sender, _x, _y)
71
+ enter(sender)
72
+
73
+ :handled
74
+ end
75
+
76
+ def clicked_left_mouse_button(_sender, _x, _y)
77
+ @block.call(self) if @enabled && @block
78
+
79
+ :handled
80
+ end
81
+
82
+ def leave(_sender)
83
+ unless @enabled
84
+ @style.background_canvas.background = @style.disabled[:background]
85
+ @text.color = @style.disabled[:color]
86
+ else
87
+ @style.background_canvas.background = @style.background
88
+ @text.color = @style.color
89
+ end
90
+
91
+ :handled
92
+ end
93
+
94
+ def blur(_sender)
95
+ @focus = false
96
+
97
+ :handled
98
+ end
99
+
100
+ def recalculate
101
+ unless @enabled
102
+ @style.background_canvas.background = @style.disabled[:background]
103
+ @text.color = @style.disabled[:color]
104
+ else
105
+ @style.background_canvas.background = @style.background
106
+ @text.color = @style.color
107
+ end
108
+
109
+ if @image
110
+ @width = 0
111
+ @height = 0
112
+
113
+ _width = dimensional_size(@style.image_width, :width)
114
+ _height = dimensional_size(@style.image_height, :height)
115
+
116
+ if _width && _height
117
+ @scale_x = _width.to_f / @image.width
118
+ @scale_y = _height.to_f / @image.height
119
+ elsif _width
120
+ @scale_x = _width.to_f / @image.width
121
+ @scale_y = @scale_x
122
+ elsif _height
123
+ @scale_y = _height.to_f / @image.height
124
+ @scale_x = @scale_y
125
+ else
126
+ @scale_x = 1
127
+ @scale_y = 1
128
+ end
129
+
130
+ @width = _width || @image.width.round * @scale_x
131
+ @height = _height || @image.height.round * @scale_y
132
+
133
+ update_background
134
+ else
135
+ super
136
+ end
137
+ end
138
+
139
+ def value
140
+ @image || super
141
+ end
142
+
143
+ def value=(value)
144
+ if value.is_a?(Gosu::Image)
145
+ @image = value
146
+ else
147
+ super
148
+ end
149
+
150
+ old_width = width
151
+ old_height = height
152
+ recalculate
153
+
154
+ root.gui_state.request_recalculate if old_width != width || old_height != height
155
+
156
+ publish(:changed, self.value)
157
+ end
158
+ end
159
+ end
160
+ end
@@ -1,59 +1,51 @@
1
- module CyberarmEngine
2
- class Element
3
- class CheckBox < Flow
4
- def initialize(text, options, block = nil)
5
- super({}, block = nil)
6
- options[:toggled] = options[:checked]
7
-
8
- @toggle_button = ToggleButton.new(options)
9
- @label = Label.new(text, options)
10
-
11
- define_label_singletons
12
-
13
- add(@toggle_button)
14
- add(@label)
15
- end
16
-
17
- def text=(text)
18
- @label.text = text
19
- recalculate
20
- end
21
-
22
- def value
23
- @toggle_button.value
24
- end
25
-
26
- def value=(bool)
27
- @toggle_button.vlaue = bool
28
- end
29
-
30
- def define_label_singletons
31
- @label.define_singleton_method(:_toggle_button) do |button|
32
- @_toggle_button = button
33
- end
34
-
35
- @label._toggle_button(@toggle_button)
36
-
37
- @label.define_singleton_method(:holding_left_mouse_button) do |sender, x, y|
38
- @_toggle_button.left_mouse_button(sender, x, y)
39
- end
40
-
41
- @label.define_singleton_method(:released_left_mouse_button) do |sender, x, y|
42
- @_toggle_button.released_left_mouse_button(sender, x, y)
43
- end
44
-
45
- @label.define_singleton_method(:clicked_left_mouse_button) do |sender, x, y|
46
- @_toggle_button.clicked_left_mouse_button(sender, x, y)
47
- end
48
-
49
- @label.define_singleton_method(:enter) do |sender|
50
- @_toggle_button.enter(sender)
51
- end
52
-
53
- @label.define_singleton_method(:leave) do |sender|
54
- @_toggle_button.leave(sender)
55
- end
56
- end
57
- end
58
- end
59
- end
1
+ module CyberarmEngine
2
+ class Element
3
+ class CheckBox < Flow
4
+ def initialize(text, options, block = nil)
5
+ super(options, block)
6
+ options[:toggled] = options[:checked]
7
+
8
+ @toggle_button = ToggleButton.new(options)
9
+ @label = TextBlock.new(text, options)
10
+
11
+ @label.subscribe(:holding_left_mouse_button) do |sender, x, y|
12
+ @toggle_button.left_mouse_button(sender, x, y)
13
+ end
14
+
15
+ @label.subscribe(:released_left_mouse_button) do |sender, x, y|
16
+ @toggle_button.released_left_mouse_button(sender, x, y)
17
+ end
18
+
19
+ @label.subscribe(:clicked_left_mouse_button) do |sender, x, y|
20
+ @toggle_button.clicked_left_mouse_button(sender, x, y)
21
+ publish(:changed, @toggle_button.value)
22
+ end
23
+
24
+ @label.subscribe(:enter) do |sender|
25
+ @toggle_button.enter(sender)
26
+ end
27
+
28
+ @label.subscribe(:leave) do |sender|
29
+ @toggle_button.leave(sender)
30
+ end
31
+
32
+ add(@toggle_button)
33
+ add(@label)
34
+ end
35
+
36
+ def text=(text)
37
+ @label.text = text
38
+ recalculate
39
+ end
40
+
41
+ def value
42
+ @toggle_button.value
43
+ end
44
+
45
+ def value=(bool)
46
+ @toggle_button.value = bool
47
+ publish(:changed, @toggle_button.value)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,176 +1,256 @@
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
8
- attr_reader :scroll_x, :scroll_y
9
-
10
- def initialize(options = {}, block = nil)
11
- @gui_state = options.delete(:gui_state)
12
- super
13
-
14
- @scroll_x, @scroll_y = 0, 0
15
- @scroll_speed = 10
16
-
17
- @text_color = options[:color]
18
-
19
- @children = []
20
- end
21
-
22
- def build
23
- @block.call(self) if @block
24
-
25
- recalculate
26
- end
27
-
28
- def add(element)
29
- @children << element
30
-
31
- recalculate
32
- end
33
-
34
- def clear(&block)
35
- @children.clear
36
-
37
- old_container = $__current_container__
38
-
39
- $__current_container__ = self
40
- block.call(self) if block
41
-
42
- $__current_container__ = old_container
43
-
44
- recalculate
45
- root.gui_state.request_recalculate
46
- end
47
-
48
- def render
49
- Gosu.clip_to(@x, @y, width, height) do
50
- @children.each(&:draw)
51
- end
52
- end
53
-
54
- def update
55
- @children.each(&:update)
56
- end
57
-
58
- def hit_element?(x, y)
59
- @children.reverse_each do |child|
60
- case child
61
- when Container
62
- if element = child.hit_element?(x, y)
63
- return element
64
- end
65
- else
66
- return child if child.hit?(x, y)
67
- end
68
- end
69
-
70
- self if hit?(x, y)
71
- end
72
-
73
- def recalculate
74
- @current_position = Vector.new(@style.margin_left + @style.padding_left, @style.margin_top + @style.padding_top)
75
- return unless visible?
76
- stylize
77
-
78
- layout
79
-
80
- if is_root?
81
- @width = @style.width = window.width
82
- @height = @style.height = window.height
83
- else
84
- @width, @height = 0, 0
85
-
86
- _width = dimensional_size(@style.width, :width)
87
- _height= dimensional_size(@style.height,:height)
88
-
89
- @width = _width ? _width : (@children.map {|c| c.x + c.outer_width }.max || 0).round
90
- @height = _height ? _height : (@children.map {|c| c.y + c.outer_height}.max || 0).round
91
- end
92
-
93
-
94
- # Move child to parent after positioning
95
- @children.each do |child|
96
- child.x += @x
97
- child.y += @y
98
-
99
- child.stylize
100
- child.recalculate
101
- child.reposition # TODO: Implement top,bottom,left,center, and right positioning
102
- end
103
-
104
- update_background
105
- end
106
-
107
- def layout
108
- raise "Not overridden"
109
- end
110
-
111
- def max_width
112
- @max_width ? @max_width : window.width - (@parent ? @parent.style.margin_right + @style.margin_right : @style.margin_right)
113
- end
114
-
115
- def fits_on_line?(element) # Flow
116
- @current_position.x + element.outer_width <= max_width &&
117
- @current_position.x + element.outer_width <= window.width
118
- end
119
-
120
- def position_on_current_line(element) # Flow
121
- element.x = element.style.margin_left + @current_position.x
122
- element.y = element.style.margin_top + @current_position.y
123
-
124
- element.recalculate
125
-
126
- @current_position.x += element.outer_width
127
- @current_position.x = @style.margin_left if @current_position.x >= max_width
128
- end
129
-
130
- def tallest_neighbor(querier, y_position) # Flow
131
- response = querier
132
- @children.each do |child|
133
- response = child if child.outer_height > response.outer_height
134
- break if child == querier
135
- end
136
-
137
- return response
138
- end
139
-
140
- def position_on_next_line(child) # Flow
141
- @current_position.x = @style.margin_left
142
- @current_position.y += tallest_neighbor(child, @current_position.y).outer_height
143
-
144
- child.x = child.style.margin_left + @current_position.x
145
- child.y = child.style.margin_top + @current_position.y
146
-
147
- child.recalculate
148
-
149
- @current_position.x += child.outer_width
150
- end
151
-
152
- def move_to_next_line(element) # Stack
153
- element.x = element.style.margin_left + @current_position.x
154
- element.y = element.style.margin_top + @current_position.y
155
-
156
- element.recalculate
157
-
158
- @current_position.y += element.outer_height
159
- end
160
-
161
- # def mouse_wheel_up(sender, x, y)
162
- # @children.each {|c| c.y -= @scroll_speed}
163
- # @children.each {|c| c.recalculate}
164
- # end
165
-
166
- # def mouse_wheel_down(sender, x, y)
167
- # @children.each {|c| c.y += @scroll_speed}
168
- # @children.each {|c| c.recalculate}
169
- # end
170
-
171
- def value
172
- @children.map {|c| c.class}.join(", ")
173
- end
174
- end
175
- end
176
- 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
8
+
9
+ def initialize(options = {}, block = nil)
10
+ @gui_state = options.delete(:gui_state)
11
+ super
12
+
13
+ @scroll_position = Vector.new(0, 0)
14
+ @scroll_speed = 40
15
+
16
+ @text_color = options[:color]
17
+
18
+ @children = []
19
+
20
+ event(:window_size_changed)
21
+ end
22
+
23
+ def build
24
+ @block.call(self) if @block
25
+
26
+ root.gui_state.request_recalculate
27
+ end
28
+
29
+ def add(element)
30
+ @children << element
31
+
32
+ root.gui_state.request_recalculate
33
+ end
34
+
35
+ def clear(&block)
36
+ @children.clear
37
+
38
+ old_container = $__current_container__
39
+
40
+ $__current_container__ = self
41
+ block.call(self) if block
42
+
43
+ $__current_container__ = old_container
44
+
45
+ root.gui_state.request_recalculate
46
+ end
47
+
48
+ def apend(&block)
49
+ old_container = $__current_container__
50
+
51
+ $__current_container__ = self
52
+ block.call(self) if block
53
+
54
+ $__current_container__ = old_container
55
+
56
+ root.gui_state.request_recalculate
57
+ end
58
+
59
+ def render
60
+ Gosu.clip_to(@x, @y, width, height) do
61
+ @children.each(&:draw)
62
+ end
63
+
64
+ if false # DEBUG
65
+ Gosu.flush
66
+
67
+ Gosu.draw_line(
68
+ x, y, Gosu::Color::RED,
69
+ x + outer_width, y, Gosu::Color::RED,
70
+ Float::INFINITY
71
+ )
72
+ Gosu.draw_line(
73
+ x + outer_width, y, Gosu::Color::RED,
74
+ x + outer_width, y + outer_height, Gosu::Color::RED,
75
+ Float::INFINITY
76
+ )
77
+ Gosu.draw_line(
78
+ x + outer_width, y + outer_height, Gosu::Color::RED,
79
+ x, y + outer_height, Gosu::Color::RED,
80
+ Float::INFINITY
81
+ )
82
+ Gosu.draw_line(
83
+ x, outer_height, Gosu::Color::RED,
84
+ x, y, Gosu::Color::RED,
85
+ Float::INFINITY
86
+ )
87
+ end
88
+ end
89
+
90
+ def update
91
+ @children.each(&:update)
92
+ end
93
+
94
+ def hit_element?(x, y)
95
+ @children.reverse_each do |child|
96
+ next unless child.visible?
97
+
98
+ case child
99
+ when Container
100
+ if element = child.hit_element?(x, y)
101
+ return element
102
+ end
103
+ else
104
+ return child if child.hit?(x, y)
105
+ end
106
+ end
107
+
108
+ self if hit?(x, y)
109
+ end
110
+
111
+ def recalculate
112
+ @current_position = Vector.new(@style.margin_left + @style.padding_left, @style.margin_top + @style.padding_top)
113
+ @current_position += @scroll_position
114
+
115
+ return unless visible?
116
+
117
+ Stats.increment(:gui_recalculations_last_frame, 1)
118
+
119
+ stylize
120
+
121
+ layout
122
+
123
+ if is_root?
124
+ @width = @style.width = window.width
125
+ @height = @style.height = window.height
126
+ else
127
+ @width = 0
128
+ @height = 0
129
+
130
+ _width = dimensional_size(@style.width, :width)
131
+ _height = dimensional_size(@style.height, :height)
132
+
133
+ @width = _width || (@children.map { |c| c.x + c.outer_width }.max || 0).round
134
+ @height = _height || (@children.map { |c| c.y + c.outer_height }.max || 0).round
135
+ end
136
+
137
+ # Move child to parent after positioning
138
+ @children.each do |child|
139
+ child.x += (@x + @style.border_thickness_left) - style.margin_left
140
+ child.y += (@y + @style.border_thickness_top) - style.margin_top
141
+
142
+ child.stylize
143
+ child.recalculate
144
+ child.reposition # TODO: Implement top,bottom,left,center, and right positioning
145
+
146
+ Stats.increment(:gui_recalculations_last_frame, 1)
147
+ end
148
+
149
+ update_background
150
+ end
151
+
152
+ def layout
153
+ raise "Not overridden"
154
+ end
155
+
156
+ def max_width
157
+ _width = dimensional_size(@style.width, :width)
158
+ if _width
159
+ outer_width
160
+ else
161
+ window.width - (@parent ? @parent.style.margin_right + @style.margin_right : @style.margin_right)
162
+ end
163
+ end
164
+
165
+ def fits_on_line?(element) # Flow
166
+ p [@options[:id], @width] if @options[:id]
167
+ @current_position.x + element.outer_width <= max_width &&
168
+ @current_position.x + element.outer_width <= window.width
169
+ end
170
+
171
+ def position_on_current_line(element) # Flow
172
+ element.x = element.style.margin_left + @current_position.x
173
+ element.y = element.style.margin_top + @current_position.y
174
+
175
+ @current_position.x += element.outer_width
176
+ @current_position.x = @style.margin_left if @current_position.x >= max_width
177
+ end
178
+
179
+ def tallest_neighbor(querier, _y_position) # Flow
180
+ response = querier
181
+ @children.each do |child|
182
+ response = child if child.outer_height > response.outer_height
183
+ break if child == querier
184
+ end
185
+
186
+ response
187
+ end
188
+
189
+ def position_on_next_line(child) # Flow
190
+ @current_position.x = @style.margin_left
191
+ @current_position.y += tallest_neighbor(child, @current_position.y).outer_height
192
+
193
+ child.x = child.style.margin_left + @current_position.x
194
+ child.y = child.style.margin_top + @current_position.y
195
+
196
+ @current_position.x += child.outer_width
197
+ end
198
+
199
+ def move_to_next_line(element) # Stack
200
+ element.x = element.style.margin_left + @current_position.x
201
+ element.y = element.style.margin_top + @current_position.y
202
+
203
+ @current_position.y += element.outer_height
204
+ end
205
+
206
+ def mouse_wheel_up(sender, x, y)
207
+ return unless @style.scroll
208
+ return if height < max_scroll_height
209
+
210
+ if @scroll_position.y < 0
211
+ @scroll_position.y += @scroll_speed
212
+ @scroll_position.y = 0 if @scroll_position.y > 0
213
+ recalculate
214
+
215
+ return :handled
216
+ end
217
+ end
218
+
219
+ def mouse_wheel_down(sender, x, y)
220
+ return unless @style.scroll
221
+ return if height < max_scroll_height
222
+
223
+ if @scroll_position.y.abs < max_scroll_height
224
+ @scroll_position.y -= @scroll_speed
225
+ @scroll_position.y = -max_scroll_height if @scroll_position.y.abs > max_scroll_height
226
+ recalculate
227
+
228
+ return :handled
229
+ end
230
+ end
231
+
232
+ def value
233
+ @children.map { |c| c.class }.join(", ")
234
+ end
235
+
236
+ def to_s
237
+ "#{self.class} x=#{x} y=#{y} width=#{width} height=#{height} children=#{@children.size}"
238
+ end
239
+
240
+ def write_tree(indent = "", _index = 0)
241
+ puts self
242
+
243
+ indent += " "
244
+ @children.each_with_index do |child, i|
245
+ print "#{indent}#{i}: "
246
+
247
+ if child.is_a?(Container)
248
+ child.write_tree(indent)
249
+ else
250
+ puts child
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end