cyberarm_engine 0.21.0 → 0.23.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.
@@ -65,6 +65,8 @@ module CyberarmEngine
65
65
 
66
66
  set_border_thickness
67
67
  set_border_color
68
+
69
+ root.gui_state.request_repaint
68
70
  end
69
71
 
70
72
  def safe_style_fetch(*args)
@@ -166,10 +168,10 @@ module CyberarmEngine
166
168
  return if self.is_a?(ToolTip)
167
169
 
168
170
  if old_width != width || old_height != height
169
- (root&.gui_state || @gui_state).request_recalculate
170
- else
171
- stylize
171
+ root.gui_state.request_recalculate
172
172
  end
173
+
174
+ stylize
173
175
  end
174
176
 
175
177
  def default_events
@@ -256,6 +258,8 @@ module CyberarmEngine
256
258
  end
257
259
 
258
260
  def enabled=(boolean)
261
+ root.gui_state.request_repaint if @enabled != boolean
262
+
259
263
  @enabled = boolean
260
264
 
261
265
  recalculate
@@ -267,6 +271,10 @@ module CyberarmEngine
267
271
  @enabled
268
272
  end
269
273
 
274
+ def focused?
275
+ @focus
276
+ end
277
+
270
278
  def visible?
271
279
  @visible
272
280
  end
@@ -278,18 +286,21 @@ module CyberarmEngine
278
286
  def toggle
279
287
  @visible = !@visible
280
288
  root.gui_state.request_recalculate
289
+ root.gui_state.request_repaint
281
290
  end
282
291
 
283
292
  def show
284
293
  bool = visible?
285
294
  @visible = true
286
295
  root.gui_state.request_recalculate unless bool
296
+ root.gui_state.request_repaint unless bool
287
297
  end
288
298
 
289
299
  def hide
290
300
  bool = visible?
291
301
  @visible = false
292
302
  root.gui_state.request_recalculate if bool
303
+ root.gui_state.request_repaint if bool
293
304
  end
294
305
 
295
306
  def draw
@@ -423,9 +434,9 @@ module CyberarmEngine
423
434
 
424
435
  pairs_ << a_ unless pairs_.last == a_
425
436
 
426
- pairs_.sum { |pair| pair.map(&:outer_height).max } + @style.padding_bottom + @style.border_thickness_bottom
437
+ pairs_.sum { |pair| + @style.padding_top + @style.border_thickness_top + pair.map(&:outer_height).max } + @style.padding_bottom + @style.border_thickness_bottom
427
438
  else
428
- @children.sum(&:outer_height) + @style.padding_bottom + @style.border_thickness_bottom
439
+ @style.padding_top + @style.border_thickness_top + @children.sum(&:outer_height) + @style.padding_bottom + @style.border_thickness_bottom
429
440
  end
430
441
  end
431
442
 
@@ -440,37 +451,47 @@ module CyberarmEngine
440
451
  def dimensional_size(size, dimension)
441
452
  raise "dimension must be either :width or :height" unless %i[width height].include?(dimension)
442
453
 
443
- new_size = if size.is_a?(Numeric) && size.between?(0.0, 1.0)
444
- (@parent.send(:"content_#{dimension}") * size).floor - send(:"noncontent_#{dimension}").floor
445
- else
446
- size
447
- end
454
+ new_size = if size.is_a?(Float) && size.between?(0.0, 1.0)
455
+ (@parent.send(:"content_#{dimension}") * size).floor - send(:"noncontent_#{dimension}").floor
456
+ else
457
+ size
458
+ end
448
459
 
449
- if @parent && @style.fill # Handle fill behavior
450
- fill_siblings = @parent.children.select { |c| c.style.fill }.count.to_f # include self since we're dividing
460
+ # Handle fill behavior
461
+ if @parent && @style.fill &&
462
+ (dimension == :width && @parent.is_a?(Flow) ||
463
+ dimension == :height && @parent.is_a?(Stack))
464
+ return space_available_width - noncontent_width if dimension == :width && @parent.is_a?(Flow)
465
+ return space_available_height - noncontent_height if dimension == :height && @parent.is_a?(Stack)
451
466
 
