cyberarm_engine 0.24.5 → 0.25.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 (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 +25 -24
  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 -115
  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 +158 -180
  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 +131 -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 +111 -98
  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/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 -216
  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 -52
  48. data/lib/cyberarm_engine/opengl/orthographic_camera.rb +46 -46
  49. data/lib/cyberarm_engine/opengl/perspective_camera.rb +41 -41
  50. data/lib/cyberarm_engine/opengl/renderer/bounding_box_renderer.rb +249 -249
  51. data/lib/cyberarm_engine/opengl/renderer/g_buffer.rb +167 -167
  52. data/lib/cyberarm_engine/opengl/renderer/opengl_renderer.rb +307 -307
  53. data/lib/cyberarm_engine/opengl/renderer/renderer.rb +33 -33
  54. data/lib/cyberarm_engine/opengl/shader.rb +408 -408
  55. data/lib/cyberarm_engine/opengl/texture.rb +69 -69
  56. data/lib/cyberarm_engine/opengl.rb +53 -53
  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 -126
  63. data/lib/cyberarm_engine/trees/aabb_tree.rb +55 -55
  64. data/lib/cyberarm_engine/trees/aabb_tree_debug.rb +29 -29
  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 +407 -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 +222 -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 +326 -321
  86. data/lib/cyberarm_engine/ui/style.rb +61 -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 +76 -70
  92. data/mrbgem.rake +29 -29
  93. metadata +3 -3
@@ -1,321 +1,326 @@
1
- module CyberarmEngine
2
- class GuiState < GameState
3
- include Common
4
- include DSL
5
-
6
- def initialize(options = {})
7
- @options = options
8
- @game_objects = []
9
- @global_pause = false
10
-
11
- @down_keys = {}
12
-
13
- @root_container = Element::Stack.new(gui_state: self)
14
- @game_objects << @root_container
15
- CyberarmEngine::Element::Container.current_container = @root_container
16
-
17
- @active_width = window.width
18
- @active_height = window.height
19
-
20
- @menu = nil
21
- @focus = nil
22
- @mouse_over = nil
23
- @mouse_down_on = {}
24
- @mouse_down_position = {}
25
- @last_mouse_pos = nil
26
- @dragging_element = nil
27
- @pending_recalculate_request = false
28
- @pending_element_recalculate_requests = []
29
-
30
- @needs_repaint = false
31
-
32
- @menu = nil
33
- @min_drag_distance = 0
34
- @mouse_pos = Vector.new
35
- end
36
-
37
- def post_setup
38
- @tip = Element::ToolTip.new("", parent: @root_container, z: Float::INFINITY, theme: current_theme)
39
- end
40
-
41
- def menu
42
- @menu
43
- end
44
-
45
- # throws :blur event to focused element and sets GuiState focused element
46
- # Does NOT throw :focus event at element or set element as focused
47
- def focus=(element)
48
- @focus.publish(:blur) if @focus && element && @focus != element
49
- @focus = element
50
- end
51
-
52
- def focused
53
- @focus
54
- end
55
-
56
- def draw
57
- Stats.frame.start_timing(:gui_element_recalculate_requests)
58
-
59
- # puts "PENDING REQUESTS: #{@pending_element_recalculate_requests.size}" if @pending_element_recalculate_requests.size.positive?
60
- @pending_element_recalculate_requests.each(&:recalculate)
61
- @pending_element_recalculate_requests.clear
62
-
63
- Stats.frame.end_timing(:gui_element_recalculate_requests)
64
-
65
- if @pending_recalculate_request
66
- Stats.frame.start_timing(:gui_recalculate)
67
-
68
- @root_container.recalculate
69
-
70
- @pending_recalculate_request = false
71
-
72
- Stats.frame.end_timing(:gui_recalculate)
73
- end
74
-
75
- super
76
-
77
- if @menu
78
- Gosu.flush
79
- @menu.draw
80
- end
81
-
82
- if @tip && @tip.value.length.positive?
83
- Gosu.flush
84
-
85
- @tip.draw
86
- end
87
-
88
- if CyberarmEngine.const_defined?("GUI_DEBUG")
89
- Gosu.flush
90
-
91
- @root_container.debug_draw
92
- end
93
-
94
- @needs_repaint = false
95
- end
96
-
97
- def needs_repaint?
98
- @needs_repaint
99
- end
100
-
101
- def update
102
- if @pending_focus_request
103
- @pending_focus_request = false
104
-
105
- self.focus = @pending_focus_element
106
- @pending_focus_element.publish(:focus)
107
- end
108
-
109
- @menu&.update
110
-
111
- super
112
-
113
- if @active_width != window.width || @active_height != window.height
114
- request_recalculate
115
- @root_container.publish(:window_size_changed)
116
- end
117
-
118
- @active_width = window.width
119
- @active_height = window.height
120
-
121
- return unless window.has_focus?
122
- return unless window.current_state == self
123
-
124
- new_mouse_over = @menu.hit_element?(window.mouse_x, window.mouse_y) if @menu
125
- new_mouse_over ||= @root_container.hit_element?(window.mouse_x, window.mouse_y)
126
-
127
- if new_mouse_over
128
- new_mouse_over.publish(:enter) if new_mouse_over != @mouse_over
129
- new_mouse_over.publish(:hover)
130
- # puts "#{new_mouse_over.class}[#{new_mouse_over.value}]: #{new_mouse_over.x}:#{new_mouse_over.y} #{new_mouse_over.width}:#{new_mouse_over.height}" if new_mouse_over != @mouse_over
131
- end
132
- @mouse_over.publish(:leave) if @mouse_over && new_mouse_over != @mouse_over
133
- @mouse_over = new_mouse_over
134
-
135
- redirect_holding_mouse_button(:left) if @mouse_over && Gosu.button_down?(Gosu::MS_LEFT)
136
- redirect_holding_mouse_button(:middle) if @mouse_over && Gosu.button_down?(Gosu::MS_MIDDLE)
137
- redirect_holding_mouse_button(:right) if @mouse_over && Gosu.button_down?(Gosu::MS_RIGHT)
138
-
139
- if Vector.new(window.mouse_x, window.mouse_y) == @last_mouse_pos
140
- if @mouse_over && (Gosu.milliseconds - @mouse_moved_at) > tool_tip_delay
141
- @tip.value = @mouse_over.tip if @mouse_over
142
- @tip.x = window.mouse_x
143
- @tip.x = 0 if @tip.x < 0
144
- @tip.x = window.width - @tip.width if @tip.x + @tip.width > window.width
145
- @tip.y = window.mouse_y - (@tip.height + 5)
146
- @tip.y = 0 if @tip.y < 0
147
- @tip.y = window.height - @tip.height if @tip.y + @tip.height > window.height
148
- @tip.update
149
- @tip.recalculate
150
- else
151
- @tip.value = ""
152
- end
153
- else
154
- @mouse_moved_at = Gosu.milliseconds
155
- end
156
-
157
- @last_mouse_pos = Vector.new(window.mouse_x, window.mouse_y)
158
- @mouse_pos = @last_mouse_pos.clone
159
- end
160
-
161
- def button_down(id)
162
- super
163
-
164
- case id
165
- when Gosu::MS_LEFT
166
- redirect_mouse_button(:left)
167
- when Gosu::MS_MIDDLE
168
- redirect_mouse_button(:middle)
169
- when Gosu::MS_RIGHT
170
- redirect_mouse_button(:right)
171
- when Gosu::KB_F5
172
- request_recalculate
173
- end
174
-
175
- @focus.button_down(id) if @focus.respond_to?(:button_down)
176
- end
177
-
178
- def button_up(id)
179
- super
180
-
181
- case id
182
- when Gosu::MS_LEFT
183
- redirect_released_mouse_button(:left)
184
- when Gosu::MS_MIDDLE
185
- redirect_released_mouse_button(:middle)
186
- when Gosu::MS_RIGHT
187
- redirect_released_mouse_button(:right)
188
- when Gosu::MS_WHEEL_UP
189
- redirect_mouse_wheel(:up)
190
- when Gosu::MS_WHEEL_DOWN
191
- redirect_mouse_wheel(:down)
192
- when Gosu::KB_HOME
193
- redirect_scroll_jump_to(:top)
194
- when Gosu::KB_END
195
- redirect_scroll_jump_to(:end)
196
- when Gosu::KB_PAGE_UP
197
- redirect_scroll_page(:up)
198
- when Gosu::KB_PAGE_DOWN
199
- redirect_scroll_page(:down)
200
- end
201
-
202
- @focus.button_up(id) if @focus.respond_to?(:button_up)
203
-
204
- # Prevents menu from popping back up if the listbox is clicked to hide it.
205
- @hid_menu_for = nil
206
- end
207
-
208
- def tool_tip_delay
209
- @tip.style.delay || 250 # ms
210
- end
211
-
212
- def redirect_mouse_button(button)
213
- hide_menu unless @menu && (@menu == @mouse_over) || (@mouse_over&.parent == @menu)
214
-
215
- if @focus && @mouse_over != @focus
216
- @focus.publish(:blur)
217
- @focus = nil
218
- end
219
-
220
- if @mouse_over && @hid_menu_for != @mouse_over
221
- @mouse_down_position[button] = Vector.new(window.mouse_x, window.mouse_y)
222
- @mouse_down_on[button] = @mouse_over
223
-
224
- @mouse_over.publish(:"#{button}_mouse_button", window.mouse_x, window.mouse_y)
225
- else
226
- @mouse_down_position[button] = nil
227
- @mouse_down_on[button] = nil
228
- end
229
- end
230
-
231
- def redirect_released_mouse_button(button)
232
- hide_menu if @menu && (@menu == @mouse_over) || (@mouse_over&.parent == @menu)
233
-
234
- if @mouse_over && @hid_menu_for != @mouse_over
235
- @mouse_over.publish(:"released_#{button}_mouse_button", window.mouse_x, window.mouse_y)
236
- if @mouse_over == @mouse_down_on[button]
237
- @mouse_over.publish(:"clicked_#{button}_mouse_button", window.mouse_x,
238
- window.mouse_y)
239
- end
240
- end
241
-
242
- if @dragging_element
243
- @dragging_element.publish(:end_drag, window.mouse_x, window.mouse_y, button)
244
- @dragging_element = nil
245
- end
246
-
247
- @mouse_down_position[button] = nil
248
- @mouse_down_on[button] = nil
249
- end
250
-
251
- def redirect_holding_mouse_button(button)
252
- if !@dragging_element && @mouse_down_on[button] && @mouse_down_on[button].draggable?(button) && @mouse_pos.distance(@mouse_down_position[button]) > @min_drag_distance
253
- @dragging_element = @mouse_down_on[button]
254
- @dragging_element.publish(:begin_drag, window.mouse_x, window.mouse_y, button)
255
- end
256
-
257
- if @dragging_element
258
- @dragging_element.publish(:drag_update, window.mouse_x, window.mouse_y, button) if @dragging_element
259
- elsif @mouse_over
260
- @mouse_over.publish(:"holding_#{button}_mouse_button", window.mouse_x, window.mouse_y)
261
- end
262
- end
263
-
264
- def redirect_mouse_wheel(button)
265
- @mouse_over.publish(:"mouse_wheel_#{button}", window.mouse_x, window.mouse_y) if (@mouse_over && !@menu) || (@mouse_over && @mouse_over == @menu)
266
- end
267
-
268
- def redirect_scroll_jump_to(edge)
269
- @mouse_over.publish(:"scroll_jump_to_#{edge}", window.mouse_x, window.mouse_y) if (@mouse_over && !@menu) || (@mouse_over && @mouse_over == @menu)
270
- end
271
-
272
- def redirect_scroll_page(edge)
273
- @mouse_over.publish(:"scroll_page_#{edge}", window.mouse_x, window.mouse_y) if (@mouse_over && !@menu) || (@mouse_over && @mouse_over == @menu)
274
- end
275
-
276
- # Schedule a full GUI recalculation on next update
277
- def request_recalculate
278
- @pending_recalculate_request = true
279
- end
280
-
281
- def request_recalculate_for(element)
282
- # element is already queued
283
- return if @pending_element_recalculate_requests.detect { |e| e == element }
284
-
285
- @pending_element_recalculate_requests << element
286
- end
287
-
288
- def request_focus(element)
289
- @pending_focus_request = true
290
- @pending_focus_element = element
291
- end
292
-
293
- def request_repaint
294
- # puts caller[0..4]
295
- # puts
296
- @needs_repaint = true
297
- end
298
-
299
- def show_menu(list_box)
300
- @menu = list_box
301
- end
302
-
303
- def hide_menu
304
- return unless @menu
305
-
306
- request_repaint
307
-
308
- @hid_menu_for = @menu.parent
309
- @menu = nil
310
- end
311
-
312
- def to_s
313
- # "#{self.class} children=#{@children.map { |c| c.to_s }}"
314
- @root_container.to_s
315
- end
316
-
317
- def inspect
318
- to_s
319
- end
320
- end
321
- end
1
+ module CyberarmEngine
2
+ class GuiState < GameState
3
+ include Common
4
+ include DSL
5
+
6
+ def initialize(options = {})
7
+ @options = options
8
+ @game_objects = []
9
+ @global_pause = false
10
+
11
+ @down_keys = {}
12
+
13
+ @root_container = Element::Stack.new(gui_state: self)
14
+ @game_objects << @root_container
15
+ CyberarmEngine::Element::Container.current_container = @root_container
16
+
17
+ @active_width = window.width
18
+ @active_height = window.height
19
+
20
+ @menu = nil
21
+ @focus = nil
22
+ @mouse_over = nil
23
+ @mouse_down_on = {}
24
+ @mouse_down_position = {}
25
+ @last_mouse_pos = nil
26
+ @dragging_element = nil
27
+ @pending_recalculate_request = false
28
+ @pending_element_recalculate_requests = []
29
+
30
+ @needs_repaint = false
31
+
32
+ @menu = nil
33
+ @min_drag_distance = 0
34
+ @mouse_pos = Vector.new
35
+ end
36
+
37
+ def post_setup
38
+ @tip = Element::ToolTip.new("", parent: @root_container, z: Float::INFINITY, theme: current_theme)
39
+ end
40
+
41
+ def menu
42
+ @menu
43
+ end
44
+
45
+ # throws :blur event to focused element and sets GuiState focused element
46
+ # Does NOT throw :focus event at element or set element as focused
47
+ def focus=(element)
48
+ @focus.publish(:blur) if @focus && element && @focus != element
49
+ @focus = element
50
+ end
51
+
52
+ def focused
53
+ @focus
54
+ end
55
+
56
+ def draw
57
+ # t = Gosu.milliseconds
58
+ # report_recalculate_time = @pending_recalculate_request || @pending_element_recalculate_requests.size.positive?
59
+
60
+ StackProf.start(mode: :wall) if RUBY_ENGINE != "mruby" && defined?(StackProf)
61
+ Stats.frame.start_timing(:gui_element_recalculate_requests)
62
+
63
+ # puts "PENDING REQUESTS: #{@pending_element_recalculate_requests.size}" if @pending_element_recalculate_requests.size.positive?
64
+ @pending_element_recalculate_requests.shift(&:recalculate)
65
+
66
+ Stats.frame.end_timing(:gui_element_recalculate_requests)
67
+
68
+ Stats.frame.start_timing(:gui_recalculate)
69
+
70
+ while(@pending_recalculate_request)
71
+ @pending_recalculate_request = false
72
+
73
+ @root_container.recalculate
74
+ end
75
+
76
+ StackProf.stop if RUBY_ENGINE != "mruby" && defined?(StackProf)
77
+ Stats.frame.end_timing(:gui_recalculate)
78
+ # puts "TOOK: #{Gosu.milliseconds - t}ms to recalculate #{self.class}:0x#{object_id.to_s(16)}" if report_recalculate_time && Gosu.milliseconds - t > 0
79
+
80
+ super
81
+
82
+ if @menu
83
+ Gosu.flush
84
+ @menu.draw
85
+ end
86
+
87
+ if @tip && @tip.value.length.positive?
88
+ Gosu.flush
89
+
90
+ @tip.draw
91
+ end
92
+
93
+ if CyberarmEngine.const_defined?("GUI_DEBUG")
94
+ Gosu.flush
95
+
96
+ @root_container.debug_draw
97
+ end
98
+
99
+ @needs_repaint = false
100
+ end
101
+
102
+ def needs_repaint?
103
+ @needs_repaint
104
+ end
105
+
106
+ def update
107
+ if @pending_focus_request
108
+ @pending_focus_request = false
109
+
110
+ self.focus = @pending_focus_element
111
+ @pending_focus_element.publish(:focus)
112
+ end
113
+
114
+ @menu&.update
115
+
116
+ super
117
+
118
+ if @active_width != window.width || @active_height != window.height
119
+ request_recalculate
120
+ @root_container.publish(:window_size_changed)
121
+ end
122
+
123
+ @active_width = window.width
124
+ @active_height = window.height
125
+
126
+ return unless window.has_focus?
127
+ return unless window.current_state == self
128
+
129
+ new_mouse_over = @menu.hit_element?(window.mouse_x, window.mouse_y) if @menu
130
+ new_mouse_over ||= @root_container.hit_element?(window.mouse_x, window.mouse_y)
131
+
132
+ if new_mouse_over
133
+ new_mouse_over.publish(:enter) if new_mouse_over != @mouse_over
134
+ new_mouse_over.publish(:hover)
135
+ # puts "#{new_mouse_over.class}[#{new_mouse_over.value}]: #{new_mouse_over.x}:#{new_mouse_over.y} #{new_mouse_over.width}:#{new_mouse_over.height}" if new_mouse_over != @mouse_over
136
+ end
137
+ @mouse_over.publish(:leave) if @mouse_over && new_mouse_over != @mouse_over
138
+ @mouse_over = new_mouse_over
139
+
140
+ redirect_holding_mouse_button(:left) if @mouse_over && Gosu.button_down?(Gosu::MS_LEFT)
141
+ redirect_holding_mouse_button(:middle) if @mouse_over && Gosu.button_down?(Gosu::MS_MIDDLE)
142
+ redirect_holding_mouse_button(:right) if @mouse_over && Gosu.button_down?(Gosu::MS_RIGHT)
143
+
144
+ if Vector.new(window.mouse_x, window.mouse_y) == @last_mouse_pos
145
+ if @mouse_over && (Gosu.milliseconds - @mouse_moved_at) > tool_tip_delay
146
+ @tip.value = @mouse_over.tip if @mouse_over
147
+ @tip.x = window.mouse_x
148
+ @tip.x = 0 if @tip.x < 0
149
+ @tip.x = window.width - @tip.width if @tip.x + @tip.width > window.width
150
+ @tip.y = window.mouse_y - (@tip.height + 5)
151
+ @tip.y = 0 if @tip.y < 0
152
+ @tip.y = window.height - @tip.height if @tip.y + @tip.height > window.height
153
+ @tip.update
154
+ @tip.recalculate
155
+ else
156
+ @tip.value = ""
157
+ end
158
+ else
159
+ @mouse_moved_at = Gosu.milliseconds
160
+ end
161
+
162
+ @last_mouse_pos = Vector.new(window.mouse_x, window.mouse_y)
163
+ @mouse_pos = @last_mouse_pos.clone
164
+ end
165
+
166
+ def button_down(id)
167
+ super
168
+
169
+ case id
170
+ when Gosu::MS_LEFT
171
+ redirect_mouse_button(:left)
172
+ when Gosu::MS_MIDDLE
173
+ redirect_mouse_button(:middle)
174
+ when Gosu::MS_RIGHT
175
+ redirect_mouse_button(:right)
176
+ when Gosu::KB_F5
177
+ request_recalculate
178
+ end
179
+
180
+ @focus.button_down(id) if @focus.respond_to?(:button_down)
181
+ end
182
+
183
+ def button_up(id)
184
+ super
185
+
186
+ case id
187
+ when Gosu::MS_LEFT
188
+ redirect_released_mouse_button(:left)
189
+ when Gosu::MS_MIDDLE
190
+ redirect_released_mouse_button(:middle)
191
+ when Gosu::MS_RIGHT
192
+ redirect_released_mouse_button(:right)
193
+ when Gosu::MS_WHEEL_UP
194
+ redirect_mouse_wheel(:up)
195
+ when Gosu::MS_WHEEL_DOWN
196
+ redirect_mouse_wheel(:down)
197
+ when Gosu::KB_HOME
198
+ redirect_scroll_jump_to(:top)
199
+ when Gosu::KB_END
200
+ redirect_scroll_jump_to(:end)
201
+ when Gosu::KB_PAGE_UP
202
+ redirect_scroll_page(:up)
203
+ when Gosu::KB_PAGE_DOWN
204
+ redirect_scroll_page(:down)
205
+ end
206
+
207
+ @focus.button_up(id) if @focus.respond_to?(:button_up)
208
+
209
+ # Prevents menu from popping back up if the listbox is clicked to hide it.
210
+ @hid_menu_for = nil
211
+ end
212
+
213
+ def tool_tip_delay
214
+ @tip.style.delay || 250 # ms
215
+ end
216
+
217
+ def redirect_mouse_button(button)
218
+ hide_menu unless @menu && (@menu == @mouse_over) || (@mouse_over&.parent == @menu)
219
+
220
+ if @focus && @mouse_over != @focus
221
+ @focus.publish(:blur)
222
+ @focus = nil
223
+ end
224
+
225
+ if @mouse_over && @hid_menu_for != @mouse_over
226
+ @mouse_down_position[button] = Vector.new(window.mouse_x, window.mouse_y)
227
+ @mouse_down_on[button] = @mouse_over
228
+
229
+ @mouse_over.publish(:"#{button}_mouse_button", window.mouse_x, window.mouse_y)
230
+ else
231
+ @mouse_down_position[button] = nil
232
+ @mouse_down_on[button] = nil
233
+ end
234
+ end
235
+
236
+ def redirect_released_mouse_button(button)
237
+ hide_menu if @menu && (@menu == @mouse_over) || (@mouse_over&.parent == @menu)
238
+
239
+ if @mouse_over && @hid_menu_for != @mouse_over
240
+ @mouse_over.publish(:"released_#{button}_mouse_button", window.mouse_x, window.mouse_y)
241
+ if @mouse_over == @mouse_down_on[button]
242
+ @mouse_over.publish(:"clicked_#{button}_mouse_button", window.mouse_x,
243
+ window.mouse_y)
244
+ end
245
+ end
246
+
247
+ if @dragging_element
248
+ @dragging_element.publish(:end_drag, window.mouse_x, window.mouse_y, button)
249
+ @dragging_element = nil
250
+ end
251
+
252
+ @mouse_down_position[button] = nil
253
+ @mouse_down_on[button] = nil
254
+ end
255
+
256
+ def redirect_holding_mouse_button(button)
257
+ if !@dragging_element && @mouse_down_on[button] && @mouse_down_on[button].draggable?(button) && @mouse_pos.distance(@mouse_down_position[button]) > @min_drag_distance
258
+ @dragging_element = @mouse_down_on[button]
259
+ @dragging_element.publish(:begin_drag, window.mouse_x, window.mouse_y, button)
260
+ end
261
+
262
+ if @dragging_element
263
+ @dragging_element.publish(:drag_update, window.mouse_x, window.mouse_y, button) if @dragging_element
264
+ elsif @mouse_over
265
+ @mouse_over.publish(:"holding_#{button}_mouse_button", window.mouse_x, window.mouse_y)
266
+ end
267
+ end
268
+
269
+ def redirect_mouse_wheel(button)
270
+ @mouse_over.publish(:"mouse_wheel_#{button}", window.mouse_x, window.mouse_y) if (@mouse_over && !@menu) || (@mouse_over && @mouse_over == @menu)
271
+ end
272
+
273
+ def redirect_scroll_jump_to(edge)
274
+ @mouse_over.publish(:"scroll_jump_to_#{edge}", window.mouse_x, window.mouse_y) if (@mouse_over && !@menu) || (@mouse_over && @mouse_over == @menu)
275
+ end
276
+
277
+ def redirect_scroll_page(edge)
278
+ @mouse_over.publish(:"scroll_page_#{edge}", window.mouse_x, window.mouse_y) if (@mouse_over && !@menu) || (@mouse_over && @mouse_over == @menu)
279
+ end
280
+
281
+ # Schedule a full GUI recalculation on next update
282
+ def request_recalculate
283
+ @pending_recalculate_request = true
284
+ end
285
+
286
+ def request_recalculate_for(element)
287
+ # element is already queued
288
+ return if @pending_element_recalculate_requests.detect { |e| e == element }
289
+
290
+ @pending_element_recalculate_requests << element
291
+ end
292
+
293
+ def request_focus(element)
294
+ @pending_focus_request = true
295
+ @pending_focus_element = element
296
+ end
297
+
298
+ def request_repaint
299
+ # puts caller[0..4]
300
+ # puts
301
+ @needs_repaint = true
302
+ end
303
+
304
+ def show_menu(list_box)
305
+ @menu = list_box
306
+ end
307
+
308
+ def hide_menu
309
+ return unless @menu
310
+
311
+ request_repaint
312
+
313
+ @hid_menu_for = @menu.parent
314
+ @menu = nil
315
+ end
316
+
317
+ def to_s
318
+ # "#{self.class} children=#{@children.map { |c| c.to_s }}"
319
+ @root_container.to_s
320
+ end
321
+
322
+ def inspect
323
+ to_s
324
+ end
325
+ end
326
+ end