glimmer-dsl-libui 0.4.11 → 0.4.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/README.md +473 -57
  4. data/VERSION +1 -1
  5. data/examples/basic_image.rb +5 -3
  6. data/examples/basic_image2.rb +1 -3
  7. data/examples/basic_image3.rb +3 -3
  8. data/examples/basic_image4.rb +0 -3
  9. data/examples/basic_image5.rb +0 -2
  10. data/examples/basic_table_color.rb +104 -26
  11. data/examples/basic_table_color2.rb +2 -14
  12. data/examples/basic_table_color3.rb +37 -0
  13. data/examples/basic_table_image.rb +1 -1
  14. data/examples/basic_table_image2.rb +2 -14
  15. data/examples/basic_table_image3.rb +44 -0
  16. data/examples/basic_table_image_text.rb +1 -2
  17. data/examples/basic_table_image_text2.rb +2 -13
  18. data/examples/basic_table_image_text3.rb +44 -0
  19. data/examples/cpu_percentage.rb +1 -1
  20. data/examples/editable_column_table.rb +5 -0
  21. data/examples/form_table.rb +10 -4
  22. data/examples/form_table2.rb +12 -6
  23. data/examples/form_table3.rb +9 -3
  24. data/examples/form_table4.rb +9 -3
  25. data/examples/form_table5.rb +9 -3
  26. data/examples/meta_example.rb +3 -1
  27. data/glimmer-dsl-libui.gemspec +0 -0
  28. data/lib/glimmer/libui/attributed_string.rb +17 -8
  29. data/lib/glimmer/libui/control_proxy/area_proxy.rb +17 -17
  30. data/lib/glimmer/libui/control_proxy/box.rb +1 -0
  31. data/lib/glimmer/libui/control_proxy/column/background_color_column_proxy.rb +4 -0
  32. data/lib/glimmer/libui/control_proxy/column/button_column_proxy.rb +1 -29
  33. data/lib/glimmer/libui/control_proxy/form_proxy.rb +1 -0
  34. data/lib/glimmer/libui/control_proxy/image_proxy.rb +92 -10
  35. data/lib/glimmer/libui/control_proxy/menu_item_proxy/quit_menu_item_proxy.rb +18 -8
  36. data/lib/glimmer/libui/control_proxy/open_type_features_proxy.rb +11 -2
  37. data/lib/glimmer/libui/control_proxy/open_type_tag_proxy.rb +2 -0
  38. data/lib/glimmer/libui/control_proxy/path_proxy.rb +2 -0
  39. data/lib/glimmer/libui/control_proxy/table_proxy.rb +14 -12
  40. data/lib/glimmer/libui/control_proxy/text_proxy.rb +2 -0
  41. data/lib/glimmer/libui/control_proxy/window_proxy.rb +34 -35
  42. data/lib/glimmer/libui/control_proxy.rb +45 -9
  43. data/lib/glimmer/libui/shape.rb +1 -0
  44. data/lib/glimmer/libui.rb +2 -2
  45. metadata +5 -2
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.4.11
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.15
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)
@@ -100,7 +100,7 @@ class FormTable
100
100
  end
101
101
 
102
102
  def launch