452
- if dimension == :width && @parent.is_a?(Flow)
453
- space_available_width = ((@parent.content_width - (@parent.children.reject { |c| c.style.fill }).map(&:outer_width).sum) / fill_siblings)
454
- space_available_width = space_available_width.nan? ? 0 : space_available_width.floor # The parent element might not have its dimensions, yet.
467
+ # Handle min_width/height and max_width/height
468
+ else
469
+ return @style.send(:"min_#{dimension}") if @style.send(:"min_#{dimension}") && new_size.to_f < @style.send(:"min_#{dimension}")
470
+ return @style.send(:"max_#{dimension}") if @style.send(:"max_#{dimension}") && new_size.to_f > @style.send(:"max_#{dimension}")
471
+ end
455
472
 
456
- return space_available_width - noncontent_width
473
+ new_size
474
+ end
457
475
 
458
- elsif dimension == :height && @parent.is_a?(Stack)
459
- space_available_height = ((@parent.content_height - (@parent.children.reject { |c| c.style.fill }).map(&:outer_height).sum) / fill_siblings)
460
- space_available_height = space_available_height.nan? ? 0 : space_available_height.floor # The parent element might not have its dimensions, yet.
476
+ def space_available_width
477
+ # TODO: This may get expensive if there are a lot of children, probably should cache it somehow
478
+ fill_siblings = @parent.children.select { |c| c.style.fill }.count.to_f # include self since we're dividing
461
479
 
462
- return space_available_height - noncontent_height
463
- end
480
+ available_space = ((@parent.content_width - (@parent.children.reject { |c| c.style.fill }).map(&:outer_width).sum) / fill_siblings)
481
+ (available_space.nan? || available_space.infinite?) ? 0 : available_space.floor # The parent element might not have its dimensions, yet.
482
+ end
464
483
 
465
- else # Handle min_width/height and max_width/height
466
- return @style.send(:"min_#{dimension}") if @style.send(:"min_#{dimension}") && new_size < @style.send(:"min_#{dimension}")
467
- return @style.send(:"max_#{dimension}") if @style.send(:"max_#{dimension}") && new_size > @style.send(:"max_#{dimension}")
468
- end
484
+ def space_available_height
485
+ # TODO: This may get expensive if there are a lot of children, probably should cache it somehow
486
+ fill_siblings = @parent.children.select { |c| c.style.fill }.count.to_f # include self since we're dividing
469
487
 
470
- new_size
488
+ available_space = ((@parent.content_height - (@parent.children.reject { |c| c.style.fill }).map(&:outer_height).sum) / fill_siblings)
489
+ (available_space.nan? || available_space.infinite?) ? 0 : available_space.floor # The parent element might not have its dimensions, yet.
471
490
  end
472
491
 
473
492
  def background=(_background)
493
+ root.gui_state.request_repaint
494
+
474
495
  @style.background_canvas.background = _background
475
496
  update_background
476
497
  end
@@ -489,6 +510,8 @@ module CyberarmEngine
489
510
  end
490
511
 
491
512
  def background_nine_slice=(_image_path)
513
+ root.gui_state.request_repaint
514
+
492
515
  @style.background_nine_slice_canvas.image = _image_path
493
516
  update_background_nine_slice
494
517
  end
@@ -513,6 +536,8 @@ module CyberarmEngine
513
536
  end
514
537
 
515
538
  def background_image=(image_path)
539
+ root.gui_state.request_repaint
540
+
516
541
  @style.background_image = image_path.is_a?(Gosu::Image) ? image_path : get_image(image_path)
517
542
  update_background_image
518
543
  end
@@ -42,6 +42,10 @@ module CyberarmEngine
42
42
  root.gui_state.request_recalculate
43
43
  end
44
44
 
45
+ def remove(element)
46
+ root.gui_state.request_recalculate if @children.delete(element)
47
+ end
48
+
45
49
  def clear(&block)
46
50
  @children.clear
47
51
 
@@ -122,6 +126,9 @@ module CyberarmEngine
122
126
 
123
127
  layout
124
128
 
129
+ old_width = @width
130
+ old_height = @height
131
+
125
132
  if is_root?
126
133
  @width = @style.width = window.width
127
134
  @height = @style.height = window.height
