glimmer-dsl-libui 0.4.10 → 0.4.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.10
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
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)
@@ -77,6 +77,127 @@ Mac | Windows | Linux
77
77
  ----|---------|------
78
78
  ![glimmer-dsl-libui-mac-basic-table-progress-bar.png](images/glimmer-dsl-libui-mac-basic-table-progress-bar.png) | ![glimmer-dsl-libui-windows-basic-table-progress-bar.png](images/glimmer-dsl-libui-windows-basic-table-progress-bar.png) | ![glimmer-dsl-libui-linux-basic-table-progress-bar.png](images/glimmer-dsl-libui-linux-basic-table-progress-bar.png)
79
79
 
80
+ Form Table
81
+
82
+ ```ruby
83
+ require 'glimmer-dsl-libui'
84
+
85
+ class FormTable
86
+ Contact = Struct.new(:name, :email, :phone, :city, :state)
87
+
88
+ include Glimmer
89
+
90
+ attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
91
+
92
+ def initialize
93
+ @contacts = [
94
+ Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
95
+ Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
96
+ Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
97
+ Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
98
+ Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
99
+ ]
100
+ end
101
+
102
+ def launch
103
+ window('Contacts', 600, 600) { |w|
104
+ margined true
105
+
106
+ vertical_box {
107
+ form {
108
+ stretchy false
109
+
110
+ entry {
111
+ label 'Name'
112
+ text <=> [self, :name] # bidirectional data-binding between entry text and self.name
113
+ }
114
+
115
+ entry {
116
+ label 'Email'
117
+ text <=> [self, :email]
118
+ }
119
+
120
+ entry {
121
+ label 'Phone'
122
+ text <=> [self, :phone]
123
+ }
124
+
125
+ entry {
126
+ label 'City'
127
+ text <=> [self, :city]
128
+ }
129
+
130
+ entry {
131
+ label 'State'
132
+ text <=> [self, :state]
133
+ }
134
+ }
135
+
136
+ button('Save Contact') {
137
+ stretchy false
138
+
139
+ on_clicked do
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.')
143
+ else
144
+ @contacts << Contact.new(*new_row) # automatically inserts a row into the table due to explicit data-binding
145
+ @unfiltered_contacts = @contacts.dup
146
+ self.name = '' # automatically clears name entry through explicit data-binding
147
+ self.email = ''
148
+ self.phone = ''
149
+ self.city = ''
150
+ self.state = ''
151
+ end
152
+ end
153
+ }
154
+
155
+ search_entry {
156
+ stretchy false
157
+ # bidirectional data-binding of text to self.filter_value with after_write option
158
+ text <=> [self, :filter_value,
159
+ after_write: ->(filter_value) { # execute after write to self.filter_value
160
+ @unfiltered_contacts ||= @contacts.dup
161
+ # Unfilter first to remove any previous filters
162
+ self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
163
+ # Now, apply filter if entered
164
+ unless filter_value.empty?
165
+ self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
166
+ contact.members.any? do |attribute|
167
+ contact[attribute].to_s.downcase.include?(filter_value.downcase)
168
+ end
169
+ end
170
+ end
171
+ }
172
+ ]
173
+ }
174
+
175
+ table {
176
+ text_column('Name')
177
+ text_column('Email')
178
+ text_column('Phone')
179
+ text_column('City')
180
+ text_column('State')
181
+
182
+ editable true
183
+ cell_rows <=> [self, :contacts] # explicit data-binding to Model Array
184
+
185
+ on_changed do |row, type, row_data|
186
+ puts "Row #{row} #{type}: #{row_data}"
187
+ end
188
+ }
189
+ }
190
+ }.show
191
+ end
192
+ end
193
+
194
+ FormTable.new.launch
195
+ ```
196
+
197
+ Mac | Windows | Linux
198
+ ----|---------|------
199
+ ![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)
200
+
80
201
  Area Gallery
81
202
 
82
203
  ```ruby
@@ -374,10 +495,20 @@ gem install glimmer-dsl-libui
374
495
  Or install via Bundler `Gemfile`:
375
496
 
376
497
  ```ruby
