cyberarm_engine 0.25.0 → 0.30.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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +9 -8
  3. data/.rubocop.yml +7 -7
  4. data/.travis.yml +5 -5
  5. data/Gemfile +6 -6
  6. data/Gemfile.lock +37 -25
  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/assets/textures/ui/circle.png +0 -0
  17. data/assets/textures/ui/circle_small.png +0 -0
  18. data/assets/textures/ui/rounded.png +0 -0
  19. data/assets/textures/ui/rounded_bottom.png +0 -0
  20. data/assets/textures/ui/rounded_left.png +0 -0
  21. data/assets/textures/ui/rounded_left_bottom.png +0 -0
  22. data/assets/textures/ui/rounded_left_top.png +0 -0
  23. data/assets/textures/ui/rounded_right.png +0 -0
  24. data/assets/textures/ui/rounded_right_bottom.png +0 -0
  25. data/assets/textures/ui/rounded_right_top.png +0 -0
  26. data/assets/textures/ui/rounded_small.png +0 -0
  27. data/assets/textures/ui/rounded_tiny.png +0 -0
  28. data/assets/textures/ui/rounded_top.png +0 -0
  29. data/assets/textures/ui/square.png +0 -0
  30. data/bin/console +14 -14
  31. data/bin/setup +8 -8
  32. data/cyberarm_engine.gemspec +35 -36
  33. data/lib/cyberarm_engine/animator.rb +219 -219
  34. data/lib/cyberarm_engine/background.rb +156 -158
  35. data/lib/cyberarm_engine/background_image.rb +93 -93
  36. data/lib/cyberarm_engine/background_nine_slice.rb +182 -142
  37. data/lib/cyberarm_engine/bounding_box.rb +150 -150
  38. data/lib/cyberarm_engine/builtin/intro_state.rb +130 -130
  39. data/lib/cyberarm_engine/cache/download_manager.rb +123 -123
  40. data/lib/cyberarm_engine/cache.rb +4 -4
  41. data/lib/cyberarm_engine/common.rb +133 -131
  42. data/lib/cyberarm_engine/config_file.rb +46 -46
  43. data/lib/cyberarm_engine/console/command.rb +157 -157
  44. data/lib/cyberarm_engine/console/commands/help_command.rb +43 -43
  45. data/lib/cyberarm_engine/console/subcommand.rb +99 -99
  46. data/lib/cyberarm_engine/console.rb +248 -248
  47. data/lib/cyberarm_engine/event_bus.rb +39 -0
  48. data/lib/cyberarm_engine/game_object.rb +244 -244
  49. data/lib/cyberarm_engine/game_state.rb +134 -124
  50. data/lib/cyberarm_engine/gosu_ext/draw_arc.rb +111 -111
  51. data/lib/cyberarm_engine/gosu_ext/draw_circle.rb +30 -30
  52. data/lib/cyberarm_engine/gosu_ext/draw_path.rb +17 -17
  53. data/lib/cyberarm_engine/model/material.rb +21 -21
  54. data/lib/cyberarm_engine/model/mesh.rb +131 -131
  55. data/lib/cyberarm_engine/model/parser.rb +74 -74
  56. data/lib/cyberarm_engine/model/parsers/collada_parser.rb +138 -138
  57. data/lib/cyberarm_engine/model/parsers/wavefront_parser.rb +154 -154
  58. data/lib/cyberarm_engine/model.rb +216 -216
  59. data/lib/cyberarm_engine/model_cache.rb +31 -31
  60. data/lib/cyberarm_engine/notification.rb +82 -82
  61. data/lib/cyberarm_engine/notification_manager.rb +241 -241
  62. data/lib/cyberarm_engine/opengl/light.rb +52 -52
  63. data/lib/cyberarm_engine/opengl/orthographic_camera.rb +46 -46
  64. data/lib/cyberarm_engine/opengl/perspective_camera.rb +41 -41
  65. data/lib/cyberarm_engine/opengl/renderer/bounding_box_renderer.rb +249 -249
  66. data/lib/cyberarm_engine/opengl/renderer/g_buffer.rb +167 -167
  67. data/lib/cyberarm_engine/opengl/renderer/opengl_renderer.rb +307 -307
  68. data/lib/cyberarm_engine/opengl/renderer/renderer.rb +33 -33
  69. data/lib/cyberarm_engine/opengl/shader.rb +408 -408
  70. data/lib/cyberarm_engine/opengl/texture.rb +69 -69
  71. data/lib/cyberarm_engine/opengl.rb +53 -53
  72. data/lib/cyberarm_engine/pathfinding/graph.rb +64 -0
  73. data/lib/cyberarm_engine/pathfinding/graph_search.rb +6 -0
  74. data/lib/cyberarm_engine/pathfinding/graph_searches/astar_search.rb +6 -0
  75. data/lib/cyberarm_engine/pathfinding/graph_searches/breadth_first_search.rb +27 -0
  76. data/lib/cyberarm_engine/pathfinding/graph_searches/dijkstra_search.rb +6 -0
  77. data/lib/cyberarm_engine/pathfinding/graphs/grid.rb +52 -0
  78. data/lib/cyberarm_engine/ray.rb +56 -56
  79. data/lib/cyberarm_engine/result.rb +19 -0
  80. data/lib/cyberarm_engine/stats.rb +200 -200
  81. data/lib/cyberarm_engine/text.rb +288 -260
  82. data/lib/cyberarm_engine/timer.rb +27 -23
  83. data/lib/cyberarm_engine/transform.rb +296 -296
  84. data/lib/cyberarm_engine/trees/aabb_node.rb +126 -126
  85. data/lib/cyberarm_engine/trees/aabb_tree.rb +55 -55
  86. data/lib/cyberarm_engine/trees/aabb_tree_debug.rb +29 -29
  87. data/lib/cyberarm_engine/ui/border_canvas.rb +102 -102
  88. data/lib/cyberarm_engine/ui/dialog.rb +24 -0
  89. data/lib/cyberarm_engine/ui/dsl.rb +170 -142
  90. data/lib/cyberarm_engine/ui/element.rb +664 -662
  91. data/lib/cyberarm_engine/ui/elements/button.rb +99 -100
  92. data/lib/cyberarm_engine/ui/elements/check_box.rb +58 -54
  93. data/lib/cyberarm_engine/ui/elements/container.rb +413 -407
  94. data/lib/cyberarm_engine/ui/elements/edit_box.rb +200 -179
  95. data/lib/cyberarm_engine/ui/elements/edit_line.rb +299 -297
  96. data/lib/cyberarm_engine/ui/elements/flow.rb +15 -15
  97. data/lib/cyberarm_engine/ui/elements/image.rb +78 -72
  98. data/lib/cyberarm_engine/ui/elements/list_box.rb +79 -79
  99. data/lib/cyberarm_engine/ui/elements/menu.rb +27 -27
  100. data/lib/cyberarm_engine/ui/elements/menu_item.rb +6 -6
  101. data/lib/cyberarm_engine/ui/elements/progress.rb +95 -93
  102. data/lib/cyberarm_engine/ui/elements/radio.rb +6 -6
  103. data/lib/cyberarm_engine/ui/elements/slider.rb +111 -107
  104. data/lib/cyberarm_engine/ui/elements/stack.rb +11 -11
  105. data/lib/cyberarm_engine/ui/elements/text_block.rb +236 -222
  106. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +71 -67
  107. data/lib/cyberarm_engine/ui/elements/widget.rb +45 -0
  108. data/lib/cyberarm_engine/ui/event.rb +54 -54
  109. data/lib/cyberarm_engine/ui/gui_state.rb +402 -326
  110. data/lib/cyberarm_engine/ui/page.rb +39 -0
  111. data/lib/cyberarm_engine/ui/style.rb +94 -61
  112. data/lib/cyberarm_engine/ui/theme.rb +237 -225
  113. data/lib/cyberarm_engine/vector.rb +312 -312
  114. data/lib/cyberarm_engine/version.rb +4 -4
  115. data/lib/cyberarm_engine/window.rb +234 -195
  116. data/lib/cyberarm_engine.rb +88 -76
  117. data/mrbgem.rake +29 -29
  118. metadata +30 -5