@@ -136,7 +143,31 @@ module CyberarmEngine
136
143
  @height = _height || (@children.map { |c| c.y + c.outer_height }.max || 0).floor
137
144
  end
138
145
 
139
- # Move child to parent after positioning
146
+ # FIXME: Correctly handle alignment when element has siblings
147
+ # FIXME: Enable alignment for any element, not just containers
148
+ if @style.v_align
149
+ space = space_available_height
150
+
151
+ case @style.v_align
152
+ when :center
153
+ @y = parent.height / 2 - height / 2
154
+ when :bottom
155
+ @y = parent.height - height
156
+ end
157
+ end
158
+
159
+ if @style.h_align
160
+ space = space_available_width
161
+
162
+ case @style.h_align
163
+ when :center
164
+ @x = parent.width / 2 - width / 2
165
+ when :right
166
+ @x = parent.width - width
167
+ end
168
+ end
169
+
170
+ # Move children to parent after positioning
140
171
  @children.each do |child|
141
172
  child.x += (@x + @style.border_thickness_left) - style.margin_left
142
173
  child.y += (@y + @style.border_thickness_top) - style.margin_top
@@ -154,6 +185,8 @@ module CyberarmEngine
154
185
  # puts "TOOK: #{Gosu.milliseconds - s}ms to recalculate #{self.class}:0x#{self.object_id.to_s(16)}"
155
186
 
156
187
  update_background
188
+
189
+ root.gui_state.request_repaint if @width != old_width || @height != old_height
157
190
  end
158
191
 
159
192
  def layout
@@ -172,7 +205,6 @@ module CyberarmEngine
172
205
  end
173
206
 
174
207
  def fits_on_line?(element) # Flow
175
- p [@options[:id], @width] if @options[:id]
176
208
  @current_position.x + element.outer_width <= max_width &&
177
209
  @current_position.x + element.outer_width <= window.width
178
210
  end
@@ -182,7 +214,6 @@ module CyberarmEngine
182
214
  element.y = element.style.margin_top + @current_position.y
183
215
 
184
216
  @current_position.x += element.outer_width
185
- @current_position.x = @style.margin_left if @current_position.x >= max_width
186
217
  end
187
218
 
188
219
  def tallest_neighbor(querier, _y_position) # Flow
@@ -195,14 +226,14 @@ module CyberarmEngine
195
226
  response
196
227
  end
197
228
 
198
- def position_on_next_line(child) # Flow
199
- @current_position.x = @style.margin_left
200
- @current_position.y += tallest_neighbor(child, @current_position.y).outer_height
229
+ def position_on_next_line(element) # Flow
230
+ @current_position.x = @style.margin_left + @style.padding_left
231
+ @current_position.y += tallest_neighbor(element, @current_position.y).outer_height
201
232
 
202
- child.x = child.style.margin_left + @current_position.x
203
- child.y = child.style.margin_top + @current_position.y
233
+ element.x = element.style.margin_left + @current_position.x
234
+ element.y = element.style.margin_top + @current_position.y
204
235
 
205
- @current_position.x += child.outer_width
236
+ @current_position.x += element.outer_width
206
237
  end
207
238
 
208
239
  def move_to_next_line(element) # Stack
@@ -220,6 +251,7 @@ module CyberarmEngine
220
251
  @scroll_position.y = 0 if @scroll_position.y > 0
221
252
 
222
253
  root.gui_state.request_recalculate_for(self)
254
+ root.gui_state.request_repaint
223
255
 
224
256
  return :handled
225
257
  end
@@ -235,6 +267,7 @@ module CyberarmEngine
235
267
  @scroll_position.y = -max_scroll_height if @scroll_position.y.abs > max_scroll_height
236
268
 
237
269
  root.gui_state.request_recalculate_for(self)
270
+ root.gui_state.request_repaint
238
271
 
239
272
  return :handled
240
273
  end
@@ -256,7 +289,7 @@ module CyberarmEngine
256
289
  end
257
290
 
258
291
  def value
259
- @children.map { |c| c.class }.join(", ")
292
+ @children.map(&:class).join(", ")
260
293
  end
261
294
 
262
295
  def to_s
@@ -1,6 +1,20 @@
1
1
  module CyberarmEngine
