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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +1072 -27
  4. data/VERSION +1 -1
  5. data/glimmer-dsl-gtk.gemspec +0 -0
  6. data/images/breaking-blue-wave.png +0 -0
  7. data/lib/glimmer/dsl/gtk/observe_expression.rb +35 -0
  8. data/lib/glimmer/gtk/shape/arc_negative.rb +70 -0
  9. data/lib/glimmer/gtk/shape/path.rb +33 -0
  10. data/lib/glimmer/gtk/shape/square.rb +76 -0
  11. data/lib/glimmer/gtk/shape.rb +56 -10
  12. data/lib/glimmer/gtk/transformable.rb +93 -0
  13. data/lib/glimmer/gtk/widget_proxy/drawing_area_proxy.rb +16 -0
  14. data/lib/glimmer/gtk/widget_proxy.rb +2 -2
  15. data/samples/cairo/arc.rb +44 -0
  16. data/samples/cairo/arc_negative.rb +44 -0
  17. data/samples/cairo/clip.rb +34 -0
  18. data/samples/cairo/clip_image.rb +28 -0
  19. data/samples/cairo/curve_to.rb +39 -0
  20. data/samples/cairo/dashes.rb +30 -0
  21. data/samples/cairo/fill_and_stroke2.rb +36 -0
  22. data/samples/cairo/fill_style.rb +43 -0
  23. data/samples/cairo/gradient.rb +31 -0
  24. data/samples/cairo/image.rb +23 -0
  25. data/samples/cairo/image_gradient.rb +32 -0
  26. data/samples/cairo/multi_segment_caps.rb +27 -0
  27. data/samples/cairo/rounded_rectangle.rb +20 -0
  28. data/samples/cairo/set_line_cap.rb +53 -0
  29. data/samples/cairo/set_line_join.rb +43 -0
  30. data/samples/elaborate/tetris/model/block.rb +48 -0
  31. data/samples/elaborate/tetris/model/game.rb +320 -0
  32. data/samples/elaborate/tetris/model/past_game.rb +39 -0
  33. data/samples/elaborate/tetris/model/tetromino.rb +329 -0
  34. data/samples/elaborate/tetris.rb +338 -0
  35. data/samples/hello/hello_drawing_area.rb +1 -3
  36. data/samples/hello/hello_drawing_area_manual.rb +20 -21
  37. 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.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 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. 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.2'
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
- rectangle(0, 0, 400, 400) {
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.rectangle(0, 0, 400, 400)
483
- cairo_context.set_source_rgb(255, 255, 255)
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