cyberarm_engine 0.13.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
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