glimmer-dsl-libui 0.5.3 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '08fe50ceac6ad2e11ec6599ba592d0a040cd4b7158a788dd8d001fddfa7e337e'
4
- data.tar.gz: b10af93758d8b4141962c9b73c87f16419f302785b29b84cf62befe86181d507
3
+ metadata.gz: 48926d5aab22eec664c80bc9ebe5a94a841e4b6173cca343ad9876fb33924b03
4
+ data.tar.gz: 890dba3f171225448aad354ec767819faaf3cb59e48764adc63b0f35bcbc0fe3
5
5
  SHA512:
6
- metadata.gz: ffcf60fe7615eaac2109035a3ce64510ca2f76c5726c2c1c48fe0adba98ad1780c5f599fc9cb154ed789571710e5e2239f950e8dc16ec041f4727e5d0bfcef53
7
- data.tar.gz: 4bb634aec81f105cd2de9aa9b2e0e3b4bd9c0eb1e715b8309cbdb4f7cd42113631e2f9d51e3db14ea9362bde930d6602ee23d7df6bb987edaf337a0d94f2756d
6
+ metadata.gz: 94650e60ccb1ed496dd682f937e4a91a3cffad37cfc3da17c462fd2db7349e06af9572c425f1b90d8a3a522c09eddd2b7eed6b4982c31a77fff97322aff6bbed
7
+ data.tar.gz: fc41c1e9882b42155729547ee2d83e43a40a64ae56c2523ff880cd2f818357fb42328a72b79810ccfb75fc7356ac70910e1d4202249488cb4f8aef9d3a8034fe
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.5.6
4
+
5
+ - Upgrade to glimmer 2.7.1 and document its support for keyed data-binding
6
+ - Support class-based custom controls
7
+ - examples/class_based_custom_controls.rb example
8
+ - Rename examples/method_based_custom_keyword.rb example to examples/method_based_custom_controls.rb
9
+
10
+ ## 0.5.5
11
+
12
+ - Upgrade to libui 0.0.15 (with official Mac ARM64 support)
13
+ - Update [README](/README.md) with [Area Animation](/README.md#area-animation) `spinner` custom control
14
+
15
+ ## 0.5.4
16
+
17
+ - Support `figure` `bounding_box` (minx, miny, width, height), `contain?` method (checking if shape contains point inside) and `include?` method (checking on outline if stroked and inside if filled?)
18
+ - Have `path` `draw_fill_mode` return `:winding` or `:alternate` while `draw_fill_mode_value` returns `0` or `1` respectively
19
+
3
20
  ## 0.5.3
4
21
 
5
22
  - Support `polyline` `bounding_box` (minx, miny, width, height), `contain?` method (checking if shape contains point inside) and `include?` method (checking on outline if stroked and inside if filled?)
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.5.3
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.5.6
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)
@@ -19,13 +19,11 @@ The main trade-off in using [Glimmer DSL for LibUI](https://rubygems.org/gems/gl
19
19
  - [Declarative DSL syntax](#glimmer-gui-dsl-concepts) that visually maps to the GUI control hierarchy
20
20
  - [Convention over configuration](#smart-defaults-and-conventions) via smart defaults and automation of low-level details
21
21
  - Requiring the [least amount of syntax](#glimmer-gui-dsl-concepts) possible to build GUI
22
- - [Custom Keyword](#custom-keywords) support
22
+ - [Custom Control](#custom-keywords) support
23
23
  - [Bidirectional/Unidirectional Data-Binding](#data-binding) to declaratively wire and automatically synchronize GUI Views with Models
24
24
  - [Far Future Plan] Scaffolding for new custom controls, apps, and gems
25
25
  - [Far Future Plan] Native-Executable packaging on Mac, Windows, and Linux.
26
26
 
27
- Note that currently, [LibUI](https://github.com/kojix2/LibUI) only includes x86_64 binaries out of the box, but there are plans to include ARM64/AARCH64 binaries too in the future.
28
-
29
27
  Hello, World!
30
28
 
31
29
  ```ruby
@@ -338,7 +336,7 @@ DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
338
336
  [Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)](https://github.com/AndyObtiva/glimmer-dsl-swt) | Mac / Windows / Linux | Yes | Yes (Canvas Shape DSL) | Very Mature / Scaffolding / Native Executable Packaging / Custom Widgets | Slow JRuby Startup Time / Heavy Memory Footprint | Java / JRuby
339
337
  [Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)](https://github.com/AndyObtiva/glimmer-dsl-opal) | All Web Browsers | No | Yes (Canvas Shape DSL) | Simpler than All JavaScript Technologies / Auto-Webify Desktop Apps | Setup Process / Only Rails 5 Support for Now | Rails
340
338
  [Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-libui) | Mac / Windows / Linux | Yes | Yes (Area API) | Fast Startup Time / Light Memory Footprint | LibUI is an Incomplete Mid-Alpha Only | None Other Than MRI Ruby
341
- [Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-tk) | Mac / Windows / Linux | Some Native-Themed Widgets (Not Truly Native) | Yes (Canvas) | Fast Startup Time / Light Memory Footprint | Widgets Do Not Look Truly Native, Espcially on Linux | ActiveTcl / MRI Ruby
339
+ [Glimmer DSL for Tk (Ruby Tk Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-tk) | Mac / Windows / Linux | Some Native-Themed Widgets (Not Truly Native) | Yes (Canvas) | Fast Startup Time / Light Memory Footprint | Complicated Setup / Widgets Do Not Look Truly Native, Espcially on Linux | ActiveTcl / MRI Ruby
342
340
  [Glimmer DSL for GTK (Ruby-GNOME Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-gtk) | Mac / Windows / Linux | Only on Linux | Yes (Cairo) | Complete Access to GNOME Features on Linux (Forte) | Not Native on Mac and Windows | None Other Than MRI Ruby on Linux / Brew Packages on Mac / MSYS & MING Toolchains on Windows / MRI Ruby
343
341
  [Glimmer DSL for FX (FOX Toolkit Ruby Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-fx) | Mac (requires XQuartz) / Windows / Linux | No | Yes (Canvas) | No Prerequisites on Windows (Forte Since Binaries Are Included Out of The Box) | Widgets Do Not Look Native / Mac Usage Obtrusively Starts XQuartz | None Other Than MRI Ruby on Windows / XQuarts on Mac / MRI Ruby
344
342
  [Glimmer DSL for JFX (JRuby JavaFX Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-jfx) | Mac / Windows / Linux | No | Yes (javafx.scene.shape and javafx.scene.canvas) | Rich in Custom Widgets | Slow JRuby Startup Time / Heavy Memory Footprint / Widgets Do Not Look Native | Java / JRuby / JavaFX SDK
@@ -370,6 +368,7 @@ DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
370
368
  - [Area Listeners](#area-listeners)
371
369
  - [Area Methods/Attributes](#area-methods-attributes)
372
370
  - [Area Transform Matrix](#area-transform-matrix)
371
+ - [Area Animation](#area-animation)
373
372
  - [Smart Defaults and Conventions](#smart-defaults-and-conventions)
374
373
  - [Custom Keywords](#custom-keywords)
375
374
  - [Observer Pattern](#observer-pattern)
@@ -420,7 +419,8 @@ DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
420
419
  - [Grid](#grid)
421
420
  - [Histogram](#histogram)
422
421
  - [Login](#login)
423
- - [Method-Based Custom Keyword](#method-based-custom-keyword)
422
+ - [Method-Based Custom Controls](#method-based-custom-controls)
423
+ - [Class-Based Custom Controls](#class-based-custom-controls)
424
424
  - [Area-Based Custom Controls](#area-based-custom-controls)
425
425
  - [Midi Player](#midi-player)
426
426
  - [Snake](#snake)
@@ -452,10 +452,68 @@ The Glimmer GUI DSL provides object-oriented declarative hierarchical syntax for
452
452
  - Requires the minimum amount of syntax needed to describe an app's GUI
453
453
 
454
454
  The Glimmer GUI DSL follows these simple concepts in mapping from [LibUI](https://github.com/kojix2/LibUI) syntax:
455
- - **Keyword(args)**: [LibUI](https://github.com/kojix2/LibUI) controls may be declared by lower-case underscored name (aka keyword) (e.g. `window` or `button`). Behind the scenes, they are represented by keyword methods that map to corresponding `LibUI.new_keyword` methods receiving args (e.g. `window('hello world', 300, 200, true)`).
456
- - **Content Block** (Properties/Listeners/Controls): Any keyword may be optionally followed by a Ruby curly-brace multi-line content block containing properties (attributes), listeners, and/or nested controls (e.g. `window {title 'hello world'; on_closing {puts 'Bye'}; button('greet')}`). Content block optionally receives one arg representing the control (e.g. `button('greet') {|b| on_clicked { puts b.text}}`)
457
- - **Property**: Control properties may be declared inside keyword blocks with lower-case underscored name followed by property value args (e.g. `title "hello world"` inside `group`). Behind the scenes, properties correspond to `LibUI.control_set_property` methods.
458
- - **Listener**: Control listeners may be declared inside keyword blocks with listener lower-case underscored name beginning with `on_` and receiving required block handler (e.g. `on_clicked {puts 'clicked'}` inside `button`). Optionally, the listener block can receive an arg representing the control (e.g. `on_clicked {|btn| puts btn.text}`). Behind the scenes, listeners correspond to `LibUI.control_on_event` methods.
455
+
456
+ **Keyword(args)**: [LibUI](https://github.com/kojix2/LibUI) controls may be declared by lower-case underscored name (aka keyword from list of [supported keywords](#supported-keywords)) (e.g. `window` or `button`). Behind the scenes, they are represented by keyword methods that map to corresponding `LibUI.new_keyword` methods receiving args (e.g. `window('hello world', 300, 200, true)`).
457
+
458
+ **Content Block** (Properties/Listeners/Controls): Any keyword may be optionally followed by a Ruby curly-brace multi-line content block containing properties (attributes), listeners, and/or nested controls.
459
+
460
+ Example:
461
+
462
+ ```ruby
463
+ window {
464
+ title 'hello world' # property
465
+
466
+ on_closing do # listener (always has a do; end block to signify logic)
467
+ puts 'Bye'
468
+ end
469
+
470
+ button('greet') { # nested control
471
+ on_clicked do
472
+ puts 'hello world'
473
+ end
474
+ }
475
+ }
476
+ ```
477
+
478
+ Content block optionally receives one arg representing the controll
479
+
480
+ Example:
481
+
482
+ ```ruby
483
+ button('greet') { |b|
484
+ on_clicked do
485
+ puts b.text
486
+ end
487
+ }
488
+ ```
489
+
490
+ **Property**: Control properties may be declared inside keyword blocks with lower-case underscored name followed by property value args (e.g. `title "hello world"` inside `group`). Behind the scenes, properties correspond to `LibUI.control_set_property` methods.
491
+
492
+ **Listener**: Control listeners may be declared inside keyword blocks with listener lower-case underscored name beginning with `on_` and receiving required block handler (always followed by a `do; end` style block to signify logic).
493
+
494
+ Example:
495
+
496
+ ```ruby
497
+ button('click') {
498
+ on_clicked do
499
+ puts 'clicked'
500
+ end
501
+ }
502
+ ```
503
+
504
+ Optionally, the listener block can receive an arg representing the control.
505
+
506
+ ```ruby
507
+ button('click') {
508
+ on_clicked do |btn|
509
+ puts btn.text
510
+ end
511
+ }
512
+ ```
513
+
514
+ Behind the scenes, listeners correspond to `LibUI.control_on_event` methods.
515
+
516
+ **Method**: Controls have methods that invoke certain operations on them. For example, `window` has a `#show` method that shows the window GUI. More methods are mentioned under [API](#api)
459
517
 
460
518
  Example of an app written in [LibUI](https://github.com/kojix2/LibUI)'s procedural imperative syntax:
461
519
 
@@ -510,7 +568,7 @@ window('hello world', 300, 200) {
510
568
 
511
569
  ## Usage
512
570
 
513
- Install [glimmer-dsl-libui](https://rubygems.org/gems/glimmer-dsl-libui) gem directly:
571
+ Install [glimmer-dsl-libui](https://rubygems.org/gems/glimmer-dsl-libui) gem directly into a [maintained Ruby version](https://www.ruby-lang.org/en/downloads/):
514
572
 
515
573
  ```
516
574
  gem install glimmer-dsl-libui
@@ -519,7 +577,7 @@ gem install glimmer-dsl-libui
519
577
  Or install via Bundler `Gemfile`:
520
578
 
521
579
  ```ruby
522
- gem 'glimmer-dsl-libui', '~> 0.5.3'
580
+ gem 'glimmer-dsl-libui', '~> 0.5.6'
523
581
  ```
524
582
 
525
583
  Test that installation worked by running the [Meta-Example](#examples):
@@ -1404,6 +1462,92 @@ You can set a `matrix`/`transform` on `area` directly to conveniently apply to a
1404
1462
 
1405
1463
  Note that `area`, `path`, and nested shapes are all truly declarative, meaning they do not care about the ordering of calls to `fill`, `stroke`, and `transform`. Furthermore, any transform that is applied is reversed at the end of the block, so you never have to worry about the ordering of `transform` calls among different paths. You simply set a transform on the `path`s that need it and it is guaranteed to be called before all its content is drawn, and then undone afterwards to avoid affecting later paths. Matrix `transform` can be set on an entire `area` too, applying to all nested `path`s.
1406
1464
 
1465
+ #### Area Animation
1466
+
1467
+ If you need to animate `area` vector graphics, you just have to use the [`Glimmer::LibUI::timer`](#libui-operations) method along with making changes to shape attributes.
1468
+
1469
+ Spinner example that has a fully customizable method-based custom control called `spinner`, which is destroyed if you click on it (you may copy/paste in [`girb`](#girb-glimmer-irb)):
1470
+
1471
+ ```ruby
1472
+ require 'glimmer-dsl-libui'
1473
+
1474
+ class SpinnerExample
1475
+ include Glimmer
1476
+
1477
+ SIZE = 120
1478
+
1479
+ def initialize
1480
+ create_gui
1481
+ end
1482
+
1483
+ def launch
1484
+ @main_window.show
1485
+ end
1486
+
1487
+ def create_gui
1488
+ @main_window = window {
1489
+ title 'Spinner'
1490
+ content_size SIZE*2, SIZE*2
1491
+
1492
+ horizontal_box {
1493
+ padded false
1494
+
1495
+ vertical_box {
1496
+ padded false
1497
+
1498
+ spinner(size: SIZE)
1499
+ spinner(size: SIZE, fill_color: [42, 153, 214])
1500
+ }
1501
+
1502
+ vertical_box {
1503
+ padded false
1504
+
1505
+ spinner(size: SIZE/2.0, fill_color: :orange)
1506
+ spinner(size: SIZE/2.0, fill_color: {x0: 0, y0: 0, x1: SIZE/2.0, y1: SIZE/2.0, stops: [{pos: 0.25, r: 204, g: 102, b: 204}, {pos: 1, r: 2, g: 2, b: 254}]})
1507
+ spinner(size: SIZE/2.0, fill_color: :green, unfilled_color: :yellow)
1508
+ spinner(size: SIZE/2.0, fill_color: :white, unfilled_color: :gray, background_color: :black)
1509
+ }
1510
+ }
1511
+ }
1512
+ end
1513
+
1514
+ def spinner(size: 40.0, fill_color: :gray, background_color: :white, unfilled_color: {r: 243, g: 243, b: 243}, donut_percentage: 0.25)
1515
+ arc1 = arc2 = nil
1516
+ area { |the_area|
1517
+ rectangle(0, 0, size, size) {
1518
+ fill background_color
1519
+ }
1520
+ circle(size/2.0, size/2.0, size/2.0) {
1521
+ fill fill_color
1522
+ }
1523
+ arc1 = arc(size/2.0, size/2.0, size/2.0, 0, 180) {
1524
+ fill unfilled_color
1525
+ }
1526
+ arc2 = arc(size/2.0, size/2.0, size/2.0, 90, 180) {
1527
+ fill unfilled_color
1528
+ }
1529
+ circle(size/2.0, size/2.0, (size/2.0)*(1.0 - donut_percentage)) {
1530
+ fill background_color
1531
+ }
1532
+
1533
+ on_mouse_up do
1534
+ the_area.destroy
1535
+ end
1536
+ }.tap do
1537
+ Glimmer::LibUI.timer(0.05) do
1538
+ delta = 10
1539
+ arc1.start_angle += delta
1540
+ arc2.start_angle += delta
1541
+ end
1542
+ end
1543
+ end
1544
+ end
1545
+
1546
+ SpinnerExample.new.launch
1547
+ ```
1548
+
1549
+ ![mac spinner](/images/glimmer-dsl-libui-mac-spinner.gif)
1550
+
1407
1551
  ### Smart Defaults and Conventions
1408
1552
 
1409
1553
  - `horizontal_box`, `vertical_box`, `grid`, and `form` controls have `padded` as `true` upon instantiation to ensure more user-friendly GUI by default
@@ -1455,15 +1599,15 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
1455
1599
 
1456
1600
  Custom keywords can be defined to represent custom controls (components) that provide new features or act as composites of [existing controls](#supported-keywords) that need to be reused multiple times in an application or across multiple applications. Custom keywords save a lot of development time, improving productivity and maintainability immensely.
1457
1601
 
1458
- For example, you can define a custom `address` control as an aggregate of multiple `label` controls to reuse multiple times as a standard address View, displaying street, city, state, and zip code.
1602
+ For example, you can define a custom `address_view` control as an aggregate of multiple `label` controls to reuse multiple times as a standard address View, displaying street, city, state, and zip code.
1459
1603
 
1460
- To define custom keywords, simply define a method representing the custom control you want (e.g. `address`) with any arguments needed (e.g. `address(address_model)`).
1604
+ There are two ways to define custom keywords:
1605
+ - Method-Based: simply define a method representing the custom control you want (e.g. `address_view`) with any arguments needed (e.g. `address(address_model)`).
1606
+ - Class-Based: define a class matching the camelcased name of the custom control by convention (e.g. the `address_view` custom control keyword would have a class called `AddressView`) and `include Glimmer::LibUI::CustomControl`. Classes add the benefit of being able to distribute the custom controls into separate files and reuse externally from multiple places or share via Ruby gems.
1461
1607
 
1462
- To make custom keywords externally reusable, you can define in modules and simply include the modules in the view classes that need them.
1608
+ It is OK to use the terms "custom keyword" and "custom control" synonymously though "custom keyword" is a broader term that covers things other than controls too like custom shapes (e.g. `cylinder`), custom attributed strings (e.g. `alternating_color_string`), and custom transforms (`isometric_transform`).
1463
1609
 
1464
- It is OK to use terms "custom keyword" and "custom control" synonymously though "custom keyword" is a broader term that covers things other than controls too like custom shapes (e.g. `cylinder`), custom attributed strings (e.g. `alternating_color_string`), and custom transforms (`isometric_transform`).
1465
-
1466
- Example that defines `form_field`, `address_form`, `label_pair`, and `address` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
1610
+ Example that defines `form_field`, `address_form`, `label_pair`, and `address_view` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
1467
1611
 
1468
1612
  ```ruby
1469
1613
  require 'glimmer-dsl-libui'
@@ -1500,7 +1644,7 @@ def label_pair(model, attribute, value)
1500
1644
  }
1501
1645
  end
1502
1646
 
1503
- def address(address_model)
1647
+ def address_view(address_model)
1504
1648
  vertical_box {
1505
1649
  address_model.each_pair do |attribute, value|
1506
1650
  label_pair(address_model, attribute, value)
@@ -1530,7 +1674,7 @@ window('Method-Based Custom Keyword') {
1530
1674
  stretchy false
1531
1675
  }
1532
1676
 
1533
- address(address1)
1677
+ address_view(address1)
1534
1678
  }
1535
1679
 
1536
1680
  vertical_separator {
@@ -1552,7 +1696,7 @@ window('Method-Based Custom Keyword') {
1552
1696
  stretchy false
1553
1697
  }
1554
1698
 
1555
- address(address2)
1699
+ address_view(address2)
1556
1700
  }
1557
1701
  }
1558
1702
  }.show
@@ -1835,6 +1979,7 @@ Data-bound model attribute can be:
1835
1979
  - **Direct:** `Symbol` representing attribute reader/writer (e.g. `[person, :name`])
1836
1980
  - **Nested:** `String` representing nested attribute path (e.g. `[company, 'address.street']`). That results in "nested data-binding"
1837
1981
  - **Indexed:** `String` containing array attribute index (e.g. `[customer, 'addresses[0].street']`). That results in "indexed data-binding"
1982
+ - **Keyed:** `String` containing hash attribute key (e.g. `[customer, 'addresses[:main].street']`). That results in "keyed data-binding"
1838
1983
 
1839
1984
  Data-binding options include:
1840
1985
  - `before_read {|value| ...}`: performs an operation before reading data from Model to update View.
@@ -1868,6 +2013,7 @@ Learn more from data-binding usage in [Login](#login) (4 data-binding versions),
1868
2013
  - There is no proper way to destroy `grid` children due to [libui](https://github.com/andlabs/libui) not offering any API for deleting them from `grid` (no `grid_delete` similar to `box_delete` for `horizontal_box` and `vertical_box`).
1869
2014
  - `table` `checkbox_column` checkbox editing only works on Linux and Windows (not Mac) due to a current limitation in [libui](https://github.com/andlabs/ui/issues/357).
1870
2015
  - `table` `checkbox_text_column` checkbox editing only works on Linux (not Mac or Windows) due to a current limitation in [libui](https://github.com/andlabs/ui/issues/357).
2016
+ - `checkbox` only supports obtaining the `checked` property, but not updating it due to a current limitation in [libui](https://github.com/andlabs/libui).
1871
2017
  - `text` `align` property seems not to work on the Mac ([libui](https://github.com/andlabs/libui) has an [issue](https://github.com/andlabs/libui/pull/407) about it)
1872
2018
  - `text` `string` `background` does not work on Windows due to an [issue in libui](https://github.com/andlabs/libui/issues/347).
1873
2019
  - `table` `progress_bar` column on Windows cannot be updated with a positive value if it started initially with `-1` (it ignores update to avoid crashing due to an issue in [libui](https://github.com/andlabs/libui) on Windows.
@@ -1875,6 +2021,7 @@ Learn more from data-binding usage in [Login](#login) (4 data-binding versions),
1875
2021
  - It seems that [libui](https://github.com/andlabs/libui) does not support nesting multiple `area` controls under a `grid` as only the first one shows up in that scenario. To workaround that limitation, use a `vertical_box` with nested `horizontal_box`s instead to include multiple `area`s in a GUI.
1876
2022
  - As per the code of [examples/basic_transform.rb](#basic-transform), Windows requires different ordering of transforms than Mac and Linux.
1877
2023
  - `scrolling_area#scroll_to` does not seem to work on Windows and Linux, but works fine on Mac
2024
+ - When creating/showing a window other than the main window and then closing the secondary window, the entire app closes. This is a current limitation to the windowing system that should be fixed with [child window support](https://github.com/andlabs/libui/issues/137) in [libui](https://github.com/andlabs/libui)
1878
2025
 
1879
2026
  ### Original API
1880
2027
 
@@ -8435,26 +8582,26 @@ window('Login') {
8435
8582
  }.show
8436
8583
  ```
8437
8584
 
8438
- #### Method-Based Custom Keyword
8585
+ #### Method-Based Custom Controls
8439
8586
 
8440
8587
  [Custom keywords](#custom-keywords) can be defined to represent custom controls (components) that provide new features or act as composites of existing controls that need to be reused multiple times in an application or across multiple applications. Custom keywords save a lot of development time, improving productivity and maintainability immensely.
8441
8588
 
8442
- This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom control keywords.
8589
+ This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom controls (keywords).
8443
8590
 
8444
8591
  The custom keywords are defined via methods (thus are "method-based").
8445
8592
 
8446
- [examples/method_based_custom_keyword.rb](examples/method_based_custom_keyword.rb)
8593
+ [examples/method_based_custom_controls.rb](examples/method_based_custom_controls.rb)
8447
8594
 
8448
8595
  Run with this command from the root of the project if you cloned the project:
8449
8596
 
8450
8597
  ```
8451
- ruby -r './lib/glimmer-dsl-libui' examples/method_based_custom_keyword.rb
8598
+ ruby -r './lib/glimmer-dsl-libui' examples/method_based_custom_controls.rb
8452
8599
  ```
8453
8600
 
8454
8601
  Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
8455
8602
 
8456
8603
  ```
8457
- ruby -r glimmer-dsl-libui -e "require 'examples/method_based_custom_keyword'"
8604
+ ruby -r glimmer-dsl-libui -e "require 'examples/method_based_custom_controls'"
8458
8605
  ```
8459
8606
 
8460
8607
  Mac | Windows | Linux
@@ -8479,13 +8626,13 @@ def form_field(model, attribute)
8479
8626
  }
8480
8627
  end
8481
8628
 
8482
- def address_form(address)
8629
+ def address_form(address_model)
8483
8630
  form {
8484
- form_field(address, :street)
8485
- form_field(address, :p_o_box)
8486
- form_field(address, :city)
8487
- form_field(address, :state)
8488
- form_field(address, :zip_code)
8631
+ form_field(address_model, :street)
8632
+ form_field(address_model, :p_o_box)
8633
+ form_field(address_model, :city)
8634
+ form_field(address_model, :state)
8635
+ form_field(address_model, :zip_code)
8489
8636
  }
8490
8637
  end
8491
8638
 
@@ -8498,10 +8645,10 @@ def label_pair(model, attribute, value)
8498
8645
  }
8499
8646
  end
8500
8647
 
8501
- def address(address)
8648
+ def address_view(address_model)
8502
8649
  vertical_box {
8503
- address.each_pair do |attribute, value|
8504
- label_pair(address, attribute, value)
8650
+ address_model.each_pair do |attribute, value|
8651
+ label_pair(address_model, attribute, value)
8505
8652
  end
8506
8653
  }
8507
8654
  end
@@ -8509,7 +8656,7 @@ end
8509
8656
  address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
8510
8657
  address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
8511
8658
 
8512
- window('Method-Based Custom Keyword') {
8659
+ window('Method-Based Custom Controls') {
8513
8660
  margined true
8514
8661
 
8515
8662
  horizontal_box {
@@ -8528,7 +8675,7 @@ window('Method-Based Custom Keyword') {
8528
8675
  stretchy false
8529
8676
  }
8530
8677
 
8531
- address(address1)
8678
+ address_view(address1)
8532
8679
  }
8533
8680
 
8534
8681
  vertical_separator {
@@ -8550,7 +8697,7 @@ window('Method-Based Custom Keyword') {
8550
8697
  stretchy false
8551
8698
  }
8552
8699
 
8553
- address(address2)
8700
+ address_view(address2)
8554
8701
  }
8555
8702
  }
8556
8703
  }.show
@@ -8578,13 +8725,13 @@ def form_field(model, property)
8578
8725
  }
8579
8726
  end
8580
8727
 
8581
- def address_form(address)
8728
+ def address_form(address_model)
8582
8729
  form {
8583
- form_field(address, :street)
8584
- form_field(address, :p_o_box)
8585
- form_field(address, :city)
8586
- form_field(address, :state)
8587
- form_field(address, :zip_code)
8730
+ form_field(address_model, :street)
8731
+ form_field(address_model, :p_o_box)
8732
+ form_field(address_model, :city)
8733
+ form_field(address_model, :state)
8734
+ form_field(address_model, :zip_code)
8588
8735
  }
8589
8736
  end
8590
8737
 
@@ -8600,10 +8747,10 @@ def label_pair(model, attribute, value)
8600
8747
  end
8601
8748
  end
8602
8749
 
8603
- def address(address)
8750
+ def address_view(address_model)
8604
8751
  vertical_box {
8605
- address.each_pair do |attribute, value|
8606
- label_pair(address, attribute, value)
8752
+ address_model.each_pair do |attribute, value|
8753
+ label_pair(address_model, attribute, value)
8607
8754
  end
8608
8755
  }
8609
8756
  end
@@ -8611,7 +8758,7 @@ end
8611
8758
  address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
8612
8759
  address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
8613
8760
 
8614
- window('Method-Based Custom Keyword') {
8761
+ window('Method-Based Custom Controls') {
8615
8762
  margined true
8616
8763
 
8617
8764
  horizontal_box {
@@ -8630,7 +8777,7 @@ window('Method-Based Custom Keyword') {
8630
8777
  stretchy false
8631
8778
  }
8632
8779
 
8633
- address(address1)
8780
+ address_view(address1)
8634
8781
  }
8635
8782
 
8636
8783
  vertical_separator {
@@ -8652,7 +8799,151 @@ window('Method-Based Custom Keyword') {
8652
8799
  stretchy false
8653
8800
  }
8654
8801
 
8655
- address(address2)
8802
+ address_view(address2)
8803
+ }
8804
+ }
8805
+ }.show
8806
+ ```
8807
+
8808
+ #### Class-Based Custom Controls
8809
+
8810
+ [Custom keywords](#custom-keywords) can be defined to represent custom controls (components) that provide new features or act as composites of existing controls that need to be reused multiple times in an application or across multiple applications. Custom keywords save a lot of development time, improving productivity and maintainability immensely.
8811
+
8812
+ This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom controls (keywords).
8813
+
8814
+ The custom keywords are defined via classes that include `Glimmer::LibUI::CustomControl` (thus are "class-based"), thus enabling offloading each custom control into its own file when needed for better code organization.
8815
+
8816
+ [examples/class_based_custom_controls.rb](examples/class_based_custom_controls.rb)
8817
+
8818
+ Run with this command from the root of the project if you cloned the project:
8819
+
8820
+ ```
8821
+ ruby -r './lib/glimmer-dsl-libui' examples/class_based_custom_controls.rb
8822
+ ```
8823
+
8824
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
8825
+
8826
+ ```
8827
+ ruby -r glimmer-dsl-libui -e "require 'examples/class_based_custom_controls'"
8828
+ ```
8829
+
8830
+ Mac | Windows | Linux
8831
+ ----|---------|------
8832
+ ![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)
8833
+
8834
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
8835
+
8836
+ ```ruby
8837
+ require 'glimmer-dsl-libui'
8838
+ require 'facets'
8839
+
8840
+ include Glimmer
8841
+
8842
+ Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
8843
+
8844
+ class FormField
8845
+ include Glimmer::LibUI::CustomControl
8846
+
8847
+ options :model, :attribute
8848
+
8849
+ body {
8850
+ entry { |e|
8851
+ label attribute.to_s.underscore.split('_').map(&:capitalize).join(' ')
8852
+ text <=> [model, attribute]
8853
+ }
8854
+ }
8855
+ end
8856
+
8857
+ class AddressForm
8858
+ include Glimmer::LibUI::CustomControl
8859
+
8860
+ options :address
8861
+
8862
+ body {
8863
+ form {
8864
+ form_field(model: address, attribute: :street)
8865
+ form_field(model: address, attribute: :p_o_box)
8866
+ form_field(model: address, attribute: :city)
8867
+ form_field(model: address, attribute: :state)
8868
+ form_field(model: address, attribute: :zip_code)
8869
+ }
8870
+ }
8871
+ end
8872
+
8873
+ class LabelPair
8874
+ include Glimmer::LibUI::CustomControl
8875
+
8876
+ options :model, :attribute, :value
8877
+
8878
+ body {
8879
+ horizontal_box {
8880
+ label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
8881
+ label(value.to_s) {
8882
+ text <= [model, attribute]
8883
+ }
8884
+ }
8885
+ }
8886
+ end
8887
+
8888
+ class AddressView
8889
+ include Glimmer::LibUI::CustomControl
8890
+
8891
+ options :address
8892
+
8893
+ body {
8894
+ vertical_box {
8895
+ address.each_pair do |attribute, value|
8896
+ label_pair(model: address, attribute: attribute, value: value)
8897
+ end
8898
+ }
8899
+ }
8900
+ end
8901
+
8902
+ address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
8903
+ address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
8904
+
8905
+ window('Class-Based Custom Keyword') {
8906
+ margined true
8907
+
8908
+ horizontal_box {
8909
+ vertical_box {
8910
+ label('Address 1') {
8911
+ stretchy false
8912
+ }
8913
+
8914
+ address_form(address: address1)
8915
+
8916
+ horizontal_separator {
8917
+ stretchy false
8918
+ }
8919
+
8920
+ label('Address 1 (Saved)') {
8921
+ stretchy false
8922
+ }
8923
+
8924
+ address_view(address: address1)
8925
+ }
8926
+
8927
+ vertical_separator {
8928
+ stretchy false
8929
+ }
8930
+
8931
+ vertical_box {
8932
+ label('Address 2') {
8933
+ stretchy false
8934
+ }
8935
+
8936
+ address_form(address: address2)
8937
+
8938
+ horizontal_separator {
8939
+ stretchy false
8940
+ }
8941
+
8942
+ label('Address 2 (Saved)') {
8943
+ stretchy false
8944
+ }
8945
+
8946
+ address_view(address: address2)
8656
8947
  }
8657
8948
  }
8658
8949
  }.show
@@ -9369,6 +9660,8 @@ Snake provides an example of building a desktop application [test-first](/spec/e
9369
9660
 
9370
9661
  Use arrows to move and spacebar to pause/resume.
9371
9662
 
9663
+ Note that Snake relies on the new [Ruby Pattern Matching feature](https://docs.ruby-lang.org/en/3.0/doc/syntax/pattern_matching_rdoc.html) available starting in Ruby 2.7 experimentally and in Ruby 3.0 officially.
9664
+
9372
9665
  [examples/snake.rb](examples/snake.rb)
9373
9666
 
9374
9667
  Run with this command from the root of the project if you cloned the project:
@@ -9593,6 +9886,8 @@ Snake.new.launch
9593
9886
 
9594
9887
  Glimmer Tetris utilizes many small areas to represent Tetromino blocks because this ensures smaller redraws per tetromino block color change, thus achieving higher performance than redrawing one large area on every little change.
9595
9888
 
9889
+ Note that Tetris relies on the new [Ruby Pattern Matching feature](https://docs.ruby-lang.org/en/3.0/doc/syntax/pattern_matching_rdoc.html) available starting in Ruby 2.7 experimentally and in Ruby 3.0 officially.
9890
+
9596
9891
  [examples/tetris.rb](examples/tetris.rb)
9597
9892
 
9598
9893
  Run with this command from the root of the project if you cloned the project:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.3
1
+ 0.5.6