glimmer-dsl-libui 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -1
- data/README.md +625 -119
- data/VERSION +1 -1
- 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/form_table.rb +0 -2
- data/examples/form_table2.rb +0 -2
- data/examples/histogram.rb +98 -91
- data/examples/histogram2.rb +109 -0
- 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/spinbox_proxy.rb +38 -0
- metadata +7 -2
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.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.5
|
|
2
2
|
## Prerequisite-Free Ruby Desktop Development GUI Library
|
|
3
3
|
[](http://badge.fury.io/rb/glimmer-dsl-libui)
|
|
4
4
|
[](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
@@ -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.5'
|
|
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.
|
|
@@ -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
|

|
|
@@ -1367,6 +1386,7 @@ Data-binding supports utilizing the [MVP (Model View Presenter)](https://en.wiki
|
|
|
1367
1386
|
- `multiline_entry` `text` property
|
|
1368
1387
|
- `non_wrapping_multiline_entry` `text` property
|
|
1369
1388
|
- `search_entry` `text` property
|
|
1389
|
+
- `spinbox` `value` property
|
|
1370
1390
|
|
|
1371
1391
|
Example of bidirectional data-binding:
|
|
1372
1392
|
|
|
@@ -1435,7 +1455,9 @@ entry {
|
|
|
1435
1455
|
}
|
|
1436
1456
|
```
|
|
1437
1457
|
|
|
1438
|
-
|
|
1458
|
+
Data-binding gotchas:
|
|
1459
|
+
- 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)
|
|
1460
|
+
- 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
1461
|
|
|
1440
1462
|
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
1463
|
|
|
@@ -1880,17 +1902,23 @@ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version
|
|
|
1880
1902
|
```ruby
|
|
1881
1903
|
require 'glimmer-dsl-libui'
|
|
1882
1904
|
require 'facets'
|
|
1905
|
+
require 'fileutils'
|
|
1883
1906
|
|
|
1884
1907
|
class MetaExample
|
|
1885
1908
|
include Glimmer
|
|
1886
1909
|
|
|
1910
|
+
ADDITIONAL_BASIC_EXAMPLES = ['Color Button', 'Font Button', 'Form', 'Date Time Picker', 'Simple Notepad']
|
|
1911
|
+
|
|
1912
|
+
attr_accessor :code_text
|
|
1913
|
+
|
|
1887
1914
|
def initialize
|
|
1888
|
-
@selected_example_index =
|
|
1915
|
+
@selected_example_index = examples_with_versions.index(basic_examples_with_versions.first)
|
|
1916
|
+
@code_text = File.read(file_path_for(selected_example))
|
|
1889
1917
|
end
|
|
1890
1918
|
|
|
1891
1919
|
def examples
|
|
1892
1920
|
if @examples.nil?
|
|
1893
|
-
example_files = Dir.glob(File.join(File.expand_path('.', __dir__), '
|
|
1921
|
+
example_files = Dir.glob(File.join(File.expand_path('.', __dir__), '*.rb'))
|
|
1894
1922
|
example_file_names = example_files.map { |f| File.basename(f, '.rb') }
|
|
1895
1923
|
example_file_names = example_file_names.reject { |f| f == 'meta_example' || f.match(/\d$/) }
|
|
1896
1924
|
@examples = example_file_names.map { |f| f.underscore.titlecase }
|
|
@@ -1904,12 +1932,20 @@ class MetaExample
|
|
|
1904
1932
|
end
|
|
1905
1933
|
end
|
|
1906
1934
|
|
|
1935
|
+
def basic_examples_with_versions
|
|
1936
|
+
examples_with_versions.select {|example| example.start_with?('Basic') || ADDITIONAL_BASIC_EXAMPLES.include?(example) }
|
|
1937
|
+
end
|
|
1938
|
+
|
|
1939
|
+
def advanced_examples_with_versions
|
|
1940
|
+
examples_with_versions - basic_examples_with_versions
|
|
1941
|
+
end
|
|
1942
|
+
|
|
1907
1943
|
def file_path_for(example)
|
|
1908
1944
|
File.join(File.expand_path('.', __dir__), "#{example.underscore}.rb")
|
|
1909
1945
|
end
|
|
1910
1946
|
|
|
1911
1947
|
def version_count_for(example)
|
|
1912
|
-
Dir.glob(File.join(File.expand_path('.', __dir__), "#{example.underscore}*.rb")).select {|file| file.match(
|
|
1948
|
+
Dir.glob(File.join(File.expand_path('.', __dir__), "#{example.underscore}*.rb")).select {|file| file.match(/#{example.underscore}\d\.rb$/)}.count + 1
|
|
1913
1949
|
end
|
|
1914
1950
|
|
|
1915
1951
|
def glimmer_dsl_libui_file
|
|
@@ -1945,17 +1981,47 @@ class MetaExample
|
|
|
1945
1981
|
vertical_box {
|
|
1946
1982
|
stretchy false
|
|
1947
1983
|
|
|
1948
|
-
|
|
1984
|
+
tab {
|
|
1949
1985
|
stretchy false
|
|
1950
|
-
items examples_with_versions
|
|
1951
|
-
selected @selected_example_index
|
|
1952
1986
|
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1987
|
+
tab_item('Basic') {
|
|
1988
|
+
vertical_box {
|
|
1989
|
+
@basic_example_radio_buttons = radio_buttons {
|
|
1990
|
+
stretchy false
|
|
1991
|
+
items basic_examples_with_versions
|
|
1992
|
+
selected basic_examples_with_versions.index(examples_with_versions[@selected_example_index])
|
|
1993
|
+
|
|
1994
|
+
on_selected do
|
|
1995
|
+
@selected_example_index = examples_with_versions.index(basic_examples_with_versions[@basic_example_radio_buttons.selected])
|
|
1996
|
+
example = selected_example
|
|
1997
|
+
self.code_text = File.read(file_path_for(example))
|
|
1998
|
+
@version_spinbox.value = 1
|
|
1999
|
+
end
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
label # filler
|
|
2003
|
+
label # filler
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
tab_item('Advanced') {
|
|
2008
|
+
vertical_box {
|
|
2009
|
+
@advanced_example_radio_buttons = radio_buttons {
|
|
2010
|
+
stretchy false
|
|
2011
|
+
items advanced_examples_with_versions
|
|
2012
|
+
|
|
2013
|
+
on_selected do
|
|
2014
|
+
@selected_example_index = examples_with_versions.index(advanced_examples_with_versions[@advanced_example_radio_buttons.selected])
|
|
2015
|
+
example = selected_example
|
|
2016
|
+
self.code_text = File.read(file_path_for(example))
|
|
2017
|
+
@version_spinbox.value = 1
|
|
2018
|
+
end
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
label # filler
|
|
2022
|
+
label # filler
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
1959
2025
|
}
|
|
1960
2026
|
|
|
1961
2027
|
horizontal_box {
|
|
@@ -1973,7 +2039,7 @@ class MetaExample
|
|
|
1973
2039
|
else
|
|
1974
2040
|
version_number = @version_spinbox.value == 1 ? '' : @version_spinbox.value
|
|
1975
2041
|
example = "#{selected_example}#{version_number}"
|
|
1976
|
-
|
|
2042
|
+
self.code_text = File.read(file_path_for(example))
|
|
1977
2043
|
end
|
|
1978
2044
|
end
|
|
1979
2045
|
}
|
|
@@ -1985,9 +2051,15 @@ class MetaExample
|
|
|
1985
2051
|
button('Launch') {
|
|
1986
2052
|
on_clicked do
|
|
1987
2053
|
begin
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
2054
|
+
parent_dir = File.join(Dir.home, '.glimmer-dsl-libui', 'examples')
|
|
2055
|
+
FileUtils.mkdir_p(parent_dir)
|
|
2056
|
+
example_file = File.join(parent_dir, "#{selected_example.underscore}.rb")
|
|
2057
|
+
File.write(example_file, code_text)
|
|
2058
|
+
example_supporting_directory = File.expand_path(selected_example.underscore, __dir__)
|
|
2059
|
+
FileUtils.cp_r(example_supporting_directory, parent_dir) if Dir.exist?(example_supporting_directory)
|
|
2060
|
+
FileUtils.cp_r(File.expand_path('../icons', __dir__), File.dirname(parent_dir))
|
|
2061
|
+
FileUtils.cp_r(File.expand_path('../sounds', __dir__), File.dirname(parent_dir))
|
|
2062
|
+
run_example(example_file)
|
|
1991
2063
|
rescue => e
|
|
1992
2064
|
puts e.full_message
|
|
1993
2065
|
puts 'Unable to write code changes! Running original example...'
|
|
@@ -1997,14 +2069,14 @@ class MetaExample
|
|
|
1997
2069
|
}
|
|
1998
2070
|
button('Reset') {
|
|
1999
2071
|
on_clicked do
|
|
2000
|
-
|
|
2072
|
+
self.code_text = File.read(file_path_for(selected_example))
|
|
2001
2073
|
end
|
|
2002
2074
|
}
|
|
2003
2075
|
}
|
|
2004
2076
|
}
|
|
2005
2077
|
|
|
2006
2078
|
@code_entry = non_wrapping_multiline_entry {
|
|
2007
|
-
text
|
|
2079
|
+
text <=> [self, :code_text]
|
|
2008
2080
|
}
|
|
2009
2081
|
}
|
|
2010
2082
|
}.show
|
|
@@ -5751,7 +5823,96 @@ Mac | Windows | Linux
|
|
|
5751
5823
|
----|---------|------
|
|
5752
5824
|
  |   |  
|
|
5753
5825
|
|
|
5754
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
|
5826
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
|
5827
|
+
|
|
5828
|
+
```ruby
|
|
5829
|
+
require 'glimmer-dsl-libui'
|
|
5830
|
+
|
|
5831
|
+
class DynamicArea
|
|
5832
|
+
include Glimmer
|
|
5833
|
+
|
|
5834
|
+
attr_accessor :rectangle_x, :rectangle_y, :rectangle_width, :rectangle_height, :rectangle_red, :rectangle_green, :rectangle_blue, :rectangle_alpha
|
|
5835
|
+
|
|
5836
|
+
def initialize
|
|
5837
|
+
@rectangle_x = 25
|
|
5838
|
+
@rectangle_y = 25
|
|
5839
|
+
@rectangle_width = 150
|
|
5840
|
+
@rectangle_height = 150
|
|
5841
|
+
@rectangle_red = 102
|
|
5842
|
+
@rectangle_green = 102
|
|
5843
|
+
@rectangle_blue = 204
|
|
5844
|
+
@rectangle_alpha = 100
|
|
5845
|
+
end
|
|
5846
|
+
|
|
5847
|
+
def launch
|
|
5848
|
+
window('Dynamic Area', 240, 600) {
|
|
5849
|
+
margined true
|
|
5850
|
+
|
|
5851
|
+
vertical_box {
|
|
5852
|
+
label('Rectangle Properties') {
|
|
5853
|
+
stretchy false
|
|
5854
|
+
}
|
|
5855
|
+
|
|
5856
|
+
form {
|
|
5857
|
+
stretchy false
|
|
5858
|
+
|
|
5859
|
+
spinbox(0, 1000) {
|
|
5860
|
+
label 'x'
|
|
5861
|
+
value <=> [self, :rectangle_x, after_write: -> {@area.queue_redraw_all}]
|
|
5862
|
+
}
|
|
5863
|
+
|
|
5864
|
+
spinbox(0, 1000) {
|
|
5865
|
+
label 'y'
|
|
5866
|
+
value <=> [self, :rectangle_y, after_write: -> {@area.queue_redraw_all}]
|
|
5867
|
+
}
|
|
5868
|
+
|
|
5869
|
+
spinbox(0, 1000) {
|
|
5870
|
+
label 'width'
|
|
5871
|
+
value <=> [self, :rectangle_width, after_write: -> {@area.queue_redraw_all}]
|
|
5872
|
+
}
|
|
5873
|
+
|
|
5874
|
+
spinbox(0, 1000) {
|
|
5875
|
+
label 'height'
|
|
5876
|
+
value <=> [self, :rectangle_height, after_write: -> {@area.queue_redraw_all}]
|
|
5877
|
+
}
|
|
5878
|
+
|
|
5879
|
+
spinbox(0, 255) {
|
|
5880
|
+
label 'red'
|
|
5881
|
+
value <=> [self, :rectangle_red, after_write: -> {@area.queue_redraw_all}]
|
|
5882
|
+
}
|
|
5883
|
+
|
|
5884
|
+
spinbox(0, 255) {
|
|
5885
|
+
label 'green'
|
|
5886
|
+
value <=> [self, :rectangle_green, after_write: -> {@area.queue_redraw_all}]
|
|
5887
|
+
}
|
|
5888
|
+
|
|
5889
|
+
spinbox(0, 255) {
|
|
5890
|
+
label 'blue'
|
|
5891
|
+
value <=> [self, :rectangle_blue, after_write: -> {@area.queue_redraw_all}]
|
|
5892
|
+
}
|
|
5893
|
+
|
|
5894
|
+
spinbox(0, 100) {
|
|
5895
|
+
label 'alpha'
|
|
5896
|
+
value <=> [self, :rectangle_alpha, after_write: -> {@area.queue_redraw_all}]
|
|
5897
|
+
}
|
|
5898
|
+
}
|
|
5899
|
+
|
|
5900
|
+
@area = area {
|
|
5901
|
+
on_draw do |area_draw_params|
|
|
5902
|
+
rectangle(rectangle_x, rectangle_y, rectangle_width, rectangle_height) { # a dynamic path is added semi-declaratively inside on_draw block
|
|
5903
|
+
fill r: rectangle_red, g: rectangle_green, b: rectangle_blue, a: rectangle_alpha / 100.0
|
|
5904
|
+
}
|
|
5905
|
+
end
|
|
5906
|
+
}
|
|
5907
|
+
}
|
|
5908
|
+
}.show
|
|
5909
|
+
end
|
|
5910
|
+
end
|
|
5911
|
+
|
|
5912
|
+
DynamicArea.new.launch
|
|
5913
|
+
```
|
|
5914
|
+
|
|
5915
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
|
5755
5916
|
|
|
5756
5917
|
```ruby
|
|
5757
5918
|
require 'glimmer-dsl-libui'
|
|
@@ -5853,7 +6014,102 @@ window('Dynamic Area', 240, 600) {
|
|
|
5853
6014
|
}.show
|
|
5854
6015
|
```
|
|
5855
6016
|
|
|
5856
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version
|
|
6017
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (declarative stable `path` approach with [data-binding](#data-binding)):
|
|
6018
|
+
|
|
6019
|
+
```ruby
|
|
6020
|
+
require 'glimmer-dsl-libui'
|
|
6021
|
+
|
|
6022
|
+
class DynamicArea
|
|
6023
|
+
include Glimmer
|
|
6024
|
+
|
|
6025
|
+
attr_accessor :rectangle_x, :rectangle_y, :rectangle_width, :rectangle_height, :rectangle_red, :rectangle_green, :rectangle_blue, :rectangle_alpha
|
|
6026
|
+
|
|
6027
|
+
def initialize
|
|
6028
|
+
@rectangle_x = 25
|
|
6029
|
+
@rectangle_y = 25
|
|
6030
|
+
@rectangle_width = 150
|
|
6031
|
+
@rectangle_height = 150
|
|
6032
|
+
@rectangle_red = 102
|
|
6033
|
+
@rectangle_green = 102
|
|
6034
|
+
@rectangle_blue = 204
|
|
6035
|
+
@rectangle_alpha = 100
|
|
6036
|
+
end
|
|
6037
|
+
|
|
6038
|
+
def rectangle_fill
|
|
6039
|
+
{ r: rectangle_red, g: rectangle_green, b: rectangle_blue, a: rectangle_alpha / 100.0 }
|
|
6040
|
+
end
|
|
6041
|
+
|
|
6042
|
+
def launch
|
|
6043
|
+
window('Dynamic Area', 240, 600) {
|
|
6044
|
+
margined true
|
|
6045
|
+
|
|
6046
|
+
vertical_box {
|
|
6047
|
+
label('Rectangle Properties') {
|
|
6048
|
+
stretchy false
|
|
6049
|
+
}
|
|
6050
|
+
|
|
6051
|
+
form {
|
|
6052
|
+
stretchy false
|
|
6053
|
+
|
|
6054
|
+
@x_spinbox = spinbox(0, 1000) {
|
|
6055
|
+
label 'x'
|
|
6056
|
+
value <=> [self, :rectangle_x]
|
|
6057
|
+
}
|
|
6058
|
+
|
|
6059
|
+
@y_spinbox = spinbox(0, 1000) {
|
|
6060
|
+
label 'y'
|
|
6061
|
+
value <=> [self, :rectangle_y]
|
|
6062
|
+
}
|
|
6063
|
+
|
|
6064
|
+
@width_spinbox = spinbox(0, 1000) {
|
|
6065
|
+
label 'width'
|
|
6066
|
+
value <=> [self, :rectangle_width]
|
|
6067
|
+
}
|
|
6068
|
+
|
|
6069
|
+
@height_spinbox = spinbox(0, 1000) {
|
|
6070
|
+
label 'height'
|
|
6071
|
+
value <=> [self, :rectangle_height]
|
|
6072
|
+
}
|
|
6073
|
+
|
|
6074
|
+
@red_spinbox = spinbox(0, 255) {
|
|
6075
|
+
label 'red'
|
|
6076
|
+
value <=> [self, :rectangle_red]
|
|
6077
|
+
}
|
|
6078
|
+
|
|
6079
|
+
@green_spinbox = spinbox(0, 255) {
|
|
6080
|
+
label 'green'
|
|
6081
|
+
value <=> [self, :rectangle_green]
|
|
6082
|
+
}
|
|
6083
|
+
|
|
6084
|
+
@blue_spinbox = spinbox(0, 255) {
|
|
6085
|
+
label 'blue'
|
|
6086
|
+
value <=> [self, :rectangle_blue]
|
|
6087
|
+
}
|
|
6088
|
+
|
|
6089
|
+
@alpha_spinbox = spinbox(0, 100) {
|
|
6090
|
+
label 'alpha'
|
|
6091
|
+
value <=> [self, :rectangle_alpha]
|
|
6092
|
+
}
|
|
6093
|
+
}
|
|
6094
|
+
|
|
6095
|
+
area {
|
|
6096
|
+
@rectangle = rectangle { # stable implicit path shape
|
|
6097
|
+
x <= [self, :rectangle_x]
|
|
6098
|
+
y <= [self, :rectangle_y]
|
|
6099
|
+
width <= [self, :rectangle_width]
|
|
6100
|
+
height <= [self, :rectangle_height]
|
|
6101
|
+
fill <= [self, :rectangle_fill, computed_by: [:rectangle_red, :rectangle_green, :rectangle_blue, :rectangle_alpha]]
|
|
6102
|
+
}
|
|
6103
|
+
}
|
|
6104
|
+
}
|
|
6105
|
+
}.show
|
|
6106
|
+
end
|
|
6107
|
+
end
|
|
6108
|
+
|
|
6109
|
+
DynamicArea.new.launch
|
|
6110
|
+
```
|
|
6111
|
+
|
|
6112
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 4 (declarative stable `path` approach without [data-binding](#data-binding)):
|
|
5857
6113
|
|
|
5858
6114
|
```ruby
|
|
5859
6115
|
require 'glimmer-dsl-libui'
|
|
@@ -5945,7 +6201,7 @@ window('Dynamic Area', 240, 600) {
|
|
|
5945
6201
|
}
|
|
5946
6202
|
|
|
5947
6203
|
area {
|
|
5948
|
-
@rectangle = rectangle(@x_spinbox.value, @y_spinbox.value, @width_spinbox.value, @height_spinbox.value) { # stable path
|
|
6204
|
+
@rectangle = rectangle(@x_spinbox.value, @y_spinbox.value, @width_spinbox.value, @height_spinbox.value) { # stable implicit path shape
|
|
5949
6205
|
fill r: @red_spinbox.value, g: @green_spinbox.value, b: @blue_spinbox.value, a: @alpha_spinbox.value / 100.0
|
|
5950
6206
|
}
|
|
5951
6207
|
}
|
|
@@ -6643,7 +6899,126 @@ UI.main
|
|
|
6643
6899
|
UI.quit
|
|
6644
6900
|
```
|
|
6645
6901
|
|
|
6646
|
-
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
|
6902
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
|
6903
|
+
|
|
6904
|
+
```ruby
|
|
6905
|
+
# https://github.com/jamescook/libui-ruby/blob/master/example/histogram.rb
|
|
6906
|
+
|
|
6907
|
+
require 'glimmer-dsl-libui'
|
|
6908
|
+
|
|
6909
|
+
class Histogram
|
|
6910
|
+
include Glimmer
|
|
6911
|
+
|
|
6912
|
+
X_OFF_LEFT = 20
|
|
6913
|
+
Y_OFF_TOP = 20
|
|
6914
|
+
X_OFF_RIGHT = 20
|
|
6915
|
+
Y_OFF_BOTTOM = 20
|
|
6916
|
+
POINT_RADIUS = 5
|
|
6917
|
+
COLOR_BLUE = Glimmer::LibUI.interpret_color(0x1E90FF)
|
|
6918
|
+
|
|
6919
|
+
attr_accessor :datapoints, :histogram_color
|
|
6920
|
+
|
|
6921
|
+
def initialize
|
|
6922
|
+
@datapoints = 10.times.map {Random.new.rand(90)}
|
|
6923
|
+
@histogram_color = COLOR_BLUE
|
|
6924
|
+
end
|
|
6925
|
+
|
|
6926
|
+
def graph_size(area_width, area_height)
|
|
6927
|
+
graph_width = area_width - X_OFF_LEFT - X_OFF_RIGHT
|
|
6928
|
+
graph_height = area_height - Y_OFF_TOP - Y_OFF_BOTTOM
|
|
6929
|
+
[graph_width, graph_height]
|
|
6930
|
+
end
|
|
6931
|
+
|
|
6932
|
+
def point_locations(width, height)
|
|
6933
|
+
xincr = width / 9.0 # 10 - 1 to make the last point be at the end
|
|
6934
|
+
yincr = height / 100.0
|
|
6935
|
+
|
|
6936
|
+
@datapoints.each_with_index.map do |value, i|
|
|
6937
|
+
val = 100 - value
|
|
6938
|
+
[xincr * i, yincr * val]
|
|
6939
|
+
end
|
|
6940
|
+
end
|
|
6941
|
+
|
|
6942
|
+
# method-based custom control representing a graph path
|
|
6943
|
+
def graph_path(width, height, should_extend, &block)
|
|
6944
|
+
locations = point_locations(width, height).flatten
|
|
6945
|
+
path {
|
|
6946
|
+
if should_extend
|
|
6947
|
+
polygon(locations + [width, height, 0, height])
|
|
6948
|
+
else
|
|
6949
|
+
polyline(locations)
|
|
6950
|
+
end
|
|
6951
|
+
|
|
6952
|
+
# apply a transform to the coordinate space for this path so (0, 0) is the top-left corner of the graph
|
|
6953
|
+
transform {
|
|
6954
|
+
translate X_OFF_LEFT, Y_OFF_TOP
|
|
6955
|
+
}
|
|
6956
|
+
|
|
6957
|
+
block.call
|
|
6958
|
+
}
|
|
6959
|
+
end
|
|
6960
|
+
|
|
6961
|
+
def launch
|
|
6962
|
+
window('histogram example', 640, 480) {
|
|
6963
|
+
margined true
|
|
6964
|
+
|
|
6965
|
+
horizontal_box {
|
|
6966
|
+
vertical_box {
|
|
6967
|
+
stretchy false
|
|
6968
|
+
|
|
6969
|
+
10.times do |i|
|
|
6970
|
+
spinbox(0, 100) { |sb|
|
|
6971
|
+
stretchy false
|
|
6972
|
+
value <=> [self, "datapoints[#{i}]", after_write: -> { @area.queue_redraw_all }]
|
|
6973
|
+
}
|
|
6974
|
+
end
|
|
6975
|
+
|
|
6976
|
+
color_button { |cb|
|
|
6977
|
+
stretchy false
|
|
6978
|
+
color COLOR_BLUE
|
|
6979
|
+
|
|
6980
|
+
on_changed do
|
|
6981
|
+
@histogram_color = cb.color
|
|
6982
|
+
@area.queue_redraw_all
|
|
6983
|
+
end
|
|
6984
|
+
}
|
|
6985
|
+
}
|
|
6986
|
+
|
|
6987
|
+
@area = area {
|
|
6988
|
+
on_draw do |area_draw_params|
|
|
6989
|
+
rectangle(0, 0, area_draw_params[:area_width], area_draw_params[:area_height]) {
|
|
6990
|
+
fill 0xFFFFFF
|
|
6991
|
+
}
|
|
6992
|
+
|
|
6993
|
+
graph_width, graph_height = *graph_size(area_draw_params[:area_width], area_draw_params[:area_height])
|
|
6994
|
+
|
|
6995
|
+
figure(X_OFF_LEFT, Y_OFF_TOP) {
|
|
6996
|
+
line(X_OFF_LEFT, Y_OFF_TOP + graph_height)
|
|
6997
|
+
line(X_OFF_LEFT + graph_width, Y_OFF_TOP + graph_height)
|
|
6998
|
+
|
|
6999
|
+
stroke 0x000000, thickness: 2, miter_limit: 10
|
|
7000
|
+
}
|
|
7001
|
+
|
|
7002
|
+
# now create the fill for the graph below the graph line
|
|
7003
|
+
graph_path(graph_width, graph_height, true) {
|
|
7004
|
+
fill @histogram_color.merge(a: 0.5)
|
|
7005
|
+
}
|
|
7006
|
+
|
|
7007
|
+
# now draw the histogram line
|
|
7008
|
+
graph_path(graph_width, graph_height, false) {
|
|
7009
|
+
stroke @histogram_color.merge(thickness: 2, miter_limit: 10)
|
|
7010
|
+
}
|
|
7011
|
+
end
|
|
7012
|
+
}
|
|
7013
|
+
}
|
|
7014
|
+
}.show
|
|
7015
|
+
end
|
|
7016
|
+
end
|
|
7017
|
+
|
|
7018
|
+
Histogram.new.launch
|
|
7019
|
+
```
|
|
7020
|
+
|
|
7021
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
|
6647
7022
|
|
|
6648
7023
|
```ruby
|
|
6649
7024
|
# https://github.com/jamescook/libui-ruby/blob/master/example/histogram.rb
|
|
@@ -6657,9 +7032,10 @@ Y_OFF_TOP = 20
|
|
|
6657
7032
|
X_OFF_RIGHT = 20
|
|
6658
7033
|
Y_OFF_BOTTOM = 20
|
|
6659
7034
|
POINT_RADIUS = 5
|
|
6660
|
-
COLOR_BLUE = 0x1E90FF
|
|
7035
|
+
COLOR_BLUE = Glimmer::LibUI.interpret_color(0x1E90FF)
|
|
6661
7036
|
|
|
6662
7037
|
@datapoints = 10.times.map {Random.new.rand(90)}
|
|
7038
|
+
@color = COLOR_BLUE
|
|
6663
7039
|
|
|
6664
7040
|
def graph_size(area_width, area_height)
|
|
6665
7041
|
graph_width = area_width - X_OFF_LEFT - X_OFF_RIGHT
|
|
@@ -6715,11 +7091,12 @@ window('histogram example', 640, 480) {
|
|
|
6715
7091
|
}
|
|
6716
7092
|
end
|
|
6717
7093
|
|
|
6718
|
-
|
|
7094
|
+
color_button { |cb|
|
|
6719
7095
|
stretchy false
|
|
6720
7096
|
color COLOR_BLUE
|
|
6721
7097
|
|
|
6722
7098
|
on_changed do
|
|
7099
|
+
@color = cb.color
|
|
6723
7100
|
@area.queue_redraw_all
|
|
6724
7101
|
end
|
|
6725
7102
|
}
|
|
@@ -6727,31 +7104,27 @@ window('histogram example', 640, 480) {
|
|
|
6727
7104
|
|
|
6728
7105
|
@area = area {
|
|
6729
7106
|
on_draw do |area_draw_params|
|
|
6730
|
-
|
|
6731
|
-
rectangle(0, 0, area_draw_params[:area_width], area_draw_params[:area_height])
|
|
6732
|
-
|
|
7107
|
+
rectangle(0, 0, area_draw_params[:area_width], area_draw_params[:area_height]) {
|
|
6733
7108
|
fill 0xFFFFFF
|
|
6734
7109
|
}
|
|
6735
7110
|
|
|
6736
7111
|
graph_width, graph_height = *graph_size(area_draw_params[:area_width], area_draw_params[:area_height])
|
|
6737
7112
|
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
line(X_OFF_LEFT + graph_width, Y_OFF_TOP + graph_height)
|
|
6742
|
-
}
|
|
7113
|
+
figure(X_OFF_LEFT, Y_OFF_TOP) {
|
|
7114
|
+
line(X_OFF_LEFT, Y_OFF_TOP + graph_height)
|
|
7115
|
+
line(X_OFF_LEFT + graph_width, Y_OFF_TOP + graph_height)
|
|
6743
7116
|
|
|
6744
7117
|
stroke 0x000000, thickness: 2, miter_limit: 10
|
|
6745
7118
|
}
|
|
6746
7119
|
|
|
6747
7120
|
# now create the fill for the graph below the graph line
|
|
6748
7121
|
graph_path(graph_width, graph_height, true) {
|
|
6749
|
-
fill @
|
|
7122
|
+
fill @color.merge(a: 0.5)
|
|
6750
7123
|
}
|
|
6751
7124
|
|
|
6752
7125
|
# now draw the histogram line
|
|
6753
7126
|
graph_path(graph_width, graph_height, false) {
|
|
6754
|
-
stroke @
|
|
7127
|
+
stroke @color.merge(thickness: 2, miter_limit: 10)
|
|
6755
7128
|
}
|
|
6756
7129
|
end
|
|
6757
7130
|
}
|
|
@@ -8140,7 +8513,140 @@ Mac | Windows | Linux
|
|
|
8140
8513
|
----|---------|------
|
|
8141
8514
|
  |   |  
|
|
8142
8515
|
|
|
8143
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
|
8516
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
|
8517
|
+
|
|
8518
|
+
```ruby
|
|
8519
|
+
require 'glimmer-dsl-libui'
|
|
8520
|
+
|
|
8521
|
+
class Timer
|
|
8522
|
+
include Glimmer
|
|
8523
|
+
|
|
8524
|
+
SECOND_MAX = 59
|
|
8525
|
+
MINUTE_MAX = 59
|
|
8526
|
+
HOUR_MAX = 23
|
|
8527
|
+
|
|
8528
|
+
attr_accessor :hour, :min, :sec, :started, :played
|
|
8529
|
+
|
|
8530
|
+
def initialize
|
|
8531
|
+
@pid = nil
|
|
8532
|
+
@alarm_file = File.expand_path('../sounds/AlanWalker-Faded.mid', __dir__)
|
|
8533
|
+
@hour = @min = @sec = 0
|
|
8534
|
+
at_exit { stop_alarm }
|
|
8535
|
+
setup_timer
|
|
8536
|
+
create_gui
|
|
8537
|
+
end
|
|
8538
|
+
|
|
8539
|
+
def stop_alarm
|
|
8540
|
+
if @pid
|
|
8541
|
+
Process.kill(:SIGKILL, @pid) if @th.alive?
|
|
8542
|
+
@pid = nil
|
|
8543
|
+
end
|
|
8544
|
+
end
|
|
8545
|
+
|
|
8546
|
+
def play_alarm
|
|
8547
|
+
stop_alarm
|
|
8548
|
+
if @pid.nil?
|
|
8549
|
+
begin
|
|
8550
|
+
@pid = spawn "timidity -G 0.0-10.0 #{@alarm_file}"
|
|
8551
|
+
@th = Process.detach @pid
|
|
8552
|
+
rescue Errno::ENOENT
|
|
8553
|
+
warn 'Timidty++ not found. Please install Timidity++.'
|
|
8554
|
+
warn 'https://sourceforge.net/projects/timidity/'
|
|
8555
|
+
end
|
|
8556
|
+
end
|
|
8557
|
+
end
|
|
8558
|
+
|
|
8559
|
+
def setup_timer
|
|
8560
|
+
unless @setup_timer
|
|
8561
|
+
Glimmer::LibUI.timer(1) do
|
|
8562
|
+
if @started
|
|
8563
|
+
seconds = @sec
|
|
8564
|
+
minutes = @min
|
|
8565
|
+
hours = @hour
|
|
8566
|
+
if seconds > 0
|
|
8567
|
+
self.sec = seconds -= 1
|
|
8568
|
+
end
|
|
8569
|
+
if seconds == 0
|
|
8570
|
+
if minutes > 0
|
|
8571
|
+
self.min = minutes -= 1
|
|
8572
|
+
self.sec = seconds = SECOND_MAX
|
|
8573
|
+
end
|
|
8574
|
+
if minutes == 0
|
|
8575
|
+
if hours > 0
|
|
8576
|
+
self.hour = hours -= 1
|
|
8577
|
+
self.min = minutes = MINUTE_MAX
|
|
8578
|
+
self.sec = seconds = SECOND_MAX
|
|
8579
|
+
end
|
|
8580
|
+
if hours == 0 && minutes == 0 && seconds == 0
|
|
8581
|
+
self.started = false
|
|
8582
|
+
unless @played
|
|
8583
|
+
play_alarm
|
|
8584
|
+
msg_box('Alarm', 'Countdown Is Finished!')
|
|
8585
|
+
self.played = true
|
|
8586
|
+
end
|
|
8587
|
+
end
|
|
8588
|
+
end
|
|
8589
|
+
end
|
|
8590
|
+
end
|
|
8591
|
+
end
|
|
8592
|
+
@setup_timer = true
|
|
8593
|
+
end
|
|
8594
|
+
end
|
|
8595
|
+
|
|
8596
|
+
def create_gui
|
|
8597
|
+
window('Timer') {
|
|
8598
|
+
margined true
|
|
8599
|
+
|
|
8600
|
+
group('Countdown') {
|
|
8601
|
+
vertical_box {
|
|
8602
|
+
horizontal_box {
|
|
8603
|
+
spinbox(0, HOUR_MAX) {
|
|
8604
|
+
stretchy false
|
|
8605
|
+
value <=> [self, :hour]
|
|
8606
|
+
}
|
|
8607
|
+
label(':') {
|
|
8608
|
+
stretchy false
|
|
8609
|
+
}
|
|
8610
|
+
spinbox(0, MINUTE_MAX) {
|
|
8611
|
+
stretchy false
|
|
8612
|
+
value <=> [self, :min]
|
|
8613
|
+
}
|
|
8614
|
+
label(':') {
|
|
8615
|
+
stretchy false
|
|
8616
|
+
}
|
|
8617
|
+
spinbox(0, SECOND_MAX) {
|
|
8618
|
+
stretchy false
|
|
8619
|
+
value <=> [self, :sec]
|
|
8620
|
+
}
|
|
8621
|
+
}
|
|
8622
|
+
horizontal_box {
|
|
8623
|
+
button('Start') {
|
|
8624
|
+
enabled <= [self, :started, on_read: :!]
|
|
8625
|
+
|
|
8626
|
+
on_clicked do
|
|
8627
|
+
self.started = true
|
|
8628
|
+
self.played = false
|
|
8629
|
+
end
|
|
8630
|
+
}
|
|
8631
|
+
|
|
8632
|
+
button('Stop') {
|
|
8633
|
+
enabled <= [self, :started]
|
|
8634
|
+
|
|
8635
|
+
on_clicked do
|
|
8636
|
+
self.started = false
|
|
8637
|
+
end
|
|
8638
|
+
}
|
|
8639
|
+
}
|
|
8640
|
+
}
|
|
8641
|
+
}
|
|
8642
|
+
}.show
|
|
8643
|
+
end
|
|
8644
|
+
end
|
|
8645
|
+
|
|
8646
|
+
Timer.new
|
|
8647
|
+
```
|
|
8648
|
+
|
|
8649
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
|
8144
8650
|
|
|
8145
8651
|
```ruby
|
|
8146
8652
|
require 'glimmer-dsl-libui'
|