glimmer-dsl-libui 0.4.4 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -1
  3. data/README.md +932 -160
  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/dynamic_area.rb +77 -90
  11. data/examples/dynamic_area2.rb +14 -12
  12. data/examples/dynamic_area3.rb +90 -0
  13. data/examples/dynamic_area4.rb +95 -0
  14. data/examples/font_button.rb +17 -12
  15. data/examples/font_button2.rb +18 -0
  16. data/examples/form_table.rb +0 -2
  17. data/examples/form_table2.rb +0 -2
  18. data/examples/histogram.rb +93 -91
  19. data/examples/histogram2.rb +109 -0
  20. data/examples/meta_example.rb +17 -6
  21. data/examples/midi_player.rb +5 -6
  22. data/examples/midi_player2.rb +83 -0
  23. data/examples/midi_player3.rb +84 -0
  24. data/examples/tetris.rb +15 -18
  25. data/examples/timer.rb +28 -31
  26. data/examples/timer2.rb +129 -0
  27. data/glimmer-dsl-libui.gemspec +0 -0
  28. data/lib/glimmer/libui/control_proxy/checkbox_proxy.rb +5 -0
  29. data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +5 -0
  30. data/lib/glimmer/libui/control_proxy/combobox_proxy.rb +18 -2
  31. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy.rb +5 -0
  32. data/lib/glimmer/libui/control_proxy/editable_combobox_proxy.rb +5 -0
  33. data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +4 -0
  34. data/lib/glimmer/libui/control_proxy/menu_item_proxy/check_menu_item_proxy.rb +5 -0
  35. data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +17 -4
  36. data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +20 -0
  37. data/lib/glimmer/libui/control_proxy/slider_proxy.rb +38 -0
  38. data/lib/glimmer/libui/control_proxy/spinbox_proxy.rb +38 -0
  39. data/lib/glimmer/libui/control_proxy.rb +2 -2
  40. metadata +13 -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.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.4.8
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.4'
376
+ gem 'glimmer-dsl-libui', '~> 0.4.8'
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`
@@ -620,93 +620,112 @@ Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
620
620
  ```ruby
621
621
  require 'glimmer-dsl-libui'
622
622
 
623
- include Glimmer
624
-
625
- data = [
626
- ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO', '80014'],
627
- ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA', '02101'],
628
- ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL', '60007'],
629
- ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA', '98101'],
630
- ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA', '90001'],
631
- ]
632
-
633
- window('Contacts', 600, 600) { |w|
634
- margined true
623
+ class FormTable
624
+ include Glimmer
635
625
 
636
- vertical_box {
637
- form {
638
- stretchy false
639
-
640
- @name_entry = entry {
641
- label 'Name'
642
- }
643
- @email_entry = entry {
644
- label 'Email'
645
- }
646
- @phone_entry = entry {
647
- label 'Phone'
648
- }
649
- @city_entry = entry {
650
- label 'City'
651
- }
652
- @state_entry = entry {
653
- label 'State'
654
- }
655
- }
656
-
657
- button('Save Contact') {
658
- stretchy false
659
-
660
- on_clicked do
661
- new_row = [@name_entry.text, @email_entry.text, @phone_entry.text, @city_entry.text, @state_entry.text]
662
- if new_row.include?('')
663
- msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
664
- else
665
- data << new_row # automatically inserts a row into the table due to implicit data-binding
666
- @unfiltered_data = data.dup
667
- @name_entry.text = ''
668
- @email_entry.text = ''
669
- @phone_entry.text = ''
670
- @city_entry.text = ''
671
- @state_entry.text = ''
672
- end
673
- end
674
- }
675
-
676
- search_entry { |se|
677
- stretchy false
626
+ attr_accessor :name, :email, :phone, :city, :state, :filter_value
627
+
628
+ def initialize
629
+ @data = [
630
+ ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO', '80014'],
631
+ ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA', '02101'],
632
+ ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL', '60007'],
633
+ ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA', '98101'],
634
+ ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA', '90001'],
635
+ ]
636
+ end
637
+
638
+ def launch
639
+ window('Contacts', 600, 600) { |w|
640
+ margined true
678
641
 
679
- on_changed do
680
- filter_value = se.text
681
- @unfiltered_data ||= data.dup
682
- # Unfilter first to remove any previous filters
683
- data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
684
- # Now, apply filter if entered
685
- unless filter_value.empty?
686
- data.filter! do |row_data| # affects table indirectly through implicit data-binding
687
- row_data.any? do |cell|
688
- cell.to_s.downcase.include?(filter_value.downcase)
642
+ vertical_box {
643
+ form {
644
+ stretchy false
645
+
646
+ entry {
647
+ label 'Name'
648
+ text <=> [self, :name]
649
+ }
650
+
651
+ entry {
652
+ label 'Email'
653
+ text <=> [self, :email]
654
+ }
655
+
656
+ entry {
657
+ label 'Phone'
658
+ text <=> [self, :phone]
659
+ }
660
+
661
+ entry {
662
+ label 'City'
663
+ text <=> [self, :city]
664
+ }
665
+
666
+ entry {
667
+ label 'State'
668
+ text <=> [self, :state]
669
+ }
670
+ }
671
+
672
+ button('Save Contact') {
673
+ stretchy false
674
+
675
+ on_clicked do
676
+ new_row = [name, email, phone, city, state]
677
+ if new_row.include?('')
678
+ msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
679
+ else
680
+ @data << new_row # automatically inserts a row into the table due to implicit data-binding
681
+ @unfiltered_data = @data.dup
682
+ self.name = '' # automatically clears name entry through explicit data-binding
683
+ self.email = ''
684
+ self.phone = ''
685
+ self.city = ''
686
+ self.state = ''
689
687
  end
690
688
  end
691
- end
692
- end
693
- }
689
+ }
690
+
691
+ search_entry {
692
+ stretchy false
693
+ text <=> [self, :filter_value, # bidirectional data-binding of text to self.filter_value with after_write option
694
+ after_write: ->(filter_value) { # execute after write to self.filter_value
695
+ @unfiltered_data ||= @data.dup
696
+ # Unfilter first to remove any previous filters
697
+ @data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
698
+ # Now, apply filter if entered
699
+ unless filter_value.empty?
700
+ @data.filter! do |row_data| # affects table indirectly through implicit data-binding
701
+ row_data.any? do |cell|
702
+ cell.to_s.downcase.include?(filter_value.downcase)
703
+ end
704
+ end
705
+ end
706
+ }
707
+ ]
708
+ }
709
+
710
+ table {
711
+ text_column('Name')
712
+ text_column('Email')
713
+ text_column('Phone')
714
+ text_column('City')
715
+ text_column('State')
694
716
 
695
- table {
696
- text_column('Name')
697
- text_column('Email')
698
- text_column('Phone')
699
- text_column('City')
700
- text_column('State')
717
+ cell_rows @data # implicit data-binding
718
+
719
+ on_changed do |row, type, row_data|
720
+ puts "Row #{row} #{type}: #{row_data}"
721
+ end
722
+ }
723
+ }
724
+ }.show
725
+ end
726
+ end
701
727
 
702
- cell_rows data # implicit data-binding
703
-
704
- on_changed do |row, type, row_data|
705
- puts "Row #{row} #{type}: #{row_data}"
706
- end
707
- }
708
- }
709
- }.show
728
+ FormTable.new.launch
710
729
  ```
711
730
 
712
731
  ![glimmer-dsl-libui-linux-form-table.png](images/glimmer-dsl-libui-linux-form-table.png)
@@ -1363,10 +1382,23 @@ Data-binding supports utilizing the [MVP (Model View Presenter)](https://en.wiki
1363
1382
  ![MVP](https://www.researchgate.net/profile/Gilles-Perrouin/publication/320249584/figure/fig8/AS:668260987068418@1536337243385/Model-view-presenter-architecture.png)
1364
1383
 
1365
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):
1366
- - `entry` `text` property
1367
- - `multiline_entry` `text` property
1368
- - `non_wrapping_multiline_entry` `text` property
1369
- - `search_entry` `text` 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
+ - `time_picker`: `time`
1370
1402
 
1371
1403
  Example of bidirectional data-binding:
1372
1404
 
@@ -1416,6 +1448,11 @@ To summarize the data-binding API:
1416
1448
 
1417
1449
  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.
1418
1450
 
1451
+ Data-bound model attribute can be:
1452
+ - **Direct:** `Symbol` representing attribute reader/writer (e.g. `[person, :name`])
1453
+ - **Nested:** `String` representing nested attribute path (e.g. `[company, 'address.street']`). That results in "nested data-binding"
1454
+ - **Indexed:** `String` containing array attribute index (e.g. `[customer, 'addresses[0].street']`). That results in "indexed data-binding"
1455
+
1419
1456
  Data-binding options include:
1420
1457
  - `before_read {|value| ...}`: performs an operation before reading data from Model to update the View.
1421
1458
  - `on_read {|value| ...}`: converts value read from Model to update the View.
@@ -1423,7 +1460,7 @@ Data-binding options include:
1423
1460
  - `before_write {|value| ...}`: performs an operation before writing data to Model from View.
1424
1461
  - `on_write {|value| ...}`: converts value read from View to update the Model.
1425
1462
  - `after_write {|converted_value| ...}`: performs an operation after writing to Model from View.
1426
- - `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))
1463
+ - `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".
1427
1464
 
1428
1465
  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.
1429
1466
 
@@ -1435,7 +1472,9 @@ entry {
1435
1472
  }
1436
1473
  ```