@@ -1,326 +1,402 @@
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
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
+ @timers = []
10
+ @global_pause = false
11
+
12
+ @down_keys = {}
13
+
14
+ @root_container = Element::Stack.new(gui_state: self)
15
+ @game_objects << @root_container
16
+ CyberarmEngine::Element::Container.current_container = @root_container
17
+
18
+ @active_width = window.width
19
+ @active_height = window.height
20
+
21
+ @pages = {}
22
+ @page_host = nil
23
+ @page = nil
24
+
25
+ @menu = nil
26
+ @focus = nil
27
+ @mouse_over = nil
28
+ @mouse_down_on = {}
29
+ @mouse_down_position = {}
30
+ @last_mouse_pos = nil
31
+ @dragging_element = nil
32
+ @pending_recalculate_request = false
33
+ @pending_element_recalculate_requests = []
34
+ @hit_elements = []
35
+
36
+ @needs_repaint = false
37
+
38
+ @menu = nil
39
+ @min_drag_distance = 0
40
+ @mouse_pos = Vector.new
41
+ end
42
+
43
+ def post_setup
44
+ @tip = Element::ToolTip.new("", parent: @root_container, z: Float::INFINITY, theme: current_theme)
45
+ end
46
+
47
+ def active_menu
48
+ @menu
49
+ end
50
+
51
+ def page(klass, options = {})
52
+ @page&.blur
53
+
54
+ @pages[klass] = klass.new(parent: options[:parent] || self) unless @pages[klass]
55
+ @page = @pages[klass]
56
+
57
+ @page.options = options
58
+ page_host.clear do
59
+ @page.setup
60
+ end
61
+ @page.focus
62
+ end
63
+
64
+ def current_page
65
+ @page
66
+ end
67
+
68
+ def page_host
69
+ return @page.parent if @page.parent.is_a?(CyberarmEngine::Element::Container)
70
+ return @page_host if @page_host && @page_host.is_a?(CyberarmEngine::Element::Container)
71
+
72
+ @root_container
73
+ end
74
+
75
+ def page_host=(container)
76
+ raise "page host must be a CyberarmEngine::Element::Container" unless container.is_a?(Container)
77
+
78
+ @page_host = container
79
+ end
80
+
81
+ # throws :blur event to focused element and sets GuiState focused element
82
+ # Does NOT throw :focus event at element or set element as focused
83
+ def focus=(element)
84
+ @focus.publish(:blur) if @focus && element && @focus != element
85
+ @hit_elements.delete(@focus)
86
+ @focus = element
87
+ end
88
+
89
+ def focused
90
+ @focus
91
+ end
92
+
93
+ def draw
94
+ # t = Gosu.milliseconds
95
+ # report_recalculate_time = @pending_recalculate_request || @pending_element_recalculate_requests.size.positive?
96
+
97
+ StackProf.start(mode: :wall) if RUBY_ENGINE != "mruby" && defined?(StackProf)
98
+ Stats.frame.start_timing(:gui_element_recalculate_requests)
99
+
100
+ # puts "PENDING REQUESTS: #{@pending_element_recalculate_requests.size}" if @pending_element_recalculate_requests.size.positive?
101
+ while (e = @pending_element_recalculate_requests.shift)
102
+ # its pointless to recalculate individual elements when we've been asked to recalculate the whole gui
103
+ if @pending_recalculate_request
104
+ @pending_element_recalculate_requests.clear
105
+ break
106
+ end
107
+
108
+ e.recalculate
109
+ end
110
+
111
+ Stats.frame.end_timing(:gui_element_recalculate_requests)
112
+
113
+ Stats.frame.start_timing(:gui_recalculate)
114
+
115
+ while(@pending_recalculate_request)
116
+ @pending_recalculate_request = false
117
+
118
+ @root_container.recalculate
119
+ end
120
+
121
+ StackProf.stop if RUBY_ENGINE != "mruby" && defined?(StackProf)
122
+ Stats.frame.end_timing(:gui_recalculate)
123
+ # puts "TOOK: #{Gosu.milliseconds - t}ms to recalculate #{self.class}:0x#{object_id.to_s(16)}" if report_recalculate_time && Gosu.milliseconds - t > 0
124
+
125
+ super
126
+
127
+ if @menu
128
+ Gosu.flush
129
+ @menu.draw
130
+ end
131
+
132
+ if @tip && @tip.value.length.positive?
133
+ Gosu.flush
134
+
135
+ @tip.draw
136
+ end
137
+
138
+ if CyberarmEngine.const_defined?("GUI_DEBUG")
139
+ Gosu.flush
140
+
141
+ @root_container.debug_draw
142
+ end
143
+
144
+ @needs_repaint = false
145
+
146
+ @page&.draw
147
+ end
148
+
149
+ def needs_repaint?
150
+ @needs_repaint
151
+ end
152
+
153
+ def update
154
+ if @pending_focus_request
155
+ @pending_focus_request = false
156
+
157
+ self.focus = @pending_focus_element
158
+ @pending_focus_element.publish(:focus)
159
+ end
160
+
161
+ @menu&.update
162
+
163
+ super
164
+
165
+ if @active_width != window.width || @active_height != window.height
166
+ request_recalculate
167
+ @root_container.publish(:window_size_changed)
168
+ end
169
+
170
+ @active_width = window.width
171
+ @active_height = window.height
172
+
173
+ return unless window.has_focus?
174
+ return unless window.current_state == self
175
+
176
+ # list of containers decending down to hit element
177
+ new_hit_elements = (@menu || @root_container).hit_element?(window.mouse_x, window.mouse_y)
178
+ # element the mouse is over, if any.
179
+ new_mouse_over = new_hit_elements&.find { |c| c.is_a?(CyberarmEngine::Element::Widget) } || new_hit_elements&.last
180
+
181
+ # is the currently hit element the same as last hit element?
182
+ same_element = @mouse_over && new_mouse_over == @mouse_over
183
+
184
+ unless @hit_elements == new_hit_elements
185
+ added_hit_elements = (new_hit_elements || []) - @hit_elements
186
+ removed_hit_elements = @hit_elements - (new_hit_elements || [])
187
+
188
+ added_hit_elements.each do |e|
189
+ e.publish(:enter)
190
+ e.publish(:hover)
191
+ end
192
+
193
+ removed_hit_elements.each do |e|
194
+ e.publish(:leave)
195
+ end
196
+ end
197
+
198
+ @mouse_over = new_mouse_over
199
+ @hit_elements = new_hit_elements || []
200
+
201
+ redirect_holding_mouse_button(:left) if @mouse_over && Gosu.button_down?(Gosu::MS_LEFT)
202
+ redirect_holding_mouse_button(:middle) if @mouse_over && Gosu.button_down?(Gosu::MS_MIDDLE)
203
+ redirect_holding_mouse_button(:right) if @mouse_over && Gosu.button_down?(Gosu::MS_RIGHT)
204
+
205
+ # handle tooltip
206
+ if same_element
207
+ if @mouse_over && (Gosu.milliseconds - @mouse_moved_at) > tool_tip_delay
208
+ @tip.value = @mouse_over.tip if @mouse_over
209
+ @tip.x = window.mouse_x
210
+ @tip.x = 0 if @tip.x < 0
211
+ @tip.x = window.width - @tip.width if @tip.x + @tip.width > window.width
212
+ @tip.y = window.mouse_y - (@tip.height + 5)
213
+ @tip.y = 0 if @tip.y < 0
214
+ @tip.y = window.height - @tip.height if @tip.y + @tip.height > window.height
215
+ @tip.update
216
+ @tip.recalculate
217
+ else
218
+ @tip.value = ""
219
+ end
220
+ else
221
+ @mouse_moved_at = Gosu.milliseconds
222
+ end
223
+
224
+ @last_mouse_pos = Vector.new(window.mouse_x, window.mouse_y)
225
+ @mouse_pos = @last_mouse_pos.clone
226
+
227
+ @page&.update
228
+ end
229
+
230
+ def button_down(id)
231
+ super
232
+
233
+ case id
234
+ when Gosu::MS_LEFT
235
+ redirect_mouse_button(:left)
236
+ when Gosu::MS_MIDDLE
237
+ redirect_mouse_button(:middle)
238
+ when Gosu::MS_RIGHT
239
+ redirect_mouse_button(:right)
240
+ when Gosu::KB_F5
241
+ request_recalculate
242
+ when Gosu::KB_F12
243
+ CyberarmEngine.const_set(:GUI_DEBUG, !CyberarmEngine::GUI_DEBUG)
244
+ request_repaint
245
+ end
246
+
247
+ @focus.button_down(id) if @focus.respond_to?(:button_down)
248
+
249
+ @page&.button_down(id)
250
+ end
251
+
252
+ def button_up(id)
253
+ super
254
+
255
+ case id
256
+ when Gosu::MS_LEFT
257
+ redirect_released_mouse_button(:left)
258
+ when Gosu::MS_MIDDLE
259
+ redirect_released_mouse_button(:middle)
260
+ when Gosu::MS_RIGHT
261
+ redirect_released_mouse_button(:right)
262
+ when Gosu::MS_WHEEL_UP
263
+ redirect_mouse_wheel(:up)
264
+ when Gosu::MS_WHEEL_DOWN
265
+ redirect_mouse_wheel(:down)
266
+ when Gosu::KB_HOME
267
+ redirect_scroll_jump_to(:top)
268
+ when Gosu::KB_END
269
+ redirect_scroll_jump_to(:end)
270
+ when Gosu::KB_PAGE_UP
271
+ redirect_scroll_page(:up)
272
+ when Gosu::KB_PAGE_DOWN
273
+ redirect_scroll_page(:down)
274
+ end
275
+
276
+ @focus.button_up(id) if @focus.respond_to?(:button_up)
277
+
278
+ # Prevents menu from popping back up if the listbox is clicked to hide it.
279
+ @hid_menu_for = nil
280
+
281
+ @page&.button_up(id)
282
+ end
283
+
284
+ def tool_tip_delay
285
+ @tip.style.delay || 250 # ms
286
+ end
287
+
288
+ def redirect_mouse_button(button)
289
+ hide_menu unless @menu && (@menu == @mouse_over) || (@mouse_over&.parent == @menu)
290
+
291
+ if @focus && @mouse_over != @focus
292
+ @hit_elements.delete(@focus)
293
+ @focus.publish(:blur)
294
+ @focus = nil
295
+ end
296
+
297
+ if @mouse_over && @hid_menu_for != @mouse_over
298
+ @mouse_down_position[button] = Vector.new(window.mouse_x, window.mouse_y)
299
+ @mouse_down_on[button] = @mouse_over
300
+
301
+ @mouse_over.publish(:"#{button}_mouse_button", window.mouse_x, window.mouse_y)
302
+ else
303
+ @mouse_down_position[button] = nil
304
+ @mouse_down_on[button] = nil
305
+ end
306
+ end
307
+
308
+ def redirect_released_mouse_button(button)
309
+ hide_menu if @menu && (@menu == @mouse_over) || (@mouse_over&.parent == @menu)
310
+
311
+ if @mouse_over && @hid_menu_for != @mouse_over
312
+ @mouse_over.publish(:"released_#{button}_mouse_button", window.mouse_x, window.mouse_y)
313
+ if @mouse_over == @mouse_down_on[button]
314
+ @mouse_over.publish(:"clicked_#{button}_mouse_button", window.mouse_x,
315
+ window.mouse_y)
316
+ end
317
+ end
318
+
319
+ if @dragging_element
320
+ @dragging_element.publish(:end_drag, window.mouse_x, window.mouse_y, button)
321
+ @dragging_element = nil
322
+ end
323
+
324
+ @mouse_down_position[button] = nil
325
+ @mouse_down_on[button] = nil
326
+ end
327
+
328
+ def redirect_holding_mouse_button(button)
329
+ if !@dragging_element && @mouse_down_on[button] && @mouse_down_on[button].draggable?(button) && @mouse_pos.distance(@mouse_down_position[button]) > @min_drag_distance
330
+ @dragging_element = @mouse_down_on[button]
331
+ @dragging_element.publish(:begin_drag, window.mouse_x, window.mouse_y, button)
332
+ end
333
+
334
+ if @dragging_element
335
+ @dragging_element.publish(:drag_update, window.mouse_x, window.mouse_y, button) if @dragging_element
336
+ elsif @mouse_over
337
+ @mouse_over.publish(:"holding_#{button}_mouse_button", window.mouse_x, window.mouse_y)
338
+ end
339
+ end
340
+
341
+ def redirect_mouse_wheel(button)
342
+ @mouse_over.publish(:"mouse_wheel_#{button}", window.mouse_x, window.mouse_y) if (@mouse_over && !@menu) || (@mouse_over && @mouse_over == @menu)
343
+ end
344
+
345
+ def redirect_scroll_jump_to(edge)
346
+ return if window.text_input
347
+
348
+ @mouse_over.publish(:"scroll_jump_to_#{edge}", window.mouse_x, window.mouse_y) if (@mouse_over && !@menu) || (@mouse_over && @mouse_over == @menu)
349
+ end
350
+
351
+ def redirect_scroll_page(edge)
352
+ return if window.text_input
353
+
354
+ @mouse_over.publish(:"scroll_page_#{edge}", window.mouse_x, window.mouse_y) if (@mouse_over && !@menu) || (@mouse_over && @mouse_over == @menu)
355
+ end
356
+
357
+ # Schedule a full GUI recalculation on next update
358
+ def request_recalculate
359
+ @pending_recalculate_request = true
360
+ end
361
+
362
+ def request_recalculate_for(element)
363
+ # element is already queued
364
+ return if @pending_element_recalculate_requests.detect { |e| e == element }
365
+
366
+ @pending_element_recalculate_requests << element
367
+ end
368
+
369
+ def request_focus(element)
370
+ @pending_focus_request = true
371
+ @pending_focus_element = element
372
+ end
373
+
374
+ def request_repaint
375
+ # puts caller[0..4]
376
+ # puts
377
+ @needs_repaint = true
378
+ end
379
+
380
+ def show_menu(list_box)
381
+ @menu = list_box
382
+ end
383
+
384
+ def hide_menu
385
+ return unless @menu
386
+
387
+ request_repaint
388
+
389
+ @hid_menu_for = @menu.parent
390
+ @menu = nil
391
+ end
392
+
393
+ def to_s
394
+ # "#{self.class} children=#{@children.map { |c| c.to_s }}"
395
+ @root_container.to_s
396
+ end
397
+
398
+ def inspect
399
+ to_s
400
+ end
401
+ end
402
+ end