metro 0.2.5 → 0.2.6

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 (35) hide show
  1. data/changelog.md +11 -0
  2. data/lib/assets/menu-movement.wav +0 -0
  3. data/lib/assets/menu-selection.wav +0 -0
  4. data/lib/metro.rb +1 -0
  5. data/lib/metro/asset_path.rb +27 -9
  6. data/lib/metro/events/event_dictionary.rb +1 -2
  7. data/lib/metro/font.rb +66 -0
  8. data/lib/metro/image.rb +4 -3
  9. data/lib/metro/models/draws.rb +8 -7
  10. data/lib/metro/models/model.rb +8 -5
  11. data/lib/metro/models/model_factory.rb +4 -6
  12. data/lib/metro/models/properties/animation_property.rb +1 -1
  13. data/lib/metro/models/properties/array_property.rb +24 -0
  14. data/lib/metro/models/properties/boolean_property.rb +27 -0
  15. data/lib/metro/models/properties/font_property.rb +5 -29
  16. data/lib/metro/models/properties/options_property/no_option.rb +29 -0
  17. data/lib/metro/models/properties/options_property/options.rb +94 -0
  18. data/lib/metro/models/properties/options_property/options_property.rb +125 -0
  19. data/lib/metro/models/properties/property.rb +14 -5
  20. data/lib/metro/models/properties/property_owner.rb +1 -0
  21. data/lib/metro/models/ui/generic.rb +22 -5
  22. data/lib/metro/models/ui/grid_drawer.rb +25 -15
  23. data/lib/metro/models/ui/image.rb +7 -3
  24. data/lib/metro/models/ui/label.rb +25 -15
  25. data/lib/metro/models/ui/menu.rb +106 -95
  26. data/lib/metro/models/ui/rectangle.rb +21 -8
  27. data/lib/metro/version.rb +1 -1
  28. data/spec/metro/models/properties/array_property_spec.rb +60 -0
  29. data/spec/metro/models/properties/{color_spec.rb → color_property_spec.rb} +0 -0
  30. data/spec/metro/models/properties/{font_spec.rb → font_property_spec.rb} +16 -18
  31. data/spec/metro/models/properties/options_property/no_option_spec.rb +25 -0
  32. data/spec/metro/models/properties/options_property/options_property_spec.rb +133 -0
  33. data/spec/metro/models/properties/options_property/options_spec.rb +125 -0
  34. data/spec/metro/models/ui/label_spec.rb +56 -15
  35. metadata +29 -11
@@ -8,141 +8,152 @@ module Metro
8
8
  #
9
9
  # @note Only one 'menu' can be defined for a given scene
10
10
  #
11
+ # @example Creating a menu with basic options
12
+ #
13
+ # menu:
14
+ # model: metro::ui::menu
15
+ # position: "472.0,353.0,5.0"
16
+ # alpha: 0
17
+ # unselected_color: "rgba(119,119,119,1.0)"
18
+ # selected_color: "rgba(255,255,255,1.0)"
19
+ # options: [ 'Start Game', 'Exit' ]
20
+ #
21
+ # @example Creating a menu with a selected item
22
+ #
23
+ # menu:
24
+ # model: metro::ui::menu
25
+ # position: "472.0,353.0,5.0"
26
+ # alpha: 0
27
+ # unselected_color: "rgba(119,119,119,1.0)"
28
+ # selected_color: "rgba(255,255,255,1.0)"
29
+ # options:
30
+ # selected: 0
31
+ # items: [ 'Start Game', 'Exit' ]
32
+ #
33
+ #
34
+ # @example Creating a menu with complex options
35
+ #
36
+ # menu:
37
+ # model: metro::ui::menu
38
+ # position: "472.0,353.0,5.0"
39
+ # alpha: 0
40
+ # layout: vertical
41
+ # # layout: horizontal
42
+ # unselected_color: "rgba(119,119,119,1.0)"
43
+ # selected_color: "rgba(255,255,255,1.0)"
44
+ # options:
45
+ # selected: 1
46
+ # items:
47
+ # -
48
+ # model: "metro::ui::label"
49
+ # text: "Start Game"
50
+ # action: start_game
51
+ # -
52
+ # model: metro::ui::label
53
+ # text: Exit
54
+ # action: exit_game
55
+ #
56
+ #
11
57
  class Menu < Model
12
58
 
13
59
  property :position, default: Game.center
60
+ property :alpha, default: 255
14
61
 
15
62
  property :scale, default: Scale.one
16
63
 
17
- property :padding, type: :numeric, default: 40
18
-
19
- property :unselected_color, type: :color, default: "rgba(119,119,119,1.0)"
20
- property :selected_color, type: :color, default: "rgba(255,255,255,1.0)"
64
+ property :padding, default: 10
21
65
 
