cyberarm_engine 0.25.1 → 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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile.lock +20 -8
  4. data/README.md +1 -1
  5. data/assets/textures/ui/circle.png +0 -0
  6. data/assets/textures/ui/circle_small.png +0 -0
  7. data/assets/textures/ui/rounded.png +0 -0
  8. data/assets/textures/ui/rounded_bottom.png +0 -0
  9. data/assets/textures/ui/rounded_left.png +0 -0
  10. data/assets/textures/ui/rounded_left_bottom.png +0 -0
  11. data/assets/textures/ui/rounded_left_top.png +0 -0
  12. data/assets/textures/ui/rounded_right.png +0 -0
  13. data/assets/textures/ui/rounded_right_bottom.png +0 -0
  14. data/assets/textures/ui/rounded_right_top.png +0 -0
  15. data/assets/textures/ui/rounded_small.png +0 -0
  16. data/assets/textures/ui/rounded_tiny.png +0 -0
  17. data/assets/textures/ui/rounded_top.png +0 -0
  18. data/assets/textures/ui/square.png +0 -0
  19. data/cyberarm_engine.gemspec +1 -2
  20. data/lib/cyberarm_engine/background.rb +16 -18
  21. data/lib/cyberarm_engine/background_nine_slice.rb +62 -22
  22. data/lib/cyberarm_engine/common.rb +2 -0
  23. data/lib/cyberarm_engine/event_bus.rb +39 -0
  24. data/lib/cyberarm_engine/game_state.rb +11 -1
  25. data/lib/cyberarm_engine/pathfinding/graph.rb +64 -0
  26. data/lib/cyberarm_engine/pathfinding/graph_search.rb +6 -0
  27. data/lib/cyberarm_engine/pathfinding/graph_searches/astar_search.rb +6 -0
  28. data/lib/cyberarm_engine/pathfinding/graph_searches/breadth_first_search.rb +27 -0
  29. data/lib/cyberarm_engine/pathfinding/graph_searches/dijkstra_search.rb +6 -0
  30. data/lib/cyberarm_engine/pathfinding/graphs/grid.rb +52 -0
  31. data/lib/cyberarm_engine/result.rb +1 -2
  32. data/lib/cyberarm_engine/text.rb +54 -26
  33. data/lib/cyberarm_engine/timer.rb +4 -0
  34. data/lib/cyberarm_engine/ui/border_canvas.rb +16 -16
  35. data/lib/cyberarm_engine/ui/dialog.rb +24 -0
  36. data/lib/cyberarm_engine/ui/dsl.rb +29 -1
  37. data/lib/cyberarm_engine/ui/element.rb +132 -130
  38. data/lib/cyberarm_engine/ui/elements/button.rb +12 -13
  39. data/lib/cyberarm_engine/ui/elements/check_box.rb +5 -1
  40. data/lib/cyberarm_engine/ui/elements/container.rb +52 -46
  41. data/lib/cyberarm_engine/ui/elements/edit_box.rb +30 -9
  42. data/lib/cyberarm_engine/ui/elements/edit_line.rb +4 -2
  43. data/lib/cyberarm_engine/ui/elements/image.rb +9 -3
  44. data/lib/cyberarm_engine/ui/elements/list_box.rb +3 -3
  45. data/lib/cyberarm_engine/ui/elements/menu.rb +3 -3
  46. data/lib/cyberarm_engine/ui/elements/progress.rb +8 -6
  47. data/lib/cyberarm_engine/ui/elements/slider.rb +7 -3
  48. data/lib/cyberarm_engine/ui/elements/text_block.rb +31 -17
  49. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +5 -1
  50. data/lib/cyberarm_engine/ui/elements/widget.rb +45 -0
  51. data/lib/cyberarm_engine/ui/event.rb +1 -1
  52. data/lib/cyberarm_engine/ui/gui_state.rb +86 -10
  53. data/lib/cyberarm_engine/ui/page.rb +39 -0
  54. data/lib/cyberarm_engine/ui/style.rb +48 -15
  55. data/lib/cyberarm_engine/ui/theme.rb +17 -5
  56. data/lib/cyberarm_engine/version.rb +1 -1
  57. data/lib/cyberarm_engine/window.rb +40 -1
  58. data/lib/cyberarm_engine.rb +11 -0
  59. metadata +28 -4
