glimmer-dsl-libui 0.4.10 → 0.4.11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![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.
|
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
|
+
![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
|
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
|
-
![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
|
-
|
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
|
+
![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 :
|
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
|
-
![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
|
-
|
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}"
|