22
66
  property :dimensions do
23
- width = font.text_width(longest_option_text)
24
- height = options.length * font.height + (options.length - 1) * padding
25
- Dimensions.of width, height
26
- end
27
-
28
- # This is a temporary method as there is no options propery yet defined
29
- def options
30
- properties[:options]
31
- end
32
-
33
- def alpha
34
- self.unselected_color_alpha
67
+ Dimensions.none
35
68
  end
36
69
 
37
- def alpha=(value)
38
- self.unselected_color_alpha = value.floor
39
- self.selected_color_alpha = value.floor
40
- end
70
+ property :options
41
71
 
42
- property :font
72
+ property :unselected_color, type: :color, default: "rgba(119,119,119,1.0)"
73
+ property :selected_color, type: :color, default: "rgba(255,255,255,1.0)"
43
74
 
44
- event :on_up, KbLeft, GpLeft, KbUp, GpUp do
45
- previous_option
75
+ def alpha_changed(alpha)
76
+ adjust_alpha_on_colors(alpha)
77
+ options.each { |option| option.alpha = alpha.floor }
46
78
  end
47
79
 
48
- event :on_up, KbRight, GpRight, KbDown, GpDown do
49
- next_option
80
+ def adjust_alpha_on_colors(alpha)
81
+ self.selected_color_alpha = alpha
82
+ self.unselected_color_alpha = alpha
50
83
  end
51
84
 
52
- event :on_up, KbEnter, KbReturn, GpButton0 do
53
- selection
54
- end
85
+ property :selection_sample, type: :sample, path: "menu-selection.wav"
86
+ property :movement_sample, type: :sample, path: "menu-movement.wav"
55
87
 
56
- attr_reader :selected_index, :menu_options
88
+ property :enabled, type: :boolean, default: true
57
89
 
58
- def after_initialize
59
- @selected_index = 0
60
- end
90
+ # Allows the menu to be layouted out horizontal or vertical
91
+ property :layout, type: :text, default: "vertical"
61
92
 
62
- def window=(value)
63
- @window = value
64
- @menu_options = options.map {|option| Option.new option }
93
+ def contains?(x,y)
94
+ bounds.contains?(x,y)
65
95
  end
66
96
 
67
- def selection
68
- scene_method = option_at_index(selected_index).method
69
- scene.send scene_method
97
+ def bounds
98
+ Bounds.new x: x, y: y, width: width, height: height
70
99
  end
71
100
 
72
- def previous_option
73
- @selected_index = @selected_index - 1
74
- @selected_index = options.length - 1 if @selected_index <= -1
75
- end
101
+ # @TODO: enable the user to define the events for this interaction
102
+ #################################################################
76
103
 
77
- def next_option
78
- @selected_index = @selected_index + 1
79
- @selected_index = 0 if @selected_index >= options.length
104
+ event :on_up, KbLeft, GpLeft, KbUp, GpUp do
105
+ if enabled
106
+ movement_sample.play
107
+ options.previous!
108
+ update_options
109
+ end
80
110
  end
81
111
 
82
- def font
83
- @font ||= Gosu::Font.new(window, Gosu::default_font_name, 20)
112
+ event :on_up, KbRight, GpRight, KbDown, GpDown do
113
+ if enabled
114
+ movement_sample.play
115
+ options.next!
116
+ update_options
117
+ end
84
118
  end
85
119
 
86
- def contains?(x,y)
87
- bounds.contains?(x,y)
120
+ event :on_up, KbEnter, KbReturn, GpButton0 do
121
+ if enabled
122
+ selection_sample.play
123
+ scene.send options.selected_action
124
+ end
88
125
  end
89
126
 
90
- def bounds
91
- Bounds.new x: x, y: y, width: width, height: height
92
- end
127
+ #################################################################
93
128
 
94
- def longest_option_text
95
- longest = options.map {|opt| opt }.inject("") {|longest,opt| opt.length > longest.length ? opt : longest }
96
- end
129
+ def show
130
+ adjust_alpha_on_colors(alpha)
97
131
 
98
- def option_at_index(index)
99
- menu_options[index]
100
- end
132
+ previous_width = 0
101
133
 
102
- def draw
103
134
  options.each_with_index do |option,index|
135
+ option.color = unselected_color
136
+ option.scale = scale
104
137
 
105
- option_name = option_at_index(index).name
138
+ option_x = x + (layout == "horizontal" ? (previous_width + padding) * index : 0)
139
+ previous_width = option.width
140
+ option_y = y + (layout == "vertical" ? (option.height + padding) * index : 0)
141
+ option_z = z
106
142
 
