cyberarm_engine 0.12.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/Gemfile +1 -1
  4. data/README.md +33 -3
  5. data/Rakefile +1 -1
  6. data/assets/textures/default.png +0 -0
  7. data/cyberarm_engine.gemspec +11 -8
  8. data/lib/cyberarm_engine.rb +24 -4
  9. data/lib/cyberarm_engine/animator.rb +56 -0
  10. data/lib/cyberarm_engine/background.rb +19 -15
  11. data/lib/cyberarm_engine/background_nine_slice.rb +125 -0
  12. data/lib/cyberarm_engine/bounding_box.rb +18 -18
  13. data/lib/cyberarm_engine/cache.rb +4 -0
  14. data/lib/cyberarm_engine/cache/download_manager.rb +121 -0
  15. data/lib/cyberarm_engine/common.rb +16 -16
  16. data/lib/cyberarm_engine/config_file.rb +46 -0
  17. data/lib/cyberarm_engine/game_object.rb +63 -72
  18. data/lib/cyberarm_engine/game_state.rb +7 -4
  19. data/lib/cyberarm_engine/model.rb +207 -0
  20. data/lib/cyberarm_engine/model/material.rb +21 -0
  21. data/lib/cyberarm_engine/model/model_object.rb +131 -0
  22. data/lib/cyberarm_engine/model/parser.rb +74 -0
  23. data/lib/cyberarm_engine/model/parsers/collada_parser.rb +138 -0
  24. data/lib/cyberarm_engine/model/parsers/wavefront_parser.rb +154 -0
  25. data/lib/cyberarm_engine/model_cache.rb +31 -0
  26. data/lib/cyberarm_engine/opengl.rb +28 -0
  27. data/lib/cyberarm_engine/opengl/light.rb +50 -0
  28. data/lib/cyberarm_engine/opengl/orthographic_camera.rb +46 -0
  29. data/lib/cyberarm_engine/opengl/perspective_camera.rb +38 -0
  30. data/lib/cyberarm_engine/opengl/renderer/bounding_box_renderer.rb +249 -0
  31. data/lib/cyberarm_engine/opengl/renderer/g_buffer.rb +164 -0
  32. data/lib/cyberarm_engine/opengl/renderer/opengl_renderer.rb +289 -0
  33. data/lib/cyberarm_engine/opengl/renderer/renderer.rb +22 -0
  34. data/lib/cyberarm_engine/opengl/shader.rb +406 -0
  35. data/lib/cyberarm_engine/opengl/texture.rb +69 -0
  36. data/lib/cyberarm_engine/ray.rb +5 -5
  37. data/lib/cyberarm_engine/stats.rb +21 -0
  38. data/lib/cyberarm_engine/text.rb +45 -31
  39. data/lib/cyberarm_engine/timer.rb +1 -1
  40. data/lib/cyberarm_engine/transform.rb +221 -9
  41. data/lib/cyberarm_engine/ui/border_canvas.rb +4 -3
  42. data/lib/cyberarm_engine/ui/dsl.rb +68 -50
  43. data/lib/cyberarm_engine/ui/element.rb +57 -18
  44. data/lib/cyberarm_engine/ui/elements/button.rb +86 -16
  45. data/lib/cyberarm_engine/ui/elements/check_box.rb +24 -32
  46. data/lib/cyberarm_engine/ui/elements/container.rb +88 -24
  47. data/lib/cyberarm_engine/ui/elements/edit_box.rb +179 -0
  48. data/lib/cyberarm_engine/ui/elements/edit_line.rb +180 -27
  49. data/lib/cyberarm_engine/ui/elements/flow.rb +1 -3
  50. data/lib/cyberarm_engine/ui/elements/image.rb +12 -9
  51. data/lib/cyberarm_engine/ui/elements/label.rb +96 -15
  52. data/lib/cyberarm_engine/ui/elements/list_box.rb +68 -0
  53. data/lib/cyberarm_engine/ui/elements/progress.rb +6 -5
  54. data/lib/cyberarm_engine/ui/elements/radio.rb +6 -0
  55. data/lib/cyberarm_engine/ui/elements/slider.rb +104 -0
  56. data/lib/cyberarm_engine/ui/elements/stack.rb +1 -3
  57. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +40 -31
  58. data/lib/cyberarm_engine/ui/event.rb +8 -7
  59. data/lib/cyberarm_engine/ui/gui_state.rb +89 -6
  60. data/lib/cyberarm_engine/ui/style.rb +10 -9
  61. data/lib/cyberarm_engine/ui/theme.rb +39 -21
  62. data/lib/cyberarm_engine/vector.rb +132 -38
  63. data/lib/cyberarm_engine/version.rb +2 -2
  64. data/lib/cyberarm_engine/{engine.rb → window.rb} +30 -19
  65. metadata +87 -16
  66. data/lib/cyberarm_engine/shader.rb +0 -205
