cyberarm_engine 0.1.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/.gitignore +8 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/cyberarm_engine.gemspec +36 -0
- data/lib/cyberarm_engine.rb +36 -0
- data/lib/cyberarm_engine/background.rb +166 -0
- data/lib/cyberarm_engine/common.rb +85 -0
- data/lib/cyberarm_engine/engine.rb +95 -0
- data/lib/cyberarm_engine/game_object.rb +256 -0
- data/lib/cyberarm_engine/game_state.rb +90 -0
- data/lib/cyberarm_engine/lib/bounding_box.rb +124 -0
- data/lib/cyberarm_engine/lib/vector.rb +97 -0
- data/lib/cyberarm_engine/objects/multi_line_text.rb +67 -0
- data/lib/cyberarm_engine/objects/text.rb +96 -0
- data/lib/cyberarm_engine/objects/timer.rb +23 -0
- data/lib/cyberarm_engine/ui/border_canvas.rb +101 -0
- data/lib/cyberarm_engine/ui/button.rb +53 -0
- data/lib/cyberarm_engine/ui/check_box.rb +52 -0
- data/lib/cyberarm_engine/ui/container.rb +154 -0
- data/lib/cyberarm_engine/ui/dsl.rb +82 -0
- data/lib/cyberarm_engine/ui/edit_line.rb +88 -0
- data/lib/cyberarm_engine/ui/element.rb +197 -0
- data/lib/cyberarm_engine/ui/event.rb +46 -0
- data/lib/cyberarm_engine/ui/flow.rb +15 -0
- data/lib/cyberarm_engine/ui/gui_state.rb +119 -0
- data/lib/cyberarm_engine/ui/image.rb +42 -0
- data/lib/cyberarm_engine/ui/label.rb +34 -0
- data/lib/cyberarm_engine/ui/stack.rb +11 -0
- data/lib/cyberarm_engine/ui/style.rb +15 -0
- data/lib/cyberarm_engine/ui/theme.rb +91 -0
- data/lib/cyberarm_engine/ui/toggle_button.rb +49 -0
- data/lib/cyberarm_engine/version.rb +4 -0
- metadata +137 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
module CyberarmEngine
|
2
|
+
class Container < Element
|
3
|
+
include Common
|
4
|
+
|
5
|
+
attr_accessor :stroke_color, :fill_color
|
6
|
+
attr_reader :children
|
7
|
+
attr_reader :scroll_x, :scroll_y
|
8
|
+
|
9
|
+
def initialize(options = {}, block = nil)
|
10
|
+
super
|
11
|
+
|
12
|
+
@scroll_x, @scroll_y = 0, 0
|
13
|
+
@scroll_speed = 10
|
14
|
+
|
15
|
+
@text_color = options[:color]
|
16
|
+
|
17
|
+
@children = []
|
18
|
+
|
19
|
+
@theme = {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def build
|
23
|
+
@theme.merge(@parent.theme) if @parent
|
24
|
+
@block.call(self) if @block
|
25
|
+
|
26
|
+
recalculate
|
27
|
+
end
|
28
|
+
|
29
|
+
def add(element)
|
30
|
+
@children << element
|
31
|
+
|
32
|
+
recalculate
|
33
|
+
end
|
34
|
+
|
35
|
+
def render
|
36
|
+
@children.each(&:draw)
|
37
|
+
end
|
38
|
+
|
39
|
+
def update
|
40
|
+
@children.each(&:update)
|
41
|
+
end
|
42
|
+
|
43
|
+
def theme
|
44
|
+
@theme
|
45
|
+
end
|
46
|
+
|
47
|
+
def color(color)
|
48
|
+
@theme[:color] = color
|
49
|
+
end
|
50
|
+
|
51
|
+
def hit_element?(x, y)
|
52
|
+
@children.reverse_each do |child|
|
53
|
+
case child
|
54
|
+
when Container
|
55
|
+
if element = child.hit_element?(x, y)
|
56
|
+
return element
|
57
|
+
end
|
58
|
+
else
|
59
|
+
return child if child.hit?(x, y)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
self if hit?(x, y)
|
64
|
+
end
|
65
|
+
|
66
|
+
def recalculate
|
67
|
+
@current_position = Vector.new(@margin_left, @margin_top)
|
68
|
+
|
69
|
+
layout
|
70
|
+
|
71
|
+
@width = @max_width ? @max_width : (@children.map {|c| c.x + c.width + c.margin_right }.max || 0).round
|
72
|
+
@height = @max_height ? @max_height : (@children.map {|c| c.y + c.height + c.margin_bottom}.max || 0).round
|
73
|
+
|
74
|
+
# Move child to parent after positioning
|
75
|
+
@children.each do |child|
|
76
|
+
child.x += @x
|
77
|
+
child.y += @y
|
78
|
+
|
79
|
+
# Fix child being displaced
|
80
|
+
child.recalculate
|
81
|
+
end
|
82
|
+
|
83
|
+
update_background
|
84
|
+
end
|
85
|
+
|
86
|
+
def layout
|
87
|
+
raise "Not overridden"
|
88
|
+
end
|
89
|
+
|
90
|
+
def max_width
|
91
|
+
@max_width ? @max_width : window.width - (@parent ? @parent.margin_right + @margin_right : @margin_right)
|
92
|
+
end
|
93
|
+
|
94
|
+
def fits_on_line?(element) # Flow
|
95
|
+
@current_position.x + element.outer_width <= max_width &&
|
96
|
+
@current_position.x + element.outer_width <= window.width
|
97
|
+
end
|
98
|
+
|
99
|
+
def position_on_current_line(element) # Flow
|
100
|
+
element.x = element.margin_left + @current_position.x
|
101
|
+
element.y = element.margin_top + @current_position.y
|
102
|
+
|
103
|
+
element.recalculate
|
104
|
+
|
105
|
+
@current_position.x += element.outer_width
|
106
|
+
@current_position.x = @margin_left if @current_position.x >= max_width
|
107
|
+
end
|
108
|
+
|
109
|
+
def tallest_neighbor(querier, y_position) # Flow
|
110
|
+
response = querier
|
111
|
+
@children.each do |child|
|
112
|
+
response = child if child.outer_height > response.outer_height
|
113
|
+
break if child == querier
|
114
|
+
end
|
115
|
+
|
116
|
+
return response
|
117
|
+
end
|
118
|
+
|
119
|
+
def position_on_next_line(child) # Flow
|
120
|
+
@current_position.x = @margin_left
|
121
|
+
@current_position.y += tallest_neighbor(child, @current_position.y).outer_height
|
122
|
+
|
123
|
+
child.x = child.margin_left + @current_position.x
|
124
|
+
child.y = child.margin_top + @current_position.y
|
125
|
+
|
126
|
+
child.recalculate
|
127
|
+
|
128
|
+
@current_position.x += child.outer_width
|
129
|
+
end
|
130
|
+
|
131
|
+
def move_to_next_line(element) # Stack
|
132
|
+
element.x = element.margin_left + @current_position.x
|
133
|
+
element.y = element.margin_top + @current_position.y
|
134
|
+
|
135
|
+
element.recalculate
|
136
|
+
|
137
|
+
@current_position.y += element.outer_height
|
138
|
+
end
|
139
|
+
|
140
|
+
# def mouse_wheel_up(sender, x, y)
|
141
|
+
# @children.each {|c| c.y -= @scroll_speed}
|
142
|
+
# @children.each {|c| c.recalculate}
|
143
|
+
# end
|
144
|
+
|
145
|
+
# def mouse_wheel_down(sender, x, y)
|
146
|
+
# @children.each {|c| c.y += @scroll_speed}
|
147
|
+
# @children.each {|c| c.recalculate}
|
148
|
+
# end
|
149
|
+
|
150
|
+
def value
|
151
|
+
@children.map {|c| c.class}.join(", ")
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module CyberarmEngine
|
2
|
+
module DSL
|
3
|
+
def flow(options = {}, &block)
|
4
|
+
options[:parent] = @containers.last
|
5
|
+
_container = Flow.new(options, block)
|
6
|
+
@containers << _container
|
7
|
+
_container.build
|
8
|
+
options[:parent].add(_container)
|
9
|
+
@containers.pop
|
10
|
+
|
11
|
+
return _container
|
12
|
+
end
|
13
|
+
|
14
|
+
def stack(options = {}, &block)
|
15
|
+
options[:parent] = @containers.last
|
16
|
+
_container = Stack.new(options, block)
|
17
|
+
@containers << _container
|
18
|
+
_container.build
|
19
|
+
options[:parent].add(_container)
|
20
|
+
@containers.pop
|
21
|
+
|
22
|
+
return _container
|
23
|
+
end
|
24
|
+
|
25
|
+
def label(text, options = {}, &block)
|
26
|
+
options[:parent] = @containers.last
|
27
|
+
_element = Label.new(text, options, block)
|
28
|
+
@containers.last.add(_element)
|
29
|
+
|
30
|
+
return _element
|
31
|
+
end
|
32
|
+
|
33
|
+
def button(text, options = {}, &block)
|
34
|
+
options[:parent] = @containers.last
|
35
|
+
_element = Button.new(text, options, block) { if block.is_a?(Proc); block.call; end }
|
36
|
+
@containers.last.add(_element)
|
37
|
+
|
38
|
+
return _element
|
39
|
+
end
|
40
|
+
|
41
|
+
def edit_line(text, options = {}, &block)
|
42
|
+
options[:parent] = @containers.last
|
43
|
+
_element = EditLine.new(text, options, block)
|
44
|
+
@containers.last.add(_element)
|
45
|
+
|
46
|
+
return _element
|
47
|
+
end
|
48
|
+
|
49
|
+
def toggle_button(options = {}, &block)
|
50
|
+
options[:parent] = @containers.last
|
51
|
+
_element = ToggleButton.new(options, block)
|
52
|
+
@containers.last.add(_element)
|
53
|
+
|
54
|
+
return _element
|
55
|
+
end
|
56
|
+
|
57
|
+
def check_box(text, options = {}, &block)
|
58
|
+
options[:parent] = @containers.last
|
59
|
+
_element = CheckBox.new(text, options, block)
|
60
|
+
@containers.last.add(_element)
|
61
|
+
|
62
|
+
return _element
|
63
|
+
end
|
64
|
+
|
65
|
+
def image(path, options = {}, &block)
|
66
|
+
options[:parent] = @containers.last
|
67
|
+
_element = Image.new(path, options, block)
|
68
|
+
@containers.last.add(_element)
|
69
|
+
|
70
|
+
return _element
|
71
|
+
end
|
72
|
+
|
73
|
+
def background(color = Gosu::Color::NONE)
|
74
|
+
@containers.last.background = color
|
75
|
+
end
|
76
|
+
|
77
|
+
# Foreground color, e.g. Text
|
78
|
+
def color(color)
|
79
|
+
@containers.last.color(color)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module CyberarmEngine
|
2
|
+
class EditLine < Button
|
3
|
+
def initialize(text, options = {}, block = nil)
|
4
|
+
super(text, options, block)
|
5
|
+
|
6
|
+
@type = default(:type)
|
7
|
+
|
8
|
+
|
9
|
+
@caret_width = default(:caret_width)
|
10
|
+
@caret_height= @text.height
|
11
|
+
@caret_color = default(:caret_color)
|
12
|
+
@caret_interval = default(:caret_interval)
|
13
|
+
@caret_last_interval = Gosu.milliseconds
|
14
|
+
@show_caret = true
|
15
|
+
|
16
|
+
@text_input = Gosu::TextInput.new
|
17
|
+
@text_input.text = text
|
18
|
+
|
19
|
+
return self
|
20
|
+
end
|
21
|
+
|
22
|
+
def render
|
23
|
+
Gosu.clip_to(@text.x, @text.y, @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
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def update
|
30
|
+
if @type == :password
|
31
|
+
@text.text = default(:password_character) * @text_input.text.length
|
32
|
+
else
|
33
|
+
@text.text = @text_input.text
|
34
|
+
end
|
35
|
+
|
36
|
+
if Gosu.milliseconds >= @caret_last_interval + @caret_interval
|
37
|
+
@caret_last_interval = Gosu.milliseconds
|
38
|
+
|
39
|
+
@show_caret = !@show_caret
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def left_mouse_button(sender, x, y)
|
44
|
+
super
|
45
|
+
window.text_input = @text_input
|
46
|
+
end
|
47
|
+
|
48
|
+
def enter(sender)
|
49
|
+
if @focus
|
50
|
+
@background_canvas.background = default(:active, :background)
|
51
|
+
@text.color = default(:active, :color)
|
52
|
+
else
|
53
|
+
@background_canvas.background = default(:hover, :background)
|
54
|
+
@text.color = default(:hover, :color)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def leave(sender)
|
59
|
+
end
|
60
|
+
|
61
|
+
def blur(sender)
|
62
|
+
@focus = false
|
63
|
+
@background_canvas.background = default(:background)
|
64
|
+
@text.color = default(:color)
|
65
|
+
window.text_input = nil
|
66
|
+
end
|
67
|
+
|
68
|
+
# TODO: Fix caret rendering in wrong position unless caret_pos is at end of text
|
69
|
+
def caret_position
|
70
|
+
if @type == :password
|
71
|
+
@text.x + @text.textobject.text_width(default(:password_character) * @text_input.text[0..@text_input.caret_pos-1].length)
|
72
|
+
else
|
73
|
+
@text.x + @text.textobject.text_width(@text_input.text[0..@text_input.caret_pos-1])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def recalculate
|
78
|
+
super
|
79
|
+
|
80
|
+
@width = default(:width)
|
81
|
+
update_background
|
82
|
+
end
|
83
|
+
|
84
|
+
def value
|
85
|
+
@text_input.text
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
module CyberarmEngine
|
2
|
+
class Element
|
3
|
+
include Theme
|
4
|
+
include Event
|
5
|
+
include Common
|
6
|
+
|
7
|
+
attr_accessor :x, :y, :z, :enabled
|
8
|
+
attr_reader :width, :height, :parent, :options, :event_handler, :background_canvas, :border_canvas
|
9
|
+
|
10
|
+
attr_reader :border_thickness, :border_thickness_left, :border_thickness_right, :border_thickness_top, :border_thickness_bottom
|
11
|
+
attr_reader :border_color, :border_color_left, :border_color_right, :border_color_top, :border_color_bottom
|
12
|
+
|
13
|
+
attr_reader :padding, :padding_left, :padding_right, :padding_top, :padding_bottom
|
14
|
+
attr_reader :margin, :margin_left, :margin_right, :margin_top, :margin_bottom
|
15
|
+
|
16
|
+
def initialize(options = {}, block = nil)
|
17
|
+
@parent = options[:parent] # parent Container (i.e. flow/stack)
|
18
|
+
options = theme_defaults.merge(options)
|
19
|
+
@options = options
|
20
|
+
@block = block
|
21
|
+
|
22
|
+
@style = Style.new(options)
|
23
|
+
@focus = false
|
24
|
+
@background_canvas = Background.new
|
25
|
+
@border_canvas = BorderCanvas.new(element: self)
|
26
|
+
|
27
|
+
@x = default(:x)
|
28
|
+
@y = default(:y)
|
29
|
+
@z = default(:z)
|
30
|
+
|
31
|
+
@fixed_x = @x if @x != 0
|
32
|
+
@fixed_y = @y if @y != 0
|
33
|
+
|
34
|
+
@width = default(:width) || $window.width
|
35
|
+
@height = default(:height) || $window.height
|
36
|
+
|
37
|
+
set_border_thickness(default(:border_thickness))
|
38
|
+
|
39
|
+
set_padding(default(:padding))
|
40
|
+
|
41
|
+
set_margin(default(:margin))
|
42
|
+
|
43
|
+
set_background(default(:background))
|
44
|
+
set_border_color(default(:border_color))
|
45
|
+
|
46
|
+
raise "#{self.class} 'x' must be a number" unless @x.is_a?(Numeric)
|
47
|
+
raise "#{self.class} 'y' must be a number" unless @y.is_a?(Numeric)
|
48
|
+
raise "#{self.class} 'z' must be a number" unless @z.is_a?(Numeric)
|
49
|
+
raise "#{self.class} 'width' must be a number" unless @width.is_a?(Numeric) || @width.nil?
|
50
|
+
raise "#{self.class} 'height' must be a number" unless @height.is_a?(Numeric) || @height.nil?
|
51
|
+
raise "#{self.class} 'options' must be a Hash" unless @options.is_a?(Hash)
|
52
|
+
|
53
|
+
# raise "#{self.class} 'padding' must be a number" unless @padding.is_a?(Numeric)
|
54
|
+
|
55
|
+
@enabled = true
|
56
|
+
|
57
|
+
default_events
|
58
|
+
end
|
59
|
+
|
60
|
+
def set_background(background)
|
61
|
+
@background = background
|
62
|
+
@background_canvas.background = background
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_border_thickness(border_thickness)
|
66
|
+
@border_thickness = border_thickness
|
67
|
+
|
68
|
+
@border_thickness_left = default(:border_thickness_left) || @border_thickness
|
69
|
+
@border_thickness_right = default(:border_thickness_right) || @border_thickness
|
70
|
+
@border_thickness_top = default(:border_thickness_top) || @border_thickness
|
71
|
+
@border_thickness_bottom = default(:border_thickness_bottom) || @border_thickness
|
72
|
+
end
|
73
|
+
|
74
|
+
def set_border_color(color)
|
75
|
+
@border_color = color
|
76
|
+
|
77
|
+
@border_color_left = default(:border_color_left) || @border_color
|
78
|
+
@border_color_right = default(:border_color_right) || @border_color
|
79
|
+
@border_color_top = default(:border_color_top) || @border_color
|
80
|
+
@border_color_bottom = default(:border_color_bottom) || @border_color
|
81
|
+
|
82
|
+
@border_canvas.color = color
|
83
|
+
end
|
84
|
+
|
85
|
+
def set_padding(padding)
|
86
|
+
@padding = padding
|
87
|
+
|
88
|
+
@padding_left = default(:padding_left) || @padding
|
89
|
+
@padding_right = default(:padding_right) || @padding
|
90
|
+
@padding_top = default(:padding_top) || @padding
|
91
|
+
@padding_bottom = default(:padding_bottom) || @padding
|
92
|
+
end
|
93
|
+
|
94
|
+
def set_margin(margin)
|
95
|
+
@margin = margin
|
96
|
+
|
97
|
+
@margin_left = default(:margin_left) || @margin
|
98
|
+
@margin_right = default(:margin_right) || @margin
|
99
|
+
@margin_top = default(:margin_top) || @margin
|
100
|
+
@margin_bottom = default(:margin_bottom) || @margin
|
101
|
+
end
|
102
|
+
|
103
|
+
def default_events
|
104
|
+
[:left, :middle, :right].each do |button|
|
105
|
+
event(:"#{button}_mouse_button")
|
106
|
+
event(:"released_#{button}_mouse_button")
|
107
|
+
event(:"clicked_#{button}_mouse_button")
|
108
|
+
event(:"holding_#{button}_mouse_button")
|
109
|
+
end
|
110
|
+
|
111
|
+
event(:mouse_wheel_up)
|
112
|
+
event(:mouse_wheel_down)
|
113
|
+
|
114
|
+
event(:enter)
|
115
|
+
event(:hover)
|
116
|
+
event(:leave)
|
117
|
+
|
118
|
+
event(:blur)
|
119
|
+
end
|
120
|
+
|
121
|
+
def enabled?
|
122
|
+
@enabled
|
123
|
+
end
|
124
|
+
|
125
|
+
def draw
|
126
|
+
@background_canvas.draw
|
127
|
+
@border_canvas.draw
|
128
|
+
render
|
129
|
+
end
|
130
|
+
|
131
|
+
def update
|
132
|
+
end
|
133
|
+
|
134
|
+
def button_down(id)
|
135
|
+
end
|
136
|
+
|
137
|
+
def button_up(id)
|
138
|
+
end
|
139
|
+
|
140
|
+
def render
|
141
|
+
end
|
142
|
+
|
143
|
+
def hit?(x, y)
|
144
|
+
x.between?(@x, @x + width) &&
|
145
|
+
y.between?(@y, @y + height)
|
146
|
+
end
|
147
|
+
|
148
|
+
def width
|
149
|
+
(@border_thickness_left + @padding_left) + @width + (@padding_right + @border_thickness_right)
|
150
|
+
end
|
151
|
+
|
152
|
+
def outer_width
|
153
|
+
@margin_left + width + @margin_right
|
154
|
+
end
|
155
|
+
|
156
|
+
def height
|
157
|
+
(@border_thickness_top + @padding_top) + @height + (@padding_bottom + @border_thickness_bottom)
|
158
|
+
end
|
159
|
+
|
160
|
+
def outer_height
|
161
|
+
@margin_top + height + @margin_bottom
|
162
|
+
end
|
163
|
+
|
164
|
+
def style(hash)
|
165
|
+
if hash
|
166
|
+
@style.set(hash)
|
167
|
+
else
|
168
|
+
@style.hash
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def background=(_background)
|
173
|
+
@background_canvas.background=(_background)
|
174
|
+
update_background
|
175
|
+
end
|
176
|
+
|
177
|
+
def update_background
|
178
|
+
@background_canvas.x = @x
|
179
|
+
@background_canvas.y = @y
|
180
|
+
@background_canvas.z = @z
|
181
|
+
@background_canvas.width = width
|
182
|
+
@background_canvas.height = height
|
183
|
+
|
184
|
+
@background_canvas.update
|
185
|
+
|
186
|
+
@border_canvas.update
|
187
|
+
end
|
188
|
+
|
189
|
+
def recalculate
|
190
|
+
raise "#{self.class}#recalculate was not overridden!"
|
191
|
+
end
|
192
|
+
|
193
|
+
def value
|
194
|
+
raise "#{self.class}#value was not overridden!"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|