glimmer-dsl-swt 4.18.3.1 → 4.18.4.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 +4 -4
- data/CHANGELOG.md +55 -1
- data/README.md +739 -324
- data/VERSION +1 -1
- data/glimmer-dsl-swt.gemspec +9 -4
- data/lib/ext/rouge/themes/glimmer.rb +29 -0
- data/lib/glimmer-dsl-swt.rb +0 -1
- data/lib/glimmer/data_binding/table_items_binding.rb +8 -5
- data/lib/glimmer/data_binding/widget_binding.rb +9 -1
- data/lib/glimmer/dsl/swt/image_expression.rb +14 -6
- data/lib/glimmer/dsl/swt/layout_data_expression.rb +4 -4
- data/lib/glimmer/dsl/swt/layout_expression.rb +5 -3
- data/lib/glimmer/swt/custom/code_text.rb +171 -53
- data/lib/glimmer/swt/custom/drawable.rb +4 -6
- data/lib/glimmer/swt/custom/shape.rb +69 -12
- data/lib/glimmer/swt/date_time_proxy.rb +1 -3
- data/lib/glimmer/swt/display_proxy.rb +12 -1
- data/lib/glimmer/swt/font_proxy.rb +1 -0
- data/lib/glimmer/swt/image_proxy.rb +79 -1
- data/lib/glimmer/swt/shell_proxy.rb +13 -1
- data/lib/glimmer/swt/table_proxy.rb +43 -15
- data/lib/glimmer/swt/widget_proxy.rb +11 -2
- data/lib/glimmer/ui/custom_shell.rb +1 -0
- data/lib/glimmer/ui/custom_widget.rb +10 -3
- data/samples/elaborate/meta_sample.rb +1 -1
- data/samples/elaborate/meta_sample/meta_sample_logo.png +0 -0
- data/samples/elaborate/tetris.rb +90 -17
- data/samples/elaborate/tetris/model/game.rb +89 -8
- data/samples/elaborate/tetris/{view/game_over_dialog.rb → model/past_game.rb} +12 -41
- data/samples/elaborate/tetris/model/tetromino.rb +18 -5
- data/samples/elaborate/tetris/view/block.rb +8 -13
- data/samples/elaborate/tetris/view/high_score_dialog.rb +131 -0
- data/samples/elaborate/tetris/view/playfield.rb +1 -1
- data/samples/elaborate/tetris/view/score_lane.rb +6 -6
- data/samples/elaborate/tetris/view/tetris_menu_bar.rb +70 -3
- data/samples/hello/hello_canvas.rb +10 -9
- data/samples/hello/hello_canvas_animation.rb +5 -5
- data/samples/hello/hello_code_text.rb +92 -0
- data/samples/hello/hello_table.rb +6 -4
- data/samples/hello/hello_table/baseball_park.png +0 -0
- metadata +8 -3
@@ -77,7 +77,7 @@ module Glimmer
|
|
77
77
|
composite.layout = GridLayout.new if composite.get_layout.nil?
|
78
78
|
end,
|
79
79
|
canvas: lambda do |canvas|
|
80
|
-
canvas.layout = nil
|
80
|
+
canvas.layout = nil if canvas.respond_to?('layout=') && !canvas.get_layout.nil?
|
81
81
|
end,
|
82
82
|
scrolled_composite: lambda do |scrolled_composite|
|
83
83
|
scrolled_composite.expand_horizontal = true
|
@@ -165,6 +165,11 @@ module Glimmer
|
|
165
165
|
DEFAULT_INITIALIZERS[underscored_widget_name.to_s.to_sym]&.call(@swt_widget)
|
166
166
|
@parent_proxy.post_initialize_child(self)
|
167
167
|
end
|
168
|
+
if respond_to?(:on_widget_disposed)
|
169
|
+
on_widget_disposed {
|
170
|
+
clear_shapes
|
171
|
+
}
|
172
|
+
end
|
168
173
|
end
|
169
174
|
|
170
175
|
# Subclasses may override to perform post initialization work on an added child
|
@@ -547,6 +552,10 @@ module Glimmer
|
|
547
552
|
@swt_widget.dispose
|
548
553
|
end
|
549
554
|
|
555
|
+
def disposed?
|
556
|
+
@swt_widget.isDisposed
|
557
|
+
end
|
558
|
+
|
550
559
|
# TODO Consider renaming these methods as they are mainly used for data-binding
|
551
560
|
|
552
561
|
def can_add_observer?(property_name)
|
@@ -663,7 +672,7 @@ module Glimmer
|
|
663
672
|
can_handle_observation_request?(method) ||
|
664
673
|
swt_widget.respond_to?(method, *args, &block)
|
665
674
|
end
|
666
|
-
|
675
|
+
|
667
676
|
private
|
668
677
|
|
669
678
|
def style(underscored_widget_name, styles)
|
@@ -32,6 +32,7 @@ module Glimmer
|
|
32
32
|
|
33
33
|
def launch(*args, &content)
|
34
34
|
@launched_custom_shell = send(keyword, *args, &content) if @launched_custom_shell.nil? || @launched_custom_shell.disposed?
|
35
|
+
@launched_custom_shell.swt_widget.set_data('launched', true)
|
35
36
|
@launched_custom_shell.open
|
36
37
|
end
|
37
38
|
end
|
@@ -163,6 +163,8 @@ module Glimmer
|
|
163
163
|
attr_reader :body_root, :swt_widget, :parent, :parent_proxy, :swt_style, :options
|
164
164
|
|
165
165
|
def initialize(parent, *swt_constants, options, &content)
|
166
|
+
@parent_proxy = @parent = parent
|
167
|
+
@parent_proxy = @parent&.get_data('proxy') if @parent.respond_to?(:get_data) && @parent.get_data('proxy')
|
166
168
|
@swt_style = SWT::SWTProxy[*swt_constants]
|
167
169
|
options ||= {}
|
168
170
|
@options = self.class.options.merge(options)
|
@@ -174,9 +176,6 @@ module Glimmer
|
|
174
176
|
raise Glimmer::Error, 'Invalid custom widget for having an empty body! Please fill body block!' if @body_root.nil?
|
175
177
|
@swt_widget = @body_root.swt_widget
|
176
178
|
@swt_widget.set_data('custom_widget', self)
|
177
|
-
@parent = parent
|
178
|
-
@parent ||= @swt_widget.parent
|
179
|
-
@parent_proxy ||= @parent&.get_data('proxy')
|
180
179
|
execute_hook('after_body')
|
181
180
|
end
|
182
181
|
|
@@ -250,10 +249,18 @@ module Glimmer
|
|
250
249
|
def attribute_setter(attribute_name)
|
251
250
|
"#{attribute_name}="
|
252
251
|
end
|
252
|
+
|
253
|
+
def disposed?
|
254
|
+
swt_widget.isDisposed
|
255
|
+
end
|
253
256
|
|
254
257
|
def has_style?(style)
|
255
258
|
(swt_style & SWT::SWTProxy[style]) == SWT::SWTProxy[style]
|
256
259
|
end
|
260
|
+
|
261
|
+
def pack(*args)
|
262
|
+
body_root.pack(*args)
|
263
|
+
end
|
257
264
|
|
258
265
|
# TODO see if it is worth it to eliminate duplication of async_exec/sync_exec
|
259
266
|
# delegation to DisplayProxy, via a module
|
@@ -204,7 +204,7 @@ class MetaSampleApplication
|
|
204
204
|
shell(:fill_screen) {
|
205
205
|
minimum_size 1280, 768
|
206
206
|
text 'Glimmer Meta-Sample (The Sample of Samples)'
|
207
|
-
image File.expand_path('
|
207
|
+
image File.expand_path('meta_sample/meta_sample_logo.png', __dir__)
|
208
208
|
|
209
209
|
sash_form {
|
210
210
|
composite {
|
Binary file
|
data/samples/elaborate/tetris.rb
CHANGED
@@ -25,13 +25,17 @@ require_relative 'tetris/model/game'
|
|
25
25
|
|
26
26
|
require_relative 'tetris/view/playfield'
|
27
27
|
require_relative 'tetris/view/score_lane'
|
28
|
-
require_relative 'tetris/view/
|
28
|
+
require_relative 'tetris/view/high_score_dialog'
|
29
29
|
require_relative 'tetris/view/tetris_menu_bar'
|
30
30
|
|
31
31
|
class Tetris
|
32
32
|
include Glimmer::UI::CustomShell
|
33
33
|
|
34
34
|
BLOCK_SIZE = 25
|
35
|
+
FONT_NAME = 'Menlo'
|
36
|
+
FONT_TITLE_HEIGHT = 32
|
37
|
+
FONT_TITLE_STYLE = :bold
|
38
|
+
BEVEL_CONSTANT = 20
|
35
39
|
|
36
40
|
option :playfield_width, default: Model::Game::PLAYFIELD_WIDTH
|
37
41
|
option :playfield_height, default: Model::Game::PLAYFIELD_HEIGHT
|
@@ -50,36 +54,58 @@ class Tetris
|
|
50
54
|
display {
|
51
55
|
@keyboard_listener = on_swt_keydown { |key_event|
|
52
56
|
case key_event.keyCode
|
53
|
-
when swt(:arrow_down)
|
57
|
+
when swt(:arrow_down), 's'.bytes.first
|
54
58
|
game.down!
|
55
|
-
when swt(:
|
59
|
+
when swt(:arrow_up)
|
60
|
+
case game.up_arrow_action
|
61
|
+
when :instant_down
|
62
|
+
game.down!(instant: true)
|
63
|
+
when :rotate_right
|
64
|
+
game.rotate!(:right)
|
65
|
+
when :rotate_left
|
66
|
+
game.rotate!(:left)
|
67
|
+
end
|
68
|
+
when swt(:arrow_left), 'a'.bytes.first
|
56
69
|
game.left!
|
57
|
-
when swt(:arrow_right)
|
70
|
+
when swt(:arrow_right), 'd'.bytes.first
|
58
71
|
game.right!
|
59
|
-
when swt(:shift)
|
72
|
+
when swt(:shift), swt(:alt)
|
60
73
|
if key_event.keyLocation == swt(:right) # right shift key
|
61
74
|
game.rotate!(:right)
|
62
75
|
elsif key_event.keyLocation == swt(:left) # left shift key
|
63
76
|
game.rotate!(:left)
|
64
77
|
end
|
65
|
-
when
|
66
|
-
game.rotate!(:right)
|
67
|
-
when 'a'.bytes.first
|
78
|
+
when swt(:ctrl)
|
68
79
|
game.rotate!(:left)
|
69
80
|
end
|
70
81
|
}
|
82
|
+
|
83
|
+
# if running in app mode, set the Mac app about dialog (ignored in platforms)
|
84
|
+
@about_observer = on_about {
|
85
|
+
show_about_dialog
|
86
|
+
}
|
87
|
+
|
88
|
+
@quit_observer = on_quit {
|
89
|
+
exit(0)
|
90
|
+
}
|
71
91
|
}
|
72
92
|
}
|
73
93
|
|
74
94
|
after_body {
|
75
95
|
@game_over_observer = observe(@game, :game_over) do |game_over|
|
76
96
|
if game_over
|
77
|
-
|
78
|
-
@game_over_dialog.show
|
97
|
+
show_high_score_dialog
|
79
98
|
else
|
80
99
|
start_moving_tetrominos_down
|
81
100
|
end
|
82
101
|
end
|
102
|
+
@show_high_scores_observer = observe(@game, :show_high_scores) do |show_high_scores|
|
103
|
+
if show_high_scores
|
104
|
+
show_high_score_dialog
|
105
|
+
else
|
106
|
+
@high_score_dialog.close unless @high_score_dialog.nil? || @high_score_dialog.disposed? || !@high_score_dialog.visible?
|
107
|
+
end
|
108
|
+
end
|
83
109
|
@game.start!
|
84
110
|
}
|
85
111
|
|
@@ -95,22 +121,55 @@ class Tetris
|
|
95
121
|
|
96
122
|
text 'Glimmer Tetris'
|
97
123
|
minimum_size 475, 500
|
98
|
-
|
99
|
-
|
124
|
+
image tetris_icon
|
125
|
+
|
100
126
|
tetris_menu_bar(game: game)
|
101
|
-
|
127
|
+
|
102
128
|
playfield(game_playfield: game.playfield, playfield_width: playfield_width, playfield_height: playfield_height, block_size: BLOCK_SIZE)
|
103
|
-
|
129
|
+
|
104
130
|
score_lane(game: game, block_size: BLOCK_SIZE) {
|
105
131
|
layout_data(:fill, :fill, true, true)
|
106
132
|
}
|
107
|
-
|
133
|
+
|
108
134
|
on_widget_disposed {
|
109
135
|
deregister_observers
|
110
136
|
}
|
111
137
|
}
|
112
138
|
}
|
113
139
|
|
140
|
+
def tetris_icon
|
141
|
+
icon_block_size = 64
|
142
|
+
icon_bevel_size = icon_block_size.to_f / 25.to_f
|
143
|
+
icon_bevel_pixel_size = 0.16*icon_block_size.to_f
|
144
|
+
icon_size = 8
|
145
|
+
icon_pixel_size = icon_block_size * icon_size
|
146
|
+
image(icon_pixel_size, icon_pixel_size) {
|
147
|
+
icon_size.times { |row|
|
148
|
+
icon_size.times { |column|
|
149
|
+
colored = row >= 1 && column.between?(1, 6)
|
150
|
+
color = colored ? color(([:white] + Model::Tetromino::LETTER_COLORS.values).sample) : color(:white)
|
151
|
+
x = column * icon_block_size
|
152
|
+
y = row * icon_block_size
|
153
|
+
rectangle(x, y, icon_block_size, icon_block_size, fill: true) {
|
154
|
+
background color
|
155
|
+
}
|
156
|
+
polygon(x, y, x + icon_block_size, y, x + icon_block_size - icon_bevel_pixel_size, y + icon_bevel_pixel_size, x + icon_bevel_pixel_size, y + icon_bevel_pixel_size, fill: true) {
|
157
|
+
background rgb(color.red + 4*BEVEL_CONSTANT, color.green + 4*BEVEL_CONSTANT, color.blue + 4*BEVEL_CONSTANT)
|
158
|
+
}
|
159
|
+
polygon(x + icon_block_size, y, x + icon_block_size - icon_bevel_pixel_size, y + icon_bevel_pixel_size, x + icon_block_size - icon_bevel_pixel_size, y + icon_block_size - icon_bevel_pixel_size, x + icon_block_size, y + icon_block_size, fill: true) {
|
160
|
+
background rgb(color.red - BEVEL_CONSTANT, color.green - BEVEL_CONSTANT, color.blue - BEVEL_CONSTANT)
|
161
|
+
}
|
162
|
+
polygon(x + icon_block_size, y + icon_block_size, x, y + icon_block_size, x + icon_bevel_pixel_size, y + icon_block_size - icon_bevel_pixel_size, x + icon_block_size - icon_bevel_pixel_size, y + icon_block_size - icon_bevel_pixel_size, fill: true) {
|
163
|
+
background rgb(color.red - 2*BEVEL_CONSTANT, color.green - 2*BEVEL_CONSTANT, color.blue - 2*BEVEL_CONSTANT)
|
164
|
+
}
|
165
|
+
polygon(x, y, x, y + icon_block_size, x + icon_bevel_pixel_size, y + icon_block_size - icon_bevel_pixel_size, x + icon_bevel_pixel_size, y + icon_bevel_pixel_size, fill: true) {
|
166
|
+
background rgb(color.red - BEVEL_CONSTANT, color.green - BEVEL_CONSTANT, color.blue - BEVEL_CONSTANT)
|
167
|
+
}
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
114
173
|
def start_moving_tetrominos_down
|
115
174
|
Thread.new do
|
116
175
|
@mutex.synchronize do
|
@@ -126,11 +185,25 @@ class Tetris
|
|
126
185
|
end
|
127
186
|
end
|
128
187
|
|
188
|
+
def show_high_score_dialog
|
189
|
+
return if @high_score_dialog&.visible?
|
190
|
+
@high_score_dialog = high_score_dialog(parent_shell: body_root, game: @game) if @high_score_dialog.nil? || @high_score_dialog.disposed?
|
191
|
+
@high_score_dialog.show
|
192
|
+
end
|
193
|
+
|
194
|
+
def show_about_dialog
|
195
|
+
message_box {
|
196
|
+
text 'Glimmer Tetris'
|
197
|
+
message "Glimmer Tetris\n\nGlimmer DSL for SWT Sample\n\nCopyright (c) 2007-2021 Andy Maleh"
|
198
|
+
}.open
|
199
|
+
end
|
200
|
+
|
129
201
|
def deregister_observers
|
202
|
+
@show_high_scores_observer&.deregister
|
130
203
|
@game_over_observer&.deregister
|
131
|
-
@game_over_observer = nil
|
132
204
|
@keyboard_listener&.deregister
|
133
|
-
@
|
205
|
+
@about_observer&.deregister
|
206
|
+
@quit_observer&.deregister
|
134
207
|
end
|
135
208
|
end
|
136
209
|
|
@@ -19,8 +19,15 @@
|
|
19
19
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
21
|
|
22
|
+
require 'fileutils'
|
23
|
+
require 'etc'
|
24
|
+
require 'json'
|
25
|
+
require 'glimmer/data_binding/observer'
|
26
|
+
require 'glimmer/config'
|
27
|
+
|
22
28
|
require_relative 'block'
|
23
29
|
require_relative 'tetromino'
|
30
|
+
require_relative 'past_game'
|
24
31
|
|
25
32
|
class Tetris
|
26
33
|
module Model
|
@@ -32,15 +39,22 @@ class Tetris
|
|
32
39
|
SCORE_MULTIPLIER = {1 => 40, 2 => 100, 3 => 300, 4 => 1200}
|
33
40
|
|
34
41
|
attr_reader :playfield_width, :playfield_height
|
35
|
-
attr_accessor :game_over, :paused, :preview_tetromino, :lines, :score, :level
|
42
|
+
attr_accessor :game_over, :paused, :preview_tetromino, :lines, :score, :level, :high_scores, :beeping, :added_high_score, :show_high_scores, :up_arrow_action
|
36
43
|
alias game_over? game_over
|
37
44
|
alias paused? paused
|
45
|
+
alias beeping? beeping
|
46
|
+
alias added_high_score? added_high_score
|
38
47
|
|
39
48
|
def initialize(playfield_width = PLAYFIELD_WIDTH, playfield_height = PLAYFIELD_HEIGHT)
|
40
49
|
@playfield_width = playfield_width
|
41
50
|
@playfield_height = playfield_height
|
51
|
+
@high_scores = []
|
52
|
+
@show_high_scores = false
|
53
|
+
@beeping = true
|
54
|
+
@up_arrow_action = :rotate_left
|
55
|
+
load_high_scores!
|
42
56
|
end
|
43
|
-
|
57
|
+
|
44
58
|
def configure_beeper(&beeper)
|
45
59
|
@beeper = beeper
|
46
60
|
end
|
@@ -50,6 +64,7 @@ class Tetris
|
|
50
64
|
end
|
51
65
|
|
52
66
|
def start!
|
67
|
+
self.show_high_scores = false
|
53
68
|
self.paused = false
|
54
69
|
self.level = 1
|
55
70
|
self.score = 0
|
@@ -63,10 +78,51 @@ class Tetris
|
|
63
78
|
end
|
64
79
|
alias restart! start!
|
65
80
|
|
66
|
-
def
|
81
|
+
def game_over!
|
82
|
+
add_high_score!
|
83
|
+
beep
|
84
|
+
self.game_over = true
|
85
|
+
end
|
86
|
+
|
87
|
+
def clear_high_scores!
|
88
|
+
high_scores.clear
|
89
|
+
end
|
90
|
+
|
91
|
+
def add_high_score!
|
92
|
+
self.added_high_score = true
|
93
|
+
high_scores.prepend(PastGame.new("Player #{high_scores.count + 1}", score, lines, level))
|
94
|
+
end
|
95
|
+
|
96
|
+
def save_high_scores!
|
97
|
+
high_score_file_content = @high_scores.map {|past_game| past_game.to_a.join("\t") }.join("\n")
|
98
|
+
FileUtils.mkdir_p(tetris_dir)
|
99
|
+
File.write(tetris_high_score_file, high_score_file_content)
|
100
|
+
rescue => e
|
101
|
+
# Fail safely by keeping high scores in memory if unable to access disk
|
102
|
+
Glimmer::Config.logger.error {"Failed to save high scores in: #{tetris_high_score_file}\n#{e.full_message}"}
|
103
|
+
end
|
104
|
+
|
105
|
+
def load_high_scores!
|
106
|
+
if File.exist?(tetris_high_score_file)
|
107
|
+
self.high_scores = File.read(tetris_high_score_file).split("\n").map {|line| PastGame.new(*line.split("\t")) }
|
108
|
+
end
|
109
|
+
rescue => e
|
110
|
+
# Fail safely by keeping high scores in memory if unable to access disk
|
111
|
+
Glimmer::Config.logger.error {"Failed to load high scores from: #{tetris_high_score_file}\n#{e.full_message}"}
|
112
|
+
end
|
113
|
+
|
114
|
+
def tetris_dir
|
115
|
+
@tetris_dir ||= File.join(Etc.getpwuid.dir, '.glimmer-tetris')
|
116
|
+
end
|
117
|
+
|
118
|
+
def tetris_high_score_file
|
119
|
+
File.join(tetris_dir, "high_scores.txt")
|
120
|
+
end
|
121
|
+
|
122
|
+
def down!(instant: false)
|
67
123
|
return unless game_in_progress?
|
68
|
-
current_tetromino.down!
|
69
|
-
|
124
|
+
current_tetromino.down!(instant: instant)
|
125
|
+
game_over! if current_tetromino.row <= 0 && current_tetromino.stopped?
|
70
126
|
end
|
71
127
|
|
72
128
|
def right!
|
@@ -147,7 +203,31 @@ class Tetris
|
|
147
203
|
end
|
148
204
|
|
149
205
|
def beep
|
150
|
-
@beeper&.call
|
206
|
+
@beeper&.call if beeping
|
207
|
+
end
|
208
|
+
|
209
|
+
def instant_down_on_up=(value)
|
210
|
+
self.up_arrow_action = :instant_down if value
|
211
|
+
end
|
212
|
+
|
213
|
+
def instant_down_on_up
|
214
|
+
self.up_arrow_action == :instant_down
|
215
|
+
end
|
216
|
+
|
217
|
+
def rotate_right_on_up=(value)
|
218
|
+
self.up_arrow_action = :rotate_right if value
|
219
|
+
end
|
220
|
+
|
221
|
+
def rotate_right_on_up
|
222
|
+
self.up_arrow_action == :rotate_right
|
223
|
+
end
|
224
|
+
|
225
|
+
def rotate_left_on_up=(value)
|
226
|
+
self.up_arrow_action = :rotate_left if value
|
227
|
+
end
|
228
|
+
|
229
|
+
def rotate_left_on_up
|
230
|
+
self.up_arrow_action == :rotate_left
|
151
231
|
end
|
152
232
|
|
153
233
|
def reset_tetrominoes
|
@@ -195,12 +275,13 @@ class Tetris
|
|
195
275
|
|
196
276
|
def playfield_remaining_heights(tetromino = nil)
|
197
277
|
@playfield_width.times.map do |playfield_column|
|
278
|
+
bottom_most_block = tetromino.bottom_most_block_for_column(playfield_column)
|
198
279
|
(playfield.each_with_index.detect do |row, playfield_row|
|
199
280
|
!row[playfield_column].clear? &&
|
200
281
|
(
|
201
282
|
tetromino.nil? ||
|
202
|
-
|
203
|
-
(playfield_row > tetromino.row + bottom_most_block[:
|
283
|
+
bottom_most_block.nil? ||
|
284
|
+
(playfield_row > tetromino.row + bottom_most_block[:row_index])
|
204
285
|
)
|
205
286
|
end || [nil, @playfield_height])[1]
|
206
287
|
end.to_a
|
@@ -19,50 +19,21 @@
|
|
19
19
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
21
|
|
22
|
-
require_relative 'tetris_menu_bar'
|
23
|
-
|
24
22
|
class Tetris
|
25
|
-
module
|
26
|
-
class
|
27
|
-
|
28
|
-
|
29
|
-
options :parent_shell, :game
|
23
|
+
module Model
|
24
|
+
class PastGame
|
25
|
+
attr_accessor :name, :score, :lines, :level
|
30
26
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
def initialize(name, score, lines, level)
|
28
|
+
@name = name
|
29
|
+
@score = score.to_i
|
30
|
+
@lines = lines.to_i
|
31
|
+
@level = level.to_i
|
32
|
+
end
|
36
33
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
type :vertical
|
41
|
-
center true
|
42
|
-
}
|
43
|
-
text 'Tetris'
|
44
|
-
|
45
|
-
tetris_menu_bar(game: game)
|
46
|
-
|
47
|
-
label(:center) {
|
48
|
-
text 'Game Over!'
|
49
|
-
font name: 'Menlo', height: 30, style: :bold
|
50
|
-
}
|
51
|
-
label # filler
|
52
|
-
button {
|
53
|
-
text 'Play Again?'
|
54
|
-
|
55
|
-
on_widget_selected {
|
56
|
-
hide
|
57
|
-
game.restart!
|
58
|
-
}
|
59
|
-
}
|
60
|
-
|
61
|
-
on_shell_activated {
|
62
|
-
display.beep
|
63
|
-
}
|
64
|
-
}
|
65
|
-
}
|
34
|
+
def to_a
|
35
|
+
[@name, @score, @lines, @level]
|
36
|
+
end
|
66
37
|
end
|
67
38
|
end
|
68
39
|
end
|