@@ -80,31 +80,41 @@ module CyberarmEngine
80
80
 
81
81
  def render
82
82
  Gosu.clip_to(
83
- @x + @style.border_thickness_left + @style.padding_left,
84
- @y + @style.border_thickness_top + @style.padding_top,
83
+ @x - 1 + styled(:margin_left) + styled(:border_thickness_left) + styled(:padding_left),
84
+ @y - 1 + styled(:margin_top) + styled(:border_thickness_top) + styled(:padding_top),
85
85
  content_width + 1,
86
86
  content_height + 1
87
87
  ) do
88
+
88
89
  Gosu.translate(@scroll_position.x, @scroll_position.y) do
89
90
  @children.each(&:draw)
91
+
92
+ @children.each do |child|
93
+ next unless child.visible?
94
+
95
+ child.debug_draw
96
+ end
90
97
  end
91
98
  end
99
+
100
+ debug_draw
92
101
  end
93
102
 
94
103
  def debug_draw
95
104
  super
96
-
97
- @children.each(&:debug_draw)
98
105
  end
99
106
 
100
107
  def update
101
- update_scroll if @style.scroll
108
+ update_scroll if styled(:scroll)
102
109
  @children.each(&:update)
103
110
  end
104
111
 
105
- def hit_element?(x, y)
112
+ # return nil if element was not hit, or array of hit elements if hit, includes self.
113
+ def hit_element?(x, y, elements = [])
106
114
  return unless hit?(x, y)
107
115
 
116
+ elements << self
117
+
108
118
  # Offset child hit point by scroll position/offset
109
119
  child_x = x - @scroll_position.x
110
120
  child_y = y - @scroll_position.y
@@ -114,20 +124,23 @@ module CyberarmEngine
114
124
 
115
125
  case child
116
126
  when Container
117
- if (element = child.hit_element?(child_x, child_y))
118
- return element
127
+ if (child.hit_element?(child_x, child_y, elements))
128
+ return elements
119
129
  end
120
130
  else
121
- return child if child.hit?(child_x, child_y)
131
+ if child.hit?(child_x, child_y)
132
+ elements << child
133
+ return elements
134
+ end
122
135
  end
123
136
  end
124
137
 
125
- self if hit?(x, y)
138
+ elements
126
139
  end
127
140
 
128
141
  def update_child_element_visibity(child)
129
- child.element_visible = child.x >= (@x - @scroll_position.x) - child.width && child.x <= (@x - @scroll_position.x) + width &&
130
- child.y >= (@y - @scroll_position.y) - child.height && child.y <= (@y - @scroll_position.y) + height
142
+ child.element_visible = child.x >= ((styled(:margin_left) + @x) - @scroll_position.x) - child.width && child.x <= ((styled(:margin_left) + @x) - @scroll_position.x) + width &&
143
+ child.y >= ((styled(:margin_top) + @y) - @scroll_position.y) - child.height && child.y <= ((styled(:margin_top) + @y) - @scroll_position.y) + height
131
144
  end
132
145
 
133
146
  def update_scroll
@@ -154,7 +167,7 @@ module CyberarmEngine
154
167
  end
155
168
 
156
169
  def recalculate
157
- @current_position = Vector.new(@style.margin_left + @style.padding_left, @style.margin_top + @style.padding_top)
170
+ @current_position = Vector.new(styled(:margin_left) + styled(:padding_left), styled(:margin_top) + styled(:padding_top))
158
171
 
159
172
  return unless visible?
160
173
 
@@ -173,7 +186,7 @@ module CyberarmEngine
173
186
  @cached_scroll_height = nil
174
187
 
175
188
  if is_root?
176
- @width = @style.width = window.width
189
+ @width = @style.width = window.width
177
190
  @height = @style.height = window.height
178
191
  else
179
192
  @width = 0
@@ -182,39 +195,39 @@ module CyberarmEngine
182
195
  _width = dimensional_size(@style.width, :width)
183
196
  _height = dimensional_size(@style.height, :height)
184
197
 
185
- @width = _width || (@children.map { |c| c.x + c.outer_width }.max || 0).floor
186
- @height = _height || (@children.map { |c| c.y + c.outer_height }.max || 0).floor
198
+ @width = _width || (@children.map { |c| c.x + c.outer_width }.max.to_f - (styled(:margin_left) + styled(:padding_left))).floor
199
+ @height = _height || (@children.map { |c| c.y + c.outer_height }.max.to_f - (styled(:margin_top) + styled(:padding_top))).floor
187
200
  end
