glimmer-dsl-libui 0.4.1 → 0.4.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -1
- data/README.md +1628 -204
- data/VERSION +1 -1
- data/examples/basic_entry.rb +27 -24
- data/examples/basic_entry2.rb +31 -0
- data/examples/button_counter.rb +27 -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/form.rb +42 -30
- data/examples/form2.rb +37 -0
- data/examples/form_table.rb +100 -87
- data/examples/form_table2.rb +93 -0
- data/examples/histogram.rb +98 -91
- data/examples/histogram2.rb +109 -0
- data/examples/login.rb +45 -39
- data/examples/login2.rb +55 -0
- data/examples/login3.rb +65 -0
- data/examples/login4.rb +61 -0
- data/examples/login5.rb +43 -0
- data/examples/meta_example.rb +10 -7
- data/examples/method_based_custom_keyword.rb +8 -15
- data/examples/method_based_custom_keyword2.rb +97 -0
- data/examples/snake.rb +1 -1
- data/examples/timer.rb +28 -31
- data/examples/timer2.rb +129 -0
- data/glimmer-dsl-libui.gemspec +0 -0
- data/lib/glimmer/dsl/libui/data_binding_expression.rb +4 -6
- data/lib/glimmer/libui/attributed_string.rb +3 -0
- data/lib/glimmer/libui/control_proxy/area_proxy.rb +52 -46
- data/lib/glimmer/libui/control_proxy/entry_proxy.rb +5 -0
- data/lib/glimmer/libui/control_proxy/image_proxy.rb +4 -5
- data/lib/glimmer/libui/control_proxy/multiline_entry_proxy.rb +5 -0
- data/lib/glimmer/libui/control_proxy/spinbox_proxy.rb +38 -0
- data/lib/glimmer/libui/control_proxy/table_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy.rb +8 -1
- data/lib/glimmer/libui/data_bindable.rb +39 -0
- data/lib/glimmer/libui/shape.rb +7 -2
- metadata +17 -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
|
[![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)
|
@@ -14,11 +14,11 @@ Mac | Windows | Linux
|
|
14
14
|
The main trade-off in using [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) as opposed to [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) or [Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk) is the fact that [SWT](https://www.eclipse.org/swt/) and [Tk](https://www.tcl.tk/) are more mature than mid-alpha [libui](https://github.com/andlabs/libui) as GUI toolkits. Still, if there is only a need to build a small simple application, [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) could be a good convenient choice due to having zero prerequisites beyond the dependencies included in the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui). Also, just like [Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk), its apps start instantly and have a small memory footprint. [LibUI](https://github.com/kojix2/LibUI) is a promising new GUI toolkit that might prove quite worthy in the future.
|
15
15
|
|
16
16
|
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) aims to provide a DSL similar to the [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) to enable more productive desktop development in Ruby with:
|
17
|
-
- Declarative DSL syntax that visually maps to the GUI control hierarchy
|
18
|
-
- Convention over configuration via smart defaults and automation of low-level details
|
19
|
-
- Requiring the least amount of syntax possible to build GUI
|
20
|
-
- Custom Keyword support
|
21
|
-
- Bidirectional Data-Binding to declaratively wire and automatically synchronize GUI with
|
17
|
+
- [Declarative DSL syntax](#glimmer-gui-dsl-concepts) that visually maps to the GUI control hierarchy
|
18
|
+
- [Convention over configuration](#smart-defaults-and-conventions) via smart defaults and automation of low-level details
|
19
|
+
- Requiring the [least amount of syntax](#glimmer-gui-dsl-concepts) possible to build GUI
|
20
|
+
- [Custom Keyword](#custom-keywords) support
|
21
|
+
- [Bidirectional/Unidirectional Data-Binding](#data-binding) to declaratively wire and automatically synchronize GUI Views with Models
|
22
22
|
- [Far Future Plan] Scaffolding for new custom controls, apps, and gems
|
23
23
|
- [Far Future Plan] Native-Executable packaging on Mac, Windows, and Linux.
|
24
24
|
|
@@ -264,6 +264,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
|
|
264
264
|
- [Basic Draw Text](#basic-draw-text)
|
265
265
|
- [Advanced Examples](#advanced-examples)
|
266
266
|
- [Area Gallery](#area-gallery)
|
267
|
+
- [Button Counter](#button-counter)
|
267
268
|
- [Color The Circles](#color-the-circles)
|
268
269
|
- [Control Gallery](#control-gallery)
|
269
270
|
- [Custom Draw Text](#custom-draw-text)
|
@@ -372,7 +373,7 @@ gem install glimmer-dsl-libui
|
|
372
373
|
Or install via Bundler `Gemfile`:
|
373
374
|
|
374
375
|
```ruby
|
375
|
-
gem 'glimmer-dsl-libui', '~> 0.4.
|
376
|
+
gem 'glimmer-dsl-libui', '~> 0.4.5'
|
376
377
|
```
|
377
378
|
|
378
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.
|
@@ -619,93 +620,112 @@ Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
|
619
620
|
```ruby
|
620
621
|
require 'glimmer-dsl-libui'
|
621
622
|
|
622
|
-
|
623
|
-
|
624
|
-
data = [
|
625
|
-
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO', '80014'],
|
626
|
-
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA', '02101'],
|
627
|
-
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL', '60007'],
|
628
|
-
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA', '98101'],
|
629
|
-
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA', '90001'],
|
630
|
-
]
|
631
|
-
|
632
|
-
window('Contacts', 600, 600) { |w|
|
633
|
-
margined true
|
623
|
+
class FormTable
|
624
|
+
include Glimmer
|
634
625
|
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
@
|
640
|
-
|
641
|
-
|
642
|
-
@
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
}
|
651
|
-
@state_entry = entry {
|
652
|
-
label 'State'
|
653
|
-
}
|
654
|
-
}
|
655
|
-
|
656
|
-
button('Save Contact') {
|
657
|
-
stretchy false
|
658
|
-
|
659
|
-
on_clicked do
|
660
|
-
new_row = [@name_entry.text, @email_entry.text, @phone_entry.text, @city_entry.text, @state_entry.text]
|
661
|
-
if new_row.include?('')
|
662
|
-
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
663
|
-
else
|
664
|
-
data << new_row # automatically inserts a row into the table due to implicit data-binding
|
665
|
-
@unfiltered_data = data.dup
|
666
|
-
@name_entry.text = ''
|
667
|
-
@email_entry.text = ''
|
668
|
-
@phone_entry.text = ''
|
669
|
-
@city_entry.text = ''
|
670
|
-
@state_entry.text = ''
|
671
|
-
end
|
672
|
-
end
|
673
|
-
}
|
674
|
-
|
675
|
-
search_entry { |se|
|
676
|
-
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
|
677
641
|
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
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 = ''
|
688
687
|
end
|
689
688
|
end
|
690
|
-
|
691
|
-
|
692
|
-
|
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')
|
693
716
|
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
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
|
700
727
|
|
701
|
-
|
702
|
-
|
703
|
-
on_changed do |row, type, row_data|
|
704
|
-
puts "Row #{row} #{type}: #{row_data}"
|
705
|
-
end
|
706
|
-
}
|
707
|
-
}
|
708
|
-
}.show
|
728
|
+
FormTable.new.launch
|
709
729
|
```
|
710
730
|
|
711
731
|
![glimmer-dsl-libui-linux-form-table.png](images/glimmer-dsl-libui-linux-form-table.png)
|
@@ -778,9 +798,9 @@ Check [examples/dynamic_area.rb](#dynamic-area) for a more detailed semi-declara
|
|
778
798
|
- `scroll_to(x as Numeric, y as Numeric, width as Numeric = main_window.width, height as Numeric = main_window.height)`: scrolls to `x`/`y` location with `width` and `height` viewport size.
|
779
799
|
- `set_size(width as Numeric, height as Numeric)`: set size of scrolling area, which must must exceed that of visible viewport in order for scrolling to be enabled.
|
780
800
|
|
781
|
-
Mac |
|
782
|
-
|
783
|
-
![glimmer-dsl-libui-mac-dynamic-area.png](images/glimmer-dsl-libui-mac-basic-scrolling-area.png) ![glimmer-dsl-libui-mac-dynamic-area-updated.png](images/glimmer-dsl-libui-mac-basic-scrolling-area-scrolled.png)
|
801
|
+
Mac |Linux
|
802
|
+
----|-----
|
803
|
+
![glimmer-dsl-libui-mac-dynamic-area.png](images/glimmer-dsl-libui-mac-basic-scrolling-area.png) ![glimmer-dsl-libui-mac-dynamic-area-updated.png](images/glimmer-dsl-libui-mac-basic-scrolling-area-scrolled.png) | ![glimmer-dsl-libui-linux-dynamic-area.png](images/glimmer-dsl-libui-linux-basic-scrolling-area.png) ![glimmer-dsl-libui-linux-dynamic-area-updated.png](images/glimmer-dsl-libui-linux-basic-scrolling-area-scrolled.png)
|
784
804
|
|
785
805
|
Check [examples/basic_scrolling_area.rb](#basic-scrolling-area) for a more detailed example.
|
786
806
|
|
@@ -876,7 +896,7 @@ Given that it is very new and is not a [libui](https://github.com/andlabs/libui)
|
|
876
896
|
- Including an `image` inside an `area` `on_draw` listener improves performance due to not retaining pixel/line data in memory.
|
877
897
|
- Supplying `width` and `height` (2nd and 3rd arguments) greatly improves performance when shrinking image.
|
878
898
|
|
879
|
-
Currently, it is recommended to use `image` with very small `width` and `height` values only.
|
899
|
+
Currently, it is recommended to use `image` with very small `width` and `height` values only (e.g. 24x24).
|
880
900
|
|
881
901
|
Setting a [`transform` `matrix`](#area-transform-matrix) is supported under `image` just like it is under `path` and `text` inside `area`.
|
882
902
|
|
@@ -1032,6 +1052,8 @@ window('Basic Image', 96, 96) {
|
|
1032
1052
|
|
1033
1053
|
One final note is that in Linux, table images grow and shrink with the image size unlike on the Mac where table row heights are constant regardless of image sizes. As such, you may be able to repurpose a table with a single image column and a single row as an image control with more native libui rendering if you are only targeting Linux with your app.
|
1034
1054
|
|
1055
|
+
![linux table image](images/glimmer-dsl-libui-linux-basic-table-image.png)
|
1056
|
+
|
1035
1057
|
Check out [examples/basic_image.rb](#basic-image) (all versions) for examples of using `image` Glimmer custom control.
|
1036
1058
|
|
1037
1059
|
#### Colors
|
@@ -1328,7 +1350,7 @@ The [Observer Design Pattern](https://en.wikipedia.org/wiki/Observer_pattern) (a
|
|
1328
1350
|
|
1329
1351
|
![MVC - Model View Controller](https://www.researchgate.net/profile/Danny-Weyns/publication/269303611/figure/fig2/AS:858133056462866@1581606272800/Smalltalk80-MVC-pattern-View-and-Controller-work-as-a-pair-allowing-the-user-to-interact.ppm)
|
1330
1352
|
|
1331
|
-
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) supports the [Observer Design Pattern](https://en.wikipedia.org/wiki/Observer_pattern) via the `observe(model,
|
1353
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) supports the [Observer Design Pattern](https://en.wikipedia.org/wiki/Observer_pattern) via the `observe(model, attribute_or_key=nil)` keyword, which can observe `Object` models with attributes, `Hash`es with keys, and `Array`s. It automatically enhances objects as needed to support automatically notifying observers of changes via `observable#notify_observers(attribute_or_key = nil)` method:
|
1332
1354
|
- `Object` becomes `Glimmer::DataBinding::ObservableModel`, which supports observing specified `Object` model attributes.
|
1333
1355
|
- `Hash` becomes `Glimmer::DataBinding::ObservableHash`, which supports observing all `Hash` keys or a specific `Hash` key
|
1334
1356
|
- `Array` becomes `Glimmer::DataBinding::ObservableArray`, which supports observing `Array` changes like those done with `push`, `<<`, `delete`, and `map!` methods (all mutation methods).
|
@@ -1349,13 +1371,46 @@ See examples of the `observe` keyword at [Color The Circles](#color-the-circles)
|
|
1349
1371
|
|
1350
1372
|
### Data-Binding
|
1351
1373
|
|
1352
|
-
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) supports
|
1374
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) supports both bidirectional (two-way) data-binding and unidirectional (one-way) data-binding.
|
1353
1375
|
|
1354
|
-
|
1376
|
+
Data-binding enables writing very expressive, terse, and declarative code to synchronize View properties with Model attributes without writing many lines or pages of imperative code doing the same thing, increasing productivity immensely.
|
1355
1377
|
|
1356
|
-
|
1378
|
+
Data-binding automatically takes advantage of the [Observer Pattern](#observer-pattern) behind the scenes and is very well suited to declaring View property data sources piecemeal. On the other hand, explicit use of the [Observer Pattern](#observer-pattern) is sometimes more suitable when needing to make multiple View updates upon a single Model attribute change.
|
1357
1379
|
|
1358
|
-
|
1380
|
+
Data-binding supports utilizing the [MVP (Model View Presenter)](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter) flavor of [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) by observing both the View and a Presenter for changes and updating the opposite side upon encountering them. This enables writing more decoupled cleaner code that keeps View code and Model code disentangled and highly maintainable. For example, check out the Snake game presenters for [Grid](/examples/snake/presenter/grid.rb) and [Cell](/examples/snake/presenter/cell.rb), which act as proxies for the actual Snake game models [Snake](/examples/snake/model/snake.rb) and [Apple](/examples/snake/model/apple.rb), mediating synchronization of data between them and the [Snake View GUI](/examples/snake.rb).
|
1381
|
+
|
1382
|
+
![MVP](https://www.researchgate.net/profile/Gilles-Perrouin/publication/320249584/figure/fig8/AS:668260987068418@1536337243385/Model-view-presenter-architecture.png)
|
1383
|
+
|
1384
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) supports bidirectional (two-way) data-binding of the following controls/properties via the `<=>` operator (indicating data is moving in both directions between View and Model):
|
1385
|
+
- `entry` `text` property
|
1386
|
+
- `multiline_entry` `text` property
|
1387
|
+
- `non_wrapping_multiline_entry` `text` property
|
1388
|
+
- `search_entry` `text` property
|
1389
|
+
- `spinbox` `value` property
|
1390
|
+
|
1391
|
+
Example of bidirectional data-binding:
|
1392
|
+
|
1393
|
+
```ruby
|
1394
|
+
entry {
|
1395
|
+
text <=> [contract, :legal_text]
|
1396
|
+
}
|
1397
|
+
```
|
1398
|
+
|
1399
|
+
That is data-binding a contract's legal text to an `entry` `text` property.
|
1400
|
+
|
1401
|
+
Another example of bidirectional data-binding with an option:
|
1402
|
+
|
1403
|
+
```ruby
|
1404
|
+
entry {
|
1405
|
+
text <=> [self, :entered_text, after_write: ->(text) {puts text}]
|
1406
|
+
}
|
1407
|
+
```
|
1408
|
+
|
1409
|
+
That is data-binding `entered_text` attribute on `self` to `entry` `text` property and printing text after write to the model.
|
1410
|
+
|
1411
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) supports unidirectional (one-way) data-binding of any control/shape/attributed-string property via the `<=` operator (indicating data is moving from the right side, which is the Model, to the left side, which is the GUI View object).
|
1412
|
+
|
1413
|
+
Example of unidirectional data-binding:
|
1359
1414
|
|
1360
1415
|
```ruby
|
1361
1416
|
square(0, 0, CELL_SIZE) {
|
@@ -1365,7 +1420,7 @@ square(0, 0, CELL_SIZE) {
|
|
1365
1420
|
|
1366
1421
|
That is data-binding a grid cell color to a `square` shape's `fill` property. That means if the `color` attribute of the grid cell is updated, the `fill` property of the `square` shape is automatically updated accordingly.
|
1367
1422
|
|
1368
|
-
Another Example:
|
1423
|
+
Another Example of unidirectional data-binding with an option:
|
1369
1424
|
|
1370
1425
|
```ruby
|
1371
1426
|
window {
|
@@ -1375,14 +1430,36 @@ window {
|
|
1375
1430
|
|
1376
1431
|
That is data-binding the `window` `title` property to the `score` attribute of a `@game`, but converting on read from the Model to a `String`.
|
1377
1432
|
|
1378
|
-
|
1433
|
+
To summarize the data-binding API:
|
1434
|
+
- `view_property <=> [model, attribute, *read_or_write_options]`: Bidirectional (two-way) data-binding to Model attribute accessor
|
1435
|
+
- `view_property <= [model, attribute, *read_only_options]`: Unidirectional (one-way) data-binding to Model attribute reader
|
1379
1436
|
|
1380
|
-
|
1437
|
+
This is also known as the [Glimmer Shine](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#shine) syntax for data-binding, a [Glimmer](https://github.com/AndyObtiva/glimmer)-only unique innovation that takes advantage of [Ruby](https://www.ruby-lang.org/en/)'s highly expressive syntax and malleable DSL support.
|
1438
|
+
|
1439
|
+
Data-binding options include:
|
1381
1440
|
- `before_read {|value| ...}`: performs an operation before reading data from Model to update the View.
|
1382
1441
|
- `on_read {|value| ...}`: converts value read from Model to update the View.
|
1383
1442
|
- `after_read {|converted_value| ...}`: performs an operation after read from Model and updating the View.
|
1443
|
+
- `before_write {|value| ...}`: performs an operation before writing data to Model from View.
|
1444
|
+
- `on_write {|value| ...}`: converts value read from View to update the Model.
|
1445
|
+
- `after_write {|converted_value| ...}`: performs an operation after writing to Model from View.
|
1446
|
+
- `computed_by attribute` or `computed_by [attribute1, attribute2, ...]`: indicates model attribute is computed from specified attribute(s), thus updated when they are updated (see in [Login example version 2](/examples/login2.rb))
|
1447
|
+
|
1448
|
+
Note that with both `on_read` and `on_write` converters, you could pass a `Symbol` representing the name of a method on the value object to invoke.
|
1449
|
+
|
1450
|
+
Example:
|
1384
1451
|
|
1385
|
-
|
1452
|
+
```ruby
|
1453
|
+
entry {
|
1454
|
+
text <=> [product, :price, on_read: :to_s, on_write: :to_i]
|
1455
|
+
}
|
1456
|
+
```
|
1457
|
+
|
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.
|
1461
|
+
|
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.
|
1386
1463
|
|
1387
1464
|
### API Gotchas
|
1388
1465
|
|
@@ -1398,6 +1475,270 @@ Learn more from data-binding usage in [Snake](#snake) and [Tic Tac Toe](#tic_tac
|
|
1398
1475
|
|
1399
1476
|
### Original API
|
1400
1477
|
|
1478
|
+
Here are all the lower-level [LibUI](https://github.com/kojix2/LibUI) API methods utilized by [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui):
|
1479
|
+
- `alloc_control`
|
1480
|
+
- `area_begin_user_window_move`
|
1481
|
+
- `area_begin_user_window_resize`
|
1482
|
+
- `area_queue_redraw_all`
|
1483
|
+
- `area_scroll_to`
|
1484
|
+
- `area_set_size`
|
1485
|
+
- `attribute_color`
|
1486
|
+
- `attribute_family`
|
1487
|
+
- `attribute_features`
|
1488
|
+
- `attribute_get_type`
|
1489
|
+
- `attribute_italic`
|
1490
|
+
- `attribute_size`
|
1491
|
+
- `attribute_stretch`
|
1492
|
+
- `attribute_underline`
|
1493
|
+
- `attribute_underline_color`
|
1494
|
+
- `attribute_weight`
|
1495
|
+
- `attributed_string_append_unattributed`
|
1496
|
+
- `attributed_string_byte_index_to_grapheme`
|
1497
|
+
- `attributed_string_delete`
|
1498
|
+
- `attributed_string_for_each_attribute`
|
1499
|
+
- `attributed_string_grapheme_to_byte_index`
|
1500
|
+
- `attributed_string_insert_at_unattributed`
|
1501
|
+
- `attributed_string_len`
|
1502
|
+
- `attributed_string_num_graphemes`
|
1503
|
+
- `attributed_string_set_attribute`
|
1504
|
+
- `attributed_string_string`
|
1505
|
+
- `box_append`
|
1506
|
+
- `box_delete`
|
1507
|
+
- `box_padded`
|
1508
|
+
- `box_set_padded`
|
1509
|
+
- `button_on_clicked`
|
1510
|
+
- `button_set_text`
|
1511
|
+
- `button_text`
|
1512
|
+
- `checkbox_checked`
|
1513
|
+
- `checkbox_on_toggled`
|
1514
|
+
- `checkbox_set_checked`
|
1515
|
+
- `checkbox_set_text`
|
1516
|
+
- `checkbox_text`
|
1517
|
+
- `color_button_color`
|
1518
|
+
- `color_button_on_changed`
|
1519
|
+
- `color_button_set_color`
|
1520
|
+
- `combobox_append`
|
1521
|
+
- `combobox_on_selected`
|
1522
|
+
- `combobox_selected`
|
1523
|
+
- `combobox_set_selected`
|
1524
|
+
- `control_destroy`
|
1525
|
+
- `control_disable`
|
1526
|
+
- `control_enable`
|
1527
|
+
- `control_enabled`
|
1528
|
+
- `control_enabled_to_user`
|
1529
|
+
- `control_handle`
|
1530
|
+
- `control_hide`
|
1531
|
+
- `control_parent`
|
1532
|
+
- `control_set_parent`
|
1533
|
+
- `control_show`
|
1534
|
+
- `control_toplevel`
|
1535
|
+
- `control_verify_set_parent`
|
1536
|
+
- `control_visible`
|
1537
|
+
- `date_time_picker_on_changed`
|
1538
|
+
- `date_time_picker_set_time`
|
1539
|
+
- `date_time_picker_time`
|
1540
|
+
- `draw_clip`
|
1541
|
+
- `draw_fill`
|
1542
|
+
- `draw_free_path`
|
1543
|
+
- `draw_free_text_layout`
|
1544
|
+
- `draw_matrix_invert`
|
1545
|
+
- `draw_matrix_invertible`
|
1546
|
+
- `draw_matrix_multiply`
|
1547
|
+
- `draw_matrix_rotate`
|
1548
|
+
- `draw_matrix_scale`
|
1549
|
+
- `draw_matrix_set_identity`
|
1550
|
+
- `draw_matrix_skew`
|
1551
|
+
- `draw_matrix_transform_point`
|
1552
|
+
- `draw_matrix_transform_size`
|
1553
|
+
- `draw_matrix_translate`
|
1554
|
+
- `draw_new_path`
|
1555
|
+
- `draw_new_text_layout`
|
1556
|
+
- `draw_path_add_rectangle`
|
1557
|
+
- `draw_path_arc_to`
|
1558
|
+
- `draw_path_bezier_to`
|
1559
|
+
- `draw_path_close_figure`
|
1560
|
+
- `draw_path_end`
|
1561
|
+
- `draw_path_line_to`
|
1562
|
+
- `draw_path_new_figure`
|
1563
|
+
- `draw_path_new_figure_with_arc`
|
1564
|
+
- `draw_restore`
|
1565
|
+
- `draw_save`
|
1566
|
+
- `draw_stroke`
|
1567
|
+
- `draw_text`
|
1568
|
+
- `draw_text_layout_extents`
|
1569
|
+
- `draw_transform`
|
1570
|
+
- `editable_combobox_append`
|
1571
|
+
- `editable_combobox_on_changed`
|
1572
|
+
- `editable_combobox_set_text`
|
1573
|
+
- `editable_combobox_text`
|
1574
|
+
- `entry_on_changed`
|
1575
|
+
- `entry_read_only`
|
1576
|
+
- `entry_set_read_only`
|
1577
|
+
- `entry_set_text`
|
1578
|
+
- `entry_text`
|
1579
|
+
- `ffi_lib`
|
1580
|
+
- `ffi_lib=`
|
1581
|
+
- `font_button_font`
|
1582
|
+
- `font_button_on_changed`
|
1583
|
+
- `form_append`
|
1584
|
+
- `form_delete`
|
1585
|
+
- `form_padded`
|
1586
|
+
- `form_set_padded`
|
1587
|
+
- `free_attribute`
|
1588
|
+
- `free_attributed_string`
|
1589
|
+
- `free_control`
|
1590
|
+
- `free_font_button_font`
|
1591
|
+
- `free_image`
|
1592
|
+
- `free_init_error`
|
1593
|
+
- `free_open_type_features`
|
1594
|
+
- `free_table_model`
|
1595
|
+
- `free_table_value`
|
1596
|
+
- `free_text`
|
1597
|
+
- `grid_append`
|
1598
|
+
- `grid_insert_at`
|
1599
|
+
- `grid_padded`
|
1600
|
+
- `grid_set_padded`
|
1601
|
+
- `group_margined`
|
1602
|
+
- `group_set_child`
|
1603
|
+
- `group_set_margined`
|
1604
|
+
- `group_set_title`
|
1605
|
+
- `group_title`
|
1606
|
+
- `image_append`
|
1607
|
+
- `init`
|
1608
|
+
- `label_set_text`
|
1609
|
+
- `label_text`
|
1610
|
+
- `main`
|
1611
|
+
- `main_step`
|
1612
|
+
- `main_steps`
|
1613
|
+
- `menu_append_about_item`
|
1614
|
+
- `menu_append_check_item`
|
1615
|
+
- `menu_append_item`
|
1616
|
+
- `menu_append_preferences_item`
|
1617
|
+
- `menu_append_quit_item`
|
1618
|
+
- `menu_append_separator`
|
1619
|
+
- `menu_item_checked`
|
1620
|
+
- `menu_item_disable`
|
1621
|
+
- `menu_item_enable`
|
1622
|
+
- `menu_item_on_clicked`
|
1623
|
+
- `menu_item_set_checked`
|
1624
|
+
- `msg_box`
|
1625
|
+
- `msg_box_error`
|
1626
|
+
- `multiline_entry_append`
|
1627
|
+
- `multiline_entry_on_changed`
|
1628
|
+
- `multiline_entry_read_only`
|
1629
|
+
- `multiline_entry_set_read_only`
|
1630
|
+
- `multiline_entry_set_text`
|
1631
|
+
- `multiline_entry_text`
|
1632
|
+
- `new_area`
|
1633
|
+
- `new_attributed_string`
|
1634
|
+
- `new_background_attribute`
|
1635
|
+
- `new_button`
|
1636
|
+
- `new_checkbox`
|
1637
|
+
- `new_color_attribute`
|
1638
|
+
- `new_color_button`
|
1639
|
+
- `new_combobox`
|
1640
|
+
- `new_date_picker`
|
1641
|
+
- `new_date_time_picker`
|
1642
|
+
- `new_editable_combobox`
|
1643
|
+
- `new_entry`
|
1644
|
+
- `new_family_attribute`
|
1645
|
+
- `new_features_attribute`
|
1646
|
+
- `new_font_button`
|
1647
|
+
- `new_form`
|
1648
|
+
- `new_grid`
|
1649
|
+
- `new_group`
|
1650
|
+
- `new_horizontal_box`
|
1651
|
+
- `new_horizontal_separator`
|
1652
|
+
- `new_image`
|
1653
|
+
- `new_italic_attribute`
|
1654
|
+
- `new_label`
|
1655
|
+
- `new_menu`
|
1656
|
+
- `new_multiline_entry`
|
1657
|
+
- `new_non_wrapping_multiline_entry`
|
1658
|
+
- `new_open_type_features`
|
1659
|
+
- `new_password_entry`
|
1660
|
+
- `new_progress_bar`
|
1661
|
+
- `new_radio_buttons`
|
1662
|
+
- `new_scrolling_area`
|
1663
|
+
- `new_search_entry`
|
1664
|
+
- `new_size_attribute`
|
1665
|
+
- `new_slider`
|
1666
|
+
- `new_spinbox`
|
1667
|
+
- `new_stretch_attribute`
|
1668
|
+
- `new_tab`
|
1669
|
+
- `new_table`
|
1670
|
+
- `new_table_model`
|
1671
|
+
- `new_table_value_color`
|
1672
|
+
- `new_table_value_image`
|
1673
|
+
- `new_table_value_int`
|
1674
|
+
- `new_table_value_string`
|
1675
|
+
- `new_time_picker`
|
1676
|
+
- `new_underline_attribute`
|
1677
|
+
- `new_underline_color_attribute`
|
1678
|
+
- `new_vertical_box`
|
1679
|
+
- `new_vertical_separator`
|
1680
|
+
- `new_weight_attribute`
|
1681
|
+
- `new_window`
|
1682
|
+
- `on_should_quit`
|
1683
|
+
- `open_file`
|
1684
|
+
- `open_type_features_add`
|
1685
|
+
- `open_type_features_clone`
|
1686
|
+
- `open_type_features_for_each`
|
1687
|
+
- `open_type_features_get`
|
1688
|
+
- `open_type_features_remove`
|
1689
|
+
- `progress_bar_set_value`
|
1690
|
+
- `progress_bar_value`
|
1691
|
+
- `queue_main`
|
1692
|
+
- `quit`
|
1693
|
+
- `radio_buttons_append`
|
1694
|
+
- `radio_buttons_on_selected`
|
1695
|
+
- `radio_buttons_selected`
|
1696
|
+
- `radio_buttons_set_selected`
|
1697
|
+
- `save_file`
|
1698
|
+
- `slider_on_changed`
|
1699
|
+
- `slider_set_value`
|
1700
|
+
- `slider_value`
|
1701
|
+
- `spinbox_on_changed`
|
1702
|
+
- `spinbox_set_value`
|
1703
|
+
- `spinbox_value`
|
1704
|
+
- `tab_append`
|
1705
|
+
- `tab_delete`
|
1706
|
+
- `tab_insert_at`
|
1707
|
+
- `tab_margined`
|
1708
|
+
- `tab_num_pages`
|
1709
|
+
- `tab_set_margined`
|
1710
|
+
- `table_append_button_column`
|
1711
|
+
- `table_append_checkbox_column`
|
1712
|
+
- `table_append_checkbox_text_column`
|
1713
|
+
- `table_append_image_column`
|
1714
|
+
- `table_append_image_text_column`
|
1715
|
+
- `table_append_progress_bar_column`
|
1716
|
+
- `table_append_text_column`
|
1717
|
+
- `table_model_row_changed`
|
1718
|
+
- `table_model_row_deleted`
|
1719
|
+
- `table_model_row_inserted`
|
1720
|
+
- `table_value_color`
|
1721
|
+
- `table_value_get_type`
|
1722
|
+
- `table_value_image`
|
1723
|
+
- `table_value_int`
|
1724
|
+
- `table_value_string`
|
1725
|
+
- `timer`
|
1726
|
+
- `uninit`
|
1727
|
+
- `user_bug_cannot_set_parent_on_toplevel`
|
1728
|
+
- `window_borderless`
|
1729
|
+
- `window_content_size`
|
1730
|
+
- `window_fullscreen`
|
1731
|
+
- `window_margined`
|
1732
|
+
- `window_on_closing`
|
1733
|
+
- `window_on_content_size_changed`
|
1734
|
+
- `window_set_borderless`
|
1735
|
+
- `window_set_child`
|
1736
|
+
- `window_set_content_size`
|
1737
|
+
- `window_set_fullscreen`
|
1738
|
+
- `window_set_margined`
|
1739
|
+
- `window_set_title`
|
1740
|
+
- `window_title`
|
1741
|
+
|
1401
1742
|
To learn more about the [LibUI](https://github.com/kojix2/LibUI) API exposed through [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui):
|
1402
1743
|
- Check out [LibUI ffi.rb](https://github.com/kojix2/LibUI/blob/main/lib/libui/ffi.rb)
|
1403
1744
|
- Check out the [libui C Headers](https://github.com/andlabs/libui/blob/master/ui.h)
|
@@ -1561,17 +1902,23 @@ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version
|
|
1561
1902
|
```ruby
|
1562
1903
|
require 'glimmer-dsl-libui'
|
1563
1904
|
require 'facets'
|
1905
|
+
require 'fileutils'
|
1564
1906
|
|
1565
1907
|
class MetaExample
|
1566
1908
|
include Glimmer
|
1567
1909
|
|
1910
|
+
ADDITIONAL_BASIC_EXAMPLES = ['Color Button', 'Font Button', 'Form', 'Date Time Picker', 'Simple Notepad']
|
1911
|
+
|
1912
|
+
attr_accessor :code_text
|
1913
|
+
|
1568
1914
|
def initialize
|
1569
|
-
@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))
|
1570
1917
|
end
|
1571
1918
|
|
1572
1919
|
def examples
|
1573
1920
|
if @examples.nil?
|
1574
|
-
example_files = Dir.glob(File.join(File.expand_path('.', __dir__), '
|
1921
|
+
example_files = Dir.glob(File.join(File.expand_path('.', __dir__), '*.rb'))
|
1575
1922
|
example_file_names = example_files.map { |f| File.basename(f, '.rb') }
|
1576
1923
|
example_file_names = example_file_names.reject { |f| f == 'meta_example' || f.match(/\d$/) }
|
1577
1924
|
@examples = example_file_names.map { |f| f.underscore.titlecase }
|
@@ -1585,12 +1932,20 @@ class MetaExample
|
|
1585
1932
|
end
|
1586
1933
|
end
|
1587
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
|
+
|
1588
1943
|
def file_path_for(example)
|
1589
1944
|
File.join(File.expand_path('.', __dir__), "#{example.underscore}.rb")
|
1590
1945
|
end
|
1591
1946
|
|
1592
1947
|
def version_count_for(example)
|
1593
|
-
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
|
1594
1949
|
end
|
1595
1950
|
|
1596
1951
|
def glimmer_dsl_libui_file
|
@@ -1626,17 +1981,47 @@ class MetaExample
|
|
1626
1981
|
vertical_box {
|
1627
1982
|
stretchy false
|
1628
1983
|
|
1629
|
-
|
1984
|
+
tab {
|
1630
1985
|
stretchy false
|
1631
|
-
items examples_with_versions
|
1632
|
-
selected @selected_example_index
|
1633
1986
|
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
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
|
+
}
|
1640
2025
|
}
|
1641
2026
|
|
1642
2027
|
horizontal_box {
|
@@ -1654,7 +2039,7 @@ class MetaExample
|
|
1654
2039
|
else
|
1655
2040
|
version_number = @version_spinbox.value == 1 ? '' : @version_spinbox.value
|
1656
2041
|
example = "#{selected_example}#{version_number}"
|
1657
|
-
|
2042
|
+
self.code_text = File.read(file_path_for(example))
|
1658
2043
|
end
|
1659
2044
|
end
|
1660
2045
|
}
|
@@ -1666,9 +2051,15 @@ class MetaExample
|
|
1666
2051
|
button('Launch') {
|
1667
2052
|
on_clicked do
|
1668
2053
|
begin
|
1669
|
-
|
1670
|
-
|
1671
|
-
|
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)
|
1672
2063
|
rescue => e
|
1673
2064
|
puts e.full_message
|
1674
2065
|
puts 'Unable to write code changes! Running original example...'
|
@@ -1678,14 +2069,14 @@ class MetaExample
|
|
1678
2069
|
}
|
1679
2070
|
button('Reset') {
|
1680
2071
|
on_clicked do
|
1681
|
-
|
2072
|
+
self.code_text = File.read(file_path_for(selected_example))
|
1682
2073
|
end
|
1683
2074
|
}
|
1684
2075
|
}
|
1685
2076
|
}
|
1686
2077
|
|
1687
2078
|
@code_entry = non_wrapping_multiline_entry {
|
1688
|
-
text
|
2079
|
+
text <=> [self, :code_text]
|
1689
2080
|
}
|
1690
2081
|
}
|
1691
2082
|
}.show
|
@@ -1904,7 +2295,44 @@ UI.main
|
|
1904
2295
|
UI.quit
|
1905
2296
|
```
|
1906
2297
|
|
1907
|
-
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
2298
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
2299
|
+
|
2300
|
+
```ruby
|
2301
|
+
require 'glimmer-dsl-libui'
|
2302
|
+
|
2303
|
+
class BasicEntry
|
2304
|
+
include Glimmer
|
2305
|
+
|
2306
|
+
attr_accessor :entry_text
|
2307
|
+
|
2308
|
+
def launch
|
2309
|
+
window('Basic Entry', 300, 50) {
|
2310
|
+
horizontal_box {
|
2311
|
+
entry {
|
2312
|
+
# stretchy true # Smart default option for appending to horizontal_box
|
2313
|
+
text <=> [self, :entry_text, after_write: ->(text) {puts text; $stdout.flush}] # bidirectional data-binding between text property and entry_text attribute, printing after write to model.
|
2314
|
+
}
|
2315
|
+
|
2316
|
+
button('Button') {
|
2317
|
+
stretchy false # stretchy property is available when control is nested under horizontal_box
|
2318
|
+
|
2319
|
+
on_clicked do
|
2320
|
+
msg_box('You entered', entry_text)
|
2321
|
+
end
|
2322
|
+
}
|
2323
|
+
}
|
2324
|
+
|
2325
|
+
on_closing do
|
2326
|
+
puts 'Bye Bye'
|
2327
|
+
end
|
2328
|
+
}.show
|
2329
|
+
end
|
2330
|
+
end
|
2331
|
+
|
2332
|
+
BasicEntry.new.launch
|
2333
|
+
```
|
2334
|
+
|
2335
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
1908
2336
|
|
1909
2337
|
```ruby
|
1910
2338
|
require 'glimmer-dsl-libui'
|
@@ -2228,7 +2656,59 @@ Mac | Windows | Linux
|
|
2228
2656
|
----|---------|------
|
2229
2657
|
![glimmer-dsl-libui-mac-form.png](images/glimmer-dsl-libui-mac-form.png) ![glimmer-dsl-libui-mac-form-msg-box.png](images/glimmer-dsl-libui-mac-form-msg-box.png) | ![glimmer-dsl-libui-windows-form.png](images/glimmer-dsl-libui-windows-form.png) ![glimmer-dsl-libui-windows-form-msg-box.png](images/glimmer-dsl-libui-windows-form-msg-box.png) | ![glimmer-dsl-libui-linux-form.png](images/glimmer-dsl-libui-linux-form.png) ![glimmer-dsl-libui-linux-form-msg-box.png](images/glimmer-dsl-libui-linux-form-msg-box.png)
|
2230
2658
|
|
2231
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
2659
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
2660
|
+
|
2661
|
+
```ruby
|
2662
|
+
require 'glimmer-dsl-libui'
|
2663
|
+
|
2664
|
+
class Form
|
2665
|
+
include Glimmer
|
2666
|
+
|
2667
|
+
attr_accessor :first_name, :last_name, :phone, :email
|
2668
|
+
|
2669
|
+
def launch
|
2670
|
+
window('Form') {
|
2671
|
+
margined true
|
2672
|
+
|
2673
|
+
vertical_box {
|
2674
|
+
form {
|
2675
|
+
entry {
|
2676
|
+
label 'First Name' # label property is available when control is nested under form
|
2677
|
+
text <=> [self, :first_name] # bidirectional data-binding of entry text property to self first_name attribute
|
2678
|
+
}
|
2679
|
+
|
2680
|
+
entry {
|
2681
|
+
label 'Last Name' # label property is available when control is nested under form
|
2682
|
+
text <=> [self, :last_name]
|
2683
|
+
}
|
2684
|
+
|
2685
|
+
entry {
|
2686
|
+
label 'Phone' # label property is available when control is nested under form
|
2687
|
+
text <=> [self, :phone]
|
2688
|
+
}
|
2689
|
+
|
2690
|
+
entry {
|
2691
|
+
label 'Email' # label property is available when control is nested under form
|
2692
|
+
text <=> [self, :email]
|
2693
|
+
}
|
2694
|
+
}
|
2695
|
+
|
2696
|
+
button('Display Info') {
|
2697
|
+
stretchy false
|
2698
|
+
|
2699
|
+
on_clicked do
|
2700
|
+
msg_box('Info', "#{first_name} #{last_name} has phone #{phone} and email #{email}")
|
2701
|
+
end
|
2702
|
+
}
|
2703
|
+
}
|
2704
|
+
}.show
|
2705
|
+
end
|
2706
|
+
end
|
2707
|
+
|
2708
|
+
Form.new.launch
|
2709
|
+
```
|
2710
|
+
|
2711
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
2232
2712
|
|
2233
2713
|
```ruby
|
2234
2714
|
require 'glimmer-dsl-libui'
|
@@ -3110,9 +3590,9 @@ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/
|
|
3110
3590
|
ruby -r glimmer-dsl-libui -e "require 'examples/basic_scrolling_area'"
|
3111
3591
|
```
|
3112
3592
|
|
3113
|
-
Mac |
|
3114
|
-
|
3115
|
-
![glimmer-dsl-libui-mac-dynamic-area.png](images/glimmer-dsl-libui-mac-basic-scrolling-area.png) ![glimmer-dsl-libui-mac-dynamic-area-updated.png](images/glimmer-dsl-libui-mac-basic-scrolling-area-scrolled.png)
|
3593
|
+
Mac | Linux
|
3594
|
+
----|------
|
3595
|
+
![glimmer-dsl-libui-mac-dynamic-area.png](images/glimmer-dsl-libui-mac-basic-scrolling-area.png) ![glimmer-dsl-libui-mac-dynamic-area-updated.png](images/glimmer-dsl-libui-mac-basic-scrolling-area-scrolled.png) | ![glimmer-dsl-libui-linux-dynamic-area.png](images/glimmer-dsl-libui-linux-basic-scrolling-area.png) ![glimmer-dsl-libui-linux-dynamic-area-updated.png](images/glimmer-dsl-libui-linux-basic-scrolling-area-scrolled.png)
|
3116
3596
|
|
3117
3597
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
3118
3598
|
|
@@ -4433,52 +4913,102 @@ window('Area Gallery', 400, 400) {
|
|
4433
4913
|
}.show
|
4434
4914
|
```
|
4435
4915
|
|
4436
|
-
####
|
4916
|
+
#### Button Counter
|
4437
4917
|
|
4438
|
-
[examples/
|
4918
|
+
[examples/button_counter.rb](examples/button_counter.rb)
|
4439
4919
|
|
4440
4920
|
Run with this command from the root of the project if you cloned the project:
|
4441
4921
|
|
4442
4922
|
```
|
4443
|
-
ruby -r './lib/glimmer-dsl-libui' examples/
|
4923
|
+
ruby -r './lib/glimmer-dsl-libui' examples/button_counter.rb
|
4444
4924
|
```
|
4445
4925
|
|
4446
4926
|
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
4447
4927
|
|
4448
4928
|
```
|
4449
|
-
ruby -r glimmer-dsl-libui -e "require 'examples/
|
4929
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/button_counter'"
|
4450
4930
|
```
|
4451
4931
|
|
4452
|
-
Mac |
|
4453
|
-
|
4454
|
-
![glimmer-dsl-libui-mac-
|
4932
|
+
Mac | Linux
|
4933
|
+
----|------
|
4934
|
+
![glimmer-dsl-libui-mac-button-counter.png](images/glimmer-dsl-libui-mac-button-counter.png) | ![glimmer-dsl-libui-linux-button-counter.png](images/glimmer-dsl-libui-linux-button-counter.png)
|
4455
4935
|
|
4456
4936
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
4457
4937
|
|
4458
4938
|
```ruby
|
4459
4939
|
require 'glimmer-dsl-libui'
|
4460
4940
|
|
4461
|
-
class
|
4941
|
+
class ButtonCounter
|
4462
4942
|
include Glimmer
|
4463
|
-
|
4464
|
-
|
4465
|
-
|
4466
|
-
SHAPE_MIN_SIZE = 15
|
4467
|
-
SHAPE_MAX_SIZE = 75
|
4468
|
-
MARGIN_WIDTH = 55
|
4469
|
-
MARGIN_HEIGHT = 155
|
4470
|
-
TIME_MAX_EASY = 4
|
4471
|
-
TIME_MAX_MEDIUM = 3
|
4472
|
-
TIME_MAX_HARD = 2
|
4473
|
-
TIME_MAX_INSANE = 1
|
4474
|
-
|
4475
|
-
attr_accessor :score
|
4476
|
-
|
4943
|
+
|
4944
|
+
attr_accessor :count
|
4945
|
+
|
4477
4946
|
def initialize
|
4478
|
-
@
|
4479
|
-
|
4480
|
-
|
4481
|
-
|
4947
|
+
@count = 0
|
4948
|
+
end
|
4949
|
+
|
4950
|
+
def launch
|
4951
|
+
window('Hello, Button!') {
|
4952
|
+
button {
|
4953
|
+
text <= [self, :count, on_read: ->(count) {"Count: #{count}"}] # data-bind button text to self count, converting to string on read.
|
4954
|
+
|
4955
|
+
on_clicked do
|
4956
|
+
self.count += 1
|
4957
|
+
end
|
4958
|
+
}
|
4959
|
+
}.show
|
4960
|
+
end
|
4961
|
+
end
|
4962
|
+
|
4963
|
+
ButtonCounter.new.launch
|
4964
|
+
```
|
4965
|
+
|
4966
|
+
#### Color The Circles
|
4967
|
+
|
4968
|
+
[examples/color_the_circles.rb](examples/color_the_circles.rb)
|
4969
|
+
|
4970
|
+
Run with this command from the root of the project if you cloned the project:
|
4971
|
+
|
4972
|
+
```
|
4973
|
+
ruby -r './lib/glimmer-dsl-libui' examples/color_the_circles.rb
|
4974
|
+
```
|
4975
|
+
|
4976
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
4977
|
+
|
4978
|
+
```
|
4979
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/color_the_circles'"
|
4980
|
+
```
|
4981
|
+
|
4982
|
+
Mac | Windows | Linux
|
4983
|
+
----|---------|------
|
4984
|
+
![glimmer-dsl-libui-mac-color-the-circles.png](images/glimmer-dsl-libui-mac-color-the-circles.png) ![glimmer-dsl-libui-mac-color-the-circles-lost.png](images/glimmer-dsl-libui-mac-color-the-circles-lost.png) ![glimmer-dsl-libui-mac-color-the-circles-won.png](images/glimmer-dsl-libui-mac-color-the-circles-won.png) | ![glimmer-dsl-libui-windows-color-the-circles.png](images/glimmer-dsl-libui-windows-color-the-circles.png) ![glimmer-dsl-libui-windows-color-the-circles-lost.png](images/glimmer-dsl-libui-windows-color-the-circles-lost.png) ![glimmer-dsl-libui-windows-color-the-circles-won.png](images/glimmer-dsl-libui-windows-color-the-circles-won.png) | ![glimmer-dsl-libui-linux-color-the-circles.png](images/glimmer-dsl-libui-linux-color-the-circles.png) ![glimmer-dsl-libui-linux-color-the-circles-lost.png](images/glimmer-dsl-libui-linux-color-the-circles-lost.png) ![glimmer-dsl-libui-linux-color-the-circles-won.png](images/glimmer-dsl-libui-linux-color-the-circles-won.png)
|
4985
|
+
|
4986
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
4987
|
+
|
4988
|
+
```ruby
|
4989
|
+
require 'glimmer-dsl-libui'
|
4990
|
+
|
4991
|
+
class ColorTheCircles
|
4992
|
+
include Glimmer
|
4993
|
+
|
4994
|
+
WINDOW_WIDTH = 800
|
4995
|
+
WINDOW_HEIGHT = 600
|
4996
|
+
SHAPE_MIN_SIZE = 15
|
4997
|
+
SHAPE_MAX_SIZE = 75
|
4998
|
+
MARGIN_WIDTH = 55
|
4999
|
+
MARGIN_HEIGHT = 155
|
5000
|
+
TIME_MAX_EASY = 4
|
5001
|
+
TIME_MAX_MEDIUM = 3
|
5002
|
+
TIME_MAX_HARD = 2
|
5003
|
+
TIME_MAX_INSANE = 1
|
5004
|
+
|
5005
|
+
attr_accessor :score
|
5006
|
+
|
5007
|
+
def initialize
|
5008
|
+
@circles_data = []
|
5009
|
+
@score = 0
|
5010
|
+
@time_max = TIME_MAX_HARD
|
5011
|
+
@game_over = false
|
4482
5012
|
register_observers
|
4483
5013
|
setup_circle_factory
|
4484
5014
|
end
|
@@ -5293,7 +5823,96 @@ Mac | Windows | Linux
|
|
5293
5823
|
----|---------|------
|
5294
5824
|
![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)
|
5295
5825
|
|
5296
|
-
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)):
|
5297
5916
|
|
5298
5917
|
```ruby
|
5299
5918
|
require 'glimmer-dsl-libui'
|
@@ -5395,7 +6014,102 @@ window('Dynamic Area', 240, 600) {
|
|
5395
6014
|
}.show
|
5396
6015
|
```
|
5397
6016
|
|
5398
|
-
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)):
|
5399
6113
|
|
5400
6114
|
```ruby
|
5401
6115
|
require 'glimmer-dsl-libui'
|
@@ -5487,7 +6201,7 @@ window('Dynamic Area', 240, 600) {
|
|
5487
6201
|
}
|
5488
6202
|
|
5489
6203
|
area {
|
5490
|
-
@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
|
5491
6205
|
fill r: @red_spinbox.value, g: @green_spinbox.value, b: @blue_spinbox.value, a: @alpha_spinbox.value / 100.0
|
5492
6206
|
}
|
5493
6207
|
}
|
@@ -5629,7 +6343,120 @@ Mac | Windows | Linux
|
|
5629
6343
|
----|---------|------
|
5630
6344
|
![glimmer-dsl-libui-mac-form-table.png](images/glimmer-dsl-libui-mac-form-table.png) ![glimmer-dsl-libui-mac-form-table-contact-entered.png](images/glimmer-dsl-libui-mac-form-table-contact-entered.png) ![glimmer-dsl-libui-mac-form-table-filtered.png](images/glimmer-dsl-libui-mac-form-table-filtered.png) | ![glimmer-dsl-libui-windows-form-table.png](images/glimmer-dsl-libui-windows-form-table.png) ![glimmer-dsl-libui-windows-form-table-contact-entered.png](images/glimmer-dsl-libui-windows-form-table-contact-entered.png) ![glimmer-dsl-libui-windows-form-table-filtered.png](images/glimmer-dsl-libui-windows-form-table-filtered.png) | ![glimmer-dsl-libui-linux-form-table.png](images/glimmer-dsl-libui-linux-form-table.png) ![glimmer-dsl-libui-linux-form-table-contact-entered.png](images/glimmer-dsl-libui-linux-form-table-contact-entered.png) ![glimmer-dsl-libui-linux-form-table-filtered.png](images/glimmer-dsl-libui-linux-form-table-filtered.png)
|
5631
6345
|
|
5632
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
6346
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
6347
|
+
|
6348
|
+
```ruby
|
6349
|
+
require 'glimmer-dsl-libui'
|
6350
|
+
|
6351
|
+
class FormTable
|
6352
|
+
include Glimmer
|
6353
|
+
|
6354
|
+
attr_accessor :name, :email, :phone, :city, :state, :filter_value
|
6355
|
+
|
6356
|
+
def initialize
|
6357
|
+
@data = [
|
6358
|
+
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO', '80014'],
|
6359
|
+
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA', '02101'],
|
6360
|
+
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL', '60007'],
|
6361
|
+
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA', '98101'],
|
6362
|
+
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA', '90001'],
|
6363
|
+
]
|
6364
|
+
end
|
6365
|
+
|
6366
|
+
def launch
|
6367
|
+
window('Contacts', 600, 600) { |w|
|
6368
|
+
margined true
|
6369
|
+
|
6370
|
+
vertical_box {
|
6371
|
+
form {
|
6372
|
+
stretchy false
|
6373
|
+
|
6374
|
+
entry {
|
6375
|
+
label 'Name'
|
6376
|
+
text <=> [self, :name]
|
6377
|
+
}
|
6378
|
+
|
6379
|
+
entry {
|
6380
|
+
label 'Email'
|
6381
|
+
text <=> [self, :email]
|
6382
|
+
}
|
6383
|
+
|
6384
|
+
entry {
|
6385
|
+
label 'Phone'
|
6386
|
+
text <=> [self, :phone]
|
6387
|
+
}
|
6388
|
+
|
6389
|
+
entry {
|
6390
|
+
label 'City'
|
6391
|
+
text <=> [self, :city]
|
6392
|
+
}
|
6393
|
+
|
6394
|
+
entry {
|
6395
|
+
label 'State'
|
6396
|
+
text <=> [self, :state]
|
6397
|
+
}
|
6398
|
+
}
|
6399
|
+
|
6400
|
+
button('Save Contact') {
|
6401
|
+
stretchy false
|
6402
|
+
|
6403
|
+
on_clicked do
|
6404
|
+
new_row = [name, email, phone, city, state]
|
6405
|
+
if new_row.include?('')
|
6406
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
6407
|
+
else
|
6408
|
+
@data << new_row # automatically inserts a row into the table due to implicit data-binding
|
6409
|
+
@unfiltered_data = @data.dup
|
6410
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
6411
|
+
self.email = ''
|
6412
|
+
self.phone = ''
|
6413
|
+
self.city = ''
|
6414
|
+
self.state = ''
|
6415
|
+
end
|
6416
|
+
end
|
6417
|
+
}
|
6418
|
+
|
6419
|
+
search_entry {
|
6420
|
+
stretchy false
|
6421
|
+
text <=> [self, :filter_value, # bidirectional data-binding of text to self.filter_value with after_write option
|
6422
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
6423
|
+
@unfiltered_data ||= @data.dup
|
6424
|
+
# Unfilter first to remove any previous filters
|
6425
|
+
@data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
|
6426
|
+
# Now, apply filter if entered
|
6427
|
+
unless filter_value.empty?
|
6428
|
+
@data.filter! do |row_data| # affects table indirectly through implicit data-binding
|
6429
|
+
row_data.any? do |cell|
|
6430
|
+
cell.to_s.downcase.include?(filter_value.downcase)
|
6431
|
+
end
|
6432
|
+
end
|
6433
|
+
end
|
6434
|
+
}
|
6435
|
+
]
|
6436
|
+
}
|
6437
|
+
|
6438
|
+
table {
|
6439
|
+
text_column('Name')
|
6440
|
+
text_column('Email')
|
6441
|
+
text_column('Phone')
|
6442
|
+
text_column('City')
|
6443
|
+
text_column('State')
|
6444
|
+
|
6445
|
+
cell_rows @data # implicit data-binding
|
6446
|
+
|
6447
|
+
on_changed do |row, type, row_data|
|
6448
|
+
puts "Row #{row} #{type}: #{row_data}"
|
6449
|
+
end
|
6450
|
+
}
|
6451
|
+
}
|
6452
|
+
}.show
|
6453
|
+
end
|
6454
|
+
end
|
6455
|
+
|
6456
|
+
FormTable.new.launch
|
6457
|
+
```
|
6458
|
+
|
6459
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
5633
6460
|
|
5634
6461
|
```ruby
|
5635
6462
|
require 'glimmer-dsl-libui'
|
@@ -6072,50 +6899,170 @@ UI.main
|
|
6072
6899
|
UI.quit
|
6073
6900
|
```
|
6074
6901
|
|
6075
|
-
[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)):
|
6076
6903
|
|
6077
6904
|
```ruby
|
6078
6905
|
# https://github.com/jamescook/libui-ruby/blob/master/example/histogram.rb
|
6079
6906
|
|
6080
6907
|
require 'glimmer-dsl-libui'
|
6081
6908
|
|
6082
|
-
|
6083
|
-
|
6084
|
-
|
6085
|
-
|
6086
|
-
|
6087
|
-
|
6088
|
-
|
6089
|
-
|
6090
|
-
|
6091
|
-
|
6092
|
-
|
6093
|
-
|
6094
|
-
|
6095
|
-
|
6096
|
-
|
6097
|
-
end
|
6098
|
-
|
6099
|
-
def point_locations(width, height)
|
6100
|
-
xincr = width / 9.0 # 10 - 1 to make the last point be at the end
|
6101
|
-
yincr = height / 100.0
|
6102
|
-
|
6103
|
-
@datapoints.each_with_index.map do |value, i|
|
6104
|
-
val = 100 - value
|
6105
|
-
[xincr * i, yincr * val]
|
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
|
6106
6924
|
end
|
6107
|
-
|
6108
|
-
|
6109
|
-
|
6110
|
-
|
6111
|
-
|
6112
|
-
|
6113
|
-
|
6114
|
-
|
6115
|
-
|
6116
|
-
|
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]
|
6117
6939
|
end
|
6118
|
-
|
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)):
|
7022
|
+
|
7023
|
+
```ruby
|
7024
|
+
# https://github.com/jamescook/libui-ruby/blob/master/example/histogram.rb
|
7025
|
+
|
7026
|
+
require 'glimmer-dsl-libui'
|
7027
|
+
|
7028
|
+
include Glimmer
|
7029
|
+
|
7030
|
+
X_OFF_LEFT = 20
|
7031
|
+
Y_OFF_TOP = 20
|
7032
|
+
X_OFF_RIGHT = 20
|
7033
|
+
Y_OFF_BOTTOM = 20
|
7034
|
+
POINT_RADIUS = 5
|
7035
|
+
COLOR_BLUE = Glimmer::LibUI.interpret_color(0x1E90FF)
|
7036
|
+
|
7037
|
+
@datapoints = 10.times.map {Random.new.rand(90)}
|
7038
|
+
@color = COLOR_BLUE
|
7039
|
+
|
7040
|
+
def graph_size(area_width, area_height)
|
7041
|
+
graph_width = area_width - X_OFF_LEFT - X_OFF_RIGHT
|
7042
|
+
graph_height = area_height - Y_OFF_TOP - Y_OFF_BOTTOM
|
7043
|
+
[graph_width, graph_height]
|
7044
|
+
end
|
7045
|
+
|
7046
|
+
def point_locations(width, height)
|
7047
|
+
xincr = width / 9.0 # 10 - 1 to make the last point be at the end
|
7048
|
+
yincr = height / 100.0
|
7049
|
+
|
7050
|
+
@datapoints.each_with_index.map do |value, i|
|
7051
|
+
val = 100 - value
|
7052
|
+
[xincr * i, yincr * val]
|
7053
|
+
end
|
7054
|
+
end
|
7055
|
+
|
7056
|
+
# method-based custom control representing a graph path
|
7057
|
+
def graph_path(width, height, should_extend, &block)
|
7058
|
+
locations = point_locations(width, height).flatten
|
7059
|
+
path {
|
7060
|
+
if should_extend
|
7061
|
+
polygon(locations + [width, height, 0, height])
|
7062
|
+
else
|
7063
|
+
polyline(locations)
|
7064
|
+
end
|
7065
|
+
|
6119
7066
|
# apply a transform to the coordinate space for this path so (0, 0) is the top-left corner of the graph
|
6120
7067
|
transform {
|
6121
7068
|
translate X_OFF_LEFT, Y_OFF_TOP
|
@@ -6144,11 +7091,12 @@ window('histogram example', 640, 480) {
|
|
6144
7091
|
}
|
6145
7092
|
end
|
6146
7093
|
|
6147
|
-
|
7094
|
+
color_button { |cb|
|
6148
7095
|
stretchy false
|
6149
7096
|
color COLOR_BLUE
|
6150
7097
|
|
6151
7098
|
on_changed do
|
7099
|
+
@color = cb.color
|
6152
7100
|
@area.queue_redraw_all
|
6153
7101
|
end
|
6154
7102
|
}
|
@@ -6156,31 +7104,27 @@ window('histogram example', 640, 480) {
|
|
6156
7104
|
|
6157
7105
|
@area = area {
|
6158
7106
|
on_draw do |area_draw_params|
|
6159
|
-
|
6160
|
-
rectangle(0, 0, area_draw_params[:area_width], area_draw_params[:area_height])
|
6161
|
-
|
7107
|
+
rectangle(0, 0, area_draw_params[:area_width], area_draw_params[:area_height]) {
|
6162
7108
|
fill 0xFFFFFF
|
6163
7109
|
}
|
6164
7110
|
|
6165
7111
|
graph_width, graph_height = *graph_size(area_draw_params[:area_width], area_draw_params[:area_height])
|
6166
7112
|
|
6167
|
-
|
6168
|
-
|
6169
|
-
|
6170
|
-
line(X_OFF_LEFT + graph_width, Y_OFF_TOP + graph_height)
|
6171
|
-
}
|
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)
|
6172
7116
|
|
6173
7117
|
stroke 0x000000, thickness: 2, miter_limit: 10
|
6174
7118
|
}
|
6175
7119
|
|
6176
7120
|
# now create the fill for the graph below the graph line
|
6177
7121
|
graph_path(graph_width, graph_height, true) {
|
6178
|
-
fill @
|
7122
|
+
fill @color.merge(a: 0.5)
|
6179
7123
|
}
|
6180
7124
|
|
6181
7125
|
# now draw the histogram line
|
6182
7126
|
graph_path(graph_width, graph_height, false) {
|
6183
|
-
stroke @
|
7127
|
+
stroke @color.merge(thickness: 2, miter_limit: 10)
|
6184
7128
|
}
|
6185
7129
|
end
|
6186
7130
|
}
|
@@ -6208,7 +7152,259 @@ Mac | Windows | Linux
|
|
6208
7152
|
----|---------|------
|
6209
7153
|
![glimmer-dsl-libui-mac-login.png](images/glimmer-dsl-libui-mac-login.png) ![glimmer-dsl-libui-mac-login-logged-in.png](images/glimmer-dsl-libui-mac-login-logged-in.png) | ![glimmer-dsl-libui-windows-login.png](images/glimmer-dsl-libui-windows-login.png) ![glimmer-dsl-libui-windows-login-logged-in.png](images/glimmer-dsl-libui-windows-login-logged-in.png) | ![glimmer-dsl-libui-linux-login.png](images/glimmer-dsl-libui-linux-login.png) ![glimmer-dsl-libui-linux-login-logged-in.png](images/glimmer-dsl-libui-linux-login-logged-in.png)
|
6210
7154
|
|
6211
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
7155
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
7156
|
+
|
7157
|
+
```ruby
|
7158
|
+
require 'glimmer-dsl-libui'
|
7159
|
+
|
7160
|
+
class Login
|
7161
|
+
include Glimmer
|
7162
|
+
|
7163
|
+
attr_accessor :username, :password, :logged_in
|
7164
|
+
|
7165
|
+
def launch
|
7166
|
+
window('Login') {
|
7167
|
+
margined true
|
7168
|
+
|
7169
|
+
vertical_box {
|
7170
|
+
form {
|
7171
|
+
entry {
|
7172
|
+
label 'Username:'
|
7173
|
+
text <=> [self, :username]
|
7174
|
+
enabled <= [self, :logged_in, on_read: :!]
|
7175
|
+
}
|
7176
|
+
|
7177
|
+
password_entry {
|
7178
|
+
label 'Password:'
|
7179
|
+
text <=> [self, :password]
|
7180
|
+
enabled <= [self, :logged_in, on_read: :!]
|
7181
|
+
}
|
7182
|
+
}
|
7183
|
+
|
7184
|
+
horizontal_box {
|
7185
|
+
button('Login') {
|
7186
|
+
enabled <= [self, :logged_in, on_read: :!]
|
7187
|
+
|
7188
|
+
on_clicked do
|
7189
|
+
self.logged_in = true
|
7190
|
+
end
|
7191
|
+
}
|
7192
|
+
|
7193
|
+
button('Logout') {
|
7194
|
+
enabled <= [self, :logged_in]
|
7195
|
+
|
7196
|
+
on_clicked do
|
7197
|
+
self.logged_in = false
|
7198
|
+
self.username = ''
|
7199
|
+
self.password = ''
|
7200
|
+
end
|
7201
|
+
}
|
7202
|
+
}
|
7203
|
+
}
|
7204
|
+
}.show
|
7205
|
+
end
|
7206
|
+
end
|
7207
|
+
|
7208
|
+
Login.new.launch
|
7209
|
+
```
|
7210
|
+
|
7211
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (with [data-binding](#data-binding)):
|
7212
|
+
|
7213
|
+
```ruby
|
7214
|
+
require 'glimmer-dsl-libui'
|
7215
|
+
|
7216
|
+
class Login
|
7217
|
+
include Glimmer
|
7218
|
+
|
7219
|
+
attr_accessor :username, :password, :logged_in
|
7220
|
+
|
7221
|
+
def logged_out
|
7222
|
+
!logged_in
|
7223
|
+
end
|
7224
|
+
|
7225
|
+
def launch
|
7226
|
+
window('Login') {
|
7227
|
+
margined true
|
7228
|
+
|
7229
|
+
vertical_box {
|
7230
|
+
form {
|
7231
|
+
entry {
|
7232
|
+
label 'Username:'
|
7233
|
+
text <=> [self, :username]
|
7234
|
+
enabled <= [self, :logged_out, computed_by: :logged_in] # computed_by option ensures being notified of changes to logged_in
|
7235
|
+
}
|
7236
|
+
|
7237
|
+
password_entry {
|
7238
|
+
label 'Password:'
|
7239
|
+
text <=> [self, :password]
|
7240
|
+
enabled <= [self, :logged_out, computed_by: :logged_in]
|
7241
|
+
}
|
7242
|
+
}
|
7243
|
+
|
7244
|
+
horizontal_box {
|
7245
|
+
button('Login') {
|
7246
|
+
enabled <= [self, :logged_out, computed_by: :logged_in]
|
7247
|
+
|
7248
|
+
on_clicked do
|
7249
|
+
self.logged_in = true
|
7250
|
+
end
|
7251
|
+
}
|
7252
|
+
|
7253
|
+
button('Logout') {
|
7254
|
+
enabled <= [self, :logged_in]
|
7255
|
+
|
7256
|
+
on_clicked do
|
7257
|
+
self.logged_in = false
|
7258
|
+
self.username = ''
|
7259
|
+
self.password = ''
|
7260
|
+
end
|
7261
|
+
}
|
7262
|
+
}
|
7263
|
+
}
|
7264
|
+
}.show
|
7265
|
+
end
|
7266
|
+
end
|
7267
|
+
|
7268
|
+
Login.new.launch
|
7269
|
+
```
|
7270
|
+
|
7271
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (with [data-binding](#data-binding)):
|
7272
|
+
|
7273
|
+
```ruby
|
7274
|
+
require 'glimmer-dsl-libui'
|
7275
|
+
|
7276
|
+
class Login
|
7277
|
+
include Glimmer
|
7278
|
+
|
7279
|
+
attr_accessor :username, :password
|
7280
|
+
attr_reader :logged_in
|
7281
|
+
|
7282
|
+
def logged_in=(value)
|
7283
|
+
@logged_in = value
|
7284
|
+
self.logged_out = !value # calling logged_out= method notifies logged_out observers
|
7285
|
+
end
|
7286
|
+
|
7287
|
+
def logged_out=(value)
|
7288
|
+
self.logged_in = !value unless logged_in == !value
|
7289
|
+
end
|
7290
|
+
|
7291
|
+
def logged_out
|
7292
|
+
!logged_in
|
7293
|
+
end
|
7294
|
+
|
7295
|
+
def launch
|
7296
|
+
window('Login') {
|
7297
|
+
margined true
|
7298
|
+
|
7299
|
+
vertical_box {
|
7300
|
+
form {
|
7301
|
+
entry {
|
7302
|
+
label 'Username:'
|
7303
|
+
text <=> [self, :username]
|
7304
|
+
enabled <= [self, :logged_out]
|
7305
|
+
}
|
7306
|
+
|
7307
|
+
password_entry {
|
7308
|
+
label 'Password:'
|
7309
|
+
text <=> [self, :password]
|
7310
|
+
enabled <= [self, :logged_out]
|
7311
|
+
}
|
7312
|
+
}
|
7313
|
+
|
7314
|
+
horizontal_box {
|
7315
|
+
button('Login') {
|
7316
|
+
enabled <= [self, :logged_out]
|
7317
|
+
|
7318
|
+
on_clicked do
|
7319
|
+
self.logged_in = true
|
7320
|
+
end
|
7321
|
+
}
|
7322
|
+
|
7323
|
+
button('Logout') {
|
7324
|
+
enabled <= [self, :logged_in]
|
7325
|
+
|
7326
|
+
on_clicked do
|
7327
|
+
self.logged_in = false
|
7328
|
+
self.username = ''
|
7329
|
+
self.password = ''
|
7330
|
+
end
|
7331
|
+
}
|
7332
|
+
}
|
7333
|
+
}
|
7334
|
+
}.show
|
7335
|
+
end
|
7336
|
+
end
|
7337
|
+
|
7338
|
+
Login.new.launch
|
7339
|
+
```
|
7340
|
+
|
7341
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 4 (with [data-binding](#data-binding)):
|
7342
|
+
|
7343
|
+
```ruby
|
7344
|
+
require 'glimmer-dsl-libui'
|
7345
|
+
|
7346
|
+
class Login
|
7347
|
+
include Glimmer
|
7348
|
+
|
7349
|
+
attr_accessor :username, :password
|
7350
|
+
attr_reader :logged_in
|
7351
|
+
|
7352
|
+
def logged_in=(value)
|
7353
|
+
@logged_in = value
|
7354
|
+
notify_observers(:logged_out) # manually notify observers of logged_out upon logged_in changes; this method comes automatically from enhancement as Glimmer::DataBinding::ObservableModel via data-binding
|
7355
|
+
end
|
7356
|
+
|
7357
|
+
def logged_out
|
7358
|
+
!logged_in
|
7359
|
+
end
|
7360
|
+
|
7361
|
+
def launch
|
7362
|
+
window('Login') {
|
7363
|
+
margined true
|
7364
|
+
|
7365
|
+
vertical_box {
|
7366
|
+
form {
|
7367
|
+
entry {
|
7368
|
+
label 'Username:'
|
7369
|
+
text <=> [self, :username]
|
7370
|
+
enabled <= [self, :logged_out]
|
7371
|
+
}
|
7372
|
+
|
7373
|
+
password_entry {
|
7374
|
+
label 'Password:'
|
7375
|
+
text <=> [self, :password]
|
7376
|
+
enabled <= [self, :logged_out]
|
7377
|
+
}
|
7378
|
+
}
|
7379
|
+
|
7380
|
+
horizontal_box {
|
7381
|
+
button('Login') {
|
7382
|
+
enabled <= [self, :logged_out]
|
7383
|
+
|
7384
|
+
on_clicked do
|
7385
|
+
self.logged_in = true
|
7386
|
+
end
|
7387
|
+
}
|
7388
|
+
|
7389
|
+
button('Logout') {
|
7390
|
+
enabled <= [self, :logged_in]
|
7391
|
+
|
7392
|
+
on_clicked do
|
7393
|
+
self.logged_in = false
|
7394
|
+
self.username = ''
|
7395
|
+
self.password = ''
|
7396
|
+
end
|
7397
|
+
}
|
7398
|
+
}
|
7399
|
+
}
|
7400
|
+
}.show
|
7401
|
+
end
|
7402
|
+
end
|
7403
|
+
|
7404
|
+
Login.new.launch
|
7405
|
+
```
|
7406
|
+
|
7407
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 5 (without [data-binding](#data-binding)):
|
6212
7408
|
|
6213
7409
|
```ruby
|
6214
7410
|
require 'glimmer-dsl-libui'
|
@@ -6276,7 +7472,102 @@ Mac | Windows | Linux
|
|
6276
7472
|
----|---------|------
|
6277
7473
|
![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png) | ![glimmer-dsl-libui-windows-method-based-custom-keyword.png](images/glimmer-dsl-libui-windows-method-based-custom-keyword.png) | ![glimmer-dsl-libui-linux-method-based-custom-keyword.png](images/glimmer-dsl-libui-linux-method-based-custom-keyword.png)
|
6278
7474
|
|
6279
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
7475
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
7476
|
+
|
7477
|
+
```ruby
|
7478
|
+
require 'glimmer-dsl-libui'
|
7479
|
+
require 'facets'
|
7480
|
+
|
7481
|
+
include Glimmer
|
7482
|
+
|
7483
|
+
Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
|
7484
|
+
|
7485
|
+
def form_field(model, attribute)
|
7486
|
+
attribute = attribute.to_s
|
7487
|
+
entry { |e|
|
7488
|
+
label attribute.underscore.split('_').map(&:capitalize).join(' ')
|
7489
|
+
text <=> [model, attribute]
|
7490
|
+
}
|
7491
|
+
end
|
7492
|
+
|
7493
|
+
def address_form(address)
|
7494
|
+
form {
|
7495
|
+
form_field(address, :street)
|
7496
|
+
form_field(address, :p_o_box)
|
7497
|
+
form_field(address, :city)
|
7498
|
+
form_field(address, :state)
|
7499
|
+
form_field(address, :zip_code)
|
7500
|
+
}
|
7501
|
+
end
|
7502
|
+
|
7503
|
+
def label_pair(model, attribute, value)
|
7504
|
+
horizontal_box {
|
7505
|
+
label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
|
7506
|
+
label(value.to_s) {
|
7507
|
+
text <= [model, attribute]
|
7508
|
+
}
|
7509
|
+
}
|
7510
|
+
end
|
7511
|
+
|
7512
|
+
def address(address)
|
7513
|
+
vertical_box {
|
7514
|
+
address.each_pair do |attribute, value|
|
7515
|
+
label_pair(address, attribute, value)
|
7516
|
+
end
|
7517
|
+
}
|
7518
|
+
end
|
7519
|
+
|
7520
|
+
address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
|
7521
|
+
address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
|
7522
|
+
|
7523
|
+
window('Method-Based Custom Keyword') {
|
7524
|
+
margined true
|
7525
|
+
|
7526
|
+
horizontal_box {
|
7527
|
+
vertical_box {
|
7528
|
+
label('Address 1') {
|
7529
|
+
stretchy false
|
7530
|
+
}
|
7531
|
+
|
7532
|
+
address_form(address1)
|
7533
|
+
|
7534
|
+
horizontal_separator {
|
7535
|
+
stretchy false
|
7536
|
+
}
|
7537
|
+
|
7538
|
+
label('Address 1 (Saved)') {
|
7539
|
+
stretchy false
|
7540
|
+
}
|
7541
|
+
|
7542
|
+
address(address1)
|
7543
|
+
}
|
7544
|
+
|
7545
|
+
vertical_separator {
|
7546
|
+
stretchy false
|
7547
|
+
}
|
7548
|
+
|
7549
|
+
vertical_box {
|
7550
|
+
label('Address 2') {
|
7551
|
+
stretchy false
|
7552
|
+
}
|
7553
|
+
|
7554
|
+
address_form(address2)
|
7555
|
+
|
7556
|
+
horizontal_separator {
|
7557
|
+
stretchy false
|
7558
|
+
}
|
7559
|
+
|
7560
|
+
label('Address 2 (Saved)') {
|
7561
|
+
stretchy false
|
7562
|
+
}
|
7563
|
+
|
7564
|
+
address(address2)
|
7565
|
+
}
|
7566
|
+
}
|
7567
|
+
}.show
|
7568
|
+
```
|
7569
|
+
|
7570
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
6280
7571
|
|
6281
7572
|
```ruby
|
6282
7573
|
require 'glimmer-dsl-libui'
|
@@ -7222,7 +8513,140 @@ Mac | Windows | Linux
|
|
7222
8513
|
----|---------|------
|
7223
8514
|
![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)
|
7224
8515
|
|
7225
|
-
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)):
|
7226
8650
|
|
7227
8651
|
```ruby
|
7228
8652
|
require 'glimmer-dsl-libui'
|