2
2
  class Element
3
3
  class EditLine < Button
4
+ class TextInput < Gosu::TextInput
5
+ def filter=(filter)
6
+ @filter = filter
7
+ end
8
+
9
+ def filter(text_in)
10
+ if @filter
11
+ @filter.call(text_in)
12
+ else
13
+ text_in
14
+ end
15
+ end
16
+ end
17
+
4
18
  def initialize(text, options = {}, block = nil)
5
19
  @filter = options.delete(:filter)
6
20
  super(text, options, block)
@@ -14,17 +28,11 @@ module CyberarmEngine
14
28
  @caret_last_interval = Gosu.milliseconds
15
29
  @show_caret = true
16
30
 
17
- @text_input = Gosu::TextInput.new
31
+ @text_input = TextInput.new
32
+ @text_input.filter = @filter
18
33
  @text_input.text = text
19
34
  @last_text_value = text
20
-
21
- if @filter && @filter.respond_to?(:call)
22
- @text_input.instance_variable_set(:@filter, @filter)
23
-
24
- def @text_input.filter(text_in)
25
- @filter.call(text_in)
26
- end
27
- end
35
+ @last_caret_position = @text_input.caret_pos
28
36
 
29
37
  @offset_x = 0
30
38
  @offset_y = 0
@@ -72,10 +80,22 @@ module CyberarmEngine
72
80
  @show_caret = true
73
81
  @caret_last_interval = Gosu.milliseconds
74
82
 
83
+ root.gui_state.request_repaint
84
+
75
85
  publish(:changed, value)
76
86
  end
77
87
 
88
+ if @last_caret_position != @text_input.caret_pos
89
+ @last_caret_position = @text_input.caret_pos
90
+ root.gui_state.request_repaint
91
+
92
+ @show_caret = true
93
+ @caret_last_interval = Gosu.milliseconds
94
+ end
95
+
78
96
  if Gosu.milliseconds >= @caret_last_interval + @caret_interval
97
+ root.gui_state.request_repaint
98
+
79
99
  @caret_last_interval = Gosu.milliseconds
80
100
 
81
101
  @show_caret = !@show_caret
@@ -98,20 +118,20 @@ module CyberarmEngine
98
118
  @text_input.caret_pos = @text_input.text.length
99
119
 
100
120
  when Gosu::KB_C
101
- if @text_input.selection_start < @text_input.caret_pos
102
- Clipboard.copy(@text_input.text[@text_input.selection_start...@text_input.caret_pos])
103
- else
104
- Clipboard.copy(@text_input.text[@text_input.caret_pos...@text_input.selection_start])
105
- end
121
+ Gosu.clipboard = if @text_input.selection_start < @text_input.caret_pos
122
+ @text_input.text[@text_input.selection_start...@text_input.caret_pos]
123
+ else
124
+ @text_input.text[@text_input.caret_pos...@text_input.selection_start]
125
+ end
106
126
 
107
127
  when Gosu::KB_X
108
128
  chars = @text_input.text.chars
109
129
 
110
130
  if @text_input.selection_start < @text_input.caret_pos
111
- Clipboard.copy(@text_input.text[@text_input.selection_start...@text_input.caret_pos])
131
+ Gosu.clipboard = @text_input.text[@text_input.selection_start...@text_input.caret_pos]
112
132
  chars.slice!(@text_input.selection_start, @text_input.caret_pos)
113
133
  else
114
- Clipboard.copy(@text_input.text[@text_input.caret_pos...@text_input.selection_start])
134
+ Gosu.clipboard = @text_input.text[@text_input.caret_pos...@text_input.selection_start]
115
135
  chars.slice!(@text_input.caret_pos, @text_input.selection_start)
116
136
  end
117
137
 
@@ -119,10 +139,9 @@ module CyberarmEngine
119
139
 
120
140
  when Gosu::KB_V
121
141
  if instance_of?(EditLine) # EditLine assumes a single line of text
122
- @text_input.text = @text_input.text.insert(@text_input.caret_pos,
123
- Clipboard.paste.encode("UTF-8").gsub("\n", ""))
142
+ @text_input.insert_text(Gosu.clipboard.gsub("\n", ""))
124
143
  else