188
201
 
189
202
  # FIXME: Correctly handle alignment when element has siblings
190
203
  # FIXME: Enable alignment for any element, not just containers
191
- if @style.v_align
204
+ if styled(:v_align)
192
205
  space = space_available_height
193
206
 
194
- case @style.v_align
207
+ case styled(:v_align)
195
208
  when :center
196
- @y = parent.height / 2 - height / 2
209
+ @y = parent.y + parent.outer_height / 2 - outer_height / 2
197
210
  when :bottom
198
- @y = parent.height - height
211
+ @y = parent.y + parent.outer_height - outer_height
199
212
  end
200
213
  end
201
214
 
202
- if @style.h_align
215
+ if styled(:h_align)
203
216
  space = space_available_width
204
217
 
205
- case @style.h_align
218
+ case styled(:h_align)
206
219
  when :center
207
- @x = parent.width / 2 - width / 2
220
+ @x = parent.x + parent.outer_width / 2 - outer_width / 2
208
221
  when :right
209
- @x = parent.width - width
222
+ @x = parent.x + parent.outer_width - outer_width
210
223
  end
211
224
  end
212
225
 
213
226
  # t = Gosu.milliseconds
214
227
  # Move children to parent after positioning
215
228
  @children.each do |child|
216
- child.x += (@x + @style.border_thickness_left) - style.margin_left
217
- child.y += (@y + @style.border_thickness_top) - style.margin_top
229
+ child.x += (@x + styled(:border_thickness_left))
230
+ child.y += (@y + styled(:border_thickness_top))
218
231
 
219
232
  child.stylize
220
233
  child.recalculate
@@ -249,13 +262,6 @@ module CyberarmEngine
249
262
  end
250
263
 
251
264
  def max_width
252
- # _width = dimensional_size(@style.width, :width)
253
- # if _width
254
- # outer_width
255
- # else
256
- # window.width - (@parent ? @parent.style.margin_right + @style.margin_right : @style.margin_right)
257
- # end
258
-
259
265
  outer_width
260
266
  end
261
267
 
@@ -265,8 +271,8 @@ module CyberarmEngine
265
271
  end
266
272
 
267
273
  def position_on_current_line(element) # Flow
268
- element.x = element.style.margin_left + @current_position.x
269
- element.y = element.style.margin_top + @current_position.y
274
+ element.x = @current_position.x
275
+ element.y = @current_position.y
270
276
 
271
277
  @current_position.x += element.outer_width
272
278
  end
@@ -282,24 +288,24 @@ module CyberarmEngine
282
288
  end
283
289
 
284
290
  def position_on_next_line(element) # Flow
285
- @current_position.x = @style.margin_left + @style.padding_left
291
+ @current_position.x = styled(:margin_left) + styled(:padding_left)
286
292
  @current_position.y += tallest_neighbor(element, @current_position.y).outer_height
287
293
 
288
- element.x = element.style.margin_left + @current_position.x
289
- element.y = element.style.margin_top + @current_position.y
294
+ element.x = @current_position.x
295
+ element.y = @current_position.y
290
296
 
291
297
  @current_position.x += element.outer_width
292
298
  end
293
299
 
294
300
  def move_to_next_line(element) # Stack
295
- element.x = element.style.margin_left + @current_position.x
296
- element.y = element.style.margin_top + @current_position.y
301
+ element.x = @current_position.x
302
+ element.y = @current_position.y
297
303
 
298
304
  @current_position.y += element.outer_height
299
305
  end
300
306
 
301
307
  def mouse_wheel_up(sender, x, y)
302
- return unless @style.scroll
308
+ return unless styled(:scroll)
303
309
 
304
310
  # Allow overscrolling UP, only if one can scroll DOWN
305
311
  return unless height < scroll_height
@@ -314,7 +320,7 @@ module CyberarmEngine
314
320
  end
315
321
 
316
322
  def mouse_wheel_down(sender, x, y)
317
- return unless @style.scroll
323
+ return unless styled(:scroll)
318
324
 
319
325
  return unless height < scroll_height
320
326
 
@@ -328,7 +334,7 @@ module CyberarmEngine
328
334
  end
329
335
 
330
336
  def scroll_jump_to_top(sender, x, y)