377
- gem 'glimmer-dsl-libui', '~> 0.4.10'
498
+ gem 'glimmer-dsl-libui', '~> 0.4.11'
499
+ ```
500
+
501
+ Test that installation worked by running the [Meta-Example](#examples):
502
+
503
+ ```
504
+ ruby -r glimmer-dsl-libui -e "require 'examples/meta_example'"
378
505
  ```
379
506
 
380
- 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.
507
+ Mac | Windows | Linux
508
+ ----|---------|------
509
+ ![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)
510
+
511
+ 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.
381
512
 
382
513
  Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
383
514
 
@@ -452,7 +583,7 @@ Keyword(Args) | Properties | Listeners
452
583
  `about_menu_item` | None | `on_clicked`
453
584
  `area` | `auto_draw_enabled` | `on_draw(area_draw_params)`, `on_mouse_event(area_mouse_event)`, `on_mouse_down(area_mouse_event)`, `on_mouse_up(area_mouse_event)`, `on_mouse_drag_started(area_mouse_event)`, `on_mouse_dragged(area_mouse_event)`, `on_mouse_dropped(area_mouse_event)`, `on_mouse_entered`, `on_mouse_exited`, `on_key_event(area_key_event)`, `on_key_down(area_key_event)`, `on_key_up(area_key_event)`
454
585
  `arc(x_center as Numeric, y_center as Numeric, radius as Numeric, start_angle as Numeric, sweep as Numeric, is_negative as Boolean)` | `x_center` (`Numeric`), `y_center` (`Numeric`), `radius` (`Numeric`), `start_angle` (`Numeric`), `sweep` (`Numeric`), `is_negative` (Boolean) | None
455
- `background_color_column(name as String)` | None | None
586
+ `background_color_column` | None | None
456
587
  `bezier(c1_x as Numeric, c1_y as Numeric, c2_x as Numeric, c2_y as Numeric, end_x as Numeric, end_y as Numeric)` | `c1_x` (`Numeric`), `c1_y` (`Numeric`), `c2_x` (`Numeric`), `c2_y` (`Numeric`), `end_x` (`Numeric`), `end_y` (`Numeric`) | None
457
588
  `button(text as String)` | `text` (`String`) | `on_clicked`
458
589
  `button_column(name as String)` | `enabled` (Boolean) | None
@@ -616,159 +747,147 @@ Note that the `cell_rows` property declaration results in "implicit data-binding
616
747
  - Inserting cell rows: Calling `Array#<<`, `Array#push`, `Array#prepend`, or any insertion/addition `Array` method automatically inserts rows in actual `table` control
617
748
  - Changing cell rows: Calling `Array#[]=`, `Array#map!`, or any update `Array` method automatically updates rows in actual `table` control
618
749
 