107
- draw_color = unselected_color
108
- draw_color = selected_color if index == selected_index
143
+ option.position = option.position + Point.at(option_x,option_y,option_z)
109
144
 
110
- y_position = y + padding * index
111
- font.draw option_name, x, y_position, z_order, x_factor, y_factor, draw_color
112
145
  end
146
+
147
+ options.selected.color = selected_color
113
148
  end
114
149
 
115
- #
116
- # The Option represents a choice within the menu.
117
- #
118
- class Option
119
-
120
- #
121
- # The raw data that was used to create the option.
122
- #
123
- attr_reader :data
124
-
125
- #
126
- # The human readable name of the option.
127
- #
128
- attr_accessor :name
129
-
130
- #
131
- # The method to execute within the scene when the option is selected.
132
- #
133
- attr_accessor :method
134
-
135
- def initialize(data)
136
- @data = data
137
-
138
- if data.is_a?(Hash)
139
- @name = data.keys.first
140
- @method = data.values.first
141
- else
142
- @name = data
143
- @method = data.to_s.downcase.gsub(/\s/,'_').gsub(/^[^a-zA-Z]*/,'').gsub(/[^a-zA-Z0-9\s_]/,'')
144
- end
145
- end
150
+ def update_options
151
+ options.unselected.each { |option| option.color = unselected_color }
152
+ options.selected.color = selected_color
153
+ end
154
+
155
+ def draw
156
+ options.each_with_index { |label| label.draw }
146
157
  end
147
158
 
148
159
  end
@@ -1,6 +1,17 @@
1
1
  module Metro
2
2
  module UI
3
3
 
4
+ #
5
+ # The rectangle will draw a rectangle from the specified position out to the specified
6
+ # dimensions in the specified color.
7
+ #
8
+ # @example Drawing a red rectangle that starts at (20,20) and is 200 by 200
9
+ #
10
+ # class IntroScene < GameScene
11
+ # draw :backdrop, model: "metro::ui::rectangle", position: "20,20",
12
+ # color: "rgba(255,0,0,1.0)", dimensions: "200,200"
13
+ # end
14
+ #
4
15
  class Rectangle < ::Metro::Model
5
16
 
6
17
  property :position
@@ -8,10 +19,19 @@ module Metro
8
19
  property :color
9
20
 
10
21
  property :dimensions do
11
- # By default the dimensions of the rectangle will be the size of the window
12
22
  window.dimensions
13
23
  end
14
24
 
25
+ def draw
26
+ window.draw_quad(left_x,top_y,color,
27
+ right_x,top_y,color,
28
+ right_x,bottom_y,color,
29
+ left_x,bottom_y,color,
30
+ z_order)
31
+ end
32
+
33
+ private
34
+
15
35
  def left_x
16
36
  x
17
37
  end
@@ -28,13 +48,6 @@ module Metro
28
48
  y + height
29
49
  end
30
50
 
31
- def draw
32
- window.draw_quad(left_x,top_y,color,
33
- right_x,top_y,color,
34
- right_x,bottom_y,color,
35
- left_x,bottom_y,color,
36
- z_order)
37
- end
38
51
  end
39
52
  end
40
53
  end
data/lib/metro/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Metro
2
- VERSION = "0.2.5"
2
+ VERSION = "0.2.6"
3
3
  WEBSITE = "https://github.com/burtlo/metro"
4
4
  CONTACT_EMAILS = ["franklin.webber@gmail.com"]
5
5
 
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metro::Model::ArrayProperty do
4
+
5
+ subject { described_class.new model }
6
+ let(:model) { "model" }
7
+
8
+ describe "#get" do
9
+ context "when the value is nil" do
10
+
11
+ context "when no default value has been specified" do
12
+ let(:expected) { [] }
13
+
14
+ it "should return an empty array" do
15
+ subject.get(nil).should eq expected
16
+ end
17
+ end
18
+
19
+ context "when a default value has been specified" do
20
+ subject { described_class.new model, default: expected }
21
+ let(:expected) { [:default_1,:default_2] }
22
+
23
+ it "should return the specified default" do
24
+ subject.get(nil).should eq expected
25
+ end
26
+ end
27
+
28
+ end
29
+ end
30
+
31
+ describe "#set" do
32
+ context "when the value is nil" do
33
+ let(:expected) { [] }
34
+
35
+ it "should return an empty array" do
36
+ subject.set(nil).should eq expected
37
+ end
38
+ end
39
+
40
+ context "when the value contains symbols" do
41
+ let(:input) { [ :a, :b, :c ] }
42
+ let(:expected) { input }
43
+
44
+ it "should not convert the items to strings" do
45
+ subject.set(input).should eq expected
46
+ end
47
+ end
48
+
49
+ context "when the value contains numbers" do
50
+ let(:input) { [ 1, 5.4, 0xABAB ] }
51
+ let(:expected) { input }
52
+
53
+ it "should not convert the items to strings" do
54
+ subject.set(input).should eq expected
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -7,14 +7,16 @@ describe Metro::Model::FontProperty do
7
7
  let(:window) { mock('window') }
