glimmer-dsl-libui 0.4.5 → 0.4.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/README.md +388 -57
  4. data/VERSION +1 -1
  5. data/examples/button_counter.rb +2 -1
  6. data/examples/color_button.rb +18 -13
  7. data/examples/color_button2.rb +14 -0
  8. data/examples/date_time_picker.rb +19 -14
  9. data/examples/date_time_picker2.rb +20 -0
  10. data/examples/font_button.rb +17 -12
  11. data/examples/font_button2.rb +18 -0
  12. data/examples/histogram.rb +1 -6
  13. data/examples/meta_example.rb +17 -6
  14. data/examples/midi_player.rb +5 -6
  15. data/examples/midi_player2.rb +83 -0
  16. data/examples/midi_player3.rb +84 -0
  17. data/examples/snake.rb +19 -10
  18. data/examples/tetris.rb +15 -18
  19. data/glimmer-dsl-libui.gemspec +0 -0
  20. data/lib/glimmer/libui/control_proxy/checkbox_proxy.rb +4 -0
  21. data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +4 -0
  22. data/lib/glimmer/libui/control_proxy/combobox_proxy.rb +17 -2
  23. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy.rb +4 -0
  24. data/lib/glimmer/libui/control_proxy/editable_combobox_proxy.rb +4 -0
  25. data/lib/glimmer/libui/control_proxy/entry_proxy.rb +1 -2
  26. data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +8 -0
  27. data/lib/glimmer/libui/control_proxy/menu_item_proxy/check_menu_item_proxy.rb +4 -0
  28. data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +16 -4
  29. data/lib/glimmer/libui/control_proxy/multiline_entry_proxy.rb +1 -2
  30. data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +19 -0
  31. data/lib/glimmer/libui/control_proxy/slider_proxy.rb +37 -0
  32. data/lib/glimmer/libui/control_proxy/spinbox_proxy.rb +1 -2
  33. data/lib/glimmer/libui/control_proxy.rb +2 -2
  34. data/lib/glimmer/libui/data_bindable.rb +27 -2
  35. metadata +8 -2
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 LibUI 0.4.5
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.4.9
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
  [![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 [LibUI](https://github.com/kojix2/LibUI) is a prerequisite-free Ruby desktop development GUI library. No need to pre-install any prerequisites. Just install the gem and have platform-independent native GUI that just works!
6
+ [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for [LibUI](https://github.com/kojix2/LibUI) is a prerequisite-free Ruby desktop development GUI (Graphical User Interface) library. No need to pre-install any prerequisites. Just install the [gem](https://rubygems.org/gems/glimmer-dsl-libui) and have platform-independent native GUI that just works!
7
7
 
8
8
  Mac | Windows | Linux
9
9
  ----|---------|------
@@ -373,7 +373,7 @@ gem install glimmer-dsl-libui
373
373
  Or install via Bundler `Gemfile`:
374
374
 
375
375
  ```ruby
376
- gem 'glimmer-dsl-libui', '~> 0.4.5'
376
+ gem 'glimmer-dsl-libui', '~> 0.4.9'
377
377
  ```
378
378
 
379
379
  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.
@@ -460,7 +460,7 @@ Keyword(Args) | Properties | Listeners
460
460
  `checkbox_text_column(name as String)` | `editable` (Boolean), `editable_checkbox` (Boolean), `editable_text` (Boolean) | None
461
461
  `checkbox_text_color_column(name as String)` | `editable` (Boolean), `editable_checkbox` (Boolean), `editable_text` (Boolean) | None
462
462
  `check_menu_item(text as String)` | `checked` (Boolean) | `on_clicked`
463
- `combobox` | `items` (`Array` of `String`), `selected` (`Integer`) | `on_selected`
463
+ `combobox` | `items` (`Array` of `String`), `selected` (`Integer`), `selected_item` (`String`) | `on_selected`
464
464
  `color_button` | `color` (Array of `red` as `Float`, `green` as `Float`, `blue` as `Float`, `alpha` as `Float`), `red` as `Float`, `green` as `Float`, `blue` as `Float`, `alpha` as `Float` | `on_changed`
465
465
  `date_picker` | `time` (`Hash` of keys: `sec` as `Integer`, `min` as `Integer`, `hour` as `Integer`, `mday` as `Integer`, `mon` as `Integer`, `year` as `Integer`, `wday` as `Integer`, `yday` as `Integer`, `dst` as Boolean) | `on_changed`
466
466
  `date_time_picker` | `time` (`Hash` of keys: `sec` as `Integer`, `min` as `Integer`, `hour` as `Integer`, `mday` as `Integer`, `mon` as `Integer`, `year` as `Integer`, `wday` as `Integer`, `yday` as `Integer`, `dst` as Boolean) | `on_changed`
@@ -1382,11 +1382,24 @@ Data-binding supports utilizing the [MVP (Model View Presenter)](https://en.wiki
1382
1382
  ![MVP](https://www.researchgate.net/profile/Gilles-Perrouin/publication/320249584/figure/fig8/AS:668260987068418@1536337243385/Model-view-presenter-architecture.png)
1383
1383
 
1384
1384
  [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) supports bidirectional (two-way) data-binding of the following controls/properties via the `<=>` operator (indicating data is moving in both directions between View and Model):
1385
- - `entry` `text` property
1386
- - `multiline_entry` `text` property
1387
- - `non_wrapping_multiline_entry` `text` property
1388
- - `search_entry` `text` property
1389
- - `spinbox` `value` property
1385
+ - `checkbox`: `checked`
1386
+ - `check_menu_item`: `checked`
1387
+ - `color_button`: `color`
1388
+ - `combobox`: `selected`, `selected_item`
1389
+ - `date_picker`: `time`
1390
+ - `date_time_picker`: `time`
1391
+ - `editable_combobox`: `text`
1392
+ - `entry`: `text`
1393
+ - `font_button`: `font`
1394
+ - `multiline_entry`: `text`
1395
+ - `non_wrapping_multiline_entry`: `text`
1396
+ - `radio_buttons`: `selected`
1397
+ - `radio_menu_item`: `checked`
1398
+ - `search_entry`: `text`
1399
+ - `slider`: `value`
1400
+ - `spinbox`: `value`
1401
+ - `table`: `cell_rows` (explicit data-binding using `<=>` and [implicit data-binding](#table-api) by assigning value directly)
1402
+ - `time_picker`: `time`
1390
1403
 
1391
1404
  Example of bidirectional data-binding:
1392
1405
 
@@ -1436,6 +1449,11 @@ To summarize the data-binding API:
1436
1449
 
1437
1450
  This is also known as the [Glimmer Shine](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#shine) syntax for data-binding, a [Glimmer](https://github.com/AndyObtiva/glimmer)-only unique innovation that takes advantage of [Ruby](https://www.ruby-lang.org/en/)'s highly expressive syntax and malleable DSL support.
1438
1451
 
1452
+ Data-bound model attribute can be:
1453
+ - **Direct:** `Symbol` representing attribute reader/writer (e.g. `[person, :name`])
1454
+ - **Nested:** `String` representing nested attribute path (e.g. `[company, 'address.street']`). That results in "nested data-binding"
1455
+ - **Indexed:** `String` containing array attribute index (e.g. `[customer, 'addresses[0].street']`). That results in "indexed data-binding"
1456
+
1439
1457
  Data-binding options include:
1440
1458
  - `before_read {|value| ...}`: performs an operation before reading data from Model to update the View.
1441
1459
  - `on_read {|value| ...}`: converts value read from Model to update the View.
@@ -1443,7 +1461,7 @@ Data-binding options include:
1443
1461
  - `before_write {|value| ...}`: performs an operation before writing data to Model from View.
1444
1462
  - `on_write {|value| ...}`: converts value read from View to update the Model.
1445
1463
  - `after_write {|converted_value| ...}`: performs an operation after writing to Model from View.
1446
- - `computed_by attribute` or `computed_by [attribute1, attribute2, ...]`: indicates model attribute is computed from specified attribute(s), thus updated when they are updated (see in [Login example version 2](/examples/login2.rb))
1464
+ - `computed_by attribute` or `computed_by [attribute1, attribute2, ...]`: indicates model attribute is computed from specified attribute(s), thus updated when they are updated (see in [Login example version 2](/examples/login2.rb)). That is known as "computed data-binding".
1447
1465
 
1448
1466
  Note that with both `on_read` and `on_write` converters, you could pass a `Symbol` representing the name of a method on the value object to invoke.
1449
1467
 
@@ -1875,7 +1893,7 @@ Example:
1875
1893
 
1876
1894
  ## Examples
1877
1895
 
1878
- The following examples include reimplementions of the examples in the [LibUI](https://github.com/kojix2/LibUI) project utilizing the [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) as well as brand new examples.
1896
+ The following examples include reimplementions of the examples in the [LibUI](https://github.com/kojix2/LibUI) project utilizing the [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) (with and without [data-binding](#data-binding)) as well as brand new examples.
1879
1897
 
1880
1898
  To browse all examples, simply launch the [Meta-Example](examples/meta_example.rb), which lists all examples and displays each example's code when selected. It also enables code editing to facilitate experimentation and learning.
1881
1899
 
@@ -2490,7 +2508,33 @@ UI.quit
2490
2508
 
2491
2509
  ```
2492
2510
 
2493
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
2511
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
2512
+
2513
+ ```ruby
2514
+ require 'glimmer-dsl-libui'
2515
+
2516
+ class FontButton
2517
+ include Glimmer
2518
+
2519
+ attr_accessor :font_descriptor
2520
+
2521
+ def launch
2522
+ window('hello world', 300, 200) {
2523
+ font_button {
2524
+ font <=> [self, :font_descriptor, after_write: -> { p font_descriptor }]
2525
+ }
2526
+
2527
+ on_closing do
2528
+ puts 'Bye Bye'
2529
+ end
2530
+ }.show
2531
+ end
2532
+ end
2533
+
2534
+ FontButton.new.launch
2535
+ ```
2536
+
2537
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
2494
2538
 
2495
2539
  ```ruby
2496
2540
  require 'glimmer-dsl-libui'
@@ -2531,7 +2575,33 @@ Mac | Windows | Linux
2531
2575
  ----|---------|------
2532
2576
  ![glimmer-dsl-libui-mac-color-button.png](images/glimmer-dsl-libui-mac-color-button.png) ![glimmer-dsl-libui-mac-color-button-selection.png](images/glimmer-dsl-libui-mac-color-button-selection.png) | ![glimmer-dsl-libui-windows-color-button.png](images/glimmer-dsl-libui-windows-color-button.png) ![glimmer-dsl-libui-windows-color-button-selection.png](images/glimmer-dsl-libui-windows-color-button-selection.png) | ![glimmer-dsl-libui-linux-color-button.png](images/glimmer-dsl-libui-linux-color-button.png) ![glimmer-dsl-libui-linux-color-button-selection.png](images/glimmer-dsl-libui-linux-color-button-selection.png)
2533
2577
 
2534
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
2578
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
2579
+
2580
+ ```ruby
2581
+ require 'glimmer-dsl-libui'
2582
+
2583
+ class ColorButton
2584
+ include Glimmer
2585
+
2586
+ attr_accessor :selected_color
2587
+
2588
+ def initialize
2589
+ @selected_color = :blue
2590
+ end
2591
+
2592
+ def launch
2593
+ window('color button', 240) {
2594
+ color_button {
2595
+ color <=> [self, :selected_color, after_write: ->(color) {p color}]
2596
+ }
2597
+ }.show
2598
+ end
2599
+ end
2600
+
2601
+ ColorButton.new.launch
2602
+ ```
2603
+
2604
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
2535
2605
 
2536
2606
  ```ruby
2537
2607
  require 'glimmer-dsl-libui'
@@ -2613,7 +2683,35 @@ UI.main
2613
2683
  UI.quit
2614
2684
  ```
2615
2685
 
2616
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
2686
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
2687
+
2688
+ ```ruby
2689
+ require 'glimmer-dsl-libui'
2690
+
2691
+ class DateTimePicker
2692
+ include Glimmer
2693
+
2694
+ attr_accessor :picked_time
2695
+
2696
+ def launch
2697
+ window('Date Time Pickers', 300, 200) {
2698
+ vertical_box {
2699
+ date_time_picker {
2700
+ time <=> [self, :picked_time, after_write: ->(time) { p time }]
2701
+ }
2702
+ }
2703
+
2704
+ on_closing do
2705
+ puts 'Bye Bye'
2706
+ end
2707
+ }.show
2708
+ end
2709
+ end
2710
+
2711
+ DateTimePicker.new.launch
2712
+ ```
2713
+
2714
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
2617
2715
 
2618
2716
  ```ruby
2619
2717
  require 'glimmer-dsl-libui'
@@ -3171,7 +3269,62 @@ Mac | Windows | Linux
3171
3269
  ----|---------|------
3172
3270
  ![glimmer-dsl-libui-mac-basic-table-button.png](images/glimmer-dsl-libui-mac-basic-table-button.png) ![glimmer-dsl-libui-mac-basic-table-button-deleted.png](images/glimmer-dsl-libui-mac-basic-table-button-deleted.png) | ![glimmer-dsl-libui-windows-basic-table-button.png](images/glimmer-dsl-libui-windows-basic-table-button.png) ![glimmer-dsl-libui-windows-basic-table-button-deleted.png](images/glimmer-dsl-libui-windows-basic-table-button-deleted.png) | ![glimmer-dsl-libui-linux-basic-table-button.png](images/glimmer-dsl-libui-linux-basic-table-button.png) ![glimmer-dsl-libui-linux-basic-table-button-deleted.png](images/glimmer-dsl-libui-linux-basic-table-button-deleted.png)
3173
3271
 
3174
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
3272
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
3273
+
3274
+ ```ruby
3275
+ # frozen_string_literal: true
3276
+
3277
+ require 'glimmer-dsl-libui'
3278
+
3279
+ class BasicTableButton
3280
+ include Glimmer
3281
+
3282
+ attr_accessor :data
3283
+
3284
+ def initialize
3285
+ @data = [
3286
+ %w[cat meow delete],
3287
+ %w[dog woof delete],
3288
+ %w[chicken cock-a-doodle-doo delete],
3289
+ %w[horse neigh delete],
3290
+ %w[cow moo delete]
3291
+ ]
3292
+ end
3293
+
3294
+ def launch
3295
+ window('Animal sounds', 400, 200) {
3296
+ horizontal_box {
3297
+ table {
3298
+ text_column('Animal')
3299
+ text_column('Description')
3300
+ button_column('Action') {
3301
+ on_clicked do |row|
3302
+ # Option 1: direct data deletion is the simpler solution
3303
+ # @data.delete_at(row) # automatically deletes actual table row due to explicit data-binding
3304
+
3305
+ # Option 2: cloning only to demonstrate table row deletion upon explicit setting of data attribute (cloning is not recommended beyond demonstrating this point)
3306
+ new_data = @data.clone
3307
+ new_data.delete_at(row)
3308
+ self.data = new_data # automatically loses deleted table row due to explicit data-binding
3309
+ end
3310
+ }
3311
+
3312
+ cell_rows <=> [self, :data] # explicit data-binding of table cell_rows to self.data
3313
+
3314
+ on_changed do |row, type, row_data|
3315
+ puts "Row #{row} #{type}: #{row_data}"
3316
+ $stdout.flush
3317
+ end
3318
+ }
3319
+ }
3320
+ }.show
3321
+ end
3322
+ end
3323
+
3324
+ BasicTableButton.new.launch
3325
+ ```
3326
+
3327
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (with implicit [data-binding](#data-binding)):
3175
3328
 
3176
3329
  ```ruby
3177
3330
  require 'glimmer-dsl-libui'
@@ -4950,7 +5103,8 @@ class ButtonCounter
4950
5103
  def launch
4951
5104
  window('Hello, Button!') {
4952
5105
  button {
4953
- text <= [self, :count, on_read: ->(count) {"Count: #{count}"}] # data-bind button text to self count, converting to string on read.
5106
+ # data-bind button text to self count, converting to string on read.
5107
+ text <= [self, :count, on_read: ->(count) {"Count: #{count}"}]
4954
5108
 
4955
5109
  on_clicked do
4956
5110
  self.count += 1
@@ -6351,7 +6505,7 @@ require 'glimmer-dsl-libui'
6351
6505
  class FormTable
6352
6506
  include Glimmer
6353
6507
 
6354
- attr_accessor :name, :email, :phone, :city, :state, :filter_value
6508
+ attr_accessor :data, :name, :email, :phone, :city, :state, :filter_value
6355
6509
 
6356
6510
  def initialize
6357
6511
  @data = [
@@ -6422,10 +6576,10 @@ class FormTable
6422
6576
  after_write: ->(filter_value) { # execute after write to self.filter_value
6423
6577
  @unfiltered_data ||= @data.dup
6424
6578
  # Unfilter first to remove any previous filters
6425
- @data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
6579
+ self.data = @unfiltered_data # affects table indirectly through explicit data-binding
6426
6580
  # Now, apply filter if entered
6427
6581
  unless filter_value.empty?
6428
- @data.filter! do |row_data| # affects table indirectly through implicit data-binding
6582
+ self.data = @data.filter do |row_data| # affects table indirectly through explicit data-binding
6429
6583
  row_data.any? do |cell|
6430
6584
  cell.to_s.downcase.include?(filter_value.downcase)
6431
6585
  end
@@ -6442,7 +6596,7 @@ class FormTable
6442
6596
  text_column('City')
6443
6597
  text_column('State')
6444
6598
 
6445
- cell_rows @data # implicit data-binding
6599
+ cell_rows <=> [self, :data] # explicit data-binding
6446
6600
 
6447
6601
  on_changed do |row, type, row_data|
6448
6602
  puts "Row #{row} #{type}: #{row_data}"
@@ -6975,12 +7129,7 @@ class Histogram
6975
7129
 
6976
7130
  color_button { |cb|
6977
7131
  stretchy false
6978
- color COLOR_BLUE
6979
-
6980
- on_changed do
6981
- @histogram_color = cb.color
6982
- @area.queue_redraw_all
6983
- end
7132
+ color <=> [self, :histogram_color, after_write: -> { @area.queue_redraw_all }]
6984
7133
  }
6985
7134
  }
6986
7135
 
@@ -7802,7 +7951,181 @@ end
7802
7951
  TinyMidiPlayer.new
7803
7952
  ```
7804
7953
 
7805
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
7954
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
7955
+
7956
+ ```ruby
7957
+ # frozen_string_literal: true
7958
+
7959
+ require 'glimmer-dsl-libui'
7960
+
7961
+ class TinyMidiPlayer
7962
+ include Glimmer
7963
+
7964
+ VERSION = '0.0.1'
7965
+
7966
+ attr_accessor :selected_file
7967
+
7968
+ def initialize
7969
+ @pid = nil
7970
+ @music_directory = File.expand_path('../sounds', __dir__)
7971
+ @midi_files = Dir.glob(File.join(@music_directory, '**/*.mid'))
7972
+ .sort_by { |path| File.basename(path) }
7973
+ at_exit { stop_midi }
7974
+ create_gui
7975
+ end
7976
+
7977
+ def stop_midi
7978
+ if @pid
7979
+ Process.kill(:SIGKILL, @pid) if @th.alive?
7980
+ @pid = nil
7981
+ end
7982
+ end
7983
+
7984
+ def play_midi
7985
+ stop_midi
7986
+ if @pid.nil? && @selected_file
7987
+ begin
7988
+ @pid = spawn "timidity #{@selected_file}"
7989
+ @th = Process.detach @pid
7990
+ rescue Errno::ENOENT
7991
+ warn 'Timidty++ not found. Please install Timidity++.'
7992
+ warn 'https://sourceforge.net/projects/timidity/'
7993
+ end
7994
+ end
7995
+ end
7996
+
7997
+ def show_version
7998
+ msg_box('Tiny Midi Player',
7999
+ "Written in Ruby\n" \
8000
+ "https://github.com/kojix2/libui\n" \
8001
+ "Version #{VERSION}")
8002
+ end
8003
+
8004
+ def create_gui
8005
+ menu('Help') {
8006
+ menu_item('Version') {
8007
+ on_clicked do
8008
+ show_version
8009
+ end
8010
+ }
8011
+ }
8012
+ window('Tiny Midi Player', 200, 50) {
8013
+ horizontal_box {
8014
+ vertical_box {
8015
+ stretchy false
8016
+
8017
+ button('▶') {
8018
+ on_clicked do
8019
+ play_midi
8020
+ end
8021
+ }
8022
+ button('■') {
8023
+ on_clicked do
8024
+ stop_midi
8025
+ end
8026
+ }
8027
+ }
8028
+
8029
+ combobox {
8030
+ items @midi_files.map { |path| File.basename(path) }
8031
+ # data-bind selected item (String) to self.selected_file with on-read/on-write converters and after_write operation
8032
+ selected_item <=> [self, :selected_file, on_read: ->(f) {File.basename(f.to_s)}, on_write: ->(f) {File.join(@music_directory, f)}, after_write: -> { play_midi if @th&.alive? }]
8033
+ }
8034
+ }
8035
+ }.show
8036
+ end
8037
+ end
8038
+
8039
+ TinyMidiPlayer.new
8040
+ ```
8041
+
8042
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (with [data-binding](#data-binding)):
8043
+
8044
+ ```ruby
8045
+ require 'glimmer-dsl-libui'
8046
+
8047
+ class TinyMidiPlayer
8048
+ include Glimmer
8049
+
8050
+ VERSION = '0.0.1'
8051
+
8052
+ attr_accessor :selected_file
8053
+
8054
+ def initialize
8055
+ @pid = nil
8056
+ @music_directory = File.expand_path('../sounds', __dir__)
8057
+ @midi_files = Dir.glob(File.join(@music_directory, '**/*.mid'))
8058
+ .sort_by { |path| File.basename(path) }
8059
+ at_exit { stop_midi }
8060
+ create_gui
8061
+ end
8062
+
8063
+ def stop_midi
8064
+ if @pid
8065
+ Process.kill(:SIGKILL, @pid) if @th.alive?
8066
+ @pid = nil
8067
+ end
8068
+ end
8069
+
8070
+ def play_midi
8071
+ stop_midi
8072
+ if @pid.nil? && @selected_file
8073
+ begin
8074
+ @pid = spawn "timidity #{@selected_file}"
8075
+ @th = Process.detach @pid
8076
+ rescue Errno::ENOENT
8077
+ warn 'Timidty++ not found. Please install Timidity++.'
8078
+ warn 'https://sourceforge.net/projects/timidity/'
8079
+ end
8080
+ end
8081
+ end
8082
+
8083
+ def show_version
8084
+ msg_box('Tiny Midi Player',
8085
+ "Written in Ruby\n" \
8086
+ "https://github.com/kojix2/libui\n" \
8087
+ "Version #{VERSION}")
8088
+ end
8089
+
8090
+ def create_gui
8091
+ menu('Help') {
8092
+ menu_item('Version') {
8093
+ on_clicked do
8094
+ show_version
8095
+ end
8096
+ }
8097
+ }
8098
+ window('Tiny Midi Player', 200, 50) {
8099
+ horizontal_box {
8100
+ vertical_box {
8101
+ stretchy false
8102
+
8103
+ button('▶') {
8104
+ on_clicked do
8105
+ play_midi
8106
+ end
8107
+ }
8108
+ button('■') {
8109
+ on_clicked do
8110
+ stop_midi
8111
+ end
8112
+ }
8113
+ }
8114
+
8115
+ combobox {
8116
+ items @midi_files.map { |path| File.basename(path) }
8117
+ # data-bind selected index (Integer) to self.selected_file with on-read/on-write converters and after_write operation
8118
+ selected <=> [self, :selected_file, on_read: ->(f) {@midi_files.index(f)}, on_write: ->(i) {@midi_files[i]}, after_write: -> { play_midi if @th&.alive? }]
8119
+ }
8120
+ }
8121
+ }.show
8122
+ end
8123
+ end
8124
+
8125
+ TinyMidiPlayer.new
8126
+ ```
8127
+
8128
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (without [data-binding](#data-binding)):
7806
8129
 
7807
8130
  ```ruby
7808
8131
  require 'glimmer-dsl-libui'
@@ -7928,6 +8251,7 @@ class Snake
7928
8251
  @game = Model::Game.new
7929
8252
  @grid = Presenter::Grid.new(@game)
7930
8253
  @game.start
8254
+ @keypress_queue = []
7931
8255
  create_gui
7932
8256
  register_observers
7933
8257
  end
@@ -7947,14 +8271,30 @@ class Snake
7947
8271
  end
7948
8272
 
7949
8273
  Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
7950
- @game.snake.move unless @game.over?
8274
+ unless @game.over?
8275
+ process_queued_keypress
8276
+ @game.snake.move
8277
+ end
8278
+ end
8279
+ end
8280
+
8281
+ def process_queued_keypress
8282
+ # key press queue ensures one turn per snake move to avoid a double-turn resulting in instant death (due to snake illogically going back against itself)
8283
+ key = @keypress_queue.shift
8284
+ case [@game.snake.head.orientation, key]
8285
+ in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
8286
+ @game.snake.turn_right
8287
+ in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
8288
+ @game.snake.turn_left
8289
+ else
8290
+ # No Op
7951
8291
  end
7952
8292
  end
7953
8293
 
7954
8294
  def create_gui
7955
8295
  @main_window = window {
7956
8296
  # data-bind window title to game score, converting it to a title string on read from the model
7957
- title <= [@game, :score, on_read: -> (score) {"Glimmer Snake (Score: #{@game.score})"}]
8297
+ title <= [@game, :score, on_read: -> (score) {"Snake (Score: #{@game.score})"}]
7958
8298
  content_size @game.width * CELL_SIZE, @game.height * CELL_SIZE
7959
8299
  resizable false
7960
8300
 
@@ -7972,15 +8312,7 @@ class Snake
7972
8312
  }
7973
8313
 
7974
8314
  on_key_up do |area_key_event|
7975
- orientation_and_key = [@game.snake.head.orientation, area_key_event[:ext_key]]
7976
- case orientation_and_key
7977
- in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
7978
- @game.snake.turn_right
7979
- in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
7980
- @game.snake.turn_left
7981
- else
7982
- # No Op
7983
- end
8315
+ @keypress_queue << area_key_event[:ext_key]
7984
8316
  end
7985
8317
  }
7986
8318
  end
@@ -8130,22 +8462,23 @@ class Tetris
8130
8462
  menu('Game') {
8131
8463
  @pause_menu_item = check_menu_item('Pause') {
8132
8464
  enabled false
8133
-
8134
- on_clicked do
8135
- @game.paused = @pause_menu_item.checked?
8136
- end
8465
+ checked <=> [@game, :paused]
8137
8466
  }
8467
+
8138
8468
  menu_item('Restart') {
8139
8469
  on_clicked do
8140
8470
  @game.restart!
8141
8471
  end
8142
8472
  }
8473
+
8143
8474
  separator_menu_item
8475
+
8144
8476
  menu_item('Exit') {
8145
8477
  on_clicked do
8146
8478
  exit(0)
8147
8479
  end
8148
8480
  }
8481
+
8149
8482
  quit_menu_item if OS.mac?
8150
8483
  }
8151
8484
 
@@ -8155,6 +8488,7 @@ class Tetris
8155
8488
  show_high_scores
8156
8489
  end
8157
8490
  }
8491
+
8158
8492
  menu_item('Clear High Scores') {
8159
8493
  on_clicked {
8160
8494
  @game.clear_high_scores!
@@ -8163,22 +8497,16 @@ class Tetris
8163
8497
  }
8164
8498
 
8165
8499
  menu('Options') {
8166
- radio_menu_item('Instant Down on Up Arrow') {
8167
- on_clicked do
8168
- @game.instant_down_on_up = true
8169
- end
8500
+ radio_menu_item('Instant Down on Up Arrow') { |r|
8501
+ checked <=> [@game, :instant_down_on_up]
8170
8502
  }
8171
- radio_menu_item('Rotate Right on Up Arrow') {
8172
- on_clicked do
8173
- @game.rotate_right_on_up = true
8174
- end
8503
+
8504
+ radio_menu_item('Rotate Right on Up Arrow') { |r|
8505
+ checked <=> [@game, :rotate_right_on_up]
8175
8506
  }
8176
- radio_menu_item('Rotate Left on Up Arrow') {
8177
- checked true
8178
-
8179
- on_clicked do
8180
- @game.rotate_left_on_up = true
8181
- end
8507
+
8508
+ radio_menu_item('Rotate Left on Up Arrow') { |r|
8509
+ checked <=> [@game, :rotate_left_on_up]
8182
8510
  }
8183
8511
  }
8184
8512
 
@@ -8190,6 +8518,7 @@ class Tetris
8190
8518
  end
8191
8519
  }
8192
8520
  end
8521
+
8193
8522
  menu_item('About') {
8194
8523
  on_clicked do
8195
8524
  show_about_dialog
@@ -8819,7 +9148,7 @@ https://github.com/iraamaro/i3off-gtk-ruby
8819
9148
 
8820
9149
  ### Issues
8821
9150
 
8822
- If you encounter [issues](https://github.com/AndyObtiva/glimmer-dsl-libui/issues) that are not reported, discover missing features that are not mentioned in [TODO.md](TODO.md), or think up better ways to use [libui](https://github.com/andlabs/libui) than what is possible with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui), you may submit an [issue](https://github.com/AndyObtiva/glimmer-dsl-libui/issues/new) or [pull request](https://github.com/AndyObtiva/glimmer-dsl-libui/compare) on [GitHub](https://github.com).
9151
+ If you encounter [issues](https://github.com/AndyObtiva/glimmer-dsl-libui/issues) that are not reported, discover missing features that are not mentioned in [TODO.md](TODO.md), or think up better ways to use [libui](https://github.com/andlabs/libui) than what is possible with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui), you may submit an [issue](https://github.com/AndyObtiva/glimmer-dsl-libui/issues/new) or [pull request](https://github.com/AndyObtiva/glimmer-dsl-libui/compare) on [GitHub](https://github.com). In the meantime, you may try older gem versions of [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) till you find one that works until issues are resolved.
8823
9152
 
8824
9153
  ### Chat
8825
9154
 
@@ -8851,6 +9180,8 @@ These features have been planned or suggested. You might see them in a future ve
8851
9180
  is fine, but please isolate to its own commit so I can cherry-pick
8852
9181
  around it.
8853
9182
 
9183
+ Note that the latest development sometimes takes place in the [development](https://github.com/AndyObtiva/glimmer-dsl-libui/tree/development) branch (usually deleted once merged back to [master](https://github.com/AndyObtiva/glimmer-dsl-libui)).
9184
+
8854
9185
  ## Contributors
8855
9186
 
8856
9187
  * [Andy Maleh](https://github.com/AndyObtiva) (Founder)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.5
1
+ 0.4.9
@@ -13,7 +13,8 @@ class ButtonCounter
13
13
  window('Hello, Button!', 190, 20) {
14
14
  vertical_box {
15
15
  button {
16
- text <= [self, :count, on_read: ->(count) {"Count: #{count}"}] # data-bind button text to self count, converting to string on read.
16
+ # data-bind button text to self count, converting to string on read.
17
+ text <= [self, :count, on_read: ->(count) {"Count: #{count}"}]
17
18
 
18
19
  on_clicked do
19
20
  self.count += 1
@@ -1,16 +1,21 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'glimmer-dsl-libui'
4
2
 
5
- include Glimmer
3
+ class ColorButton
4
+ include Glimmer
5
+
6
+ attr_accessor :selected_color
7
+
8
+ def initialize
9
+ @selected_color = :blue
10
+ end
11
+
12
+ def launch
13
+ window('color button', 240) {
14
+ color_button {
15
+ color <=> [self, :selected_color, after_write: ->(color) {p color}]
16
+ }
17
+ }.show
18
+ end
19
+ end
6
20
 
7
- window('color button', 240) {
8
- color_button { |cb|
9
- color :blue
10
-
11
- on_changed do
12
- rgba = cb.color
13
- p rgba
14
- end
15
- }
16
- }.show
21
+ ColorButton.new.launch