619
- Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
620
-
621
- ```ruby
622
- require 'glimmer-dsl-libui'
623
-
624
- class FormTable
625
- include Glimmer
626
-
627
- attr_accessor :name, :email, :phone, :city, :state, :filter_value
628
-
629
- def initialize
630
- @data = [
631
- ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
632
- ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
633
- ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
634
- ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
635
- ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
636
- ]
637
- end
638
-
639
- def launch
640
- window('Contacts', 600, 600) { |w|
641
- margined true
642
-
643
- vertical_box {
644
- form {
645
- stretchy false
646
-
647
- entry {
648
- label 'Name'
649
- text <=> [self, :name]
650
- }
651
-
652
- entry {
653
- label 'Email'
654
- text <=> [self, :email]
655
- }
656
-
657
- entry {
658
- label 'Phone'
659
- text <=> [self, :phone]
660
- }
661
-
662
- entry {
663
- label 'City'
664
- text <=> [self, :city]
665
- }
666
-
667
- entry {
668
- label 'State'
669
- text <=> [self, :state]
670
- }
671
- }
672
-
673
- button('Save Contact') {
674
- stretchy false
675
-
676
- on_clicked do
677
- new_row = [name, email, phone, city, state]
678
- if new_row.include?('')
679
- msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
680
- else
681
- @data << new_row # automatically inserts a row into the table due to implicit data-binding
682
- @unfiltered_data = @data.dup
683
- self.name = '' # automatically clears name entry through explicit data-binding
684
- self.email = ''
685
- self.phone = ''
686
- self.city = ''
687
- self.state = ''
688
- end
689
- end
690
- }
691
-
692
- search_entry {
693
- stretchy false
694
- text <=> [self, :filter_value, # bidirectional data-binding of text to self.filter_value with after_write option
695
- after_write: ->(filter_value) { # execute after write to self.filter_value
696
- @unfiltered_data ||= @data.dup
697
- # Unfilter first to remove any previous filters
698
- @data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
699
- # Now, apply filter if entered
700
- unless filter_value.empty?
701
- @data.filter! do |row_data| # affects table indirectly through implicit data-binding
702
- row_data.any? do |cell|
703
- cell.to_s.downcase.include?(filter_value.downcase)
704
- end
705
- end
706
- end
707
- }
708
- ]
709
- }
710
-
711
- table {
712
- text_column('Name')
713
- text_column('Email')
714
- text_column('Phone')
715
- text_column('City')
716
- text_column('State')
717
-
718
- cell_rows @data # implicit data-binding
719
-
720
- on_changed do |row, type, row_data|
721
- puts "Row #{row} #{type}: #{row_data}"
722
- end
723
- }
724
- }
725
- }.show
726
- end
727
- end
728
-
729
- FormTable.new.launch
730
- ```
731
-
732
- ![glimmer-dsl-libui-linux-form-table.png](images/glimmer-dsl-libui-linux-form-table.png)
733
-
734
- Learn more by checking out [examples](#examples).
735
-
736
- ### Area API
737
-
738
- The `area` control is a canvas-like control for drawing paths that can be used in one of two ways:
739
- - Declaratively via stable paths: useful for stable paths that will not change often later on. Simply nest `path` and figures like `rectangle` and all drawing logic is generated automatically. Path proxy objects are preserved across redraws assuming there would be relatively few stable paths (mostly for decorative reasons).
740
- - Semi-declaratively via on_draw listener dynamic paths: useful for more dynamic paths that will definitely change very often. Open an `on_draw` listener block that receives an [`area_draw_params`](#area-draw-params) argument and nest `path` and figures like `rectangle` and all drawing logic is generated automatically. Path proxy objects are destroyed (thrown-away) at the end of drawing, thus having less memory overhead for drawing thousands of dynamic paths.
741
-
742
- Note that when nesting an `area` directly underneath `window` (without a layout control like `vertical_box`), it is automatically reparented with `vertical_box` in between the `window` and `area` since it would not show up on Linux otherwise.
750
+ ([explicit data-binding](#data-binding) supports everything available with implicit data-binding too)
743
751
 
744
- Here is an example of a declarative `area` with a stable path (you may copy/paste in [`girb`](#girb-glimmer-irb)):
752
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
745
753
 
746
754
  ```ruby
747
755
  require 'glimmer-dsl-libui'
748
756
 
749
757
  include Glimmer
750
758
 
751
- window('Basic Area', 400, 400) {
759
+ data = [
760
+ ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
761
+ ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
762
+ ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
763
+ ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
764
+ ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
765
+ ]
766
+
767
+ window('Contacts', 600, 600) { |w|
752
768
  margined true
753
769
 
754
770
  vertical_box {
755
- area {
756
- path { # a stable path is added declaratively
757
- rectangle(0, 0, 400, 400)
758
-
759
- fill r: 102, g: 102, b: 204, a: 1.0
771
+ form {
772
+ stretchy false
773
+
774
+ @name_entry = entry {
775
+ label 'Name'
760
776
  }
761
- }
762
- }
763
- }.show
764
- ```
765
-
766
- Mac | Windows | Linux
767
- ----|---------|------
768
- ![glimmer-dsl-libui-mac-basic-area.png](images/glimmer-dsl-libui-mac-basic-area.png) | ![glimmer-dsl-libui-windows-basic-area.png](images/glimmer-dsl-libui-windows-basic-area.png) | ![glimmer-dsl-libui-linux-basic-area.png](images/glimmer-dsl-libui-linux-basic-area.png)
769
-
770
- Here is the same example using a semi-declarative `area` with `on_draw` listener that receives a [`area_draw_params`](#area-draw-params) argument and a dynamic path (you may copy/paste in [`girb`](#girb-glimmer-irb)):
771
-
777
+
778
+ @email_entry = entry {
779
+ label 'Email'
780
+ }
781
+
782
+ @phone_entry = entry {
783
+ label 'Phone'
784
+ }
785
+
786
+ @city_entry = entry {
787
+ label 'City'
788
+ }
789
+
790
+ @state_entry = entry {
791
+ label 'State'
792
+ }
793
+ }
794
+
795
+ button('Save Contact') {
796
+ stretchy false
797
+
798
+ on_clicked do
799
+ 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.')
802
+ else
803
+ data << new_row # automatically inserts a row into the table due to implicit data-binding
804
+ @unfiltered_data = data.dup
805
+ @name_entry.text = ''
806
+ @email_entry.text = ''
807
+ @phone_entry.text = ''
808
+ @city_entry.text = ''
809
+ @state_entry.text = ''
810
+ end
811
+ end
812
+ }
813
+
814
+ search_entry { |se|
815
+ stretchy false
816
+
817
+ on_changed do
818
+ filter_value = se.text
819
+ @unfiltered_data ||= data.dup
820
+ # Unfilter first to remove any previous filters
821
+ data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
822
+ # Now, apply filter if entered
823
+ unless filter_value.empty?
824
+ data.filter! do |row_data| # affects table indirectly through implicit data-binding
825
+ row_data.any? do |cell|
826
+ cell.to_s.downcase.include?(filter_value.downcase)
827
+ end
828
+ end
829
+ end
830
+ end
831
+ }
832
+
833
+ table {
834
+ text_column('Name')
835
+ text_column('Email')
836
+ text_column('Phone')
837
+ text_column('City')
838
+ text_column('State')
839
+
840
+ editable true
841
+ cell_rows data # implicit data-binding to raw data Array of Arrays
842
+
843
+ on_changed do |row, type, row_data|
844
+ puts "Row #{row} #{type}: #{row_data}"
845
+ end
846
+ }
847
+ }
848
+ }.show
849
+ ```
850
+
851
+ ![glimmer-dsl-libui-linux-form-table.png](images/glimmer-dsl-libui-linux-form-table.png)
852
+
853
+ Learn more by checking out [examples](#examples).
854
+
855
+ ### Area API
856
+
857
+ The `area` control is a canvas-like control for drawing paths that can be used in one of two ways:
858
+ - Declaratively via stable paths: useful for stable paths that will not change often later on. Simply nest `path` and figures like `rectangle` and all drawing logic is generated automatically. Path proxy objects are preserved across redraws assuming there would be relatively few stable paths (mostly for decorative reasons).
859
+ - Semi-declaratively via on_draw listener dynamic paths: useful for more dynamic paths that will definitely change very often. Open an `on_draw` listener block that receives an [`area_draw_params`](#area-draw-params) argument and nest `path` and figures like `rectangle` and all drawing logic is generated automatically. Path proxy objects are destroyed (thrown-away) at the end of drawing, thus having less memory overhead for drawing thousands of dynamic paths.
860
+
861
+ Note that when nesting an `area` directly underneath `window` (without a layout control like `vertical_box`), it is automatically reparented with `vertical_box` in between the `window` and `area` since it would not show up on Linux otherwise.
862
+
863
+ Here is an example of a declarative `area` with a stable path (you may copy/paste in [`girb`](#girb-glimmer-irb)):
864
+
865
+ ```ruby
866
+ require 'glimmer-dsl-libui'
867
+
868
+ include Glimmer
869
+
870
+ window('Basic Area', 400, 400) {
871
+ margined true
872
+
873
+ vertical_box {
874
+ area {
875
+ path { # a stable path is added declaratively
876
+ rectangle(0, 0, 400, 400)
877
+
878
+ fill r: 102, g: 102, b: 204, a: 1.0
879
+ }
880
+ }
881
+ }
882
+ }.show
883
+ ```
884
+
885
+ Mac | Windows | Linux
886
+ ----|---------|------
887
+ ![glimmer-dsl-libui-mac-basic-area.png](images/glimmer-dsl-libui-mac-basic-area.png) | ![glimmer-dsl-libui-windows-basic-area.png](images/glimmer-dsl-libui-windows-basic-area.png) | ![glimmer-dsl-libui-linux-basic-area.png](images/glimmer-dsl-libui-linux-basic-area.png)
888
+
889
+ Here is the same example using a semi-declarative `area` with `on_draw` listener that receives a [`area_draw_params`](#area-draw-params) argument and a dynamic path (you may copy/paste in [`girb`](#girb-glimmer-irb)):
890
+
772
891
  ```ruby
773
892
  require 'glimmer-dsl-libui'
774
893
 
@@ -799,9 +918,9 @@ Check [examples/dynamic_area.rb](#dynamic-area) for a more detailed semi-declara
799
918
  - `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.
800
919
  - `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.
801
920
 
802
- Mac |Linux
803
- ----|-----
804
- ![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)
921
+ Mac | Windows | Linux
922
+ ----|---------|------
923
+ ![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-windows-dynamic-area.png](images/glimmer-dsl-libui-windows-basic-scrolling-area.png) ![glimmer-dsl-libui-windows-dynamic-area-updated.png](images/glimmer-dsl-libui-windows-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)
805
924
 
806
925
  Check [examples/basic_scrolling_area.rb](#basic-scrolling-area) for a more detailed example.
807
926
 
@@ -1217,6 +1336,7 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
1217
1336
  - When destroying a control nested under a `horizontal_box` or `vertical_box`, it is automatically deleted from the box's children
1218
1337
  - When destroying a control nested under a `form`, it is automatically deleted from the form's children
1219
1338
  - When destroying a control nested under a `window` or `group`, it is automatically unset as their child to allow successful destruction
1339
+ - When destroying a control that has a data-binding to a model attribute, the data-binding observer registration is automatically deregistered
1220
1340
  - For `date_time_picker`, `date_picker`, and `time_picker`, make sure `time` hash values for `mon`, `wday`, and `yday` are 1-based instead of [libui](https://github.com/andlabs/libui) original 0-based values, and return `dst` as Boolean instead of `isdst` as `1`/`0`
1221
1341
  - Smart defaults for `grid` child properties are `left` (`0`), `top` (`0`), `xspan` (`1`), `yspan` (`1`), `hexpand` (`false`), `halign` (`:fill`), `vexpand` (`false`), and `valign` (`:fill`)
1222
1342
  - The `table` control automatically constructs required `TableModelHandler`, `TableModel`, and `TableParams`, calculating all their arguments from `cell_rows` and `editable` properties (e.g. `NumRows`) as well as nested columns (e.g. `text_column`)
@@ -1491,6 +1611,7 @@ Learn more from data-binding usage in [Login](#login) (4 data-binding versions),
1491
1611
  - `table` `progress_bar` column on Windows cannot be updated with a positive value if it started initially with `-1` (it ignores update to avoid crashing due to an issue in [libui](https://github.com/andlabs/libui) on Windows.
1492
1612
  - It seems that [libui](https://github.com/andlabs/libui) does not support nesting multiple `area` controls under a `grid` as only the first one shows up in that scenario. To workaround that limitation, use a `vertical_box` with nested `horizontal_box`s instead to include multiple `area`s in a GUI.
1493
1613
  - As per the code of [examples/basic_transform.rb](#basic-transform), Windows requires different ordering of transforms than Mac and Linux.
1614
+ - `scrolling_area#scroll_to` does not seem to work on Windows and Linux, but works fine on Mac
1494
1615
 
1495
1616
  ### Original API
1496
1617
 
@@ -3012,22 +3133,28 @@ Mac | Windows | Linux
3012
3133
  New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
3013
3134
 
3014
3135
  ```ruby
3015
- # frozen_string_literal: true
3016
-
3017
3136
  require 'glimmer-dsl-libui'
3018
3137
 
3019
3138
  class BasicTableButton
3139
+ BasicAnimal = Struct.new(:name, :sound)
3140
+
3141
+ class Animal < BasicAnimal
3142
+ def action
3143
+ 'delete'
3144
+ end
3145
+ end
3146
+
3020
3147
  include Glimmer
3021
3148
 
3022
- attr_accessor :data
3149
+ attr_accessor :animals
3023
3150
 
3024
3151
  def initialize
3025
- @data = [
3026
- %w[cat meow delete],
3027
- %w[dog woof delete],
3028
- %w[chicken cock-a-doodle-doo delete],
3029
- %w[horse neigh delete],
3030
- %w[cow moo delete]
3152
+ @animals = [
3153
+ Animal.new('cat', 'meow'),
3154
+ Animal.new('dog', 'woof'),
3155
+ Animal.new('chicken', 'cock-a-doodle-doo'),
3156
+ Animal.new('horse', 'neigh'),
3157
+ Animal.new('cow', 'moo'),
3031
3158
  ]
3032
3159
  end
3033
3160
 
@@ -3040,17 +3167,19 @@ class BasicTableButton
3040
3167
  button_column('Action') {
3041
3168
  on_clicked do |row|
3042
3169
  # Option 1: direct data deletion is the simpler solution
3043
- # @data.delete_at(row) # automatically deletes actual table row due to explicit data-binding
3170
+ # @animals.delete_at(row) # automatically deletes actual table row due to explicit data-binding
3044
3171
 
3045
- # Option 2: cloning only to demonstrate table row deletion upon explicit setting of data attribute (cloning is not recommended beyond demonstrating this point)
3046
- new_data = @data.clone
3047
- new_data.delete_at(row)
3048
- self.data = new_data # automatically loses deleted table row due to explicit data-binding
3172
+ # Option 2: cloning only to demonstrate table row deletion upon explicit setting of animals attribute (cloning is not recommended beyond demonstrating this point)
3173
+ new_animals = @animals.clone
3174
+ new_animals.delete_at(row)
3175
+ self.animals = new_animals # automatically loses deleted table row due to explicit data-binding
3049
3176
  end
3050
3177
  }
3051
3178
 
3052
- cell_rows <=> [self, :data] # explicit data-binding of table cell_rows to self.data
3053
3179
 
3180
+ cell_rows <= [self, :animals, column_attributes: {'Animal' => :name, 'Description' => :sound}]
3181
+
3182
+ # explicit unidirectional data-binding of table cell_rows to self.animals
3054
3183
  on_changed do |row, type, row_data|
3055
3184
  puts "Row #{row} #{type}: #{row_data}"
3056
3185
  $stdout.flush
@@ -3299,7 +3428,7 @@ window('Animals', 500, 200) {
3299
3428
  text_color_column('Sound')
3300
3429
  checkbox_text_color_column('Description')
3301
3430
  image_text_color_column('GUI')
3302
- background_color_column('Mammal')
3431
+ background_color_column # must be the last column
3303
3432
 
3304
3433
  cell_rows data
3305
3434
  }
@@ -3341,7 +3470,7 @@ window('Animals', 500, 200) {
3341
3470
  text_color_column('Sound')
3342
3471
  checkbox_text_color_column('Description')
3343
3472
  image_text_color_column('GUI')
3344
- background_color_column('Mammal')
3473
+ background_color_column
3345
3474
 
3346
3475
  cell_rows data
3347
3476
  }
@@ -3483,9 +3612,9 @@ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/
3483
3612
  ruby -r glimmer-dsl-libui -e "require 'examples/basic_scrolling_area'"
3484
3613
  ```
3485
3614
 
3486
- Mac | Linux
3487
- ----|------
3488
- ![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)
3615
+ Mac | Windows | Linux
3616
+ ----|---------|------
3617
+ ![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-windows-dynamic-area.png](images/glimmer-dsl-libui-windows-basic-scrolling-area.png) ![glimmer-dsl-libui-windows-dynamic-area-updated.png](images/glimmer-dsl-libui-windows-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)
3489
3618
 
3490
3619
  New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
3491
3620
 
@@ -3572,6 +3701,8 @@ BasicScrollingArea.new.launch
3572
3701
 
3573
3702
  #### Basic Image
3574
3703
 
3704
+ Please note the caveats of [Area Image](#area-image) **(Alpha Feature)** with regards to this example.
3705
+
3575
3706
  [examples/basic_image.rb](examples/basic_image.rb)
3576
3707
 
3577
3708
  Run with this command from the root of the project if you cloned the project:
@@ -4822,9 +4953,9 @@ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/
4822
4953
  ruby -r glimmer-dsl-libui -e "require 'examples/button_counter'"
4823
4954
  ```
4824
4955
 
4825
- Mac | Linux
4826
- ----|------
4827
- ![glimmer-dsl-libui-mac-button-counter.png](images/glimmer-dsl-libui-mac-button-counter.png) | ![glimmer-dsl-libui-linux-button-counter.png](images/glimmer-dsl-libui-linux-button-counter.png)
4956
+ Mac | Windows | Linux
4957
+ ----|---------|------
4958
+ ![glimmer-dsl-libui-mac-button-counter.png](images/glimmer-dsl-libui-mac-button-counter.png) | ![glimmer-dsl-libui-windows-button-counter.png](images/glimmer-dsl-libui-windows-button-counter.png) | ![glimmer-dsl-libui-linux-button-counter.png](images/glimmer-dsl-libui-linux-button-counter.png)
4828
4959
 
4829
4960
  New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
4830
4961
 
@@ -6263,8 +6394,8 @@ window('Editable animal sounds', 300, 200) {
6263
6394
  text_column('Animal')
6264
6395
  text_column('Description')
6265
6396
 
6266
- cell_rows data
6267
6397
  editable true
6398
+ cell_rows data
6268
6399
 
6269
6400
  on_changed do |row, type, row_data| # fires on all changes (even ones happening through data array)
6270
6401
  puts "Row #{row} #{type}: #{row_data}"
@@ -6302,7 +6433,359 @@ Mac | Windows | Linux
6302
6433
  ----|---------|------
6303
6434
  ![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)
6304
6435
 
6305
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
6436
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
6437
+
6438
+ ```ruby
6439
+ require 'glimmer-dsl-libui'
6440
+
6441
+ class FormTable
6442
+ Contact = Struct.new(:name, :email, :phone, :city, :state)
6443
+
6444
+ include Glimmer
6445
+
6446
+ attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
6447
+
6448
+ def initialize
6449
+ @contacts = [
6450
+ Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
6451
+ Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
6452
+ Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
6453
+ Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
6454
+ Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
6455
+ ]
6456
+ end
6457
+
6458
+ def launch
6459
+ window('Contacts', 600, 600) { |w|
6460
+ margined true
6461
+
6462
+ vertical_box {
6463
+ form {
6464
+ stretchy false
6465
+
6466
+ entry {
6467
+ label 'Name'
6468
+ text <=> [self, :name] # bidirectional data-binding between entry text and self.name
6469
+ }
6470
+
6471
+ entry {
6472
+ label 'Email'
6473
+ text <=> [self, :email]
6474
+ }
6475
+
6476
+ entry {
6477
+ label 'Phone'
6478
+ text <=> [self, :phone]
6479
+ }
6480
+
6481
+ entry {
6482
+ label 'City'
6483
+ text <=> [self, :city]
6484
+ }
6485
+
6486
+ entry {
6487
+ label 'State'
6488
+ text <=> [self, :state]
6489
+ }
6490
+ }
6491
+
6492
+ button('Save Contact') {
6493
+ stretchy false
6494
+
6495
+ on_clicked do
6496
+ 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.')
6499
+ else
6500
+ @contacts << Contact.new(*new_row) # automatically inserts a row into the table due to explicit data-binding
6501
+ @unfiltered_contacts = @contacts.dup
6502
+ self.name = '' # automatically clears name entry through explicit data-binding
6503
+ self.email = ''
6504
+ self.phone = ''
6505
+ self.city = ''
6506
+ self.state = ''
6507
+ end
6508
+ end
6509
+ }
6510
+
6511
+ search_entry {
6512
+ stretchy false
6513
+ # bidirectional data-binding of text to self.filter_value with after_write option
6514
+ text <=> [self, :filter_value,
6515
+ after_write: ->(filter_value) { # execute after write to self.filter_value
6516
+ @unfiltered_contacts ||= @contacts.dup
6517
+ # Unfilter first to remove any previous filters
6518
+ self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
6519
+ # Now, apply filter if entered
6520
+ unless filter_value.empty?
6521
+ self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
6522
+ contact.members.any? do |attribute|
6523
+ contact[attribute].to_s.downcase.include?(filter_value.downcase)
6524
+ end
6525
+ end
6526
+ end
6527
+ }
6528
+ ]
6529
+ }
6530
+
6531
+ table {
6532
+ text_column('Name')
6533
+ text_column('Email')
6534
+ text_column('Phone')
6535
+ text_column('City')
6536
+ text_column('State')
6537
+
6538
+ editable true
6539
+ cell_rows <=> [self, :contacts] # explicit data-binding to Model Array
6540
+
6541
+ on_changed do |row, type, row_data|
6542
+ puts "Row #{row} #{type}: #{row_data}"
6543
+ end
6544
+ }
6545
+ }
6546
+ }.show
6547
+ end
6548
+ end
6549
+
6550
+ FormTable.new.launch
6551
+ ```
6552
+
6553
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
6554
+
6555
+ ```ruby
6556
+ require 'glimmer-dsl-libui'
6557
+
6558
+ class FormTable
6559
+ Contact = Struct.new(:name, :email, :phone, :city, :state)
6560
+
6561
+ include Glimmer
6562
+
6563
+ attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
6564
+
6565
+ def initialize
6566
+ @contacts = [
6567
+ Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
6568
+ Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
6569
+ Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
6570
+ Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
6571
+ Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
6572
+ ]
6573
+ end
6574
+
6575
+ def launch
6576
+ window('Contacts', 600, 600) { |w|
6577
+ margined true
6578
+
6579
+ vertical_box {
6580
+ form {
6581
+ stretchy false
6582
+
6583
+ entry {
6584
+ label 'Name'
6585
+ text <=> [self, :name] # bidirectional data-binding between entry text and self.name
6586
+ }
6587
+
6588
+ entry {
6589
+ label 'Email'
6590
+ text <=> [self, :email]
6591
+ }
6592
+
6593
+ entry {
6594
+ label 'Phone'
6595
+ text <=> [self, :phone]
6596
+ }
6597
+
6598
+ entry {
6599
+ label 'City'
6600
+ text <=> [self, :city]
6601
+ }
6602
+
6603
+ entry {
6604
+ label 'State'
6605
+ text <=> [self, :state]
6606
+ }
6607
+ }
6608
+
6609
+ button('Save Contact') {
6610
+ stretchy false
6611
+
6612
+ on_clicked do
6613
+ 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.')
6616
+ else
6617
+ @contacts << Contact.new(*new_row) # automatically inserts a row into the table due to implicit data-binding
6618
+ @unfiltered_contacts = @contacts.dup
6619
+ self.name = '' # automatically clears name entry through explicit data-binding
6620
+ self.email = ''
6621
+ self.phone = ''
6622
+ self.city = ''
6623
+ self.state = ''
6624
+ end
6625
+ end
6626
+ }
6627
+
6628
+ search_entry {
6629
+ stretchy false
6630
+ # bidirectional data-binding of text to self.filter_value with after_write option
6631
+ text <=> [self, :filter_value,
6632
+ after_write: ->(filter_value) { # execute after write to self.filter_value
6633
+ @unfiltered_contacts ||= @contacts.dup
6634
+ # Unfilter first to remove any previous filters
6635
+ self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
6636
+ # Now, apply filter if entered
6637
+ unless filter_value.empty?
6638
+ self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
6639
+ contact.members.any? do |attribute|
6640
+ contact[attribute].to_s.downcase.include?(filter_value.downcase)
6641
+ end
6642
+ end
6643
+ end
6644
+ }
6645
+ ]
6646
+ }
6647
+
6648
+ table {
6649
+ text_column('Name')
6650
+ text_column('Email')
6651
+ text_column('Phone')
6652
+ text_column('City')
6653
+ text_column('State/Province')
6654
+
6655
+ 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
6657
+
6658
+ on_changed do |row, type, row_data|
6659
+ puts "Row #{row} #{type}: #{row_data}"
6660
+ end
6661
+ }
6662
+ }
6663
+ }.show
6664
+ end
6665
+ end
6666
+
6667
+ FormTable.new.launch
6668
+ ```
6669
+
6670
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
6671
+
6672
+ ```ruby
6673
+
6674
+ require 'glimmer-dsl-libui'
6675
+
6676
+ class FormTable
6677
+ Contact = Struct.new(:full_name, :email_address, :phone_number, :city_or_town, :state_or_province)
6678
+
6679
+ include Glimmer
6680
+
6681
+ attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
6682
+
6683
+ def initialize
6684
+ @contacts = [
6685
+ Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
6686
+ Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
6687
+ Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
6688
+ Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
6689
+ Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
6690
+ ]
6691
+ end
6692
+
6693
+ def launch
6694
+ window('Contacts', 600, 600) { |w|
6695
+ margined true
6696
+
6697
+ vertical_box {
6698
+ form {
6699
+ stretchy false
6700
+
6701
+ entry {
6702
+ label 'Name'
6703
+ text <=> [self, :name] # bidirectional data-binding between entry text and self.name
6704
+ }
6705
+
6706
+ entry {
6707
+ label 'Email'
6708
+ text <=> [self, :email]
6709
+ }
6710
+
6711
+ entry {
6712
+ label 'Phone'
6713
+ text <=> [self, :phone]
6714
+ }
6715
+
6716
+ entry {
6717
+ label 'City'
6718
+ text <=> [self, :city]
6719
+ }
6720
+
6721
+ entry {
6722
+ label 'State'
6723
+ text <=> [self, :state]
6724
+ }
6725
+ }
6726
+
6727
+ button('Save Contact') {
6728
+ stretchy false
6729
+
6730
+ on_clicked do
6731
+ 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.')
6734
+ else
6735
+ @contacts << Contact.new(*new_row) # automatically inserts a row into the table due to implicit data-binding
6736
+ @unfiltered_contacts = @contacts.dup
6737
+ self.name = '' # automatically clears name entry through explicit data-binding
6738
+ self.email = ''
6739
+ self.phone = ''
6740
+ self.city = ''
6741
+ self.state = ''
6742
+ end
6743
+ end
6744
+ }
6745
+
6746
+ search_entry {
6747
+ stretchy false
6748
+ # bidirectional data-binding of text to self.filter_value with after_write option
6749
+ text <=> [self, :filter_value,
6750
+ after_write: ->(filter_value) { # execute after write to self.filter_value
6751
+ @unfiltered_contacts ||= @contacts.dup
6752
+ # Unfilter first to remove any previous filters
6753
+ self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
6754
+ # Now, apply filter if entered
6755
+ unless filter_value.empty?
6756
+ self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
6757
+ contact.members.any? do |attribute|
6758
+ contact[attribute].to_s.downcase.include?(filter_value.downcase)
6759
+ end
6760
+ end
6761
+ end
6762
+ }
6763
+ ]
6764
+ }
6765
+
6766
+ table {
6767
+ text_column('Name')
6768
+ text_column('Email')
6769
+ text_column('Phone')
6770
+ text_column('City')
6771
+ text_column('State')
6772
+
6773
+ editable true
6774
+ cell_rows <=> [self, :contacts, column_attributes: [:full_name, :email_address, :phone_number, :city_or_town, :state_or_province]] # explicit data-binding to Model Array with column_attributes mapping for all columns
6775
+
6776
+ on_changed do |row, type, row_data|
6777
+ puts "Row #{row} #{type}: #{row_data}"
6778
+ end
6779
+ }
6780
+ }
6781
+ }.show
6782
+ end
6783
+ end
6784
+
6785
+ FormTable.new.launch
6786
+ ```
6787
+
6788
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 4 (with explicit [data-binding](#data-binding) to raw data):
6306
6789
 
6307
6790
  ```ruby
6308
6791
  require 'glimmer-dsl-libui'
@@ -6332,7 +6815,7 @@ class FormTable
6332
6815
 
6333
6816
  entry {
6334
6817
  label 'Name'
6335
- text <=> [self, :name]
6818
+ text <=> [self, :name] # bidirectional data-binding between entry text and self.name
6336
6819
  }
6337
6820
 
6338
6821
  entry {
@@ -6364,8 +6847,8 @@ class FormTable
6364
6847
  if new_row.include?('')
6365
6848
  msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
6366
6849
  else
6367
- @data << new_row # automatically inserts a row into the table due to implicit data-binding
6368
- @unfiltered_data = @data.dup
6850
+ data << new_row # automatically inserts a row into the table due to implicit data-binding
6851
+ @unfiltered_data = data.dup
6369
6852
  self.name = '' # automatically clears name entry through explicit data-binding
6370
6853
  self.email = ''
6371
6854
  self.phone = ''
@@ -6377,14 +6860,15 @@ class FormTable
6377
6860
 
6378
6861
  search_entry {
6379
6862
  stretchy false
6380
- text <=> [self, :filter_value, # bidirectional data-binding of text to self.filter_value with after_write option
6863
+ # bidirectional data-binding of text to self.filter_value with after_write option
6864
+ text <=> [self, :filter_value,
6381
6865
  after_write: ->(filter_value) { # execute after write to self.filter_value
6382
- @unfiltered_data ||= @data.dup
6866
+ @unfiltered_data ||= data.dup
6383
6867
  # Unfilter first to remove any previous filters
6384
- self.data = @unfiltered_data # affects table indirectly through explicit data-binding
6868
+ data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
6385
6869
  # Now, apply filter if entered
6386
6870
  unless filter_value.empty?
6387
- self.data = @data.filter do |row_data| # affects table indirectly through explicit data-binding
6871
+ data.filter! do |row_data| # affects table indirectly through implicit data-binding
6388
6872
  row_data.any? do |cell|
6389
6873
  cell.to_s.downcase.include?(filter_value.downcase)
6390
6874
  end
@@ -6400,8 +6884,9 @@ class FormTable
6400
6884
  text_column('Phone')
6401
6885
  text_column('City')
6402
6886
  text_column('State')
6403
-
6404
- cell_rows <=> [self, :data] # explicit data-binding
6887
+
6888
+ editable true
6889
+ cell_rows <=> [self, :data] # explicit data-binding to raw data Array of Arrays
6405
6890
 
6406
6891
  on_changed do |row, type, row_data|
6407
6892
  puts "Row #{row} #{type}: #{row_data}"
@@ -6415,7 +6900,7 @@ end
6415
6900
  FormTable.new.launch
6416
6901
  ```
6417
6902
 
6418
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
6903
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 5 (with implicit [data-binding](#data-binding)):
6419
6904
 
6420
6905
  ```ruby
6421
6906
  require 'glimmer-dsl-libui'
@@ -6503,7 +6988,8 @@ window('Contacts', 600, 600) { |w|
6503
6988
  text_column('City')
6504
6989
  text_column('State')
6505
6990
 
6506
- cell_rows data # implicit data-binding
6991
+ editable true
6992
+ cell_rows data # implicit data-binding to raw data Array of Arrays
6507
6993
 
6508
6994
  on_changed do |row, type, row_data|
6509
6995
  puts "Row #{row} #{type}: #{row_data}"