8
8
 
9
9
  describe "#get" do
10
+ let(:expected_font) { stub('font') }
11
+ let(:expected_options) { { name: expected_font_name, size: expected_font_size, window: window } }
12
+
10
13
  context "when the value is nil" do
11
14
  context "when no default value has been specified" do
12
15
  let(:expected_font_name) { Gosu::default_font_name }
13
16
  let(:expected_font_size) { 40 }
14
- let(:expected_font) { stub('font') }
15
-
17
+
16
18
  it "should return the default value" do
17
- described_class.stub(:create_font).with(window,expected_font_name,expected_font_size) { expected_font }
19
+ described_class.stub(:font_for).with(expected_options) { expected_font }
18
20
  subject.get(nil).should eq expected_font
19
21
  end
20
22
  end
@@ -26,10 +28,9 @@ describe Metro::Model::FontProperty do
26
28
 
27
29
  let(:expected_font_name) { 'Times New Roman' }
28
30
  let(:expected_font_size) { 60 }
29
- let(:expected_font) { stub('font') }
30
31
 
31
32
  it "should return the specified default value" do
32
- described_class.stub(:create_font).with(window,expected_font_name,expected_font_size) { expected_font }
33
+ described_class.stub(:font_for).with(expected_options) { expected_font }
33
34
  subject.get(nil).should eq expected_font
34
35
  end
35
36
  end
@@ -38,13 +39,11 @@ describe Metro::Model::FontProperty do
38
39
  context "when the value is a hash" do
39
40
  let(:expected_font_name) { 'Helvetica' }
40
41
  let(:expected_font_size) { 80 }
41
- let(:expected_font) { stub('font') }
42
-
42
+
43
43
  let(:font_hash) { { name: expected_font_name, size: expected_font_size } }
44
-
45
-
44
+
46
45
  it "should return the font value" do
47
- described_class.stub(:create_font).with(window,expected_font_name,expected_font_size) { expected_font }
46
+ described_class.stub(:font_for).with(expected_options) { expected_font }
48
47
  subject.get(font_hash).should eq expected_font
49
48
  end
50
49
  end
@@ -52,10 +51,9 @@ describe Metro::Model::FontProperty do
52
51
  context "when the same font is requested" do
53
52
  let(:expected_font_name) { Gosu::default_font_name }
54
53
  let(:expected_font_size) { 40 }
55
- let(:expected_font) { stub('font') }
56
-
54
+
57
55
  it "should not be created a second time (pullled from memory)" do
58
- described_class.should_receive(:create_font).once.and_return(expected_font)
56
+ described_class.should_receive(:font_for).twice { expected_font }
59
57
  subject.get(nil)
60
58
  subject.get(nil)
61
59
  end
@@ -72,10 +70,10 @@ describe Metro::Model::FontProperty do
72
70
  let(:expected_font) { stub('font') }
73
71
 
74
72
  let(:expected_result) { { name: expected_font_name, size: expected_font_size } }
75
-
73
+ let(:expected_options) { { name: expected_font_name, size: expected_font_size, window: window } }
76
74
 
77
75
  it "should return a hash of the default value" do
78
- described_class.stub(:create_font).with(window,expected_font_name,expected_font_size) { expected_font }
76
+ described_class.stub(:font_for).with(expected_options) { expected_font }
79
77
  subject.set(nil).should eq expected_result
80
78
  end
81
79
  end
@@ -100,7 +98,7 @@ describe Metro::Model::FontProperty do
100
98
 
101
99
  let(:gosu_font) do
102
100
  font = stub('font', name: expected_font_name, height: expected_font_size)
103
- font.stub(:class) { Gosu::Font }
101
+ font.stub(:class) { Metro::Font }
104
102
  font
105
103
  end
106
104
 
@@ -115,11 +113,11 @@ describe Metro::Model::FontProperty do
115
113
  end
116
114
 
117
115
  context "when the value is a hash" do
118
-
116
+
119
117
  let(:expected_font_name) { 'Wingdings' }
120
118
  let(:expected_font_size) { 33 }
121
119
  let(:expected_result) { { name: expected_font_name, size: expected_font_size } }
122
-
120
+
123
121
  it "should return the hash representation of the font" do
124
122
  subject.set(expected_result).should eq expected_result
125
123
  end