@@ -2,35 +2,73 @@ module CyberarmEngine
2
2
  class Element
3
3
  class EditLine < Button
4
4
  def initialize(text, options = {}, block = nil)
5
+ @filter = options.delete(:filter)
5
6
  super(text, options, block)
6
7
 
7
8
  @type = default(:type)
8
9
 
9
10
  @caret_width = default(:caret_width)
10
- @caret_height= @text.height
11
+ @caret_height = @text.textobject.height
11
12
  @caret_color = default(:caret_color)
12
13
  @caret_interval = default(:caret_interval)
13
14
  @caret_last_interval = Gosu.milliseconds
14
- @show_caret = true
15
+ @show_caret = true
15
16
 
16
17
  @text_input = Gosu::TextInput.new
17
18
  @text_input.text = text
19
+ @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
28
+
29
+ @offset_x = 0
30
+ @offset_y = 0
18
31
 
19
- return self
32
+ event(:begin_drag)
33
+ event(:drag_update)
34
+ event(:end_drag)
20
35
  end
21
36
 
22
37
  def render
23
- Gosu.clip_to(@text.x, @text.y, @style.width, @text.height) do
24
- draw_text
25
- Gosu.draw_rect(caret_position, @text.y, @caret_width, @caret_height, @caret_color, @z + 40) if @focus && @show_caret
38
+ Gosu.clip_to(@text.x, @text.y, @width, @height) do
39
+ Gosu.translate(-@offset_x, -@offset_y) do
40
+ draw_selection
41
+ draw_caret if @focus && @show_caret
42
+ draw_text
43
+ end
26
44
  end
27
45
  end
28
46
 
47
+ def draw_text
48
+ @text.draw(:draw_text)
49
+ end
50
+
51
+ def draw_caret
52
+ Gosu.draw_rect(caret_position, @text.y, @caret_width, @caret_height, @caret_color, @z)
53
+ end
54
+
55
+ def draw_selection
56
+ selection_width = caret_position - selection_start_position
57
+
58
+ Gosu.draw_rect(selection_start_position, @text.y, selection_width, @text.height, default(:selection_color), @z)
59
+ end
60
+
29
61
  def update
30
- if @type == :password
31
- @text.text = default(:password_character) * @text_input.text.length
32
- else
33
- @text.text = @text_input.text
62
+ @text.text = if @type == :password
63
+ default(:password_character) * @text_input.text.length
64
+ else
65
+ @text_input.text
66
+ end
67
+
68
+ if @last_text_value != value
69
+ @last_text_value = value
70
+
71
+ publish(:changed, value)
34
72
  end
35
73
 
36
74
  if Gosu.milliseconds >= @caret_last_interval + @caret_interval
@@ -38,6 +76,108 @@ module CyberarmEngine
38
76
 
39
77
  @show_caret = !@show_caret
40
78
  end
