glimmer-dsl-libui 0.1.11 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/README.md +573 -23
  4. data/VERSION +1 -1
  5. data/examples/area_gallery.rb +7 -1
  6. data/examples/area_gallery2.rb +14 -4
  7. data/examples/area_gallery3.rb +8 -2
  8. data/examples/area_gallery4.rb +15 -5
  9. data/examples/basic_draw_text.rb +67 -0
  10. data/examples/color_the_circles.rb +220 -0
  11. data/examples/midi_player.rb +2 -2
  12. data/examples/timer.rb +135 -0
  13. data/glimmer-dsl-libui.gemspec +0 -0
  14. data/lib/glimmer/dsl/libui/control_expression.rb +0 -1
  15. data/lib/glimmer/dsl/libui/property_expression.rb +3 -1
  16. data/lib/glimmer/dsl/libui/string_expression.rb +48 -0
  17. data/lib/glimmer/libui/attributed_string.rb +90 -0
  18. data/lib/glimmer/libui/control_proxy/area_proxy.rb +14 -3
  19. data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +3 -3
  20. data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +69 -0
  21. data/lib/glimmer/libui/control_proxy/menu_proxy.rb +3 -0
  22. data/lib/glimmer/libui/control_proxy/path_proxy.rb +0 -2
  23. data/lib/glimmer/libui/control_proxy/text_proxy.rb +158 -0
  24. data/lib/glimmer/libui/control_proxy/window_proxy.rb +0 -6
  25. data/lib/glimmer/libui/control_proxy.rb +7 -1
  26. data/lib/glimmer/libui/shape/arc.rb +6 -3
  27. data/lib/glimmer/libui/shape/circle.rb +50 -0
  28. data/lib/glimmer/libui/shape/rectangle.rb +4 -0
  29. data/lib/glimmer/libui/shape/square.rb +4 -0
  30. data/lib/glimmer/libui.rb +73 -6
  31. data/sounds/AlanWalker-Faded.mid +0 -0
  32. data/sounds/AlanWalker-SingMeToSleep.mid +0 -0
  33. data/sounds/CalvinHarris-Blame.mid +0 -0
  34. data/sounds/CalvinHarris-MyWay.mid +0 -0
  35. data/sounds/deadmau5-2448.mid +0 -0
  36. data/sounds/deadmau5-SoThereIWas.mid +0 -0
  37. metadata +16 -2
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 LibUI 0.1.11
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 LibUI 0.2.3
2
2
  ## Prerequisite-Free Ruby Desktop Development GUI Library
