bevy 1.0.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.
- checksums.yaml +7 -0
- data/Cargo.lock +4279 -0
- data/Cargo.toml +36 -0
- data/README.md +226 -0
- data/crates/bevy/Cargo.toml +52 -0
- data/crates/bevy/src/app.rs +43 -0
- data/crates/bevy/src/component.rs +111 -0
- data/crates/bevy/src/entity.rs +30 -0
- data/crates/bevy/src/error.rs +32 -0
- data/crates/bevy/src/event.rs +190 -0
- data/crates/bevy/src/input_bridge.rs +300 -0
- data/crates/bevy/src/lib.rs +42 -0
- data/crates/bevy/src/mesh_renderer.rs +328 -0
- data/crates/bevy/src/query.rs +53 -0
- data/crates/bevy/src/render_app.rs +689 -0
- data/crates/bevy/src/resource.rs +28 -0
- data/crates/bevy/src/schedule.rs +186 -0
- data/crates/bevy/src/sprite_renderer.rs +355 -0
- data/crates/bevy/src/system.rs +44 -0
- data/crates/bevy/src/text_renderer.rs +258 -0
- data/crates/bevy/src/types/color.rs +114 -0
- data/crates/bevy/src/types/dynamic.rs +131 -0
- data/crates/bevy/src/types/math.rs +260 -0
- data/crates/bevy/src/types/mod.rs +9 -0
- data/crates/bevy/src/types/transform.rs +166 -0
- data/crates/bevy/src/world.rs +163 -0
- data/crates/bevy_ruby_render/Cargo.toml +22 -0
- data/crates/bevy_ruby_render/src/asset.rs +360 -0
- data/crates/bevy_ruby_render/src/audio.rs +511 -0
- data/crates/bevy_ruby_render/src/camera.rs +365 -0
- data/crates/bevy_ruby_render/src/gamepad.rs +398 -0
- data/crates/bevy_ruby_render/src/lib.rs +26 -0
- data/crates/bevy_ruby_render/src/material.rs +310 -0
- data/crates/bevy_ruby_render/src/mesh.rs +491 -0
- data/crates/bevy_ruby_render/src/sprite.rs +289 -0
- data/ext/bevy/Cargo.toml +20 -0
- data/ext/bevy/extconf.rb +6 -0
- data/ext/bevy/src/conversions.rs +137 -0
- data/ext/bevy/src/lib.rs +29 -0
- data/ext/bevy/src/ruby_app.rs +65 -0
- data/ext/bevy/src/ruby_color.rs +149 -0
- data/ext/bevy/src/ruby_component.rs +189 -0
- data/ext/bevy/src/ruby_entity.rs +33 -0
- data/ext/bevy/src/ruby_math.rs +384 -0
- data/ext/bevy/src/ruby_query.rs +64 -0
- data/ext/bevy/src/ruby_render_app.rs +779 -0
- data/ext/bevy/src/ruby_system.rs +122 -0
- data/ext/bevy/src/ruby_world.rs +107 -0
- data/lib/bevy/animation.rb +597 -0
- data/lib/bevy/app.rb +675 -0
- data/lib/bevy/asset.rb +613 -0
- data/lib/bevy/audio.rb +545 -0
- data/lib/bevy/audio_effects.rb +224 -0
- data/lib/bevy/camera.rb +412 -0
- data/lib/bevy/component.rb +91 -0
- data/lib/bevy/diagnostics.rb +227 -0
- data/lib/bevy/ecs_advanced.rb +296 -0
- data/lib/bevy/event.rb +199 -0
- data/lib/bevy/gizmos.rb +158 -0
- data/lib/bevy/gltf.rb +227 -0
- data/lib/bevy/hierarchy.rb +444 -0
- data/lib/bevy/input.rb +514 -0
- data/lib/bevy/lighting.rb +369 -0
- data/lib/bevy/material.rb +248 -0
- data/lib/bevy/mesh.rb +257 -0
- data/lib/bevy/navigation.rb +344 -0
- data/lib/bevy/networking.rb +335 -0
- data/lib/bevy/particle.rb +337 -0
- data/lib/bevy/physics.rb +396 -0
- data/lib/bevy/plugins/default_plugins.rb +34 -0
- data/lib/bevy/plugins/input_plugin.rb +49 -0
- data/lib/bevy/reflect.rb +361 -0
- data/lib/bevy/render_graph.rb +210 -0
- data/lib/bevy/resource.rb +185 -0
- data/lib/bevy/scene.rb +254 -0
- data/lib/bevy/shader.rb +319 -0
- data/lib/bevy/shape.rb +195 -0
- data/lib/bevy/skeletal.rb +248 -0
- data/lib/bevy/sprite.rb +152 -0
- data/lib/bevy/sprite_sheet.rb +444 -0
- data/lib/bevy/state.rb +277 -0
- data/lib/bevy/system.rb +206 -0
- data/lib/bevy/text.rb +99 -0
- data/lib/bevy/text_advanced.rb +455 -0
- data/lib/bevy/timer.rb +147 -0
- data/lib/bevy/transform.rb +158 -0
- data/lib/bevy/ui.rb +454 -0
- data/lib/bevy/ui_advanced.rb +568 -0
- data/lib/bevy/version.rb +5 -0
- data/lib/bevy/visibility.rb +250 -0
- data/lib/bevy/window.rb +302 -0
- data/lib/bevy.rb +390 -0
- metadata +150 -0
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bevy
|
|
4
|
+
class ScrollView
|
|
5
|
+
attr_accessor :offset, :content_size, :viewport_size
|
|
6
|
+
attr_accessor :scroll_x, :scroll_y, :overscroll_behavior
|
|
7
|
+
|
|
8
|
+
OVERSCROLL_NONE = :none
|
|
9
|
+
OVERSCROLL_CONTAIN = :contain
|
|
10
|
+
OVERSCROLL_AUTO = :auto
|
|
11
|
+
|
|
12
|
+
def initialize(
|
|
13
|
+
scroll_x: true,
|
|
14
|
+
scroll_y: true,
|
|
15
|
+
overscroll_behavior: OVERSCROLL_CONTAIN
|
|
16
|
+
)
|
|
17
|
+
@offset = Vec2.zero
|
|
18
|
+
@content_size = Vec2.zero
|
|
19
|
+
@viewport_size = Vec2.zero
|
|
20
|
+
@scroll_x = scroll_x
|
|
21
|
+
@scroll_y = scroll_y
|
|
22
|
+
@overscroll_behavior = overscroll_behavior
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def scroll_by(delta)
|
|
26
|
+
new_offset_x = @scroll_x ? @offset.x + delta.x : @offset.x
|
|
27
|
+
new_offset_y = @scroll_y ? @offset.y + delta.y : @offset.y
|
|
28
|
+
|
|
29
|
+
if @overscroll_behavior == OVERSCROLL_CONTAIN
|
|
30
|
+
max_x = [@content_size.x - @viewport_size.x, 0.0].max
|
|
31
|
+
max_y = [@content_size.y - @viewport_size.y, 0.0].max
|
|
32
|
+
new_offset_x = [[new_offset_x, 0.0].max, max_x].min
|
|
33
|
+
new_offset_y = [[new_offset_y, 0.0].max, max_y].min
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
@offset = Vec2.new(new_offset_x, new_offset_y)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def scroll_to(position)
|
|
40
|
+
@offset = position
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def scroll_to_top
|
|
44
|
+
@offset = Vec2.new(@offset.x, 0.0)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def scroll_to_bottom
|
|
48
|
+
max_y = [@content_size.y - @viewport_size.y, 0.0].max
|
|
49
|
+
@offset = Vec2.new(@offset.x, max_y)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def scroll_percentage
|
|
53
|
+
return Vec2.zero if @content_size.x <= @viewport_size.x && @content_size.y <= @viewport_size.y
|
|
54
|
+
|
|
55
|
+
max_x = [@content_size.x - @viewport_size.x, 1.0].max
|
|
56
|
+
max_y = [@content_size.y - @viewport_size.y, 1.0].max
|
|
57
|
+
Vec2.new(@offset.x / max_x, @offset.y / max_y)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def type_name
|
|
61
|
+
'ScrollView'
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class TextInput
|
|
66
|
+
attr_accessor :value, :placeholder, :cursor_position, :selection_start, :selection_end
|
|
67
|
+
attr_accessor :max_length, :multiline, :password, :readonly, :focused
|
|
68
|
+
|
|
69
|
+
def initialize(
|
|
70
|
+
value: '',
|
|
71
|
+
placeholder: '',
|
|
72
|
+
max_length: nil,
|
|
73
|
+
multiline: false,
|
|
74
|
+
password: false,
|
|
75
|
+
readonly: false
|
|
76
|
+
)
|
|
77
|
+
@value = value
|
|
78
|
+
@placeholder = placeholder
|
|
79
|
+
@cursor_position = value.length
|
|
80
|
+
@selection_start = nil
|
|
81
|
+
@selection_end = nil
|
|
82
|
+
@max_length = max_length
|
|
83
|
+
@multiline = multiline
|
|
84
|
+
@password = password
|
|
85
|
+
@readonly = readonly
|
|
86
|
+
@focused = false
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def insert(text)
|
|
90
|
+
return if @readonly
|
|
91
|
+
return if @max_length && @value.length + text.length > @max_length
|
|
92
|
+
|
|
93
|
+
delete_selection if has_selection?
|
|
94
|
+
|
|
95
|
+
@value = @value[0...@cursor_position] + text + @value[@cursor_position..-1]
|
|
96
|
+
@cursor_position += text.length
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def delete_backward
|
|
100
|
+
return if @readonly
|
|
101
|
+
return if @cursor_position == 0 && !has_selection?
|
|
102
|
+
|
|
103
|
+
if has_selection?
|
|
104
|
+
delete_selection
|
|
105
|
+
else
|
|
106
|
+
@value = @value[0...(@cursor_position - 1)] + @value[@cursor_position..-1]
|
|
107
|
+
@cursor_position -= 1
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def delete_forward
|
|
112
|
+
return if @readonly
|
|
113
|
+
return if @cursor_position >= @value.length && !has_selection?
|
|
114
|
+
|
|
115
|
+
if has_selection?
|
|
116
|
+
delete_selection
|
|
117
|
+
else
|
|
118
|
+
@value = @value[0...@cursor_position] + @value[(@cursor_position + 1)..-1]
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def move_cursor(direction)
|
|
123
|
+
case direction
|
|
124
|
+
when :left
|
|
125
|
+
@cursor_position = [@cursor_position - 1, 0].max
|
|
126
|
+
when :right
|
|
127
|
+
@cursor_position = [@cursor_position + 1, @value.length].min
|
|
128
|
+
when :home
|
|
129
|
+
@cursor_position = 0
|
|
130
|
+
when :end
|
|
131
|
+
@cursor_position = @value.length
|
|
132
|
+
end
|
|
133
|
+
clear_selection
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def select_all
|
|
137
|
+
@selection_start = 0
|
|
138
|
+
@selection_end = @value.length
|
|
139
|
+
@cursor_position = @value.length
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def clear_selection
|
|
143
|
+
@selection_start = nil
|
|
144
|
+
@selection_end = nil
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def has_selection?
|
|
148
|
+
@selection_start && @selection_end && @selection_start != @selection_end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def selected_text
|
|
152
|
+
return '' unless has_selection?
|
|
153
|
+
|
|
154
|
+
start_pos = [@selection_start, @selection_end].min
|
|
155
|
+
end_pos = [@selection_start, @selection_end].max
|
|
156
|
+
@value[start_pos...end_pos]
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def display_value
|
|
160
|
+
@password ? '*' * @value.length : @value
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def type_name
|
|
164
|
+
'TextInput'
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
private
|
|
168
|
+
|
|
169
|
+
def delete_selection
|
|
170
|
+
return unless has_selection?
|
|
171
|
+
|
|
172
|
+
start_pos = [@selection_start, @selection_end].min
|
|
173
|
+
end_pos = [@selection_start, @selection_end].max
|
|
174
|
+
@value = @value[0...start_pos] + @value[end_pos..-1]
|
|
175
|
+
@cursor_position = start_pos
|
|
176
|
+
clear_selection
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
class FocusState
|
|
181
|
+
attr_reader :focused_entity, :focus_order
|
|
182
|
+
|
|
183
|
+
def initialize
|
|
184
|
+
@focused_entity = nil
|
|
185
|
+
@focus_order = []
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def focus(entity)
|
|
189
|
+
@focused_entity = entity
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def blur
|
|
193
|
+
@focused_entity = nil
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def focused?(entity)
|
|
197
|
+
@focused_entity == entity
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def register(entity, order: nil)
|
|
201
|
+
if order
|
|
202
|
+
@focus_order.insert(order, entity)
|
|
203
|
+
else
|
|
204
|
+
@focus_order << entity
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def unregister(entity)
|
|
209
|
+
@focus_order.delete(entity)
|
|
210
|
+
blur if @focused_entity == entity
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def focus_next
|
|
214
|
+
return if @focus_order.empty?
|
|
215
|
+
|
|
216
|
+
current_index = @focused_entity ? @focus_order.index(@focused_entity) : -1
|
|
217
|
+
next_index = (current_index + 1) % @focus_order.length
|
|
218
|
+
@focused_entity = @focus_order[next_index]
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def focus_previous
|
|
222
|
+
return if @focus_order.empty?
|
|
223
|
+
|
|
224
|
+
current_index = @focused_entity ? @focus_order.index(@focused_entity) : @focus_order.length
|
|
225
|
+
prev_index = (current_index - 1) % @focus_order.length
|
|
226
|
+
@focused_entity = @focus_order[prev_index]
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def type_name
|
|
230
|
+
'FocusState'
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
class Slider
|
|
235
|
+
attr_accessor :value, :min, :max, :step, :orientation, :disabled
|
|
236
|
+
|
|
237
|
+
HORIZONTAL = :horizontal
|
|
238
|
+
VERTICAL = :vertical
|
|
239
|
+
|
|
240
|
+
def initialize(
|
|
241
|
+
value: 0.0,
|
|
242
|
+
min: 0.0,
|
|
243
|
+
max: 1.0,
|
|
244
|
+
step: nil,
|
|
245
|
+
orientation: HORIZONTAL,
|
|
246
|
+
disabled: false
|
|
247
|
+
)
|
|
248
|
+
@min = min.to_f
|
|
249
|
+
@max = max.to_f
|
|
250
|
+
@step = step
|
|
251
|
+
@orientation = orientation
|
|
252
|
+
@disabled = disabled
|
|
253
|
+
self.value = value
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def value=(val)
|
|
257
|
+
clamped = [[val.to_f, @min].max, @max].min
|
|
258
|
+
@value = @step ? (clamped / @step).round * @step : clamped
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def normalized_value
|
|
262
|
+
return 0.0 if @max == @min
|
|
263
|
+
|
|
264
|
+
(@value - @min) / (@max - @min)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def set_from_normalized(normalized)
|
|
268
|
+
self.value = @min + normalized * (@max - @min)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def increment
|
|
272
|
+
return if @disabled
|
|
273
|
+
|
|
274
|
+
self.value = @value + (@step || (@max - @min) / 10.0)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def decrement
|
|
278
|
+
return if @disabled
|
|
279
|
+
|
|
280
|
+
self.value = @value - (@step || (@max - @min) / 10.0)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def type_name
|
|
284
|
+
'Slider'
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
class Checkbox
|
|
289
|
+
attr_accessor :checked, :disabled, :label
|
|
290
|
+
|
|
291
|
+
def initialize(checked: false, disabled: false, label: nil)
|
|
292
|
+
@checked = checked
|
|
293
|
+
@disabled = disabled
|
|
294
|
+
@label = label
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def toggle
|
|
298
|
+
return if @disabled
|
|
299
|
+
|
|
300
|
+
@checked = !@checked
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def type_name
|
|
304
|
+
'Checkbox'
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
class RadioGroup
|
|
309
|
+
attr_reader :options, :selected_index, :name
|
|
310
|
+
|
|
311
|
+
def initialize(name:, options: [])
|
|
312
|
+
@name = name
|
|
313
|
+
@options = options
|
|
314
|
+
@selected_index = nil
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def add_option(option)
|
|
318
|
+
@options << option
|
|
319
|
+
self
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def select(index)
|
|
323
|
+
return if index < 0 || index >= @options.length
|
|
324
|
+
|
|
325
|
+
@selected_index = index
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def select_value(value)
|
|
329
|
+
index = @options.index(value)
|
|
330
|
+
select(index) if index
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def selected_value
|
|
334
|
+
@selected_index ? @options[@selected_index] : nil
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def selected?(index)
|
|
338
|
+
@selected_index == index
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def type_name
|
|
342
|
+
'RadioGroup'
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
class Dropdown
|
|
347
|
+
attr_accessor :options, :selected_index, :open, :placeholder, :disabled
|
|
348
|
+
|
|
349
|
+
def initialize(
|
|
350
|
+
options: [],
|
|
351
|
+
selected_index: nil,
|
|
352
|
+
placeholder: 'Select...',
|
|
353
|
+
disabled: false
|
|
354
|
+
)
|
|
355
|
+
@options = options
|
|
356
|
+
@selected_index = selected_index
|
|
357
|
+
@open = false
|
|
358
|
+
@placeholder = placeholder
|
|
359
|
+
@disabled = disabled
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def toggle
|
|
363
|
+
return if @disabled
|
|
364
|
+
|
|
365
|
+
@open = !@open
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def close
|
|
369
|
+
@open = false
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def select(index)
|
|
373
|
+
return if index < 0 || index >= @options.length
|
|
374
|
+
|
|
375
|
+
@selected_index = index
|
|
376
|
+
@open = false
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def selected_value
|
|
380
|
+
@selected_index ? @options[@selected_index] : nil
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def display_text
|
|
384
|
+
selected_value || @placeholder
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def type_name
|
|
388
|
+
'Dropdown'
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
class TabContainer
|
|
393
|
+
attr_reader :tabs, :active_index
|
|
394
|
+
|
|
395
|
+
def initialize
|
|
396
|
+
@tabs = []
|
|
397
|
+
@active_index = 0
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def add_tab(title, content = nil)
|
|
401
|
+
@tabs << { title: title, content: content }
|
|
402
|
+
self
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def remove_tab(index)
|
|
406
|
+
return if index < 0 || index >= @tabs.length
|
|
407
|
+
|
|
408
|
+
@tabs.delete_at(index)
|
|
409
|
+
@active_index = [@active_index, @tabs.length - 1].min if @active_index >= @tabs.length
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
def select_tab(index)
|
|
413
|
+
return if index < 0 || index >= @tabs.length
|
|
414
|
+
|
|
415
|
+
@active_index = index
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def active_tab
|
|
419
|
+
@tabs[@active_index]
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def tab_count
|
|
423
|
+
@tabs.length
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def type_name
|
|
427
|
+
'TabContainer'
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
class ProgressBar
|
|
432
|
+
attr_accessor :value, :min, :max, :show_label
|
|
433
|
+
|
|
434
|
+
def initialize(value: 0.0, min: 0.0, max: 1.0, show_label: false)
|
|
435
|
+
@min = min.to_f
|
|
436
|
+
@max = max.to_f
|
|
437
|
+
@value = [[value.to_f, @min].max, @max].min
|
|
438
|
+
@show_label = show_label
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
def percentage
|
|
442
|
+
return 0.0 if @max == @min
|
|
443
|
+
|
|
444
|
+
((@value - @min) / (@max - @min)) * 100.0
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
def normalized
|
|
448
|
+
return 0.0 if @max == @min
|
|
449
|
+
|
|
450
|
+
(@value - @min) / (@max - @min)
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
def complete?
|
|
454
|
+
@value >= @max
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def type_name
|
|
458
|
+
'ProgressBar'
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
class Tooltip
|
|
463
|
+
attr_accessor :text, :position, :visible, :delay
|
|
464
|
+
|
|
465
|
+
def initialize(text:, delay: 0.5)
|
|
466
|
+
@text = text
|
|
467
|
+
@position = Vec2.zero
|
|
468
|
+
@visible = false
|
|
469
|
+
@delay = delay.to_f
|
|
470
|
+
@hover_timer = 0.0
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def update(delta, hovering)
|
|
474
|
+
if hovering
|
|
475
|
+
@hover_timer += delta
|
|
476
|
+
@visible = @hover_timer >= @delay
|
|
477
|
+
else
|
|
478
|
+
@hover_timer = 0.0
|
|
479
|
+
@visible = false
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
def show_at(position)
|
|
484
|
+
@position = position
|
|
485
|
+
@visible = true
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def hide
|
|
489
|
+
@visible = false
|
|
490
|
+
@hover_timer = 0.0
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
def type_name
|
|
494
|
+
'Tooltip'
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
class Modal
|
|
499
|
+
attr_accessor :visible, :title, :content, :closable
|
|
500
|
+
|
|
501
|
+
def initialize(title: nil, content: nil, closable: true)
|
|
502
|
+
@visible = false
|
|
503
|
+
@title = title
|
|
504
|
+
@content = content
|
|
505
|
+
@closable = closable
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
def open
|
|
509
|
+
@visible = true
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
def close
|
|
513
|
+
@visible = false if @closable
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
def toggle
|
|
517
|
+
@visible = !@visible
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
def type_name
|
|
521
|
+
'Modal'
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
class ContextMenu
|
|
526
|
+
attr_reader :items, :position
|
|
527
|
+
attr_accessor :visible
|
|
528
|
+
|
|
529
|
+
def initialize
|
|
530
|
+
@items = []
|
|
531
|
+
@position = Vec2.zero
|
|
532
|
+
@visible = false
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
def add_item(label, action = nil)
|
|
536
|
+
@items << { label: label, action: action, enabled: true }
|
|
537
|
+
self
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
def add_separator
|
|
541
|
+
@items << { separator: true }
|
|
542
|
+
self
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
def show_at(position)
|
|
546
|
+
@position = position
|
|
547
|
+
@visible = true
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
def hide
|
|
551
|
+
@visible = false
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
def select(index)
|
|
555
|
+
return if index < 0 || index >= @items.length
|
|
556
|
+
|
|
557
|
+
item = @items[index]
|
|
558
|
+
return if item[:separator] || !item[:enabled]
|
|
559
|
+
|
|
560
|
+
item[:action]&.call
|
|
561
|
+
hide
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
def type_name
|
|
565
|
+
'ContextMenu'
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
end
|