79
+
80
+ keep_caret_visible
81
+ end
82
+
83
+ def button_down(id)
84
+ handle_keyboard_shortcuts(id)
85
+ end
86
+
87
+ def handle_keyboard_shortcuts(id)
88
+ return unless @focus && @enabled
89
+
90
+ if Gosu.button_down?(Gosu::KB_LEFT_CONTROL) || Gosu.button_down?(Gosu::KB_RIGHT_CONTROL)
91
+ case id
92
+ when Gosu::KB_A
93
+ @text_input.selection_start = 0
94
+ @text_input.caret_pos = @text_input.text.length
95
+
96
+ when Gosu::KB_C
97
+ if @text_input.selection_start < @text_input.caret_pos
98
+ Clipboard.copy(@text_input.text[@text_input.selection_start...@text_input.caret_pos])
99
+ else
100
+ Clipboard.copy(@text_input.text[@text_input.caret_pos...@text_input.selection_start])
101
+ end
102
+
103
+ when Gosu::KB_X
104
+ chars = @text_input.text.chars
105
+
106
+ if @text_input.selection_start < @text_input.caret_pos
107
+ Clipboard.copy(@text_input.text[@text_input.selection_start...@text_input.caret_pos])
108
+ chars.slice!(@text_input.selection_start, @text_input.caret_pos)
109
+ else
110
+ Clipboard.copy(@text_input.text[@text_input.caret_pos...@text_input.selection_start])
111
+ chars.slice!(@text_input.caret_pos, @text_input.selection_start)
112
+ end
113
+
114
+ @text_input.text = chars.join
115
+
116
+ when Gosu::KB_V
117
+ if instance_of?(EditLine) # EditLine assumes a single line of text
118
+ @text_input.text = @text_input.text.insert(@text_input.caret_pos,
119
+ Clipboard.paste.encode("UTF-8").gsub("\n", ""))
120
+ else
121
+ @text_input.text = @text_input.text.insert(@text_input.caret_pos, Clipboard.paste.encode("UTF-8"))
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ def caret_position_under_mouse(mouse_x)
128
+ 1.upto(@text.text.length) do |i|
129
+ return i - 1 if mouse_x < @text.x - @offset_x + @text.width(@text.text[0...i])
130
+ end
131
+
132
+ @text_input.text.length
133
+ end
134
+
135
+ def move_caret_to_mouse(mouse_x, _mouse_y)
136
+ @text_input.caret_pos = @text_input.selection_start = caret_position_under_mouse(mouse_x)
137
+ end
138
+
139
+ def keep_caret_visible
140
+ caret_pos = (caret_position - @text.x) + @caret_width
141
+
142
+ @last_text ||= "/\\"
143
+ @last_pos ||= -1
144
+
145
+ @last_text = @text.text
146
+ @last_pos = caret_pos
147
+
148
+ if caret_pos.between?(@offset_x, @width + @offset_x)
149
+ # Do nothing
150
+
151
+ elsif caret_pos < @offset_x
152
+ @offset_x = if caret_pos > @width
153
+ caret_pos + @width
154
+ else
155
+ 0
156
+ end
157
+
158
+ elsif caret_pos > @width
159
+ @offset_x = caret_pos - @width
160
+
161
+ else
162
+ # Reset to Zero
163
+ @offset_x = 0
164
+ end
165
+ end
166
+
167
+ def caret_position
168
+ text_input_position_for(:caret_pos)
169
+ end
170
+
171
+ def selection_start_position
172
+ text_input_position_for(:selection_start)
173
+ end
174
+
175
+ def text_input_position_for(method)
176
+ if @type == :password
177
+ @text.x + @text.width(default(:password_character) * @text_input.text[0...@text_input.send(method)].length)
178
+ else
179
+ @text.x + @text.width(@text_input.text[0...@text_input.send(method)])
180
+ end
41
181
  end
42
182
 
43
183
  def left_mouse_button(sender, x, y)
@@ -47,10 +187,12 @@ module CyberarmEngine
47
187
  @caret_last_interval = Gosu.milliseconds
48
188
  @show_caret = true
49
189
 
50
- return :handled
190
+ move_caret_to_mouse(x, y)
191
+
192
+ :handled
51
193
  end
52
194
 
53
- def enter(sender)
195
+ def enter(_sender)
54
196
  if @focus
55
197
  @style.background_canvas.background = default(:active, :background)
56
198
  @text.color = default(:active, :color)
@@ -59,33 +201,44 @@ module CyberarmEngine
59
201
  @text.color = default(:hover, :color)
60
202
  end
61
203
 
62
- return :handled
204
+ :handled
63
205
  end
64
206
 
65
207
  def leave(sender)
66
- unless @focus
67
- super
68
- end
208
+ super unless @focus
69
209
 
70
- return :handled
210
+ :handled
71
211
  end
72
212
 
73
- def blur(sender)
213
+ def blur(_sender)
74
214
  @focus = false
75
215
  @style.background_canvas.background = default(:background)
76
216
  @text.color = default(:color)
77
217
  window.text_input = nil
78
218
 
79
- return :handled
219
+ :handled
80
220
  end
81
221
 