1437
1474
 
1438
- Gotcha: never data-bind a control property to an attribute on the same view object with the same exact name (e.g. binding `entry` `text` property to `self` `text` attribute) as it would conflict with it. Instead, data-bind view property to an attribute with a different name on the view object or with the same name, but on a presenter or model object (e.g. data-bind `entry` `text` to `self` `legal_text` attribute or to `contract` model `text` attribute)
1475
+ Data-binding gotchas:
1476
+ - Never data-bind a control property to an attribute on the same view object with the same exact name (e.g. binding `entry` `text` property to `self` `text` attribute) as it would conflict with it. Instead, data-bind view property to an attribute with a different name on the view object or with the same name, but on a presenter or model object (e.g. data-bind `entry` `text` to `self` `legal_text` attribute or to `contract` model `text` attribute)
1477
+ - Data-binding a property utilizes the control's listener associated with the property (e.g. `on_changed` for `entry` `text`), so you cannot hook into the listener directly anymore as that would negate data-binding. Instead, you can add an `after_write: ->(val) {}` option to perform something on trigger of the control listener instead.
1439
1478
 
1440
1479
  Learn more from data-binding usage in [Login](#login) (4 data-binding versions), [Basic Entry](#basic-entry), [Form](#form), [Form Table](#form-table), [Method-Based Custom Keyword](#method-based-custom-keyword), [Snake](#snake) and [Tic Tac Toe](#tic_tac_toe) examples.
1441
1480
 
@@ -1853,7 +1892,7 @@ Example:
1853
1892
 
1854
1893
  ## Examples
1855
1894
 
1856
- 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.
1895
+ 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.
1857
1896
 
1858
1897
  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.
1859
1898
 
@@ -1880,17 +1919,23 @@ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version
1880
1919
  ```ruby
1881
1920
  require 'glimmer-dsl-libui'
1882
1921
  require 'facets'
1922
+ require 'fileutils'
1883
1923
 
1884
1924
  class MetaExample
1885
1925
  include Glimmer
1886
1926
 
1927
+ ADDITIONAL_BASIC_EXAMPLES = ['Color Button', 'Font Button', 'Form', 'Date Time Picker', 'Simple Notepad']
1928
+
1929
+ attr_accessor :code_text
1930
+
1887
1931
  def initialize
1888
- @selected_example_index = 0
1932
+ @selected_example_index = examples_with_versions.index(basic_examples_with_versions.first)
1933
+ @code_text = File.read(file_path_for(selected_example))
1889
1934
  end
1890
1935
 
1891
1936
  def examples
1892
1937
  if @examples.nil?
1893
- example_files = Dir.glob(File.join(File.expand_path('.', __dir__), '**', '*.rb'))
1938
+ example_files = Dir.glob(File.join(File.expand_path('.', __dir__), '*.rb'))
1894
1939
  example_file_names = example_files.map { |f| File.basename(f, '.rb') }
1895
1940
  example_file_names = example_file_names.reject { |f| f == 'meta_example' || f.match(/\d$/) }
1896
1941
  @examples = example_file_names.map { |f| f.underscore.titlecase }
@@ -1904,12 +1949,20 @@ class MetaExample
1904
1949
  end
1905
1950
  end
1906
1951
 
1952
+ def basic_examples_with_versions
1953
+ examples_with_versions.select {|example| example.start_with?('Basic') || ADDITIONAL_BASIC_EXAMPLES.include?(example) }
1954
+ end
1955
+
1956
+ def advanced_examples_with_versions
1957
+ examples_with_versions - basic_examples_with_versions
1958
+ end
1959
+
1907
1960
  def file_path_for(example)
1908
1961
  File.join(File.expand_path('.', __dir__), "#{example.underscore}.rb")
1909
1962
  end
1910
1963
 
1911
1964
  def version_count_for(example)
1912
- Dir.glob(File.join(File.expand_path('.', __dir__), "#{example.underscore}*.rb")).select {|file| file.match(/\d\.rb$/)}.count + 1
1965
+ Dir.glob(File.join(File.expand_path('.', __dir__), "#{example.underscore}*.rb")).select {|file| file.match(/#{example.underscore}\d\.rb$/)}.count + 1
1913
1966
  end
1914
1967
 
1915
1968
  def glimmer_dsl_libui_file
@@ -1945,17 +1998,47 @@ class MetaExample
1945
1998
  vertical_box {
1946
1999
  stretchy false
1947
2000
 
1948
- @example_radio_buttons = radio_buttons {
2001
+ tab {
1949
2002
  stretchy false
1950
- items examples_with_versions
1951
- selected @selected_example_index
1952
2003
 
1953
- on_selected do
1954
- @selected_example_index = @example_radio_buttons.selected
1955
- example = selected_example
1956
- @code_entry.text = File.read(file_path_for(example))
1957
- @version_spinbox.value = 1
1958
- end
2004
+ tab_item('Basic') {
2005
+ vertical_box {
2006
+ @basic_example_radio_buttons = radio_buttons {
2007
+ stretchy false
2008
+ items basic_examples_with_versions
2009
+ selected basic_examples_with_versions.index(examples_with_versions[@selected_example_index])
2010
+
2011
+ on_selected do
2012
+ @selected_example_index = examples_with_versions.index(basic_examples_with_versions[@basic_example_radio_buttons.selected])
2013
+ example = selected_example
2014
+ self.code_text = File.read(file_path_for(example))
2015
+ @version_spinbox.value = 1
2016
+ end
2017
+ }
2018
+
2019
+ label # filler
2020
+ label # filler
2021
+ }
2022
+ }
2023
+
2024
+ tab_item('Advanced') {
2025
+ vertical_box {
2026
+ @advanced_example_radio_buttons = radio_buttons {
2027
+ stretchy false
2028
+ items advanced_examples_with_versions
2029
+
2030
+ on_selected do
2031
+ @selected_example_index = examples_with_versions.index(advanced_examples_with_versions[@advanced_example_radio_buttons.selected])
2032
+ example = selected_example
2033
+ self.code_text = File.read(file_path_for(example))
2034
+ @version_spinbox.value = 1
2035
+ end
2036
+ }
2037
+
2038
+ label # filler
2039
+ label # filler
2040
+ }
2041
+ }
1959
2042
  }
1960
2043
 
1961
2044
  horizontal_box {
@@ -1973,7 +2056,7 @@ class MetaExample
1973
2056
  else
1974
2057
  version_number = @version_spinbox.value == 1 ? '' : @version_spinbox.value
1975
2058
  example = "#{selected_example}#{version_number}"
1976
- @code_entry.text = File.read(file_path_for(example))
2059
+ self.code_text = File.read(file_path_for(example))
1977
2060
  end
1978
2061
  end
1979
2062
  }
@@ -1985,9 +2068,15 @@ class MetaExample
1985
2068
  button('Launch') {
1986
2069
  on_clicked do
1987
2070
  begin
1988
- meta_example_file = File.join(Dir.home, '.meta_example.rb')
1989
- File.write(meta_example_file, @code_entry.text)
1990
- run_example(meta_example_file)
2071
+ parent_dir = File.join(Dir.home, '.glimmer-dsl-libui', 'examples')
2072
+ FileUtils.mkdir_p(parent_dir)
2073
+ example_file = File.join(parent_dir, "#{selected_example.underscore}.rb")
2074
+ File.write(example_file, code_text)
2075
+ example_supporting_directory = File.expand_path(selected_example.underscore, __dir__)
2076
+ FileUtils.cp_r(example_supporting_directory, parent_dir) if Dir.exist?(example_supporting_directory)
2077
+ FileUtils.cp_r(File.expand_path('../icons', __dir__), File.dirname(parent_dir))
2078
+ FileUtils.cp_r(File.expand_path('../sounds', __dir__), File.dirname(parent_dir))
2079
+ run_example(example_file)
1991
2080
  rescue => e
1992
2081
  puts e.full_message
1993
2082
  puts 'Unable to write code changes! Running original example...'
@@ -1997,14 +2086,14 @@ class MetaExample
1997
2086
  }
1998
2087
  button('Reset') {
1999
2088
  on_clicked do
2000
- @code_entry.text = File.read(file_path_for(selected_example))
2089
+ self.code_text = File.read(file_path_for(selected_example))
2001
2090
  end
2002
2091
  }
2003
2092
  }
2004
2093
  }
2005
2094
 
2006
2095
  @code_entry = non_wrapping_multiline_entry {
2007
- text File.read(file_path_for(selected_example))
2096
+ text <=> [self, :code_text]
2008
2097
  }
2009
2098
  }
2010
2099
  }.show
@@ -2418,7 +2507,33 @@ UI.quit
2418
2507
 
2419
2508
  ```
2420
2509
 
2421
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
2510
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
2511
+
2512
+ ```ruby
2513
+ require 'glimmer-dsl-libui'
2514
+
2515
+ class FontButton
2516
+ include Glimmer
2517
+
2518
+ attr_accessor :font_descriptor
2519
+
2520
+ def launch
2521
+ window('hello world', 300, 200) {
2522
+ font_button {
2523
+ font <=> [self, :font_descriptor, after_write: -> { p font_descriptor }]
2524
+ }
2525
+
2526
+ on_closing do
2527
+ puts 'Bye Bye'
2528
+ end
2529
+ }.show
2530
+ end
2531
+ end
2532
+
2533
+ FontButton.new.launch
2534
+ ```
2535
+
2536
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
2422
2537
 
2423
2538
  ```ruby
2424
2539
  require 'glimmer-dsl-libui'
@@ -2459,7 +2574,33 @@ Mac | Windows | Linux
2459
2574
  ----|---------|------
2460
2575
  ![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)
2461
2576
 
2462
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
2577
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
2578
+
2579
+ ```ruby
2580
+ require 'glimmer-dsl-libui'
2581
+
2582
+ class ColorButton
2583
+ include Glimmer
2584
+
2585
+ attr_accessor :selected_color
2586
+
2587
+ def initialize
2588
+ @selected_color = :blue
2589
+ end
2590
+
2591
+ def launch
2592
+ window('color button', 240) {
2593
+ color_button {
2594
+ color <=> [self, :selected_color, after_write: ->(color) {p color}]
2595
+ }
2596
+ }.show
2597
+ end
2598
+ end
2599
+
2600
+ ColorButton.new.launch
2601
+ ```
2602
+
2603
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
2463
2604
 
2464
2605
  ```ruby
2465
2606
  require 'glimmer-dsl-libui'
@@ -2541,20 +2682,48 @@ UI.main
2541
2682
  UI.quit
2542
2683
  ```
2543
2684
 
2544
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
2685
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
2545
2686
 
2546
2687
  ```ruby
2547
2688
  require 'glimmer-dsl-libui'
2548
2689
 
2549
- include Glimmer
2550
-
2551
- window('Date Time Pickers', 300, 200) {
2552
- vertical_box {
2553
- date_time_picker { |dtp|
2554
- on_changed do
2555
- time = dtp.time
2556
- p time
2557
- end
2690
+ class DateTimePicker
2691
+ include Glimmer
2692
+
2693
+ attr_accessor :picked_time
2694
+
2695
+ def launch
2696
+ window('Date Time Pickers', 300, 200) {
2697
+ vertical_box {
2698
+ date_time_picker {
2699
+ time <=> [self, :picked_time, after_write: ->(time) { p time }]
2700
+ }
2701
+ }
2702
+
2703
+ on_closing do
2704
+ puts 'Bye Bye'
2705
+ end
2706
+ }.show
2707
+ end
2708
+ end
2709
+
2710
+ DateTimePicker.new.launch
2711
+ ```
2712
+
2713
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
2714
+
2715
+ ```ruby
2716
+ require 'glimmer-dsl-libui'
2717
+
2718
+ include Glimmer
2719
+
2720
+ window('Date Time Pickers', 300, 200) {
2721
+ vertical_box {
2722
+ date_time_picker { |dtp|
2723
+ on_changed do
2724
+ time = dtp.time
2725
+ p time
2726
+ end
2558
2727
  }
2559
2728
  }
2560
2729
 
@@ -4878,7 +5047,8 @@ class ButtonCounter
4878
5047
  def launch
4879
5048
  window('Hello, Button!') {
4880
5049
  button {
4881
- text <= [self, :count, on_read: ->(count) {"Count: #{count}"}] # data-bind button text to self count, converting to string on read.
5050
+ # data-bind button text to self count, converting to string on read.
5051
+ text <= [self, :count, on_read: ->(count) {"Count: #{count}"}]
4882
5052
 
4883
5053
  on_clicked do
4884
5054
  self.count += 1
@@ -5751,7 +5921,96 @@ Mac | Windows | Linux
5751
5921
  ----|---------|------
5752
5922
  ![glimmer-dsl-libui-mac-dynamic-area.png](images/glimmer-dsl-libui-mac-dynamic-area.png) ![glimmer-dsl-libui-mac-dynamic-area-updated.png](images/glimmer-dsl-libui-mac-dynamic-area-updated.png) | ![glimmer-dsl-libui-windows-dynamic-area.png](images/glimmer-dsl-libui-windows-dynamic-area.png) ![glimmer-dsl-libui-windows-dynamic-area-updated.png](images/glimmer-dsl-libui-windows-dynamic-area-updated.png) | ![glimmer-dsl-libui-linux-dynamic-area.png](images/glimmer-dsl-libui-linux-dynamic-area.png) ![glimmer-dsl-libui-linux-dynamic-area-updated.png](images/glimmer-dsl-libui-linux-dynamic-area-updated.png)
5753
5923
 
5754
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
5924
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
5925
+
5926
+ ```ruby
5927
+ require 'glimmer-dsl-libui'
5928
+
5929
+ class DynamicArea
5930
+ include Glimmer
5931
+
5932
+ attr_accessor :rectangle_x, :rectangle_y, :rectangle_width, :rectangle_height, :rectangle_red, :rectangle_green, :rectangle_blue, :rectangle_alpha
5933
+
5934
+ def initialize
5935
+ @rectangle_x = 25
5936
+ @rectangle_y = 25
5937
+ @rectangle_width = 150
5938
+ @rectangle_height = 150
5939
+ @rectangle_red = 102
5940
+ @rectangle_green = 102
5941
+ @rectangle_blue = 204
5942
+ @rectangle_alpha = 100
5943
+ end
5944
+
5945
+ def launch
5946
+ window('Dynamic Area', 240, 600) {
5947
+ margined true
5948
+
5949
+ vertical_box {
5950
+ label('Rectangle Properties') {
5951
+ stretchy false
5952
+ }
5953
+
5954
+ form {
5955
+ stretchy false
5956
+
5957
+ spinbox(0, 1000) {
5958
+ label 'x'
5959
+ value <=> [self, :rectangle_x, after_write: -> {@area.queue_redraw_all}]
5960
+ }
5961
+
5962
+ spinbox(0, 1000) {
5963
+ label 'y'
5964
+ value <=> [self, :rectangle_y, after_write: -> {@area.queue_redraw_all}]
5965
+ }
5966
+
5967
+ spinbox(0, 1000) {
5968
+ label 'width'
5969
+ value <=> [self, :rectangle_width, after_write: -> {@area.queue_redraw_all}]
5970
+ }
5971
+
5972
+ spinbox(0, 1000) {
5973
+ label 'height'
5974
+ value <=> [self, :rectangle_height, after_write: -> {@area.queue_redraw_all}]
5975
+ }
5976
+
5977
+ spinbox(0, 255) {
5978
+ label 'red'
5979
+ value <=> [self, :rectangle_red, after_write: -> {@area.queue_redraw_all}]
5980
+ }
5981
+
5982
+ spinbox(0, 255) {
5983
+ label 'green'
5984
+ value <=> [self, :rectangle_green, after_write: -> {@area.queue_redraw_all}]
5985
+ }
5986
+
5987
+ spinbox(0, 255) {
5988
+ label 'blue'
5989
+ value <=> [self, :rectangle_blue, after_write: -> {@area.queue_redraw_all}]
5990
+ }
5991
+
5992
+ spinbox(0, 100) {
5993
+ label 'alpha'
5994
+ value <=> [self, :rectangle_alpha, after_write: -> {@area.queue_redraw_all}]
5995
+ }
5996
+ }
5997
+
5998
+ @area = area {
5999
+ on_draw do |area_draw_params|
6000
+ rectangle(rectangle_x, rectangle_y, rectangle_width, rectangle_height) { # a dynamic path is added semi-declaratively inside on_draw block
6001
+ fill r: rectangle_red, g: rectangle_green, b: rectangle_blue, a: rectangle_alpha / 100.0
6002
+ }
6003
+ end
6004
+ }
6005
+ }
6006
+ }.show
6007
+ end
6008
+ end
6009
+
6010
+ DynamicArea.new.launch
6011
+ ```
6012
+
6013
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
5755
6014
 
5756
6015
  ```ruby
5757
6016
  require 'glimmer-dsl-libui'
@@ -5853,7 +6112,102 @@ window('Dynamic Area', 240, 600) {
5853
6112
  }.show
5854
6113
  ```
5855
6114
 
5856
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (declarative stable `path` approach):
6115
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (declarative stable `path` approach with [data-binding](#data-binding)):
6116
+
6117
+ ```ruby
6118
+ require 'glimmer-dsl-libui'
6119
+
6120
+ class DynamicArea
6121
+ include Glimmer
6122
+
6123
+ attr_accessor :rectangle_x, :rectangle_y, :rectangle_width, :rectangle_height, :rectangle_red, :rectangle_green, :rectangle_blue, :rectangle_alpha
6124
+
6125
+ def initialize
6126
+ @rectangle_x = 25
6127
+ @rectangle_y = 25
6128
+ @rectangle_width = 150
6129
+ @rectangle_height = 150
6130
+ @rectangle_red = 102
6131
+ @rectangle_green = 102
6132
+ @rectangle_blue = 204
6133
+ @rectangle_alpha = 100
6134
+ end
6135
+
6136
+ def rectangle_fill
6137
+ { r: rectangle_red, g: rectangle_green, b: rectangle_blue, a: rectangle_alpha / 100.0 }
6138
+ end
6139
+
6140
+ def launch
6141
+ window('Dynamic Area', 240, 600) {
6142
+ margined true
6143
+
6144
+ vertical_box {
6145
+ label('Rectangle Properties') {
6146
+ stretchy false
6147
+ }
6148
+
6149
+ form {
6150
+ stretchy false
6151
+
6152
+ @x_spinbox = spinbox(0, 1000) {
6153
+ label 'x'
6154
+ value <=> [self, :rectangle_x]
6155
+ }
6156
+
6157
+ @y_spinbox = spinbox(0, 1000) {
6158
+ label 'y'
6159
+ value <=> [self, :rectangle_y]
6160
+ }
6161
+
6162
+ @width_spinbox = spinbox(0, 1000) {
6163
+ label 'width'
6164
+ value <=> [self, :rectangle_width]
6165
+ }
6166
+
6167
+ @height_spinbox = spinbox(0, 1000) {
6168
+ label 'height'
6169
+ value <=> [self, :rectangle_height]
6170
+ }
6171
+
6172
+ @red_spinbox = spinbox(0, 255) {
6173
+ label 'red'
6174
+ value <=> [self, :rectangle_red]
6175
+ }
6176
+
6177
+ @green_spinbox = spinbox(0, 255) {
6178
+ label 'green'
6179
+ value <=> [self, :rectangle_green]
6180
+ }
6181
+
6182
+ @blue_spinbox = spinbox(0, 255) {
6183
+ label 'blue'
6184
+ value <=> [self, :rectangle_blue]
6185
+ }
6186
+
6187
+ @alpha_spinbox = spinbox(0, 100) {
6188
+ label 'alpha'
6189
+ value <=> [self, :rectangle_alpha]
6190
+ }
6191
+ }
6192
+
6193
+ area {
6194
+ @rectangle = rectangle { # stable implicit path shape
6195
+ x <= [self, :rectangle_x]
6196
+ y <= [self, :rectangle_y]
6197
+ width <= [self, :rectangle_width]
6198
+ height <= [self, :rectangle_height]
6199
+ fill <= [self, :rectangle_fill, computed_by: [:rectangle_red, :rectangle_green, :rectangle_blue, :rectangle_alpha]]
6200
+ }
6201
+ }
6202
+ }
6203
+ }.show
6204
+ end
6205
+ end
6206
+
6207
+ DynamicArea.new.launch
6208
+ ```
6209
+
6210
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 4 (declarative stable `path` approach without [data-binding](#data-binding)):
5857
6211
 
5858
6212
  ```ruby
5859
6213
  require 'glimmer-dsl-libui'
@@ -5945,7 +6299,7 @@ window('Dynamic Area', 240, 600) {
5945
6299
  }
5946
6300
 
5947
6301
  area {
5948
- @rectangle = rectangle(@x_spinbox.value, @y_spinbox.value, @width_spinbox.value, @height_spinbox.value) { # stable path
6302
+ @rectangle = rectangle(@x_spinbox.value, @y_spinbox.value, @width_spinbox.value, @height_spinbox.value) { # stable implicit path shape
5949
6303
  fill r: @red_spinbox.value, g: @green_spinbox.value, b: @blue_spinbox.value, a: @alpha_spinbox.value / 100.0
5950
6304
  }
5951
6305
  }
@@ -6643,7 +6997,121 @@ UI.main
6643
6997
  UI.quit
6644
6998
  ```
6645
6999
 
6646
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
7000
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
7001
+
7002
+ ```ruby
7003
+ # https://github.com/jamescook/libui-ruby/blob/master/example/histogram.rb
7004
+
7005
+ require 'glimmer-dsl-libui'
7006
+
7007
+ class Histogram
7008
+ include Glimmer
7009
+
7010
+ X_OFF_LEFT = 20
7011
+ Y_OFF_TOP = 20
7012
+ X_OFF_RIGHT = 20
7013
+ Y_OFF_BOTTOM = 20
7014
+ POINT_RADIUS = 5
7015
+ COLOR_BLUE = Glimmer::LibUI.interpret_color(0x1E90FF)
7016
+
7017
+ attr_accessor :datapoints, :histogram_color
7018
+
7019
+ def initialize
7020
+ @datapoints = 10.times.map {Random.new.rand(90)}
7021
+ @histogram_color = COLOR_BLUE
7022
+ end
7023
+
7024
+ def graph_size(area_width, area_height)
7025
+ graph_width = area_width - X_OFF_LEFT - X_OFF_RIGHT
7026
+ graph_height = area_height - Y_OFF_TOP - Y_OFF_BOTTOM
7027
+ [graph_width, graph_height]
7028
+ end
7029
+
7030
+ def point_locations(width, height)
7031
+ xincr = width / 9.0 # 10 - 1 to make the last point be at the end
7032
+ yincr = height / 100.0
7033
+
7034
+ @datapoints.each_with_index.map do |value, i|
7035
+ val = 100 - value
7036
+ [xincr * i, yincr * val]
7037
+ end
7038
+ end
7039
+
7040
+ # method-based custom control representing a graph path
7041
+ def graph_path(width, height, should_extend, &block)
7042
+ locations = point_locations(width, height).flatten
7043
+ path {
7044
+ if should_extend
7045
+ polygon(locations + [width, height, 0, height])
7046
+ else
7047
+ polyline(locations)
7048
+ end
7049
+
7050
+ # apply a transform to the coordinate space for this path so (0, 0) is the top-left corner of the graph
7051
+ transform {
7052
+ translate X_OFF_LEFT, Y_OFF_TOP
7053
+ }
7054
+
7055
+ block.call
7056
+ }
7057
+ end
7058
+
7059
+ def launch
7060
+ window('histogram example', 640, 480) {
7061
+ margined true
7062
+
7063
+ horizontal_box {
7064
+ vertical_box {
7065
+ stretchy false
7066
+
7067
+ 10.times do |i|
7068
+ spinbox(0, 100) { |sb|
7069
+ stretchy false
7070
+ value <=> [self, "datapoints[#{i}]", after_write: -> { @area.queue_redraw_all }]
7071
+ }
7072
+ end
7073
+
7074
+ color_button { |cb|
7075
+ stretchy false
7076
+ color <=> [self, :histogram_color, after_write: -> { @area.queue_redraw_all }]
7077
+ }
7078
+ }
7079
+
7080
+ @area = area {
7081
+ on_draw do |area_draw_params|
7082
+ rectangle(0, 0, area_draw_params[:area_width], area_draw_params[:area_height]) {
7083
+ fill 0xFFFFFF
7084
+ }
7085
+
7086
+ graph_width, graph_height = *graph_size(area_draw_params[:area_width], area_draw_params[:area_height])
7087
+
7088
+ figure(X_OFF_LEFT, Y_OFF_TOP) {
7089
+ line(X_OFF_LEFT, Y_OFF_TOP + graph_height)
7090
+ line(X_OFF_LEFT + graph_width, Y_OFF_TOP + graph_height)
7091
+
7092
+ stroke 0x000000, thickness: 2, miter_limit: 10
7093
+ }
7094
+
7095
+ # now create the fill for the graph below the graph line
7096
+ graph_path(graph_width, graph_height, true) {
7097
+ fill @histogram_color.merge(a: 0.5)
7098
+ }
7099
+
7100
+ # now draw the histogram line
7101
+ graph_path(graph_width, graph_height, false) {
7102
+ stroke @histogram_color.merge(thickness: 2, miter_limit: 10)
7103
+ }
7104
+ end
7105
+ }
7106
+ }
7107
+ }.show
7108
+ end
7109
+ end
7110
+
7111
+ Histogram.new.launch
7112
+ ```
7113
+
7114
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
6647
7115
 
6648
7116
  ```ruby
6649
7117
  # https://github.com/jamescook/libui-ruby/blob/master/example/histogram.rb
@@ -6657,9 +7125,10 @@ Y_OFF_TOP = 20
6657
7125
  X_OFF_RIGHT = 20
6658
7126
  Y_OFF_BOTTOM = 20
6659
7127
  POINT_RADIUS = 5
6660
- COLOR_BLUE = 0x1E90FF
7128
+ COLOR_BLUE = Glimmer::LibUI.interpret_color(0x1E90FF)
6661
7129
 
6662
7130
  @datapoints = 10.times.map {Random.new.rand(90)}
7131
+ @color = COLOR_BLUE
6663
7132
 
6664
7133
  def graph_size(area_width, area_height)
6665
7134
  graph_width = area_width - X_OFF_LEFT - X_OFF_RIGHT
@@ -6715,11 +7184,12 @@ window('histogram example', 640, 480) {
6715
7184
  }
6716
7185
  end
6717
7186
 
6718
- @color_button = color_button {
7187
+ color_button { |cb|
6719
7188
  stretchy false
6720
7189
  color COLOR_BLUE
6721
7190
 
6722
7191
  on_changed do
7192
+ @color = cb.color
6723
7193
  @area.queue_redraw_all
6724
7194
  end
6725
7195
  }
@@ -6727,31 +7197,27 @@ window('histogram example', 640, 480) {
6727
7197
 
6728
7198
  @area = area {
6729
7199
  on_draw do |area_draw_params|
6730
- path {
6731
- rectangle(0, 0, area_draw_params[:area_width], area_draw_params[:area_height])
6732
-
7200
+ rectangle(0, 0, area_draw_params[:area_width], area_draw_params[:area_height]) {
6733
7201
  fill 0xFFFFFF
6734
7202
  }
6735
7203
 
6736
7204
  graph_width, graph_height = *graph_size(area_draw_params[:area_width], area_draw_params[:area_height])
6737
7205
 
6738
- path {
6739
- figure(X_OFF_LEFT, Y_OFF_TOP) {
6740
- line(X_OFF_LEFT, Y_OFF_TOP + graph_height)
6741
- line(X_OFF_LEFT + graph_width, Y_OFF_TOP + graph_height)
6742
- }
7206
+ figure(X_OFF_LEFT, Y_OFF_TOP) {
7207
+ line(X_OFF_LEFT, Y_OFF_TOP + graph_height)
7208
+ line(X_OFF_LEFT + graph_width, Y_OFF_TOP + graph_height)
6743
7209
 
6744
7210
  stroke 0x000000, thickness: 2, miter_limit: 10
6745
7211
  }
6746
7212
 
6747
7213
  # now create the fill for the graph below the graph line
6748
7214
  graph_path(graph_width, graph_height, true) {
6749
- fill @color_button.color.merge(a: 0.5)
7215
+ fill @color.merge(a: 0.5)
6750
7216
  }
6751
7217
 
6752
7218
  # now draw the histogram line
6753
7219
  graph_path(graph_width, graph_height, false) {
6754
- stroke @color_button.color.merge(thickness: 2, miter_limit: 10)
7220
+ stroke @color.merge(thickness: 2, miter_limit: 10)
6755
7221
  }
6756
7222
  end
6757
7223
  }
@@ -7429,7 +7895,181 @@ end
7429
7895
  TinyMidiPlayer.new
7430
7896
  ```
7431
7897
 
7432
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
7898
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
7899
+
7900
+ ```ruby
7901
+ # frozen_string_literal: true
7902
+
7903
+ require 'glimmer-dsl-libui'
7904
+
7905
+ class TinyMidiPlayer
7906
+ include Glimmer
7907
+
7908
+ VERSION = '0.0.1'
7909
+
7910
+ attr_accessor :selected_file
7911
+
7912
+ def initialize
7913
+ @pid = nil
7914
+ @music_directory = File.expand_path('../sounds', __dir__)
7915
+ @midi_files = Dir.glob(File.join(@music_directory, '**/*.mid'))
7916
+ .sort_by { |path| File.basename(path) }
7917
+ at_exit { stop_midi }
7918
+ create_gui
7919
+ end
7920
+
7921
+ def stop_midi
7922
+ if @pid
7923
+ Process.kill(:SIGKILL, @pid) if @th.alive?
7924
+ @pid = nil
7925
+ end
7926
+ end
7927
+
7928
+ def play_midi
7929
+ stop_midi
7930
+ if @pid.nil? && @selected_file
7931
+ begin
7932
+ @pid = spawn "timidity #{@selected_file}"
7933
+ @th = Process.detach @pid
7934
+ rescue Errno::ENOENT
7935
+ warn 'Timidty++ not found. Please install Timidity++.'
7936
+ warn 'https://sourceforge.net/projects/timidity/'
7937
+ end
7938
+ end
7939
+ end
7940
+
7941
+ def show_version
7942
+ msg_box('Tiny Midi Player',
7943
+ "Written in Ruby\n" \
7944
+ "https://github.com/kojix2/libui\n" \
7945
+ "Version #{VERSION}")
7946
+ end
7947
+
7948
+ def create_gui
7949
+ menu('Help') {
7950
+ menu_item('Version') {
7951
+ on_clicked do
7952
+ show_version
7953
+ end
7954
+ }
7955
+ }
7956
+ window('Tiny Midi Player', 200, 50) {
7957
+ horizontal_box {
7958
+ vertical_box {
7959
+ stretchy false
7960
+
7961
+ button('▶') {
7962
+ on_clicked do
7963
+ play_midi
7964
+ end
7965
+ }
7966
+ button('■') {
7967
+ on_clicked do
7968
+ stop_midi
7969
+ end
7970
+ }
7971
+ }
7972
+
7973
+ combobox {
7974
+ items @midi_files.map { |path| File.basename(path) }
7975
+ # data-bind selected item (String) to self.selected_file with on-read/on-write converters and after_write operation
7976
+ 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? }]
7977
+ }
7978
+ }
7979
+ }.show
7980
+ end
7981
+ end
7982
+
7983
+ TinyMidiPlayer.new
7984
+ ```
7985
+
7986
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (with [data-binding](#data-binding)):
7987
+
7988
+ ```ruby
7989
+ require 'glimmer-dsl-libui'
7990
+
7991
+ class TinyMidiPlayer
7992
+ include Glimmer
7993
+
7994
+ VERSION = '0.0.1'
7995
+
7996
+ attr_accessor :selected_file
7997
+
7998
+ def initialize
7999
+ @pid = nil
8000
+ @music_directory = File.expand_path('../sounds', __dir__)
8001
+ @midi_files = Dir.glob(File.join(@music_directory, '**/*.mid'))
8002
+ .sort_by { |path| File.basename(path) }
8003
+ at_exit { stop_midi }
8004
+ create_gui
8005
+ end
8006
+
8007
+ def stop_midi
8008
+ if @pid
8009
+ Process.kill(:SIGKILL, @pid) if @th.alive?
8010
+ @pid = nil
8011
+ end
8012
+ end
8013
+
8014
+ def play_midi
8015
+ stop_midi
8016
+ if @pid.nil? && @selected_file
8017
+ begin
8018
+ @pid = spawn "timidity #{@selected_file}"
8019
+ @th = Process.detach @pid
8020
+ rescue Errno::ENOENT
8021
+ warn 'Timidty++ not found. Please install Timidity++.'
8022
+ warn 'https://sourceforge.net/projects/timidity/'
8023
+ end
8024
+ end
8025
+ end
8026
+
8027
+ def show_version
8028
+ msg_box('Tiny Midi Player',
8029
+ "Written in Ruby\n" \
8030
+ "https://github.com/kojix2/libui\n" \
8031
+ "Version #{VERSION}")
8032
+ end
8033
+
8034
+ def create_gui
8035
+ menu('Help') {
8036
+ menu_item('Version') {
8037
+ on_clicked do
8038
+ show_version
8039
+ end
8040
+ }
8041
+ }
8042
+ window('Tiny Midi Player', 200, 50) {
8043
+ horizontal_box {
8044
+ vertical_box {
8045
+ stretchy false
8046
+
8047
+ button('▶') {
8048
+ on_clicked do
8049
+ play_midi
8050
+ end
8051
+ }
8052
+ button('■') {
8053
+ on_clicked do
8054
+ stop_midi
8055
+ end
8056
+ }
8057
+ }
8058
+
8059
+ combobox {
8060
+ items @midi_files.map { |path| File.basename(path) }
8061
+ # data-bind selected index (Integer) to self.selected_file with on-read/on-write converters and after_write operation
8062
+ selected <=> [self, :selected_file, on_read: ->(f) {@midi_files.index(f)}, on_write: ->(i) {@midi_files[i]}, after_write: -> { play_midi if @th&.alive? }]
8063
+ }
8064
+ }
8065
+ }.show
8066
+ end
8067
+ end
8068
+
8069
+ TinyMidiPlayer.new
8070
+ ```
8071
+
8072
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (without [data-binding](#data-binding)):
7433
8073
 
7434
8074
  ```ruby
7435
8075
  require 'glimmer-dsl-libui'
@@ -7757,22 +8397,23 @@ class Tetris
7757
8397
  menu('Game') {
7758
8398
  @pause_menu_item = check_menu_item('Pause') {
7759
8399
  enabled false
7760
-
7761
- on_clicked do
7762
- @game.paused = @pause_menu_item.checked?
7763
- end
8400
+ checked <=> [@game, :paused]
7764
8401
  }
8402
+
7765
8403
  menu_item('Restart') {
7766
8404
  on_clicked do
7767
8405
  @game.restart!
7768
8406
  end
7769
8407
  }
8408
+
7770
8409
  separator_menu_item
8410
+
7771
8411
  menu_item('Exit') {
7772
8412
  on_clicked do
7773
8413
  exit(0)
7774
8414
  end
7775
8415
  }
8416
+
7776
8417
  quit_menu_item if OS.mac?
7777
8418
  }
7778
8419
 
@@ -7782,6 +8423,7 @@ class Tetris
7782
8423
  show_high_scores
7783
8424
  end
7784
8425
  }
8426
+
7785
8427
  menu_item('Clear High Scores') {
7786
8428
  on_clicked {
7787
8429
  @game.clear_high_scores!
@@ -7790,22 +8432,16 @@ class Tetris
7790
8432
  }
7791
8433
 
7792
8434
  menu('Options') {
7793
- radio_menu_item('Instant Down on Up Arrow') {
7794
- on_clicked do
7795
- @game.instant_down_on_up = true
7796
- end
8435
+ radio_menu_item('Instant Down on Up Arrow') { |r|
8436
+ checked <=> [@game, :instant_down_on_up]
7797
8437
  }
7798
- radio_menu_item('Rotate Right on Up Arrow') {
7799
- on_clicked do
7800
- @game.rotate_right_on_up = true
7801
- end
8438
+
8439
+ radio_menu_item('Rotate Right on Up Arrow') { |r|
8440
+ checked <=> [@game, :rotate_right_on_up]
7802
8441
  }
7803
- radio_menu_item('Rotate Left on Up Arrow') {
7804
- checked true
7805
-
7806
- on_clicked do
7807
- @game.rotate_left_on_up = true
7808
- end
8442
+
8443
+ radio_menu_item('Rotate Left on Up Arrow') { |r|
8444
+ checked <=> [@game, :rotate_left_on_up]
7809
8445
  }
7810
8446
  }
7811
8447
 
@@ -7817,6 +8453,7 @@ class Tetris
7817
8453
  end
7818
8454
  }
7819
8455
  end
8456
+
7820
8457
  menu_item('About') {
7821
8458
  on_clicked do
7822
8459
  show_about_dialog
@@ -8140,7 +8777,140 @@ Mac | Windows | Linux
8140
8777
  ----|---------|------
8141
8778
  ![glimmer-dsl-libui-mac-timer.png](images/glimmer-dsl-libui-mac-timer.png) ![glimmer-dsl-libui-mac-timer-in-progress.png](images/glimmer-dsl-libui-mac-timer-in-progress.png) | ![glimmer-dsl-libui-windows-timer.png](images/glimmer-dsl-libui-windows-timer.png) ![glimmer-dsl-libui-windows-timer-in-progress.png](images/glimmer-dsl-libui-windows-timer-in-progress.png) | ![glimmer-dsl-libui-linux-timer.png](images/glimmer-dsl-libui-linux-timer.png) ![glimmer-dsl-libui-linux-timer-in-progress.png](images/glimmer-dsl-libui-linux-timer-in-progress.png)
8142
8779
 
8143
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
8780
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
8781
+
8782
+ ```ruby
8783
+ require 'glimmer-dsl-libui'
8784
+
8785
+ class Timer
8786
+ include Glimmer
8787
+
8788
+ SECOND_MAX = 59
8789
+ MINUTE_MAX = 59
8790
+ HOUR_MAX = 23
8791
+
8792
+ attr_accessor :hour, :min, :sec, :started, :played
8793
+
8794
+ def initialize
8795
+ @pid = nil
8796
+ @alarm_file = File.expand_path('../sounds/AlanWalker-Faded.mid', __dir__)
8797
+ @hour = @min = @sec = 0
8798
+ at_exit { stop_alarm }
8799
+ setup_timer
8800
+ create_gui
8801
+ end
8802
+
8803
+ def stop_alarm
8804
+ if @pid
8805
+ Process.kill(:SIGKILL, @pid) if @th.alive?
8806
+ @pid = nil
8807
+ end
8808
+ end
8809
+
8810
+ def play_alarm
8811
+ stop_alarm
8812
+ if @pid.nil?
8813
+ begin
8814
+ @pid = spawn "timidity -G 0.0-10.0 #{@alarm_file}"
8815
+ @th = Process.detach @pid
8816
+ rescue Errno::ENOENT
8817
+ warn 'Timidty++ not found. Please install Timidity++.'
8818
+ warn 'https://sourceforge.net/projects/timidity/'
8819
+ end
8820
+ end
8821
+ end
8822
+
8823
+ def setup_timer
8824
+ unless @setup_timer
8825
+ Glimmer::LibUI.timer(1) do
8826
+ if @started
8827
+ seconds = @sec
8828
+ minutes = @min
8829
+ hours = @hour
8830
+ if seconds > 0
8831
+ self.sec = seconds -= 1
8832
+ end
8833
+ if seconds == 0
8834
+ if minutes > 0
8835
+ self.min = minutes -= 1
8836
+ self.sec = seconds = SECOND_MAX
8837
+ end
8838
+ if minutes == 0
8839
+ if hours > 0
8840
+ self.hour = hours -= 1
8841
+ self.min = minutes = MINUTE_MAX
8842
+ self.sec = seconds = SECOND_MAX
8843
+ end
8844
+ if hours == 0 && minutes == 0 && seconds == 0
8845
+ self.started = false
8846
+ unless @played
8847
+ play_alarm
8848
+ msg_box('Alarm', 'Countdown Is Finished!')
8849
+ self.played = true
8850
+ end
8851
+ end
8852
+ end
8853
+ end
8854
+ end
8855
+ end
8856
+ @setup_timer = true
8857
+ end
8858
+ end
8859
+
8860
+ def create_gui
8861
+ window('Timer') {
8862
+ margined true
8863
+
8864
+ group('Countdown') {
8865
+ vertical_box {
8866
+ horizontal_box {
8867
+ spinbox(0, HOUR_MAX) {
8868
+ stretchy false
8869
+ value <=> [self, :hour]
8870
+ }
8871
+ label(':') {
8872
+ stretchy false
8873
+ }
8874
+ spinbox(0, MINUTE_MAX) {
8875
+ stretchy false
8876
+ value <=> [self, :min]
8877
+ }
8878
+ label(':') {
8879
+ stretchy false
8880
+ }
8881
+ spinbox(0, SECOND_MAX) {
8882
+ stretchy false
8883
+ value <=> [self, :sec]
8884
+ }
8885
+ }
8886
+ horizontal_box {
8887
+ button('Start') {
8888
+ enabled <= [self, :started, on_read: :!]
8889
+
8890
+ on_clicked do
8891
+ self.started = true
8892
+ self.played = false
8893
+ end
8894
+ }
8895
+
8896
+ button('Stop') {
8897
+ enabled <= [self, :started]
8898
+
8899
+ on_clicked do
8900
+ self.started = false
8901
+ end
8902
+ }
8903
+ }
8904
+ }
8905
+ }
8906
+ }.show
8907
+ end
8908
+ end
8909
+
8910
+ Timer.new
8911
+ ```
8912
+
8913
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
8144
8914
 
8145
8915
  ```ruby
8146
8916
  require 'glimmer-dsl-libui'
@@ -8313,7 +9083,7 @@ https://github.com/iraamaro/i3off-gtk-ruby
8313
9083
 
8314
9084
  ### Issues
8315
9085
 
8316
- 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).
9086
+ 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.
8317
9087
 
8318
9088
  ### Chat
8319
9089
 
@@ -8345,6 +9115,8 @@ These features have been planned or suggested. You might see them in a future ve
8345
9115
  is fine, but please isolate to its own commit so I can cherry-pick
8346
9116
  around it.
8347
9117
 
9118
+ Note that the latest development sometimes takes place in [development](https://github.com/AndyObtiva/glimmer-dsl-libui/tree/development) branch (which is deleted once merged back to [master](https://github.com/AndyObtiva/glimmer-dsl-libui)).
9119
+
8348
9120
  ## Contributors
8349
9121
 
8350
9122
  * [Andy Maleh](https://github.com/AndyObtiva) (Founder)