glimmer-dsl-libui 0.4.0 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -1
- data/README.md +998 -47
- 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/color_the_circles.rb +2 -2
- data/examples/form.rb +42 -30
- data/examples/form2.rb +37 -0
- data/examples/form_table.rb +100 -85
- data/examples/form_table2.rb +95 -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/presenter/grid.rb +5 -3
- data/examples/snake.rb +4 -4
- data/examples/tetris.rb +12 -12
- 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/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 +12 -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.4
|
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
|
|
@@ -233,6 +233,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
|
|
233
233
|
- [Area Transform Matrix](#area-transform-matrix)
|
234
234
|
- [Smart Defaults and Conventions](#smart-defaults-and-conventions)
|
235
235
|
- [Custom Keywords](#custom-keywords)
|
236
|
+
- [Observer Pattern](#observer-pattern)
|
236
237
|
- [Data-Binding](#data-binding)
|
237
238
|
- [API Gotchas](#api-gotchas)
|
238
239
|
- [Original API](#original-api)
|
@@ -263,6 +264,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
|
|
263
264
|
- [Basic Draw Text](#basic-draw-text)
|
264
265
|
- [Advanced Examples](#advanced-examples)
|
265
266
|
- [Area Gallery](#area-gallery)
|
267
|
+
- [Button Counter](#button-counter)
|
266
268
|
- [Color The Circles](#color-the-circles)
|
267
269
|
- [Control Gallery](#control-gallery)
|
268
270
|
- [Custom Draw Text](#custom-draw-text)
|
@@ -371,7 +373,7 @@ gem install glimmer-dsl-libui
|
|
371
373
|
Or install via Bundler `Gemfile`:
|
372
374
|
|
373
375
|
```ruby
|
374
|
-
gem 'glimmer-dsl-libui', '~> 0.4.
|
376
|
+
gem 'glimmer-dsl-libui', '~> 0.4.4'
|
375
377
|
```
|
376
378
|
|
377
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.
|
@@ -777,9 +779,9 @@ Check [examples/dynamic_area.rb](#dynamic-area) for a more detailed semi-declara
|
|
777
779
|
- `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.
|
778
780
|
- `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.
|
779
781
|
|
780
|
-
Mac |
|
781
|
-
|
782
|
-
![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)
|
782
|
+
Mac |Linux
|
783
|
+
----|-----
|
784
|
+
![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)
|
783
785
|
|
784
786
|
Check [examples/basic_scrolling_area.rb](#basic-scrolling-area) for a more detailed example.
|
785
787
|
|
@@ -875,7 +877,7 @@ Given that it is very new and is not a [libui](https://github.com/andlabs/libui)
|
|
875
877
|
- Including an `image` inside an `area` `on_draw` listener improves performance due to not retaining pixel/line data in memory.
|
876
878
|
- Supplying `width` and `height` (2nd and 3rd arguments) greatly improves performance when shrinking image.
|
877
879
|
|
878
|
-
Currently, it is recommended to use `image` with very small `width` and `height` values only.
|
880
|
+
Currently, it is recommended to use `image` with very small `width` and `height` values only (e.g. 24x24).
|
879
881
|
|
880
882
|
Setting a [`transform` `matrix`](#area-transform-matrix) is supported under `image` just like it is under `path` and `text` inside `area`.
|
881
883
|
|
@@ -1031,6 +1033,8 @@ window('Basic Image', 96, 96) {
|
|
1031
1033
|
|
1032
1034
|
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.
|
1033
1035
|
|
1036
|
+
![linux table image](images/glimmer-dsl-libui-linux-basic-table-image.png)
|
1037
|
+
|
1034
1038
|
Check out [examples/basic_image.rb](#basic-image) (all versions) for examples of using `image` Glimmer custom control.
|
1035
1039
|
|
1036
1040
|
#### Colors
|
@@ -1321,23 +1325,72 @@ window('Method-Based Custom Keyword') {
|
|
1321
1325
|
|
1322
1326
|
![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
|
1323
1327
|
|
1324
|
-
###
|
1328
|
+
### Observer Pattern
|
1325
1329
|
|
1326
|
-
[
|
1330
|
+
The [Observer Design Pattern](https://en.wikipedia.org/wiki/Observer_pattern) (a.k.a. Observer Pattern) is fundamental to building GUIs (Graphical User Interfaces) following the [MVC (Model View Controller) Architectural Pattern](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) or any of its variations like [MVP (Model View Presenter)](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter). In the original Smalltalk-MVC, the View observes the Model for changes and updates itself accordingly.
|
1327
1331
|
|
1328
|
-
|
1332
|
+
![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)
|
1333
|
+
|
1334
|
+
[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:
|
1335
|
+
- `Object` becomes `Glimmer::DataBinding::ObservableModel`, which supports observing specified `Object` model attributes.
|
1336
|
+
- `Hash` becomes `Glimmer::DataBinding::ObservableHash`, which supports observing all `Hash` keys or a specific `Hash` key
|
1337
|
+
- `Array` becomes `Glimmer::DataBinding::ObservableArray`, which supports observing `Array` changes like those done with `push`, `<<`, `delete`, and `map!` methods (all mutation methods).
|
1329
1338
|
|
1330
1339
|
Example:
|
1331
1340
|
|
1332
1341
|
```ruby
|
1333
|
-
|
1334
|
-
|
1342
|
+
observe(person, :name) do |new_name|
|
1343
|
+
@name_label.text = new_name
|
1344
|
+
end
|
1345
|
+
```
|
1346
|
+
|
1347
|
+
That observes a person's name attribute for changes and updates the name `label` `text` property accordingly.
|
1348
|
+
|
1349
|
+
[Learn about Glimmer's Observer Pattern capabilities and options in more detail at the Glimmer project page.](https://github.com/AndyObtiva/glimmer#data-binding-library)
|
1350
|
+
|
1351
|
+
See examples of the `observe` keyword at [Color The Circles](#color-the-circles), [Method-Based Custom Keyword](#method-based-custom-keyword), [Snake](#snake), and [Tetris](#tetris).
|
1352
|
+
|
1353
|
+
### Data-Binding
|
1354
|
+
|
1355
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) supports both bidirectional (two-way) data-binding and unidirectional (one-way) data-binding.
|
1356
|
+
|
1357
|
+
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.
|
1358
|
+
|
1359
|
+
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.
|
1360
|
+
|
1361
|
+
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).
|
1362
|
+
|
1363
|
+
![MVP](https://www.researchgate.net/profile/Gilles-Perrouin/publication/320249584/figure/fig8/AS:668260987068418@1536337243385/Model-view-presenter-architecture.png)
|
1364
|
+
|
1365
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) supports bidirectional (two-way) data-binding of the following controls/properties via the `<=>` operator (indicating data is moving in both directions between View and Model):
|
1366
|
+
- `entry` `text` property
|
1367
|
+
- `multiline_entry` `text` property
|
1368
|
+
- `non_wrapping_multiline_entry` `text` property
|
1369
|
+
- `search_entry` `text` property
|
1370
|
+
|
1371
|
+
Example of bidirectional data-binding:
|
1372
|
+
|
1373
|
+
```ruby
|
1374
|
+
entry {
|
1375
|
+
text <=> [contract, :legal_text]
|
1335
1376
|
}
|
1336
1377
|
```
|
1337
1378
|
|
1338
|
-
That is data-binding
|
1379
|
+
That is data-binding a contract's legal text to an `entry` `text` property.
|
1380
|
+
|
1381
|
+
Another example of bidirectional data-binding with an option:
|
1382
|
+
|
1383
|
+
```ruby
|
1384
|
+
entry {
|
1385
|
+
text <=> [self, :entered_text, after_write: ->(text) {puts text}]
|
1386
|
+
}
|
1387
|
+
```
|
1388
|
+
|
1389
|
+
That is data-binding `entered_text` attribute on `self` to `entry` `text` property and printing text after write to the model.
|
1390
|
+
|
1391
|
+
[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).
|
1339
1392
|
|
1340
|
-
|
1393
|
+
Example of unidirectional data-binding:
|
1341
1394
|
|
1342
1395
|
```ruby
|
1343
1396
|
square(0, 0, CELL_SIZE) {
|
@@ -1347,9 +1400,44 @@ square(0, 0, CELL_SIZE) {
|
|
1347
1400
|
|
1348
1401
|
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.
|
1349
1402
|
|
1350
|
-
|
1403
|
+
Another Example of unidirectional data-binding with an option:
|
1351
1404
|
|
1352
|
-
|
1405
|
+
```ruby
|
1406
|
+
window {
|
1407
|
+
title <= [@game, :score, on_read: -> (score) {"Glimmer Snake (Score: #{@game.score})"}]
|
1408
|
+
}
|
1409
|
+
```
|
1410
|
+
|
1411
|
+
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`.
|
1412
|
+
|
1413
|
+
To summarize the data-binding API:
|
1414
|
+
- `view_property <=> [model, attribute, *read_or_write_options]`: Bidirectional (two-way) data-binding to Model attribute accessor
|
1415
|
+
- `view_property <= [model, attribute, *read_only_options]`: Unidirectional (one-way) data-binding to Model attribute reader
|
1416
|
+
|
1417
|
+
This is also known as the [Glimmer Shine](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#shine) syntax for data-binding, a [Glimmer](https://github.com/AndyObtiva/glimmer)-only unique innovation that takes advantage of [Ruby](https://www.ruby-lang.org/en/)'s highly expressive syntax and malleable DSL support.
|
1418
|
+
|
1419
|
+
Data-binding options include:
|
1420
|
+
- `before_read {|value| ...}`: performs an operation before reading data from Model to update the View.
|
1421
|
+
- `on_read {|value| ...}`: converts value read from Model to update the View.
|
1422
|
+
- `after_read {|converted_value| ...}`: performs an operation after read from Model and updating the View.
|
1423
|
+
- `before_write {|value| ...}`: performs an operation before writing data to Model from View.
|
1424
|
+
- `on_write {|value| ...}`: converts value read from View to update the Model.
|
1425
|
+
- `after_write {|converted_value| ...}`: performs an operation after writing to Model from View.
|
1426
|
+
- `computed_by attribute` or `computed_by [attribute1, attribute2, ...]`: indicates model attribute is computed from specified attribute(s), thus updated when they are updated (see in [Login example version 2](/examples/login2.rb))
|
1427
|
+
|
1428
|
+
Note that with both `on_read` and `on_write` converters, you could pass a `Symbol` representing the name of a method on the value object to invoke.
|
1429
|
+
|
1430
|
+
Example:
|
1431
|
+
|
1432
|
+
```ruby
|
1433
|
+
entry {
|
1434
|
+
text <=> [product, :price, on_read: :to_s, on_write: :to_i]
|
1435
|
+
}
|
1436
|
+
```
|
1437
|
+
|
1438
|
+
Gotcha: never data-bind a control property to an attribute on the same view object with the same exact name (e.g. binding `entry` `text` property to `self` `text` attribute) as it would conflict with it. Instead, data-bind view property to an attribute with a different name on the view object or with the same name, but on a presenter or model object (e.g. data-bind `entry` `text` to `self` `legal_text` attribute or to `contract` model `text` attribute)
|
1439
|
+
|
1440
|
+
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.
|
1353
1441
|
|
1354
1442
|
### API Gotchas
|
1355
1443
|
|
@@ -1365,6 +1453,270 @@ Learn more from data-binding usage in [Snake](#snake) and [Tic Tac Toe](#tic_tac
|
|
1365
1453
|
|
1366
1454
|
### Original API
|
1367
1455
|
|
1456
|
+
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):
|
1457
|
+
- `alloc_control`
|
1458
|
+
- `area_begin_user_window_move`
|
1459
|
+
- `area_begin_user_window_resize`
|
1460
|
+
- `area_queue_redraw_all`
|
1461
|
+
- `area_scroll_to`
|
1462
|
+
- `area_set_size`
|
1463
|
+
- `attribute_color`
|
1464
|
+
- `attribute_family`
|
1465
|
+
- `attribute_features`
|
1466
|
+
- `attribute_get_type`
|
1467
|
+
- `attribute_italic`
|
1468
|
+
- `attribute_size`
|
1469
|
+
- `attribute_stretch`
|
1470
|
+
- `attribute_underline`
|
1471
|
+
- `attribute_underline_color`
|
1472
|
+
- `attribute_weight`
|
1473
|
+
- `attributed_string_append_unattributed`
|
1474
|
+
- `attributed_string_byte_index_to_grapheme`
|
1475
|
+
- `attributed_string_delete`
|
1476
|
+
- `attributed_string_for_each_attribute`
|
1477
|
+
- `attributed_string_grapheme_to_byte_index`
|
1478
|
+
- `attributed_string_insert_at_unattributed`
|
1479
|
+
- `attributed_string_len`
|
1480
|
+
- `attributed_string_num_graphemes`
|
1481
|
+
- `attributed_string_set_attribute`
|
1482
|
+
- `attributed_string_string`
|
1483
|
+
- `box_append`
|
1484
|
+
- `box_delete`
|
1485
|
+
- `box_padded`
|
1486
|
+
- `box_set_padded`
|
1487
|
+
- `button_on_clicked`
|
1488
|
+
- `button_set_text`
|
1489
|
+
- `button_text`
|
1490
|
+
- `checkbox_checked`
|
1491
|
+
- `checkbox_on_toggled`
|
1492
|
+
- `checkbox_set_checked`
|
1493
|
+
- `checkbox_set_text`
|
1494
|
+
- `checkbox_text`
|
1495
|
+
- `color_button_color`
|
1496
|
+
- `color_button_on_changed`
|
1497
|
+
- `color_button_set_color`
|
1498
|
+
- `combobox_append`
|
1499
|
+
- `combobox_on_selected`
|
1500
|
+
- `combobox_selected`
|
1501
|
+
- `combobox_set_selected`
|
1502
|
+
- `control_destroy`
|
1503
|
+
- `control_disable`
|
1504
|
+
- `control_enable`
|
1505
|
+
- `control_enabled`
|
1506
|
+
- `control_enabled_to_user`
|
1507
|
+
- `control_handle`
|
1508
|
+
- `control_hide`
|
1509
|
+
- `control_parent`
|
1510
|
+
- `control_set_parent`
|
1511
|
+
- `control_show`
|
1512
|
+
- `control_toplevel`
|
1513
|
+
- `control_verify_set_parent`
|
1514
|
+
- `control_visible`
|
1515
|
+
- `date_time_picker_on_changed`
|
1516
|
+
- `date_time_picker_set_time`
|
1517
|
+
- `date_time_picker_time`
|
1518
|
+
- `draw_clip`
|
1519
|
+
- `draw_fill`
|
1520
|
+
- `draw_free_path`
|
1521
|
+
- `draw_free_text_layout`
|
1522
|
+
- `draw_matrix_invert`
|
1523
|
+
- `draw_matrix_invertible`
|
1524
|
+
- `draw_matrix_multiply`
|
1525
|
+
- `draw_matrix_rotate`
|
1526
|
+
- `draw_matrix_scale`
|
1527
|
+
- `draw_matrix_set_identity`
|
1528
|
+
- `draw_matrix_skew`
|
1529
|
+
- `draw_matrix_transform_point`
|
1530
|
+
- `draw_matrix_transform_size`
|
1531
|
+
- `draw_matrix_translate`
|
1532
|
+
- `draw_new_path`
|
1533
|
+
- `draw_new_text_layout`
|
1534
|
+
- `draw_path_add_rectangle`
|
1535
|
+
- `draw_path_arc_to`
|
1536
|
+
- `draw_path_bezier_to`
|
1537
|
+
- `draw_path_close_figure`
|
1538
|
+
- `draw_path_end`
|
1539
|
+
- `draw_path_line_to`
|
1540
|
+
- `draw_path_new_figure`
|
1541
|
+
- `draw_path_new_figure_with_arc`
|
1542
|
+
- `draw_restore`
|
1543
|
+
- `draw_save`
|
1544
|
+
- `draw_stroke`
|
1545
|
+
- `draw_text`
|
1546
|
+
- `draw_text_layout_extents`
|
1547
|
+
- `draw_transform`
|
1548
|
+
- `editable_combobox_append`
|
1549
|
+
- `editable_combobox_on_changed`
|
1550
|
+
- `editable_combobox_set_text`
|
1551
|
+
- `editable_combobox_text`
|
1552
|
+
- `entry_on_changed`
|
1553
|
+
- `entry_read_only`
|
1554
|
+
- `entry_set_read_only`
|
1555
|
+
- `entry_set_text`
|
1556
|
+
- `entry_text`
|
1557
|
+
- `ffi_lib`
|
1558
|
+
- `ffi_lib=`
|
1559
|
+
- `font_button_font`
|
1560
|
+
- `font_button_on_changed`
|
1561
|
+
- `form_append`
|
1562
|
+
- `form_delete`
|
1563
|
+
- `form_padded`
|
1564
|
+
- `form_set_padded`
|
1565
|
+
- `free_attribute`
|
1566
|
+
- `free_attributed_string`
|
1567
|
+
- `free_control`
|
1568
|
+
- `free_font_button_font`
|
1569
|
+
- `free_image`
|
1570
|
+
- `free_init_error`
|
1571
|
+
- `free_open_type_features`
|
1572
|
+
- `free_table_model`
|
1573
|
+
- `free_table_value`
|
1574
|
+
- `free_text`
|
1575
|
+
- `grid_append`
|
1576
|
+
- `grid_insert_at`
|
1577
|
+
- `grid_padded`
|
1578
|
+
- `grid_set_padded`
|
1579
|
+
- `group_margined`
|
1580
|
+
- `group_set_child`
|
1581
|
+
- `group_set_margined`
|
1582
|
+
- `group_set_title`
|
1583
|
+
- `group_title`
|
1584
|
+
- `image_append`
|
1585
|
+
- `init`
|
1586
|
+
- `label_set_text`
|
1587
|
+
- `label_text`
|
1588
|
+
- `main`
|
1589
|
+
- `main_step`
|
1590
|
+
- `main_steps`
|
1591
|
+
- `menu_append_about_item`
|
1592
|
+
- `menu_append_check_item`
|
1593
|
+
- `menu_append_item`
|
1594
|
+
- `menu_append_preferences_item`
|
1595
|
+
- `menu_append_quit_item`
|
1596
|
+
- `menu_append_separator`
|
1597
|
+
- `menu_item_checked`
|
1598
|
+
- `menu_item_disable`
|
1599
|
+
- `menu_item_enable`
|
1600
|
+
- `menu_item_on_clicked`
|
1601
|
+
- `menu_item_set_checked`
|
1602
|
+
- `msg_box`
|
1603
|
+
- `msg_box_error`
|
1604
|
+
- `multiline_entry_append`
|
1605
|
+
- `multiline_entry_on_changed`
|
1606
|
+
- `multiline_entry_read_only`
|
1607
|
+
- `multiline_entry_set_read_only`
|
1608
|
+
- `multiline_entry_set_text`
|
1609
|
+
- `multiline_entry_text`
|
1610
|
+
- `new_area`
|
1611
|
+
- `new_attributed_string`
|
1612
|
+
- `new_background_attribute`
|
1613
|
+
- `new_button`
|
1614
|
+
- `new_checkbox`
|
1615
|
+
- `new_color_attribute`
|
1616
|
+
- `new_color_button`
|
1617
|
+
- `new_combobox`
|
1618
|
+
- `new_date_picker`
|
1619
|
+
- `new_date_time_picker`
|
1620
|
+
- `new_editable_combobox`
|
1621
|
+
- `new_entry`
|
1622
|
+
- `new_family_attribute`
|
1623
|
+
- `new_features_attribute`
|
1624
|
+
- `new_font_button`
|
1625
|
+
- `new_form`
|
1626
|
+
- `new_grid`
|
1627
|
+
- `new_group`
|
1628
|
+
- `new_horizontal_box`
|
1629
|
+
- `new_horizontal_separator`
|
1630
|
+
- `new_image`
|
1631
|
+
- `new_italic_attribute`
|
1632
|
+
- `new_label`
|
1633
|
+
- `new_menu`
|
1634
|
+
- `new_multiline_entry`
|
1635
|
+
- `new_non_wrapping_multiline_entry`
|
1636
|
+
- `new_open_type_features`
|
1637
|
+
- `new_password_entry`
|
1638
|
+
- `new_progress_bar`
|
1639
|
+
- `new_radio_buttons`
|
1640
|
+
- `new_scrolling_area`
|
1641
|
+
- `new_search_entry`
|
1642
|
+
- `new_size_attribute`
|
1643
|
+
- `new_slider`
|
1644
|
+
- `new_spinbox`
|
1645
|
+
- `new_stretch_attribute`
|
1646
|
+
- `new_tab`
|
1647
|
+
- `new_table`
|
1648
|
+
- `new_table_model`
|
1649
|
+
- `new_table_value_color`
|
1650
|
+
- `new_table_value_image`
|
1651
|
+
- `new_table_value_int`
|
1652
|
+
- `new_table_value_string`
|
1653
|
+
- `new_time_picker`
|
1654
|
+
- `new_underline_attribute`
|
1655
|
+
- `new_underline_color_attribute`
|
1656
|
+
- `new_vertical_box`
|
1657
|
+
- `new_vertical_separator`
|
1658
|
+
- `new_weight_attribute`
|
1659
|
+
- `new_window`
|
1660
|
+
- `on_should_quit`
|
1661
|
+
- `open_file`
|
1662
|
+
- `open_type_features_add`
|
1663
|
+
- `open_type_features_clone`
|
1664
|
+
- `open_type_features_for_each`
|
1665
|
+
- `open_type_features_get`
|
1666
|
+
- `open_type_features_remove`
|
1667
|
+
- `progress_bar_set_value`
|
1668
|
+
- `progress_bar_value`
|
1669
|
+
- `queue_main`
|
1670
|
+
- `quit`
|
1671
|
+
- `radio_buttons_append`
|
1672
|
+
- `radio_buttons_on_selected`
|
1673
|
+
- `radio_buttons_selected`
|
1674
|
+
- `radio_buttons_set_selected`
|
1675
|
+
- `save_file`
|
1676
|
+
- `slider_on_changed`
|
1677
|
+
- `slider_set_value`
|
1678
|
+
- `slider_value`
|
1679
|
+
- `spinbox_on_changed`
|
1680
|
+
- `spinbox_set_value`
|
1681
|
+
- `spinbox_value`
|
1682
|
+
- `tab_append`
|
1683
|
+
- `tab_delete`
|
1684
|
+
- `tab_insert_at`
|
1685
|
+
- `tab_margined`
|
1686
|
+
- `tab_num_pages`
|
1687
|
+
- `tab_set_margined`
|
1688
|
+
- `table_append_button_column`
|
1689
|
+
- `table_append_checkbox_column`
|
1690
|
+
- `table_append_checkbox_text_column`
|
1691
|
+
- `table_append_image_column`
|
1692
|
+
- `table_append_image_text_column`
|
1693
|
+
- `table_append_progress_bar_column`
|
1694
|
+
- `table_append_text_column`
|
1695
|
+
- `table_model_row_changed`
|
1696
|
+
- `table_model_row_deleted`
|
1697
|
+
- `table_model_row_inserted`
|
1698
|
+
- `table_value_color`
|
1699
|
+
- `table_value_get_type`
|
1700
|
+
- `table_value_image`
|
1701
|
+
- `table_value_int`
|
1702
|
+
- `table_value_string`
|
1703
|
+
- `timer`
|
1704
|
+
- `uninit`
|
1705
|
+
- `user_bug_cannot_set_parent_on_toplevel`
|
1706
|
+
- `window_borderless`
|
1707
|
+
- `window_content_size`
|
1708
|
+
- `window_fullscreen`
|
1709
|
+
- `window_margined`
|
1710
|
+
- `window_on_closing`
|
1711
|
+
- `window_on_content_size_changed`
|
1712
|
+
- `window_set_borderless`
|
1713
|
+
- `window_set_child`
|
1714
|
+
- `window_set_content_size`
|
1715
|
+
- `window_set_fullscreen`
|
1716
|
+
- `window_set_margined`
|
1717
|
+
- `window_set_title`
|
1718
|
+
- `window_title`
|
1719
|
+
|
1368
1720
|
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):
|
1369
1721
|
- Check out [LibUI ffi.rb](https://github.com/kojix2/LibUI/blob/main/lib/libui/ffi.rb)
|
1370
1722
|
- Check out the [libui C Headers](https://github.com/andlabs/libui/blob/master/ui.h)
|
@@ -1871,7 +2223,44 @@ UI.main
|
|
1871
2223
|
UI.quit
|
1872
2224
|
```
|
1873
2225
|
|
1874
|
-
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
2226
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
2227
|
+
|
2228
|
+
```ruby
|
2229
|
+
require 'glimmer-dsl-libui'
|
2230
|
+
|
2231
|
+
class BasicEntry
|
2232
|
+
include Glimmer
|
2233
|
+
|
2234
|
+
attr_accessor :entry_text
|
2235
|
+
|
2236
|
+
def launch
|
2237
|
+
window('Basic Entry', 300, 50) {
|
2238
|
+
horizontal_box {
|
2239
|
+
entry {
|
2240
|
+
# stretchy true # Smart default option for appending to horizontal_box
|
2241
|
+
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.
|
2242
|
+
}
|
2243
|
+
|
2244
|
+
button('Button') {
|
2245
|
+
stretchy false # stretchy property is available when control is nested under horizontal_box
|
2246
|
+
|
2247
|
+
on_clicked do
|
2248
|
+
msg_box('You entered', entry_text)
|
2249
|
+
end
|
2250
|
+
}
|
2251
|
+
}
|
2252
|
+
|
2253
|
+
on_closing do
|
2254
|
+
puts 'Bye Bye'
|
2255
|
+
end
|
2256
|
+
}.show
|
2257
|
+
end
|
2258
|
+
end
|
2259
|
+
|
2260
|
+
BasicEntry.new.launch
|
2261
|
+
```
|
2262
|
+
|
2263
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
1875
2264
|
|
1876
2265
|
```ruby
|
1877
2266
|
require 'glimmer-dsl-libui'
|
@@ -2195,7 +2584,59 @@ Mac | Windows | Linux
|
|
2195
2584
|
----|---------|------
|
2196
2585
|
![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)
|
2197
2586
|
|
2198
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
2587
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
2588
|
+
|
2589
|
+
```ruby
|
2590
|
+
require 'glimmer-dsl-libui'
|
2591
|
+
|
2592
|
+
class Form
|
2593
|
+
include Glimmer
|
2594
|
+
|
2595
|
+
attr_accessor :first_name, :last_name, :phone, :email
|
2596
|
+
|
2597
|
+
def launch
|
2598
|
+
window('Form') {
|
2599
|
+
margined true
|
2600
|
+
|
2601
|
+
vertical_box {
|
2602
|
+
form {
|
2603
|
+
entry {
|
2604
|
+
label 'First Name' # label property is available when control is nested under form
|
2605
|
+
text <=> [self, :first_name] # bidirectional data-binding of entry text property to self first_name attribute
|
2606
|
+
}
|
2607
|
+
|
2608
|
+
entry {
|
2609
|
+
label 'Last Name' # label property is available when control is nested under form
|
2610
|
+
text <=> [self, :last_name]
|
2611
|
+
}
|
2612
|
+
|
2613
|
+
entry {
|
2614
|
+
label 'Phone' # label property is available when control is nested under form
|
2615
|
+
text <=> [self, :phone]
|
2616
|
+
}
|
2617
|
+
|
2618
|
+
entry {
|
2619
|
+
label 'Email' # label property is available when control is nested under form
|
2620
|
+
text <=> [self, :email]
|
2621
|
+
}
|
2622
|
+
}
|
2623
|
+
|
2624
|
+
button('Display Info') {
|
2625
|
+
stretchy false
|
2626
|
+
|
2627
|
+
on_clicked do
|
2628
|
+
msg_box('Info', "#{first_name} #{last_name} has phone #{phone} and email #{email}")
|
2629
|
+
end
|
2630
|
+
}
|
2631
|
+
}
|
2632
|
+
}.show
|
2633
|
+
end
|
2634
|
+
end
|
2635
|
+
|
2636
|
+
Form.new.launch
|
2637
|
+
```
|
2638
|
+
|
2639
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
2199
2640
|
|
2200
2641
|
```ruby
|
2201
2642
|
require 'glimmer-dsl-libui'
|
@@ -3077,9 +3518,9 @@ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/
|
|
3077
3518
|
ruby -r glimmer-dsl-libui -e "require 'examples/basic_scrolling_area'"
|
3078
3519
|
```
|
3079
3520
|
|
3080
|
-
Mac |
|
3081
|
-
|
3082
|
-
![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)
|
3521
|
+
Mac | Linux
|
3522
|
+
----|------
|
3523
|
+
![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)
|
3083
3524
|
|
3084
3525
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
3085
3526
|
|
@@ -4400,6 +4841,56 @@ window('Area Gallery', 400, 400) {
|
|
4400
4841
|
}.show
|
4401
4842
|
```
|
4402
4843
|
|
4844
|
+
#### Button Counter
|
4845
|
+
|
4846
|
+
[examples/button_counter.rb](examples/button_counter.rb)
|
4847
|
+
|
4848
|
+
Run with this command from the root of the project if you cloned the project:
|
4849
|
+
|
4850
|
+
```
|
4851
|
+
ruby -r './lib/glimmer-dsl-libui' examples/button_counter.rb
|
4852
|
+
```
|
4853
|
+
|
4854
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
4855
|
+
|
4856
|
+
```
|
4857
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/button_counter'"
|
4858
|
+
```
|
4859
|
+
|
4860
|
+
Mac | Linux
|
4861
|
+
----|------
|
4862
|
+
![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)
|
4863
|
+
|
4864
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
4865
|
+
|
4866
|
+
```ruby
|
4867
|
+
require 'glimmer-dsl-libui'
|
4868
|
+
|
4869
|
+
class ButtonCounter
|
4870
|
+
include Glimmer
|
4871
|
+
|
4872
|
+
attr_accessor :count
|
4873
|
+
|
4874
|
+
def initialize
|
4875
|
+
@count = 0
|
4876
|
+
end
|
4877
|
+
|
4878
|
+
def launch
|
4879
|
+
window('Hello, Button!') {
|
4880
|
+
button {
|
4881
|
+
text <= [self, :count, on_read: ->(count) {"Count: #{count}"}] # data-bind button text to self count, converting to string on read.
|
4882
|
+
|
4883
|
+
on_clicked do
|
4884
|
+
self.count += 1
|
4885
|
+
end
|
4886
|
+
}
|
4887
|
+
}.show
|
4888
|
+
end
|
4889
|
+
end
|
4890
|
+
|
4891
|
+
ButtonCounter.new.launch
|
4892
|
+
```
|
4893
|
+
|
4403
4894
|
#### Color The Circles
|
4404
4895
|
|
4405
4896
|
[examples/color_the_circles.rb](examples/color_the_circles.rb)
|
@@ -4451,7 +4942,8 @@ class ColorTheCircles
|
|
4451
4942
|
end
|
4452
4943
|
|
4453
4944
|
def register_observers
|
4454
|
-
|
4945
|
+
# observe automatically enhances self to become Glimmer::DataBinding::ObservableModel and notify observer block of score attribute changes
|
4946
|
+
observe(self, :score) do |new_score|
|
4455
4947
|
Glimmer::LibUI.queue_main do
|
4456
4948
|
@score_label.text = new_score.to_s
|
4457
4949
|
if new_score == -20
|
@@ -4465,7 +4957,6 @@ class ColorTheCircles
|
|
4465
4957
|
end
|
4466
4958
|
end
|
4467
4959
|
end
|
4468
|
-
observer.observe(self, :score) # automatically enhances self to become Glimmer::DataBinding::ObservableModel and notify observer on score attribute changes
|
4469
4960
|
end
|
4470
4961
|
|
4471
4962
|
def setup_circle_factory
|
@@ -5596,7 +6087,120 @@ Mac | Windows | Linux
|
|
5596
6087
|
----|---------|------
|
5597
6088
|
![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)
|
5598
6089
|
|
5599
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
6090
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
6091
|
+
|
6092
|
+
```ruby
|
6093
|
+
require 'glimmer-dsl-libui'
|
6094
|
+
|
6095
|
+
class FormTable
|
6096
|
+
include Glimmer
|
6097
|
+
|
6098
|
+
attr_accessor :name, :email, :phone, :city, :state, :filter_value
|
6099
|
+
|
6100
|
+
def initialize
|
6101
|
+
@data = [
|
6102
|
+
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO', '80014'],
|
6103
|
+
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA', '02101'],
|
6104
|
+
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL', '60007'],
|
6105
|
+
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA', '98101'],
|
6106
|
+
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA', '90001'],
|
6107
|
+
]
|
6108
|
+
end
|
6109
|
+
|
6110
|
+
def launch
|
6111
|
+
window('Contacts', 600, 600) { |w|
|
6112
|
+
margined true
|
6113
|
+
|
6114
|
+
vertical_box {
|
6115
|
+
form {
|
6116
|
+
stretchy false
|
6117
|
+
|
6118
|
+
entry {
|
6119
|
+
label 'Name'
|
6120
|
+
text <=> [self, :name]
|
6121
|
+
}
|
6122
|
+
|
6123
|
+
entry {
|
6124
|
+
label 'Email'
|
6125
|
+
text <=> [self, :email]
|
6126
|
+
}
|
6127
|
+
|
6128
|
+
entry {
|
6129
|
+
label 'Phone'
|
6130
|
+
text <=> [self, :phone]
|
6131
|
+
}
|
6132
|
+
|
6133
|
+
entry {
|
6134
|
+
label 'City'
|
6135
|
+
text <=> [self, :city]
|
6136
|
+
}
|
6137
|
+
|
6138
|
+
entry {
|
6139
|
+
label 'State'
|
6140
|
+
text <=> [self, :state]
|
6141
|
+
}
|
6142
|
+
}
|
6143
|
+
|
6144
|
+
button('Save Contact') {
|
6145
|
+
stretchy false
|
6146
|
+
|
6147
|
+
on_clicked do
|
6148
|
+
new_row = [name, email, phone, city, state]
|
6149
|
+
if new_row.include?('')
|
6150
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
6151
|
+
else
|
6152
|
+
@data << new_row # automatically inserts a row into the table due to implicit data-binding
|
6153
|
+
@unfiltered_data = @data.dup
|
6154
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
6155
|
+
self.email = ''
|
6156
|
+
self.phone = ''
|
6157
|
+
self.city = ''
|
6158
|
+
self.state = ''
|
6159
|
+
end
|
6160
|
+
end
|
6161
|
+
}
|
6162
|
+
|
6163
|
+
search_entry {
|
6164
|
+
stretchy false
|
6165
|
+
text <=> [self, :filter_value, # bidirectional data-binding of text to self.filter_value with after_write option
|
6166
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
6167
|
+
@unfiltered_data ||= @data.dup
|
6168
|
+
# Unfilter first to remove any previous filters
|
6169
|
+
@data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
|
6170
|
+
# Now, apply filter if entered
|
6171
|
+
unless filter_value.empty?
|
6172
|
+
@data.filter! do |row_data| # affects table indirectly through implicit data-binding
|
6173
|
+
row_data.any? do |cell|
|
6174
|
+
cell.to_s.downcase.include?(filter_value.downcase)
|
6175
|
+
end
|
6176
|
+
end
|
6177
|
+
end
|
6178
|
+
}
|
6179
|
+
]
|
6180
|
+
}
|
6181
|
+
|
6182
|
+
table {
|
6183
|
+
text_column('Name')
|
6184
|
+
text_column('Email')
|
6185
|
+
text_column('Phone')
|
6186
|
+
text_column('City')
|
6187
|
+
text_column('State')
|
6188
|
+
|
6189
|
+
cell_rows @data # implicit data-binding
|
6190
|
+
|
6191
|
+
on_changed do |row, type, row_data|
|
6192
|
+
puts "Row #{row} #{type}: #{row_data}"
|
6193
|
+
end
|
6194
|
+
}
|
6195
|
+
}
|
6196
|
+
}.show
|
6197
|
+
end
|
6198
|
+
end
|
6199
|
+
|
6200
|
+
FormTable.new.launch
|
6201
|
+
```
|
6202
|
+
|
6203
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
5600
6204
|
|
5601
6205
|
```ruby
|
5602
6206
|
require 'glimmer-dsl-libui'
|
@@ -6175,7 +6779,259 @@ Mac | Windows | Linux
|
|
6175
6779
|
----|---------|------
|
6176
6780
|
![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)
|
6177
6781
|
|
6178
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
6782
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
6783
|
+
|
6784
|
+
```ruby
|
6785
|
+
require 'glimmer-dsl-libui'
|
6786
|
+
|
6787
|
+
class Login
|
6788
|
+
include Glimmer
|
6789
|
+
|
6790
|
+
attr_accessor :username, :password, :logged_in
|
6791
|
+
|
6792
|
+
def launch
|
6793
|
+
window('Login') {
|
6794
|
+
margined true
|
6795
|
+
|
6796
|
+
vertical_box {
|
6797
|
+
form {
|
6798
|
+
entry {
|
6799
|
+
label 'Username:'
|
6800
|
+
text <=> [self, :username]
|
6801
|
+
enabled <= [self, :logged_in, on_read: :!]
|
6802
|
+
}
|
6803
|
+
|
6804
|
+
password_entry {
|
6805
|
+
label 'Password:'
|
6806
|
+
text <=> [self, :password]
|
6807
|
+
enabled <= [self, :logged_in, on_read: :!]
|
6808
|
+
}
|
6809
|
+
}
|
6810
|
+
|
6811
|
+
horizontal_box {
|
6812
|
+
button('Login') {
|
6813
|
+
enabled <= [self, :logged_in, on_read: :!]
|
6814
|
+
|
6815
|
+
on_clicked do
|
6816
|
+
self.logged_in = true
|
6817
|
+
end
|
6818
|
+
}
|
6819
|
+
|
6820
|
+
button('Logout') {
|
6821
|
+
enabled <= [self, :logged_in]
|
6822
|
+
|
6823
|
+
on_clicked do
|
6824
|
+
self.logged_in = false
|
6825
|
+
self.username = ''
|
6826
|
+
self.password = ''
|
6827
|
+
end
|
6828
|
+
}
|
6829
|
+
}
|
6830
|
+
}
|
6831
|
+
}.show
|
6832
|
+
end
|
6833
|
+
end
|
6834
|
+
|
6835
|
+
Login.new.launch
|
6836
|
+
```
|
6837
|
+
|
6838
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (with [data-binding](#data-binding)):
|
6839
|
+
|
6840
|
+
```ruby
|
6841
|
+
require 'glimmer-dsl-libui'
|
6842
|
+
|
6843
|
+
class Login
|
6844
|
+
include Glimmer
|
6845
|
+
|
6846
|
+
attr_accessor :username, :password, :logged_in
|
6847
|
+
|
6848
|
+
def logged_out
|
6849
|
+
!logged_in
|
6850
|
+
end
|
6851
|
+
|
6852
|
+
def launch
|
6853
|
+
window('Login') {
|
6854
|
+
margined true
|
6855
|
+
|
6856
|
+
vertical_box {
|
6857
|
+
form {
|
6858
|
+
entry {
|
6859
|
+
label 'Username:'
|
6860
|
+
text <=> [self, :username]
|
6861
|
+
enabled <= [self, :logged_out, computed_by: :logged_in] # computed_by option ensures being notified of changes to logged_in
|
6862
|
+
}
|
6863
|
+
|
6864
|
+
password_entry {
|
6865
|
+
label 'Password:'
|
6866
|
+
text <=> [self, :password]
|
6867
|
+
enabled <= [self, :logged_out, computed_by: :logged_in]
|
6868
|
+
}
|
6869
|
+
}
|
6870
|
+
|
6871
|
+
horizontal_box {
|
6872
|
+
button('Login') {
|
6873
|
+
enabled <= [self, :logged_out, computed_by: :logged_in]
|
6874
|
+
|
6875
|
+
on_clicked do
|
6876
|
+
self.logged_in = true
|
6877
|
+
end
|
6878
|
+
}
|
6879
|
+
|
6880
|
+
button('Logout') {
|
6881
|
+
enabled <= [self, :logged_in]
|
6882
|
+
|
6883
|
+
on_clicked do
|
6884
|
+
self.logged_in = false
|
6885
|
+
self.username = ''
|
6886
|
+
self.password = ''
|
6887
|
+
end
|
6888
|
+
}
|
6889
|
+
}
|
6890
|
+
}
|
6891
|
+
}.show
|
6892
|
+
end
|
6893
|
+
end
|
6894
|
+
|
6895
|
+
Login.new.launch
|
6896
|
+
```
|
6897
|
+
|
6898
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (with [data-binding](#data-binding)):
|
6899
|
+
|
6900
|
+
```ruby
|
6901
|
+
require 'glimmer-dsl-libui'
|
6902
|
+
|
6903
|
+
class Login
|
6904
|
+
include Glimmer
|
6905
|
+
|
6906
|
+
attr_accessor :username, :password
|
6907
|
+
attr_reader :logged_in
|
6908
|
+
|
6909
|
+
def logged_in=(value)
|
6910
|
+
@logged_in = value
|
6911
|
+
self.logged_out = !value # calling logged_out= method notifies logged_out observers
|
6912
|
+
end
|
6913
|
+
|
6914
|
+
def logged_out=(value)
|
6915
|
+
self.logged_in = !value unless logged_in == !value
|
6916
|
+
end
|
6917
|
+
|
6918
|
+
def logged_out
|
6919
|
+
!logged_in
|
6920
|
+
end
|
6921
|
+
|
6922
|
+
def launch
|
6923
|
+
window('Login') {
|
6924
|
+
margined true
|
6925
|
+
|
6926
|
+
vertical_box {
|
6927
|
+
form {
|
6928
|
+
entry {
|
6929
|
+
label 'Username:'
|
6930
|
+
text <=> [self, :username]
|
6931
|
+
enabled <= [self, :logged_out]
|
6932
|
+
}
|
6933
|
+
|
6934
|
+
password_entry {
|
6935
|
+
label 'Password:'
|
6936
|
+
text <=> [self, :password]
|
6937
|
+
enabled <= [self, :logged_out]
|
6938
|
+
}
|
6939
|
+
}
|
6940
|
+
|
6941
|
+
horizontal_box {
|
6942
|
+
button('Login') {
|
6943
|
+
enabled <= [self, :logged_out]
|
6944
|
+
|
6945
|
+
on_clicked do
|
6946
|
+
self.logged_in = true
|
6947
|
+
end
|
6948
|
+
}
|
6949
|
+
|
6950
|
+
button('Logout') {
|
6951
|
+
enabled <= [self, :logged_in]
|
6952
|
+
|
6953
|
+
on_clicked do
|
6954
|
+
self.logged_in = false
|
6955
|
+
self.username = ''
|
6956
|
+
self.password = ''
|
6957
|
+
end
|
6958
|
+
}
|
6959
|
+
}
|
6960
|
+
}
|
6961
|
+
}.show
|
6962
|
+
end
|
6963
|
+
end
|
6964
|
+
|
6965
|
+
Login.new.launch
|
6966
|
+
```
|
6967
|
+
|
6968
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 4 (with [data-binding](#data-binding)):
|
6969
|
+
|
6970
|
+
```ruby
|
6971
|
+
require 'glimmer-dsl-libui'
|
6972
|
+
|
6973
|
+
class Login
|
6974
|
+
include Glimmer
|
6975
|
+
|
6976
|
+
attr_accessor :username, :password
|
6977
|
+
attr_reader :logged_in
|
6978
|
+
|
6979
|
+
def logged_in=(value)
|
6980
|
+
@logged_in = value
|
6981
|
+
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
|
6982
|
+
end
|
6983
|
+
|
6984
|
+
def logged_out
|
6985
|
+
!logged_in
|
6986
|
+
end
|
6987
|
+
|
6988
|
+
def launch
|
6989
|
+
window('Login') {
|
6990
|
+
margined true
|
6991
|
+
|
6992
|
+
vertical_box {
|
6993
|
+
form {
|
6994
|
+
entry {
|
6995
|
+
label 'Username:'
|
6996
|
+
text <=> [self, :username]
|
6997
|
+
enabled <= [self, :logged_out]
|
6998
|
+
}
|
6999
|
+
|
7000
|
+
password_entry {
|
7001
|
+
label 'Password:'
|
7002
|
+
text <=> [self, :password]
|
7003
|
+
enabled <= [self, :logged_out]
|
7004
|
+
}
|
7005
|
+
}
|
7006
|
+
|
7007
|
+
horizontal_box {
|
7008
|
+
button('Login') {
|
7009
|
+
enabled <= [self, :logged_out]
|
7010
|
+
|
7011
|
+
on_clicked do
|
7012
|
+
self.logged_in = true
|
7013
|
+
end
|
7014
|
+
}
|
7015
|
+
|
7016
|
+
button('Logout') {
|
7017
|
+
enabled <= [self, :logged_in]
|
7018
|
+
|
7019
|
+
on_clicked do
|
7020
|
+
self.logged_in = false
|
7021
|
+
self.username = ''
|
7022
|
+
self.password = ''
|
7023
|
+
end
|
7024
|
+
}
|
7025
|
+
}
|
7026
|
+
}
|
7027
|
+
}.show
|
7028
|
+
end
|
7029
|
+
end
|
7030
|
+
|
7031
|
+
Login.new.launch
|
7032
|
+
```
|
7033
|
+
|
7034
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 5 (without [data-binding](#data-binding)):
|
6179
7035
|
|
6180
7036
|
```ruby
|
6181
7037
|
require 'glimmer-dsl-libui'
|
@@ -6243,7 +7099,102 @@ Mac | Windows | Linux
|
|
6243
7099
|
----|---------|------
|
6244
7100
|
![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)
|
6245
7101
|
|
6246
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
7102
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
7103
|
+
|
7104
|
+
```ruby
|
7105
|
+
require 'glimmer-dsl-libui'
|
7106
|
+
require 'facets'
|
7107
|
+
|
7108
|
+
include Glimmer
|
7109
|
+
|
7110
|
+
Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
|
7111
|
+
|
7112
|
+
def form_field(model, attribute)
|
7113
|
+
attribute = attribute.to_s
|
7114
|
+
entry { |e|
|
7115
|
+
label attribute.underscore.split('_').map(&:capitalize).join(' ')
|
7116
|
+
text <=> [model, attribute]
|
7117
|
+
}
|
7118
|
+
end
|
7119
|
+
|
7120
|
+
def address_form(address)
|
7121
|
+
form {
|
7122
|
+
form_field(address, :street)
|
7123
|
+
form_field(address, :p_o_box)
|
7124
|
+
form_field(address, :city)
|
7125
|
+
form_field(address, :state)
|
7126
|
+
form_field(address, :zip_code)
|
7127
|
+
}
|
7128
|
+
end
|
7129
|
+
|
7130
|
+
def label_pair(model, attribute, value)
|
7131
|
+
horizontal_box {
|
7132
|
+
label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
|
7133
|
+
label(value.to_s) {
|
7134
|
+
text <= [model, attribute]
|
7135
|
+
}
|
7136
|
+
}
|
7137
|
+
end
|
7138
|
+
|
7139
|
+
def address(address)
|
7140
|
+
vertical_box {
|
7141
|
+
address.each_pair do |attribute, value|
|
7142
|
+
label_pair(address, attribute, value)
|
7143
|
+
end
|
7144
|
+
}
|
7145
|
+
end
|
7146
|
+
|
7147
|
+
address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
|
7148
|
+
address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
|
7149
|
+
|
7150
|
+
window('Method-Based Custom Keyword') {
|
7151
|
+
margined true
|
7152
|
+
|
7153
|
+
horizontal_box {
|
7154
|
+
vertical_box {
|
7155
|
+
label('Address 1') {
|
7156
|
+
stretchy false
|
7157
|
+
}
|
7158
|
+
|
7159
|
+
address_form(address1)
|
7160
|
+
|
7161
|
+
horizontal_separator {
|
7162
|
+
stretchy false
|
7163
|
+
}
|
7164
|
+
|
7165
|
+
label('Address 1 (Saved)') {
|
7166
|
+
stretchy false
|
7167
|
+
}
|
7168
|
+
|
7169
|
+
address(address1)
|
7170
|
+
}
|
7171
|
+
|
7172
|
+
vertical_separator {
|
7173
|
+
stretchy false
|
7174
|
+
}
|
7175
|
+
|
7176
|
+
vertical_box {
|
7177
|
+
label('Address 2') {
|
7178
|
+
stretchy false
|
7179
|
+
}
|
7180
|
+
|
7181
|
+
address_form(address2)
|
7182
|
+
|
7183
|
+
horizontal_separator {
|
7184
|
+
stretchy false
|
7185
|
+
}
|
7186
|
+
|
7187
|
+
label('Address 2 (Saved)') {
|
7188
|
+
stretchy false
|
7189
|
+
}
|
7190
|
+
|
7191
|
+
address(address2)
|
7192
|
+
}
|
7193
|
+
}
|
7194
|
+
}.show
|
7195
|
+
```
|
7196
|
+
|
7197
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
6247
7198
|
|
6248
7199
|
```ruby
|
6249
7200
|
require 'glimmer-dsl-libui'
|
@@ -6282,9 +7233,9 @@ def label_pair(model, attribute, value)
|
|
6282
7233
|
name_label = label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
|
6283
7234
|
value_label = label(value.to_s)
|
6284
7235
|
}
|
6285
|
-
|
7236
|
+
observe(model, attribute) do
|
6286
7237
|
value_label.text = model.send(attribute)
|
6287
|
-
end
|
7238
|
+
end
|
6288
7239
|
end
|
6289
7240
|
|
6290
7241
|
def address(address)
|
@@ -6591,14 +7542,14 @@ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version
|
|
6591
7542
|
|
6592
7543
|
```ruby
|
6593
7544
|
require 'glimmer-dsl-libui'
|
6594
|
-
require 'glimmer/data_binding/observer'
|
6595
7545
|
|
6596
7546
|
require_relative 'snake/presenter/grid'
|
6597
7547
|
|
6598
7548
|
class Snake
|
7549
|
+
include Glimmer
|
7550
|
+
|
6599
7551
|
CELL_SIZE = 15
|
6600
7552
|
SNAKE_MOVE_DELAY = 0.1
|
6601
|
-
include Glimmer
|
6602
7553
|
|
6603
7554
|
def initialize
|
6604
7555
|
@game = Model::Game.new
|
@@ -6644,7 +7595,7 @@ class Snake
|
|
6644
7595
|
@game.width.times do |column|
|
6645
7596
|
area {
|
6646
7597
|
square(0, 0, CELL_SIZE) {
|
6647
|
-
fill <= [@grid.cells[row][column], :color]
|
7598
|
+
fill <= [@grid.cells[row][column], :color] # data-bind square fill to grid cell color
|
6648
7599
|
}
|
6649
7600
|
|
6650
7601
|
on_key_up do |area_key_event|
|
@@ -6739,7 +7690,7 @@ class Tetris
|
|
6739
7690
|
end
|
6740
7691
|
|
6741
7692
|
def register_observers
|
6742
|
-
|
7693
|
+
observe(@game, :game_over) do |game_over|
|
6743
7694
|
if game_over
|
6744
7695
|
@pause_menu_item.enabled = false
|
6745
7696
|
show_game_over_dialog
|
@@ -6747,11 +7698,11 @@ class Tetris
|
|
6747
7698
|
@pause_menu_item.enabled = true
|
6748
7699
|
start_moving_tetrominos_down
|
6749
7700
|
end
|
6750
|
-
end
|
7701
|
+
end
|
6751
7702
|
|
6752
7703
|
Model::Game::PLAYFIELD_HEIGHT.times do |row|
|
6753
7704
|
Model::Game::PLAYFIELD_WIDTH.times do |column|
|
6754
|
-
|
7705
|
+
observe(@game.playfield[row][column], :color) do |new_color|
|
6755
7706
|
Glimmer::LibUI.queue_main do
|
6756
7707
|
color = Glimmer::LibUI.interpret_color(new_color)
|
6757
7708
|
block = @playfield_blocks[row][column]
|
@@ -6762,13 +7713,13 @@ class Tetris
|
|
6762
7713
|
block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
|
6763
7714
|
block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
|
6764
7715
|
end
|
6765
|
-
end
|
7716
|
+
end
|
6766
7717
|
end
|
6767
7718
|
end
|
6768
7719
|
|
6769
7720
|
Model::Game::PREVIEW_PLAYFIELD_HEIGHT.times do |row|
|
6770
7721
|
Model::Game::PREVIEW_PLAYFIELD_WIDTH.times do |column|
|
6771
|
-
|
7722
|
+
observe(@game.preview_playfield[row][column], :color) do |new_color|
|
6772
7723
|
Glimmer::LibUI.queue_main do
|
6773
7724
|
color = Glimmer::LibUI.interpret_color(new_color)
|
6774
7725
|
block = @preview_playfield_blocks[row][column]
|
@@ -6779,27 +7730,27 @@ class Tetris
|
|
6779
7730
|
block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
|
6780
7731
|
block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
|
6781
7732
|
end
|
6782
|
-
end
|
7733
|
+
end
|
6783
7734
|
end
|
6784
7735
|
end
|
6785
7736
|
|
6786
|
-
|
7737
|
+
observe(@game, :score) do |new_score|
|
6787
7738
|
Glimmer::LibUI.queue_main do
|
6788
7739
|
@score_label.text = new_score.to_s
|
6789
7740
|
end
|
6790
|
-
end
|
7741
|
+
end
|
6791
7742
|
|
6792
|
-
|
7743
|
+
observe(@game, :lines) do |new_lines|
|
6793
7744
|
Glimmer::LibUI.queue_main do
|
6794
7745
|
@lines_label.text = new_lines.to_s
|
6795
7746
|
end
|
6796
|
-
end
|
7747
|
+
end
|
6797
7748
|
|
6798
|
-
|
7749
|
+
observe(@game, :level) do |new_level|
|
6799
7750
|
Glimmer::LibUI.queue_main do
|
6800
7751
|
@level_label.text = new_level.to_s
|
6801
7752
|
end
|
6802
|
-
end
|
7753
|
+
end
|
6803
7754
|
end
|
6804
7755
|
|
6805
7756
|
def menu_bar
|