glimmer-dsl-gtk 0.0.2 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/README.md +1072 -27
- data/VERSION +1 -1
- data/glimmer-dsl-gtk.gemspec +0 -0
- data/images/breaking-blue-wave.png +0 -0
- data/lib/glimmer/dsl/gtk/observe_expression.rb +35 -0
- data/lib/glimmer/gtk/shape/arc_negative.rb +70 -0
- data/lib/glimmer/gtk/shape/path.rb +33 -0
- data/lib/glimmer/gtk/shape/square.rb +76 -0
- data/lib/glimmer/gtk/shape.rb +56 -10
- data/lib/glimmer/gtk/transformable.rb +93 -0
- data/lib/glimmer/gtk/widget_proxy/drawing_area_proxy.rb +16 -0
- data/lib/glimmer/gtk/widget_proxy.rb +2 -2
- data/samples/cairo/arc.rb +44 -0
- data/samples/cairo/arc_negative.rb +44 -0
- data/samples/cairo/clip.rb +34 -0
- data/samples/cairo/clip_image.rb +28 -0
- data/samples/cairo/curve_to.rb +39 -0
- data/samples/cairo/dashes.rb +30 -0
- data/samples/cairo/fill_and_stroke2.rb +36 -0
- data/samples/cairo/fill_style.rb +43 -0
- data/samples/cairo/gradient.rb +31 -0
- data/samples/cairo/image.rb +23 -0
- data/samples/cairo/image_gradient.rb +32 -0
- data/samples/cairo/multi_segment_caps.rb +27 -0
- data/samples/cairo/rounded_rectangle.rb +20 -0
- data/samples/cairo/set_line_cap.rb +53 -0
- data/samples/cairo/set_line_join.rb +43 -0
- data/samples/elaborate/tetris/model/block.rb +48 -0
- data/samples/elaborate/tetris/model/game.rb +320 -0
- data/samples/elaborate/tetris/model/past_game.rb +39 -0
- data/samples/elaborate/tetris/model/tetromino.rb +329 -0
- data/samples/elaborate/tetris.rb +338 -0
- data/samples/hello/hello_drawing_area.rb +1 -3
- data/samples/hello/hello_drawing_area_manual.rb +20 -21
- metadata +29 -4
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 GTK 0.0.
|
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 GTK 0.0.6
|
2
2
|
## Ruby-GNOME Desktop Development GUI Library
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/glimmer-dsl-gtk.svg)](http://badge.fury.io/rb/glimmer-dsl-gtk)
|
4
4
|
[![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
@@ -30,7 +30,7 @@ Linux | Mac | Windows
|
|
30
30
|
------|-----|--------
|
31
31
|
![hello world screenshot Windows](/screenshots/glimmer-dsl-gtk-linux-hello-world.png) | ![hello world screenshot Mac](/screenshots/glimmer-dsl-gtk-mac-hello-world.png) | ![hello world screenshot Windows](/screenshots/glimmer-dsl-gtk-windows-hello-world.png)
|
32
32
|
|
33
|
-
NOTE: Glimmer DSL for GTK is currently in early alpha mode (incomplete proof-of-concept). If you want it developed faster, then [open an issue report](https://github.com/AndyObtiva/glimmer-dsl-gtk/issues/new). I have completed some GitHub project features much faster before due to [issue reports](https://github.com/AndyObtiva/glimmer-dsl-gtk/issues) and [pull requests](https://github.com/AndyObtiva/glimmer-dsl-gtk/pulls). Please help make better by contributing, adopting for small or low risk projects, and providing feedback. It is still an early alpha, so the more feedback and issues you report the better.
|
33
|
+
NOTE: Glimmer DSL for GTK is currently in early alpha mode (incomplete proof-of-concept). If you want it developed faster, then [open an issue report](https://github.com/AndyObtiva/glimmer-dsl-gtk/issues/new). I have completed some GitHub project features much faster before due to [issue reports](https://github.com/AndyObtiva/glimmer-dsl-gtk/issues) and [pull requests](https://github.com/AndyObtiva/glimmer-dsl-gtk/pulls). Please help make better by contributing, adopting for small or low risk projects, and providing feedback. It is still an early alpha, so the more feedback and issues you report the better.
|
34
34
|
|
35
35
|
Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interested in:
|
36
36
|
- [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
|
@@ -61,6 +61,7 @@ sudo apt install -y -V libgirepository1.0-dev
|
|
61
61
|
On the Mac, make sure to:
|
62
62
|
- Have [Homebrew](https://brew.sh/) installed
|
63
63
|
- Run this [Homebrew](https://brew.sh/) command to have GTK display GUI icons: `brew install adwaita-icon-theme`
|
64
|
+
- (Optional) You can upgrade your GTK3/GTK4/GTK+ by running: `brew install gtk+3` / `brew install gtk+4` / `brew install gtk+`
|
64
65
|
|
65
66
|
### Windows
|
66
67
|
|
@@ -79,7 +80,7 @@ gem install glimmer-dsl-gtk
|
|
79
80
|
|
80
81
|
Add the following to `Gemfile`:
|
81
82
|
```
|
82
|
-
gem 'glimmer-dsl-gtk', '~> 0.0.
|
83
|
+
gem 'glimmer-dsl-gtk', '~> 0.0.6'
|
83
84
|
```
|
84
85
|
|
85
86
|
And, then run:
|
@@ -135,6 +136,690 @@ SomeGlimmerApplication.new.launch
|
|
135
136
|
- Properties: All GTK widget properties can be set via lowercase underscored names (without the 'set_' prefix) nested under widget keywords (e.g. `window {title 'Hello, World'}` sets `title` property of `window`)
|
136
137
|
- Signals: All GTK signals can be wired with `on(signal) { ... }` syntax (e.g. `on(:activate) { do_something }`)
|
137
138
|
|
139
|
+
### MVC Observer Pattern
|
140
|
+
|
141
|
+
In Smalltalk-MVC ([Model View Controller](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) Architectural Pattern), the View is an active View that observes the Model for changes and updates itself.
|
142
|
+
|
143
|
+
![MVC](http://3.bp.blogspot.com/-4eW59Ao0ess/ToiBzAiYdZI/AAAAAAAAAOg/SiYa6XHwBFE/s320/Screen+shot+2011-10-02+at+10.22.11+AM.png)
|
144
|
+
|
145
|
+
This can be achieved with the Glimmer GUI DSL using the `observe` keyword, which takes a model (any object, including `self`) and attribute Symbol or String expression (e.g. `:count` or `'address.street'`).
|
146
|
+
|
147
|
+
The model is automatically enhanced as an `Glimmer::DataBinding::ObservableModel` / `Glimmer::DataBinding::ObservableHash` / `Glimmer::DataBinding::ObservableArray` depending on its type to support notifying observers of attribute changes (when performed using the attribute writer, which automatically calls added method `notify_observers(attribute)`)
|
148
|
+
|
149
|
+
Note that it is usually recommended to observe external model objects (not `self`), but `self` is OK in very simple cases or presentation-related attributes only.
|
150
|
+
|
151
|
+
### Declarative Cairo Graphics
|
152
|
+
|
153
|
+
[Cairo](https://www.cairographics.org/) is the engine behind drawing arbitrary 2D geometric shapes in [GTK](https://www.gtk.org/).
|
154
|
+
|
155
|
+
In [Glimmer DSL for GTK](https://rubygems.org/gems/glimmer-dsl-gtk), you can draw Cairo shapes declaratively in a way similar to how SVG works, but using one language; Ruby, thus being able to utilize Ruby logic (e.g. if statement or each loop) with it effortlessly when needed. Declarative syntax also yields less code that is simpler, not dependent on ordering of nested properties, and more understandable/maintainable.
|
156
|
+
|
157
|
+
Below is a quick tutorial consisting of samples inspired and ported from [Mohit Sindhwani's blog post "Cairo with Ruby - Samples using RCairo"](https://notepad.onghu.com/2021/cairo-samples-in-ruby/).
|
158
|
+
|
159
|
+
### Arc
|
160
|
+
|
161
|
+
[samples/cairo/arc.rb](/samples/cairo/arc.rb)
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
require 'glimmer-dsl-gtk'
|
165
|
+
|
166
|
+
include Glimmer
|
167
|
+
|
168
|
+
window {
|
169
|
+
title 'Hello, Drawing Area!'
|
170
|
+
default_size 256, 256
|
171
|
+
|
172
|
+
drawing_area {
|
173
|
+
# Surface Paint
|
174
|
+
paint 242.25, 242.25, 242.25
|
175
|
+
|
176
|
+
# Set up the parameters
|
177
|
+
xc = 128.0
|
178
|
+
yc = 128.0
|
179
|
+
radius = 100.0
|
180
|
+
angle1 = 45.0 * (Math::PI/180.0) # angles are specified
|
181
|
+
angle2 = 180.0 * (Math::PI/180.0) # in radians
|
182
|
+
|
183
|
+
# The main arc
|
184
|
+
arc(xc, yc, radius, angle1, angle2) {
|
185
|
+
stroke 0, 0, 0
|
186
|
+
line_width 10
|
187
|
+
}
|
188
|
+
|
189
|
+
# Draw helping lines
|
190
|
+
|
191
|
+
# First, the circle at the centre
|
192
|
+
arc(xc, yc, 10.0, 0, 2*Math::PI) {
|
193
|
+
fill 255, 51, 51, 0.6
|
194
|
+
}
|
195
|
+
|
196
|
+
# Then, the lines reaching out
|
197
|
+
path {
|
198
|
+
arc xc, yc, radius, angle1, angle1
|
199
|
+
line_to xc, yc
|
200
|
+
arc xc, yc, radius, angle2, angle2
|
201
|
+
line_to xc, yc
|
202
|
+
|
203
|
+
stroke 255, 51, 51, 0.6
|
204
|
+
line_width 6
|
205
|
+
}
|
206
|
+
}
|
207
|
+
}.show
|
208
|
+
```
|
209
|
+
|
210
|
+
![Arc](/screenshots/glimmer-dsl-gtk-mac-cairo-arc.png)
|
211
|
+
|
212
|
+
### Arc Negative
|
213
|
+
|
214
|
+
[samples/cairo/arc_negative.rb](/samples/cairo/arc_negative.rb)
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
require 'glimmer-dsl-gtk'
|
218
|
+
|
219
|
+
include Glimmer
|
220
|
+
|
221
|
+
window {
|
222
|
+
title 'Arc Negative'
|
223
|
+
default_size 256, 256
|
224
|
+
|
225
|
+
drawing_area {
|
226
|
+
# Surface Paint
|
227
|
+
paint 255, 255, 255
|
228
|
+
|
229
|
+
# Set up the parameters
|
230
|
+
xc = 128.0
|
231
|
+
yc = 128.0
|
232
|
+
radius = 100.0
|
233
|
+
angle1 = 45.0 * (Math::PI/180.0) # angles are specified
|
234
|
+
angle2 = 180.0 * (Math::PI/180.0) # in radians
|
235
|
+
|
236
|
+
# The main negative arc
|
237
|
+
arc_negative(xc, yc, radius, angle1, angle2) {
|
238
|
+
stroke 0, 0, 0
|
239
|
+
line_width 10
|
240
|
+
}
|
241
|
+
|
242
|
+
# Draw helping lines
|
243
|
+
|
244
|
+
# First, the circle at the centre
|
245
|
+
arc(xc, yc, 10.0, 0, 2*Math::PI) {
|
246
|
+
fill 255, 51, 51, 0.6
|
247
|
+
}
|
248
|
+
|
249
|
+
# Then, the lines reaching out
|
250
|
+
path {
|
251
|
+
arc(xc, yc, radius, angle1, angle1)
|
252
|
+
line_to(xc, yc)
|
253
|
+
arc(xc, yc, radius, angle2, angle2)
|
254
|
+
line_to(xc, yc)
|
255
|
+
|
256
|
+
stroke 255, 51, 51, 0.6
|
257
|
+
line_width 6
|
258
|
+
}
|
259
|
+
}
|
260
|
+
}.show
|
261
|
+
```
|
262
|
+
|
263
|
+
![Arc Negative](/screenshots/glimmer-dsl-gtk-mac-cairo-arc-negative.png)
|
264
|
+
|
265
|
+
### Clip
|
266
|
+
|
267
|
+
[samples/cairo/clip.rb](/samples/cairo/clip.rb)
|
268
|
+
|
269
|
+
```ruby
|
270
|
+
require 'glimmer-dsl-gtk'
|
271
|
+
|
272
|
+
include Glimmer
|
273
|
+
|
274
|
+
window {
|
275
|
+
title 'Clip'
|
276
|
+
default_size 256, 256
|
277
|
+
|
278
|
+
drawing_area {
|
279
|
+
# Surface Paint
|
280
|
+
paint 255, 255, 255
|
281
|
+
|
282
|
+
# Designate arc as the clipping area
|
283
|
+
arc(128.0, 128.0, 76.8, 0, 2 * Math::PI) {
|
284
|
+
clip true
|
285
|
+
}
|
286
|
+
|
287
|
+
# Rectangle will get clipped by arc
|
288
|
+
rectangle(0, 0, 256, 256) {
|
289
|
+
fill 0, 0, 0
|
290
|
+
}
|
291
|
+
|
292
|
+
# Path will get clipped by arc
|
293
|
+
path {
|
294
|
+
move_to 0, 0
|
295
|
+
line_to 256, 256
|
296
|
+
move_to 256, 0
|
297
|
+
line_to 0, 256
|
298
|
+
|
299
|
+
stroke 0, 255, 0
|
300
|
+
line_width 10
|
301
|
+
}
|
302
|
+
}
|
303
|
+
}.show
|
304
|
+
```
|
305
|
+
|
306
|
+
![Clip](/screenshots/glimmer-dsl-gtk-mac-cairo-clip.png)
|
307
|
+
|
308
|
+
### Clip Image
|
309
|
+
|
310
|
+
[samples/cairo/clip_image.rb](/samples/cairo/clip_image.rb)
|
311
|
+
|
312
|
+
```ruby
|
313
|
+
require 'glimmer-dsl-gtk'
|
314
|
+
|
315
|
+
include Glimmer
|
316
|
+
|
317
|
+
window {
|
318
|
+
title 'Clip Image'
|
319
|
+
default_size 256, 256
|
320
|
+
|
321
|
+
drawing_area {
|
322
|
+
paint 242.25, 242.25, 242.25
|
323
|
+
|
324
|
+
arc(128.0, 128.0, 76.8, 0, 2 * Math::PI) {
|
325
|
+
clip true # designate arc as the clipping area
|
326
|
+
}
|
327
|
+
|
328
|
+
rectangle(0, 0, 256, 256) {
|
329
|
+
# Source image is from:
|
330
|
+
# - https://www.publicdomainpictures.net/en/view-image.php?image=7683&picture=breaking-blue-wave
|
331
|
+
# Converted to PNG before using it
|
332
|
+
image = Cairo::ImageSurface.from_png(File.expand_path(File.join('..', '..', 'images', 'breaking-blue-wave.png'), __dir__))
|
333
|
+
w = image.width
|
334
|
+
h = image.height
|
335
|
+
|
336
|
+
scale 256.0/w, 256.0/h, exclude: :shape # applies scale to fill source image only
|
337
|
+
fill image, 0, 0
|
338
|
+
}
|
339
|
+
}
|
340
|
+
}.show
|
341
|
+
```
|
342
|
+
|
343
|
+
![Clip Image](/screenshots/glimmer-dsl-gtk-mac-cairo-clip-image.png)
|
344
|
+
|
345
|
+
### Curve to
|
346
|
+
|
347
|
+
[samples/cairo/curve_to.rb](/samples/cairo/curve_to.rb)
|
348
|
+
|
349
|
+
```ruby
|
350
|
+
require 'glimmer-dsl-gtk'
|
351
|
+
|
352
|
+
include Glimmer
|
353
|
+
|
354
|
+
window {
|
355
|
+
title 'Curve to'
|
356
|
+
default_size 256, 256
|
357
|
+
|
358
|
+
drawing_area {
|
359
|
+
paint 242.25, 242.25, 242.25
|
360
|
+
|
361
|
+
x=25.6
|
362
|
+
y=128.0
|
363
|
+
x1=102.4
|
364
|
+
y1=230.4
|
365
|
+
x2=153.6
|
366
|
+
y2=25.6
|
367
|
+
x3=230.4
|
368
|
+
y3=128.0
|
369
|
+
|
370
|
+
path {
|
371
|
+
move_to x, y
|
372
|
+
curve_to x1, y1, x2, y2, x3, y3
|
373
|
+
|
374
|
+
line_width 10
|
375
|
+
stroke 0, 0, 0
|
376
|
+
}
|
377
|
+
|
378
|
+
path {
|
379
|
+
move_to x,y
|
380
|
+
line_to x1,y1
|
381
|
+
move_to x2,y2
|
382
|
+
line_to x3,y3
|
383
|
+
|
384
|
+
line_width 6
|
385
|
+
stroke 255, 51, 51, 0.6
|
386
|
+
}
|
387
|
+
}
|
388
|
+
}.show
|
389
|
+
```
|
390
|
+
|
391
|
+
![Curve to](/screenshots/glimmer-dsl-gtk-mac-cairo-curve-to.png)
|
392
|
+
|
393
|
+
### Dashes
|
394
|
+
|
395
|
+
[samples/cairo/dashes.rb](/samples/cairo/dashes.rb)
|
396
|
+
|
397
|
+
```ruby
|
398
|
+
require 'glimmer-dsl-gtk'
|
399
|
+
|
400
|
+
include Glimmer
|
401
|
+
|
402
|
+
window {
|
403
|
+
title 'Dashes'
|
404
|
+
default_size 256, 256
|
405
|
+
|
406
|
+
drawing_area {
|
407
|
+
paint 242.25, 242.25, 242.25
|
408
|
+
|
409
|
+
dashes = [ 50.0, # ink
|
410
|
+
10.0, # skip
|
411
|
+
10.0, # ink
|
412
|
+
10.0 # skip
|
413
|
+
]
|
414
|
+
offset = -50.0
|
415
|
+
|
416
|
+
path {
|
417
|
+
move_to 128.0, 25.6
|
418
|
+
line_to 230.4, 230.4
|
419
|
+
rel_line_to -102.4, 0.0
|
420
|
+
curve_to 51.2, 230.4, 51.2, 128.0, 128.0, 128.0
|
421
|
+
|
422
|
+
line_width 10
|
423
|
+
dash dashes, offset
|
424
|
+
stroke 0, 0, 0
|
425
|
+
}
|
426
|
+
}
|
427
|
+
}.show
|
428
|
+
```
|
429
|
+
|
430
|
+
![Dashes](/screenshots/glimmer-dsl-gtk-mac-cairo-dashes.png)
|
431
|
+
|
432
|
+
### Fill and Stroke 2
|
433
|
+
|
434
|
+
(note: there is no Fill and Stroke 1; this was adopted from [Mohit's blog post](https://notepad.onghu.com/2021/cairo-samples-in-ruby/), which only mentioned Fill and Stroke 2)
|
435
|
+
|
436
|
+
[samples/cairo/fill_and_stroke2.rb](/samples/cairo/fill_and_stroke2.rb)
|
437
|
+
|
438
|
+
```ruby
|
439
|
+
require 'glimmer-dsl-gtk'
|
440
|
+
|
441
|
+
include Glimmer
|
442
|
+
|
443
|
+
window {
|
444
|
+
title 'Fill and Stroke 2'
|
445
|
+
default_size 256, 256
|
446
|
+
|
447
|
+
drawing_area {
|
448
|
+
paint 242.25, 242.25, 242.25
|
449
|
+
|
450
|
+
path {
|
451
|
+
move_to 128.0, 25.6
|
452
|
+
line_to 230.4, 230.4
|
453
|
+
rel_line_to -102.4, 0.0
|
454
|
+
curve_to 51.2, 230.4, 51.2, 128.0, 128.0, 128.0
|
455
|
+
close_path
|
456
|
+
|
457
|
+
fill 0, 0, 255
|
458
|
+
stroke 0, 0, 0
|
459
|
+
line_width 10
|
460
|
+
}
|
461
|
+
|
462
|
+
path {
|
463
|
+
move_to 64.0, 25.6
|
464
|
+
rel_line_to 51.2, 51.2
|
465
|
+
rel_line_to -51.2, 51.2
|
466
|
+
rel_line_to -51.2, -51.2
|
467
|
+
close_path
|
468
|
+
|
469
|
+
fill 0, 0, 255
|
470
|
+
stroke 0, 0, 0
|
471
|
+
line_width 10
|
472
|
+
}
|
473
|
+
}
|
474
|
+
}.show
|
475
|
+
```
|
476
|
+
|
477
|
+
![Fill and Stroke 2](/screenshots/glimmer-dsl-gtk-mac-cairo-fill-and-stroke2.png)
|
478
|
+
|
479
|
+
### Fill Style
|
480
|
+
|
481
|
+
[samples/cairo/fill_style.rb](/samples/cairo/fill_style.rb)
|
482
|
+
|
483
|
+
```ruby
|
484
|
+
require 'glimmer-dsl-gtk'
|
485
|
+
|
486
|
+
include Glimmer
|
487
|
+
|
488
|
+
window {
|
489
|
+
title 'Fill Style'
|
490
|
+
default_size 256, 256
|
491
|
+
|
492
|
+
drawing_area {
|
493
|
+
paint 242.25, 242.25, 242.25
|
494
|
+
|
495
|
+
path {
|
496
|
+
rectangle 12, 12, 232, 70
|
497
|
+
path { # sub-path
|
498
|
+
arc 64, 64, 40, 0, 2*Math::PI
|
499
|
+
}
|
500
|
+
path { # sub-path
|
501
|
+
arc_negative 192, 64, 40, 0, -2*Math::PI
|
502
|
+
}
|
503
|
+
|
504
|
+
fill_rule Cairo::FILL_RULE_EVEN_ODD
|
505
|
+
line_width 6
|
506
|
+
fill 0, 178.5, 0
|
507
|
+
stroke 0, 0, 0
|
508
|
+
}
|
509
|
+
|
510
|
+
path {
|
511
|
+
rectangle 12, 12, 232, 70
|
512
|
+
path { # sub-path
|
513
|
+
arc 64, 64, 40, 0, 2*Math::PI
|
514
|
+
}
|
515
|
+
path { # sub-path
|
516
|
+
arc_negative 192, 64, 40, 0, -2*Math::PI
|
517
|
+
}
|
518
|
+
|
519
|
+
translate 0, 128
|
520
|
+
fill_rule Cairo::FILL_RULE_WINDING
|
521
|
+
line_width 6
|
522
|
+
fill 0, 0, 229.5
|
523
|
+
stroke 0, 0, 0
|
524
|
+
}
|
525
|
+
}
|
526
|
+
}.show
|
527
|
+
```
|
528
|
+
|
529
|
+
![Fill Style](/screenshots/glimmer-dsl-gtk-mac-cairo-fill-style.png)
|
530
|
+
|
531
|
+
### Gradient
|
532
|
+
|
533
|
+
[samples/cairo/gradient.rb](/samples/cairo/gradient.rb)
|
534
|
+
|
535
|
+
```ruby
|
536
|
+
require 'glimmer-dsl-gtk'
|
537
|
+
|
538
|
+
include Glimmer
|
539
|
+
|
540
|
+
window {
|
541
|
+
title 'Gradient'
|
542
|
+
default_size 256, 256
|
543
|
+
|
544
|
+
drawing_area {
|
545
|
+
paint 242.25, 242.25, 242.25
|
546
|
+
|
547
|
+
# Create the Linear Pattern
|
548
|
+
rectangle(0, 0, 256, 256) {
|
549
|
+
pat = Cairo::LinearPattern.new(0.0, 0.0, 0.0, 256.0)
|
550
|
+
pat.add_color_stop_rgba(1, 0, 0, 0, 1)
|
551
|
+
pat.add_color_stop_rgba(0, 1, 1, 1, 1)
|
552
|
+
|
553
|
+
fill pat
|
554
|
+
}
|
555
|
+
|
556
|
+
# Create the radial pattern
|
557
|
+
arc(128.0, 128.0, 76.8, 0, 2 * Math::PI) {
|
558
|
+
pat = Cairo::RadialPattern.new(115.2, 102.4, 25.6,
|
559
|
+
102.4, 102.4, 128.0)
|
560
|
+
pat.add_color_stop_rgba(0, 1, 1, 1, 1)
|
561
|
+
pat.add_color_stop_rgba(1, 0, 0, 0, 1)
|
562
|
+
|
563
|
+
fill pat
|
564
|
+
}
|
565
|
+
}
|
566
|
+
}.show
|
567
|
+
```
|
568
|
+
|
569
|
+
![Gradient](/screenshots/glimmer-dsl-gtk-mac-cairo-gradient.png)
|
570
|
+
|
571
|
+
### Image
|
572
|
+
|
573
|
+
[samples/cairo/image.rb](/samples/cairo/image.rb)
|
574
|
+
|
575
|
+
```ruby
|
576
|
+
require 'glimmer-dsl-gtk'
|
577
|
+
|
578
|
+
include Glimmer
|
579
|
+
|
580
|
+
window {
|
581
|
+
title 'Image'
|
582
|
+
default_size 256, 256
|
583
|
+
|
584
|
+
drawing_area {
|
585
|
+
paint 242.25, 242.25, 242.25
|
586
|
+
|
587
|
+
image = Cairo::ImageSurface.from_png(File.expand_path(File.join('..', '..', 'images', 'breaking-blue-wave.png'), __dir__))
|
588
|
+
w = image.width
|
589
|
+
h = image.height
|
590
|
+
|
591
|
+
translate 128.0, 128.0
|
592
|
+
rotate 45*Math::PI/180
|
593
|
+
scale 256.0/w, 256.0/h
|
594
|
+
translate -0.5*w, -0.5*h
|
595
|
+
|
596
|
+
paint image, 0, 0
|
597
|
+
}
|
598
|
+
}.show
|
599
|
+
```
|
600
|
+
|
601
|
+
![Image](/screenshots/glimmer-dsl-gtk-mac-cairo-image.png)
|
602
|
+
|
603
|
+
### Image Gradient
|
604
|
+
|
605
|
+
[samples/cairo/image_gradient.rb](/samples/cairo/image_gradient.rb)
|
606
|
+
|
607
|
+
```ruby
|
608
|
+
require 'glimmer-dsl-gtk'
|
609
|
+
|
610
|
+
include Glimmer
|
611
|
+
|
612
|
+
window {
|
613
|
+
title 'Image Gradient'
|
614
|
+
default_size 256, 256
|
615
|
+
|
616
|
+
drawing_area {
|
617
|
+
paint 242.25, 242.25, 242.25
|
618
|
+
|
619
|
+
image = Cairo::ImageSurface.from_png(File.expand_path(File.join('..', '..', 'images', 'breaking-blue-wave.png'), __dir__))
|
620
|
+
w = image.width
|
621
|
+
h = image.height
|
622
|
+
|
623
|
+
# Load the image as a surface pattern
|
624
|
+
pattern = Cairo::SurfacePattern.new(image)
|
625
|
+
pattern.extend = Cairo::EXTEND_REPEAT
|
626
|
+
|
627
|
+
# Set up the scale matrix
|
628
|
+
pattern.matrix = Cairo::Matrix.scale(w/256.0 * 5.0, h/256.0 * 5.0)
|
629
|
+
|
630
|
+
rectangle(0, 0, 256, 256) {
|
631
|
+
translate 128.0, 128.0
|
632
|
+
rotate Math::PI / 4
|
633
|
+
scale 1/Math.sqrt(2), 1/Math.sqrt(2)
|
634
|
+
translate -128.0, -128.0
|
635
|
+
|
636
|
+
fill pattern
|
637
|
+
}
|
638
|
+
}
|
639
|
+
}.show
|
640
|
+
```
|
641
|
+
|
642
|
+
![Image Gradient](/screenshots/glimmer-dsl-gtk-mac-cairo-image-gradient.png)
|
643
|
+
|
644
|
+
### Multi Segment Caps
|
645
|
+
|
646
|
+
[samples/cairo/multi_segment_caps.rb](/samples/cairo/multi_segment_caps.rb)
|
647
|
+
|
648
|
+
```ruby
|
649
|
+
require 'glimmer-dsl-gtk'
|
650
|
+
|
651
|
+
include Glimmer
|
652
|
+
|
653
|
+
window {
|
654
|
+
title 'Multi Segment Caps'
|
655
|
+
default_size 256, 256
|
656
|
+
|
657
|
+
drawing_area {
|
658
|
+
paint 242.25, 242.25, 242.25
|
659
|
+
|
660
|
+
path {
|
661
|
+
move_to 50.0, 75.0
|
662
|
+
line_to 200.0, 75.0
|
663
|
+
|
664
|
+
move_to 50.0, 125.0
|
665
|
+
line_to 200.0, 125.0
|
666
|
+
|
667
|
+
move_to 50.0, 175.0
|
668
|
+
line_to 200.0, 175.0
|
669
|
+
|
670
|
+
line_width 30
|
671
|
+
line_cap Cairo::LINE_CAP_ROUND
|
672
|
+
stroke 0, 0, 0
|
673
|
+
}
|
674
|
+
}
|
675
|
+
}.show
|
676
|
+
```
|
677
|
+
|
678
|
+
![Multi Segment Caps](/screenshots/glimmer-dsl-gtk-mac-cairo-multi-segment-caps.png)
|
679
|
+
|
680
|
+
### Rounded Rectangle
|
681
|
+
|
682
|
+
[samples/cairo/rounded_rectangle.rb](/samples/cairo/rounded_rectangle.rb)
|
683
|
+
|
684
|
+
```ruby
|
685
|
+
require 'glimmer-dsl-gtk'
|
686
|
+
|
687
|
+
include Glimmer
|
688
|
+
|
689
|
+
window {
|
690
|
+
title 'Rounded Rectangle'
|
691
|
+
default_size 256, 256
|
692
|
+
|
693
|
+
drawing_area {
|
694
|
+
paint 242.25, 242.25, 242.25
|
695
|
+
|
696
|
+
path {
|
697
|
+
rounded_rectangle(25.6, 25.6, 204.8, 204.8, 20)
|
698
|
+
|
699
|
+
fill 127.5, 127.5, 255
|
700
|
+
line_width 10.0
|
701
|
+
stroke 127.5, 0, 0, 0.5
|
702
|
+
}
|
703
|
+
}
|
704
|
+
}.show
|
705
|
+
```
|
706
|
+
|
707
|
+
![Rounded Rectangle](/screenshots/glimmer-dsl-gtk-mac-cairo-rounded-rectangle.png)
|
708
|
+
|
709
|
+
### Set line cap
|
710
|
+
|
711
|
+
[samples/cairo/set_line_cap.rb](/samples/cairo/set_line_cap.rb)
|
712
|
+
|
713
|
+
```ruby
|
714
|
+
require 'glimmer-dsl-gtk'
|
715
|
+
|
716
|
+
include Glimmer
|
717
|
+
|
718
|
+
window {
|
719
|
+
title 'Set line cap'
|
720
|
+
default_size 256, 256
|
721
|
+
|
722
|
+
drawing_area {
|
723
|
+
paint 242.25, 242.25, 242.25
|
724
|
+
|
725
|
+
# The main code
|
726
|
+
path {
|
727
|
+
move_to 64.0, 50.0
|
728
|
+
line_to 64.0, 200.0
|
729
|
+
|
730
|
+
line_cap Cairo::LINE_CAP_BUTT # default
|
731
|
+
line_width 30
|
732
|
+
stroke 0, 0, 0
|
733
|
+
}
|
734
|
+
|
735
|
+
path {
|
736
|
+
move_to 128.0, 50.0
|
737
|
+
line_to 128.0, 200.0
|
738
|
+
|
739
|
+
line_cap Cairo::LINE_CAP_ROUND
|
740
|
+
line_width 30
|
741
|
+
stroke 0, 0, 0
|
742
|
+
}
|
743
|
+
|
744
|
+
path {
|
745
|
+
move_to 192.0, 50.0
|
746
|
+
line_to 192.0, 200.0
|
747
|
+
|
748
|
+
line_cap Cairo::LINE_CAP_SQUARE
|
749
|
+
line_width 30
|
750
|
+
stroke 0, 0, 0
|
751
|
+
}
|
752
|
+
|
753
|
+
# draw helping lines */
|
754
|
+
path {
|
755
|
+
move_to 64.0, 50.0
|
756
|
+
line_to 64.0, 200.0
|
757
|
+
move_to 128.0, 50.0
|
758
|
+
line_to 128.0, 200.0
|
759
|
+
move_to 192.0, 50.0
|
760
|
+
line_to 192.0, 200.0
|
761
|
+
|
762
|
+
line_width 2.56
|
763
|
+
stroke 255, 51, 51
|
764
|
+
}
|
765
|
+
}
|
766
|
+
}.show
|
767
|
+
```
|
768
|
+
|
769
|
+
![Set line cap](/screenshots/glimmer-dsl-gtk-mac-cairo-set-line-cap.png)
|
770
|
+
|
771
|
+
### Set line join
|
772
|
+
|
773
|
+
[samples/cairo/set_line_join.rb](/samples/cairo/set_line_join.rb)
|
774
|
+
|
775
|
+
```ruby
|
776
|
+
require 'glimmer-dsl-gtk'
|
777
|
+
|
778
|
+
include Glimmer
|
779
|
+
|
780
|
+
window {
|
781
|
+
title 'Set line join'
|
782
|
+
default_size 256, 256
|
783
|
+
|
784
|
+
drawing_area {
|
785
|
+
paint 242.25, 242.25, 242.25
|
786
|
+
|
787
|
+
# The main code
|
788
|
+
path {
|
789
|
+
move_to 76.8, 84.48
|
790
|
+
rel_line_to 51.2, -51.2
|
791
|
+
rel_line_to 51.2, 51.2
|
792
|
+
|
793
|
+
line_join Cairo::LINE_JOIN_MITER # default
|
794
|
+
line_width 40.96
|
795
|
+
stroke 0, 0, 0
|
796
|
+
}
|
797
|
+
|
798
|
+
path {
|
799
|
+
move_to 76.8, 161.28
|
800
|
+
rel_line_to 51.2, -51.2
|
801
|
+
rel_line_to 51.2, 51.2
|
802
|
+
|
803
|
+
line_join Cairo::LINE_JOIN_BEVEL
|
804
|
+
line_width 40.96
|
805
|
+
stroke 0, 0, 0
|
806
|
+
}
|
807
|
+
|
808
|
+
path {
|
809
|
+
move_to 76.8, 238.08
|
810
|
+
rel_line_to 51.2, -51.2
|
811
|
+
rel_line_to 51.2, 51.2
|
812
|
+
|
813
|
+
line_join Cairo::LINE_JOIN_ROUND
|
814
|
+
line_width 40.96
|
815
|
+
stroke 0, 0, 0
|
816
|
+
}
|
817
|
+
}
|
818
|
+
}.show
|
819
|
+
```
|
820
|
+
|
821
|
+
![Set line join](/screenshots/glimmer-dsl-gtk-mac-cairo-set-line-join.png)
|
822
|
+
|
138
823
|
## Girb (Glimmer IRB)
|
139
824
|
|
140
825
|
You can run the `girb` command (`bin/girb` if you cloned the project locally):
|
@@ -365,9 +1050,7 @@ window {
|
|
365
1050
|
default_size 400, 400
|
366
1051
|
|
367
1052
|
drawing_area {
|
368
|
-
|
369
|
-
fill 255, 255, 255
|
370
|
-
}
|
1053
|
+
paint 255, 255, 255
|
371
1054
|
|
372
1055
|
arc(85, 85, 45, (Math::PI/180)*90, -(Math::PI/180)*90) {
|
373
1056
|
fill 255, 0, 0
|
@@ -479,61 +1162,60 @@ window {
|
|
479
1162
|
|
480
1163
|
drawing_area {
|
481
1164
|
on(:draw) do |drawing_area_widget, cairo_context|
|
482
|
-
cairo_context.
|
483
|
-
cairo_context.
|
484
|
-
cairo_context.fill
|
1165
|
+
cairo_context.set_source_rgb(255/255.0, 255/255.0, 255/255.0)
|
1166
|
+
cairo_context.paint
|
485
1167
|
|
486
1168
|
cairo_context.arc(85, 85, 45, (Math::PI/180)*90, -(Math::PI/180)*90)
|
487
1169
|
cairo_context.set_source_rgb(255, 0, 0)
|
488
1170
|
cairo_context.fill
|
489
1171
|
|
490
1172
|
cairo_context.arc(85, 85, 45, (Math::PI/180)*90, -(Math::PI/180)*90)
|
491
|
-
cairo_context.set_source_rgb(0, 128, 255)
|
1173
|
+
cairo_context.set_source_rgb(0, 128/255.0, 255/255.0)
|
492
1174
|
cairo_context.set_line_width(3)
|
493
1175
|
cairo_context.stroke
|
494
1176
|
|
495
1177
|
cairo_context.arc(85, 185, 45, (Math::PI/180)*100, -(Math::PI/180)*30)
|
496
|
-
cairo_context.set_source_rgb(255, 0, 0)
|
1178
|
+
cairo_context.set_source_rgb(255/255.0, 0, 0)
|
497
1179
|
cairo_context.fill
|
498
1180
|
|
499
1181
|
cairo_context.arc(85, 185, 45, (Math::PI/180)*100, -(Math::PI/180)*30)
|
500
|
-
cairo_context.set_source_rgb(0, 128, 255)
|
1182
|
+
cairo_context.set_source_rgb(0, 128/255.0, 255/255.0)
|
501
1183
|
cairo_context.set_line_width(3)
|
502
1184
|
cairo_context.stroke
|
503
1185
|
|
504
1186
|
cairo_context.circle(85, 285, 45)
|
505
|
-
cairo_context.set_source_rgb(255, 0, 0)
|
1187
|
+
cairo_context.set_source_rgb(255/255.0, 0, 0)
|
506
1188
|
cairo_context.fill
|
507
1189
|
|
508
1190
|
cairo_context.circle(85, 285, 45)
|
509
|
-
cairo_context.set_source_rgb(0, 128, 255)
|
1191
|
+
cairo_context.set_source_rgb(0, 128/255.0, 255/255.0)
|
510
1192
|
cairo_context.set_line_width(3)
|
511
1193
|
cairo_context.stroke
|
512
1194
|
|
513
1195
|
cairo_context.rectangle(140, 40, 180, 90)
|
514
|
-
cairo_context.set_source_rgb(255, 255, 0)
|
1196
|
+
cairo_context.set_source_rgb(255/255.0, 255/255.0, 0)
|
515
1197
|
cairo_context.fill
|
516
1198
|
|
517
1199
|
cairo_context.rectangle(140, 40, 180, 90)
|
518
|
-
cairo_context.set_source_rgb(255, 0, 0)
|
1200
|
+
cairo_context.set_source_rgb(255/255.0, 0, 0)
|
519
1201
|
cairo_context.set_line_width(3)
|
520
1202
|
cairo_context.stroke
|
521
1203
|
|
522
1204
|
cairo_context.rounded_rectangle(140, 140, 180, 90, 30, 20)
|
523
|
-
cairo_context.set_source_rgb(255, 255, 0)
|
1205
|
+
cairo_context.set_source_rgb(255/255.0, 255/255.0, 0)
|
524
1206
|
cairo_context.fill
|
525
1207
|
|
526
1208
|
cairo_context.rounded_rectangle(140, 140, 180, 90, 30, 20)
|
527
|
-
cairo_context.set_source_rgb(255, 0, 0)
|
1209
|
+
cairo_context.set_source_rgb(255/255.0, 0, 0)
|
528
1210
|
cairo_context.set_line_width(3)
|
529
1211
|
cairo_context.stroke
|
530
1212
|
|
531
1213
|
cairo_context.triangle(140, 240, 320, 240, 230, 330)
|
532
|
-
cairo_context.set_source_rgb(255, 255, 0)
|
1214
|
+
cairo_context.set_source_rgb(255/255.0, 255/255.0, 0)
|
533
1215
|
cairo_context.fill
|
534
1216
|
|
535
1217
|
cairo_context.triangle(140, 240, 320, 240, 230, 330)
|
536
|
-
cairo_context.set_source_rgb(255, 0, 0)
|
1218
|
+
cairo_context.set_source_rgb(255/255.0, 0, 0)
|
537
1219
|
cairo_context.set_line_width(3)
|
538
1220
|
cairo_context.stroke
|
539
1221
|
|
@@ -542,7 +1224,7 @@ window {
|
|
542
1224
|
cairo_context.curve_to 190, 60, 200, 80, 210, 70
|
543
1225
|
cairo_context.curve_to 240, 80, 250, 100, 260, 90
|
544
1226
|
cairo_context.curve_to 290, 90, 300, 110, 310, 100
|
545
|
-
cairo_context.set_source_rgb(0, 255, 0)
|
1227
|
+
cairo_context.set_source_rgb(0, 255/255.0, 0)
|
546
1228
|
cairo_context.fill
|
547
1229
|
|
548
1230
|
cairo_context.new_path
|
@@ -550,7 +1232,7 @@ window {
|
|
550
1232
|
cairo_context.curve_to 190, 60, 200, 80, 210, 70
|
551
1233
|
cairo_context.curve_to 240, 80, 250, 100, 260, 90
|
552
1234
|
cairo_context.curve_to 290, 90, 300, 110, 310, 100
|
553
|
-
cairo_context.set_source_rgb(0, 0, 255)
|
1235
|
+
cairo_context.set_source_rgb(0, 0, 255/255.0)
|
554
1236
|
cairo_context.stroke
|
555
1237
|
|
556
1238
|
cairo_context.new_path
|
@@ -561,7 +1243,7 @@ window {
|
|
561
1243
|
cairo_context.line_to 200, 200
|
562
1244
|
cairo_context.line_to 180, 170
|
563
1245
|
cairo_context.close_path
|
564
|
-
cairo_context.set_source_rgb(0, 255, 0)
|
1246
|
+
cairo_context.set_source_rgb(0, 255/255.0, 0)
|
565
1247
|
cairo_context.fill
|
566
1248
|
|
567
1249
|
cairo_context.new_path
|
@@ -572,7 +1254,7 @@ window {
|
|
572
1254
|
cairo_context.line_to 200, 200
|
573
1255
|
cairo_context.line_to 180, 170
|
574
1256
|
cairo_context.close_path
|
575
|
-
cairo_context.set_source_rgb(0, 0, 255)
|
1257
|
+
cairo_context.set_source_rgb(0, 0, 255/255.0)
|
576
1258
|
cairo_context.stroke
|
577
1259
|
|
578
1260
|
cairo_context.new_path
|
@@ -583,7 +1265,7 @@ window {
|
|
583
1265
|
cairo_context.line_to 200, 280
|
584
1266
|
cairo_context.line_to 180, 270
|
585
1267
|
cairo_context.close_path
|
586
|
-
cairo_context.set_source_rgb(0, 255, 0)
|
1268
|
+
cairo_context.set_source_rgb(0, 255/255.0, 0)
|
587
1269
|
cairo_context.fill
|
588
1270
|
|
589
1271
|
cairo_context.new_path
|
@@ -594,7 +1276,7 @@ window {
|
|
594
1276
|
cairo_context.line_to 200, 280
|
595
1277
|
cairo_context.line_to 180, 270
|
596
1278
|
cairo_context.close_path
|
597
|
-
cairo_context.set_source_rgb(0, 0, 255)
|
1279
|
+
cairo_context.set_source_rgb(0, 0, 255/255.0)
|
598
1280
|
cairo_context.stroke
|
599
1281
|
|
600
1282
|
cairo_context.new_path
|
@@ -604,7 +1286,7 @@ window {
|
|
604
1286
|
cairo_context.line_to 220, 340
|
605
1287
|
cairo_context.line_to 200, 330
|
606
1288
|
cairo_context.line_to 180, 320
|
607
|
-
cairo_context.set_source_rgb(0, 0, 255)
|
1289
|
+
cairo_context.set_source_rgb(0, 0, 255/255.0)
|
608
1290
|
cairo_context.stroke
|
609
1291
|
end
|
610
1292
|
}
|
@@ -779,6 +1461,369 @@ application('org.glimmer.hello-application') {
|
|
779
1461
|
}.run
|
780
1462
|
```
|
781
1463
|
|
1464
|
+
#### Tetris
|
1465
|
+
|
1466
|
+
[samples/elaborate/tetris.rb](/samples/elaborate/tetris.rb)
|
1467
|
+
|
1468
|
+
Linux | Mac | Windows
|
1469
|
+
------|-----|--------
|
1470
|
+
![tetris](/screenshots/glimmer-dsl-gtk-linux-tetris.png) | ![tetris](/screenshots/glimmer-dsl-gtk-mac-tetris.png) | ![tetris](/screenshots/glimmer-dsl-gtk-windows-tetris.png)
|
1471
|
+
|
1472
|
+
Run (via installed gem):
|
1473
|
+
|
1474
|
+
```
|
1475
|
+
ruby -r glimmer-dsl-gtk -e "require 'samples/elaborate/tetris'"
|
1476
|
+
```
|
1477
|
+
|
1478
|
+
Run (via locally cloned project):
|
1479
|
+
|
1480
|
+
```
|
1481
|
+
ruby -r ./lib/glimmer-dsl-gtk.rb samples/elaborate/tetris.rb
|
1482
|
+
```
|
1483
|
+
|
1484
|
+
Code:
|
1485
|
+
|
1486
|
+
```ruby
|
1487
|
+
require 'glimmer-dsl-gtk'
|
1488
|
+
|
1489
|
+
require_relative 'tetris/model/game'
|
1490
|
+
|
1491
|
+
class Tetris
|
1492
|
+
include Glimmer
|
1493
|
+
|
1494
|
+
BLOCK_SIZE = 25
|
1495
|
+
BEVEL_CONSTANT = 20
|
1496
|
+
COLOR_GRAY = [192, 192, 192]
|
1497
|
+
|
1498
|
+
def initialize
|
1499
|
+
@game = Model::Game.new
|
1500
|
+
end
|
1501
|
+
|
1502
|
+
def launch
|
1503
|
+
create_gui
|
1504
|
+
register_observers
|
1505
|
+
@game.start!
|
1506
|
+
@main_window.show
|
1507
|
+
end
|
1508
|
+
|
1509
|
+
def create_gui
|
1510
|
+
@main_window = window {
|
1511
|
+
title 'Glimmer Tetris'
|
1512
|
+
default_size Model::Game::PLAYFIELD_WIDTH * BLOCK_SIZE, Model::Game::PLAYFIELD_HEIGHT * BLOCK_SIZE # + 98
|
1513
|
+
|
1514
|
+
box(:vertical) {
|
1515
|
+
tetris_menu_bar
|
1516
|
+
|
1517
|
+
box(:horizontal) {
|
1518
|
+
@playfield_blocks = playfield(playfield_width: @game.playfield_width, playfield_height: @game.playfield_height, block_size: BLOCK_SIZE)
|
1519
|
+
|
1520
|
+
score_board
|
1521
|
+
}
|
1522
|
+
|
1523
|
+
}
|
1524
|
+
|
1525
|
+
on(:key_press_event) do |widget, key_event|
|
1526
|
+
case key_event.keyval
|
1527
|
+
when 65364 # down arrow
|
1528
|
+
@game.down!
|
1529
|
+
when 32 # space
|
1530
|
+
@game.down!(instant: true)
|
1531
|
+
when 65362 # up arrow
|
1532
|
+
case @game.up_arrow_action
|
1533
|
+
when :instant_down
|
1534
|
+
@game.down!(instant: true)
|
1535
|
+
when :rotate_right
|
1536
|
+
@game.rotate!(:right)
|
1537
|
+
when :rotate_left
|
1538
|
+
@game.rotate!(:left)
|
1539
|
+
end
|
1540
|
+
when 65361 # left arrow
|
1541
|
+
@game.left!
|
1542
|
+
when 65363 # right arrow
|
1543
|
+
@game.right!
|
1544
|
+
when 65506 # right shift
|
1545
|
+
@game.rotate!(:right)
|
1546
|
+
when 65505 # left shift
|
1547
|
+
@game.rotate!(:left)
|
1548
|
+
else
|
1549
|
+
# Do Nothing
|
1550
|
+
end
|
1551
|
+
end
|
1552
|
+
}
|
1553
|
+
end
|
1554
|
+
|
1555
|
+
def register_observers
|
1556
|
+
observe(@game, :game_over) do |game_over|
|
1557
|
+
if game_over
|
1558
|
+
show_game_over_dialog
|
1559
|
+
else
|
1560
|
+
start_moving_tetrominos_down
|
1561
|
+
end
|
1562
|
+
end
|
1563
|
+
|
1564
|
+
@game.playfield_height.times do |row|
|
1565
|
+
@game.playfield_width.times do |column|
|
1566
|
+
observe(@game.playfield[row][column], :color) do |new_color|
|
1567
|
+
color = new_color
|
1568
|
+
block = @playfield_blocks[row][column]
|
1569
|
+
block[:background_square].fill = color
|
1570
|
+
block[:top_bevel_edge].fill = [color[0] + 4*BEVEL_CONSTANT, color[1] + 4*BEVEL_CONSTANT, color[2] + 4*BEVEL_CONSTANT]
|
1571
|
+
block[:right_bevel_edge].fill = [color[0] - BEVEL_CONSTANT, color[1] - BEVEL_CONSTANT, color[2] - BEVEL_CONSTANT]
|
1572
|
+
block[:bottom_bevel_edge].fill = [color[0] - BEVEL_CONSTANT, color[1] - BEVEL_CONSTANT, color[2] - BEVEL_CONSTANT]
|
1573
|
+
block[:left_bevel_edge].fill = [color[0] - BEVEL_CONSTANT, color[1] - BEVEL_CONSTANT, color[2] - BEVEL_CONSTANT]
|
1574
|
+
block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
|
1575
|
+
block[:drawing_area].queue_draw
|
1576
|
+
false
|
1577
|
+
end
|
1578
|
+
end
|
1579
|
+
end
|
1580
|
+
|
1581
|
+
Model::Game::PREVIEW_PLAYFIELD_HEIGHT.times do |row|
|
1582
|
+
Model::Game::PREVIEW_PLAYFIELD_WIDTH.times do |column|
|
1583
|
+
observe(@game.preview_playfield[row][column], :color) do |new_color|
|
1584
|
+
color = new_color
|
1585
|
+
block = @preview_playfield_blocks[row][column]
|
1586
|
+
block[:background_square].fill = color
|
1587
|
+
block[:top_bevel_edge].fill = [color[0] + 4*BEVEL_CONSTANT, color[1] + 4*BEVEL_CONSTANT, color[2] + 4*BEVEL_CONSTANT]
|
1588
|
+
block[:right_bevel_edge].fill = [color[0] - BEVEL_CONSTANT, color[1] - BEVEL_CONSTANT, color[2] - BEVEL_CONSTANT]
|
1589
|
+
block[:bottom_bevel_edge].fill = [color[0] - BEVEL_CONSTANT, color[1] - BEVEL_CONSTANT, color[2] - BEVEL_CONSTANT]
|
1590
|
+
block[:left_bevel_edge].fill = [color[0] - BEVEL_CONSTANT, color[1] - BEVEL_CONSTANT, color[2] - BEVEL_CONSTANT]
|
1591
|
+
block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
|
1592
|
+
block[:drawing_area].queue_draw
|
1593
|
+
end
|
1594
|
+
end
|
1595
|
+
end
|
1596
|
+
|
1597
|
+
observe(@game, :score) do |new_score|
|
1598
|
+
@score_label.text = new_score.to_s
|
1599
|
+
end
|
1600
|
+
|
1601
|
+
observe(@game, :lines) do |new_lines|
|
1602
|
+
@lines_label.text = new_lines.to_s
|
1603
|
+
end
|
1604
|
+
|
1605
|
+
observe(@game, :level) do |new_level|
|
1606
|
+
@level_label.text = new_level.to_s
|
1607
|
+
end
|
1608
|
+
end
|
1609
|
+
|
1610
|
+
def tetris_menu_bar
|
1611
|
+
menu_bar {
|
1612
|
+
menu_item(label: 'Game') { |mi|
|
1613
|
+
m = menu {
|
1614
|
+
check_menu_item(label: 'Pause') {
|
1615
|
+
on(:activate) do
|
1616
|
+
@game.paused = !@game.paused?
|
1617
|
+
end
|
1618
|
+
}
|
1619
|
+
|
1620
|
+
menu_item(label: 'Restart') {
|
1621
|
+
on(:activate) do
|
1622
|
+
@game.restart!
|
1623
|
+
end
|
1624
|
+
}
|
1625
|
+
|
1626
|
+
separator_menu_item
|
1627
|
+
|
1628
|
+
menu_item(label: 'Exit') {
|
1629
|
+
on(:activate) do
|
1630
|
+
@main_window.close
|
1631
|
+
end
|
1632
|
+
}
|
1633
|
+
}
|
1634
|
+
mi.submenu = m.gtk
|
1635
|
+
}
|
1636
|
+
|
1637
|
+
menu_item(label: 'View') { |mi|
|
1638
|
+
m = menu {
|
1639
|
+
menu_item(label: 'Show High Scores') {
|
1640
|
+
on(:activate) do
|
1641
|
+
show_high_score_dialog
|
1642
|
+
end
|
1643
|
+
}
|
1644
|
+
|
1645
|
+
menu_item(label: 'Clear High Scores') {
|
1646
|
+
on(:activate) do
|
1647
|
+
@game.clear_high_scores!
|
1648
|
+
end
|
1649
|
+
}
|
1650
|
+
}
|
1651
|
+
mi.submenu = m.gtk
|
1652
|
+
}
|
1653
|
+
|
1654
|
+
menu_item(label: 'Options') { |mi|
|
1655
|
+
m = menu {
|
1656
|
+
rmi = radio_menu_item(nil, 'Instant Down on Up') {
|
1657
|
+
on(:activate) do
|
1658
|
+
@game.instant_down_on_up!
|
1659
|
+
end
|
1660
|
+
}
|
1661
|
+
|
1662
|
+
default_rmi = radio_menu_item(rmi.group, 'Rotate Right on Up') {
|
1663
|
+
on(:activate) do
|
1664
|
+
@game.rotate_right_on_up!
|
1665
|
+
end
|
1666
|
+
}
|
1667
|
+
default_rmi.activate
|
1668
|
+
|
1669
|
+
radio_menu_item(rmi.group, 'Rotate Left on Up') {
|
1670
|
+
on(:activate) do
|
1671
|
+
@game.rotate_left_on_up!
|
1672
|
+
end
|
1673
|
+
}
|
1674
|
+
}
|
1675
|
+
mi.submenu = m.gtk
|
1676
|
+
}
|
1677
|
+
|
1678
|
+
menu_item(label: 'Options') { |mi|
|
1679
|
+
m = menu {
|
1680
|
+
menu_item(label: 'About') {
|
1681
|
+
on(:activate) do
|
1682
|
+
show_about_dialog
|
1683
|
+
end
|
1684
|
+
}
|
1685
|
+
}
|
1686
|
+
mi.submenu = m.gtk
|
1687
|
+
}
|
1688
|
+
}
|
1689
|
+
end
|
1690
|
+
|
1691
|
+
def score_board
|
1692
|
+
box(:vertical) {
|
1693
|
+
label
|
1694
|
+
@preview_playfield_blocks = playfield(playfield_width: Model::Game::PREVIEW_PLAYFIELD_WIDTH, playfield_height: Model::Game::PREVIEW_PLAYFIELD_HEIGHT, block_size: BLOCK_SIZE)
|
1695
|
+
|
1696
|
+
label
|
1697
|
+
label('Score')
|
1698
|
+
@score_label = label
|
1699
|
+
|
1700
|
+
label
|
1701
|
+
label('Lines')
|
1702
|
+
@lines_label = label
|
1703
|
+
|
1704
|
+
label
|
1705
|
+
label('Level')
|
1706
|
+
@level_label = label
|
1707
|
+
label
|
1708
|
+
}
|
1709
|
+
end
|
1710
|
+
|
1711
|
+
def playfield(playfield_width: , playfield_height: , block_size: , &extra_content)
|
1712
|
+
blocks = []
|
1713
|
+
box(:vertical) {
|
1714
|
+
playfield_height.times.map do |row|
|
1715
|
+
blocks << []
|
1716
|
+
box(:horizontal) {
|
1717
|
+
playfield_width.times.map do |column|
|
1718
|
+
blocks.last << block(row: row, column: column, block_size: block_size)
|
1719
|
+
end
|
1720
|
+
}
|
1721
|
+
end
|
1722
|
+
|
1723
|
+
extra_content&.call
|
1724
|
+
}
|
1725
|
+
blocks
|
1726
|
+
end
|
1727
|
+
|
1728
|
+
def block(row: , column: , block_size: , &extra_content)
|
1729
|
+
block = {}
|
1730
|
+
bevel_pixel_size = 0.16 * block_size.to_f
|
1731
|
+
color = Model::Block::COLOR_CLEAR
|
1732
|
+
block[:drawing_area] = drawing_area {
|
1733
|
+
size_request block_size, block_size
|
1734
|
+
|
1735
|
+
block[:background_square] = square(0, 0, block_size) {
|
1736
|
+
fill *color
|
1737
|
+
}
|
1738
|
+
|
1739
|
+
block[:top_bevel_edge] = polygon(0, 0, block_size, 0, block_size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size, bevel_pixel_size) {
|
1740
|
+
fill color[0] + 4*BEVEL_CONSTANT, color[1] + 4*BEVEL_CONSTANT, color[2] + 4*BEVEL_CONSTANT
|
1741
|
+
}
|
1742
|
+
|
1743
|
+
block[:right_bevel_edge] = polygon(block_size, 0, block_size - bevel_pixel_size, bevel_pixel_size, block_size - bevel_pixel_size, block_size - bevel_pixel_size, block_size, block_size) {
|
1744
|
+
fill color[0] - BEVEL_CONSTANT, color[1] - BEVEL_CONSTANT, color[2] - BEVEL_CONSTANT
|
1745
|
+
}
|
1746
|
+
|
1747
|
+
block[:bottom_bevel_edge] = polygon(block_size, block_size, 0, block_size, bevel_pixel_size, block_size - bevel_pixel_size, block_size - bevel_pixel_size, block_size - bevel_pixel_size) {
|
1748
|
+
fill color[0] - BEVEL_CONSTANT, color[1] - BEVEL_CONSTANT, color[2] - BEVEL_CONSTANT
|
1749
|
+
}
|
1750
|
+
|
1751
|
+
block[:left_bevel_edge] = polygon(0, 0, 0, block_size, bevel_pixel_size, block_size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size) {
|
1752
|
+
fill color[0] - BEVEL_CONSTANT, color[1] - BEVEL_CONSTANT, color[2] - BEVEL_CONSTANT
|
1753
|
+
}
|
1754
|
+
|
1755
|
+
block[:border_square] = square(0, 0, block_size) {
|
1756
|
+
stroke *COLOR_GRAY
|
1757
|
+
}
|
1758
|
+
|
1759
|
+
extra_content&.call
|
1760
|
+
}
|
1761
|
+
block
|
1762
|
+
end
|
1763
|
+
|
1764
|
+
def start_moving_tetrominos_down
|
1765
|
+
unless @tetrominos_start_moving_down
|
1766
|
+
@tetrominos_start_moving_down = true
|
1767
|
+
GLib::Timeout.add(@game.delay*1000) do
|
1768
|
+
@game.down! if !@game.game_over? && !@game.paused?
|
1769
|
+
true
|
1770
|
+
end
|
1771
|
+
end
|
1772
|
+
end
|
1773
|
+
|
1774
|
+
def show_game_over_dialog
|
1775
|
+
message_dialog(@main_window) { |md|
|
1776
|
+
title 'Game Over!'
|
1777
|
+
text "Score: #{@game.high_scores.first.score}\nLines: #{@game.high_scores.first.lines}\nLevel: #{@game.high_scores.first.level}"
|
1778
|
+
|
1779
|
+
on(:response) do
|
1780
|
+
md.destroy
|
1781
|
+
end
|
1782
|
+
}.show
|
1783
|
+
|
1784
|
+
@game.restart!
|
1785
|
+
false
|
1786
|
+
end
|
1787
|
+
|
1788
|
+
def show_high_score_dialog
|
1789
|
+
game_paused = !!@game.paused
|
1790
|
+
@game.paused = true
|
1791
|
+
|
1792
|
+
if @game.high_scores.empty?
|
1793
|
+
high_scores_string = "No games have been scored yet."
|
1794
|
+
else
|
1795
|
+
high_scores_string = @game.high_scores.map do |high_score|
|
1796
|
+
"#{high_score.name} | Score: #{high_score.score} | Lines: #{high_score.lines} | Level: #{high_score.level}"
|
1797
|
+
end.join("\n")
|
1798
|
+
end
|
1799
|
+
|
1800
|
+
message_dialog(@main_window) { |md|
|
1801
|
+
title 'High Scores'
|
1802
|
+
text high_scores_string
|
1803
|
+
|
1804
|
+
on(:response) do
|
1805
|
+
md.destroy
|
1806
|
+
end
|
1807
|
+
}.show
|
1808
|
+
|
1809
|
+
@game.paused = game_paused
|
1810
|
+
end
|
1811
|
+
|
1812
|
+
def show_about_dialog
|
1813
|
+
message_dialog(@main_window) { |md|
|
1814
|
+
title 'About'
|
1815
|
+
text "Glimmer Tetris\n\nGlimmer DSL for GTK\n\nElaborate Sample\n\nCopyright (c) 2021-2022 Andy Maleh"
|
1816
|
+
|
1817
|
+
on(:response) do
|
1818
|
+
md.destroy
|
1819
|
+
end
|
1820
|
+
}.show
|
1821
|
+
end
|
1822
|
+
end
|
1823
|
+
|
1824
|
+
Tetris.new.launch
|
1825
|
+
```
|
1826
|
+
|
782
1827
|
## Resources
|
783
1828
|
|
784
1829
|
- Ruby-Gnome Project: https://github.com/ruby-gnome/ruby-gnome
|