125
- @text_input.text = @text_input.text.insert(@text_input.caret_pos, Clipboard.paste.encode("UTF-8"))
144
+ @text_input.insert_text(Gosu.clipboard)
126
145
  end
127
146
  end
128
147
  end
@@ -180,7 +199,7 @@ module CyberarmEngine
180
199
  if @type == :password
181
200
  @text.x + @text.width(default(:password_character) * @text_input.text[0...@text_input.send(method)].length)
182
201
  else
183
- @text.x + @text.width(@text_input.text[0...@text_input.send(method)])
202
+ @text.x + @text.width(@text_input.text[0...@text_input.send(method)]) - @style.border_thickness_left
184
203
  end
185
204
  end
186
205
 
@@ -197,20 +216,35 @@ module CyberarmEngine
197
216
  end
198
217
 
199
218
  def focus(sender)
200
- super
201
-
219
+ @focus = true
202
220
  window.text_input = @text_input
203
221
  @text_input.caret_pos = @text_input.selection_start = @text_input.text.length
204
222
 
223
+ update_styles(:active)
224
+
205
225
  :handled
206
226
  end
207
227
 
208
228
  def enter(sender)
209
- _has_focus = @focus
229
+ if @enabled && @focus
230
+ update_styles(:active)
231
+ elsif @enabled && !@focus
232
+ update_styles(:hover)
233
+ else
234
+ update_styles(:disabled)
235
+ end
210
236
 
211
- super
237
+ :handled
238
+ end
212
239
 
213
- @focus = _has_focus
240
+ def leave(sender)
241
+ if @enabled && @focus
242
+ update_styles(:active)
243
+ elsif @enabled && !@focus
244
+ update_styles
245
+ else
246
+ update_styles(:disabled)
247
+ end
214
248
 
215
249
  :handled
216
250
  end
@@ -45,8 +45,8 @@ module CyberarmEngine
45
45
  @scale_y = 1
46
46
  end
47
47
 
48
- @width = _width || @image.width.floor * @scale_x
49
- @height = _height || @image.height.floor * @scale_y
48
+ @width = _width || (@image.width * @scale_x).floor
49
+ @height = _height || (@image.height * @scale_y).floor
50
50
 
51
51
  update_background
52
52
  end
@@ -13,7 +13,7 @@ module CyberarmEngine
13
13
  @style.background_canvas.background = default(:background)
14
14
 
15
15
  # TODO: "Clean Up" into own class?
16
- @menu = Stack.new(parent: parent, width: @options[:width], theme: @options[:theme])
16
+ @menu = Stack.new(parent: self, theme: @options[:theme])
17
17
  @menu.define_singleton_method(:recalculate_menu) do
18
18
  @x = @__list_box.x
19
19
  @y = @__list_box.y + @__list_box.height
@@ -64,6 +64,8 @@ module CyberarmEngine
64
64
  def show_menu
65
65
  @menu.clear
66
66
 
67
+ @menu.style.width = width
68
+
67
69
  @items.each do |item|
68
70
  next if item == self.value
69
71
 
@@ -52,6 +52,7 @@ module CyberarmEngine
52
52
  @marquee_animation_time = Gosu.milliseconds if @marquee_offset > range
53
53
 
54
54
  update_background
55
+ root.gui_state.request_repaint
55
56
  end
56
57
 
57
58
  def type=(type)
@@ -77,9 +78,13 @@ module CyberarmEngine
77
78
  def value=(decimal)
78
79
  raise "value must be number" unless decimal.is_a?(Numeric)
79
80
 
81
+ old_value = @fraction
82
+
80
83
  @fraction = decimal.clamp(0.0, 1.0)
81
84
  update_background
82
85
 
86
+ root.gui_state.request_repaint if @fraction != old_value
87
+
83
88
  publish(:changed, @fraction)
84
89
  @fraction
85
90
  end
@@ -42,7 +42,7 @@ module CyberarmEngine
42
42
  @step_size = @options[:step] || 0.1
43
43
  @value = @options[:value] || (@range.first + @range.last) / 2
44
44
 
45
- @handle = Handle.new("", parent: self, width: 8, height: 1.0) { close }
45
+ @handle = Handle.new("", parent: self, theme: options[:theme], width: 8, height: 1.0) { close }
46
46
  add(@handle)
