glimmer-dsl-swt 4.18.2.3 → 4.18.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +36 -15
- data/VERSION +1 -1
- data/glimmer-dsl-swt.gemspec +5 -3
- data/lib/glimmer/swt/layout_data_proxy.rb +3 -3
- data/lib/glimmer/swt/shell_proxy.rb +3 -2
- data/lib/glimmer/swt/widget_proxy.rb +1 -1
- data/lib/glimmer/ui/custom_shell.rb +2 -2
- data/lib/glimmer/ui/custom_widget.rb +11 -9
- data/samples/elaborate/tetris.rb +42 -36
- data/samples/elaborate/tetris/model/block.rb +1 -1
- data/samples/elaborate/tetris/model/game.rb +79 -10
- data/samples/elaborate/tetris/model/tetromino.rb +116 -31
- data/samples/elaborate/tetris/view/block.rb +3 -6
- data/samples/elaborate/tetris/view/game_over_dialog.rb +72 -0
- data/samples/elaborate/tetris/view/playfield.rb +11 -4
- data/samples/elaborate/tetris/view/score_lane.rb +87 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e12bb24a84b93822013eef2122021cd5e6a8f90f9e450ac83f7bd5b5f0b7b55
|
4
|
+
data.tar.gz: dfdba626253a34506b99971821378e8579eac7f21a955ddbd7bc0543f770ad51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d489317bc86659858e1215aca4f35441aa123f59affb4f35fe9fce1ec04f220f3856f5d203d686a1091a7a1652b746a9453ce598eb684910b1ded8717fc1552b
|
7
|
+
data.tar.gz: 10c31c53aab7d5a8905c6448f66dcacb01c98b952c9934ae1e5236437e583d37aa607161f38946754938c9fc655c876064cc268a93d73d8746667d69d69f684b
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
### 4.18.2.4
|
4
|
+
|
5
|
+
- Tetris scoring
|
6
|
+
- Tetris eliminated Line tracking
|
7
|
+
- Tetris level tracking and speed-ups
|
8
|
+
- Tetris preview upcoming tetromino shape
|
9
|
+
- Added parent_proxy to CustomWidget and CustomShell classes
|
10
|
+
- Update CustomShell#center and ShellProxy#center to center_within_display to avoid clash with `row_layout` center property
|
11
|
+
- Fixed issue with shell/dialog/custom-shell not maintaining parent when not passed
|
12
|
+
- Fix Tetris sideways edge detection
|
13
|
+
|
3
14
|
### 4.18.2.3
|
4
15
|
|
5
16
|
- Added Tetris Elaborate Sample
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for SWT 4.18.2.
|
1
|
+
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for SWT 4.18.2.4
|
2
2
|
## JRuby Desktop Development GUI Framework
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/glimmer-dsl-swt.svg)](http://badge.fury.io/rb/glimmer-dsl-swt)
|
4
4
|
[![Travis CI](https://travis-ci.com/AndyObtiva/glimmer-dsl-swt.svg?branch=master)](https://travis-ci.com/github/AndyObtiva/glimmer-dsl-swt)
|
@@ -226,6 +226,11 @@ Glimmer App:
|
|
226
226
|
|
227
227
|
[![Math Bowling App Screenshot](https://raw.githubusercontent.com/AndyObtiva/MathBowling/master/Math-Bowling-Screenshot.png)](https://github.com/AndyObtiva/MathBowling)
|
228
228
|
|
229
|
+
**Note:** I offer Glimmer as an open-source project that represents my interests in Ruby Programming, Desktop GUI application development with SWT, Object Oriented Design, Design Patterns, and Software Architecture.
|
230
|
+
Additionally, I am sharing my professional experience and expertise in Eclipse SWT given that I am an [EclipseCon](http://andymaleh.blogspot.com/2007/03/eclipsecon-2007-day-3.html)/[EclipseWorld](http://andymaleh.blogspot.com/2008/11/eclipseworld-2008-highlights.html) presenter and have built professional applications in SWT/JFace/RCP for Obtiva and the Pampered Chef in the past. This is also done in the hopes that it would indirectly bring me work in a field I am extremely passionate about.
|
231
|
+
That said, please keep in mind that I myself am learning topics in Software Engineering too everyday, including newer editions of SWT and JRuby, which seem to pop up every quarter.
|
232
|
+
If you see anything that needs to be improved, please do not hesitate to contact me on [Gitter](https://gitter.im/AndyObtiva/glimmer) or submit [Issues](https://github.com/AndyObtiva/glimmer-dsl-swt/issues)/[Pull-Requests](https://github.com/AndyObtiva/glimmer-dsl-swt/pulls).
|
233
|
+
|
229
234
|
## Table of contents
|
230
235
|
|
231
236
|
- [Glimmer (JRuby Desktop Development GUI Framework)](#jruby-desktop-development-gui-framework)
|
@@ -301,7 +306,7 @@ Glimmer App:
|
|
301
306
|
- [Custom Widget API](#custom-widget-api)
|
302
307
|
- [Content/Options Example](#contentoptions-example)
|
303
308
|
- [Gotcha](#gotcha)
|
304
|
-
- [Final Notes](#final-notes)
|
309
|
+
- [Custom Widget Final Notes](#custom-widget-final-notes)
|
305
310
|
- [Custom Shells](#custom-shells)
|
306
311
|
- [Drag and Drop](#drag-and-drop)
|
307
312
|
- [Miscellaneous](#miscellaneous)
|
@@ -453,7 +458,7 @@ jgem install glimmer-dsl-swt
|
|
453
458
|
|
454
459
|
Or this command if you want a specific version:
|
455
460
|
```
|
456
|
-
jgem install glimmer-dsl-swt -v 4.18.2.
|
461
|
+
jgem install glimmer-dsl-swt -v 4.18.2.4
|
457
462
|
|
458
463
|
|
459
464
|
```
|
@@ -473,7 +478,7 @@ Note: if you're using activerecord or activesupport, keep in mind that Glimmer u
|
|
473
478
|
|
474
479
|
Add the following to `Gemfile`:
|
475
480
|
```
|
476
|
-
gem 'glimmer-dsl-swt', '~> 4.18.2.
|
481
|
+
gem 'glimmer-dsl-swt', '~> 4.18.2.4
|
477
482
|
'
|
478
483
|
```
|
479
484
|
|
@@ -532,7 +537,7 @@ bin/glimmer samples
|
|
532
537
|
Below are the full usage instructions that come up when running `glimmer` without args.
|
533
538
|
|
534
539
|
```
|
535
|
-
Glimmer (JRuby Desktop Development GUI Framework) - JRuby Gem: glimmer-dsl-swt v4.18.2.
|
540
|
+
Glimmer (JRuby Desktop Development GUI Framework) - JRuby Gem: glimmer-dsl-swt v4.18.2.4
|
536
541
|
|
537
542
|
|
538
543
|
|
@@ -1013,7 +1018,7 @@ Output:
|
|
1013
1018
|
|
1014
1019
|
Css glimmer-dsl-css 1.1.0 AndyMaleh Glimmer DSL for CSS
|
1015
1020
|
Opal glimmer-dsl-opal 0.10.2 AndyMaleh Glimmer DSL for Opal
|
1016
|
-
Swt glimmer-dsl-swt 4.18.2.
|
1021
|
+
Swt glimmer-dsl-swt 4.18.2.4
|
1017
1022
|
|
1018
1023
|
AndyMaleh Glimmer DSL for SWT
|
1019
1024
|
Tk glimmer-dsl-tk 0.0.6 AndyMaleh Glimmer DSL for Tk
|
@@ -2340,25 +2345,33 @@ Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
|
2340
2345
|
```ruby
|
2341
2346
|
include Glimmer
|
2342
2347
|
|
2348
|
+
# image object has to be declared outside the canvas and shell to avoid confusing with canvas image property
|
2349
|
+
image_object = image(File.expand_path('./icons/scaffold_app.png'), width: 100)
|
2350
|
+
|
2343
2351
|
shell {
|
2344
2352
|
text 'Canvas Example'
|
2345
2353
|
minimum_size 320, 400
|
2346
2354
|
|
2347
2355
|
canvas {
|
2348
|
-
background :
|
2356
|
+
background :dark_yellow
|
2349
2357
|
rectangle(0, 0, 220, 400, fill: true) {
|
2350
|
-
background :
|
2358
|
+
background :dark_red
|
2351
2359
|
}
|
2352
2360
|
rectangle(50, 20, 300, 150, 30, 50, round: true, fill: true) {
|
2353
|
-
background :
|
2361
|
+
background :yellow
|
2354
2362
|
}
|
2355
2363
|
rectangle(150, 200, 100, 70, true, gradient: true) {
|
2356
|
-
background :
|
2364
|
+
background :dark_red
|
2357
2365
|
foreground :yellow
|
2358
2366
|
}
|
2367
|
+
text('Glimmer', 208, 83) {
|
2368
|
+
font height: 25, style: :bold
|
2369
|
+
}
|
2359
2370
|
rectangle(200, 80, 108, 36) {
|
2360
|
-
foreground
|
2371
|
+
foreground :black
|
2372
|
+
line_width 3
|
2361
2373
|
}
|
2374
|
+
image(image_object, 70, 50)
|
2362
2375
|
}
|
2363
2376
|
}.open
|
2364
2377
|
```
|
@@ -2400,7 +2413,11 @@ shell {
|
|
2400
2413
|
}.open
|
2401
2414
|
```
|
2402
2415
|
|
2403
|
-
In any case, if there is anything missing you would like added to the Glimmer Shape DSL that you saw available in the SWT APIs, you may [report an issue](https://github.com/AndyObtiva/glimmer-dsl-swt/issues) or implement yourself and [contribute](#contributing) via a Pull Request.
|
2416
|
+
In any case, if there is anything missing you would like added to the Glimmer Shape DSL that you saw available in the SWT APIs, you may [report an issue](https://github.com/AndyObtiva/glimmer-dsl-swt/issues) or implement yourself and [contribute](#contributing) via a [Pull Request](https://github.com/AndyObtiva/glimmer-dsl-swt/pulls).
|
2417
|
+
|
2418
|
+
#### Shapes inside a Widget
|
2419
|
+
|
2420
|
+
Keep in mind that the Shape DSL can be used inside any widget, not just `canvas`. Unlike shapes on a `canvas`, which are standalone graphics, when included in a widget, which already has its own look and feel, shapes are used as a decorative add-on that complements its look by getting painted on top of it. For example, shapes were used to decorate `composite` blocks in the [Tetris](#tetris) sample to have a more bevel look. In summary, Shapes can be used in a hybrid approach (shapes inside a widget), not just standalone in a `canvas`.
|
2404
2421
|
|
2405
2422
|
### Canvas Animation DSL
|
2406
2423
|
|
@@ -2944,7 +2961,7 @@ If you need a better widget with the ability to customize the date format patter
|
|
2944
2961
|
|
2945
2962
|
### Observer
|
2946
2963
|
|
2947
|
-
Glimmer comes with `Observer` module, which is used internally for data-binding, but can also be used externally for custom use of the Observer Pattern. It is hidden when observing widgets, and used explicitly when observing models.
|
2964
|
+
Glimmer comes with the `Observer` mixin module, which is used internally for data-binding, but can also be used externally for custom use of the Observer Pattern. It is hidden when observing widgets, and used explicitly when observing models. In bidirectional data-binding, `Observer` is automatically unregistered from models once a widget is disposed to avoid memory leaks and worrying about managing them yourself.
|
2948
2965
|
|
2949
2966
|
#### Observing Widgets
|
2950
2967
|
|
@@ -3310,7 +3327,7 @@ body {
|
|
3310
3327
|
|
3311
3328
|
The `text` method invoked in the custom widget body will call the one you defined above it. To avoid this gotcha, simply name the text property above something else, like `custom_text`.
|
3312
3329
|
|
3313
|
-
#### Final Notes
|
3330
|
+
#### Custom Widget Final Notes
|
3314
3331
|
|
3315
3332
|
This [Eclipse guide](https://www.eclipse.org/articles/Article-Writing%20Your%20Own%20Widget/Writing%20Your%20Own%20Widget.htm) for how to write custom SWT widgets is also applicable to Glimmer Custom Widgets written in Ruby. I recommend reading it:
|
3316
3333
|
[https://www.eclipse.org/articles/Article-Writing%20Your%20Own%20Widget/Writing%20Your%20Own%20Widget.htm](https://www.eclipse.org/articles/Article-Writing%20Your%20Own%20Widget/Writing%20Your%20Own%20Widget.htm)
|
@@ -4640,10 +4657,12 @@ This sample demonstrates how to build an interactive animated game with MVC arch
|
|
4640
4657
|
|
4641
4658
|
Code:
|
4642
4659
|
|
4643
|
-
[samples/elaborate/
|
4660
|
+
[samples/elaborate/tetris.rb](samples/elaborate/tetris.rb)
|
4644
4661
|
|
4645
4662
|
![Tetris](images/glimmer-tetris.png)
|
4646
4663
|
|
4664
|
+
![Tetris Game Over](images/glimmer-tetris-game-over.png)
|
4665
|
+
|
4647
4666
|
### External Samples
|
4648
4667
|
|
4649
4668
|
#### Glimmer Calculator
|
@@ -4697,6 +4716,8 @@ If you have a Glimmer app you would like referenced here, please mention in a Pu
|
|
4697
4716
|
|
4698
4717
|
Note: this section mostly applies to Mac and Windows. On Linux, you can just run `glimmer package:gem` and after installing the gem, you get an executable matching the name of the app/custom-shell-gem you are building (e.g. `calculator` command becomes available after installing the [glimmer-cs-calculator](https://github.com/AndyObtiva/glimmer-cs-calculator) gem)
|
4699
4718
|
|
4719
|
+
Note 2: Glimmer packaging has a strong dependency on JDK8 at the moment. JDK9 & JDK10 might work, but JDK11 and onward definitely won't since they dropped javapackager, which later came back as jpackage in JDK14, but it's not ready for prime time yet. Just stick to JDK8 for now, strongly supported by Oracle for the next 6 years at least.
|
4720
|
+
|
4700
4721
|
Glimmer simplifies the process of native-executable packaging and distribution on Mac and Windows via a single `glimmer package` command:
|
4701
4722
|
|
4702
4723
|
```
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.18.2.
|
1
|
+
4.18.2.4
|
data/glimmer-dsl-swt.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: glimmer-dsl-swt 4.18.2.
|
5
|
+
# stub: glimmer-dsl-swt 4.18.2.4 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "glimmer-dsl-swt".freeze
|
9
|
-
s.version = "4.18.2.
|
9
|
+
s.version = "4.18.2.4"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["AndyMaleh".freeze]
|
14
|
-
s.date = "2021-01-
|
14
|
+
s.date = "2021-01-24"
|
15
15
|
s.description = "Glimmer DSL for SWT (JRuby Desktop Development GUI Framework) is a native-GUI cross-platform desktop development library written in JRuby, an OS-threaded faster JVM version of Ruby. Glimmer's main innovation is a declarative Ruby DSL that enables productive and efficient authoring of desktop application user-interfaces by relying on the robust Eclipse SWT library. Glimmer additionally innovates by having built-in data-binding support, which greatly facilitates synchronizing the GUI with domain models, thus achieving true decoupling of object oriented components and enabling developers to solve business problems (test-first) without worrying about GUI concerns, or alternatively drive development GUI-first, and then write clean business models (test-first) afterwards. Not only does Glimmer provide a large set of GUI widgets, but it also supports drawing Canvas Graphics like Shapes and Animations. To get started quickly, Glimmer offers scaffolding options for Apps, Gems, and Custom Widgets. Glimmer also includes native-executable packaging support, sorely lacking in other libraries, thus enabling the delivery of desktop apps written in Ruby as truly native DMG/PKG/APP files on the Mac + App Store, MSI/EXE files on Windows, and Gem Packaged Shell Scripts on Linux.".freeze
|
16
16
|
s.email = "andy.am@gmail.com".freeze
|
17
17
|
s.executables = ["glimmer".freeze, "girb".freeze]
|
@@ -137,7 +137,9 @@ Gem::Specification.new do |s|
|
|
137
137
|
"samples/elaborate/tetris/model/game.rb",
|
138
138
|
"samples/elaborate/tetris/model/tetromino.rb",
|
139
139
|
"samples/elaborate/tetris/view/block.rb",
|
140
|
+
"samples/elaborate/tetris/view/game_over_dialog.rb",
|
140
141
|
"samples/elaborate/tetris/view/playfield.rb",
|
142
|
+
"samples/elaborate/tetris/view/score_lane.rb",
|
141
143
|
"samples/elaborate/tic_tac_toe.rb",
|
142
144
|
"samples/elaborate/tic_tac_toe/board.rb",
|
143
145
|
"samples/elaborate/tic_tac_toe/cell.rb",
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Copyright (c) 2007-2021 Andy Maleh
|
2
|
-
#
|
2
|
+
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
# a copy of this software and associated documentation files (the
|
5
5
|
# "Software"), to deal in the Software without restriction, including
|
@@ -7,10 +7,10 @@
|
|
7
7
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
8
|
# permit persons to whom the Software is furnished to do so, subject to
|
9
9
|
# the following conditions:
|
10
|
-
#
|
10
|
+
#
|
11
11
|
# The above copyright notice and this permission notice shall be
|
12
12
|
# included in all copies or substantial portions of the Software.
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
15
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
16
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
@@ -46,6 +46,7 @@ module Glimmer
|
|
46
46
|
@swt_widget = swt_widget
|
47
47
|
else
|
48
48
|
if args.first.is_a?(ShellProxy)
|
49
|
+
@parent_proxy = args[0]
|
49
50
|
args[0] = args[0].swt_widget
|
50
51
|
end
|
51
52
|
style_args = args.select {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
|
@@ -78,7 +79,7 @@ module Glimmer
|
|
78
79
|
end
|
79
80
|
|
80
81
|
# Centers shell within monitor it is in
|
81
|
-
def
|
82
|
+
def center_within_display
|
82
83
|
primary_monitor = @display.getPrimaryMonitor()
|
83
84
|
monitor_bounds = primary_monitor.getBounds()
|
84
85
|
shell_bounds = @swt_widget.getBounds()
|
@@ -101,7 +102,7 @@ module Glimmer
|
|
101
102
|
else
|
102
103
|
@opened_before = true
|
103
104
|
@swt_widget.pack
|
104
|
-
|
105
|
+
center_within_display
|
105
106
|
@swt_widget.open
|
106
107
|
end
|
107
108
|
end
|
@@ -158,7 +158,7 @@ module Glimmer
|
|
158
158
|
underscored_widget_name = self.class.underscored_widget_name(@swt_widget)
|
159
159
|
parent_proxy_class = self.class.widget_proxy_class(self.class.underscored_widget_name(@swt_widget.parent))
|
160
160
|
parent = swt_widget.parent
|
161
|
-
@parent_proxy = parent
|
161
|
+
@parent_proxy = parent&.get_data('proxy') || parent_proxy_class.new(swt_widget: parent)
|
162
162
|
end
|
163
163
|
if @swt_widget&.get_data('proxy').nil?
|
164
164
|
@swt_widget.set_data('proxy', self)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Copyright (c) 2007-2021 Andy Maleh
|
2
|
-
#
|
2
|
+
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
# a copy of this software and associated documentation files (the
|
5
5
|
# "Software"), to deal in the Software without restriction, including
|
@@ -7,10 +7,10 @@
|
|
7
7
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
8
|
# permit persons to whom the Software is furnished to do so, subject to
|
9
9
|
# the following conditions:
|
10
|
-
#
|
10
|
+
#
|
11
11
|
# The above copyright notice and this permission notice shall be
|
12
12
|
# included in all copies or substantial portions of the Software.
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
15
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
16
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
@@ -141,10 +141,9 @@ module Glimmer
|
|
141
141
|
end
|
142
142
|
end
|
143
143
|
|
144
|
-
attr_reader :body_root, :swt_widget, :parent, :swt_style, :options
|
144
|
+
attr_reader :body_root, :swt_widget, :parent, :parent_proxy, :swt_style, :options
|
145
145
|
|
146
146
|
def initialize(parent, *swt_constants, options, &content)
|
147
|
-
@parent = parent
|
148
147
|
@swt_style = SWT::SWTProxy[*swt_constants]
|
149
148
|
options ||= {}
|
150
149
|
@options = self.class.options.merge(options)
|
@@ -156,6 +155,9 @@ module Glimmer
|
|
156
155
|
raise Glimmer::Error, 'Invalid custom widget for having an empty body! Please fill body block!' if @body_root.nil?
|
157
156
|
@swt_widget = @body_root.swt_widget
|
158
157
|
@swt_widget.set_data('custom_widget', self)
|
158
|
+
@parent = parent
|
159
|
+
@parent ||= @swt_widget.parent
|
160
|
+
@parent_proxy ||= @parent&.get_data('proxy')
|
159
161
|
execute_hooks('after_body')
|
160
162
|
end
|
161
163
|
|
@@ -211,10 +213,10 @@ module Glimmer
|
|
211
213
|
|
212
214
|
# This method ensures it has an instance method not coming from Glimmer DSL
|
213
215
|
def has_instance_method?(method_name)
|
214
|
-
respond_to?(method_name) and
|
216
|
+
respond_to?(method_name) and
|
215
217
|
!swt_widget&.respond_to?(method_name) and
|
216
218
|
(method(method_name) rescue nil) and
|
217
|
-
!method(method_name)&.source_location&.first&.include?('glimmer/dsl/engine.rb') and
|
219
|
+
!method(method_name)&.source_location&.first&.include?('glimmer/dsl/engine.rb') and
|
218
220
|
!method(method_name)&.source_location&.first&.include?('glimmer/swt/widget_proxy.rb')
|
219
221
|
end
|
220
222
|
|
@@ -265,13 +267,13 @@ module Glimmer
|
|
265
267
|
end
|
266
268
|
end
|
267
269
|
|
268
|
-
alias local_respond_to? respond_to?
|
270
|
+
alias local_respond_to? respond_to?
|
269
271
|
def respond_to?(method, *args, &block)
|
270
272
|
super or
|
271
273
|
can_handle_observation_request?(method) or
|
272
274
|
body_root.respond_to?(method, *args, &block)
|
273
275
|
end
|
274
|
-
|
276
|
+
|
275
277
|
private
|
276
278
|
|
277
279
|
def execute_hooks(hook_name)
|
data/samples/elaborate/tetris.rb
CHANGED
@@ -24,6 +24,8 @@
|
|
24
24
|
require_relative 'tetris/model/game'
|
25
25
|
|
26
26
|
require_relative 'tetris/view/playfield'
|
27
|
+
require_relative 'tetris/view/score_lane'
|
28
|
+
require_relative 'tetris/view/game_over_dialog'
|
27
29
|
|
28
30
|
class Tetris
|
29
31
|
include Glimmer::UI::CustomShell
|
@@ -31,58 +33,50 @@ class Tetris
|
|
31
33
|
BLOCK_SIZE = 25
|
32
34
|
PLAYFIELD_WIDTH = 10
|
33
35
|
PLAYFIELD_HEIGHT = 20
|
36
|
+
PREVIEW_PLAYFIELD_WIDTH = 4
|
37
|
+
PREVIEW_PLAYFIELD_HEIGHT = 2
|
34
38
|
|
35
39
|
before_body {
|
36
|
-
Model::Game.configure_beeper do
|
37
|
-
display.beep
|
38
|
-
end
|
39
|
-
|
40
|
-
Model::Game.start
|
41
|
-
|
42
40
|
display {
|
43
41
|
on_swt_keydown { |key_event|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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)
|
42
|
+
case key_event.keyCode
|
43
|
+
when swt(:arrow_down)
|
44
|
+
Model::Game.current_tetromino.down
|
45
|
+
when swt(:arrow_left)
|
46
|
+
Model::Game.current_tetromino.left
|
47
|
+
when swt(:arrow_right)
|
48
|
+
Model::Game.current_tetromino.right
|
49
|
+
when swt(:shift)
|
50
|
+
if key_event.keyLocation == swt(:right) # right shift key
|
59
51
|
Model::Game.current_tetromino.rotate(:right)
|
60
|
-
|
52
|
+
elsif key_event.keyLocation == swt(:left) # left shift key
|
61
53
|
Model::Game.current_tetromino.rotate(:left)
|
62
54
|
end
|
55
|
+
when 'd'.bytes.first, swt(:arrow_up)
|
56
|
+
Model::Game.current_tetromino.rotate(:right)
|
57
|
+
when 'a'.bytes.first
|
58
|
+
Model::Game.current_tetromino.rotate(:left)
|
63
59
|
end
|
64
60
|
}
|
65
61
|
}
|
62
|
+
|
63
|
+
Model::Game.configure_beeper do
|
64
|
+
async_exec {
|
65
|
+
display.beep
|
66
|
+
}
|
67
|
+
end
|
66
68
|
}
|
67
69
|
|
68
70
|
after_body {
|
71
|
+
Model::Game.start
|
72
|
+
|
69
73
|
Thread.new {
|
70
74
|
loop {
|
71
|
-
sleep(
|
75
|
+
sleep(Model::Game.delay)
|
72
76
|
sync_exec {
|
73
|
-
unless
|
77
|
+
unless Model::Game.game_over?
|
74
78
|
Model::Game.current_tetromino.down
|
75
|
-
if Model::Game.current_tetromino.
|
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
|
79
|
+
game_over_dialog(parent_shell: body_root).open if Model::Game.current_tetromino.row <= 0 && Model::Game.current_tetromino.stopped?
|
86
80
|
end
|
87
81
|
}
|
88
82
|
}
|
@@ -91,10 +85,22 @@ class Tetris
|
|
91
85
|
|
92
86
|
body {
|
93
87
|
shell(:no_resize) {
|
88
|
+
grid_layout {
|
89
|
+
num_columns 2
|
90
|
+
make_columns_equal_width false
|
91
|
+
margin_width 0
|
92
|
+
margin_height 0
|
93
|
+
horizontal_spacing 0
|
94
|
+
}
|
95
|
+
|
94
96
|
text 'Glimmer Tetris'
|
95
97
|
background :gray
|
98
|
+
|
99
|
+
playfield(game_playfield: Model::Game.playfield, playfield_width: PLAYFIELD_WIDTH, playfield_height: PLAYFIELD_HEIGHT, block_size: BLOCK_SIZE)
|
96
100
|
|
97
|
-
|
101
|
+
score_lane(block_size: BLOCK_SIZE) {
|
102
|
+
layout_data(:fill, :fill, false, true)
|
103
|
+
}
|
98
104
|
}
|
99
105
|
}
|
100
106
|
end
|
@@ -31,6 +31,7 @@ class Tetris
|
|
31
31
|
@color = color
|
32
32
|
end
|
33
33
|
|
34
|
+
# Clears block color. `quietly` option indicates if it should not notify observers by setting value quietly via variable not attribute writer.
|
34
35
|
def clear
|
35
36
|
self.color = COLOR_CLEAR
|
36
37
|
end
|
@@ -45,4 +46,3 @@ class Tetris
|
|
45
46
|
end
|
46
47
|
end
|
47
48
|
end
|
48
|
-
|
@@ -25,10 +25,16 @@ require_relative 'tetromino'
|
|
25
25
|
class Tetris
|
26
26
|
module Model
|
27
27
|
class Game
|
28
|
+
SCORE_MULTIPLIER = {1 => 40, 2 => 100, 3 => 300, 4 => 1200}
|
29
|
+
|
28
30
|
class << self
|
31
|
+
attr_accessor :game_over, :preview_tetromino, :lines, :score, :level
|
32
|
+
alias game_over? game_over
|
33
|
+
|
29
34
|
def consider_adding_tetromino
|
30
35
|
if tetrominoes.empty? || Game.current_tetromino.stopped?
|
31
|
-
|
36
|
+
preview_tetromino.launch!
|
37
|
+
preview_next_tetromino!
|
32
38
|
end
|
33
39
|
end
|
34
40
|
|
@@ -42,22 +48,70 @@ class Tetris
|
|
42
48
|
|
43
49
|
# Returns blocks in the playfield
|
44
50
|
def playfield
|
45
|
-
@playfield ||= PLAYFIELD_HEIGHT.times.map {
|
51
|
+
@playfield ||= @original_playfield = PLAYFIELD_HEIGHT.times.map {
|
46
52
|
PLAYFIELD_WIDTH.times.map {
|
47
53
|
Block.new
|
48
54
|
}
|
49
55
|
}
|
50
56
|
end
|
51
57
|
|
58
|
+
def hypothetical(&block)
|
59
|
+
@playfield = hypothetical_playfield
|
60
|
+
block.call
|
61
|
+
@playfield = @original_playfield
|
62
|
+
end
|
63
|
+
|
64
|
+
def hypothetical?
|
65
|
+
@playfield != @original_playfield
|
66
|
+
end
|
67
|
+
|
68
|
+
def hypothetical_playfield
|
69
|
+
PLAYFIELD_HEIGHT.times.map { |row|
|
70
|
+
PLAYFIELD_WIDTH.times.map { |column|
|
71
|
+
playfield[row][column].clone
|
72
|
+
}
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
def preview_playfield
|
77
|
+
@preview_playfield ||= PREVIEW_PLAYFIELD_HEIGHT.times.map {|row|
|
78
|
+
PREVIEW_PLAYFIELD_WIDTH.times.map {|column|
|
79
|
+
Block.new
|
80
|
+
}
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def preview_next_tetromino!
|
85
|
+
self.preview_tetromino = Tetromino.new
|
86
|
+
end
|
87
|
+
|
88
|
+
def calculate_score!(eliminated_lines)
|
89
|
+
new_score = SCORE_MULTIPLIER[eliminated_lines] * (level + 1)
|
90
|
+
self.score += new_score
|
91
|
+
end
|
92
|
+
|
93
|
+
def level_up!
|
94
|
+
self.level += 1 if lines >= self.level*10
|
95
|
+
end
|
96
|
+
|
97
|
+
def delay
|
98
|
+
[1.1 - (level.to_i * 0.1), 0.001].max
|
99
|
+
end
|
100
|
+
|
52
101
|
def consider_eliminating_lines
|
53
|
-
|
102
|
+
eliminated_lines = 0
|
54
103
|
playfield.each_with_index do |row, playfield_row|
|
55
104
|
if row.all? {|block| !block.clear?}
|
56
|
-
|
105
|
+
eliminated_lines += 1
|
57
106
|
shift_blocks_down_above_row(playfield_row)
|
58
107
|
end
|
59
108
|
end
|
60
|
-
|
109
|
+
if eliminated_lines > 0
|
110
|
+
beep
|
111
|
+
self.lines += eliminated_lines
|
112
|
+
level_up!
|
113
|
+
calculate_score!(eliminated_lines)
|
114
|
+
end
|
61
115
|
end
|
62
116
|
|
63
117
|
def beep
|
@@ -79,14 +133,21 @@ class Tetris
|
|
79
133
|
playfield[0].each(&:clear)
|
80
134
|
end
|
81
135
|
|
82
|
-
def
|
136
|
+
def start
|
137
|
+
self.level = 1
|
138
|
+
self.score = 0
|
139
|
+
self.lines = @previoius_lines = 0
|
83
140
|
reset_playfield
|
141
|
+
reset_preview_playfield
|
84
142
|
reset_tetrominoes
|
143
|
+
preview_next_tetromino!
|
144
|
+
consider_adding_tetromino
|
145
|
+
self.game_over = false
|
85
146
|
end
|
86
|
-
alias start
|
147
|
+
alias restart start
|
87
148
|
|
88
149
|
def reset_tetrominoes
|
89
|
-
@tetrominoes = [
|
150
|
+
@tetrominoes = []
|
90
151
|
end
|
91
152
|
|
92
153
|
def reset_playfield
|
@@ -97,14 +158,22 @@ class Tetris
|
|
97
158
|
end
|
98
159
|
end
|
99
160
|
|
161
|
+
def reset_preview_playfield
|
162
|
+
preview_playfield.each do |row|
|
163
|
+
row.each do |block|
|
164
|
+
block.clear
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
100
169
|
def playfield_remaining_heights(tetromino = nil)
|
101
170
|
PLAYFIELD_WIDTH.times.map do |playfield_column|
|
102
171
|
(playfield.each_with_index.detect do |row, playfield_row|
|
103
172
|
!row[playfield_column].clear? &&
|
104
173
|
(
|
105
174
|
tetromino.nil? ||
|
106
|
-
tetromino.
|
107
|
-
(playfield_row > tetromino.row +
|
175
|
+
(bottom_most_block = tetromino.bottom_most_block_for_column(playfield_column)).nil? ||
|
176
|
+
(playfield_row > tetromino.row + bottom_most_block[:row])
|
108
177
|
)
|
109
178
|
end || [nil, PLAYFIELD_HEIGHT])[1]
|
110
179
|
end.to_a
|
@@ -38,16 +38,31 @@ class Tetris
|
|
38
38
|
Z: :red,
|
39
39
|
}
|
40
40
|
|
41
|
-
attr_reader :letter
|
41
|
+
attr_reader :letter, :preview
|
42
|
+
alias preview? preview
|
42
43
|
attr_accessor :orientation, :blocks, :row, :column
|
43
44
|
|
44
45
|
def initialize
|
45
46
|
@letter = LETTER_COLORS.keys.sample
|
46
47
|
@orientation = :north
|
47
48
|
@blocks = default_blocks
|
49
|
+
@preview = true
|
50
|
+
new_row = 0
|
51
|
+
new_column = (PREVIEW_PLAYFIELD_WIDTH - width)/2
|
52
|
+
update_playfield(new_row, new_column)
|
53
|
+
end
|
54
|
+
|
55
|
+
def playfield
|
56
|
+
@preview ? Game.preview_playfield : Game.playfield
|
57
|
+
end
|
58
|
+
|
59
|
+
def launch!
|
60
|
+
remove_from_playfield
|
61
|
+
@preview = false
|
48
62
|
new_row = 1 - height
|
49
63
|
new_column = (PLAYFIELD_WIDTH - width)/2
|
50
64
|
update_playfield(new_row, new_column)
|
65
|
+
Game.tetrominoes << self
|
51
66
|
end
|
52
67
|
|
53
68
|
def update_playfield(new_row = nil, new_column = nil)
|
@@ -61,58 +76,101 @@ class Tetris
|
|
61
76
|
|
62
77
|
def add_to_playfield
|
63
78
|
update_playfield_block do |playfield_row, playfield_column, row_index, column_index|
|
64
|
-
|
79
|
+
playfield[playfield_row][playfield_column].color = blocks[row_index][column_index].color if playfield_row >= 0 && playfield[playfield_row][playfield_column]&.clear? && !blocks[row_index][column_index].clear?
|
65
80
|
end
|
66
81
|
end
|
67
82
|
|
68
83
|
def remove_from_playfield
|
69
84
|
return if @row.nil? || @column.nil?
|
70
85
|
update_playfield_block do |playfield_row, playfield_column, row_index, column_index|
|
71
|
-
|
86
|
+
playfield[playfield_row][playfield_column].clear if playfield_row >= 0 && !blocks[row_index][column_index].clear? && playfield[playfield_row][playfield_column]&.color == color
|
72
87
|
end
|
73
88
|
end
|
74
89
|
|
75
|
-
def stopped?
|
76
|
-
|
90
|
+
def stopped?
|
91
|
+
return true if @stopped || @preview
|
77
92
|
playfield_remaining_heights = Game.playfield_remaining_heights(self)
|
78
|
-
result =
|
79
|
-
playfield_column = @column +
|
80
|
-
|
81
|
-
|
93
|
+
result = bottom_most_blocks.any? do |bottom_most_block|
|
94
|
+
playfield_column = @column + bottom_most_block[:column_index]
|
95
|
+
playfield_remaining_heights[playfield_column] &&
|
96
|
+
@row + bottom_most_block[:row] >= playfield_remaining_heights[playfield_column] - 1
|
97
|
+
end
|
98
|
+
if result && !Game.hypothetical?
|
99
|
+
@stopped = result
|
100
|
+
Game.consider_eliminating_lines
|
101
|
+
Model::Game.consider_adding_tetromino
|
82
102
|
end
|
83
|
-
Game.consider_eliminating_lines if result
|
84
103
|
result
|
85
104
|
end
|
86
105
|
|
87
|
-
# Returns blocks
|
88
|
-
def
|
89
|
-
blocks ||= @blocks
|
106
|
+
# Returns bottom-most blocks of a tetromino, which could be from multiple rows depending on shape (e.g. T)
|
107
|
+
def bottom_most_blocks
|
90
108
|
width.times.map do |column_index|
|
91
109
|
row_blocks_with_row_index = @blocks.each_with_index.to_a.reverse.detect do |row_blocks, row_index|
|
92
110
|
!row_blocks[column_index].clear?
|
93
111
|
end
|
94
|
-
|
95
|
-
|
112
|
+
bottom_most_block = row_blocks_with_row_index[0][column_index]
|
113
|
+
bottom_most_block_row = row_blocks_with_row_index[1]
|
96
114
|
{
|
97
|
-
block:
|
98
|
-
row:
|
115
|
+
block: bottom_most_block,
|
116
|
+
row: bottom_most_block_row,
|
99
117
|
column_index: column_index
|
100
118
|
}
|
101
119
|
end
|
102
120
|
end
|
103
121
|
|
104
|
-
def
|
105
|
-
|
122
|
+
def bottom_most_block_for_column(column)
|
123
|
+
bottom_most_blocks.detect {|bottom_most_block| (@column + bottom_most_block[:column_index]) == column}
|
106
124
|
end
|
107
125
|
|
108
126
|
def right_blocked?
|
109
|
-
(@column == PLAYFIELD_WIDTH - width) ||
|
127
|
+
(@column == PLAYFIELD_WIDTH - width) ||
|
128
|
+
right_most_blocks.any? { |right_most_block|
|
129
|
+
playfield[@row + right_most_block[:row_index]][@column + right_most_block[:column_index] + 1].occupied?
|
130
|
+
}
|
110
131
|
end
|
111
132
|
|
112
|
-
|
113
|
-
|
133
|
+
# Returns right-most blocks of a tetromino, which could be from multiple columns depending on shape (e.g. T)
|
134
|
+
def right_most_blocks
|
135
|
+
@blocks.each_with_index.map do |row_blocks, row_index|
|
136
|
+
column_block_with_column_index = row_blocks.each_with_index.to_a.reverse.detect do |column_block, column_index|
|
137
|
+
!column_block.clear?
|
138
|
+
end
|
139
|
+
if column_block_with_column_index
|
140
|
+
right_most_block = column_block_with_column_index[0]
|
141
|
+
{
|
142
|
+
block: right_most_block,
|
143
|
+
row_index: row_index,
|
144
|
+
column_index: column_block_with_column_index[1]
|
145
|
+
}
|
146
|
+
end
|
147
|
+
end.compact
|
114
148
|
end
|
115
149
|
|
150
|
+
def left_blocked?
|
151
|
+
(@column == 0) ||
|
152
|
+
left_most_blocks.any? { |left_most_block|
|
153
|
+
playfield[@row + left_most_block[:row_index]][@column + left_most_block[:column_index] - 1].occupied?
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
# Returns right-most blocks of a tetromino, which could be from multiple columns depending on shape (e.g. T)
|
158
|
+
def left_most_blocks
|
159
|
+
@blocks.each_with_index.map do |row_blocks, row_index|
|
160
|
+
column_block_with_column_index = row_blocks.each_with_index.to_a.detect do |column_block, column_index|
|
161
|
+
!column_block.clear?
|
162
|
+
end
|
163
|
+
if column_block_with_column_index
|
164
|
+
left_most_block = column_block_with_column_index[0]
|
165
|
+
{
|
166
|
+
block: left_most_block,
|
167
|
+
row_index: row_index,
|
168
|
+
column_index: column_block_with_column_index[1]
|
169
|
+
}
|
170
|
+
end
|
171
|
+
end.compact
|
172
|
+
end
|
173
|
+
|
116
174
|
def width
|
117
175
|
@blocks[0].size
|
118
176
|
end
|
@@ -122,6 +180,7 @@ class Tetris
|
|
122
180
|
end
|
123
181
|
|
124
182
|
def down
|
183
|
+
launch! if preview?
|
125
184
|
unless stopped?
|
126
185
|
new_row = @row + 1
|
127
186
|
update_playfield(new_row, @column)
|
@@ -145,20 +204,42 @@ class Tetris
|
|
145
204
|
# Rotate in specified direcation, which can be :right (clockwise) or :left (counterclockwise)
|
146
205
|
def rotate(direction)
|
147
206
|
return if stopped?
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
new_blocks =
|
153
|
-
|
154
|
-
new_blocks = new_blocks.reverse
|
207
|
+
can_rotate = nil
|
208
|
+
new_blocks = nil
|
209
|
+
Game.hypothetical do
|
210
|
+
hypothetical_rotated_tetromino = hypothetical_tetromino
|
211
|
+
new_blocks = hypothetical_rotated_tetromino.rotate_blocks(direction)
|
212
|
+
can_rotate = !hypothetical_rotated_tetromino.stopped? && !hypothetical_rotated_tetromino.right_blocked? && !hypothetical_rotated_tetromino.left_blocked?
|
155
213
|
end
|
156
|
-
|
157
|
-
unless stopped?(blocks: new_blocks) || right_blocked? || left_blocked?
|
214
|
+
if can_rotate
|
158
215
|
remove_from_playfield
|
216
|
+
self.orientation = ORIENTATIONS[ORIENTATIONS.rotate(direction == :right ? -1 : 1).index(@orientation)]
|
159
217
|
self.blocks = new_blocks
|
160
218
|
update_playfield(@row, @column)
|
161
219
|
end
|
220
|
+
rescue => e
|
221
|
+
puts e.full_message
|
222
|
+
end
|
223
|
+
|
224
|
+
def rotate_blocks(direction)
|
225
|
+
new_blocks = Matrix[*@blocks].transpose.to_a
|
226
|
+
if direction == :right
|
227
|
+
new_blocks = new_blocks.map(&:reverse)
|
228
|
+
else
|
229
|
+
new_blocks = new_blocks.reverse
|
230
|
+
end
|
231
|
+
Matrix[*new_blocks].to_a
|
232
|
+
end
|
233
|
+
|
234
|
+
def hypothetical_tetromino
|
235
|
+
clone.tap do |hypo_clone|
|
236
|
+
remove_from_playfield
|
237
|
+
hypo_clone.blocks = @blocks.map do |row_blocks|
|
238
|
+
row_blocks.map do |column_block|
|
239
|
+
column_block.clone
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
162
243
|
end
|
163
244
|
|
164
245
|
def default_blocks
|
@@ -204,6 +285,10 @@ class Tetris
|
|
204
285
|
LETTER_COLORS[@letter]
|
205
286
|
end
|
206
287
|
|
288
|
+
def include_block?(block)
|
289
|
+
@blocks.flatten.include?(block)
|
290
|
+
end
|
291
|
+
|
207
292
|
private
|
208
293
|
|
209
294
|
def block
|
@@ -24,16 +24,13 @@ class Tetris
|
|
24
24
|
class Block
|
25
25
|
include Glimmer::UI::CustomWidget
|
26
26
|
|
27
|
-
options :block_size, :row, :column
|
27
|
+
options :game_playfield, :block_size, :row, :column
|
28
28
|
|
29
29
|
body {
|
30
30
|
composite {
|
31
31
|
layout nil
|
32
|
-
|
33
|
-
|
34
|
-
height_hint block_size
|
35
|
-
}
|
36
|
-
background bind(Model::Game.playfield[row][column], :color)
|
32
|
+
background bind(game_playfield[row][column], :color)
|
33
|
+
# TODO improve shapes to have a bevel look
|
37
34
|
rectangle(0, 0, block_size, block_size)
|
38
35
|
rectangle(3, 3, block_size - 6, block_size - 6) {
|
39
36
|
foreground :gray
|
@@ -0,0 +1,72 @@
|
|
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 View
|
24
|
+
class GameOverDialog
|
25
|
+
include Glimmer::UI::CustomShell
|
26
|
+
|
27
|
+
options :parent_shell
|
28
|
+
|
29
|
+
body {
|
30
|
+
dialog(parent_shell) {
|
31
|
+
row_layout {
|
32
|
+
type :vertical
|
33
|
+
center true
|
34
|
+
}
|
35
|
+
text 'Tetris'
|
36
|
+
|
37
|
+
label(:center) {
|
38
|
+
text 'Game Over!'
|
39
|
+
font name: 'Menlo', height: 30, style: :bold
|
40
|
+
}
|
41
|
+
label # filler
|
42
|
+
button {
|
43
|
+
text 'Play Again?'
|
44
|
+
|
45
|
+
on_widget_selected {
|
46
|
+
Model::Game.restart
|
47
|
+
|
48
|
+
body_root.close
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
on_shell_activated {
|
53
|
+
Model::Game.game_over = true
|
54
|
+
display.beep
|
55
|
+
}
|
56
|
+
|
57
|
+
on_shell_closed {
|
58
|
+
exit_game
|
59
|
+
}
|
60
|
+
|
61
|
+
on_key_pressed { |event|
|
62
|
+
exit_game if event.keyCode == swt(:cr)
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
def exit_game
|
68
|
+
display.dispose if Model::Game.game_over?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -26,20 +26,27 @@ class Tetris
|
|
26
26
|
class Playfield
|
27
27
|
include Glimmer::UI::CustomWidget
|
28
28
|
|
29
|
-
options :playfield_width, :playfield_height, :block_size
|
29
|
+
options :game_playfield, :playfield_width, :playfield_height, :block_size
|
30
30
|
|
31
31
|
body {
|
32
32
|
composite {
|
33
|
-
grid_layout
|
33
|
+
grid_layout {
|
34
|
+
num_columns playfield_width
|
35
|
+
make_columns_equal_width true
|
34
36
|
margin_width block_size
|
35
37
|
margin_height block_size
|
36
38
|
horizontal_spacing 0
|
37
39
|
vertical_spacing 0
|
38
40
|
}
|
39
|
-
|
41
|
+
|
40
42
|
playfield_height.times { |row|
|
41
43
|
playfield_width.times { |column|
|
42
|
-
block(block_size: block_size, row: row, column: column)
|
44
|
+
block(game_playfield: game_playfield, block_size: block_size, row: row, column: column) {
|
45
|
+
layout_data {
|
46
|
+
width_hint block_size
|
47
|
+
height_hint block_size
|
48
|
+
}
|
49
|
+
}
|
43
50
|
}
|
44
51
|
}
|
45
52
|
}
|
@@ -0,0 +1,87 @@
|
|
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 'playfield'
|
24
|
+
|
25
|
+
class Tetris
|
26
|
+
module View
|
27
|
+
class ScoreLane
|
28
|
+
include Glimmer::UI::CustomWidget
|
29
|
+
|
30
|
+
options :block_size
|
31
|
+
|
32
|
+
before_body {
|
33
|
+
@font_name = 'Menlo'
|
34
|
+
@font_height = 32
|
35
|
+
}
|
36
|
+
|
37
|
+
body {
|
38
|
+
composite {
|
39
|
+
row_layout {
|
40
|
+
type :vertical
|
41
|
+
center true
|
42
|
+
fill true
|
43
|
+
margin_width 0
|
44
|
+
margin_right block_size
|
45
|
+
margin_height block_size
|
46
|
+
}
|
47
|
+
label(:center) {
|
48
|
+
text 'Next'
|
49
|
+
font name: @font_name, height: @font_height, style: :bold
|
50
|
+
}
|
51
|
+
playfield(game_playfield: Model::Game.preview_playfield, playfield_width: PREVIEW_PLAYFIELD_WIDTH, playfield_height: PREVIEW_PLAYFIELD_HEIGHT, block_size: BLOCK_SIZE)
|
52
|
+
|
53
|
+
label(:center) {
|
54
|
+
text 'Score'
|
55
|
+
font name: @font_name, height: @font_height, style: :bold
|
56
|
+
}
|
57
|
+
label(:center) {
|
58
|
+
text bind(Model::Game, :score)
|
59
|
+
font height: @font_height
|
60
|
+
}
|
61
|
+
|
62
|
+
label # spacer
|
63
|
+
|
64
|
+
label(:center) {
|
65
|
+
text 'Lines'
|
66
|
+
font name: @font_name, height: @font_height, style: :bold
|
67
|
+
}
|
68
|
+
label(:center) {
|
69
|
+
text bind(Model::Game, :lines)
|
70
|
+
font height: @font_height
|
71
|
+
}
|
72
|
+
|
73
|
+
label # spacer
|
74
|
+
|
75
|
+
label(:center) {
|
76
|
+
text 'Level'
|
77
|
+
font name: @font_name, height: @font_height, style: :bold
|
78
|
+
}
|
79
|
+
label(:center) {
|
80
|
+
text bind(Model::Game, :level)
|
81
|
+
font height: @font_height
|
82
|
+
}
|
83
|
+
}
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glimmer-dsl-swt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.18.2.
|
4
|
+
version: 4.18.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- AndyMaleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-01-
|
11
|
+
date: 2021-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -470,7 +470,9 @@ files:
|
|
470
470
|
- samples/elaborate/tetris/model/game.rb
|
471
471
|
- samples/elaborate/tetris/model/tetromino.rb
|
472
472
|
- samples/elaborate/tetris/view/block.rb
|
473
|
+
- samples/elaborate/tetris/view/game_over_dialog.rb
|
473
474
|
- samples/elaborate/tetris/view/playfield.rb
|
475
|
+
- samples/elaborate/tetris/view/score_lane.rb
|
474
476
|
- samples/elaborate/tic_tac_toe.rb
|
475
477
|
- samples/elaborate/tic_tac_toe/board.rb
|
476
478
|
- samples/elaborate/tic_tac_toe/cell.rb
|