331
- return unless @style.scroll
337
+ return unless styled(:scroll)
332
338
 
333
339
  @scroll_position.y = 0
334
340
  @scroll_target_position.y = 0
@@ -337,7 +343,7 @@ module CyberarmEngine
337
343
  end
338
344
 
339
345
  def scroll_jump_to_end(sender, x, y)
340
- return unless @style.scroll
346
+ return unless styled(:scroll)
341
347
 
342
348
  @scroll_position.y = -max_scroll_height
343
349
  @scroll_target_position.y = -max_scroll_height
@@ -346,7 +352,7 @@ module CyberarmEngine
346
352
  end
347
353
 
348
354
  def scroll_page_up(sender, x, y)
349
- return unless @style.scroll
355
+ return unless styled(:scroll)
350
356
 
351
357
  @scroll_position.y += height
352
358
  @scroll_position.y = 0 if @scroll_position.y > 0
@@ -356,7 +362,7 @@ module CyberarmEngine
356
362
  end
357
363
 
358
364
  def scroll_page_down(sender, x, y)
359
- return unless @style.scroll
365
+ return unless styled(:scroll)
360
366
 
361
367
  @scroll_position.y -= height
362
368
  @scroll_position.y = -max_scroll_height if @scroll_position.y < -max_scroll_height
@@ -45,19 +45,23 @@ module CyberarmEngine
45
45
 
46
46
  def draw_selection
47
47
  selection_width = caret_position - selection_start_position
48
+ selection_start_line = calculate_line(@text_input.caret_pos)
49
+ selection_end_line = calculate_line(@text_input.selection_start)
48
50
 
