glimmer-dsl-libui 0.5.5 → 0.5.8

Sign up to get free protection for your applications and to get access to all the features.
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.5
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.8
2
2
  ## Prerequisite-Free Ruby Desktop Development GUI Library
3
3
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-libui.svg)](http://badge.fury.io/rb/glimmer-dsl-libui)
4
4
  [![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@@ -19,7 +19,7 @@ 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.
@@ -405,6 +405,7 @@ DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
405
405
  - [Basic Image](#basic-image)
406
406
  - [Basic Transform](#basic-transform)
407
407
  - [Basic Draw Text](#basic-draw-text)
408
+ - [Basic Code Area](#basic-code-area)
408
409
  - [Advanced Examples](#advanced-examples)
409
410
  - [Area Gallery](#area-gallery)
410
411
  - [Button Counter](#button-counter)
@@ -419,7 +420,8 @@ DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
419
420
  - [Grid](#grid)
420
421
  - [Histogram](#histogram)
421
422
  - [Login](#login)
422
- - [Method-Based Custom Keyword](#method-based-custom-keyword)
423
+ - [Method-Based Custom Controls](#method-based-custom-controls)
424
+ - [Class-Based Custom Controls](#class-based-custom-controls)
423
425
  - [Area-Based Custom Controls](#area-based-custom-controls)
424
426
  - [Midi Player](#midi-player)
425
427
  - [Snake](#snake)
@@ -454,7 +456,7 @@ The Glimmer GUI DSL follows these simple concepts in mapping from [LibUI](https:
454
456
 
455
457
  **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)`).
456
458
 
457
- **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
+ **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.
458
460
 
459
461
  Example:
460
462
 
@@ -464,7 +466,7 @@ window {
464
466
 
465
467
  on_closing do # listener (always has a do; end block to signify logic)
466
468
  puts 'Bye'
467
- end
469
+ end
468
470
 
469
471
  button('greet') { # nested control
470
472
  on_clicked do
@@ -474,13 +476,13 @@ window {
474
476
  }
475
477
  ```
476
478
 
477
- Content block optionally receives one arg representing the controll
479
+ Content block optionally receives one arg representing the controll
478
480
 
479
481
  Example:
480
482
 
481
483
  ```ruby
482
- button('greet') { |b|
483
- on_clicked do
484
+ button('greet') { |b|
485
+ on_clicked do
484
486
  puts b.text
485
487
  end
486
488
  }
@@ -488,7 +490,7 @@ button('greet') { |b|
488
490
 
489
491
  **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.
490
492
 
491
- **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
+ **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).
492
494
 
493
495
  Example:
494
496
 
@@ -500,11 +502,11 @@ button('click') {
500
502
  }
501
503
  ```
502
504
 
503
- Optionally, the listener block can receive an arg representing the control.
505
+ Optionally, the listener block can receive an arg representing the control.
504
506
 
505
507
  ```ruby
506
508
  button('click') {
507
- on_clicked do |btn|
509
+ on_clicked do |btn|
508
510
  puts btn.text
509
511
  end
510
512
  }
@@ -567,7 +569,7 @@ window('hello world', 300, 200) {
567
569
 
568
570
  ## Usage
569
571
 
570
- Install [glimmer-dsl-libui](https://rubygems.org/gems/glimmer-dsl-libui) gem directly:
572
+ 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/):
571
573
 
572
574
  ```
573
575
  gem install glimmer-dsl-libui
@@ -576,7 +578,7 @@ gem install glimmer-dsl-libui
576
578
  Or install via Bundler `Gemfile`:
577
579
 
578
580
  ```ruby
579
- gem 'glimmer-dsl-libui', '~> 0.5.5'
581
+ gem 'glimmer-dsl-libui', '~> 0.5.8'
580
582
  ```
581
583
 
582
584
  Test that installation worked by running the [Meta-Example](#examples):
@@ -589,14 +591,40 @@ Mac | Windows | Linux
589
591
  ----|---------|------
590
592
  ![glimmer-dsl-libui-mac-meta-example.png](images/glimmer-dsl-libui-mac-meta-example.png) | ![glimmer-dsl-libui-windows-meta-example.png](images/glimmer-dsl-libui-windows-meta-example.png) | ![glimmer-dsl-libui-linux-meta-example.png](images/glimmer-dsl-libui-linux-meta-example.png)
591
593
 
592
- Now to use [glimmer-dsl-libui](https://rubygems.org/gems/glimmer-dsl-libui), 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.
594
+ Now to use [glimmer-dsl-libui](https://rubygems.org/gems/glimmer-dsl-libui), add `require 'glimmer-dsl-libui'` at the top.
593
595
 
594
- Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
596
+ Afterwards, `include Glimmer` into the top-level main object for testing or into an actual class for serious usage.
597
+
598
+ Alternatively, `include Glimmer::LibUI::Application` to conveniently declare the GUI `body` and run via the `::launch` method (`Glimmer::LibUI::Application` is an alias for `Glimmer::LibUI::CustomWindow` since that is what it represents).
599
+
600
+ Example including `Glimmer::LibUI::Application` (you may copy/paste in [`girb`](#girb-glimmer-irb)):
601
+
602
+ ```ruby
603
+ require 'glimmer-dsl-libui'
604
+
605
+ class SomeGlimmerApp
606
+ include Glimmer::LibUI::Application
607
+
608
+ body {
609
+ window('hello world', 300, 200) {
610
+ button('Button') {
611
+ on_clicked do
612
+ puts 'Button Clicked'
613
+ end
614
+ }
615
+ }
616
+ }
617
+ end
618
+
619
+ SomeGlimmerApp.launch
620
+ ```
621
+
622
+ Example including `Glimmer` and manually implementing the `#launch` method (you may copy/paste in [`girb`](#girb-glimmer-irb)):
595
623
 
596
624
  ```ruby
597
625
  require 'glimmer-dsl-libui'
598
626
 
599
- class Application
627
+ class SomeGlimmerApp
600
628
  include Glimmer
601
629
 
602
630
  def launch
@@ -610,7 +638,23 @@ class Application
610
638
  end
611
639
  end
612
640
 
613
- Application.new.launch
641
+ SomeGlimmerApp.new.launch
642
+ ```
643
+
644
+ Example including `Glimmer` at the top-level scope just for some prototyping/demoing/testing (you may copy/paste in [`girb`](#girb-glimmer-irb)):
645
+
646
+ ```ruby
647
+ require 'glimmer-dsl-libui'
648
+
649
+ include Glimmer
650
+
651
+ window('hello world', 300, 200) {
652
+ button('Button') {
653
+ on_clicked do
654
+ puts 'Button Clicked'
655
+ end
656
+ }
657
+ }.show
614
658
  ```
615
659
 
616
660
  If you are new to [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui), check out [Girb](#girb-glimmer-irb) and [Examples](#examples) to quickly learn through copy/paste. You may refer to the [API](#api) later on once you have gotten your feet wet with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) and need more detailed reference information.
@@ -674,6 +718,7 @@ Keyword(Args) | Properties | Listeners
674
718
  `checkbox_text_column(name as String)` | `editable` (Boolean), `editable_checkbox` (Boolean), `editable_text` (Boolean) | None
675
719
  `checkbox_text_color_column(name as String)` | `editable` (Boolean), `editable_checkbox` (Boolean), `editable_text` (Boolean) | None
676
720
  `check_menu_item(text as String)` | `checked` (Boolean) | `on_clicked`
721
+ `code_area` | `language` (String) (default: `'ruby'`), `theme` (String) (default: `'glimmer'`), `code` (String) | None
677
722
  `combobox` | `items` (`Array` of `String`), `selected` (`Integer`), `selected_item` (`String`) | `on_selected`
678
723
  `color_button` | `color` (Array of `red` as `Float`, `green` as `Float`, `blue` as `Float`, `alpha` as `Float`), `red` as `Float`, `green` as `Float`, `blue` as `Float`, `alpha` as `Float` | `on_changed`
679
724
  `date_picker` | `time` (`Hash` of keys: `sec` as `Integer`, `min` as `Integer`, `hour` as `Integer`, `mday` as `Integer`, `mon` as `Integer`, `year` as `Integer`, `wday` as `Integer`, `yday` as `Integer`, `dst` as Boolean) | `on_changed`
@@ -1598,15 +1643,15 @@ SpinnerExample.new.launch
1598
1643
 
1599
1644
  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.
1600
1645
 
1601
- 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
-
1603
- 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)`).
1646
+ 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.
1604
1647
 
1605
- To make custom keywords externally reusable, you can define in modules and simply include the modules in the view classes that need them.
1648
+ There are two ways to define custom keywords:
1649
+ - 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)`).
1650
+ - 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.
1606
1651
 
1607
- 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`).
1652
+ 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`).
1608
1653
 
1609
- Example that defines `form_field`, `address_form`, `label_pair`, and `address` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
1654
+ Example that defines `form_field`, `address_form`, `label_pair`, and `address_view` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
1610
1655
 
1611
1656
  ```ruby
1612
1657
  require 'glimmer-dsl-libui'
@@ -1643,7 +1688,7 @@ def label_pair(model, attribute, value)
1643
1688
  }
1644
1689
  end
1645
1690
 
1646
- def address(address_model)
1691
+ def address_view(address_model)
1647
1692
  vertical_box {
1648
1693
  address_model.each_pair do |attribute, value|
1649
1694
  label_pair(address_model, attribute, value)
@@ -1673,7 +1718,7 @@ window('Method-Based Custom Keyword') {
1673
1718
  stretchy false
1674
1719
  }
1675
1720
 
1676
- address(address1)
1721
+ address_view(address1)
1677
1722
  }
1678
1723
 
1679
1724
  vertical_separator {
@@ -1695,7 +1740,7 @@ window('Method-Based Custom Keyword') {
1695
1740
  stretchy false
1696
1741
  }
1697
1742
 
1698
- address(address2)
1743
+ address_view(address2)
1699
1744
  }
1700
1745
  }
1701
1746
  }.show
@@ -1703,6 +1748,136 @@ window('Method-Based Custom Keyword') {
1703
1748
 
1704
1749
  ![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
1705
1750
 
1751
+ You can also define Custom Window keywords, that is custom controls with `window` being the body root. These are also known as Applications. To define a Custom Window, you `include Glimmer::LibUI::CustomWindow` or `include Glimmer:LibUI::Application` and then you can invoke the `::launch` method on the class.
1752
+
1753
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
1754
+
1755
+ ```ruby
1756
+ require 'glimmer-dsl-libui'
1757
+ require 'facets'
1758
+
1759
+ Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
1760
+
1761
+ class FormField
1762
+ include Glimmer::LibUI::CustomControl
1763
+
1764
+ options :model, :attribute
1765
+
1766
+ body {
1767
+ entry { |e|
1768
+ label attribute.to_s.underscore.split('_').map(&:capitalize).join(' ')
1769
+ text <=> [model, attribute]
1770
+ }
1771
+ }
1772
+ end
1773
+
1774
+ class AddressForm
1775
+ include Glimmer::LibUI::CustomControl
1776
+
1777
+ options :address
1778
+
1779
+ body {
1780
+ form {
1781
+ form_field(model: address, attribute: :street)
1782
+ form_field(model: address, attribute: :p_o_box)
1783
+ form_field(model: address, attribute: :city)
1784
+ form_field(model: address, attribute: :state)
1785
+ form_field(model: address, attribute: :zip_code)
1786
+ }
1787
+ }
1788
+ end
1789
+
1790
+ class LabelPair
1791
+ include Glimmer::LibUI::CustomControl
1792
+
1793
+ options :model, :attribute, :value
1794
+
1795
+ body {
1796
+ horizontal_box {
1797
+ label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
1798
+ label(value.to_s) {
1799
+ text <= [model, attribute]
1800
+ }
1801
+ }
1802
+ }
1803
+ end
1804
+
1805
+ class AddressView
1806
+ include Glimmer::LibUI::CustomControl
1807
+
1808
+ options :address
1809
+
1810
+ body {
1811
+ vertical_box {
1812
+ address.each_pair do |attribute, value|
1813
+ label_pair(model: address, attribute: attribute, value: value)
1814
+ end
1815
+ }
1816
+ }
1817
+ end
1818
+
1819
+ class ClassBasedCustomControls
1820
+ include Glimmer::LibUI::Application # alias: Glimmer::LibUI::CustomWindow
1821
+
1822
+ before_body do
1823
+ @address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
1824
+ @address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
1825
+ end
1826
+
1827
+ body {
1828
+ window('Class-Based Custom Keyword') {
1829
+ margined true
1830
+
1831
+ horizontal_box {
1832
+ vertical_box {
1833
+ label('Address 1') {
1834
+ stretchy false
1835
+ }
1836
+
1837
+ address_form(address: @address1)
1838
+
1839
+ horizontal_separator {
1840
+ stretchy false
1841
+ }
1842
+
1843
+ label('Address 1 (Saved)') {
1844
+ stretchy false
1845
+ }
1846
+
1847
+ address_view(address: @address1)
1848
+ }
1849
+
1850
+ vertical_separator {
1851
+ stretchy false
1852
+ }
1853
+
1854
+ vertical_box {
1855
+ label('Address 2') {
1856
+ stretchy false
1857
+ }
1858
+
1859
+ address_form(address: @address2)
1860
+
1861
+ horizontal_separator {
1862
+ stretchy false
1863
+ }
1864
+
1865
+ label('Address 2 (Saved)') {
1866
+ stretchy false
1867
+ }
1868
+
1869
+ address_view(address: @address2)
1870
+ }
1871
+ }
1872
+ }
1873
+ }
1874
+ end
1875
+
1876
+ ClassBasedCustomControls.launch
1877
+ ```
1878
+
1879
+ ![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
1880
+
1706
1881
  The [`area`](#area-api) control can be utilized to build non-native custom controls from scratch by leveraging vector graphics, formattable text, keyboard events, and mouse events. This is demonstrated in the [Area-Based Custom Controls](#area-based-custom-controls) example.
1707
1882
 
1708
1883
  Defining custom keywords enables unlimited extension of the [Glimmer GUI DSL](#glimmer-gui-dsl). The sky is the limit on what can be done with custom keywords as a result. You can compose new visual vocabulary to build applications in any domain from higher concepts rather than [mere standard controls](#supported-keywords). For example, in a traffic signaling app, you could define `street`, `light_signal`, `traffic_sign`, and `car` as custom keywords and build your application from these concepts directly, saving enormous time and achieving much higher productivity.
@@ -1978,6 +2153,7 @@ Data-bound model attribute can be:
1978
2153
  - **Direct:** `Symbol` representing attribute reader/writer (e.g. `[person, :name`])
1979
2154
  - **Nested:** `String` representing nested attribute path (e.g. `[company, 'address.street']`). That results in "nested data-binding"
1980
2155
  - **Indexed:** `String` containing array attribute index (e.g. `[customer, 'addresses[0].street']`). That results in "indexed data-binding"
2156
+ - **Keyed:** `String` containing hash attribute key (e.g. `[customer, 'addresses[:main].street']`). That results in "keyed data-binding"
1981
2157
 
1982
2158
  Data-binding options include:
1983
2159
  - `before_read {|value| ...}`: performs an operation before reading data from Model to update View.
@@ -4855,6 +5031,62 @@ end
4855
5031
  BasicDrawText.new.launch
4856
5032
  ```
4857
5033
 
5034
+ #### Basic Code Area
5035
+
5036
+ [examples/basic_code_area.rb](examples/basic_code_area.rb)
5037
+
5038
+ Run with this command from the root of the project if you cloned the project:
5039
+
5040
+ ```
5041
+ ruby -r './lib/glimmer-dsl-libui' examples/basic_code_area.rb
5042
+ ```
5043
+
5044
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
5045
+
5046
+ ```
5047
+ ruby -r glimmer-dsl-libui -e "require 'examples/basic_code_area'"
5048
+ ```
5049
+
5050
+ Mac | Windows | Linux
5051
+ ----|---------|------
5052
+ ![glimmer-dsl-libui-mac-basic-code-area.png](images/glimmer-dsl-libui-mac-basic-code-area.png) | ![glimmer-dsl-libui-windows-basic-code-area.png](images/glimmer-dsl-libui-windows-basic-code-area.png) | ![glimmer-dsl-libui-linux-basic-code-area.png](images/glimmer-dsl-libui-linux-basic-code-area.png)
5053
+
5054
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
5055
+
5056
+ ```ruby
5057
+ require 'glimmer-dsl-libui'
5058
+
5059
+ class BasicCodeArea
5060
+ include Glimmer::LibUI::Application
5061
+
5062
+ before_body do
5063
+ @code = <<~CODE
5064
+ # Greets target with greeting
5065
+ def greet(greeting: 'Hello', target: 'World')
5066
+
5067
+ puts "\#{greeting}, \#{target}!"
5068
+ end
5069
+
5070
+ greet
5071
+ greet(target: 'Robert')
5072
+ greet(greeting: 'Aloha')
5073
+ greet(greeting: 'Aloha', target: 'Nancy')
5074
+ greet(greeting: 'Howdy', target: 'Doodle')
5075
+ CODE
5076
+ end
5077
+
5078
+ body {
5079
+ window('Basic Code Area', 400, 300) {
5080
+ margined true
5081
+
5082
+ code_area(language: 'ruby', code: @code)
5083
+ }
5084
+ }
5085
+ end
5086
+
5087
+ BasicCodeArea.launch
5088
+ ```
5089
+
4858
5090
  ### Advanced Examples
4859
5091
 
4860
5092
  #### Area Gallery
@@ -8580,26 +8812,26 @@ window('Login') {
8580
8812
  }.show
8581
8813
  ```
8582
8814
 
8583
- #### Method-Based Custom Keyword
8815
+ #### Method-Based Custom Controls
8584
8816
 
8585
8817
  [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.
8586
8818
 
8587
- This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom control keywords.
8819
+ This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom controls (keywords).
8588
8820
 
8589
8821
  The custom keywords are defined via methods (thus are "method-based").
8590
8822
 
8591
- [examples/method_based_custom_keyword.rb](examples/method_based_custom_keyword.rb)
8823
+ [examples/method_based_custom_controls.rb](examples/method_based_custom_controls.rb)
8592
8824
 
8593
8825
  Run with this command from the root of the project if you cloned the project:
8594
8826
 
8595
8827
  ```
8596
- ruby -r './lib/glimmer-dsl-libui' examples/method_based_custom_keyword.rb
8828
+ ruby -r './lib/glimmer-dsl-libui' examples/method_based_custom_controls.rb
8597
8829
  ```
8598
8830
 
8599
8831
  Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
8600
8832
 
8601
8833
  ```
8602
- ruby -r glimmer-dsl-libui -e "require 'examples/method_based_custom_keyword'"
8834
+ ruby -r glimmer-dsl-libui -e "require 'examples/method_based_custom_controls'"
8603
8835
  ```
8604
8836
 
8605
8837
  Mac | Windows | Linux
@@ -8624,13 +8856,13 @@ def form_field(model, attribute)
8624
8856
  }
8625
8857
  end
8626
8858
 
8627
- def address_form(address)
8859
+ def address_form(address_model)
8628
8860
  form {
8629
- form_field(address, :street)
8630
- form_field(address, :p_o_box)
8631
- form_field(address, :city)
8632
- form_field(address, :state)
8633
- form_field(address, :zip_code)
8861
+ form_field(address_model, :street)
8862
+ form_field(address_model, :p_o_box)
8863
+ form_field(address_model, :city)
8864
+ form_field(address_model, :state)
8865
+ form_field(address_model, :zip_code)
8634
8866
  }
8635
8867
  end
8636
8868
 
@@ -8643,10 +8875,10 @@ def label_pair(model, attribute, value)
8643
8875
  }
8644
8876
  end
8645
8877
 
8646
- def address(address)
8878
+ def address_view(address_model)
8647
8879
  vertical_box {
8648
- address.each_pair do |attribute, value|
8649
- label_pair(address, attribute, value)
8880
+ address_model.each_pair do |attribute, value|
8881
+ label_pair(address_model, attribute, value)
8650
8882
  end
8651
8883
  }
8652
8884
  end
@@ -8654,7 +8886,7 @@ end
8654
8886
  address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
8655
8887
  address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
8656
8888
 
8657
- window('Method-Based Custom Keyword') {
8889
+ window('Method-Based Custom Controls') {
8658
8890
  margined true
8659
8891
 
8660
8892
  horizontal_box {
@@ -8673,7 +8905,7 @@ window('Method-Based Custom Keyword') {
8673
8905
  stretchy false
8674
8906
  }
8675
8907
 
8676
- address(address1)
8908
+ address_view(address1)
8677
8909
  }
8678
8910
 
8679
8911
  vertical_separator {
@@ -8695,7 +8927,7 @@ window('Method-Based Custom Keyword') {
8695
8927
  stretchy false
8696
8928
  }
8697
8929
 
8698
- address(address2)
8930
+ address_view(address2)
8699
8931
  }
8700
8932
  }
8701
8933
  }.show
@@ -8723,13 +8955,13 @@ def form_field(model, property)
8723
8955
  }
8724
8956
  end
8725
8957
 
8726
- def address_form(address)
8958
+ def address_form(address_model)
8727
8959
  form {
8728
- form_field(address, :street)
8729
- form_field(address, :p_o_box)
8730
- form_field(address, :city)
8731
- form_field(address, :state)
8732
- form_field(address, :zip_code)
8960
+ form_field(address_model, :street)
8961
+ form_field(address_model, :p_o_box)
8962
+ form_field(address_model, :city)
8963
+ form_field(address_model, :state)
8964
+ form_field(address_model, :zip_code)
8733
8965
  }
8734
8966
  end
8735
8967
 
@@ -8745,10 +8977,10 @@ def label_pair(model, attribute, value)
8745
8977
  end
8746
8978
  end
8747
8979
 
8748
- def address(address)
8980
+ def address_view(address_model)
8749
8981
  vertical_box {
8750
- address.each_pair do |attribute, value|
8751
- label_pair(address, attribute, value)
8982
+ address_model.each_pair do |attribute, value|
8983
+ label_pair(address_model, attribute, value)
8752
8984
  end
8753
8985
  }
8754
8986
  end
@@ -8756,7 +8988,7 @@ end
8756
8988
  address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
8757
8989
  address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
8758
8990
 
8759
- window('Method-Based Custom Keyword') {
8991
+ window('Method-Based Custom Controls') {
8760
8992
  margined true
8761
8993
 
8762
8994
  horizontal_box {
@@ -8775,7 +9007,7 @@ window('Method-Based Custom Keyword') {
8775
9007
  stretchy false
8776
9008
  }
8777
9009
 
8778
- address(address1)
9010
+ address_view(address1)
8779
9011
  }
8780
9012
 
8781
9013
  vertical_separator {
@@ -8797,12 +9029,164 @@ window('Method-Based Custom Keyword') {
8797
9029
  stretchy false
8798
9030
  }
8799
9031
 
8800
- address(address2)
9032
+ address_view(address2)
8801
9033
  }
8802
9034
  }
8803
9035
  }.show
8804
9036
  ```
8805
9037
 
9038
+ #### Class-Based Custom Controls
9039
+
9040
+ [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.
9041
+
9042
+ This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom controls (keywords).
9043
+
9044
+ 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.
9045
+
9046
+ [examples/class_based_custom_controls.rb](examples/class_based_custom_controls.rb)
9047
+
9048
+ Run with this command from the root of the project if you cloned the project:
9049
+
9050
+ ```
9051
+ ruby -r './lib/glimmer-dsl-libui' examples/class_based_custom_controls.rb
9052
+ ```
9053
+
9054
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
9055
+
9056
+ ```
9057
+ ruby -r glimmer-dsl-libui -e "require 'examples/class_based_custom_controls'"
9058
+ ```
9059
+
9060
+ Mac | Windows | Linux
9061
+ ----|---------|------
9062
+ ![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)
9063
+
9064
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
9065
+
9066
+ ```ruby
9067
+ require 'glimmer-dsl-libui'
9068
+ require 'facets'
9069
+
9070
+ Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
9071
+
9072
+ class FormField
9073
+ include Glimmer::LibUI::CustomControl
9074
+
9075
+ options :model, :attribute
9076
+
9077
+ body {
9078
+ entry { |e|
9079
+ label attribute.to_s.underscore.split('_').map(&:capitalize).join(' ')
9080
+ text <=> [model, attribute]
9081
+ }
9082
+ }
9083
+ end
9084
+
9085
+ class AddressForm
9086
+ include Glimmer::LibUI::CustomControl
9087
+
9088
+ options :address
9089
+
9090
+ body {
9091
+ form {
9092
+ form_field(model: address, attribute: :street)
9093
+ form_field(model: address, attribute: :p_o_box)
9094
+ form_field(model: address, attribute: :city)
9095
+ form_field(model: address, attribute: :state)
9096
+ form_field(model: address, attribute: :zip_code)
9097
+ }
9098
+ }
9099
+ end
9100
+
9101
+ class LabelPair
9102
+ include Glimmer::LibUI::CustomControl
9103
+
9104
+ options :model, :attribute, :value
9105
+
9106
+ body {
9107
+ horizontal_box {
9108
+ label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
9109
+ label(value.to_s) {
9110
+ text <= [model, attribute]
9111
+ }
9112
+ }
9113
+ }
9114
+ end
9115
+
9116
+ class AddressView
9117
+ include Glimmer::LibUI::CustomControl
9118
+
9119
+ options :address
9120
+
9121
+ body {
9122
+ vertical_box {
9123
+ address.each_pair do |attribute, value|
9124
+ label_pair(model: address, attribute: attribute, value: value)
9125
+ end
9126
+ }
9127
+ }
9128
+ end
9129
+
9130
+ class ClassBasedCustomControls
9131
+ include Glimmer::LibUI::Application # alias: Glimmer::LibUI::CustomWindow
9132
+
9133
+ before_body do
9134
+ @address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
9135
+ @address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
9136
+ end
9137
+
9138
+ body {
9139
+ window('Class-Based Custom Keyword') {
9140
+ margined true
9141
+
9142
+ horizontal_box {
9143
+ vertical_box {
9144
+ label('Address 1') {
9145
+ stretchy false
9146
+ }
9147
+
9148
+ address_form(address: @address1)
9149
+
9150
+ horizontal_separator {
9151
+ stretchy false
9152
+ }
9153
+
9154
+ label('Address 1 (Saved)') {
9155
+ stretchy false
9156
+ }
9157
+
9158
+ address_view(address: @address1)
9159
+ }
9160
+
9161
+ vertical_separator {
9162
+ stretchy false
9163
+ }
9164
+
9165
+ vertical_box {
9166
+ label('Address 2') {
9167
+ stretchy false
9168
+ }
9169
+
9170
+ address_form(address: @address2)
9171
+
9172
+ horizontal_separator {
9173
+ stretchy false
9174
+ }
9175
+
9176
+ label('Address 2 (Saved)') {
9177
+ stretchy false
9178
+ }
9179
+
9180
+ address_view(address: @address2)
9181
+ }
9182
+ }
9183
+ }
9184
+ }
9185
+ end
9186
+
9187
+ ClassBasedCustomControls.launch
9188
+ ```
9189
+
8806
9190
  #### Area-Based Custom Controls
8807
9191
 
8808
9192
  [Custom keywords](#custom-keywords) can be defined for graphical custom controls (components) built completely from scratch as vector-graphics on top of the [`area`](#area-api) control while leveraging keyboard and mouse listeners.
@@ -9514,6 +9898,8 @@ Snake provides an example of building a desktop application [test-first](/spec/e
9514
9898
 
9515
9899
  Use arrows to move and spacebar to pause/resume.
9516
9900
 
9901
+ 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.
9902
+
9517
9903
  [examples/snake.rb](examples/snake.rb)
9518
9904
 
9519
9905
  Run with this command from the root of the project if you cloned the project:
@@ -9738,6 +10124,8 @@ Snake.new.launch
9738
10124
 
9739
10125
  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.
9740
10126
 
10127
+ 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.
10128
+
9741
10129
  [examples/tetris.rb](examples/tetris.rb)
9742
10130
 
9743
10131
  Run with this command from the root of the project if you cloned the project: