glimmer-dsl-gtk 0.0.3 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,9 +1,9 @@
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.3
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.7
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)
5
5
 
6
- [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for [GTK](https://www.gtk.org/) enables building desktop applications with [Ruby-GNOME](https://github.com/ruby-gnome/ruby-gnome).
6
+ [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for [GTK](https://www.gtk.org/) enables building desktop applications with [Ruby-GNOME](https://github.com/ruby-gnome/ruby-gnome) (including [Cairo graphics](#declarative-cairo-graphics)).
7
7
 
8
8
  [GTK](https://www.gtk.org/) (aka GIMP-Toolkit or [incorrectly] GNOME-Toolkit) is the premiere desktop GUI toolkit on [Linux](https://www.gtk.org/docs/installations/linux/), which also runs on [Mac](https://www.gtk.org/docs/installations/macos/) ([Quartz GTK+](https://wiki.gnome.org/Projects/GTK/OSX)) and [Windows](https://www.gtk.org/docs/installations/windows).
9
9
 
@@ -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.3'
83
+ gem 'glimmer-dsl-gtk', '~> 0.0.7'
83
84
  ```
84
85
 
85
86
  And, then run:
@@ -135,7 +136,7 @@ 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
 
138
- #### MVC Observer Pattern
139
+ ### MVC Observer Pattern
139
140
 
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.
141
142
 
@@ -147,6 +148,733 @@ The model is automatically enhanced as an `Glimmer::DataBinding::ObservableModel
147
148
 
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.
149
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
+
823
+ ### Text
824
+
825
+ [samples/cairo/text.rb](/samples/cairo/text.rb)
826
+
827
+ ```ruby
828
+ require 'glimmer-dsl-gtk'
829
+
830
+ include Glimmer
831
+
832
+ window {
833
+ title 'Text'
834
+ default_size 256, 256
835
+
836
+ drawing_area {
837
+ paint 242.25, 242.25, 242.25
838
+
839
+ font_family = OS.linux? ? 'Sans' : (OS.mac? ? 'Helvetica' : 'Arial')
840
+
841
+ # The main code
842
+ path {
843
+ move_to 10.0, 135.0
844
+ show_text 'Hello'
845
+
846
+ font_face font_family, Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_BOLD
847
+ font_size 90.0
848
+ line_width 2.56
849
+ fill 0, 0, 0
850
+ stroke 0, 0, 0
851
+ }
852
+
853
+ path {
854
+ move_to 70.0, 165.0
855
+ text_path 'void'
856
+
857
+ font_face font_family, Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_BOLD
858
+ font_size 90.0
859
+ line_width 2.56
860
+ fill 127.5, 127.5, 255
861
+ stroke 0, 0, 0
862
+ }
863
+
864
+ # draw helping lines
865
+ path {
866
+ arc 10.0, 135.0, 5.12, 0, 2*Math::PI
867
+ close_path
868
+ arc 70.0, 165.0, 5.12, 0, 2*Math::PI
869
+
870
+ fill 255, 51, 51, 0.6
871
+ }
872
+ }
873
+ }.show
874
+ ```
875
+
876
+ ![Text](/screenshots/glimmer-dsl-gtk-mac-cairo-text.png)
877
+
150
878
  ## Girb (Glimmer IRB)
151
879
 
152
880
  You can run the `girb` command (`bin/girb` if you cloned the project locally):
@@ -377,9 +1105,7 @@ window {
377
1105
  default_size 400, 400
378
1106
 
379
1107
  drawing_area {
380
- rectangle(0, 0, 400, 400) {
381
- fill 255, 255, 255
382
- }
1108
+ paint 255, 255, 255
383
1109
 
384
1110
  arc(85, 85, 45, (Math::PI/180)*90, -(Math::PI/180)*90) {
385
1111
  fill 255, 0, 0
@@ -491,61 +1217,60 @@ window {
491
1217
 
492
1218
  drawing_area {
493
1219
  on(:draw) do |drawing_area_widget, cairo_context|
494
- cairo_context.rectangle(0, 0, 400, 400)
495
- cairo_context.set_source_rgb(255, 255, 255)
496
- cairo_context.fill
1220
+ cairo_context.set_source_rgb(255/255.0, 255/255.0, 255/255.0)
1221
+ cairo_context.paint
497
1222
 
498
1223
  cairo_context.arc(85, 85, 45, (Math::PI/180)*90, -(Math::PI/180)*90)
499
1224
  cairo_context.set_source_rgb(255, 0, 0)
500
1225
  cairo_context.fill
501
1226
 
502
1227
  cairo_context.arc(85, 85, 45, (Math::PI/180)*90, -(Math::PI/180)*90)
503
- cairo_context.set_source_rgb(0, 128, 255)
1228
+ cairo_context.set_source_rgb(0, 128/255.0, 255/255.0)
504
1229
  cairo_context.set_line_width(3)
505
1230
  cairo_context.stroke
506
1231
 
507
1232
  cairo_context.arc(85, 185, 45, (Math::PI/180)*100, -(Math::PI/180)*30)
508
- cairo_context.set_source_rgb(255, 0, 0)
1233
+ cairo_context.set_source_rgb(255/255.0, 0, 0)
509
1234
  cairo_context.fill
510
1235
 
511
1236
  cairo_context.arc(85, 185, 45, (Math::PI/180)*100, -(Math::PI/180)*30)
512
- cairo_context.set_source_rgb(0, 128, 255)
1237
+ cairo_context.set_source_rgb(0, 128/255.0, 255/255.0)
513
1238
  cairo_context.set_line_width(3)
514
1239
  cairo_context.stroke
515
1240
 
516
1241
  cairo_context.circle(85, 285, 45)
517
- cairo_context.set_source_rgb(255, 0, 0)
1242
+ cairo_context.set_source_rgb(255/255.0, 0, 0)
518
1243
  cairo_context.fill
519
1244
 
520
1245
  cairo_context.circle(85, 285, 45)
521
- cairo_context.set_source_rgb(0, 128, 255)
1246
+ cairo_context.set_source_rgb(0, 128/255.0, 255/255.0)
522
1247
  cairo_context.set_line_width(3)
523
1248
  cairo_context.stroke
524
1249
 
525
1250
  cairo_context.rectangle(140, 40, 180, 90)
526
- cairo_context.set_source_rgb(255, 255, 0)
1251
+ cairo_context.set_source_rgb(255/255.0, 255/255.0, 0)
527
1252
  cairo_context.fill
528
1253
 
529
1254
  cairo_context.rectangle(140, 40, 180, 90)
530
- cairo_context.set_source_rgb(255, 0, 0)
1255
+ cairo_context.set_source_rgb(255/255.0, 0, 0)
531
1256
  cairo_context.set_line_width(3)
532
1257
  cairo_context.stroke
533
1258
 
534
1259
  cairo_context.rounded_rectangle(140, 140, 180, 90, 30, 20)
535
- cairo_context.set_source_rgb(255, 255, 0)
1260
+ cairo_context.set_source_rgb(255/255.0, 255/255.0, 0)
536
1261
  cairo_context.fill
537
1262
 
538
1263
  cairo_context.rounded_rectangle(140, 140, 180, 90, 30, 20)
539
- cairo_context.set_source_rgb(255, 0, 0)
1264
+ cairo_context.set_source_rgb(255/255.0, 0, 0)
540
1265
  cairo_context.set_line_width(3)
541
1266
  cairo_context.stroke
542
1267
 
543
1268
  cairo_context.triangle(140, 240, 320, 240, 230, 330)
544
- cairo_context.set_source_rgb(255, 255, 0)
1269
+ cairo_context.set_source_rgb(255/255.0, 255/255.0, 0)
545
1270
  cairo_context.fill
546
1271
 
547
1272
  cairo_context.triangle(140, 240, 320, 240, 230, 330)
548
- cairo_context.set_source_rgb(255, 0, 0)
1273
+ cairo_context.set_source_rgb(255/255.0, 0, 0)
549
1274
  cairo_context.set_line_width(3)
550
1275
  cairo_context.stroke
551
1276
 
@@ -554,7 +1279,7 @@ window {
554
1279
  cairo_context.curve_to 190, 60, 200, 80, 210, 70
555
1280
  cairo_context.curve_to 240, 80, 250, 100, 260, 90
556
1281
  cairo_context.curve_to 290, 90, 300, 110, 310, 100
557
- cairo_context.set_source_rgb(0, 255, 0)
1282
+ cairo_context.set_source_rgb(0, 255/255.0, 0)
558
1283
  cairo_context.fill
559
1284
 
560
1285
  cairo_context.new_path
@@ -562,7 +1287,7 @@ window {
562
1287
  cairo_context.curve_to 190, 60, 200, 80, 210, 70
563
1288
  cairo_context.curve_to 240, 80, 250, 100, 260, 90
564
1289
  cairo_context.curve_to 290, 90, 300, 110, 310, 100
565
- cairo_context.set_source_rgb(0, 0, 255)
1290
+ cairo_context.set_source_rgb(0, 0, 255/255.0)
566
1291
  cairo_context.stroke
567
1292
 
568
1293
  cairo_context.new_path
@@ -573,7 +1298,7 @@ window {
573
1298
  cairo_context.line_to 200, 200
574
1299
  cairo_context.line_to 180, 170
575
1300
  cairo_context.close_path
576
- cairo_context.set_source_rgb(0, 255, 0)
1301
+ cairo_context.set_source_rgb(0, 255/255.0, 0)
577
1302
  cairo_context.fill
578
1303
 
579
1304
  cairo_context.new_path
@@ -584,7 +1309,7 @@ window {
584
1309
  cairo_context.line_to 200, 200
585
1310
  cairo_context.line_to 180, 170
586
1311
  cairo_context.close_path
587
- cairo_context.set_source_rgb(0, 0, 255)
1312
+ cairo_context.set_source_rgb(0, 0, 255/255.0)
588
1313
  cairo_context.stroke
589
1314
 
590
1315
  cairo_context.new_path
@@ -595,7 +1320,7 @@ window {
595
1320
  cairo_context.line_to 200, 280
596
1321
  cairo_context.line_to 180, 270
597
1322
  cairo_context.close_path
598
- cairo_context.set_source_rgb(0, 255, 0)
1323
+ cairo_context.set_source_rgb(0, 255/255.0, 0)
599
1324
  cairo_context.fill
600
1325
 
601
1326
  cairo_context.new_path
@@ -606,7 +1331,7 @@ window {
606
1331
  cairo_context.line_to 200, 280
607
1332
  cairo_context.line_to 180, 270
608
1333
  cairo_context.close_path
609
- cairo_context.set_source_rgb(0, 0, 255)
1334
+ cairo_context.set_source_rgb(0, 0, 255/255.0)
610
1335
  cairo_context.stroke
611
1336
 
612
1337
  cairo_context.new_path
@@ -616,7 +1341,7 @@ window {
616
1341
  cairo_context.line_to 220, 340
617
1342
  cairo_context.line_to 200, 330
618
1343
  cairo_context.line_to 180, 320
619
- cairo_context.set_source_rgb(0, 0, 255)
1344
+ cairo_context.set_source_rgb(0, 0, 255/255.0)
620
1345
  cairo_context.stroke
621
1346
  end
622
1347
  }
@@ -842,7 +1567,14 @@ class Tetris
842
1567
  default_size Model::Game::PLAYFIELD_WIDTH * BLOCK_SIZE, Model::Game::PLAYFIELD_HEIGHT * BLOCK_SIZE # + 98
843
1568
 
844
1569
  box(:vertical) {
845
- @playfield_blocks = playfield(playfield_width: @game.playfield_width, playfield_height: @game.playfield_height, block_size: BLOCK_SIZE)
1570
+ tetris_menu_bar
1571
+
1572
+ box(:horizontal) {
1573
+ @playfield_blocks = playfield(playfield_width: @game.playfield_width, playfield_height: @game.playfield_height, block_size: BLOCK_SIZE)
1574
+
1575
+ score_board
1576
+ }
1577
+
846
1578
  }
847
1579
 
848
1580
  on(:key_press_event) do |widget, key_event|
@@ -900,6 +1632,135 @@ class Tetris
900
1632
  end
901
1633
  end
902
1634
  end
1635
+
1636
+ Model::Game::PREVIEW_PLAYFIELD_HEIGHT.times do |row|
1637
+ Model::Game::PREVIEW_PLAYFIELD_WIDTH.times do |column|
1638
+ observe(@game.preview_playfield[row][column], :color) do |new_color|
1639
+ color = new_color
1640
+ block = @preview_playfield_blocks[row][column]
1641
+ block[:background_square].fill = color
1642
+ block[:top_bevel_edge].fill = [color[0] + 4*BEVEL_CONSTANT, color[1] + 4*BEVEL_CONSTANT, color[2] + 4*BEVEL_CONSTANT]
1643
+ block[:right_bevel_edge].fill = [color[0] - BEVEL_CONSTANT, color[1] - BEVEL_CONSTANT, color[2] - BEVEL_CONSTANT]
1644
+ block[:bottom_bevel_edge].fill = [color[0] - BEVEL_CONSTANT, color[1] - BEVEL_CONSTANT, color[2] - BEVEL_CONSTANT]
1645
+ block[:left_bevel_edge].fill = [color[0] - BEVEL_CONSTANT, color[1] - BEVEL_CONSTANT, color[2] - BEVEL_CONSTANT]
1646
+ block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
1647
+ block[:drawing_area].queue_draw
1648
+ end
1649
+ end
1650
+ end
1651
+
1652
+ observe(@game, :score) do |new_score|
1653
+ @score_label.text = new_score.to_s
1654
+ end
1655
+
1656
+ observe(@game, :lines) do |new_lines|
1657
+ @lines_label.text = new_lines.to_s
1658
+ end
1659
+
1660
+ observe(@game, :level) do |new_level|
1661
+ @level_label.text = new_level.to_s
1662
+ end
1663
+ end
1664
+
1665
+ def tetris_menu_bar
1666
+ menu_bar {
1667
+ menu_item(label: 'Game') { |mi|
1668
+ m = menu {
1669
+ check_menu_item(label: 'Pause') {
1670
+ on(:activate) do
1671
+ @game.paused = !@game.paused?
1672
+ end
1673
+ }
1674
+
1675
+ menu_item(label: 'Restart') {
1676
+ on(:activate) do
1677
+ @game.restart!
1678
+ end
1679
+ }
1680
+
1681
+ separator_menu_item
1682
+
1683
+ menu_item(label: 'Exit') {
1684
+ on(:activate) do
1685
+ @main_window.close
1686
+ end
1687
+ }
1688
+ }
1689
+ mi.submenu = m.gtk
1690
+ }
1691
+
1692
+ menu_item(label: 'View') { |mi|
1693
+ m = menu {
1694
+ menu_item(label: 'Show High Scores') {
1695
+ on(:activate) do
1696
+ show_high_score_dialog
1697
+ end
1698
+ }
1699
+
1700
+ menu_item(label: 'Clear High Scores') {
1701
+ on(:activate) do
1702
+ @game.clear_high_scores!
1703
+ end
1704
+ }
1705
+ }
1706
+ mi.submenu = m.gtk
1707
+ }
1708
+
1709
+ menu_item(label: 'Options') { |mi|
1710
+ m = menu {
1711
+ rmi = radio_menu_item(nil, 'Instant Down on Up') {
1712
+ on(:activate) do
1713
+ @game.instant_down_on_up!
1714
+ end
1715
+ }
1716
+
1717
+ default_rmi = radio_menu_item(rmi.group, 'Rotate Right on Up') {
1718
+ on(:activate) do
1719
+ @game.rotate_right_on_up!
1720
+ end
1721
+ }
1722
+ default_rmi.activate
1723
+
1724
+ radio_menu_item(rmi.group, 'Rotate Left on Up') {
1725
+ on(:activate) do
1726
+ @game.rotate_left_on_up!
1727
+ end
1728
+ }
1729
+ }
1730
+ mi.submenu = m.gtk
1731
+ }
1732
+
1733
+ menu_item(label: 'Options') { |mi|
1734
+ m = menu {
1735
+ menu_item(label: 'About') {
1736
+ on(:activate) do
1737
+ show_about_dialog
1738
+ end
1739
+ }
1740
+ }
1741
+ mi.submenu = m.gtk
1742
+ }
1743
+ }
1744
+ end
1745
+
1746
+ def score_board
1747
+ box(:vertical) {
1748
+ label
1749
+ @preview_playfield_blocks = playfield(playfield_width: Model::Game::PREVIEW_PLAYFIELD_WIDTH, playfield_height: Model::Game::PREVIEW_PLAYFIELD_HEIGHT, block_size: BLOCK_SIZE)
1750
+
1751
+ label
1752
+ label('Score')
1753
+ @score_label = label
1754
+
1755
+ label
1756
+ label('Lines')
1757
+ @lines_label = label
1758
+
1759
+ label
1760
+ label('Level')
1761
+ @level_label = label
1762
+ label
1763
+ }
903
1764
  end
904
1765
 
905
1766
  def playfield(playfield_width: , playfield_height: , block_size: , &extra_content)
@@ -978,6 +1839,41 @@ class Tetris
978
1839
  @game.restart!
979
1840
  false
980
1841
  end
1842
+
1843
+ def show_high_score_dialog
1844
+ game_paused = !!@game.paused
1845
+ @game.paused = true
1846
+
1847
+ if @game.high_scores.empty?
1848
+ high_scores_string = "No games have been scored yet."
1849
+ else
1850
+ high_scores_string = @game.high_scores.map do |high_score|
1851
+ "#{high_score.name} | Score: #{high_score.score} | Lines: #{high_score.lines} | Level: #{high_score.level}"
1852
+ end.join("\n")
1853
+ end
1854
+
1855
+ message_dialog(@main_window) { |md|
1856
+ title 'High Scores'
1857
+ text high_scores_string
1858
+
1859
+ on(:response) do
1860
+ md.destroy
1861
+ end
1862
+ }.show
1863
+
1864
+ @game.paused = game_paused
1865
+ end
1866
+
1867
+ def show_about_dialog
1868
+ message_dialog(@main_window) { |md|
1869
+ title 'About'
1870
+ text "Glimmer Tetris\n\nGlimmer DSL for GTK\n\nElaborate Sample\n\nCopyright (c) 2021-2022 Andy Maleh"
1871
+
1872
+ on(:response) do
1873
+ md.destroy
1874
+ end
1875
+ }.show
1876
+ end
981
1877
  end
982
1878
 
983
1879
  Tetris.new.launch