82
- # TODO: Fix caret rendering in wrong position unless caret_pos is at end of text
83
- def caret_position
84
- if @type == :password
85
- @text.x + @text.textobject.text_width(default(:password_character) * @text_input.text[0..@text_input.caret_pos-1].length)
86
- else
87
- @text.x + @text.textobject.text_width(@text_input.text[0..@text_input.caret_pos-1])
88
- end
222
+ def draggable?(button)
223
+ button == :left
224
+ end
225
+
226
+ def begin_drag(_sender, x, _y, _button)
227
+ @drag_start = x
228
+ @offset_drag_start = @offset_x
229
+ @drag_caret_position = @text_input.caret_pos
230
+
231
+ :handled
232
+ end
233
+
234
+ def drag_update(_sender, x, _y, _button)
235
+ @text_input.caret_pos = caret_position_under_mouse(x)
236
+
237
+ :handled
238
+ end
239
+
240
+ def end_drag(_sender, _x, _y, _button)
241
+ :handled
89
242
  end
90
243
 
91
244
  def recalculate
@@ -100,4 +253,4 @@ module CyberarmEngine
100
253
  end
101
254
  end
102
255
  end
103
- end
256
+ end
@@ -1,8 +1,6 @@
1
1
  module CyberarmEngine
2
2
  class Element
3
3
  class Flow < Container
4
- include Common
5
-
6
4
  def layout
7
5
  @children.each do |child|
8
6
  if fits_on_line?(child)
@@ -14,4 +12,4 @@ module CyberarmEngine
14
12
  end
15
13
  end
16
14
  end
17
- end
15
+ end
@@ -6,7 +6,8 @@ module CyberarmEngine
6
6
  @path = path
7
7
 
8
8
  @image = Gosu::Image.new(path, retro: @options[:image_retro])
9
- @scale_x, @scale_y = 1, 1
9
+ @scale_x = 1
10
+ @scale_y = 1
10
11
  end
11
12
 
12
13
  def render
@@ -14,18 +15,19 @@ module CyberarmEngine
14
15
  @style.border_thickness_left + @style.padding_left + @x,
15
16
  @style.border_thickness_top + @style.padding_top + @y,
16
17
  @z + 2,
17
- @scale_x, @scale_y) # TODO: Add color support?
18
+ @scale_x, @scale_y, @style.color
19
+ )
18
20
  end
19
21
 
20
- def clicked_left_mouse_button(sender, x, y)
22
+ def clicked_left_mouse_button(_sender, _x, _y)
21
23
  @block.call(self) if @block
22
24
 
23
- return :handled
25
+ :handled
24
26
  end
25
27
 
26
28
  def recalculate
27
29
  _width = dimensional_size(@style.width, :width)
28
- _height= dimensional_size(@style.height,:height)
30
+ _height = dimensional_size(@style.height, :height)
29
31
 
30
32
  if _width && _height
31
33
  @scale_x = _width.to_f / @image.width
@@ -37,11 +39,12 @@ module CyberarmEngine
37
39
  @scale_y = _height.to_f / @image.height
38
40
  @scale_x = @scale_y
39
41
  else
40
- @scale_x, @scale_y = 1, 1
42
+ @scale_x = 1
43
+ @scale_y = 1
41
44
  end
42
45
 
43
- @width = _width ? _width : @image.width.round * @scale_x
44
- @height= _height ? _height : @image.height.round * @scale_y
46
+ @width = _width || @image.width.round * @scale_x
47
+ @height = _height || @image.height.round * @scale_y
45
48
  end
46
49
 
47
50
  def value
@@ -49,4 +52,4 @@ module CyberarmEngine
49
52
  end
50
53
  end
51
54
  end
52
- end
55
+ end
@@ -4,47 +4,128 @@ module CyberarmEngine
4
4
  def initialize(text, options = {}, block = nil)
5
5
  super(options, block)
6
6
 
7
- @text = Text.new(text, font: @options[:font], z: @z, color: @options[:color], size: @options[:text_size], shadow: @options[:text_shadow])
7
+ @text = Text.new(
8
+ text, font: @options[:font], z: @z, color: @options[:color],
9
+ size: @options[:text_size], shadow: @options[:text_shadow],
10
+ shadow_size: @options[:text_shadow_size],
11
+ shadow_color: @options[:text_shadow_color]
12
+ )
13
+
14
+ @raw_text = text
8
15
  end
9
16
 
10
17
  def render
11
18
  @text.draw
12
19
  end
13
20
 
14
- def clicked_left_mouse_button(sender, x, y)
15
- @block.call(self) if @block
21
+ def clicked_left_mouse_button(_sender, _x, _y)
22
+ @block&.call(self)
16
23
 
17
- return :handled
24
+ # return :handled
18
25
  end
19
26
 
20
27
  def recalculate