47
47
  end
48
48
 
@@ -61,10 +61,10 @@ module CyberarmEngine
61
61
  end
62
62
 
63
63
  def position_handle
64
- @handle.x = @x + @style.padding_left + @style.border_thickness_left +
64
+ @handle.x = @x + @handle.style.margin_left + @style.padding_left + @style.border_thickness_left +
65
65
  ((content_width - @handle.outer_width) * (@value - @range.min) / (@range.max - @range.min).to_f)
66
66
 
67
- @handle.y = @y + @style.border_thickness_top + @style.padding_top
67
+ @handle.y = @y + @handle.style.margin_top + @style.border_thickness_top + @style.padding_top
68
68
  end
69
69
 
70
70
  def draw
@@ -97,6 +97,8 @@ module CyberarmEngine
97
97
  position_handle
98
98
  @handle.recalculate
99
99
 
100
+ root.gui_state.request_repaint
101
+
100
102
  publish(:changed, @value)
101
103
  end
102
104
  end
@@ -7,6 +7,7 @@ module CyberarmEngine
7
7
  @text = Text.new(
8
8
  text, font: @options[:font], z: @z, color: @options[:color],
9
9
  size: @options[:text_size], shadow: @options[:text_shadow],
10
+ static: @options[:text_static],
10
11
  shadow_size: @options[:text_shadow_size],
11
12
  shadow_color: @options[:text_shadow_color],
12
13
  border: @options[:text_border],
@@ -35,6 +36,9 @@ module CyberarmEngine
35
36
  @text.color = @style.color
36
37
  end
37
38
 
39
+ old_width = @width
40
+ old_height = @height
41
+
38
42
  @width = 0
39
43
  @height = 0
40
44
 
@@ -49,7 +53,7 @@ module CyberarmEngine
49
53
  @text.y = @style.border_thickness_top + @style.padding_top + @y
50
54
  @text.z = @z + 3
51
55
 
52
- if (text_alignment = @options[:text_align])
56
+ if (text_alignment = @options[:text_align] || @options[:text_h_align])
53
57
  case text_alignment
54
58
  when :left
55
59
  @text.x = @style.border_thickness_left + @style.padding_left + @x
@@ -64,7 +68,22 @@ module CyberarmEngine
64
68
  end
65
69
  end
66
70
 
71
+ if (vertical_alignment = @options[:text_v_align])
72
+ case vertical_alignment
73
+ when :center
74
+ @text.y = if @text.height <= height
75
+ @y + height / 2 - @text.height / 2
76
+ else
77
+ @style.border_thickness_top + @style.padding_top + @y
78
+ end
79
+ when :bottom
80
+ @text.y = @y + outer_height - (@text.height + @style.border_thickness_bottom + @style.padding_bottom)
81
+ end
82
+ end
83
+
67
84
  update_background
85
+
86
+ root.gui_state.request_repaint if @width != old_width || @height != old_height
68
87
  end
69
88
 
70
89
  def handle_text_wrapping(max_width)
@@ -145,6 +164,7 @@ module CyberarmEngine
145
164
  end
146
165
 
147
166
  def value=(value)
167
+ old_value = @raw_text
148
168
  @raw_text = value.to_s.chomp
149
169
 
150
170
  old_width = width
@@ -156,6 +176,8 @@ module CyberarmEngine
156
176
  recalculate
157
177
  end
158
178
 
179
+ root.gui_state.request_repaint if old_value != @raw_text
180
+
159
181
  publish(:changed, self.value)
160
182
  end
161
183
  end
@@ -5,7 +5,7 @@ module CyberarmEngine
5
5
 
6
6
  def initialize(options, block = nil)
7
7
  if options.dig(:theme, :ToggleButton, :checkmark_image)
8
- options[:theme][:ToggleButton][:image_width] ||= options[:theme][:Label][:text_size]
8
+ options[:theme][:ToggleButton][:image_width] ||= options[:theme][:TextBlock][:text_size]
9
9
  super(get_image(options.dig(:theme, :ToggleButton, :checkmark_image)), options, block)
10
10
 
11
11
  @_image = @image