103
- window('Contacts', 600, 600) { |w|
103
+ window('Contacts', 600, 600) {
104
104
  margined true
105
105
 
106
106
  vertical_box {
@@ -138,8 +138,8 @@ class FormTable
138
138
 
139
139
  on_clicked do
140
140
  new_row = [name, email, phone, city, state]
141
- if new_row.include?('')
142
- msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
141
+ if new_row.map(&:to_s).include?('')
142
+ msg_box_error('Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
143
143
  else
144
144
  @contacts << Contact.new(*new_row) # automatically inserts a row into the table due to explicit data-binding
145
145
  @unfiltered_contacts = @contacts.dup
@@ -180,10 +180,16 @@ class FormTable
180
180
  text_column('State')
181
181
 
182
182
  editable true
183
- cell_rows <=> [self, :contacts] # explicit data-binding to Model Array
183
+ cell_rows <=> [self, :contacts] # explicit data-binding to self.contacts Modal Array, auto-inferring model attribute names from underscored table column names by convention
184
184
 
185
185
  on_changed do |row, type, row_data|
186
186
  puts "Row #{row} #{type}: #{row_data}"
187
+ $stdout.flush # for Windows
188
+ end
189
+
190
+ on_edited do |row, row_data| # only fires on direct table editing
191
+ puts "Row #{row} edited: #{row_data}"
192
+ $stdout.flush # for Windows
187
193
  end
188
194
  }
189
195
  }
@@ -356,6 +362,11 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
356
362
  - [Custom Keywords](#custom-keywords)
357
363
  - [Observer Pattern](#observer-pattern)
358
364
  - [Data-Binding](#data-binding)
365
+ - [Bidirectional (Two-Way) Data-Binding](#bidirectional-two-way-data-binding)
366
+ - [Table Data-Binding](#table-data-binding)
367
+ - [Unidirectional (One-Way) Data-Binding](#unidirectional-one-way-data-binding)
368
+ - [Data-Binding API](#data-binding-api)
369
+ - [Data-Binding Gotchas](#data-binding-gotchas)
359
370
  - [API Gotchas](#api-gotchas)
360
371
  - [Original API](#original-api)
361
372
  - [Packaging](#packaging)
@@ -495,7 +506,7 @@ gem install glimmer-dsl-libui
495
506
  Or install via Bundler `Gemfile`:
496
507
 
497
508
  ```ruby
498
- gem 'glimmer-dsl-libui', '~> 0.4.11'
509
+ gem 'glimmer-dsl-libui', '~> 0.4.15'
499
510
  ```
500
511
 
501
512
  Test that installation worked by running the [Meta-Example](#examples):
@@ -764,7 +775,7 @@ data = [
764
775
  ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
765
776
  ]
766
777
 
767
- window('Contacts', 600, 600) { |w|
778
+ window('Contacts', 600, 600) {
768
779
  margined true
769
780
 
770
781
  vertical_box {
@@ -797,8 +808,8 @@ window('Contacts', 600, 600) { |w|
797
808
 
798
809
  on_clicked do
799
810
  new_row = [@name_entry.text, @email_entry.text, @phone_entry.text, @city_entry.text, @state_entry.text]
800
- if new_row.include?('')
801
- msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
811
+ if new_row.map(&:to_s).include?('')
812
+ msg_box_error('Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
802
813
  else
803
814
  data << new_row # automatically inserts a row into the table due to implicit data-binding
804
815
  @unfiltered_data = data.dup
@@ -842,13 +853,21 @@ window('Contacts', 600, 600) { |w|
842
853
 
843
854
  on_changed do |row, type, row_data|
844
855
  puts "Row #{row} #{type}: #{row_data}"
856
+ $stdout.flush # for Windows
857
+ end
858
+
859
+ on_edited do |row, row_data| # only fires on direct table editing
860
+ puts "Row #{row} edited: #{row_data}"
861
+ $stdout.flush # for Windows
845
862
  end
846
863
  }
847
864
  }
848
865
  }.show
849
866
  ```
850
867
 
851
- ![glimmer-dsl-libui-linux-form-table.png](images/glimmer-dsl-libui-linux-form-table.png)
868
+ Mac | Windows | Linux
869
+ ----|---------|------
870
+ ![glimmer-dsl-libui-mac-form-table.png](images/glimmer-dsl-libui-mac-form-table.png) | ![glimmer-dsl-libui-windows-form-table.png](images/glimmer-dsl-libui-windows-form-table.png) | ![glimmer-dsl-libui-linux-form-table.png](images/glimmer-dsl-libui-linux-form-table.png)
852
871
 
853
872
  Learn more by checking out [examples](#examples).
854
873
 
@@ -926,9 +945,15 @@ Check [examples/basic_scrolling_area.rb](#basic-scrolling-area) for a more detai
926
945
 
927
946
  #### Area Path Shapes
928
947
 
948
+ `area` can have geometric shapes drawn by adding `path` elements.
949
+
950
+ To add `path` shapes under an `area`, you can do so:
951
+ - Explicitly: by adding `path` under `area` and nesting shapes (e.g. `rectangle`) underneath that share the same `fill`/`stroke`/`transform` properties
952
+ - Implicitly: by adding shapes directly under `area` when the shapes have unique `fill`/`stroke`/`transform` properties ([Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) automatically constructs `path`s as intermediary parents for shapes directly added under `area`)
953
+
929
954
  `path` can receive a `draw_fill_mode` argument that can accept values `:winding` or `:alternate` and defaults to `:winding`.
930
955
 
931
- Available nested `path` shapes:
956
+ Available `path` shapes (that can be nested explicitly under `path` or implicitly under `area` directly):
932
957
  - `rectangle(x as Numeric, y as Numeric, width as Numeric, height as Numeric)`
933
958
  - `square(x as Numeric, y as Numeric, length as Numeric)`
934
959
  - `arc(x_center as Numeric, y_center as Numeric, radius as Numeric, start_angle as Numeric, sweep as Numeric, is_negative as Boolean)`
@@ -1014,7 +1039,8 @@ Given that it is very new and is not a [libui](https://github.com/andlabs/libui)
1014
1039
  - It only supports the `.png` file format.
1015
1040
  - [libui](https://github.com/andlabs/libui) pixel-by-pixel rendering performance is slow.
1016
1041
  - Including an `image` inside an `area` `on_draw` listener improves performance due to not retaining pixel/line data in memory.
1017
- - Supplying `width` and `height` (2nd and 3rd arguments) greatly improves performance when shrinking image.
1042
+ - Supplying `width` and `height` options greatly improves performance when shrinking image (e.g. `image('somefile.png', width: 24, height: 24)`). You can also supply one of the two dimensions, and the other one gets calculated automatically while preserving original aspect ratio (e.g. `image('somefile.png', height: 24)`)
1043
+ - [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) lets you optionally specify `x` and `y` in addition to `file`, `width` and `height` (5 arguments total) to offset image location.
1018
1044
 
1019
1045
  Currently, it is recommended to use `image` with very small `width` and `height` values only (e.g. 24x24).
1020
1046
 
@@ -1033,7 +1059,11 @@ include Glimmer
1033
1059
 
1034
1060
  window('Basic Image', 96, 96) {
1035
1061
  area {
1036
- image(File.expand_path('icons/glimmer.png', __dir__), 96, 96)
1062
+ image(File.expand_path('icons/glimmer.png', __dir__), height: 96) # width is automatically calculated from height while preserving original aspect ratio
1063
+ # image(File.expand_path('icons/glimmer.png', __dir__), width: 96, height: 96) # you can specify both width and height options
1064
+ # image(File.expand_path('icons/glimmer.png', __dir__), 96, 96) # you can specify width, height as args
1065
+ # image(File.expand_path('../icons/glimmer.png', __dir__), 0, 0, 96, 96) # you can specify x, y, width, height args as alternative
1066
+ # image(File.expand_path('../icons/glimmer.png', __dir__), x: 0, y: 0, width: 96, height: 96) # you can specify x, y, width, height options as alternative
1037
1067
  }
1038
1068
  }.show
1039
1069
  ```
@@ -1065,6 +1095,8 @@ window('Basic Image', 96, 96) {
1065
1095
  area {
1066
1096
  image {
1067
1097
  file File.expand_path('icons/glimmer.png', __dir__)
1098
+ # x 0 # default
1099
+ # y 0 # default
1068
1100
  width 96
1069
1101
  height 96
1070
1102
  }
@@ -1320,13 +1352,16 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
1320
1352
  - `horizontal_box`, `vertical_box`, `grid`, and `form` controls have `padded` as `true` upon instantiation to ensure more user-friendly GUI by default
1321
1353
  - `group` controls have `margined` as `true` upon instantiation to ensure more user-friendly GUI by default
1322
1354
  - All controls nested under a `horizontal_box`, `vertical_box`, and `form` have `stretchy` property (fill maximum space) as `true` by default (passed to `box_append`/`form_append` method)
1355
+ - If an event listener is repeated under a control (e.g. two `on_clicked {}` listeners under `button`), it does not overwrite the previous listener, yet it is added to an `Array` of listeners for the event. [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) provides multiple-event-listener support unlike [LibUI](https://github.com/kojix2/LibUI)
1323
1356
  - `window` instatiation args can be left off, having the following defaults when unspecified: `title` as `''`, `width` as `190`, `height` as `150`, and `has_menubar` as `true`)
1324
1357
  - `window` has an `on_closing` listener by default that quits application upon hitting the close button (can be overridden with a manual `on_closing` implementation that returns integer `0` for success)
1325
1358
  - `group` has `title` property default to `''` if not specified in instantiation args, so it can be instantiated without args with `title` property specified in nested block (e.g. `group {title 'Address'; ...}`)
1326
1359
  - `button`, `checkbox`, and `label` have `text` default to `''` if not specified in instantiation args, so they can be instantiated without args with `text` property specified in nested block (e.g. `button {text 'Greet'; on_clicked {puts 'Hello'}}`)
1327
1360
  - `quit_menu_item` has an `on_clicked` listener by default that quits application upon selecting the quit menu item (can be overridden with a manual `on_clicked` implementation that returns integer `0` for success)
1328
1361
  - If an `on_closing` listener was defined on `window` and it does not return an integer, default exit behavior is assumed (`window.destroy` is called followed by `LibUI.quit`, returning `0`).
1329
- - If an `on_clicked` listener was defined on `quit_menu_item` and it does not return an integer, default exit behavior is assumed (`main_window.destroy` is called followed by `LibUI.quit`, returning `0`).
1362
+ - If multiple `on_closing` listeners were added for `window`, and none return an integer, they are all executed. On the other hand, if one of them returns an integer, it is counted as the final return value and stops the chain of listener execution.
1363
+ - If an `on_clicked` listener was defined on `quit_menu_item` and it does not return an integer, default exit behavior is assumed (`quit_menu_item.destroy` and `main_window.destroy` are called followed by `LibUI.quit`, returning `0`).
1364
+ - If multiple `on_clicked` listeners were added for `quit_menu_item`, and none return an integer, they are all executed. On the other hand, if one of them returns an integer, it is counted as the final return value and stops the chain of listener execution.
1330
1365
  - All boolean property readers return `true` or `false` in Ruby instead of the [libui](https://github.com/andlabs/libui) original `0` or `1` in C.
1331
1366
  - All boolean property writers accept `true`/`false` in addition to `1`/`0` in Ruby
1332
1367
  - All string property readers return a `String` object in Ruby instead of the [libui](https://github.com/andlabs/libui) Fiddle pointer object.
@@ -1347,7 +1382,8 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
1347
1382
  - Automatically provide shifted `:key` characters in `area_key_event` provided in `area` key listeners `on_key_event`, `on_key_down`, and `on_key_up`
1348
1383
  - `scrolling_area` `width` and `height` default to main window width and height if not specified.
1349
1384
  - `scrolling_area` `#scroll_to` 3rd and 4th arguments (`width` and `height`) default to main window width and height if not specified.
1350
- - `area` paths are specified declaratively with figures underneath (e.g. `rectangle`) and `area` draw listener is automatically generated
1385
+ - `area` paths are specified declaratively with shapes/figures underneath (e.g. `rectangle`), and `area` draw listener is automatically generated
1386
+ - `area` path shapes can be added directly under `area` without declaring `path` explicitly as a convenient shorthand
1351
1387
  - Observe figure properties (e.g. `rectangle` `width`) for changes and automatically redraw containing area accordingly
1352
1388
  - Observe `path` `fill` and `stroke` hashes for changes and automatically redraw containing area accordingly
1353
1389
  - Observe `text` and `string` properties for changes and automatically redraw containing area accordingly
@@ -1502,6 +1538,8 @@ Data-binding supports utilizing the [MVP (Model View Presenter)](https://en.wiki
1502
1538
 
1503
1539
  ![MVP](https://www.researchgate.net/profile/Gilles-Perrouin/publication/320249584/figure/fig8/AS:668260987068418@1536337243385/Model-view-presenter-architecture.png)
1504
1540
 
1541
+ #### Bidirectional (Two-Way) Data-Binding
1542
+
1505
1543
  [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):
1506
1544
  - `checkbox`: `checked`
1507
1545
  - `check_menu_item`: `checked`
@@ -1542,6 +1580,162 @@ entry {
1542
1580
 
1543
1581
  That is data-binding `entered_text` attribute on `self` to `entry` `text` property and printing text after write to the model.
1544
1582
 
1583
+ ##### Table Data-Binding
1584
+
1585
+ One note about `table` `cell_rows` data-binding is that it works with either:
1586
+ - Raw data `Array` (rows) of `Array`s (column cells)
1587
+ - Model `Array` (rows) of objects having attributes (column cells) matching the underscored names of `table` columns by convention. Model attribute names can be overridden when needed by passing an `Array` enumerating all mapped model attributes in the order of `table` columns or alternatively a `Hash` mapping only the column names that have model attribute names different from their table column underscored version.
1588
+
1589
+ Example of `table` implicit data-binding of `cell_rows` to raw data `Array` of `Array`s (you may copy/paste in [`girb`](#girb-glimmer-irb)):
1590
+
1591
+ ```ruby
1592
+ require 'glimmer-dsl-libui'
1593
+
1594
+ include Glimmer
1595
+
1596
+ data = [
1597
+ ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
1598
+ ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
1599
+ ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
1600
+ ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
1601
+ ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
1602
+ ]
1603
+
1604
+ window('Contacts', 600, 600) {
1605
+ table {
1606
+ text_column('Name')
1607
+ text_column('Email')
1608
+ text_column('Phone')
1609
+ text_column('City')
1610
+ text_column('State')
1611
+
1612
+ cell_rows data
1613
+ }
1614
+ }.show
1615
+ ```
1616
+
1617
+ Example of `table` explicit data-binding of `cell_rows` to Model `Array` (you may copy/paste in [`girb`](#girb-glimmer-irb)):
1618
+
1619
+ ```ruby
1620
+ require 'glimmer-dsl-libui'
1621
+
1622
+ class SomeTable
1623
+ Contact = Struct.new(:name, :email, :phone, :city, :state)
1624
+
1625
+ include Glimmer
1626
+
1627
+ attr_accessor :contacts
1628
+
1629
+ def initialize
1630
+ @contacts = [
1631
+ Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
1632
+ Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
1633
+ Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
1634
+ Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
1635
+ Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
1636
+ ]
1637
+ end
1638
+
1639
+ def launch
1640
+ window('Contacts', 600, 200) {
1641
+ table {
1642
+ text_column('Name')
1643
+ text_column('Email')
1644
+ text_column('Phone')
1645
+ text_column('City')
1646
+ text_column('State')
1647
+
1648
+ cell_rows <=> [self, :contacts] # explicit data-binding to self.contacts Model Array, auto-inferring model attribute names from underscored table column names by convention
1649
+ }
1650
+ }.show
1651
+ end
1652
+ end
1653
+
1654
+ SomeTable.new.launch
1655
+ ```
1656
+
1657
+ Example of `table` explicit data-binding of `cell_rows` to Model `Array` with `column_attributes` `Hash` mapping for custom column names (you may copy/paste in [`girb`](#girb-glimmer-irb)):
1658
+
1659
+ ```ruby
1660
+ require 'glimmer-dsl-libui'
1661
+
1662
+ class SomeTable
1663
+ Contact = Struct.new(:name, :email, :phone, :city, :state)
1664
+
1665
+ include Glimmer
1666
+
1667
+ attr_accessor :contacts
1668
+
1669
+ def initialize
1670
+ @contacts = [
1671
+ Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
1672
+ Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
1673
+ Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
1674
+ Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
1675
+ Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
1676
+ ]
1677
+ end
1678
+
1679
+ def launch
1680
+ window('Contacts', 600, 200) {
1681
+ table {
1682
+ text_column('Name')
1683
+ text_column('Email')
1684
+ text_column('Phone')
1685
+ text_column('City/Town')
1686
+ text_column('State/Province')
1687
+
1688
+ cell_rows <=> [self, :contacts, column_attributes: {'City/Town' => :city, 'State/Province' => :state}]
1689
+ }
1690
+ }.show
1691
+ end
1692
+ end
1693
+
1694
+ SomeTable.new.launch
1695
+ ```
1696
+
1697
+ Example of `table` explicit data-binding of `cell_rows` to Model `Array` with complete `column_attributes` `Array` mapping (you may copy/paste in [`girb`](#girb-glimmer-irb)):
1698
+
1699
+ ```ruby
1700
+ require 'glimmer-dsl-libui'
1701
+
1702
+ class SomeTable
1703
+ Contact = Struct.new(:name, :email, :phone, :city, :state)
1704
+
1705
+ include Glimmer
1706
+
1707
+ attr_accessor :contacts
1708
+
1709
+ def initialize
1710
+ @contacts = [
1711
+ Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
1712
+ Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
1713
+ Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
1714
+ Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
1715
+ Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
1716
+ ]
1717
+ end
1718
+
1719
+ def launch
1720
+ window('Contacts', 600, 200) {
1721
+ table {
1722
+ text_column('Full Name')
1723
+ text_column('Email Address')
1724
+ text_column('Phone Number')
1725
+ text_column('City or Town')
1726
+ text_column('State or Province')
1727
+
1728
+ cell_rows <=> [self, :contacts, column_attributes: [:name, :email, :phone, :city, :state]]
1729
+ }
1730
+ }.show
1731
+ end
1732
+ end
1733
+
1734
+ SomeTable.new.launch
1735
+ ```
1736
+
1737
+ #### Unidirectional (One-Way) Data-Binding
1738
+
1545
1739
  [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).
1546
1740
 
1547
1741
  Example of unidirectional data-binding:
@@ -1564,6 +1758,8 @@ window {
1564
1758
 
1565
1759
  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`.
1566
1760
 
1761
+ #### Data-Binding API
1762
+
1567
1763
  To summarize the data-binding API:
1568
1764
  - `view_property <=> [model, attribute, *read_or_write_options]`: Bidirectional (two-way) data-binding to Model attribute accessor
1569
1765
  - `view_property <= [model, attribute, *read_only_options]`: Unidirectional (one-way) data-binding to Model attribute reader
@@ -1594,11 +1790,13 @@ entry {
1594
1790
  }
1595
1791
  ```
1596
1792
 
1597
- Data-binding gotchas:
1793
+ Learn more from data-binding usage in [Login](#login) (4 data-binding versions), [Basic Entry](#basic-entry), [Form](#form), [Form Table](#form-table) (5 data-binding versions), [Method-Based Custom Keyword](#method-based-custom-keyword), [Snake](#snake) and [Tic Tac Toe](#tic_tac_toe) examples.
1794
+
1795
+ #### Data-Binding Gotchas
1796
+
1598
1797
  - 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)
1599
1798
  - Data-binding a property utilizes the control's listener associated with the property (e.g. `on_changed` for `entry` `text`), so you cannot hook into the listener directly anymore as that would negate data-binding. Instead, you can add an `after_write: ->(val) {}` option to perform something on trigger of the control listener instead.
1600
-
1601
- Learn more from data-binding usage in [Login](#login) (4 data-binding versions), [Basic Entry](#basic-entry), [Form](#form), [Form Table](#form-table), [Method-Based Custom Keyword](#method-based-custom-keyword), [Snake](#snake) and [Tic Tac Toe](#tic_tac_toe) examples.
1799
+ - Data-binding a View control to another View control directly is not a good idea. Instead, data-bind both View controls to the same Presenter/Model attribute, and that keeps them in sync while keeping the code decoupled.
1602
1800
 
1603
1801
  ### API Gotchas
1604
1802
 
@@ -2925,7 +3123,44 @@ UI.main
2925
3123
  UI.quit
2926
3124
  ```
2927
3125
 
2928
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
3126
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (passing file url as image):
3127
+
3128
+ ```ruby
3129
+ # frozen_string_literal: true
3130
+
3131
+ # NOTE:
3132
+ # This example displays images that can be freely downloaded from the Studio Ghibli website.
3133
+
3134
+ require 'glimmer-dsl-libui'
3135
+
3136
+ include Glimmer
3137
+
3138
+ IMAGE_ROWS = []
3139
+
3140
+ 50.times do |i|
3141
+ url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', (i + 1))
3142
+ puts "Processing Image: #{url}"; $stdout.flush # for Windows
3143
+ IMAGE_ROWS << [url] # array of one column cell
3144
+ rescue StandardError => e
3145
+ warn url, e.message
3146
+ end
3147
+
3148
+ window('The Red Turtle', 310, 350, false) {
3149
+ horizontal_box {
3150
+ table {
3151
+ image_column('www.ghibli.jp/works/red-turtle')
3152
+
3153
+ cell_rows IMAGE_ROWS
3154
+ }
3155
+ }
3156
+
3157
+ on_closing do
3158
+ puts 'Bye Bye'
3159
+ end
3160
+ }.show
3161
+ ```
3162
+
3163
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (automatic construction of `image`):
2929
3164
 
2930
3165
  ```ruby
2931
3166
  # NOTE:
@@ -2960,7 +3195,7 @@ window('The Red Turtle', 310, 350, false) {
2960
3195
  }.show
2961
3196
  ```
2962
3197
 
2963
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (manual construction of `image` from `image_part`):
3198
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (manual construction of `image` from `image_part`):
2964
3199
 
2965
3200
  ```ruby
2966
3201
  # NOTE:
@@ -3028,7 +3263,44 @@ Mac | Windows | Linux
3028
3263
  ----|---------|------
3029
3264
  ![glimmer-dsl-libui-mac-basic-table-image-text.png](images/glimmer-dsl-libui-mac-basic-table-image-text.png) | ![glimmer-dsl-libui-windows-basic-table-image-text.png](images/glimmer-dsl-libui-windows-basic-table-image-text.png) | ![glimmer-dsl-libui-linux-basic-table-image-text.png](images/glimmer-dsl-libui-linux-basic-table-image-text.png)
3030
3265
 
3031
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
3266
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (passing file url as image):
3267
+
3268
+ ```ruby
3269
+ # frozen_string_literal: true
3270
+
3271
+ # NOTE:
3272
+ # This example displays images that can be freely downloaded from the Studio Ghibli website.
3273
+
3274
+ require 'glimmer-dsl-libui'
3275
+
3276
+ include Glimmer
3277
+
3278
+ IMAGE_ROWS = []
3279
+
3280
+ 5.times do |i|
3281
+ url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', (i + 1))
3282
+ puts "Processing Image: #{url}"; $stdout.flush # for Windows
3283
+ text = url.sub('https://www.ghibli.jp/gallery/thumb-redturtle', '').sub('.png', '')
3284
+ IMAGE_ROWS << [[url, text], [url, text]] # cell values are dual-element arrays
3285
+ rescue StandardError => e
3286
+ warn url, e.message
3287
+ end
3288
+
3289
+ window('The Red Turtle', 670, 350) {
3290
+ horizontal_box {
3291
+ table {
3292
+ image_text_column('image/number')
3293
+ image_text_column('image/number (editable)') {
3294
+ editable true
3295
+ }
3296
+
3297
+ cell_rows IMAGE_ROWS
3298
+ }
3299
+ }
3300
+ }.show
3301
+ ```
3302
+
3303
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (automatic construction of `image`):
3032
3304
 
3033
3305
  ```ruby
3034
3306
  # NOTE:
@@ -3402,16 +3674,126 @@ Mac | Windows | Linux
3402
3674
  ----|---------|------
3403
3675
  ![glimmer-dsl-libui-mac-basic-table-color.png](images/glimmer-dsl-libui-mac-basic-table-color.png) | ![glimmer-dsl-libui-windows-basic-table-color.png](images/glimmer-dsl-libui-windows-basic-table-color.png) | ![glimmer-dsl-libui-linux-basic-table-color.png](images/glimmer-dsl-libui-linux-basic-table-color.png)
3404
3676
 
3405
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
3677
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding) to model rows using a presenter):
3406
3678
 
3407
3679
  ```ruby
3408
- # frozen_string_literal: true
3680
+ require 'glimmer-dsl-libui'
3409
3681
 
3682
+ class BasicTableColor
3683
+ Animal = Struct.new(:name, :sound, :mammal)
3684
+
3685
+ class AnimalPresenter < Animal
3686
+ def name_color
3687
+ color = case name
3688
+ when 'cat'
3689
+ :red
3690
+ when 'dog'
3691
+ :yellow
3692
+ when 'chicken'
3693
+ :beige
3694
+ when 'horse'
3695
+ :purple
3696
+ when 'cow'
3697
+ :gray
3698
+ end
3699
+ [name, color]
3700
+ end
3701
+
3702
+ def sound_color
3703
+ color = case name
3704
+ when 'cat', 'chicken', 'cow'
3705
+ :blue
3706
+ when 'dog', 'horse'
3707
+ {r: 240, g: 32, b: 32}
3708
+ end
3709
+ [sound, color]
3710
+ end
3711
+
3712
+ def mammal_description_color
3713
+ color = case name
3714
+ when 'cat', 'dog', 'horse', 'cow'
3715
+ :green
3716
+ when 'chicken'
3717
+ :red
3718
+ end
3719
+ [mammal, 'mammal', color]
3720
+ end
3721
+
3722
+ def image_description_color
3723
+ color = case name
3724
+ when 'cat', 'dog', 'horse'
3725
+ :dark_blue
3726
+ when 'chicken'
3727
+ :beige
3728
+ when 'cow'
3729
+ :brown
3730
+ end
3731
+ [img, 'Glimmer', color]
3732
+ end
3733
+
3734
+ def img
3735
+ # scale image to 24x24 (can be passed as file path String only instead of Array to avoid scaling)
3736
+ [File.expand_path('../icons/glimmer.png', __dir__), 24, 24]
3737
+ end
3738
+
3739
+ def background_color
3740
+ case name
3741
+ when 'cat'
3742
+ {r: 255, g: 120, b: 0, a: 0.5}
3743
+ when 'dog'
3744
+ :skyblue
3745
+ when 'chicken'
3746
+ {r: 5, g: 120, b: 110}
3747
+ when 'horse'
3748
+ '#13a1fb'
3749
+ when 'cow'
3750
+ 0x12ff02
3751
+ end
3752
+ end
3753
+ end
3754
+
3755
+ include Glimmer
3756
+
3757
+ attr_accessor :animals
3758
+
3759
+ def initialize
3760
+ @animals = [
3761
+ AnimalPresenter.new('cat', 'meow', true),
3762
+ AnimalPresenter.new('dog', 'woof', true),
3763
+ AnimalPresenter.new('chicken', 'cock-a-doodle-doo', false),
3764
+ AnimalPresenter.new('horse', 'neigh', true),
3765
+ AnimalPresenter.new('cow', 'moo', true),
3766
+ ]
3767
+ end
3768
+
3769
+ def launch
3770
+ window('Animals', 500, 200) {
3771
+ horizontal_box {
3772
+ table {
3773
+ text_color_column('Animal')
3774
+ text_color_column('Sound')
3775
+ checkbox_text_color_column('Description')
3776
+ image_text_color_column('GUI')
3777
+ background_color_column # must always be the last column and always expects data-binding model attribute `background_color` when binding to Array of models
3778
+
3779
+ cell_rows <= [self, :animals, column_attributes: {'Animal' => :name_color, 'Sound' => :sound_color, 'Description' => :mammal_description_color, 'GUI' => :image_description_color}]
3780
+ }
3781
+ }
3782
+ }.show
3783
+ end
3784
+ end
3785
+
3786
+ BasicTableColor.new.launch
3787
+ ```
3788
+
3789
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (with implicit [data-binding](#data-binding) to raw data rows):
3790
+
3791
+ ```ruby
3410
3792
  require 'glimmer-dsl-libui'
3411
3793
 
3412
3794
  include Glimmer
3413
3795
 
3414
- img = image(File.expand_path('../icons/glimmer.png', __dir__), 24, 24)
3796
+ img = [File.expand_path('../icons/glimmer.png', __dir__), 24, 24] # scales image to 24x24 (can be passed as file path String only instead of Array to avoid scaling)
3415
3797
 
3416
3798
  data = [
3417
3799
  [['cat', :red] , ['meow', :blue] , [true, 'mammal', :green], [img, 'Glimmer', :dark_blue], {r: 255, g: 120, b: 0, a: 0.5}],
@@ -3436,7 +3818,7 @@ window('Animals', 500, 200) {
3436
3818
  }.show
3437
3819
  ```
3438
3820
 
3439
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (manual construction of [libui](https://github.com/andlabs/libui) `image` from `image_part`):
3821
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (with implicit [data-binding](#data-binding) to raw data rows and manual construction of [libui](https://github.com/andlabs/libui) `image` from `image_part`):
3440
3822
 
3441
3823
  ```ruby
3442
3824
  require 'glimmer-dsl-libui'
@@ -3738,7 +4120,11 @@ window('Basic Image', 96, 96) {
3738
4120
  # image pixel rendered. Check basic_image2.rb for a faster alternative using on_draw manually.
3739
4121
  #
3740
4122
  # It is recommended to pass width/height args to shrink image and achieve faster performance.
3741
- image(File.expand_path('../icons/glimmer.png', __dir__), 96, 96)
4123
+ image(File.expand_path('../icons/glimmer.png', __dir__), height: 96) # width is automatically calculated from height while preserving original aspect ratio
4124
+ # image(File.expand_path('../icons/glimmer.png', __dir__), width: 96, height: 96) # you can specify both width, height options as alternative
4125
+ # image(File.expand_path('../icons/glimmer.png', __dir__), 96, 96) # you can specify width, height args as alternative
4126
+ # image(File.expand_path('../icons/glimmer.png', __dir__), 0, 0, 96, 96) # you can specify x, y, width, height args as alternative
4127
+ # image(File.expand_path('../icons/glimmer.png', __dir__), x: 0, y: 0, width: 96, height: 96) # you can specify x, y, width, height options as alternative
3742
4128
  }
3743
4129
  }.show
3744
4130
  ```
@@ -3746,8 +4132,6 @@ window('Basic Image', 96, 96) {
3746
4132
  New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (better performance via `on_draw`):
3747
4133
 
3748
4134
  ```ruby
3749
- # frozen_string_literal: true
3750
-
3751
4135
  require 'glimmer-dsl-libui'
3752
4136
 
3753
4137
  include Glimmer
@@ -3755,7 +4139,7 @@ include Glimmer
3755
4139
  window('Basic Image', 96, 96) {
3756
4140
  area {
3757
4141
  on_draw do |area_draw_params|
3758
- image(File.expand_path('../icons/glimmer.png', __dir__), 96, 96)
4142
+ image(File.expand_path('../icons/glimmer.png', __dir__), height: 96)
3759
4143
  end
3760
4144
  }
3761
4145
  }.show
@@ -3764,8 +4148,6 @@ window('Basic Image', 96, 96) {
3764
4148
  New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (explicit properties):
3765
4149
 
3766
4150
  ```ruby
3767
- # frozen_string_literal: true
3768
-
3769
4151
  require 'glimmer-dsl-libui'
3770
4152
 
3771
4153
  include Glimmer
@@ -3782,7 +4164,9 @@ window('Basic Image', 96, 96) {
3782
4164
  # It is recommended to pass width/height args to shrink image and achieve faster performance.
3783
4165
  image {
3784
4166
  file File.expand_path('../icons/glimmer.png', __dir__)
3785
- width 96
4167
+ # x 0 # default
4168
+ # y 0 # default
4169
+ # width 96 # gets calculated from height while preserving original aspect ratio of 512x512
3786
4170
  height 96
3787
4171
  }
3788
4172
  }
@@ -3792,8 +4176,6 @@ window('Basic Image', 96, 96) {
3792
4176
  New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 4 (better performance with `on_draw` when setting explicit properties):
3793
4177
 
3794
4178
  ```ruby
3795
- # frozen_string_literal: true
3796
-
3797
4179
  require 'glimmer-dsl-libui'
3798
4180
 
3799
4181
  include Glimmer
@@ -3803,7 +4185,6 @@ window('Basic Image', 96, 96) {
3803
4185
  on_draw do |area_draw_params|
3804
4186
  image {
3805
4187
  file File.expand_path('../icons/glimmer.png', __dir__)
3806
- width 96
3807
4188
  height 96
3808
4189
  }
3809
4190
  end
@@ -5671,7 +6052,7 @@ Glimmer::LibUI.timer(1) do
5671
6052
  data[0][2] = cpu_percentage_value
5672
6053
  end
5673
6054
 
5674
- window('CPU Percentage', 400, 200) {
6055
+ window('CPU Percentage', 400, 50) {
5675
6056
  vertical_box {
5676
6057
  table {
5677
6058
  text_column('Name')
@@ -6344,6 +6725,11 @@ window('Editable column animal sounds', 400, 200) {
6344
6725
  }
6345
6726
 
6346
6727
  cell_rows data
6728
+
6729
+ on_edited do |row, row_data| # only fires on direct table editing
6730
+ puts "Row #{row} edited: #{row_data}"
6731
+ $stdout.flush
6732
+ end
6347
6733
  }
6348
6734
  }
6349
6735
 
@@ -6456,7 +6842,7 @@ class FormTable
6456
6842
  end
6457
6843
 
6458
6844
  def launch
6459
- window('Contacts', 600, 600) { |w|
6845
+ window('Contacts', 600, 600) {
6460
6846
  margined true
6461
6847
 
6462
6848
  vertical_box {
@@ -6494,8 +6880,8 @@ class FormTable
6494
6880
 
6495
6881
  on_clicked do
6496
6882
  new_row = [name, email, phone, city, state]
6497
- if new_row.include?('')
6498
- msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
6883
+ if new_row.map(&:to_s).include?('')
6884
+ msg_box_error('Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
6499
6885
  else
6500
6886
  @contacts << Contact.new(*new_row) # automatically inserts a row into the table due to explicit data-binding
6501
6887
  @unfiltered_contacts = @contacts.dup
@@ -6536,10 +6922,16 @@ class FormTable
6536
6922
  text_column('State')
6537
6923
 
6538
6924
  editable true
6539
- cell_rows <=> [self, :contacts] # explicit data-binding to Model Array
6925
+ cell_rows <=> [self, :contacts] # explicit data-binding to self.contacts Model Array, auto-inferring model attribute names from underscored table column names by convention
6540
6926
 
6541
6927
  on_changed do |row, type, row_data|
6542
6928
  puts "Row #{row} #{type}: #{row_data}"
6929
+ $stdout.flush # for Windows
6930
+ end
6931
+
6932
+ on_edited do |row, row_data| # only fires on direct table editing
6933
+ puts "Row #{row} edited: #{row_data}"
6934
+ $stdout.flush # for Windows
6543
6935
  end
6544
6936
  }
6545
6937
  }
@@ -6550,13 +6942,13 @@ end
6550
6942
  FormTable.new.launch
6551
6943
  ```
6552
6944
 
6553
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
6945
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (with explicit [data-binding](#data-binding)):
6554
6946
 
6555
6947
  ```ruby
6556
6948
  require 'glimmer-dsl-libui'
6557
6949
 
6558
6950
  class FormTable
6559
- Contact = Struct.new(:name, :email, :phone, :city, :state)
6951
+ Contact = Struct.new(:name, :email, :phone, :city, :state_province)
6560
6952
 
6561
6953
  include Glimmer
6562
6954
 
@@ -6573,7 +6965,7 @@ class FormTable
6573
6965
  end
6574
6966
 
6575
6967
  def launch
6576
- window('Contacts', 600, 600) { |w|
6968
+ window('Contacts', 600, 600) {
6577
6969
  margined true
6578
6970
 
6579
6971
  vertical_box {
@@ -6611,8 +7003,8 @@ class FormTable
6611
7003
 
6612
7004
  on_clicked do
6613
7005
  new_row = [name, email, phone, city, state]
6614
- if new_row.include?('')
6615
- msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
7006
+ if new_row.map(&:to_s).include?('')
7007
+ msg_box_error('Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
6616
7008
  else
6617
7009
  @contacts << Contact.new(*new_row) # automatically inserts a row into the table due to implicit data-binding
6618
7010
  @unfiltered_contacts = @contacts.dup
@@ -6650,13 +7042,19 @@ class FormTable
6650
7042
  text_column('Email')
6651
7043
  text_column('Phone')
6652
7044
  text_column('City')
6653
- text_column('State/Province')
7045
+ text_column('State')
6654
7046
 
6655
7047
  editable true
6656
- cell_rows <=> [self, :contacts, column_attributes: {'State/Province' => :state}] # explicit data-binding to Model Array with column_attributes mapping for a specific column
7048
+ cell_rows <=> [self, :contacts, column_attributes: {'State' => :state_province}] # explicit data-binding to Model Array with column_attributes mapping for a specific column
6657
7049
 
6658
7050
  on_changed do |row, type, row_data|
6659
7051
  puts "Row #{row} #{type}: #{row_data}"
7052
+ $stdout.flush # for Windows
7053
+ end
7054
+
7055
+ on_edited do |row, row_data| # only fires on direct table editing
7056
+ puts "Row #{row} edited: #{row_data}"
7057
+ $stdout.flush # for Windows
6660
7058
  end
6661
7059
  }
6662
7060
  }
@@ -6667,7 +7065,7 @@ end
6667
7065
  FormTable.new.launch
6668
7066
  ```
6669
7067
 
6670
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
7068
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (with explicit [data-binding](#data-binding)):
6671
7069
 
6672
7070
  ```ruby
6673
7071
 
@@ -6691,7 +7089,7 @@ class FormTable
6691
7089
  end
6692
7090
 
6693
7091
  def launch
6694
- window('Contacts', 600, 600) { |w|
7092
+ window('Contacts', 600, 600) {
6695
7093
  margined true
6696
7094
 
6697
7095
  vertical_box {
@@ -6729,8 +7127,8 @@ class FormTable
6729
7127
 
6730
7128
  on_clicked do
6731
7129
  new_row = [name, email, phone, city, state]
6732
- if new_row.include?('')
6733
- msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
7130
+ if new_row.map(&:to_s).include?('')
7131
+ msg_box_error('Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
6734
7132
  else
6735
7133
  @contacts << Contact.new(*new_row) # automatically inserts a row into the table due to implicit data-binding
6736
7134
  @unfiltered_contacts = @contacts.dup
@@ -6775,6 +7173,12 @@ class FormTable
6775
7173
 
6776
7174
  on_changed do |row, type, row_data|
6777
7175
  puts "Row #{row} #{type}: #{row_data}"
7176
+ $stdout.flush # for Windows
7177
+ end
7178
+
7179
+ on_edited do |row, row_data| # only fires on direct table editing
7180
+ puts "Row #{row} edited: #{row_data}"
7181
+ $stdout.flush # for Windows
6778
7182
  end
6779
7183
  }
6780
7184
  }
@@ -6806,7 +7210,7 @@ class FormTable
6806
7210
  end
6807
7211
 
6808
7212
  def launch
6809
- window('Contacts', 600, 600) { |w|
7213
+ window('Contacts', 600, 600) {
6810
7214
  margined true
6811
7215
 
6812
7216
  vertical_box {
@@ -6844,8 +7248,8 @@ class FormTable
6844
7248
 
6845
7249
  on_clicked do
6846
7250
  new_row = [name, email, phone, city, state]
6847
- if new_row.include?('')
6848
- msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
7251
+ if new_row.map(&:to_s).include?('')
7252
+ msg_box_error('Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
6849
7253
  else
6850
7254
  data << new_row # automatically inserts a row into the table due to implicit data-binding
6851
7255
  @unfiltered_data = data.dup
@@ -6890,6 +7294,12 @@ class FormTable
6890
7294
 
6891
7295
  on_changed do |row, type, row_data|
6892
7296
  puts "Row #{row} #{type}: #{row_data}"
7297
+ $stdout.flush # for Windows
7298
+ end
7299
+
7300
+ on_edited do |row, row_data| # only fires on direct table editing
7301
+ puts "Row #{row} edited: #{row_data}"
7302
+ $stdout.flush # for Windows
6893
7303
  end
6894
7304
  }
6895
7305
  }
@@ -6915,7 +7325,7 @@ data = [
6915
7325
  ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
6916
7326
  ]
6917
7327
 
6918
- window('Contacts', 600, 600) { |w|
7328
+ window('Contacts', 600, 600) {
6919
7329
  margined true
6920
7330
 
6921
7331
  vertical_box {
@@ -6948,8 +7358,8 @@ window('Contacts', 600, 600) { |w|
6948
7358
 
6949
7359
  on_clicked do
6950
7360
  new_row = [@name_entry.text, @email_entry.text, @phone_entry.text, @city_entry.text, @state_entry.text]
6951
- if new_row.include?('')
6952
- msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
7361
+ if new_row.map(&:to_s).include?('')
7362
+ msg_box_error('Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
6953
7363
  else
6954
7364
  data << new_row # automatically inserts a row into the table due to implicit data-binding
6955
7365
  @unfiltered_data = data.dup
@@ -6993,6 +7403,12 @@ window('Contacts', 600, 600) { |w|
6993
7403
 
6994
7404
  on_changed do |row, type, row_data|
6995
7405
  puts "Row #{row} #{type}: #{row_data}"
7406
+ $stdout.flush # for Windows
7407
+ end
7408
+
7409
+ on_edited do |row, row_data| # only fires on direct table editing
7410
+ puts "Row #{row} edited: #{row_data}"
7411
+ $stdout.flush # for Windows
6996
7412
  end
6997
7413
  }
6998
7414
  }