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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -1
- data/README.md +932 -160
- data/VERSION +1 -1
- data/examples/button_counter.rb +2 -1
- data/examples/color_button.rb +18 -13
- data/examples/color_button2.rb +14 -0
- data/examples/date_time_picker.rb +19 -14
- data/examples/date_time_picker2.rb +20 -0
- data/examples/dynamic_area.rb +77 -90
- data/examples/dynamic_area2.rb +14 -12
- data/examples/dynamic_area3.rb +90 -0
- data/examples/dynamic_area4.rb +95 -0
- data/examples/font_button.rb +17 -12
- data/examples/font_button2.rb +18 -0
- data/examples/form_table.rb +0 -2
- data/examples/form_table2.rb +0 -2
- data/examples/histogram.rb +93 -91
- data/examples/histogram2.rb +109 -0
- data/examples/meta_example.rb +17 -6
- data/examples/midi_player.rb +5 -6
- data/examples/midi_player2.rb +83 -0
- data/examples/midi_player3.rb +84 -0
- data/examples/tetris.rb +15 -18
- data/examples/timer.rb +28 -31
- data/examples/timer2.rb +129 -0
- data/glimmer-dsl-libui.gemspec +0 -0
- data/lib/glimmer/libui/control_proxy/checkbox_proxy.rb +5 -0
- data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +5 -0
- data/lib/glimmer/libui/control_proxy/combobox_proxy.rb +18 -2
- data/lib/glimmer/libui/control_proxy/date_time_picker_proxy.rb +5 -0
- data/lib/glimmer/libui/control_proxy/editable_combobox_proxy.rb +5 -0
- data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +4 -0
- data/lib/glimmer/libui/control_proxy/menu_item_proxy/check_menu_item_proxy.rb +5 -0
- data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +17 -4
- data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +20 -0
- data/lib/glimmer/libui/control_proxy/slider_proxy.rb +38 -0
- data/lib/glimmer/libui/control_proxy/spinbox_proxy.rb +38 -0
- data/lib/glimmer/libui/control_proxy.rb +2 -2
- 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.
|
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.
|
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
|
-
|
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
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
@
|
641
|
-
|
642
|
-
|
643
|
-
@
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
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
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
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
|
-
|
692
|
-
|
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
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
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
|
-
|
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
|
-
- `
|
1367
|
-
- `
|
1368
|
-
- `
|
1369
|
-
- `
|
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
|
-
|
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 =
|
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__), '
|
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(
|
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
|
-
|
2001
|
+
tab {
|
1949
2002
|
stretchy false
|
1950
|
-
items examples_with_versions
|
1951
|
-
selected @selected_example_index
|
1952
2003
|
|
1953
|
-
|
1954
|
-
|
1955
|
-
|
1956
|
-
|
1957
|
-
|
1958
|
-
|
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
|
-
|
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
|
-
|
1989
|
-
|
1990
|
-
|
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
|
-
|
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
|
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
|
-
|
2550
|
-
|
2551
|
-
|
2552
|
-
|
2553
|
-
|
2554
|
-
|
2555
|
-
|
2556
|
-
|
2557
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
6739
|
-
|
6740
|
-
|
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 @
|
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 @
|
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
|
-
|
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
|
-
|
7799
|
-
|
7800
|
-
|
7801
|
-
end
|
8438
|
+
|
8439
|
+
radio_menu_item('Rotate Right on Up Arrow') { |r|
|
8440
|
+
checked <=> [@game, :rotate_right_on_up]
|
7802
8441
|
}
|
7803
|
-
|
7804
|
-
|
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)
|