21
- @width, @height = 0, 0
28
+ @width = 0
29
+ @height = 0
30
+
31
+ _width = dimensional_size(@style.width, :width)
32
+ _height = dimensional_size(@style.height, :height)
22
33
 
23
- _width = dimensional_size(@style.width, :width)
24
- _height= dimensional_size(@style.height,:height)
34
+ handle_text_wrapping(_width)
25
35
 
26
- @width = _width ? _width : @text.width.round
27
- @height= _height ? _height : @text.height.round
36
+ @width = _width || @text.width.round
37
+ @height = _height || @text.height.round
28
38
 
29
- @text.x = @style.border_thickness_left + @style.padding_left + @x
30
- @text.y = @style.border_thickness_top + @style.padding_top + @y
39
+ @text.y = @style.border_thickness_top + @style.padding_top + @y
31
40
  @text.z = @z + 3
32
41
 
42
+ if (text_alignment = @options[:text_align])
43
+ case text_alignment
44
+ when :left
45
+ @text.x = @style.border_thickness_left + @style.padding_left + @x
46
+ when :center
47
+ @text.x = if @text.width <= outer_width
48
+ @x + outer_width / 2 - @text.width / 2
49
+ else # Act as left aligned
50
+ @style.border_thickness_left + @style.padding_left + @x
51
+ end
52
+ when :right
53
+ @text.x = @x + outer_width - (@text.width + @style.border_thickness_right + @style.padding_right)
54
+ end
55
+ end
56
+
33
57
  update_background
34
58
  end
35
59
 
60
+ def handle_text_wrapping(max_width)
61
+ max_width ||= @parent&.width
62
+ max_width ||= @x - (window.width + noncontent_width)
63
+ wrap_behavior = style.text_wrap
64
+ copy = @raw_text.to_s.dup
65
+
66
+ if max_width >= line_width(copy[0]) && line_width(copy) > max_width && wrap_behavior != :none
67
+ breaks = []
68
+ line_start = 0
69
+ line_end = copy.length
70
+
71
+ while line_start != copy.length
72
+ if line_width(copy[line_start...line_end]) > max_width
73
+ line_end = ((line_end - line_start) / 2.0)
74
+ elsif line_end < copy.length && line_width(copy[line_start...line_end + 1]) < max_width
75
+ # To small, grow!
76
+ # TODO: find a more efficient way
77
+ line_end += 1
78
+
79
+ else # FOUND IT!
80
+ entering_line_end = line_end.floor
81
+ max_reach = line_end.floor - line_start < 63 ? line_end.floor - line_start : 63
82
+ reach = 0
83
+
84
+ if wrap_behavior == :word_wrap
85
+ max_reach.times do |i|
86
+ reach = i
87
+ break if copy[line_end.floor - i].to_s.match(/[[:punct:]]| /)
88
+ end
89
+
90
+ puts "Max width: #{max_width}/#{line_width(@raw_text)} Reach: {#{reach}/#{max_reach}} Line Start: #{line_start}/#{line_end.floor} (#{copy.length}|#{@raw_text.length}) [#{entering_line_end}] '#{copy}' {#{copy[line_start...line_end]}}"
91
+ line_end = line_end.floor - reach + 1 if reach != max_reach # Add +1 to walk in front of punctuation
92
+ end
93
+
94
+ breaks << line_end.floor
95
+ line_start = line_end.floor
96
+ line_end = copy.length
97
+
98
+ break if entering_line_end == copy.length || reach == max_reach
99
+ end
100
+ end
101
+
102
+ breaks.each_with_index do |pos, index|
103
+ copy.insert(pos + index, "\n") if pos + index >= 0 && pos + index < copy.length
104
+ end
105
+ end
106
+
107
+ @text.text = copy
108
+ end
109
+
110
+ def line_width(text)
111
+ (@x + @text.textobject.markup_width(text) + noncontent_width)
112
+ end
113
+
36
114
  def value
37
- @text.text
115
+ @raw_text
38
116
  end
39
117
 
40
118
  def value=(value)
41
- @text.text = value
119
+ @raw_text = value.to_s.chomp
42
120
 
43
- old_width, old_height = width, height
121
+ old_width = width
122
+ old_height = height
44
123
  recalculate
45
124
 
46
125
  root.gui_state.request_recalculate if old_width != width || old_height != height
126
+
127
+ publish(:changed, self.value)
47
128
  end
48
129
  end
49
130
  end
50
- end
131
+ end