glimmer-dsl-libui 0.4.5 → 0.4.9

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