49
- Gosu.draw_rect(selection_start_position, @text.y, selection_width, @text.textobject.height,
51
+ # pp [selection_width, selection_start_line, selection_end_line]
52
+
53
+ Gosu.draw_rect(selection_start_position, @text.y + selection_start_line * @text.textobject.height, selection_width, @text.textobject.height,
50
54
  default(:selection_color), @z)
51
55
  end
52
56
 
53
- def text_input_position_for(_method)
54
- line = @text_input.text[0...@text_input.caret_pos].lines.last
57
+ def text_input_position_for(method)
58
+ line = @text_input.text[0...@text_input.send(method)].lines.last
55
59
  _x = @text.x + @offset_x
56
60
 
57
61
  if @type == :password
58
- _x + @text.width(default(:password_character) * line.length)
62
+ _x + @text.width(default(:password_character) * line.length) - @style.border_thickness_left
59
63
  else
60
- _x + @text.width(line)
64
+ _x + @text.width(line) - @style.border_thickness_left
61
65
  end
62
66
  end
63
67
 
@@ -66,9 +70,12 @@ module CyberarmEngine
66
70
  @text_input.selection_start = @text_input.caret_pos = int
67
71
  end
68
72
 
73
+ def calculate_line(caret_pos = @text_input.caret_pos)
74
+ sub_text = @text_input.text[0...caret_pos].lines.size - 1
75
+ end
76
+
69
77
  def calculate_active_line
70
- sub_text = @text_input.text[0...@text_input.caret_pos]
71
- @active_line = sub_text.lines.size - 1
78
+ @active_line = calculate_line(@text_input.caret_pos)
72
79
  end
73
80
 
74
81
  def caret_stay_left_of_last_newline
@@ -79,8 +86,20 @@ module CyberarmEngine
79
86
  end
80
87
 
81
88
  def caret_position_under_mouse(mouse_x, mouse_y)
82
- active_line = row_at(mouse_y)
83
- right_offset = column_at(mouse_x, mouse_y)
89
+ # get y scroll offset of element to get EditBox's selected row
90
+ y_scroll_offset = 0
91
+ e = parent
92
+ while (e)
93
+ if e.is_a?(Container)
94
+ y_scroll_offset += e.scroll_position.y
95
+
96
+ # root element has no parent so loop will terminate
97
+ e = e.parent
98
+ end
99
+ end
100
+
101
+ active_line = row_at(mouse_y - y_scroll_offset)
102
+ right_offset = column_at(mouse_x, mouse_y - y_scroll_offset)
84
103
 
85
104
  buffer = @text_input.text.lines[0..active_line].join if active_line != 0
86
105
  buffer = @text_input.text.lines.first if active_line == 0
@@ -164,6 +183,8 @@ module CyberarmEngine
164
183
  pos = text.length
165
184
  end
166
185
 
186
+ pp [direction, pos, @active_line]
187
+
167
188
  set_position(pos)
168
189
  end
169
190
 
@@ -197,9 +197,9 @@ module CyberarmEngine
197
197
 
198
198
  def text_input_position_for(method)
199
199
  if @type == :password
200
- @text.x + @text.width(default(:password_character) * @text_input.text[0...@text_input.send(method)].length) - @style.border_thickness_left
200
+ @text.x + @text.width(default(:password_character) * @text_input.text[0...@text_input.send(method)].length) - styled(:border_thickness_left)
201
201
  else
202
- @text.x + @text.width(@text_input.text[0...@text_input.send(method)]) - @style.border_thickness_left
202
+ @text.x + @text.width(@text_input.text[0...@text_input.send(method)]) - styled(:border_thickness_left)
203
203
  end
204
204
  end
205
205
 
@@ -291,6 +291,8 @@ module CyberarmEngine
291
291
 
292
292
  def value=(string)
293
293
  @text_input.text = string
294
+
295
+ string
294
296
  end
295
297
  end
296
298
  end
@@ -14,10 +14,10 @@ module CyberarmEngine
14
14
 
15
15
  def render
16
16
  @image.draw(
17
- @style.border_thickness_left + @style.padding_left + @x,
18
- @style.border_thickness_top + @style.padding_top + @y,
17
+ styled(:margin_left) + styled(:border_thickness_left) + styled(:padding_left) + @x,
18
+ styled(:margin_top) + styled(:border_thickness_top) + styled(:padding_top) + @y,
19
19
  @z + 2,
20
- @scale_x, @scale_y, @style.color
20
+ @scale_x, @scale_y, styled(:color)
21
21
  )
22
22
  end
23
23
 
@@ -56,12 +56,18 @@ module CyberarmEngine
56
56
  end
57
57
 
58
58
  def value=(path_or_image, retro: false, tileable: false)
59
+ old_value = self.value
60
+
59
61
  @path = path_or_image if path_or_image.is_a?(String)
60
62
 
61
63
  @image = Gosu::Image.new(path_or_image, retro: retro, tileable: tileable) if @path
62
64
  @image = path_or_image unless @path
63
65
 
64
66
  recalculate
67
+
68
+ publish(:changed, @image) if old_value != self.value
69
+
70
+ self.value
65
71
  end
66
72
 
67
73
  def path
@@ -10,7 +10,7 @@ module CyberarmEngine
10
10
 
11
11
  super(@choose, options, block)
12
12
 
13
- @style.background_canvas.background = default(:background)
13
+ @background_canvas.background = default(:background)
14
14
 
15
15
  @menu = Menu.new(parent: self, theme: @options[:theme])
16
16
 
@@ -21,7 +21,7 @@ module CyberarmEngine
21
21
  super
22
22
 
23
23
  w = @text.textobject.text_width("▼")
24
- @text.textobject.draw_text("▼", @x + content_width - w, @y + @style.padding_top, @z, 1, 1, @text.color)
24
+ @text.textobject.draw_text("▼", @x + content_width - w, @y + styled(:padding_top), @z, 1, 1, @text.color)
25
25
  end
26
26
 
27
27
  def choose=(item)
@@ -56,7 +56,7 @@ module CyberarmEngine
56
56
  def show_menu
57
57
  @menu.clear do
58
58
 
59
- @menu.style.width = width
59
+ @menu.style.width = width - (@menu.styled(:border_thickness_left) + @menu.styled(:border_thickness_right))
60
60
 
61
61
  @items.each do |item|
62
62
  # prevent already selected item from appearing in list
@@ -11,10 +11,10 @@ module CyberarmEngine
11
11
  # FIXME: properly find scrollable parent, if any.
12
12
  parent_scroll_top = parent&.parent ? parent.parent.scroll_top : 0
13
13
 
14
- @x = @parent.x
15
- @y = parent_scroll_top + @parent.y + @parent.height
14
+ @x = @parent.x + @parent.styled(:border_thickness_left)
15
+ @y = parent_scroll_top + @parent.y + @parent.height + @parent.styled(:border_thickness_top)
16
16
 
17
- @y = (parent_scroll_top + @parent.y) - height if @y + height > window.height
17
+ @y = (parent_scroll_top + @parent.y + @parent.styled(:border_thickness_top)) - height if @y + height > window.height
18
18
  end
19
19
 
20
20
  def show
@@ -11,7 +11,7 @@ module CyberarmEngine
11
11
  @marquee_offset = 0
12
12
  @marquee_animation_time = Gosu.milliseconds
13
13
  @type = options[:type] || :linear
14
- @fraction_background = Background.new(background: @style.fraction_background)
14
+ @fraction_background = Background.new(background: styled(:fraction_background))
15
15
  self.value = options[:fraction] || 0.0
16
16
  end
17
17
 
@@ -31,13 +31,13 @@ module CyberarmEngine
31
31
  def update_background
32
32
  super
33
33
 
34
- @fraction_background.x = (@style.border_thickness_left + @style.padding_left + @x) + @marquee_offset
35
- @fraction_background.y = @style.border_thickness_top + @style.padding_top + @y
34
+ @fraction_background.x = (styled(:border_thickness_left) + styled(:padding_left) + @x) + @marquee_offset
35
+ @fraction_background.y = styled(:border_thickness_top) + styled(:padding_top) + @y
36
36
  @fraction_background.z = @z
37
37
  @fraction_background.width = @width * (@type == :marquee ? @marquee_width : @fraction)
38
38
  @fraction_background.height = @height
39
39
 
40
- @fraction_background.background = @style.fraction_background
40
+ @fraction_background.background = styled(:fraction_background)
41
41
  end
42
42
 
43
43
  def update
@@ -83,9 +83,11 @@ module CyberarmEngine
83
83
  @fraction = decimal.clamp(0.0, 1.0)
84
84
  update_background
85
85
 
86
- root.gui_state.request_repaint if @fraction != old_value
86
+ if @fraction != old_value
87
+ root.gui_state.request_repaint
88
+ publish(:changed, @fraction)
89
+ end
87
90
 
88
- publish(:changed, @fraction)
89
91
  @fraction
90
92
  end
91
93
  end
@@ -62,10 +62,10 @@ module CyberarmEngine
62
62
  end
63
63
 
64
64
  def position_handle
65
- @handle.x = @x + @handle.style.margin_left + @style.padding_left + @style.border_thickness_left +
65
+ @handle.x = styled(:margin_left) + @x + @handle.styled(:margin_left) + styled(:padding_left) + styled(:border_thickness_left) +
66
66
  ((content_width - @handle.outer_width) * (@value - @range.min) / (@range.max - @range.min).to_f)
67
67
 
68
- @handle.y = @y + @handle.style.margin_top + @style.border_thickness_top + @style.padding_top
68
+ @handle.y = styled(:margin_top) + @y + @handle.styled(:margin_top) + styled(:border_thickness_top) + styled(:padding_top)
69
69
  end
70
70
 
71
71
  def draw
@@ -94,13 +94,17 @@ module CyberarmEngine
94
94
  end
95
95
 
96
96
  def value=(n)
97
+ old_value = self.value
98
+
97
99
  @value = n
98
100
  position_handle
99
101
  @handle.recalculate
100
102
 
101
103
  root.gui_state.request_repaint
102
104
 
103
- publish(:changed, @value)
105
+ publish(:changed, @value) if old_value != n
106
+
107
+ n
104
108
  end
105
109
  end
106
110
  end
@@ -8,6 +8,7 @@ module CyberarmEngine
8
8
  text, font: @options[:font], z: @z, color: @options[:color],
9
9
  size: @options[:text_size], shadow: @options[:text_shadow],
10
10
  static: @options[:text_static],
11
+ alignment: @options[:text_align],
11
12
  shadow_size: @options[:text_shadow_size],
12
13
  shadow_color: @options[:text_shadow_color],
13
14
  border: @options[:text_border],
@@ -32,7 +33,7 @@ module CyberarmEngine
32
33
  def render
33
34
  # Gosu.clip_to is too expensive to always use so check if we actually need it.
34
35
  if @text_width > width || @text_height > height
35
- Gosu.clip_to(@x, @y, width, height) do
36
+ Gosu.clip_to(@text.x, @text.y, @width, @height) do
36
37
  @text.draw
37
38
  end
38
39
  else
@@ -41,11 +42,18 @@ module CyberarmEngine
41
42
  end
42
43
 
43
44
  def layout
44
- unless @enabled
45
- @text.color = @style.disabled[:color]
46
- else
47
- @text.color = @style.color
48
- end
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)
49
57
 
