glimmer-dsl-libui 0.1.11 → 0.2.3

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 +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