glimmer-dsl-libui 0.4.2 → 0.4.3

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.4.2
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.3
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 Business Models
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
 
@@ -373,7 +373,7 @@ gem install glimmer-dsl-libui
373
373
  Or install via Bundler `Gemfile`:
374
374
 
375
375
  ```ruby
376
- gem 'glimmer-dsl-libui', '~> 0.4.2'
376
+ gem 'glimmer-dsl-libui', '~> 0.4.3'
377
377
  ```
378
378
 
379
379
  Add `require 'glimmer-dsl-libui'` at the top, and then `include Glimmer` into the top-level main object for testing or into an actual class for serious usage.
@@ -779,9 +779,9 @@ Check [examples/dynamic_area.rb](#dynamic-area) for a more detailed semi-declara
779
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.
780
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.
781
781
 
782
- Mac |
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)
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)
785
785
 
786
786
  Check [examples/basic_scrolling_area.rb](#basic-scrolling-area) for a more detailed example.
787
787
 
@@ -1329,7 +1329,7 @@ The [Observer Design Pattern](https://en.wikipedia.org/wiki/Observer_pattern) (a
1329
1329
 
1330
1330
  ![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)
1331
1331
 
1332
- [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=nil)` keyword, which can observe `Object` models with attributes, `Hash`es, and `Array`s. It automatically enhances objects as needed to support automatically notifying observers of changes via `observable#notify_observers(attribute)` method:
1332
+ [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:
1333
1333
  - `Object` becomes `Glimmer::DataBinding::ObservableModel`, which supports observing specified `Object` model attributes.
1334
1334
  - `Hash` becomes `Glimmer::DataBinding::ObservableHash`, which supports observing all `Hash` keys or a specific `Hash` key
1335
1335
  - `Array` becomes `Glimmer::DataBinding::ObservableArray`, which supports observing `Array` changes like those done with `push`, `<<`, `delete`, and `map!` methods (all mutation methods).
@@ -1350,13 +1350,37 @@ See examples of the `observe` keyword at [Color The Circles](#color-the-circles)
1350
1350
 
1351
1351
  ### Data-Binding
1352
1352
 
1353
- [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 `<=` symbol (indicating data is moving from the right side, which is the model, to the left side, which is the GUI view object).
1353
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) supports both bidirectional (two-way) data-binding and unidirectional (one-way) data-binding.
1354
1354
 
1355
- The data-binding API is more precisely: `view_property <= [model, attribute, *options]`
1355
+ [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):
1356
+ - `entry` `text` property
1357
+ - `multiline_entry` `text` property
1358
+ - `non_wrapping_multiline_entry` `text` property
1359
+ - `search_entry` `text` property
1356
1360
 
1357
- 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.
1361
+ Example of bidirectional data-binding:
1358
1362
 
1359
- Example:
1363
+ ```ruby
1364
+ entry {
1365
+ text <=> [contract, :legal_text]
1366
+ }
1367
+ ```
1368
+
1369
+ That is data-binding a contract's legal text to an `entry` `text` property.
1370
+
1371
+ Another example of bidirectional data-binding with an option:
1372
+
1373
+ ```ruby
1374
+ entry {
1375
+ text <=> [self, :entry_text, after_write: ->(text) {puts text}]
1376
+ }
1377
+ ```
1378
+
1379
+ That is data-binding `entry_text` attribute on `self` to `entry` `text` property and printing text after write to the model.
1380
+
1381
+ [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).
1382
+
1383
+ Example of unidirectional data-binding:
1360
1384
 
1361
1385
  ```ruby
1362
1386
  square(0, 0, CELL_SIZE) {
@@ -1366,7 +1390,7 @@ square(0, 0, CELL_SIZE) {
1366
1390
 
1367
1391
  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.
1368
1392
 
1369
- Another Example:
1393
+ Another Example of unidirectional data-binding with an option:
1370
1394
 
1371
1395
  ```ruby
1372
1396
  window {
@@ -1376,14 +1400,41 @@ window {
1376
1400
 
1377
1401
  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`.
1378
1402
 
1379
- Data-binding enables writing very expressive, terse, and declarative code to synchronize View properties with Model attributes instead of pages of imperative code doing the same thing.
1403
+ The data-binding API:
1404
+ - Bidirectional (two-way) data-binding to a Model attribute accessor: `view_property <=> [model, attribute, *read_or_write_options]`
1405
+ - Unidirectional (one-way) data-binding to a Model attribute reader: `view_property <= [model, attribute, *read_only_options]`
1406
+
1407
+ 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.
1408
+
1409
+ 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.
1410
+
1411
+ 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.
1412
+
1413
+ 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).
1380
1414
 
1381
- Options include:
1415
+ ![MVP](https://www.researchgate.net/profile/Gilles-Perrouin/publication/320249584/figure/fig8/AS:668260987068418@1536337243385/Model-view-presenter-architecture.png)
1416
+
1417
+ Data-binding options include:
1382
1418
  - `before_read {|value| ...}`: performs an operation before reading data from Model to update the View.
1383
1419
  - `on_read {|value| ...}`: converts value read from Model to update the View.
1384
1420
  - `after_read {|converted_value| ...}`: performs an operation after read from Model and updating the View.
1421
+ - `before_write {|value| ...}`: performs an operation before writing data to Model from View.
1422
+ - `on_write {|value| ...}`: converts value read from View to update the Model.
1423
+ - `after_write {|converted_value| ...}`: performs an operation after writing to Model from View.
1424
+
1425
+ 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.
1426
+
1427
+ Example:
1428
+
1429
+ ```ruby
1430
+ entry {
1431
+ text <=> [product, :price, on_read: :to_s, on_write: :to_i]
1432
+ }
1433
+ ```
1434
+
1435
+ 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)
1385
1436
 
1386
- Learn more from data-binding usage in [Snake](#snake) and [Tic Tac Toe](#tic_tac_toe) examples.
1437
+ Learn more from data-binding usage in [Basic Entry](#basic-entry), [Form](#form), [Form Table](#form-table), [Login](#login), [Method-Based Custom Keyword](#method-based-custom-keyword), [Snake](#snake) and [Tic Tac Toe](#tic_tac_toe) examples.
1387
1438
 
1388
1439
  ### API Gotchas
1389
1440
 
@@ -2169,7 +2220,44 @@ UI.main
2169
2220
  UI.quit
2170
2221
  ```
2171
2222
 
2172
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
2223
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
2224
+
2225
+ ```ruby
2226
+ require 'glimmer-dsl-libui'
2227
+
2228
+ class BasicEntry
2229
+ include Glimmer
2230
+
2231
+ attr_accessor :entry_text
2232
+
2233
+ def launch
2234
+ window('Basic Entry', 300, 50) {
2235
+ horizontal_box {
2236
+ entry {
2237
+ # stretchy true # Smart default option for appending to horizontal_box
2238
+ 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.
2239
+ }
2240
+
2241
+ button('Button') {
2242
+ stretchy false # stretchy property is available when control is nested under horizontal_box
2243
+
2244
+ on_clicked do
2245
+ msg_box('You entered', entry_text)
2246
+ end
2247
+ }
2248
+ }
2249
+
2250
+ on_closing do
2251
+ puts 'Bye Bye'
2252
+ end
2253
+ }.show
2254
+ end
2255
+ end
2256
+
2257
+ BasicEntry.new.launch
2258
+ ```
2259
+
2260
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
2173
2261
 
2174
2262
  ```ruby
2175
2263
  require 'glimmer-dsl-libui'
@@ -2493,7 +2581,59 @@ Mac | Windows | Linux
2493
2581
  ----|---------|------
2494
2582
  ![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)
2495
2583
 
2496
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
2584
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
2585
+
2586
+ ```ruby
2587
+ require 'glimmer-dsl-libui'
2588
+
2589
+ class Form
2590
+ include Glimmer
2591
+
2592
+ attr_accessor :first_name, :last_name, :phone, :email
2593
+
2594
+ def launch
2595
+ window('Form') {
2596
+ margined true
2597
+
2598
+ vertical_box {
2599
+ form {
2600
+ entry {
2601
+ label 'First Name' # label property is available when control is nested under form
2602
+ text <=> [self, :first_name] # bidirectional data-binding of entry text property to self first_name attribute
2603
+ }
2604
+
2605
+ entry {
2606
+ label 'Last Name' # label property is available when control is nested under form
2607
+ text <=> [self, :last_name]
2608
+ }
2609
+
2610
+ entry {
2611
+ label 'Phone' # label property is available when control is nested under form
2612
+ text <=> [self, :phone]
2613
+ }
2614
+
2615
+ entry {
2616
+ label 'Email' # label property is available when control is nested under form
2617
+ text <=> [self, :email]
2618
+ }
2619
+ }
2620
+
2621
+ button('Display Info') {
2622
+ stretchy false
2623
+
2624
+ on_clicked do
2625
+ msg_box('Info', "#{first_name} #{last_name} has phone #{phone} and email #{email}")
2626
+ end
2627
+ }
2628
+ }
2629
+ }.show
2630
+ end
2631
+ end
2632
+
2633
+ Form.new.launch
2634
+ ```
2635
+
2636
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
2497
2637
 
2498
2638
  ```ruby
2499
2639
  require 'glimmer-dsl-libui'
@@ -5944,7 +6084,120 @@ Mac | Windows | Linux
5944
6084
  ----|---------|------
5945
6085
  ![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)
5946
6086
 
5947
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
6087
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
6088
+
6089
+ ```ruby
6090
+ require 'glimmer-dsl-libui'
6091
+
6092
+ class FormTable
6093
+ include Glimmer
6094
+
6095
+ attr_accessor :name, :email, :phone, :city, :state, :filter_value
6096
+
6097
+ def initialize
6098
+ @data = [
6099
+ ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO', '80014'],
6100
+ ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA', '02101'],
6101
+ ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL', '60007'],
6102
+ ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA', '98101'],
6103
+ ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA', '90001'],
6104
+ ]
6105
+ end
6106
+
6107
+ def launch
6108
+ window('Contacts', 600, 600) { |w|
6109
+ margined true
6110
+
6111
+ vertical_box {
6112
+ form {
6113
+ stretchy false
6114
+
6115
+ entry {
6116
+ label 'Name'
6117
+ text <=> [self, :name]
6118
+ }
6119
+
6120
+ entry {
6121
+ label 'Email'
6122
+ text <=> [self, :email]
6123
+ }
6124
+
6125
+ entry {
6126
+ label 'Phone'
6127
+ text <=> [self, :phone]
6128
+ }
6129
+
6130
+ entry {
6131
+ label 'City'
6132
+ text <=> [self, :city]
6133
+ }
6134
+
6135
+ entry {
6136
+ label 'State'
6137
+ text <=> [self, :state]
6138
+ }
6139
+ }
6140
+
6141
+ button('Save Contact') {
6142
+ stretchy false
6143
+
6144
+ on_clicked do
6145
+ new_row = [name, email, phone, city, state]
6146
+ if new_row.include?('')
6147
+ msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
6148
+ else
6149
+ @data << new_row # automatically inserts a row into the table due to implicit data-binding
6150
+ @unfiltered_data = @data.dup
6151
+ self.name = '' # automatically clears name entry through explicit data-binding
6152
+ self.email = ''
6153
+ self.phone = ''
6154
+ self.city = ''
6155
+ self.state = ''
6156
+ end
6157
+ end
6158
+ }
6159
+
6160
+ search_entry {
6161
+ stretchy false
6162
+ text <=> [self, :filter_value, # bidirectional data-binding of text to self.filter_value with after_write option
6163
+ after_write: ->(filter_value) { # execute after write to self.filter_value
6164
+ @unfiltered_data ||= @data.dup
6165
+ # Unfilter first to remove any previous filters
6166
+ @data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
6167
+ # Now, apply filter if entered
6168
+ unless filter_value.empty?
6169
+ @data.filter! do |row_data| # affects table indirectly through implicit data-binding
6170
+ row_data.any? do |cell|
6171
+ cell.to_s.downcase.include?(filter_value.downcase)
6172
+ end
6173
+ end
6174
+ end
6175
+ }
6176
+ ]
6177
+ }
6178
+
6179
+ table {
6180
+ text_column('Name')
6181
+ text_column('Email')
6182
+ text_column('Phone')
6183
+ text_column('City')
6184
+ text_column('State')
6185
+
6186
+ cell_rows @data # implicit data-binding
6187
+
6188
+ on_changed do |row, type, row_data|
6189
+ puts "Row #{row} #{type}: #{row_data}"
6190
+ end
6191
+ }
6192
+ }
6193
+ }.show
6194
+ end
6195
+ end
6196
+
6197
+ FormTable.new.launch
6198
+ ```
6199
+
6200
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
5948
6201
 
5949
6202
  ```ruby
5950
6203
  require 'glimmer-dsl-libui'
@@ -6523,7 +6776,259 @@ Mac | Windows | Linux
6523
6776
  ----|---------|------
6524
6777
  ![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)
6525
6778
 
6526
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
6779
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
6780
+
6781
+ ```ruby
6782
+ require 'glimmer-dsl-libui'
6783
+
6784
+ class Login
6785
+ include Glimmer
6786
+
6787
+ attr_accessor :username, :password, :logged_in
6788
+
6789
+ def launch
6790
+ window('Login') {
6791
+ margined true
6792
+
6793
+ vertical_box {
6794
+ form {
6795
+ entry {
6796
+ label 'Username:'
6797
+ text <=> [self, :username]
6798
+ enabled <= [self, :logged_in, on_read: :!]
6799
+ }
6800
+
6801
+ password_entry {
6802
+ label 'Password:'
6803
+ text <=> [self, :password]
6804
+ enabled <= [self, :logged_in, on_read: :!]
6805
+ }
6806
+ }
6807
+
6808
+ horizontal_box {
6809
+ button('Login') {
6810
+ enabled <= [self, :logged_in, on_read: :!]
6811
+
6812
+ on_clicked do
6813
+ self.logged_in = true
6814
+ end
6815
+ }
6816
+
6817
+ button('Logout') {
6818
+ enabled <= [self, :logged_in]
6819
+
6820
+ on_clicked do
6821
+ self.logged_in = false
6822
+ self.username = ''
6823
+ self.password = ''
6824
+ end
6825
+ }
6826
+ }
6827
+ }
6828
+ }.show
6829
+ end
6830
+ end
6831
+
6832
+ Login.new.launch
6833
+ ```
6834
+
6835
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (with [data-binding](#data-binding)):
6836
+
6837
+ ```ruby
6838
+ require 'glimmer-dsl-libui'
6839
+
6840
+ class Login
6841
+ include Glimmer
6842
+
6843
+ attr_accessor :username, :password, :logged_in
6844
+
6845
+ def logged_out
6846
+ !logged_in
6847
+ end
6848
+
6849
+ def launch
6850
+ window('Login') {
6851
+ margined true
6852
+
6853
+ vertical_box {
6854
+ form {
6855
+ entry {
6856
+ label 'Username:'
6857
+ text <=> [self, :username]
6858
+ enabled <= [self, :logged_out, computed_by: :logged_in] # computed_by option ensures being notified of changes to logged_in
6859
+ }
6860
+
6861
+ password_entry {
6862
+ label 'Password:'
6863
+ text <=> [self, :password]
6864
+ enabled <= [self, :logged_out, computed_by: :logged_in]
6865
+ }
6866
+ }
6867
+
6868
+ horizontal_box {
6869
+ button('Login') {
6870
+ enabled <= [self, :logged_out, computed_by: :logged_in]
6871
+
6872
+ on_clicked do
6873
+ self.logged_in = true
6874
+ end
6875
+ }
6876
+
6877
+ button('Logout') {
6878
+ enabled <= [self, :logged_in]
6879
+
6880
+ on_clicked do
6881
+ self.logged_in = false
6882
+ self.username = ''
6883
+ self.password = ''
6884
+ end
6885
+ }
6886
+ }
6887
+ }
6888
+ }.show
6889
+ end
6890
+ end
6891
+
6892
+ Login.new.launch
6893
+ ```
6894
+
6895
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
6896
+
6897
+ ```ruby
6898
+ require 'glimmer-dsl-libui'
6899
+
6900
+ class Login
6901
+ include Glimmer
6902
+
6903
+ attr_accessor :username, :password
6904
+ attr_reader :logged_in
6905
+
6906
+ def logged_in=(value)
6907
+ @logged_in = value
6908
+ self.logged_out = !value # calling logged_out= method notifies logged_out observers
6909
+ end
6910
+
6911
+ def logged_out=(value)
6912
+ self.logged_in = !value unless logged_in == !value
6913
+ end
6914
+
6915
+ def logged_out
6916
+ !logged_in
6917
+ end
6918
+
6919
+ def launch
6920
+ window('Login') {
6921
+ margined true
6922
+
6923
+ vertical_box {
6924
+ form {
6925
+ entry {
6926
+ label 'Username:'
6927
+ text <=> [self, :username]
6928
+ enabled <= [self, :logged_out]
6929
+ }
6930
+
6931
+ password_entry {
6932
+ label 'Password:'
6933
+ text <=> [self, :password]
6934
+ enabled <= [self, :logged_out]
6935
+ }
6936
+ }
6937
+
6938
+ horizontal_box {
6939
+ button('Login') {
6940
+ enabled <= [self, :logged_out]
6941
+
6942
+ on_clicked do
6943
+ self.logged_in = true
6944
+ end
6945
+ }
6946
+
6947
+ button('Logout') {
6948
+ enabled <= [self, :logged_in]
6949
+
6950
+ on_clicked do
6951
+ self.logged_in = false
6952
+ self.username = ''
6953
+ self.password = ''
6954
+ end
6955
+ }
6956
+ }
6957
+ }
6958
+ }.show
6959
+ end
6960
+ end
6961
+
6962
+ Login.new.launch
6963
+ ```
6964
+
6965
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
6966
+
6967
+ ```ruby
6968
+ require 'glimmer-dsl-libui'
6969
+
6970
+ class Login
6971
+ include Glimmer
6972
+
6973
+ attr_accessor :username, :password
6974
+ attr_reader :logged_in
6975
+
6976
+ def logged_in=(value)
6977
+ @logged_in = value
6978
+ 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
6979
+ end
6980
+
6981
+ def logged_out
6982
+ !logged_in
6983
+ end
6984
+
6985
+ def launch
6986
+ window('Login') {
6987
+ margined true
6988
+
6989
+ vertical_box {
6990
+ form {
6991
+ entry {
6992
+ label 'Username:'
6993
+ text <=> [self, :username]
6994
+ enabled <= [self, :logged_out]
6995
+ }
6996
+
6997
+ password_entry {
6998
+ label 'Password:'
6999
+ text <=> [self, :password]
7000
+ enabled <= [self, :logged_out]
7001
+ }
7002
+ }
7003
+
7004
+ horizontal_box {
7005
+ button('Login') {
7006
+ enabled <= [self, :logged_out]
7007
+
7008
+ on_clicked do
7009
+ self.logged_in = true
7010
+ end
7011
+ }
7012
+
7013
+ button('Logout') {
7014
+ enabled <= [self, :logged_in]
7015
+
7016
+ on_clicked do
7017
+ self.logged_in = false
7018
+ self.username = ''
7019
+ self.password = ''
7020
+ end
7021
+ }
7022
+ }
7023
+ }
7024
+ }.show
7025
+ end
7026
+ end
7027
+
7028
+ Login.new.launch
7029
+ ```
7030
+
7031
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 5 (without [data-binding](#data-binding)):
6527
7032
 
6528
7033
  ```ruby
6529
7034
  require 'glimmer-dsl-libui'
@@ -6591,7 +7096,102 @@ Mac | Windows | Linux
6591
7096
  ----|---------|------
6592
7097
  ![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)
6593
7098
 
6594
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
7099
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
7100
+
7101
+ ```ruby
7102
+ require 'glimmer-dsl-libui'
7103
+ require 'facets'
7104
+
7105
+ include Glimmer
7106
+
7107
+ Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
7108
+
7109
+ def form_field(model, attribute)
7110
+ attribute = attribute.to_s
7111
+ entry { |e|
7112
+ label attribute.underscore.split('_').map(&:capitalize).join(' ')
7113
+ text <=> [model, attribute]
7114
+ }
7115
+ end
7116
+
7117
+ def address_form(address)
7118
+ form {
7119
+ form_field(address, :street)
7120
+ form_field(address, :p_o_box)
7121
+ form_field(address, :city)
7122
+ form_field(address, :state)
7123
+ form_field(address, :zip_code)
7124
+ }
7125
+ end
7126
+
7127
+ def label_pair(model, attribute, value)
7128
+ horizontal_box {
7129
+ label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
7130
+ label(value.to_s) {
7131
+ text <= [model, attribute]
7132
+ }
7133
+ }
7134
+ end
7135
+
7136
+ def address(address)
7137
+ vertical_box {
7138
+ address.each_pair do |attribute, value|
7139
+ label_pair(address, attribute, value)
7140
+ end
7141
+ }
7142
+ end
7143
+
7144
+ address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
7145
+ address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
7146
+
7147
+ window('Method-Based Custom Keyword') {
7148
+ margined true
7149
+
7150
+ horizontal_box {
7151
+ vertical_box {
7152
+ label('Address 1') {
7153
+ stretchy false
7154
+ }
7155
+
7156
+ address_form(address1)
7157
+
7158
+ horizontal_separator {
7159
+ stretchy false
7160
+ }
7161
+
7162
+ label('Address 1 (Saved)') {
7163
+ stretchy false
7164
+ }
7165
+
7166
+ address(address1)
7167
+ }
7168
+
7169
+ vertical_separator {
7170
+ stretchy false
7171
+ }
7172
+
7173
+ vertical_box {
7174
+ label('Address 2') {
7175
+ stretchy false
7176
+ }
7177
+
7178
+ address_form(address2)
7179
+
7180
+ horizontal_separator {
7181
+ stretchy false
7182
+ }
7183
+
7184
+ label('Address 2 (Saved)') {
7185
+ stretchy false
7186
+ }
7187
+
7188
+ address(address2)
7189
+ }
7190
+ }
7191
+ }.show
7192
+ ```
7193
+
7194
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
6595
7195
 
6596
7196
  ```ruby
6597
7197
  require 'glimmer-dsl-libui'