50
58
  @width = 0
51
59
  @height = 0
@@ -62,21 +70,21 @@ module CyberarmEngine
62
70
  @width = _width || @text_width.floor
63
71
  @height = _height || @text_height.floor
64
72
 
65
- @text.y = @style.border_thickness_top + @style.padding_top + @y
73
+ @text.y = styled(:margin_top) + styled(:border_thickness_top) + styled(:padding_top) + @y
66
74
  @text.z = @z + 3
67
75
 
68
76
  if (text_alignment = @options[:text_align] || @options[:text_h_align])
69
77
  case text_alignment
70
78
  when :left
71
- @text.x = @style.border_thickness_left + @style.padding_left + @x
79
+ @text.x = styled(:margin_left) + styled(:border_thickness_left) + styled(:padding_left) + @x
72
80
  when :center
73
81
  @text.x = if @text_width <= width
74
- @x + width / 2 - @text_width / 2
82
+ @x + styled(:margin_left) + width / 2 - @text_width / 2
75
83
  else # Act as left aligned
76
- @style.border_thickness_left + @style.padding_left + @x
84
+ styled(:margin_left) + styled(:border_thickness_left) + styled(:padding_left) + @x
77
85
  end
78
86
  when :right
79
- @text.x = @x + outer_width - (@text_width + @style.border_thickness_right + @style.padding_right)
87
+ @text.x = @x + styled(:margin_left) + outer_width - (@text_width + styled(:border_thickness_right) + styled(:padding_right))
80
88
  end
