glimmer-dsl-swt 4.18.1.0 → 4.18.2.3
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 +45 -0
- data/README.md +136 -46
- data/VERSION +1 -1
- data/glimmer-dsl-swt.gemspec +10 -4
- data/lib/glimmer/dsl/swt/display_expression.rb +3 -3
- data/lib/glimmer/dsl/swt/property_expression.rb +2 -1
- data/lib/glimmer/dsl/swt/shape_expression.rb +1 -1
- data/lib/glimmer/rake_task/list.rb +5 -5
- data/lib/glimmer/rake_task/package.rb +9 -9
- data/lib/glimmer/rake_task/scaffold.rb +36 -36
- data/lib/glimmer/swt/custom/animation.rb +163 -36
- data/lib/glimmer/swt/custom/code_text.rb +2 -1
- data/lib/glimmer/swt/custom/shape.rb +83 -18
- data/lib/glimmer/swt/display_proxy.rb +2 -1
- data/lib/glimmer/swt/widget_proxy.rb +4 -6
- data/lib/glimmer/ui/custom_shell.rb +13 -0
- data/samples/elaborate/meta_sample.rb +22 -1
- data/samples/elaborate/tetris.rb +102 -0
- data/samples/elaborate/tetris/model/block.rb +48 -0
- data/samples/elaborate/tetris/model/game.rb +116 -0
- data/samples/elaborate/tetris/model/tetromino.rb +228 -0
- data/samples/elaborate/tetris/view/block.rb +45 -0
- data/samples/elaborate/tetris/view/playfield.rb +49 -0
- data/samples/hello/hello_canvas.rb +7 -6
- data/samples/hello/hello_canvas_animation.rb +1 -1
- metadata +22 -14
@@ -61,11 +61,12 @@ module Glimmer
|
|
61
61
|
|
62
62
|
before_body {
|
63
63
|
@swt_style = swt_style == 0 ? [:border, :multi, :v_scroll, :h_scroll] : swt_style
|
64
|
+
@font_name = display.get_font_list(nil, true).map(&:name).include?('Consolas') ? 'Consolas' : 'Courier'
|
64
65
|
}
|
65
66
|
|
66
67
|
body {
|
67
68
|
styled_text(swt_style) {
|
68
|
-
font name:
|
69
|
+
font name: @font_name, height: 15
|
69
70
|
foreground rgb(75, 75, 75)
|
70
71
|
left_margin 5
|
71
72
|
top_margin 5
|
@@ -20,6 +20,10 @@
|
|
20
20
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
21
|
|
22
22
|
require 'glimmer/swt/properties'
|
23
|
+
require 'glimmer/swt/swt_proxy'
|
24
|
+
require 'glimmer/swt/display_proxy'
|
25
|
+
require 'glimmer/swt/color_proxy'
|
26
|
+
require 'glimmer/swt/font_proxy'
|
23
27
|
|
24
28
|
module Glimmer
|
25
29
|
module SWT
|
@@ -28,8 +32,9 @@ module Glimmer
|
|
28
32
|
# swt_widget returns the parent (e.g. a `canvas` WidgetProxy), equivalent to `parent.swt_widget`
|
29
33
|
# That is because Shape is drawn on a parent as graphics and doesn't have an SWT widget for itself
|
30
34
|
class Shape
|
35
|
+
include Packages
|
31
36
|
include Properties
|
32
|
-
# TODO support textExtent
|
37
|
+
# TODO support textExtent sized shapes nested within text/string
|
33
38
|
# TODO support a Pattern DSL for methods that take Pattern arguments
|
34
39
|
|
35
40
|
class << self
|
@@ -48,14 +53,17 @@ module Glimmer
|
|
48
53
|
end
|
49
54
|
|
50
55
|
def method_name(keyword, args)
|
51
|
-
|
52
|
-
|
56
|
+
keyword = keyword.to_s
|
57
|
+
gradient = 'gradient_' if arg_options(args)[:gradient]
|
58
|
+
round = 'round_' if arg_options(args)[:round]
|
59
|
+
gc_instance_method_name_prefix = !['polyline', 'point', 'image', 'focus'].include?(keyword) && (arg_options(args)[:fill] || arg_options(args)[:gradient]) ? 'fill_' : 'draw_'
|
60
|
+
"#{gc_instance_method_name_prefix}#{gradient}#{round}#{keyword}"
|
53
61
|
end
|
54
62
|
end
|
55
63
|
|
56
64
|
attr_reader :parent, :name, :args, :options, :swt_widget, :paint_listener_proxy
|
57
65
|
|
58
|
-
def initialize(parent, keyword, *args)
|
66
|
+
def initialize(parent, keyword, *args, &property_block)
|
59
67
|
@parent = parent
|
60
68
|
@name = keyword
|
61
69
|
@method_name = self.class.method_name(keyword, args)
|
@@ -64,23 +72,37 @@ module Glimmer
|
|
64
72
|
@swt_widget = parent.respond_to?(:swt_display) ? parent.swt_display : parent.swt_widget
|
65
73
|
@properties = {}
|
66
74
|
@parent.shapes << self
|
75
|
+
post_add_content if property_block.nil?
|
76
|
+
end
|
77
|
+
|
78
|
+
def draw?
|
79
|
+
!fill?
|
67
80
|
end
|
68
81
|
|
69
82
|
def fill?
|
70
|
-
|
83
|
+
@options[:fill]
|
71
84
|
end
|
72
85
|
|
73
|
-
def
|
74
|
-
|
86
|
+
def gradient?
|
87
|
+
@options[:gradient]
|
88
|
+
end
|
89
|
+
|
90
|
+
def round?
|
91
|
+
@options[:round]
|
75
92
|
end
|
76
93
|
|
77
94
|
def post_add_content
|
78
95
|
event_handler = lambda do |event|
|
96
|
+
@properties['background'] = [@parent.background] if fill? && !@properties.keys.map(&:to_s).include?('background')
|
97
|
+
@properties['foreground'] = [@parent.foreground] if draw? && !@properties.keys.map(&:to_s).include?('foreground')
|
79
98
|
@properties.each do |property, args|
|
80
99
|
method_name = attribute_setter(property)
|
81
|
-
|
100
|
+
apply_property_arg_conversions(method_name, args)
|
82
101
|
event.gc.send(method_name, *args)
|
83
102
|
end
|
103
|
+
apply_shape_arg_conversions(@method_name, @args)
|
104
|
+
apply_shape_arg_defaults(@method_name, @args)
|
105
|
+
tolerate_shape_extra_args(@method_name, @args)
|
84
106
|
event.gc.send(@method_name, *@args)
|
85
107
|
end
|
86
108
|
if parent.respond_to?(:swt_display)
|
@@ -90,35 +112,78 @@ module Glimmer
|
|
90
112
|
end
|
91
113
|
end
|
92
114
|
|
93
|
-
def
|
115
|
+
def apply_property_arg_conversions(method_name, args)
|
94
116
|
the_java_method = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.detect {|m| m.name == method_name}
|
95
|
-
if args.first.is_a?(Symbol) || args.first.is_a?(String)
|
117
|
+
if (args.first.is_a?(Symbol) || args.first.is_a?(String))
|
96
118
|
if the_java_method.parameter_types.first == Color.java_class
|
97
119
|
args[0] = ColorProxy.new(args[0])
|
98
120
|
end
|
121
|
+
if the_java_method.parameter_types.first == Java::int.java_class
|
122
|
+
args[0] = SWTProxy.constant(args[0])
|
123
|
+
end
|
99
124
|
end
|
100
125
|
if args.first.is_a?(ColorProxy)
|
101
126
|
args[0] = args[0].swt_color
|
102
127
|
end
|
103
|
-
if args.first.is_a?(Hash)
|
104
|
-
|
105
|
-
args[0] = FontProxy.new(args[0])
|
106
|
-
end
|
128
|
+
if args.first.is_a?(Hash) && the_java_method.parameter_types.first == Font.java_class
|
129
|
+
args[0] = FontProxy.new(args[0])
|
107
130
|
end
|
108
131
|
if args.first.is_a?(FontProxy)
|
109
132
|
args[0] = args[0].swt_font
|
110
133
|
end
|
111
|
-
|
112
|
-
|
134
|
+
if ['setBackgroundPattern', 'setForegroundPattern'].include?(method_name.to_s)
|
135
|
+
args.each_with_index do |arg, i|
|
136
|
+
if arg.is_a?(Symbol) || arg.is_a?(String)
|
137
|
+
args[i] = ColorProxy.new(arg).swt_color
|
138
|
+
elsif arg.is_a?(ColorProxy)
|
139
|
+
args[i] = arg.swt_color
|
140
|
+
end
|
141
|
+
end
|
142
|
+
new_args = [DisplayProxy.instance.swt_display] + args
|
143
|
+
args[0] = org.eclipse.swt.graphics.Pattern.new(*new_args)
|
144
|
+
args[1..-1] = []
|
145
|
+
end
|
113
146
|
end
|
114
147
|
|
148
|
+
def apply_shape_arg_conversions(method_name, args)
|
149
|
+
if args.size > 1 && (method_name.include?('polygon') || method_name.include?('polyline'))
|
150
|
+
args[0] = args.dup
|
151
|
+
args[1..-1] = []
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def apply_shape_arg_defaults(method_name, args)
|
156
|
+
if method_name.include?('round_rectangle') && args.size.between?(4, 5)
|
157
|
+
(6 - args.size).times {args << 60}
|
158
|
+
elsif method_name.include?('rectangle') && gradient? && args.size == 4
|
159
|
+
args << true
|
160
|
+
elsif (method_name.include?('text') || method_name.include?('string')) && !@properties.keys.map(&:to_s).include?('background') && args.size == 3
|
161
|
+
args << true
|
162
|
+
end
|
163
|
+
if method_name.include?('image') && args.first.is_a?(String)
|
164
|
+
args[0] = ImageProxy.new(args[0])
|
165
|
+
end
|
166
|
+
if method_name.include?('image') && args.first.is_a?(ImageProxy)
|
167
|
+
args[0] = args[0].swt_image
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Tolerates shape extra args added by user by mistake
|
172
|
+
# (e.g. happens when switching from round rectangle to a standard one without removing all extra args)
|
173
|
+
def tolerate_shape_extra_args(method_name, args)
|
174
|
+
the_java_method_arg_count = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.select do |m|
|
175
|
+
m.name == method_name.camelcase(:lower)
|
176
|
+
end.map(&:parameter_types).map(&:size).max
|
177
|
+
if args.size > the_java_method_arg_count
|
178
|
+
args[the_java_method_arg_count..-1] = []
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
115
182
|
def has_attribute?(attribute_name, *args)
|
116
|
-
# TODO test that attribute getter responds too
|
117
183
|
self.class.gc_instance_methods.include?(attribute_setter(attribute_name))
|
118
184
|
end
|
119
185
|
|
120
186
|
def set_attribute(attribute_name, *args)
|
121
|
-
# TODO special treatment for color symbols
|
122
187
|
@properties[attribute_name] = args
|
123
188
|
end
|
124
189
|
|
@@ -45,7 +45,7 @@ module Glimmer
|
|
45
45
|
class << self
|
46
46
|
# Returns singleton instance
|
47
47
|
def instance(*args)
|
48
|
-
if @instance.nil? || @instance.swt_display.isDisposed
|
48
|
+
if @instance.nil? || @instance.swt_display.nil? || @instance.swt_display.isDisposed
|
49
49
|
@instance = new(*args)
|
50
50
|
end
|
51
51
|
@instance
|
@@ -56,6 +56,7 @@ module Glimmer
|
|
56
56
|
attr_reader :swt_display
|
57
57
|
|
58
58
|
def initialize(*args)
|
59
|
+
Display.app_name ||= 'Glimmer'
|
59
60
|
@swt_display = Display.new(*args)
|
60
61
|
@swt_display.set_data('proxy', self)
|
61
62
|
end
|
@@ -74,12 +74,10 @@ module Glimmer
|
|
74
74
|
|
75
75
|
DEFAULT_INITIALIZERS = {
|
76
76
|
composite: lambda do |composite|
|
77
|
-
if composite.get_layout.nil?
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
composite.layout = layout
|
82
|
-
end
|
77
|
+
composite.layout = GridLayout.new if composite.get_layout.nil?
|
78
|
+
end,
|
79
|
+
canvas: lambda do |canvas|
|
80
|
+
canvas.layout = nil unless canvas.get_layout.nil?
|
83
81
|
end,
|
84
82
|
scrolled_composite: lambda do |scrolled_composite|
|
85
83
|
scrolled_composite.expand_horizontal = true
|
@@ -27,6 +27,19 @@ module Glimmer
|
|
27
27
|
include SuperModule
|
28
28
|
include Glimmer::UI::CustomWidget
|
29
29
|
|
30
|
+
class << self
|
31
|
+
attr_reader :custom_shell
|
32
|
+
|
33
|
+
def launch
|
34
|
+
@custom_shell = send(self.name.underscore.gsub('::', '__'))
|
35
|
+
@custom_shell.open
|
36
|
+
end
|
37
|
+
|
38
|
+
def shutdown
|
39
|
+
@custom_shell.close
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
30
43
|
def initialize(parent, *swt_constants, options, &content)
|
31
44
|
super
|
32
45
|
@swt_widget.set_data('custom_shell', self)
|
@@ -1,3 +1,24 @@
|
|
1
|
+
# Copyright (c) 2007-2021 Andy Maleh
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
1
22
|
require 'fileutils'
|
2
23
|
require 'etc'
|
3
24
|
|
@@ -150,7 +171,7 @@ class MetaSampleApplication
|
|
150
171
|
end
|
151
172
|
|
152
173
|
def launch
|
153
|
-
Display.
|
174
|
+
Display.app_name = 'Glimmer Meta-Sample'
|
154
175
|
shell {
|
155
176
|
minimum_size 1280, 768
|
156
177
|
text 'Glimmer Meta-Sample (The Sample of Samples)'
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# Copyright (c) 2007-2021 Andy Maleh
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
# Tetris App View Custom Shell (represents `tetris` keyword)
|
23
|
+
|
24
|
+
require_relative 'tetris/model/game'
|
25
|
+
|
26
|
+
require_relative 'tetris/view/playfield'
|
27
|
+
|
28
|
+
class Tetris
|
29
|
+
include Glimmer::UI::CustomShell
|
30
|
+
|
31
|
+
BLOCK_SIZE = 25
|
32
|
+
PLAYFIELD_WIDTH = 10
|
33
|
+
PLAYFIELD_HEIGHT = 20
|
34
|
+
|
35
|
+
before_body {
|
36
|
+
Model::Game.configure_beeper do
|
37
|
+
display.beep
|
38
|
+
end
|
39
|
+
|
40
|
+
Model::Game.start
|
41
|
+
|
42
|
+
display {
|
43
|
+
on_swt_keydown { |key_event|
|
44
|
+
unless Model::Game.current_tetromino.stopped?
|
45
|
+
case key_event.keyCode
|
46
|
+
when swt(:arrow_down)
|
47
|
+
Model::Game.current_tetromino.down
|
48
|
+
when swt(:arrow_left)
|
49
|
+
Model::Game.current_tetromino.left
|
50
|
+
when swt(:arrow_right)
|
51
|
+
Model::Game.current_tetromino.right
|
52
|
+
when swt(:shift)
|
53
|
+
if key_event.keyLocation == swt(:right) # right shift key
|
54
|
+
Model::Game.current_tetromino.rotate(:right)
|
55
|
+
elsif key_event.keyLocation == swt(:left) # left shift key
|
56
|
+
Model::Game.current_tetromino.rotate(:left)
|
57
|
+
end
|
58
|
+
when 'd'.bytes.first, swt(:arrow_up)
|
59
|
+
Model::Game.current_tetromino.rotate(:right)
|
60
|
+
when 'a'.bytes.first
|
61
|
+
Model::Game.current_tetromino.rotate(:left)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
after_body {
|
69
|
+
Thread.new {
|
70
|
+
loop {
|
71
|
+
sleep(0.9)
|
72
|
+
sync_exec {
|
73
|
+
unless @game_over
|
74
|
+
Model::Game.current_tetromino.down
|
75
|
+
if Model::Game.current_tetromino.stopped? && Model::Game.current_tetromino.row <= 0
|
76
|
+
@game_over = true
|
77
|
+
display.beep
|
78
|
+
message_box(:icon_error) {
|
79
|
+
text 'Tetris'
|
80
|
+
message 'Game Over!'
|
81
|
+
}.open
|
82
|
+
Model::Game.restart
|
83
|
+
@game_over = false
|
84
|
+
end
|
85
|
+
Model::Game.consider_adding_tetromino
|
86
|
+
end
|
87
|
+
}
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
body {
|
93
|
+
shell(:no_resize) {
|
94
|
+
text 'Glimmer Tetris'
|
95
|
+
background :gray
|
96
|
+
|
97
|
+
playfield(playfield_width: PLAYFIELD_WIDTH, playfield_height: PLAYFIELD_HEIGHT, block_size: BLOCK_SIZE)
|
98
|
+
}
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
Tetris.launch
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Copyright (c) 2007-2021 Andy Maleh
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
class Tetris
|
23
|
+
module Model
|
24
|
+
class Block
|
25
|
+
COLOR_CLEAR = :white
|
26
|
+
|
27
|
+
attr_accessor :color
|
28
|
+
|
29
|
+
# Initializes with color. Default color (gray) signifies an empty block
|
30
|
+
def initialize(color = COLOR_CLEAR)
|
31
|
+
@color = color
|
32
|
+
end
|
33
|
+
|
34
|
+
def clear
|
35
|
+
self.color = COLOR_CLEAR
|
36
|
+
end
|
37
|
+
|
38
|
+
def clear?
|
39
|
+
self.color == COLOR_CLEAR
|
40
|
+
end
|
41
|
+
|
42
|
+
def occupied?
|
43
|
+
!clear?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# Copyright (c) 2007-2021 Andy Maleh
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
require_relative 'block'
|
23
|
+
require_relative 'tetromino'
|
24
|
+
|
25
|
+
class Tetris
|
26
|
+
module Model
|
27
|
+
class Game
|
28
|
+
class << self
|
29
|
+
def consider_adding_tetromino
|
30
|
+
if tetrominoes.empty? || Game.current_tetromino.stopped?
|
31
|
+
tetrominoes << Tetromino.new
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def current_tetromino
|
36
|
+
tetrominoes.last
|
37
|
+
end
|
38
|
+
|
39
|
+
def tetrominoes
|
40
|
+
@tetrominoes ||= reset_tetrominoes
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns blocks in the playfield
|
44
|
+
def playfield
|
45
|
+
@playfield ||= PLAYFIELD_HEIGHT.times.map {
|
46
|
+
PLAYFIELD_WIDTH.times.map {
|
47
|
+
Block.new
|
48
|
+
}
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def consider_eliminating_lines
|
53
|
+
cleared_line = false
|
54
|
+
playfield.each_with_index do |row, playfield_row|
|
55
|
+
if row.all? {|block| !block.clear?}
|
56
|
+
cleared_line = true
|
57
|
+
shift_blocks_down_above_row(playfield_row)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
beep if cleared_line
|
61
|
+
end
|
62
|
+
|
63
|
+
def beep
|
64
|
+
@beeper&.call
|
65
|
+
end
|
66
|
+
|
67
|
+
def configure_beeper(&beeper)
|
68
|
+
@beeper = beeper
|
69
|
+
end
|
70
|
+
|
71
|
+
def shift_blocks_down_above_row(row)
|
72
|
+
row.downto(0) do |playfield_row|
|
73
|
+
playfield[playfield_row].each_with_index do |block, playfield_column|
|
74
|
+
previous_row = playfield[playfield_row - 1]
|
75
|
+
previous_block = previous_row[playfield_column]
|
76
|
+
block.color = previous_block.color
|
77
|
+
end
|
78
|
+
end
|
79
|
+
playfield[0].each(&:clear)
|
80
|
+
end
|
81
|
+
|
82
|
+
def restart
|
83
|
+
reset_playfield
|
84
|
+
reset_tetrominoes
|
85
|
+
end
|
86
|
+
alias start restart
|
87
|
+
|
88
|
+
def reset_tetrominoes
|
89
|
+
@tetrominoes = [Tetromino.new]
|
90
|
+
end
|
91
|
+
|
92
|
+
def reset_playfield
|
93
|
+
playfield.each do |row|
|
94
|
+
row.each do |block|
|
95
|
+
block.clear
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def playfield_remaining_heights(tetromino = nil)
|
101
|
+
PLAYFIELD_WIDTH.times.map do |playfield_column|
|
102
|
+
(playfield.each_with_index.detect do |row, playfield_row|
|
103
|
+
!row[playfield_column].clear? &&
|
104
|
+
(
|
105
|
+
tetromino.nil? ||
|
106
|
+
tetromino.bottom_block_for_column(playfield_column).nil? ||
|
107
|
+
(playfield_row > tetromino.row + tetromino.bottom_block_for_column(playfield_column)[:row])
|
108
|
+
)
|
109
|
+
end || [nil, PLAYFIELD_HEIGHT])[1]
|
110
|
+
end.to_a
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|