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,222 +1,236 @@
1
- module CyberarmEngine
2
- class Element
3
- class TextBlock < Element
4
- def initialize(text, options = {}, block = nil)
5
- super(options, block)
6
-
7
- @text = Text.new(
8
- text, font: @options[:font], z: @z, color: @options[:color],
9
- size: @options[:text_size], shadow: @options[:text_shadow],
10
- static: @options[:text_static],
11
- shadow_size: @options[:text_shadow_size],
12
- shadow_color: @options[:text_shadow_color],
13
- border: @options[:text_border],
14
- border_size: @options[:text_border_size],
15
- border_color: @options[:text_border_color]
16
- )
17
-
18
- @raw_text = text
19
- @text_width = @text.width
20
- @text_height = @text.height
21
- end
22
-
23
- def update
24
- super
25
-
26
- if @text.textobject.name != safe_style_fetch(:font)
27
- set_font
28
- root.gui_state.request_recalculate
29
- end
30
- end
31
-
32
- def render
33
- # Gosu.clip_to is too expensive to always use so check if we actually need it.
34
- if @text_width > width || @text_height > height
35
- Gosu.clip_to(@x, @y, width, height) do
36
- @text.draw
37
- end
38
- else
39
- @text.draw
40
- end
41
- end
42
-
43
- def layout
44
- unless @enabled
45
- @text.color = @style.disabled[:color]
46
- else
47
- @text.color = @style.color
48
- end
49
-
50
- @width = 0
51
- @height = 0
52
-
53
- _width = dimensional_size(@style.width, :width)
54
- _height = dimensional_size(@style.height, :height)
55
-
56
- handle_text_wrapping(_width)
57
-
58
- # Update cached text width and height
59
- @text_width = @text.width
60
- @text_height = @text.height
61
-
62
- @width = _width || @text_width.floor
63
- @height = _height || @text_height.floor
64
-
65
- @text.y = @style.border_thickness_top + @style.padding_top + @y
66
- @text.z = @z + 3
67
-
68
- if (text_alignment = @options[:text_align] || @options[:text_h_align])
69
- case text_alignment
70
- when :left
71
- @text.x = @style.border_thickness_left + @style.padding_left + @x
72
- when :center
73
- @text.x = if @text_width <= width
74
- @x + width / 2 - @text_width / 2
75
- else # Act as left aligned
76
- @style.border_thickness_left + @style.padding_left + @x
77
- end
78
- when :right
79
- @text.x = @x + outer_width - (@text_width + @style.border_thickness_right + @style.padding_right)
80
- end
81
- end
82
-
83
- if (vertical_alignment = @options[:text_v_align])
84
- case vertical_alignment
85
- when :center
86
- @text.y = if @text_height <= height
87
- @y + height / 2 - @text_height / 2
88
- else
89
- @style.border_thickness_top + @style.padding_top + @y
90
- end
91
- when :bottom
92
- @text.y = @y + outer_height - (@text_height + @style.border_thickness_bottom + @style.padding_bottom)
93
- end
94
- end
95
-
96
- update_background
97
- end
98
-
99
- def handle_text_wrapping(max_width)
100
- max_width ||= @parent&.content_width
101
- max_width ||= @x - (window.width + noncontent_width)
102
- wrap_behavior = style.text_wrap
103
- copy = @raw_text.to_s.dup
104
-
105
- # Only perform text wrapping: if it is enabled, is possible to wrap, and text is too long to fit on one line
106
- if wrap_behavior != :none && line_width(copy[0]) <= max_width && line_width(copy) > max_width
107
- breaks = [] # list of indexes to insert a line break
108
- line_start = 0
109
- line_end = copy.length
110
-
111
- stalled = false
112
- stalled_interations = 0
113
- max_stalled_iterations = 10
114
- checked_copy_length = line_width(copy[line_start..line_end])
115
-
116
- # find length of lines
117
- while line_width(copy[line_start..line_end]) > max_width && stalled_interations < max_stalled_iterations
118
- search_start = line_start
119
- search_end = line_end
120
-
121
- # Perform a binary search to find length of line
122
- while search_start < search_end
123
- midpoint = ((search_start.to_f + search_end) / 2.0).floor
124
-
125
- if line_width(copy[line_start..midpoint]) > max_width
126
- search_end = midpoint
127
- else
128
- search_start = midpoint + 1
129
- end
130
- end
131
-
132
- if wrap_behavior == :word_wrap
133
- word_search_end = search_end
134
- failed = false
135
-
136
- until(copy[word_search_end].to_s.match(/[[:punct:]]| /))
137
- word_search_end -= 1
138
-
139
- if word_search_end <= 1 || word_search_end < line_start
140
- failed = true
141
- break
142
- end
143
- end
144
-
145
- line_start = failed ? search_end : word_search_end + 1 # walk in front of punctuation
146
- else
147
- line_start = search_end
148
- end
149
-
150
- breaks << line_start
151
-
152
- # Prevent locking up due to outer while loop text width < max_width check not being satisfied.
153
- stalled = checked_copy_length == line_width(copy[line_start..line_end])
154
- checked_copy_length = line_width(copy[line_start..line_end])
155
-
156
- stalled_interations += 1 if stalled
157
- stalled_interations = 0 unless stalled
158
- end
159
-
160
- breaks.each_with_index do |pos, index|
161
- copy.insert(pos + index, "\n") if pos + index >= 0 && pos + index < copy.length
162
- end
163
- end
164
-
165
- @text.text = copy
166
- end
167
-
168
- def line_width(text)
169
- (@text.textobject.markup_width(text.to_s) + noncontent_width)
170
- end
171
-
172
- def value
173
- @raw_text
174
- end
175
-
176
- def value=(value)
177
- old_value = @raw_text
178
- @raw_text = value.to_s.chomp
179
-
180
- old_width = width
181
- old_height = height
182
-
183
- if old_width != width || old_height != height
184
- root.gui_state.request_recalculate
185
- else
186
- recalculate
187
- end
188
-
189
- root.gui_state.request_repaint if old_value != @raw_text
190
-
191
- publish(:changed, self.value)
192
- end
193
- end
194
-
195
- class Banner < TextBlock
196
- end
197
-
198
- class Title < TextBlock
199
- end
200
-
201
- class Subtitle < TextBlock
202
- end
203
-
204
- class Tagline < TextBlock
205
- end
206
-
207
- class Caption < TextBlock
208
- end
209
-
210
- class Para < TextBlock
211
- end
212
-
213
- class Inscription < TextBlock
214
- end
215
-
216
- class ToolTip < TextBlock
217
- end
218
-
219
- class Link < TextBlock
220
- end
221
- end
222
- end
1
+ module CyberarmEngine
2
+ class Element
3
+ class TextBlock < Element
4
+ def initialize(text, options = {}, block = nil)
5
+ super(options, block)
6
+
7
+ @text = Text.new(
8
+ text, font: @options[:font], z: @z, color: @options[:color],
9
+ size: @options[:text_size], shadow: @options[:text_shadow],
10
+ static: @options[:text_static],
11
+ alignment: @options[:text_align],
12
+ shadow_size: @options[:text_shadow_size],
13
+ shadow_color: @options[:text_shadow_color],
14
+ border: @options[:text_border],
15
+ border_size: @options[:text_border_size],
16
+ border_color: @options[:text_border_color]
17
+ )
18
+
19
+ @raw_text = text
20
+ @text_width = @text.width
21
+ @text_height = @text.height
22
+ end
23
+
24
+ def update
25
+ super
26
+
27
+ if @text.textobject.name != safe_style_fetch(:font)
28
+ set_font
29
+ root.gui_state.request_recalculate
30
+ end
31
+ end
32
+
33
+ def render
34
+ # Gosu.clip_to is too expensive to always use so check if we actually need it.
35
+ if @text_width > width || @text_height > height
36
+ Gosu.clip_to(@text.x, @text.y, @width, @height) do
37
+ @text.draw
38
+ end
39
+ else
40
+ @text.draw
41
+ end
42
+ end
43
+
44
+ def layout
45
+ @text.color = styled(:color)
46
+ @text.alignment = styled(:text_align)
47
+
48
+ @text.shadow = styled(:text_shadow)
49
+ @text.shadow_color = styled(:text_shadow_color)
50
+ @text.shadow_alpha = styled(:text_shadow_alpha)
51
+ @text.shadow_size = styled(:text_shadow_size)
52
+
53
+ @text.border = styled(:text_border)
54
+ @text.border_color = styled(:text_border_color)
55
+ @text.border_alpha = styled(:text_border_alpha)
56
+ @text.border_size = styled(:text_border_size)
57
+
58
+ @width = 0
59
+ @height = 0
60
+
61
+ _width = dimensional_size(@style.width, :width)
62
+ _height = dimensional_size(@style.height, :height)
63
+
64
+ handle_text_wrapping(_width)
65
+
66
+ # Update cached text width and height
67
+ @text_width = @text.width
68
+ @text_height = @text.height
69
+
70
+ @width = _width || @text_width.floor
71
+ @height = _height || @text_height.floor
72
+
73
+ @text.y = styled(:margin_top) + styled(:border_thickness_top) + styled(:padding_top) + @y
74
+ @text.z = @z + 3
75
+
76
+ if (text_alignment = @options[:text_align] || @options[:text_h_align])
77
+ case text_alignment
78
+ when :left
79
+ @text.x = styled(:margin_left) + styled(:border_thickness_left) + styled(:padding_left) + @x
80
+ when :center
81
+ @text.x = if @text_width <= width
82
+ @x + styled(:margin_left) + width / 2 - @text_width / 2
83
+ else # Act as left aligned
84
+ styled(:margin_left) + styled(:border_thickness_left) + styled(:padding_left) + @x
85
+ end
86
+ when :right
87
+ @text.x = @x + styled(:margin_left) + outer_width - (@text_width + styled(:border_thickness_right) + styled(:padding_right))
88
+ end
89
+ end
90
+
91
+ if (vertical_alignment = @options[:text_v_align])
92
+ case vertical_alignment
93
+ when :center
94
+ @text.y = if @text_height <= height
95
+ @y + styled(:margin_top) + height / 2 - @text_height / 2
96
+ else
97
+ styled(:margin_top) + styled(:border_thickness_top) + styled(:padding_top) + @y
98
+ end
99
+ when :bottom
100
+ @text.y = @y + styled(:margin_top) + outer_height - (@text_height + styled(:border_thickness_bottom) + styled(:padding_bottom))
101
+ end
102
+ end
103
+
104
+ update_background
105
+ end
106
+
107
+ def handle_text_wrapping(max_width)
108
+ max_width ||= @parent&.content_width
109
+ max_width ||= @x - (window.width + noncontent_width)
110
+ wrap_behavior = styled(:text_wrap)
111
+ copy = @raw_text.to_s.dup
112
+
113
+ # Only perform text wrapping: if it is enabled, is possible to wrap, and text is too long to fit on one line
114
+ if wrap_behavior != :none && line_width(copy[0]) <= max_width && line_width(copy) > max_width
115
+ breaks = [] # list of indexes to insert a line break
116
+ line_start = 0
117
+ line_end = copy.length
118
+
119
+ stalled = false
120
+ stalled_interations = 0
121
+ max_stalled_iterations = 10
122
+ checked_copy_length = line_width(copy[line_start..line_end])
123
+
124
+ # find length of lines
125
+ while line_width(copy[line_start..line_end]) > max_width && stalled_interations < max_stalled_iterations
126
+ search_start = line_start
127
+ search_end = line_end
128
+
129
+ # Perform a binary search to find length of line
130
+ while search_start < search_end
131
+ midpoint = ((search_start.to_f + search_end) / 2.0).floor
132
+
133
+ if line_width(copy[line_start..midpoint]) > max_width
134
+ search_end = midpoint
135
+ else
136
+ search_start = midpoint + 1
137
+ end
138
+ end
139
+
140
+ if wrap_behavior == :word_wrap
141
+ word_search_end = search_end
142
+ failed = false
143
+
144
+ until(copy[word_search_end].to_s.match(/[[:punct:]]| /))
145
+ word_search_end -= 1
146
+
147
+ if word_search_end <= 1 || word_search_end < line_start
148
+ failed = true
149
+ break
150
+ end
151
+ end
152
+
153
+ line_start = failed ? search_end : word_search_end + 1 # walk in front of punctuation
154
+ else
155
+ line_start = search_end
156
+ end
157
+
158
+ breaks << line_start
159
+
160
+ # Prevent locking up due to outer while loop text width < max_width check not being satisfied.
161
+ stalled = checked_copy_length == line_width(copy[line_start..line_end])
162
+ checked_copy_length = line_width(copy[line_start..line_end])
163
+
164
+ stalled_interations += 1 if stalled
165
+ stalled_interations = 0 unless stalled
166
+ end
167
+
168
+ breaks.each_with_index do |pos, index|
169
+ copy.insert(pos + index, "\n") if pos + index >= 0 && pos + index < copy.length
170
+ end
171
+ end
172
+
173
+ @text.text = copy
174
+ end
175
+
176
+ def line_width(text)
177
+ (@text.textobject.markup_width(text.to_s) + noncontent_width)
178
+ end
179
+
180
+ def value
181
+ @raw_text
182
+ end
183
+
184
+ def value=(value)
185
+ old_value = @raw_text
186
+ @raw_text = value.to_s.chomp
187
+
188
+ old_width = width
189
+ old_height = height
190
+
191
+ if old_width != width || old_height != height
192
+ root.gui_state.request_recalculate
193
+ else
194
+ recalculate
195
+ end
196
+
197
+ if old_value != @raw_text
198
+ root.gui_state.request_repaint
199
+ publish(:changed, @raw_text)
200
+ end
201
+
202
+ value
203
+ end
204
+ end
205
+
206
+ class Banner < TextBlock
207
+ end
208
+
209
+ class Title < TextBlock
210
+ end
211
+
212
+ class Subtitle < TextBlock
213
+ end
214
+
215
+ class Tagline < TextBlock
216
+ end
217
+
218
+ class Caption < TextBlock
219
+ end
220
+
221
+ class Para < TextBlock
222
+ end
223
+
224
+ class Inscription < TextBlock
225
+ end
226
+
227
+ class ToolTip < TextBlock
228
+ def needs_repaint?
229
+ @needs_repaint || !value.to_s.empty?
230
+ end
231
+ end
232
+
233
+ class Link < TextBlock
234
+ end
235
+ end
236
+ end
@@ -1,67 +1,71 @@
1
- module CyberarmEngine
2
- class Element
3
- class ToggleButton < Button
4
- attr_reader :toggled, :value
5
-
6
- def initialize(options, block = nil)
7
- if options.dig(:theme, :ToggleButton, :checkmark_image)
8
- options[:theme][:ToggleButton][:image_width] ||= options[:theme][:TextBlock][:text_size]
9
- super(get_image(options.dig(:theme, :ToggleButton, :checkmark_image)), options, block)
10
- else
11
- super(options[:checkmark], options, block)
12
- end
13
-
14
- @value = options[:checked] || false
15
-
16
- if @value
17
- @raw_text = @options[:checkmark]
18
- else
19
- @raw_text = ""
20
- end
21
- end
22
-
23
- def clicked_left_mouse_button(_sender, _x, _y)
24
- self.value = !@value
25
-
26
- @block.call(self) if @block
27
-
28
- :handled
29
- end
30
-
31
- def render
32
- if @image
33
- draw_image if @value
34
- else
35
- draw_text
36
- end
37
- end
38
-
39
- def recalculate
40
- super
41
- return if @image
42
-
43
- _width = dimensional_size(@style.width, :width)
44
- _height = dimensional_size(@style.height, :height)
45
-
46
- @width = _width || @text.textobject.text_width(@options[:checkmark])
47
- @height = _height || @text.height
48
-
49
- update_background
50
- end
51
-
52
- def value=(boolean)
53
- @value = boolean
54
-
55
- if boolean
56
- @raw_text = @options[:checkmark]
57
- else
58
- @raw_text = ""
59
- end
60
-
61
- recalculate
62
-
63
- publish(:changed, @value)
64
- end
65
- end
66
- end
67
- end
1
+ module CyberarmEngine
2
+ class Element
3
+ class ToggleButton < Button
4
+ attr_reader :toggled, :value
5
+
6
+ def initialize(options, block = nil)
7
+ if options.dig(:theme, :ToggleButton, :checkmark_image)
8
+ options[:theme][:ToggleButton][:image_width] ||= options[:theme][:TextBlock][:text_size]
9
+ super(get_image(options.dig(:theme, :ToggleButton, :checkmark_image)), options, block)
10
+ else
11
+ super(options[:checkmark], options, block)
12
+ end
13
+
14
+ @value = options[:checked] || false
15
+
16
+ if @value
17
+ @raw_text = @options[:checkmark]
18
+ else
19
+ @raw_text = ""
20
+ end
21
+ end
22
+
23
+ def clicked_left_mouse_button(_sender, _x, _y)
24
+ self.value = !@value
25
+
26
+ @block.call(self) if @block
27
+
28
+ :handled
29
+ end
30
+
31
+ def render
32
+ if @image
33
+ draw_image if @value
34
+ else
35
+ draw_text
36
+ end
37
+ end
38
+
39
+ def recalculate
40
+ super
41
+ return if @image
42
+
43
+ _width = dimensional_size(@style.width, :width)
44
+ _height = dimensional_size(@style.height, :height)
45
+
46
+ @width = _width || @text.textobject.text_width(@options[:checkmark])
47
+ @height = _height || @text.height
48
+
49
+ update_background
50
+ end
51
+
52
+ def value=(boolean)
53
+ old_value = self.value
54
+
55
+ @value = boolean
56
+
57
+ if boolean
58
+ @raw_text = @options[:checkmark]
59
+ else
60
+ @raw_text = ""
61
+ end
62
+
63
+ recalculate
64
+
65
+ publish(:changed, @value) if old_value != boolean
66
+
67
+ boolean
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,45 @@
1
+ module CyberarmEngine
2
+ class Element
3
+ # Special container that has a layout like a Flow
4
+ # and makes all its children mirror its styling (i.e. hover, active, disabled...)
5
+ class Widget < Flow
6
+ def update_styles(style = :default)
7
+ super
8
+
9
+ @children.each do |child|
10
+ recursive_styles(style, self)
11
+ end
12
+ end
13
+
14
+ # Make child elements mirror the widgets styling
15
+ # disabled elements will not have their styling overridden
16
+ def recursive_styles(style, container)
17
+ container.children.each do |child|
18
+ child.update_styles(style)
19
+
20
+ recursive_styles(style, child) if child.is_a?(Container)
21
+ end
22
+ end
23
+
24
+ # Enable child elements to display their tooltips
25
+ # but fall back to the Widget if no hit element has a tip
26
+ def tip
27
+ parent_scroll_position = CyberarmEngine::Vector.new
28
+
29
+ element = self
30
+ while (parent_element = element.parent)
31
+ parent_scroll_position += parent_element.scroll_position
32
+
33
+ element = parent_element
34
+ end
35
+
36
+ elements = hit_element?(window.mouse_x - parent_scroll_position.x, window.mouse_y - parent_scroll_position.y)
37
+
38
+ return @tip unless elements
39
+
40
+ elements.delete(self) # prevent infinite recursive loop (Widget#tip)
41
+ elements.reverse.find { |e| !e.tip.empty? }&.tip || @tip
42
+ end
43
+ end
44
+ end
45
+ end