3
3
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-libui.svg)](http://badge.fury.io/rb/glimmer-dsl-libui)
4
4
  [![Maintainability](https://api.codeclimate.com/v1/badges/ce2853efdbecf6ebdc73/maintainability)](https://codeclimate.com/github/AndyObtiva/glimmer-dsl-libui/maintainability)
@@ -118,11 +118,17 @@ window('Area Gallery', 400, 400) {
118
118
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
119
119
  }
120
120
  path { # declarative stable path
121
- arc(200, 200, 90, 0, 360, false)
121
+ circle(200, 200, 90)
122
122
 
123
123
  fill r: 202, g: 102, b: 204, a: 0.5
124
124
  stroke r: 0, g: 0, b: 0, thickness: 2
125
125
  }
126
+ path { # declarative stable path
127
+ arc(400, 220, 180, 90, 90, false)
128
+
129
+ fill r: 204, g: 102, b: 204, a: 0.5
130
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
131
+ }
126
132
 
127
133
  on_mouse_event do |area_mouse_event|
128
134
  p area_mouse_event
@@ -191,7 +197,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
191
197
 
192
198
  ## Table of Contents
193
199
 
194
- - [Glimmer DSL for LibUI 0.1.10](#-glimmer-dsl-for-libui-0111)
200
+ - [Glimmer DSL for LibUI 0.2.3](#-glimmer-dsl-for-libui-023)
195
201
  - [Glimmer GUI DSL Concepts](#glimmer-gui-dsl-concepts)
196
202
  - [Usage](#usage)
197
203
  - [Girb (Glimmer IRB)](#girb-glimmer-irb)
@@ -199,6 +205,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
199
205
  - [Supported Controls](#supported-controls)
200
206
  - [Common Control Properties](#common-control-properties)
201
207
  - [Common Control Operations](#common-control-operations)
208
+ - [LibUI Operations](#libui-operations)
202
209
  - [Extra Dialogs](#extra-dialogs)
203
210
  - [Extra Operations](#extra-operations)
204
211
  - [Table API](#table-api)
@@ -235,6 +242,9 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
235
242
  - [Histogram](#histogram)
236
243
  - [Basic Transform](#basic-transform)
237
244
  - [Login](#login)
245
+ - [Timer](#timer)
246
+ - [Color The Circles](#color-the-circles)
247
+ - [Basic Draw Text](#basic-draw-text)
238
248
  - [Contributing to glimmer-dsl-libui](#contributing-to-glimmer-dsl-libui)
239
249
  - [Help](#help)
240
250
  - [Issues](#issues)
@@ -322,7 +332,7 @@ gem install glimmer-dsl-libui
322
332
  Or install via Bundler `Gemfile`:
323
333
 
324
334
  ```ruby
325
- gem 'glimmer-dsl-libui', '~> 0.1.10'
335
+ gem 'glimmer-dsl-libui', '~> 0.2.3'
326
336
  ```
327
337
 
328
338
  Add `require 'glimmer-dsl-libui'` at the top, and then `include Glimmer` into the top-level main object for testing or into an actual class for serious usage.
@@ -475,6 +485,13 @@ Control(Args) | Properties | Listeners
475
485
  - `hide`
476
486
  - `show`
477
487
 
488
+ ### LibUI Operations
489
+
490
+ All operations that could normally be called on `LibUI` can also be called on `Glimmer::LibUI`, but some have enhancements as detailed below.
491
+
492
+ - `Glimmer::LibUI::queue_main(&block)`: queues an operation to be run on the main event loop at the earliest opportunity possible
493
+ - `Glimmer::LibUI::timer(time_in_seconds=0.1, repeat: true, &block)`: calls block after time_in_seconds has elapsed, repeating indefinitely unless repeat is `false` or an `Integer` for finite number of repeats. Block can return `false` or `true` to override next repetition.
494
+
478
495
  ### Extra Dialogs
479
496
 
480
497
  - `open_file(window as Glimmer::LibUI::WindowProxy)`: returns selected file (`String`) or `nil` if cancelled
@@ -766,6 +783,8 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
766
783
 
767
784
  `fill` and `stroke` accept [X11](https://en.wikipedia.org/wiki/X11_color_names) color `Symbol`s/`String`s like `:skyblue` and `'sandybrown'` or 6-number hex or 3-number hex-shorthand (as `Integer` or `String` with or without `0x` prefix)
768
785
 
786
+ Available [X11](https://en.wikipedia.org/wiki/X11_color_names) colors can be obtained through `Glimmer::LibUI.x11_colors` method.
787
+
769
788
  Check [Basic Transform](#basic-transform) example for use of [X11](https://en.wikipedia.org/wiki/X11_color_names) colors.
770
789
 
771
790
  Check [Histogram](#histogram) example for use of hex colors.
@@ -1258,9 +1277,7 @@ window('Notepad', 500, 300) {
1258
1277
 
1259
1278
  ### Midi Player
1260
1279
 
1261
- This example has prerequisites:
1262
- - Install [TiMidity](http://timidity.sourceforge.net) and ensure `timidity` command is in `PATH` (can be installed via [Homebrew](https://brew.sh) on Mac or [apt-get](https://help.ubuntu.com/community/AptGet/Howto) on Linux).
1263
- - Add `*.mid` files to `~/Music` directory (you may copy the ones included in [sounds](sounds) directory)
1280
+ To run this example, install [TiMidity](http://timidity.sourceforge.net) and ensure `timidity` command is in `PATH` (can be installed via [Homebrew](https://brew.sh) on Mac or [apt-get](https://help.ubuntu.com/community/AptGet/Howto) on Linux).
1264
1281
 
1265
1282
  [examples/midi_player.rb](examples/midi_player.rb)
1266
1283
 
@@ -1399,7 +1416,7 @@ class TinyMidiPlayer
1399
1416
 
1400
1417
  def initialize
1401
1418
  @pid = nil
1402
- @music_directory = File.expand_path(ARGV[0] || '~/Music/')
1419
+ @music_directory = File.expand_path('../sounds', __dir__)
1403
1420
  @midi_files = Dir.glob(File.join(@music_directory, '**/*.mid'))
1404
1421
  .sort_by { |path| File.basename(path) }
1405
1422
  at_exit { stop_midi }
@@ -1438,7 +1455,7 @@ class TinyMidiPlayer
1438
1455
  end
1439
1456
 
1440
1457
  def create_gui
1441
- menu('Help') { |m|
1458
+ menu('Help') {
1442
1459
  menu_item('Version') {
1443
1460
  on_clicked do
1444
1461
  show_version
@@ -3523,11 +3540,17 @@ window('Area Gallery', 400, 400) {
3523
3540
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3524
3541
  }
3525
3542
  path { # declarative stable path
3526
- arc(200, 200, 90, 0, 360, false)
3543
+ circle(200, 200, 90)
3527
3544
 
3528
3545
  fill r: 202, g: 102, b: 204, a: 0.5
3529
3546
  stroke r: 0, g: 0, b: 0, thickness: 2
3530
3547
  }
3548
+ path { # declarative stable path
3549
+ arc(400, 220, 180, 90, 90, false)
3550
+
3551
+ fill r: 204, g: 102, b: 204, a: 0.5
3552
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3553
+ }
3531
3554
 
3532
3555
  on_mouse_event do |area_mouse_event|
3533
3556
  p area_mouse_event
@@ -3680,18 +3703,28 @@ window('Area Gallery', 400, 400) {
3680
3703
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3681
3704
  }
3682
3705
  path { # declarative stable path
3683
- arc {
3706
+ circle {
3684
3707
  x_center 200
3685
3708
  y_center 200
3686
3709
  radius 90
3687
- start_angle 0
3688
- sweep 360
3689
- is_negative false
3690
3710
  }
3691
3711
 
3692
3712
  fill r: 202, g: 102, b: 204, a: 0.5
3693
3713
  stroke r: 0, g: 0, b: 0, thickness: 2
3694
3714
  }
3715
+ path { # declarative stable path
3716
+ arc {
3717
+ x_center 400
3718
+ y_center 220
3719
+ radius 180
3720
+ start_angle 90
3721
+ sweep 90
3722
+ is_negative false
3723
+ }
3724
+
3725
+ fill r: 204, g: 102, b: 204, a: 0.5
3726
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3727
+ }
3695
3728
 
3696
3729
  on_mouse_event do |area_mouse_event|
3697
3730
  p area_mouse_event
@@ -3791,13 +3824,19 @@ window('Area Gallery', 400, 400) {
3791
3824
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3792
3825
  }
3793
3826
  path { # a dynamic path is added semi-declaratively inside on_draw block
3794
- arc(200, 200, 90, 0, 360, false)
3795
-
3827
+ circle(200, 200, 90)
3828
+
3796
3829
  fill r: 202, g: 102, b: 204, a: 0.5
3797
3830
  stroke r: 0, g: 0, b: 0, thickness: 2
3798
3831
  }
3832
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3833
+ arc(400, 220, 180, 90, 90, false)
3834
+
3835
+ fill r: 204, g: 102, b: 204, a: 0.5
3836
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3837
+ }
3799
3838
  end
3800
-
3839
+
3801
3840
  on_mouse_event do |area_mouse_event|
3802
3841
  p area_mouse_event
3803
3842
  end
@@ -3950,20 +3989,30 @@ window('Area Gallery', 400, 400) {
3950
3989
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3951
3990
  }
3952
3991
  path { # a dynamic path is added semi-declaratively inside on_draw block
3953
- arc {
3992
+ circle {
3954
3993
  x_center 200
3955
3994
  y_center 200
3956
3995
  radius 90
3957
- start_angle 0
3958
- sweep 360
3959
- is_negative false
3960
3996
  }
3961
-
3997
+
3962
3998
  fill r: 202, g: 102, b: 204, a: 0.5
3963
3999
  stroke r: 0, g: 0, b: 0, thickness: 2
3964
4000
  }
4001
+ path { # a dynamic path is added semi-declaratively inside on_draw block
4002
+ arc {
4003
+ x_center 400
4004
+ y_center 220
4005
+ radius 180
4006
+ start_angle 90
4007
+ sweep 90
4008
+ is_negative false
4009
+ }
4010
+
4011
+ fill r: 204, g: 102, b: 204, a: 0.5
4012
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
4013
+ }
3965
4014
  end
3966
-
4015
+
3967
4016
  on_mouse_event do |area_mouse_event|
3968
4017
  p area_mouse_event
3969
4018
  end
@@ -4486,6 +4535,507 @@ window('Login') {
4486
4535
  }.show
4487
4536
  ```
4488
4537
 
4538
+ ### Timer
4539
+
4540
+ To run this example, install [TiMidity](http://timidity.sourceforge.net) and ensure `timidity` command is in `PATH` (can be installed via [Homebrew](https://brew.sh) on Mac or [apt-get](https://help.ubuntu.com/community/AptGet/Howto) on Linux).
4541
+
4542
+ [examples/timer.rb](examples/timer.rb)
4543
+
4544
+ Run with this command from the root of the project if you cloned the project:
4545
+
4546
+ ```
4547
+ ruby -r './lib/glimmer-dsl-libui' examples/timer.rb
4548
+ ```
4549
+
4550
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
4551
+
4552
+ ```
4553
+ ruby -r glimmer-dsl-libui -e "require 'examples/timer'"
4554
+ ```
4555
+
4556
+ Mac
4557
+
4558
+ ![glimmer-dsl-libui-mac-timer.png](images/glimmer-dsl-libui-mac-timer.png)
4559
+ ![glimmer-dsl-libui-mac-timer-in-progress.png](images/glimmer-dsl-libui-mac-timer-in-progress.png)
4560
+
4561
+ Linux
4562
+
4563
+ ![glimmer-dsl-libui-linux-timer.png](images/glimmer-dsl-libui-linux-timer.png)
4564
+ ![glimmer-dsl-libui-linux-timer-in-progress.png](images/glimmer-dsl-libui-linux-timer-in-progress.png)
4565
+
4566
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
4567
+
4568
+ ```ruby
4569
+ # frozen_string_literal: true
4570
+
4571
+ require 'glimmer-dsl-libui'
4572
+
4573
+ class Timer
4574
+ include Glimmer
4575
+
4576
+ SECOND_MAX = 59
4577
+ MINUTE_MAX = 59
4578
+ HOUR_MAX = 23
4579
+
4580
+ def initialize
4581
+ @pid = nil
4582
+ @midi_file = File.expand_path('../sounds/AlanWalker-Faded.mid', __dir__)
4583
+ at_exit { stop_midi }
4584
+ setup_timer
4585
+ create_gui
4586
+ end
4587
+
4588
+ def stop_midi
4589
+ if @pid
4590
+ if @th.alive?
4591
+ Process.kill(:SIGKILL, @pid)
4592
+ @pid = nil
4593
+ else
4594
+ @pid = nil
4595
+ end
4596
+ end
4597
+ end
4598
+
4599
+ def play_midi
4600
+ stop_midi
4601
+ if @pid.nil?
4602
+ begin
4603
+ @pid = spawn "timidity -G 0.0-10.0 #{@midi_file}"
4604
+ @th = Process.detach @pid
4605
+ rescue Errno::ENOENT
4606
+ warn 'Timidty++ not found. Please install Timidity++.'
4607
+ warn 'https://sourceforge.net/projects/timidity/'
4608
+ end
4609
+ end
4610
+ end
4611
+
4612
+ def setup_timer
4613
+ unless @setup_timer
4614
+ Glimmer::LibUI.timer(1) do
4615
+ if @started
4616
+ seconds = @sec_spinbox.value
4617
+ minutes = @min_spinbox.value
4618
+ hours = @hour_spinbox.value
4619
+ if seconds > 0
4620
+ @sec_spinbox.value = seconds -= 1
4621
+ end
4622
+ if seconds == 0
4623
+ if minutes > 0
4624
+ @min_spinbox.value = minutes -= 1
4625
+ @sec_spinbox.value = seconds = SECOND_MAX
4626
+ end
4627
+ if minutes == 0
4628
+ if hours > 0
4629
+ @hour_spinbox.value = hours -= 1
4630
+ @min_spinbox.value = minutes = MINUTE_MAX
4631
+ @sec_spinbox.value = seconds = SECOND_MAX
4632
+ end
4633
+ if hours == 0 && minutes == 0 && seconds == 0
4634
+ @start_button.enabled = true
4635
+ @stop_button.enabled = false
4636
+ @started = false
4637
+ unless @played
4638
+ play_midi
4639
+ @played = true
4640
+ end
4641
+ end
4642
+ end
4643
+ end
4644
+ end
4645
+ end
4646
+ @setup_timer = true
4647
+ end
4648
+ end
4649
+
4650
+ def create_gui
4651
+ window('Timer') {
4652
+ margined true
4653
+
4654
+ group('Countdown') {
4655
+ vertical_box {
4656
+ horizontal_box {
4657
+ @hour_spinbox = spinbox(0, HOUR_MAX) {
4658
+ stretchy false
4659
+ value 0
4660
+ }
4661
+ label(':') {
4662
+ stretchy false
4663
+ }
4664
+ @min_spinbox = spinbox(0, MINUTE_MAX) {
4665
+ stretchy false
4666
+ value 0
4667
+ }
4668
+ label(':') {
4669
+ stretchy false
4670
+ }
4671
+ @sec_spinbox = spinbox(0, SECOND_MAX) {
4672
+ stretchy false
4673
+ value 0
4674
+ }
4675
+ }
4676
+ horizontal_box {
4677
+ @start_button = button('Start') {
4678
+ on_clicked do
4679
+ @start_button.enabled = false
4680
+ @stop_button.enabled = true
4681
+ @started = true
4682
+ @played = false
4683
+ end
4684
+ }
4685
+
4686
+ @stop_button = button('Stop') {
4687
+ enabled false
4688
+
4689
+ on_clicked do
4690
+ @start_button.enabled = true
4691
+ @stop_button.enabled = false
4692
+ @started = false
4693
+ end
4694
+ }
4695
+ }
4696
+ }
4697
+ }
4698
+ }.show
4699
+ end
4700
+ end
4701
+
4702
+ Timer.new
4703
+ ```
4704
+
4705
+ ### Color The Circles
4706
+
4707
+ [examples/color_the_circles.rb](examples/color_the_circles.rb)
4708
+
4709
+ Run with this command from the root of the project if you cloned the project:
4710
+
4711
+ ```
4712
+ ruby -r './lib/glimmer-dsl-libui' examples/color_the_circles.rb
4713
+ ```
4714
+
4715
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
4716
+
4717
+ ```
4718
+ ruby -r glimmer-dsl-libui -e "require 'examples/color_the_circles'"
4719
+ ```
4720
+
4721
+ Mac
4722
+
4723
+ ![glimmer-dsl-libui-mac-color-the-circles.png](images/glimmer-dsl-libui-mac-color-the-circles.png)
4724
+ ![glimmer-dsl-libui-mac-color-the-circles-lost.png](images/glimmer-dsl-libui-mac-color-the-circles-lost.png)
4725
+ ![glimmer-dsl-libui-mac-color-the-circles-won.png](images/glimmer-dsl-libui-mac-color-the-circles-won.png)
4726
+
4727
+ Linux
4728
+
4729
+ ![glimmer-dsl-libui-linux-color-the-circles.png](images/glimmer-dsl-libui-linux-color-the-circles.png)
4730
+ ![glimmer-dsl-libui-linux-color-the-circles-lost.png](images/glimmer-dsl-libui-linux-color-the-circles-lost.png)
4731
+ ![glimmer-dsl-libui-linux-color-the-circles-won.png](images/glimmer-dsl-libui-linux-color-the-circles-won.png)
4732
+
4733
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
4734
+
4735
+ ```ruby
4736
+ require 'glimmer-dsl-libui'
4737
+
4738
+ class ColorTheCircles
4739
+ include Glimmer
4740
+
4741
+ WINDOW_WIDTH = 800
4742
+ WINDOW_HEIGHT = 600
4743
+ CIRCLE_MIN_RADIUS = 15
4744
+ CIRCLE_MAX_RADIUS = 50
4745
+ MARGIN_WIDTH = 55
4746
+ MARGIN_HEIGHT = 155
4747
+ TIME_MAX_EASY = 4
4748
+ TIME_MAX_MEDIUM = 3
4749
+ TIME_MAX_HARD = 2
4750
+ TIME_MAX_INSANE = 1
4751
+
4752
+ attr_accessor :score
4753
+
4754
+ def initialize
4755
+ @circles_data = []
4756
+ @score = 0
4757
+ @time_max = TIME_MAX_HARD
4758
+ register_observers
4759
+ setup_circle_factory
4760
+ end
4761
+
4762
+ def register_observers
4763
+ observer = Glimmer::DataBinding::Observer.proc do |new_score|
4764
+ @score_label.text = new_score.to_s
4765
+ if new_score == -20
4766
+ msg_box('You Lost!', 'Sorry! Your score reached -20')
4767
+ restart_game
4768
+ elsif new_score == 0
4769
+ msg_box('You Won!', 'Congratulations! Your score reached 0')
4770
+ restart_game
4771
+ end
4772
+ end
4773
+ observer.observe(self, :score) # automatically enhances self to become Glimmer::DataBinding::ObservableModel and notify observer on score attribute changes
4774
+ end
4775
+
4776
+ def setup_circle_factory
4777
+ consumer = Proc.new do
4778
+ if @circles_data.empty?
4779
+ # start with 3 circles to make more challenging
4780
+ add_circle until @circles_data.size > 3
4781
+ else
4782
+ add_circle
4783
+ end
4784
+ delay = rand * @time_max
4785
+ Glimmer::LibUI.timer(delay, repeat: false, &consumer)
4786
+ end
4787
+ Glimmer::LibUI.queue_main(&consumer)
4788
+ end
4789
+
4790
+ def add_circle
4791
+ circle_x_center = rand * (WINDOW_WIDTH - MARGIN_WIDTH - CIRCLE_MAX_RADIUS) + CIRCLE_MAX_RADIUS
4792
+ circle_y_center = rand * (WINDOW_HEIGHT - MARGIN_HEIGHT - CIRCLE_MAX_RADIUS) + CIRCLE_MAX_RADIUS
4793
+ circle_radius = rand * (CIRCLE_MAX_RADIUS - CIRCLE_MIN_RADIUS) + CIRCLE_MIN_RADIUS
4794
+ stroke_color = Glimmer::LibUI.x11_colors.sample
4795
+ @circles_data << {
4796
+ args: [circle_x_center, circle_y_center, circle_radius],
4797
+ fill: nil,
4798
+ stroke: stroke_color
4799
+ }
4800
+ @area.queue_redraw_all
4801
+ self.score -= 1 # notifies score observers automatically of change
4802
+ end
4803
+
4804
+ def restart_game
4805
+ @score = 0 # update variable directly to avoid notifying observers
4806
+ @circles_data.clear
4807
+ end
4808
+
4809
+ def launch
4810
+ menu('Actions') {
4811
+ menu_item('Restart') {
4812
+ on_clicked do
4813
+ restart_game
4814
+ end
4815
+ }
4816
+
4817
+ quit_menu_item
4818
+ }
4819
+
4820
+ menu('Difficulty') {
4821
+ radio_menu_item('Easy') {
4822
+ on_clicked do
4823
+ @time_max = TIME_MAX_EASY
4824
+ end
4825
+ }
4826
+
4827
+ radio_menu_item('Medium') {
4828
+ on_clicked do
4829
+ @time_max = TIME_MAX_MEDIUM
4830
+ end
4831
+ }
4832
+
4833
+ radio_menu_item('Hard') {
4834
+ checked true
4835
+
4836
+ on_clicked do
4837
+ @time_max = TIME_MAX_HARD
4838
+ end
4839
+ }
4840
+
4841
+ radio_menu_item('Insane') {
4842
+ on_clicked do
4843
+ @time_max = TIME_MAX_INSANE
4844
+ end
4845
+ }
4846
+ }
4847
+
4848
+ menu('Help') {
4849
+ menu_item('Instructions') {
4850
+ on_clicked do
4851
+ msg_box('Instructions', "Score goes down as circles are added.\nIf it reaches -20, you lose!\n\nClick circles to color and score!\nOnce score reaches 0, you win!\n\nBeware of concealed light-colored circles!\nThey are revealed once darker circles intersect them.\n\nThere are four levels of difficulty.\nChange via difficulty menu if the game gets too tough.")
4852
+ end
4853
+ }
4854
+ }
4855
+
4856
+ window('Color The Circles', WINDOW_WIDTH, WINDOW_HEIGHT) {
4857
+ margined true
4858
+
4859
+ grid {
4860
+ button('Restart') {
4861
+ left 0
4862
+ top 0
4863
+ halign :center
4864
+
4865
+ on_clicked do
4866
+ restart_game
4867
+ end
4868
+ }
4869
+
4870
+ label('Score goes down as circles are added. If it reaches -20, you lose!') {
4871
+ left 0
4872
+ top 1
4873
+ halign :center
4874
+ }
4875
+
4876
+ label('Click circles to color and score! Once score reaches 0, you win!') {
4877
+ left 0
4878
+ top 2
4879
+ halign :center
4880
+ }
4881
+
4882
+ horizontal_box {
4883
+ left 0
4884
+ top 3
4885
+ halign :center
4886
+
4887
+ label('Score:') {
4888
+ stretchy false
4889
+ }
4890
+
4891
+ @score_label = label(@score.to_s) {
4892
+ stretchy false
4893
+ }
4894
+ }
4895
+
4896
+ vertical_box {
4897
+ left 0
4898
+ top 4
4899
+ hexpand true
4900
+ vexpand true
4901
+ halign :fill
4902
+ valign :fill
4903
+
4904
+ @area = area {
4905
+ on_draw do |area_draw_params|
4906
+ path {
4907
+ rectangle(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT)
4908
+
4909
+ fill :white
4910
+ }
4911
+
4912
+ @circles_data.each do |circle_data|
4913
+ path {
4914
+ circle_data[:circle] = circle(*circle_data[:args])
4915
+
4916
+ fill circle_data[:fill]
4917
+ stroke circle_data[:stroke]
4918
+ }
4919
+ end
4920
+ end
4921
+
4922
+ on_mouse_down do |area_mouse_event|
4923
+ clicked_circle_data = @circles_data.find do |circle_data|
4924
+ circle_data[:fill].nil? && circle_data[:circle].include?(area_mouse_event[:x], area_mouse_event[:y])
4925
+ end
4926
+ if clicked_circle_data
4927
+ clicked_circle_data[:fill] = clicked_circle_data[:stroke]
4928
+ @area.queue_redraw_all
4929
+ self.score += 1 # notifies score observers automatically of change
4930
+ end
4931
+ end
4932
+ }
4933
+ }
4934
+
4935
+ }
4936
+ }.show
4937
+ end
4938
+ end
4939
+
4940
+ ColorTheCircles.new.launch
4941
+ ```
4942
+
4943
+ ### Basic Draw Text
4944
+
4945
+ [examples/basic_draw_text.rb](examples/basic_draw_text.rb)
4946
+
4947
+ Run with this command from the root of the project if you cloned the project:
4948
+
4949
+ ```
4950
+ ruby -r './lib/glimmer-dsl-libui' examples/basic_draw_text.rb
4951
+ ```
4952
+
4953
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
4954
+
4955
+ ```
4956
+ ruby -r glimmer-dsl-libui -e "require 'examples/basic_draw_text'"
4957
+ ```
4958
+
4959
+ Mac
4960
+
4961
+ ![glimmer-dsl-libui-mac-basic-draw-text.png](images/glimmer-dsl-libui-mac-basic-draw-text.png)
4962
+
4963
+ Linux
4964
+
4965
+ ![glimmer-dsl-libui-linux-basic-draw-text.png](images/glimmer-dsl-libui-linux-basic-draw-text.png)
4966
+
4967
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
4968
+
4969
+ ```ruby
4970
+ require 'glimmer-dsl-libui'
4971
+
4972
+ # Michael Ende (1929-1995)
4973
+ # The Neverending Story is a fantasy novel by German writer Michael Ende,
4974
+ # The English version, translated by Ralph Manheim, was published in 1983.
4975
+ class BasicDrawText
4976
+ include Glimmer
4977
+
4978
+ def alternating_color_string(&block)
4979
+ @index ||= 0
4980
+ @index += 1
4981
+ string {
4982
+ if @index.odd?
4983
+ color r: 0.5, g: 0, b: 0.25, a: 0.7
4984
+ else
4985
+ color r: 0, g: 0.5, b: 0, a: 0.7
4986
+ end
4987
+
4988
+ block.call + "\n\n"
4989
+ }
4990
+ end
4991
+
4992
+ def launch
4993
+ window('Michael Ende (1929-1995) The Neverending Story', 600, 400) {
4994
+ margined true
4995
+
4996
+ area {
4997
+ on_draw do |area_draw_params|
4998
+ text(0, 0, area_draw_params[:area_width]) {
4999
+ align 0
5000
+ default_font family: 'Georgia', size: 13, weight: 500, italic: 0, stretch: 4
5001
+
5002
+ alternating_color_string {
5003
+ ' At last Ygramul sensed that something was coming toward ' \
5004
+ 'her. With the speed of lightning, she turned about, confronting ' \
5005
+ 'Atreyu with an enormous steel-blue face. Her single eye had a ' \
5006
+ 'vertical pupil, which stared at Atreyu with inconceivable malignancy. '
5007
+ }
5008
+ alternating_color_string {
5009
+ ' A cry of fear escaped Bastian. '
5010
+ }
5011
+ alternating_color_string {
5012
+ ' A cry of terror passed through the ravine and echoed from ' \
5013
+ 'side to side. Ygramul turned her eye to left and right, to see if ' \
5014
+ 'someone else had arrived, for that sound could not have been ' \
5015
+ 'made by the boy who stood there as though paralyzed with ' \
5016
+ 'horror. '
5017
+ }
5018
+ alternating_color_string {
5019
+ ' Could she have heard my cry? Bastion wondered in alarm. ' \
5020
+ "But that's not possible. "
5021
+ }
5022
+ alternating_color_string {
5023
+ ' And then Atreyu heard Ygramuls voice. It was very high ' \
5024
+ 'and slightly hoarse, not at all the right kind of voice for that ' \
5025
+ 'enormous face. Her lips did not move as she spoke. It was the ' \
5026
+ 'buzzing of a great swarm of hornets that shaped itself into ' \
5027
+ 'words. '
5028
+ }
5029
+ }
5030
+ end
5031
+ }
5032
+ }.show
5033
+ end
5034
+ end
5035
+
5036
+ BasicDrawText.new.launch
5037
+ ```
5038
+
4489
5039
  ## Contributing to glimmer-dsl-libui
4490
5040
 
4491
5041
  - Check out the latest master to make sure the feature hasn't been