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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +671 -185
- data/VERSION +1 -1
- data/examples/basic_table_button.rb +54 -30
- data/examples/basic_table_button2.rb +34 -0
- data/examples/basic_table_color.rb +1 -1
- data/examples/editable_table.rb +1 -1
- data/examples/form_table.rb +21 -17
- data/examples/form_table2.rb +104 -85
- data/examples/form_table3.rb +113 -0
- data/examples/form_table4.rb +110 -0
- data/examples/form_table5.rb +94 -0
- data/examples/meta_example.rb +6 -4
- data/glimmer-dsl-libui.gemspec +0 -0
- data/lib/glimmer/dsl/libui/control_expression.rb +2 -1
- data/lib/glimmer/dsl/libui/shape_expression.rb +2 -2
- data/lib/glimmer/dsl/libui/string_expression.rb +2 -1
- data/lib/glimmer/libui/attributed_string.rb +3 -2
- data/lib/glimmer/libui/control_proxy/table_proxy.rb +88 -24
- data/lib/glimmer/libui/control_proxy.rb +4 -2
- data/lib/glimmer/libui/data_bindable.rb +8 -3
- data/lib/glimmer/libui/shape.rb +3 -2
- data/lib/glimmer-dsl-libui.rb +1 -0
- metadata +5 -1
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.
|
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
|
[](http://badge.fury.io/rb/glimmer-dsl-libui)
|
4
4
|
[](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
|
 |  | 
|
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
|
+
 |  | 
|
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.
|
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
|
-
|
507
|
+
Mac | Windows | Linux
|
508
|
+
----|---------|------
|
509
|
+
 |  | 
|
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
|
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
|
-
|
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
|
-

|
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
|
-
|
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
|
-
|
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
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
771
|
+
form {
|
772
|
+
stretchy false
|
773
|
+
|
774
|
+
@name_entry = entry {
|
775
|
+
label 'Name'
|
760
776
|
}
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
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
|
+

|
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
|
+
 |  | 
|
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
|
-
  |  
|
921
|
+
Mac | Windows | Linux
|
922
|
+
----|---------|------
|
923
|
+
  |   |  
|
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 :
|
3149
|
+
attr_accessor :animals
|
3023
3150
|
|
3024
3151
|
def initialize
|
3025
|
-
@
|
3026
|
-
|
3027
|
-
|
3028
|
-
|
3029
|
-
|
3030
|
-
|
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
|
-
# @
|
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
|
3046
|
-
|
3047
|
-
|
3048
|
-
self.
|
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
|
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
|
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
|
-
  |  
|
3615
|
+
Mac | Windows | Linux
|
3616
|
+
----|---------|------
|
3617
|
+
  |   |  
|
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
|
-
 | 
|
4956
|
+
Mac | Windows | Linux
|
4957
|
+
----|---------|------
|
4958
|
+
 |  | 
|
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
|
   |    |   
|
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
|
-
|
6368
|
-
@unfiltered_data =
|
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
|
-
|
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 ||=
|
6866
|
+
@unfiltered_data ||= data.dup
|
6383
6867
|
# Unfilter first to remove any previous filters
|
6384
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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}"
|