81
89
  end
82
90
 
@@ -84,12 +92,12 @@ module CyberarmEngine
84
92
  case vertical_alignment
85
93
  when :center
86
94
  @text.y = if @text_height <= height
87
- @y + height / 2 - @text_height / 2
95
+ @y + styled(:margin_top) + height / 2 - @text_height / 2
88
96
  else
89
- @style.border_thickness_top + @style.padding_top + @y
97
+ styled(:margin_top) + styled(:border_thickness_top) + styled(:padding_top) + @y
90
98
  end
91
99
  when :bottom
92
- @text.y = @y + outer_height - (@text_height + @style.border_thickness_bottom + @style.padding_bottom)
100
+ @text.y = @y + styled(:margin_top) + outer_height - (@text_height + styled(:border_thickness_bottom) + styled(:padding_bottom))
93
101
  end
94
102
  end
95
103
 
@@ -99,7 +107,7 @@ module CyberarmEngine
99
107
  def handle_text_wrapping(max_width)
100
108
  max_width ||= @parent&.content_width
101
109
  max_width ||= @x - (window.width + noncontent_width)
102
- wrap_behavior = style.text_wrap
110
+ wrap_behavior = styled(:text_wrap)
103
111
  copy = @raw_text.to_s.dup
104
112
 
105
113
  # Only perform text wrapping: if it is enabled, is possible to wrap, and text is too long to fit on one line
@@ -186,9 +194,12 @@ module CyberarmEngine
186
194
  recalculate
187
195
  end
188
196
 
189
- root.gui_state.request_repaint if old_value != @raw_text
197
+ if old_value != @raw_text
198
+ root.gui_state.request_repaint
199
+ publish(:changed, @raw_text)
200
+ end
190
201
 
191
- publish(:changed, self.value)
202
+ value
192
203
  end
193
204
  end
194
205
 
@@ -214,6 +225,9 @@ module CyberarmEngine
214
225
  end
215
226
 
216
227
  class ToolTip < TextBlock
228
+ def needs_repaint?
229
+ @needs_repaint || !value.to_s.empty?
230
+ end
217
231
  end
218
232
 
219
233
  class Link < TextBlock
@@ -50,6 +50,8 @@ module CyberarmEngine
50
50
  end
51
51
 
52
52
  def value=(boolean)
53
+ old_value = self.value
54
+
53
55
  @value = boolean
54
56
 
55
57
  if boolean
@@ -60,7 +62,9 @@ module CyberarmEngine
60
62
 
61
63
  recalculate
62
64
 
63
- publish(:changed, @value)
65
+ publish(:changed, @value) if old_value != boolean
66
+
67
+ boolean
64
68
  end
65
69
  end
66
70
  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