glimmer-dsl-libui 0.4.10 → 0.4.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/README.md +1071 -219
- data/VERSION +1 -1
- data/examples/basic_image.rb +5 -3
- data/examples/basic_image2.rb +1 -3
- data/examples/basic_image3.rb +3 -3
- data/examples/basic_image4.rb +0 -3
- data/examples/basic_image5.rb +0 -2
- data/examples/basic_table_button.rb +54 -30
- data/examples/basic_table_button2.rb +34 -0
- data/examples/basic_table_color.rb +104 -26
- data/examples/basic_table_color2.rb +2 -14
- data/examples/basic_table_color3.rb +37 -0
- data/examples/basic_table_image.rb +1 -1
- data/examples/basic_table_image2.rb +2 -14
- data/examples/basic_table_image3.rb +44 -0
- data/examples/basic_table_image_text.rb +1 -2
- data/examples/basic_table_image_text2.rb +2 -13
- data/examples/basic_table_image_text3.rb +44 -0
- data/examples/cpu_percentage.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/column/background_color_column_proxy.rb +4 -0
- data/lib/glimmer/libui/control_proxy/image_proxy.rb +90 -10
- data/lib/glimmer/libui/control_proxy/table_proxy.rb +95 -29
- 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/libui.rb +2 -2
- data/lib/glimmer-dsl-libui.rb +1 -0
- metadata +9 -2
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.4.
|
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.14
|
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 self.contacts Modal Array, auto-inferring model attribute names from underscored table column names by convention
|
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
|
@@ -235,6 +356,11 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
|
|
235
356
|
- [Custom Keywords](#custom-keywords)
|
236
357
|
- [Observer Pattern](#observer-pattern)
|
237
358
|
- [Data-Binding](#data-binding)
|
359
|
+
- [Bidirectional (Two-Way) Data-Binding](#bidirectional-two-way-data-binding)
|
360
|
+
- [Table Data-Binding](#table-data-binding)
|
361
|
+
- [Unidirectional (One-Way) Data-Binding](#unidirectional-one-way-data-binding)
|
362
|
+
- [Data-Binding API](#data-binding-api)
|
363
|
+
- [Data-Binding Gotchas](#data-binding-gotchas)
|
238
364
|
- [API Gotchas](#api-gotchas)
|
239
365
|
- [Original API](#original-api)
|
240
366
|
- [Packaging](#packaging)
|
@@ -374,10 +500,20 @@ gem install glimmer-dsl-libui
|
|
374
500
|
Or install via Bundler `Gemfile`:
|
375
501
|
|
376
502
|
```ruby
|
377
|
-
gem 'glimmer-dsl-libui', '~> 0.4.
|
503
|
+
gem 'glimmer-dsl-libui', '~> 0.4.14'
|
504
|
+
```
|
505
|
+
|
506
|
+
Test that installation worked by running the [Meta-Example](#examples):
|
507
|
+
|
508
|
+
```
|
509
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/meta_example'"
|
378
510
|
```
|
379
511
|
|
380
|
-
|
512
|
+
Mac | Windows | Linux
|
513
|
+
----|---------|------
|
514
|
+
![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)
|
515
|
+
|
516
|
+
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
517
|
|
382
518
|
Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
383
519
|
|
@@ -452,7 +588,7 @@ Keyword(Args) | Properties | Listeners
|
|
452
588
|
`about_menu_item` | None | `on_clicked`
|
453
589
|
`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
590
|
`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
|
591
|
+
`background_color_column` | None | None
|
456
592
|
`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
593
|
`button(text as String)` | `text` (`String`) | `on_clicked`
|
458
594
|
`button_column(name as String)` | `enabled` (Boolean) | None
|
@@ -616,160 +752,150 @@ Note that the `cell_rows` property declaration results in "implicit data-binding
|
|
616
752
|
- Inserting cell rows: Calling `Array#<<`, `Array#push`, `Array#prepend`, or any insertion/addition `Array` method automatically inserts rows in actual `table` control
|
617
753
|
- Changing cell rows: Calling `Array#[]=`, `Array#map!`, or any update `Array` method automatically updates rows in actual `table` control
|
618
754
|
|
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.
|
755
|
+
([explicit data-binding](#data-binding) supports everything available with implicit data-binding too)
|
743
756
|
|
744
|
-
|
757
|
+
Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
745
758
|
|
746
759
|
```ruby
|
747
760
|
require 'glimmer-dsl-libui'
|
748
761
|
|
749
762
|
include Glimmer
|
750
763
|
|
751
|
-
|
764
|
+
data = [
|
765
|
+
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
|
766
|
+
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
|
767
|
+
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
|
768
|
+
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
|
769
|
+
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
|
770
|
+
]
|
771
|
+
|
772
|
+
window('Contacts', 600, 600) { |w|
|
752
773
|
margined true
|
753
774
|
|
754
775
|
vertical_box {
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
776
|
+
form {
|
777
|
+
stretchy false
|
778
|
+
|
779
|
+
@name_entry = entry {
|
780
|
+
label 'Name'
|
760
781
|
}
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
782
|
+
|
783
|
+
@email_entry = entry {
|
784
|
+
label 'Email'
|
785
|
+
}
|
786
|
+
|
787
|
+
@phone_entry = entry {
|
788
|
+
label 'Phone'
|
789
|
+
}
|
790
|
+
|
791
|
+
@city_entry = entry {
|
792
|
+
label 'City'
|
793
|
+
}
|
794
|
+
|
795
|
+
@state_entry = entry {
|
796
|
+
label 'State'
|
797
|
+
}
|
798
|
+
}
|
799
|
+
|
800
|
+
button('Save Contact') {
|
801
|
+
stretchy false
|
802
|
+
|
803
|
+
on_clicked do
|
804
|
+
new_row = [@name_entry.text, @email_entry.text, @phone_entry.text, @city_entry.text, @state_entry.text]
|
805
|
+
if new_row.include?('')
|
806
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
807
|
+
else
|
808
|
+
data << new_row # automatically inserts a row into the table due to implicit data-binding
|
809
|
+
@unfiltered_data = data.dup
|
810
|
+
@name_entry.text = ''
|
811
|
+
@email_entry.text = ''
|
812
|
+
@phone_entry.text = ''
|
813
|
+
@city_entry.text = ''
|
814
|
+
@state_entry.text = ''
|
815
|
+
end
|
816
|
+
end
|
817
|
+
}
|
818
|
+
|
819
|
+
search_entry { |se|
|
820
|
+
stretchy false
|
821
|
+
|
822
|
+
on_changed do
|
823
|
+
filter_value = se.text
|
824
|
+
@unfiltered_data ||= data.dup
|
825
|
+
# Unfilter first to remove any previous filters
|
826
|
+
data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
|
827
|
+
# Now, apply filter if entered
|
828
|
+
unless filter_value.empty?
|
829
|
+
data.filter! do |row_data| # affects table indirectly through implicit data-binding
|
830
|
+
row_data.any? do |cell|
|
831
|
+
cell.to_s.downcase.include?(filter_value.downcase)
|
832
|
+
end
|
833
|
+
end
|
834
|
+
end
|
835
|
+
end
|
836
|
+
}
|
837
|
+
|
838
|
+
table {
|
839
|
+
text_column('Name')
|
840
|
+
text_column('Email')
|
841
|
+
text_column('Phone')
|
842
|
+
text_column('City')
|
843
|
+
text_column('State')
|
844
|
+
|
845
|
+
editable true
|
846
|
+
cell_rows data # implicit data-binding to raw data Array of Arrays
|
847
|
+
|
848
|
+
on_changed do |row, type, row_data|
|
849
|
+
puts "Row #{row} #{type}: #{row_data}"
|
850
|
+
end
|
851
|
+
}
|
852
|
+
}
|
853
|
+
}.show
|
854
|
+
```
|
855
|
+
|
856
|
+
Mac | Windows | Linux
|
857
|
+
----|---------|------
|
858
|
+
![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)
|
859
|
+
|
860
|
+
Learn more by checking out [examples](#examples).
|
861
|
+
|
862
|
+
### Area API
|
863
|
+
|
864
|
+
The `area` control is a canvas-like control for drawing paths that can be used in one of two ways:
|
865
|
+
- 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).
|
866
|
+
- 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.
|
867
|
+
|
868
|
+
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.
|
869
|
+
|
870
|
+
Here is an example of a declarative `area` with a stable path (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
871
|
+
|
872
|
+
```ruby
|
873
|
+
require 'glimmer-dsl-libui'
|
874
|
+
|
875
|
+
include Glimmer
|
876
|
+
|
877
|
+
window('Basic Area', 400, 400) {
|
878
|
+
margined true
|
879
|
+
|
880
|
+
vertical_box {
|
881
|
+
area {
|
882
|
+
path { # a stable path is added declaratively
|
883
|
+
rectangle(0, 0, 400, 400)
|
884
|
+
|
885
|
+
fill r: 102, g: 102, b: 204, a: 1.0
|
886
|
+
}
|
887
|
+
}
|
888
|
+
}
|
889
|
+
}.show
|
890
|
+
```
|
891
|
+
|
892
|
+
Mac | Windows | Linux
|
893
|
+
----|---------|------
|
894
|
+
![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)
|
895
|
+
|
896
|
+
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)):
|
897
|
+
|
898
|
+
```ruby
|
773
899
|
require 'glimmer-dsl-libui'
|
774
900
|
|
775
901
|
include Glimmer
|
@@ -799,17 +925,23 @@ Check [examples/dynamic_area.rb](#dynamic-area) for a more detailed semi-declara
|
|
799
925
|
- `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
926
|
- `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
927
|
|
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)
|
928
|
+
Mac | Windows | Linux
|
929
|
+
----|---------|------
|
930
|
+
![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
931
|
|
806
932
|
Check [examples/basic_scrolling_area.rb](#basic-scrolling-area) for a more detailed example.
|
807
933
|
|
808
934
|
#### Area Path Shapes
|
809
935
|
|
936
|
+
`area` can have geometric shapes drawn by adding `path` elements.
|
937
|
+
|
938
|
+
To add `path` shapes under an `area`, you can do so:
|
939
|
+
- Explicitly: by adding `path` under `area` and nesting shapes (e.g. `rectangle`) underneath that share the same `fill`/`stroke`/`transform` properties
|
940
|
+
- Implicitly: by adding shapes directly under `area` when the shapes have unique `fill`/`stroke`/`transform` properties ([Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) automatically constructs `path`s as intermediary parents for shapes directly added under `area`)
|
941
|
+
|
810
942
|
`path` can receive a `draw_fill_mode` argument that can accept values `:winding` or `:alternate` and defaults to `:winding`.
|
811
943
|
|
812
|
-
Available nested `path`
|
944
|
+
Available `path` shapes (that can be nested explicitly under `path` or implicitly under `area` directly):
|
813
945
|
- `rectangle(x as Numeric, y as Numeric, width as Numeric, height as Numeric)`
|
814
946
|
- `square(x as Numeric, y as Numeric, length as Numeric)`
|
815
947
|
- `arc(x_center as Numeric, y_center as Numeric, radius as Numeric, start_angle as Numeric, sweep as Numeric, is_negative as Boolean)`
|
@@ -895,7 +1027,8 @@ Given that it is very new and is not a [libui](https://github.com/andlabs/libui)
|
|
895
1027
|
- It only supports the `.png` file format.
|
896
1028
|
- [libui](https://github.com/andlabs/libui) pixel-by-pixel rendering performance is slow.
|
897
1029
|
- Including an `image` inside an `area` `on_draw` listener improves performance due to not retaining pixel/line data in memory.
|
898
|
-
- Supplying `width` and `height`
|
1030
|
+
- Supplying `width` and `height` options greatly improves performance when shrinking image (e.g. `image('somefile.png', width: 24, height: 24)`). You can also supply one of the two dimensions, and the other one gets calculated automatically while preserving original aspect ratio (e.g. `image('somefile.png', height: 24)`)
|
1031
|
+
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) lets you optionally specify `x` and `y` in addition to `file`, `width` and `height` (5 arguments total) to offset image location.
|
899
1032
|
|
900
1033
|
Currently, it is recommended to use `image` with very small `width` and `height` values only (e.g. 24x24).
|
901
1034
|
|
@@ -914,7 +1047,11 @@ include Glimmer
|
|
914
1047
|
|
915
1048
|
window('Basic Image', 96, 96) {
|
916
1049
|
area {
|
917
|
-
image(File.expand_path('icons/glimmer.png', __dir__),
|
1050
|
+
image(File.expand_path('icons/glimmer.png', __dir__), height: 96) # width is automatically calculated from height while preserving original aspect ratio
|
1051
|
+
# image(File.expand_path('icons/glimmer.png', __dir__), width: 96, height: 96) # you can specify both width and height options
|
1052
|
+
# image(File.expand_path('icons/glimmer.png', __dir__), 96, 96) # you can specify width, height as args
|
1053
|
+
# image(File.expand_path('../icons/glimmer.png', __dir__), 0, 0, 96, 96) # you can specify x, y, width, height args as alternative
|
1054
|
+
# image(File.expand_path('../icons/glimmer.png', __dir__), x: 0, y: 0, width: 96, height: 96) # you can specify x, y, width, height options as alternative
|
918
1055
|
}
|
919
1056
|
}.show
|
920
1057
|
```
|
@@ -946,6 +1083,8 @@ window('Basic Image', 96, 96) {
|
|
946
1083
|
area {
|
947
1084
|
image {
|
948
1085
|
file File.expand_path('icons/glimmer.png', __dir__)
|
1086
|
+
# x 0 # default
|
1087
|
+
# y 0 # default
|
949
1088
|
width 96
|
950
1089
|
height 96
|
951
1090
|
}
|
@@ -1217,6 +1356,7 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
|
|
1217
1356
|
- When destroying a control nested under a `horizontal_box` or `vertical_box`, it is automatically deleted from the box's children
|
1218
1357
|
- When destroying a control nested under a `form`, it is automatically deleted from the form's children
|
1219
1358
|
- When destroying a control nested under a `window` or `group`, it is automatically unset as their child to allow successful destruction
|
1359
|
+
- When destroying a control that has a data-binding to a model attribute, the data-binding observer registration is automatically deregistered
|
1220
1360
|
- 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
1361
|
- 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
1362
|
- 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`)
|
@@ -1227,7 +1367,8 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
|
|
1227
1367
|
- Automatically provide shifted `:key` characters in `area_key_event` provided in `area` key listeners `on_key_event`, `on_key_down`, and `on_key_up`
|
1228
1368
|
- `scrolling_area` `width` and `height` default to main window width and height if not specified.
|
1229
1369
|
- `scrolling_area` `#scroll_to` 3rd and 4th arguments (`width` and `height`) default to main window width and height if not specified.
|
1230
|
-
- `area` paths are specified declaratively with figures underneath (e.g. `rectangle`) and `area` draw listener is automatically generated
|
1370
|
+
- `area` paths are specified declaratively with shapes/figures underneath (e.g. `rectangle`), and `area` draw listener is automatically generated
|
1371
|
+
- `area` path shapes can be added directly under `area` without declaring `path` explicitly as a convenient shorthand
|
1231
1372
|
- Observe figure properties (e.g. `rectangle` `width`) for changes and automatically redraw containing area accordingly
|
1232
1373
|
- Observe `path` `fill` and `stroke` hashes for changes and automatically redraw containing area accordingly
|
1233
1374
|
- Observe `text` and `string` properties for changes and automatically redraw containing area accordingly
|
@@ -1382,6 +1523,8 @@ Data-binding supports utilizing the [MVP (Model View Presenter)](https://en.wiki
|
|
1382
1523
|
|
1383
1524
|
![MVP](https://www.researchgate.net/profile/Gilles-Perrouin/publication/320249584/figure/fig8/AS:668260987068418@1536337243385/Model-view-presenter-architecture.png)
|
1384
1525
|
|
1526
|
+
#### Bidirectional (Two-Way) Data-Binding
|
1527
|
+
|
1385
1528
|
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) supports bidirectional (two-way) data-binding of the following controls/properties via the `<=>` operator (indicating data is moving in both directions between View and Model):
|
1386
1529
|
- `checkbox`: `checked`
|
1387
1530
|
- `check_menu_item`: `checked`
|
@@ -1422,6 +1565,162 @@ entry {
|
|
1422
1565
|
|
1423
1566
|
That is data-binding `entered_text` attribute on `self` to `entry` `text` property and printing text after write to the model.
|
1424
1567
|
|
1568
|
+
##### Table Data-Binding
|
1569
|
+
|
1570
|
+
One note about `table` `cell_rows` data-binding is that it works with either:
|
1571
|
+
- Raw data `Array` (rows) of `Array`s (column cells)
|
1572
|
+
- Model `Array` (rows) of objects having attributes (column cells) matching the underscored names of `table` columns by convention. Model attribute names can be overridden when needed by passing an `Array` enumerating all mapped model attributes in the order of `table` columns or alternatively a `Hash` mapping only the column names that have model attribute names different from their table column underscored version.
|
1573
|
+
|
1574
|
+
Example of `table` implicit data-binding of `cell_rows` to raw data `Array` of `Array`s (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
1575
|
+
|
1576
|
+
```ruby
|
1577
|
+
require 'glimmer-dsl-libui'
|
1578
|
+
|
1579
|
+
include Glimmer
|
1580
|
+
|
1581
|
+
data = [
|
1582
|
+
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
|
1583
|
+
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
|
1584
|
+
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
|
1585
|
+
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
|
1586
|
+
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
|
1587
|
+
]
|
1588
|
+
|
1589
|
+
window('Contacts', 600, 600) {
|
1590
|
+
table {
|
1591
|
+
text_column('Name')
|
1592
|
+
text_column('Email')
|
1593
|
+
text_column('Phone')
|
1594
|
+
text_column('City')
|
1595
|
+
text_column('State')
|
1596
|
+
|
1597
|
+
cell_rows data
|
1598
|
+
}
|
1599
|
+
}.show
|
1600
|
+
```
|
1601
|
+
|
1602
|
+
Example of `table` explicit data-binding of `cell_rows` to Model `Array` (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
1603
|
+
|
1604
|
+
```ruby
|
1605
|
+
require 'glimmer-dsl-libui'
|
1606
|
+
|
1607
|
+
class SomeTable
|
1608
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
1609
|
+
|
1610
|
+
include Glimmer
|
1611
|
+
|
1612
|
+
attr_accessor :contacts
|
1613
|
+
|
1614
|
+
def initialize
|
1615
|
+
@contacts = [
|
1616
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
1617
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
1618
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
1619
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
1620
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
1621
|
+
]
|
1622
|
+
end
|
1623
|
+
|
1624
|
+
def launch
|
1625
|
+
window('Contacts', 600, 200) {
|
1626
|
+
table {
|
1627
|
+
text_column('Name')
|
1628
|
+
text_column('Email')
|
1629
|
+
text_column('Phone')
|
1630
|
+
text_column('City')
|
1631
|
+
text_column('State')
|
1632
|
+
|
1633
|
+
cell_rows <=> [self, :contacts] # explicit data-binding to self.contacts Model Array, auto-inferring model attribute names from underscored table column names by convention
|
1634
|
+
}
|
1635
|
+
}.show
|
1636
|
+
end
|
1637
|
+
end
|
1638
|
+
|
1639
|
+
SomeTable.new.launch
|
1640
|
+
```
|
1641
|
+
|
1642
|
+
Example of `table` explicit data-binding of `cell_rows` to Model `Array` with `column_attributes` `Hash` mapping for custom column names (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
1643
|
+
|
1644
|
+
```ruby
|
1645
|
+
require 'glimmer-dsl-libui'
|
1646
|
+
|
1647
|
+
class SomeTable
|
1648
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
1649
|
+
|
1650
|
+
include Glimmer
|
1651
|
+
|
1652
|
+
attr_accessor :contacts
|
1653
|
+
|
1654
|
+
def initialize
|
1655
|
+
@contacts = [
|
1656
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
1657
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
1658
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
1659
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
1660
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
1661
|
+
]
|
1662
|
+
end
|
1663
|
+
|
1664
|
+
def launch
|
1665
|
+
window('Contacts', 600, 200) {
|
1666
|
+
table {
|
1667
|
+
text_column('Name')
|
1668
|
+
text_column('Email')
|
1669
|
+
text_column('Phone')
|
1670
|
+
text_column('City/Town')
|
1671
|
+
text_column('State/Province')
|
1672
|
+
|
1673
|
+
cell_rows <=> [self, :contacts, column_attributes: {'City/Town' => :city, 'State/Province' => :state}]
|
1674
|
+
}
|
1675
|
+
}.show
|
1676
|
+
end
|
1677
|
+
end
|
1678
|
+
|
1679
|
+
SomeTable.new.launch
|
1680
|
+
```
|
1681
|
+
|
1682
|
+
Example of `table` explicit data-binding of `cell_rows` to Model `Array` with complete `column_attributes` `Array` mapping (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
1683
|
+
|
1684
|
+
```ruby
|
1685
|
+
require 'glimmer-dsl-libui'
|
1686
|
+
|
1687
|
+
class SomeTable
|
1688
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
1689
|
+
|
1690
|
+
include Glimmer
|
1691
|
+
|
1692
|
+
attr_accessor :contacts
|
1693
|
+
|
1694
|
+
def initialize
|
1695
|
+
@contacts = [
|
1696
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
1697
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
1698
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
1699
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
1700
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
1701
|
+
]
|
1702
|
+
end
|
1703
|
+
|
1704
|
+
def launch
|
1705
|
+
window('Contacts', 600, 200) {
|
1706
|
+
table {
|
1707
|
+
text_column('Full Name')
|
1708
|
+
text_column('Email Address')
|
1709
|
+
text_column('Phone Number')
|
1710
|
+
text_column('City or Town')
|
1711
|
+
text_column('State or Province')
|
1712
|
+
|
1713
|
+
cell_rows <=> [self, :contacts, column_attributes: [:name, :email, :phone, :city, :state]]
|
1714
|
+
}
|
1715
|
+
}.show
|
1716
|
+
end
|
1717
|
+
end
|
1718
|
+
|
1719
|
+
SomeTable.new.launch
|
1720
|
+
```
|
1721
|
+
|
1722
|
+
#### Unidirectional (One-Way) Data-Binding
|
1723
|
+
|
1425
1724
|
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) supports unidirectional (one-way) data-binding of any control/shape/attributed-string property via the `<=` operator (indicating data is moving from the right side, which is the Model, to the left side, which is the GUI View object).
|
1426
1725
|
|
1427
1726
|
Example of unidirectional data-binding:
|
@@ -1444,6 +1743,8 @@ window {
|
|
1444
1743
|
|
1445
1744
|
That is data-binding the `window` `title` property to the `score` attribute of a `@game`, but converting on read from the Model to a `String`.
|
1446
1745
|
|
1746
|
+
#### Data-Binding API
|
1747
|
+
|
1447
1748
|
To summarize the data-binding API:
|
1448
1749
|
- `view_property <=> [model, attribute, *read_or_write_options]`: Bidirectional (two-way) data-binding to Model attribute accessor
|
1449
1750
|
- `view_property <= [model, attribute, *read_only_options]`: Unidirectional (one-way) data-binding to Model attribute reader
|
@@ -1474,11 +1775,13 @@ entry {
|
|
1474
1775
|
}
|
1475
1776
|
```
|
1476
1777
|
|
1477
|
-
|
1778
|
+
Learn more from data-binding usage in [Login](#login) (4 data-binding versions), [Basic Entry](#basic-entry), [Form](#form), [Form Table](#form-table) (5 data-binding versions), [Method-Based Custom Keyword](#method-based-custom-keyword), [Snake](#snake) and [Tic Tac Toe](#tic_tac_toe) examples.
|
1779
|
+
|
1780
|
+
#### Data-Binding Gotchas
|
1781
|
+
|
1478
1782
|
- Never data-bind a control property to an attribute on the same view object with the same exact name (e.g. binding `entry` `text` property to `self` `text` attribute) as it would conflict with it. Instead, data-bind view property to an attribute with a different name on the view object or with the same name, but on a presenter or model object (e.g. data-bind `entry` `text` to `self` `legal_text` attribute or to `contract` model `text` attribute)
|
1479
1783
|
- Data-binding a property utilizes the control's listener associated with the property (e.g. `on_changed` for `entry` `text`), so you cannot hook into the listener directly anymore as that would negate data-binding. Instead, you can add an `after_write: ->(val) {}` option to perform something on trigger of the control listener instead.
|
1480
|
-
|
1481
|
-
Learn more from data-binding usage in [Login](#login) (4 data-binding versions), [Basic Entry](#basic-entry), [Form](#form), [Form Table](#form-table), [Method-Based Custom Keyword](#method-based-custom-keyword), [Snake](#snake) and [Tic Tac Toe](#tic_tac_toe) examples.
|
1784
|
+
- Data-binding a View control to another View control directly is not a good idea. Instead, data-bind both View controls to the same Presenter/Model attribute, and that keeps them in sync while keeping the code decoupled.
|
1482
1785
|
|
1483
1786
|
### API Gotchas
|
1484
1787
|
|
@@ -1491,6 +1794,7 @@ Learn more from data-binding usage in [Login](#login) (4 data-binding versions),
|
|
1491
1794
|
- `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
1795
|
- 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
1796
|
- As per the code of [examples/basic_transform.rb](#basic-transform), Windows requires different ordering of transforms than Mac and Linux.
|
1797
|
+
- `scrolling_area#scroll_to` does not seem to work on Windows and Linux, but works fine on Mac
|
1494
1798
|
|
1495
1799
|
### Original API
|
1496
1800
|
|
@@ -2804,7 +3108,44 @@ UI.main
|
|
2804
3108
|
UI.quit
|
2805
3109
|
```
|
2806
3110
|
|
2807
|
-
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
3111
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (passing file url as image):
|
3112
|
+
|
3113
|
+
```ruby
|
3114
|
+
# frozen_string_literal: true
|
3115
|
+
|
3116
|
+
# NOTE:
|
3117
|
+
# This example displays images that can be freely downloaded from the Studio Ghibli website.
|
3118
|
+
|
3119
|
+
require 'glimmer-dsl-libui'
|
3120
|
+
|
3121
|
+
include Glimmer
|
3122
|
+
|
3123
|
+
IMAGE_ROWS = []
|
3124
|
+
|
3125
|
+
50.times do |i|
|
3126
|
+
url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', (i + 1))
|
3127
|
+
puts "Processing Image: #{url}"; $stdout.flush # for Windows
|
3128
|
+
IMAGE_ROWS << [url] # array of one column cell
|
3129
|
+
rescue StandardError => e
|
3130
|
+
warn url, e.message
|
3131
|
+
end
|
3132
|
+
|
3133
|
+
window('The Red Turtle', 310, 350, false) {
|
3134
|
+
horizontal_box {
|
3135
|
+
table {
|
3136
|
+
image_column('www.ghibli.jp/works/red-turtle')
|
3137
|
+
|
3138
|
+
cell_rows IMAGE_ROWS
|
3139
|
+
}
|
3140
|
+
}
|
3141
|
+
|
3142
|
+
on_closing do
|
3143
|
+
puts 'Bye Bye'
|
3144
|
+
end
|
3145
|
+
}.show
|
3146
|
+
```
|
3147
|
+
|
3148
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (automatic construction of `image`):
|
2808
3149
|
|
2809
3150
|
```ruby
|
2810
3151
|
# NOTE:
|
@@ -2839,7 +3180,7 @@ window('The Red Turtle', 310, 350, false) {
|
|
2839
3180
|
}.show
|
2840
3181
|
```
|
2841
3182
|
|
2842
|
-
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version
|
3183
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (manual construction of `image` from `image_part`):
|
2843
3184
|
|
2844
3185
|
```ruby
|
2845
3186
|
# NOTE:
|
@@ -2907,9 +3248,11 @@ Mac | Windows | Linux
|
|
2907
3248
|
----|---------|------
|
2908
3249
|
![glimmer-dsl-libui-mac-basic-table-image-text.png](images/glimmer-dsl-libui-mac-basic-table-image-text.png) | ![glimmer-dsl-libui-windows-basic-table-image-text.png](images/glimmer-dsl-libui-windows-basic-table-image-text.png) | ![glimmer-dsl-libui-linux-basic-table-image-text.png](images/glimmer-dsl-libui-linux-basic-table-image-text.png)
|
2909
3250
|
|
2910
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
3251
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (passing file url as image):
|
2911
3252
|
|
2912
3253
|
```ruby
|
3254
|
+
# frozen_string_literal: true
|
3255
|
+
|
2913
3256
|
# NOTE:
|
2914
3257
|
# This example displays images that can be freely downloaded from the Studio Ghibli website.
|
2915
3258
|
|
@@ -2923,8 +3266,7 @@ IMAGE_ROWS = []
|
|
2923
3266
|
url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', (i + 1))
|
2924
3267
|
puts "Processing Image: #{url}"; $stdout.flush # for Windows
|
2925
3268
|
text = url.sub('https://www.ghibli.jp/gallery/thumb-redturtle', '').sub('.png', '')
|
2926
|
-
|
2927
|
-
IMAGE_ROWS << [[img, text], [img, text]] # cell values are dual-element arrays
|
3269
|
+
IMAGE_ROWS << [[url, text], [url, text]] # cell values are dual-element arrays
|
2928
3270
|
rescue StandardError => e
|
2929
3271
|
warn url, e.message
|
2930
3272
|
end
|
@@ -2943,15 +3285,13 @@ window('The Red Turtle', 670, 350) {
|
|
2943
3285
|
}.show
|
2944
3286
|
```
|
2945
3287
|
|
2946
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (
|
3288
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (automatic construction of `image`):
|
2947
3289
|
|
2948
3290
|
```ruby
|
2949
3291
|
# NOTE:
|
2950
3292
|
# This example displays images that can be freely downloaded from the Studio Ghibli website.
|
2951
3293
|
|
2952
3294
|
require 'glimmer-dsl-libui'
|
2953
|
-
require 'chunky_png'
|
2954
|
-
require 'open-uri'
|
2955
3295
|
|
2956
3296
|
include Glimmer
|
2957
3297
|
|
@@ -2959,9 +3299,47 @@ IMAGE_ROWS = []
|
|
2959
3299
|
|
2960
3300
|
5.times do |i|
|
2961
3301
|
url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', (i + 1))
|
2962
|
-
puts "Processing Image: #{url}"
|
2963
|
-
|
2964
|
-
|
3302
|
+
puts "Processing Image: #{url}"; $stdout.flush # for Windows
|
3303
|
+
text = url.sub('https://www.ghibli.jp/gallery/thumb-redturtle', '').sub('.png', '')
|
3304
|
+
img = image(url)
|
3305
|
+
IMAGE_ROWS << [[img, text], [img, text]] # cell values are dual-element arrays
|
3306
|
+
rescue StandardError => e
|
3307
|
+
warn url, e.message
|
3308
|
+
end
|
3309
|
+
|
3310
|
+
window('The Red Turtle', 670, 350) {
|
3311
|
+
horizontal_box {
|
3312
|
+
table {
|
3313
|
+
image_text_column('image/number')
|
3314
|
+
image_text_column('image/number (editable)') {
|
3315
|
+
editable true
|
3316
|
+
}
|
3317
|
+
|
3318
|
+
cell_rows IMAGE_ROWS
|
3319
|
+
}
|
3320
|
+
}
|
3321
|
+
}.show
|
3322
|
+
```
|
3323
|
+
|
3324
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (manual construction of `image` from `image_part`):
|
3325
|
+
|
3326
|
+
```ruby
|
3327
|
+
# NOTE:
|
3328
|
+
# This example displays images that can be freely downloaded from the Studio Ghibli website.
|
3329
|
+
|
3330
|
+
require 'glimmer-dsl-libui'
|
3331
|
+
require 'chunky_png'
|
3332
|
+
require 'open-uri'
|
3333
|
+
|
3334
|
+
include Glimmer
|
3335
|
+
|
3336
|
+
IMAGE_ROWS = []
|
3337
|
+
|
3338
|
+
5.times do |i|
|
3339
|
+
url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', (i + 1))
|
3340
|
+
puts "Processing Image: #{url}"
|
3341
|
+
f = URI.open(url)
|
3342
|
+
canvas = ChunkyPNG::Canvas.from_io(f)
|
2965
3343
|
f.close
|
2966
3344
|
data = canvas.to_rgba_stream
|
2967
3345
|
width = canvas.width
|
@@ -3012,22 +3390,28 @@ Mac | Windows | Linux
|
|
3012
3390
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
|
3013
3391
|
|
3014
3392
|
```ruby
|
3015
|
-
# frozen_string_literal: true
|
3016
|
-
|
3017
3393
|
require 'glimmer-dsl-libui'
|
3018
3394
|
|
3019
3395
|
class BasicTableButton
|
3396
|
+
BasicAnimal = Struct.new(:name, :sound)
|
3397
|
+
|
3398
|
+
class Animal < BasicAnimal
|
3399
|
+
def action
|
3400
|
+
'delete'
|
3401
|
+
end
|
3402
|
+
end
|
3403
|
+
|
3020
3404
|
include Glimmer
|
3021
3405
|
|
3022
|
-
attr_accessor :
|
3406
|
+
attr_accessor :animals
|
3023
3407
|
|
3024
3408
|
def initialize
|
3025
|
-
@
|
3026
|
-
|
3027
|
-
|
3028
|
-
|
3029
|
-
|
3030
|
-
|
3409
|
+
@animals = [
|
3410
|
+
Animal.new('cat', 'meow'),
|
3411
|
+
Animal.new('dog', 'woof'),
|
3412
|
+
Animal.new('chicken', 'cock-a-doodle-doo'),
|
3413
|
+
Animal.new('horse', 'neigh'),
|
3414
|
+
Animal.new('cow', 'moo'),
|
3031
3415
|
]
|
3032
3416
|
end
|
3033
3417
|
|
@@ -3040,17 +3424,19 @@ class BasicTableButton
|
|
3040
3424
|
button_column('Action') {
|
3041
3425
|
on_clicked do |row|
|
3042
3426
|
# Option 1: direct data deletion is the simpler solution
|
3043
|
-
# @
|
3427
|
+
# @animals.delete_at(row) # automatically deletes actual table row due to explicit data-binding
|
3044
3428
|
|
3045
|
-
# Option 2: cloning only to demonstrate table row deletion upon explicit setting of
|
3046
|
-
|
3047
|
-
|
3048
|
-
self.
|
3429
|
+
# Option 2: cloning only to demonstrate table row deletion upon explicit setting of animals attribute (cloning is not recommended beyond demonstrating this point)
|
3430
|
+
new_animals = @animals.clone
|
3431
|
+
new_animals.delete_at(row)
|
3432
|
+
self.animals = new_animals # automatically loses deleted table row due to explicit data-binding
|
3049
3433
|
end
|
3050
3434
|
}
|
3051
3435
|
|
3052
|
-
cell_rows <=> [self, :data] # explicit data-binding of table cell_rows to self.data
|
3053
3436
|
|
3437
|
+
cell_rows <= [self, :animals, column_attributes: {'Animal' => :name, 'Description' => :sound}]
|
3438
|
+
|
3439
|
+
# explicit unidirectional data-binding of table cell_rows to self.animals
|
3054
3440
|
on_changed do |row, type, row_data|
|
3055
3441
|
puts "Row #{row} #{type}: #{row_data}"
|
3056
3442
|
$stdout.flush
|
@@ -3273,16 +3659,126 @@ Mac | Windows | Linux
|
|
3273
3659
|
----|---------|------
|
3274
3660
|
![glimmer-dsl-libui-mac-basic-table-color.png](images/glimmer-dsl-libui-mac-basic-table-color.png) | ![glimmer-dsl-libui-windows-basic-table-color.png](images/glimmer-dsl-libui-windows-basic-table-color.png) | ![glimmer-dsl-libui-linux-basic-table-color.png](images/glimmer-dsl-libui-linux-basic-table-color.png)
|
3275
3661
|
|
3276
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
3662
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding) to model rows using a presenter):
|
3277
3663
|
|
3278
3664
|
```ruby
|
3279
|
-
|
3665
|
+
require 'glimmer-dsl-libui'
|
3666
|
+
|
3667
|
+
class BasicTableColor
|
3668
|
+
Animal = Struct.new(:name, :sound, :mammal)
|
3669
|
+
|
3670
|
+
class AnimalPresenter < Animal
|
3671
|
+
def name_color
|
3672
|
+
color = case name
|
3673
|
+
when 'cat'
|
3674
|
+
:red
|
3675
|
+
when 'dog'
|
3676
|
+
:yellow
|
3677
|
+
when 'chicken'
|
3678
|
+
:beige
|
3679
|
+
when 'horse'
|
3680
|
+
:purple
|
3681
|
+
when 'cow'
|
3682
|
+
:gray
|
3683
|
+
end
|
3684
|
+
[name, color]
|
3685
|
+
end
|
3686
|
+
|
3687
|
+
def sound_color
|
3688
|
+
color = case name
|
3689
|
+
when 'cat', 'chicken', 'cow'
|
3690
|
+
:blue
|
3691
|
+
when 'dog', 'horse'
|
3692
|
+
{r: 240, g: 32, b: 32}
|
3693
|
+
end
|
3694
|
+
[sound, color]
|
3695
|
+
end
|
3696
|
+
|
3697
|
+
def mammal_description_color
|
3698
|
+
color = case name
|
3699
|
+
when 'cat', 'dog', 'horse', 'cow'
|
3700
|
+
:green
|
3701
|
+
when 'chicken'
|
3702
|
+
:red
|
3703
|
+
end
|
3704
|
+
[mammal, 'mammal', color]
|
3705
|
+
end
|
3706
|
+
|
3707
|
+
def image_description_color
|
3708
|
+
color = case name
|
3709
|
+
when 'cat', 'dog', 'horse'
|
3710
|
+
:dark_blue
|
3711
|
+
when 'chicken'
|
3712
|
+
:beige
|
3713
|
+
when 'cow'
|
3714
|
+
:brown
|
3715
|
+
end
|
3716
|
+
[img, 'Glimmer', color]
|
3717
|
+
end
|
3718
|
+
|
3719
|
+
def img
|
3720
|
+
# scale image to 24x24 (can be passed as file path String only instead of Array to avoid scaling)
|
3721
|
+
[File.expand_path('../icons/glimmer.png', __dir__), 24, 24]
|
3722
|
+
end
|
3723
|
+
|
3724
|
+
def background_color
|
3725
|
+
case name
|
3726
|
+
when 'cat'
|
3727
|
+
{r: 255, g: 120, b: 0, a: 0.5}
|
3728
|
+
when 'dog'
|
3729
|
+
:skyblue
|
3730
|
+
when 'chicken'
|
3731
|
+
{r: 5, g: 120, b: 110}
|
3732
|
+
when 'horse'
|
3733
|
+
'#13a1fb'
|
3734
|
+
when 'cow'
|
3735
|
+
0x12ff02
|
3736
|
+
end
|
3737
|
+
end
|
3738
|
+
end
|
3739
|
+
|
3740
|
+
include Glimmer
|
3741
|
+
|
3742
|
+
attr_accessor :animals
|
3743
|
+
|
3744
|
+
def initialize
|
3745
|
+
@animals = [
|
3746
|
+
AnimalPresenter.new('cat', 'meow', true),
|
3747
|
+
AnimalPresenter.new('dog', 'woof', true),
|
3748
|
+
AnimalPresenter.new('chicken', 'cock-a-doodle-doo', false),
|
3749
|
+
AnimalPresenter.new('horse', 'neigh', true),
|
3750
|
+
AnimalPresenter.new('cow', 'moo', true),
|
3751
|
+
]
|
3752
|
+
end
|
3753
|
+
|
3754
|
+
def launch
|
3755
|
+
window('Animals', 500, 200) {
|
3756
|
+
horizontal_box {
|
3757
|
+
table {
|
3758
|
+
text_color_column('Animal')
|
3759
|
+
text_color_column('Sound')
|
3760
|
+
checkbox_text_color_column('Description')
|
3761
|
+
image_text_color_column('GUI')
|
3762
|
+
background_color_column # must always be the last column and always expects data-binding model attribute `background_color` when binding to Array of models
|
3763
|
+
|
3764
|
+
cell_rows <= [self, :animals, column_attributes: {'Animal' => :name_color, 'Sound' => :sound_color, 'Description' => :mammal_description_color, 'GUI' => :image_description_color}]
|
3765
|
+
}
|
3766
|
+
}
|
3767
|
+
}.show
|
3768
|
+
end
|
3769
|
+
end
|
3770
|
+
|
3771
|
+
BasicTableColor.new.launch
|
3772
|
+
```
|
3280
3773
|
|
3774
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (with implicit [data-binding](#data-binding) to raw data rows):
|
3775
|
+
|
3776
|
+
```ruby
|
3281
3777
|
require 'glimmer-dsl-libui'
|
3282
3778
|
|
3283
3779
|
include Glimmer
|
3284
3780
|
|
3285
|
-
img =
|
3781
|
+
img = [File.expand_path('../icons/glimmer.png', __dir__), 24, 24] # scales image to 24x24 (can be passed as file path String only instead of Array to avoid scaling)
|
3286
3782
|
|
3287
3783
|
data = [
|
3288
3784
|
[['cat', :red] , ['meow', :blue] , [true, 'mammal', :green], [img, 'Glimmer', :dark_blue], {r: 255, g: 120, b: 0, a: 0.5}],
|
@@ -3299,7 +3795,7 @@ window('Animals', 500, 200) {
|
|
3299
3795
|
text_color_column('Sound')
|
3300
3796
|
checkbox_text_color_column('Description')
|
3301
3797
|
image_text_color_column('GUI')
|
3302
|
-
background_color_column
|
3798
|
+
background_color_column # must be the last column
|
3303
3799
|
|
3304
3800
|
cell_rows data
|
3305
3801
|
}
|
@@ -3307,7 +3803,7 @@ window('Animals', 500, 200) {
|
|
3307
3803
|
}.show
|
3308
3804
|
```
|
3309
3805
|
|
3310
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version
|
3806
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (with implicit [data-binding](#data-binding) to raw data rows and manual construction of [libui](https://github.com/andlabs/libui) `image` from `image_part`):
|
3311
3807
|
|
3312
3808
|
```ruby
|
3313
3809
|
require 'glimmer-dsl-libui'
|
@@ -3341,7 +3837,7 @@ window('Animals', 500, 200) {
|
|
3341
3837
|
text_color_column('Sound')
|
3342
3838
|
checkbox_text_color_column('Description')
|
3343
3839
|
image_text_color_column('GUI')
|
3344
|
-
background_color_column
|
3840
|
+
background_color_column
|
3345
3841
|
|
3346
3842
|
cell_rows data
|
3347
3843
|
}
|
@@ -3483,9 +3979,9 @@ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/
|
|
3483
3979
|
ruby -r glimmer-dsl-libui -e "require 'examples/basic_scrolling_area'"
|
3484
3980
|
```
|
3485
3981
|
|
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)
|
3982
|
+
Mac | Windows | Linux
|
3983
|
+
----|---------|------
|
3984
|
+
![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
3985
|
|
3490
3986
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
3491
3987
|
|
@@ -3572,6 +4068,8 @@ BasicScrollingArea.new.launch
|
|
3572
4068
|
|
3573
4069
|
#### Basic Image
|
3574
4070
|
|
4071
|
+
Please note the caveats of [Area Image](#area-image) **(Alpha Feature)** with regards to this example.
|
4072
|
+
|
3575
4073
|
[examples/basic_image.rb](examples/basic_image.rb)
|
3576
4074
|
|
3577
4075
|
Run with this command from the root of the project if you cloned the project:
|
@@ -3607,7 +4105,11 @@ window('Basic Image', 96, 96) {
|
|
3607
4105
|
# image pixel rendered. Check basic_image2.rb for a faster alternative using on_draw manually.
|
3608
4106
|
#
|
3609
4107
|
# It is recommended to pass width/height args to shrink image and achieve faster performance.
|
3610
|
-
image(File.expand_path('../icons/glimmer.png', __dir__),
|
4108
|
+
image(File.expand_path('../icons/glimmer.png', __dir__), height: 96) # width is automatically calculated from height while preserving original aspect ratio
|
4109
|
+
# image(File.expand_path('../icons/glimmer.png', __dir__), width: 96, height: 96) # you can specify both width, height options as alternative
|
4110
|
+
# image(File.expand_path('../icons/glimmer.png', __dir__), 96, 96) # you can specify width, height args as alternative
|
4111
|
+
# image(File.expand_path('../icons/glimmer.png', __dir__), 0, 0, 96, 96) # you can specify x, y, width, height args as alternative
|
4112
|
+
# image(File.expand_path('../icons/glimmer.png', __dir__), x: 0, y: 0, width: 96, height: 96) # you can specify x, y, width, height options as alternative
|
3611
4113
|
}
|
3612
4114
|
}.show
|
3613
4115
|
```
|
@@ -3615,8 +4117,6 @@ window('Basic Image', 96, 96) {
|
|
3615
4117
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (better performance via `on_draw`):
|
3616
4118
|
|
3617
4119
|
```ruby
|
3618
|
-
# frozen_string_literal: true
|
3619
|
-
|
3620
4120
|
require 'glimmer-dsl-libui'
|
3621
4121
|
|
3622
4122
|
include Glimmer
|
@@ -3624,7 +4124,7 @@ include Glimmer
|
|
3624
4124
|
window('Basic Image', 96, 96) {
|
3625
4125
|
area {
|
3626
4126
|
on_draw do |area_draw_params|
|
3627
|
-
image(File.expand_path('../icons/glimmer.png', __dir__),
|
4127
|
+
image(File.expand_path('../icons/glimmer.png', __dir__), height: 96)
|
3628
4128
|
end
|
3629
4129
|
}
|
3630
4130
|
}.show
|
@@ -3633,8 +4133,6 @@ window('Basic Image', 96, 96) {
|
|
3633
4133
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (explicit properties):
|
3634
4134
|
|
3635
4135
|
```ruby
|
3636
|
-
# frozen_string_literal: true
|
3637
|
-
|
3638
4136
|
require 'glimmer-dsl-libui'
|
3639
4137
|
|
3640
4138
|
include Glimmer
|
@@ -3651,7 +4149,9 @@ window('Basic Image', 96, 96) {
|
|
3651
4149
|
# It is recommended to pass width/height args to shrink image and achieve faster performance.
|
3652
4150
|
image {
|
3653
4151
|
file File.expand_path('../icons/glimmer.png', __dir__)
|
3654
|
-
|
4152
|
+
# x 0 # default
|
4153
|
+
# y 0 # default
|
4154
|
+
# width 96 # gets calculated from height while preserving original aspect ratio of 512x512
|
3655
4155
|
height 96
|
3656
4156
|
}
|
3657
4157
|
}
|
@@ -3661,8 +4161,6 @@ window('Basic Image', 96, 96) {
|
|
3661
4161
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 4 (better performance with `on_draw` when setting explicit properties):
|
3662
4162
|
|
3663
4163
|
```ruby
|
3664
|
-
# frozen_string_literal: true
|
3665
|
-
|
3666
4164
|
require 'glimmer-dsl-libui'
|
3667
4165
|
|
3668
4166
|
include Glimmer
|
@@ -3672,7 +4170,6 @@ window('Basic Image', 96, 96) {
|
|
3672
4170
|
on_draw do |area_draw_params|
|
3673
4171
|
image {
|
3674
4172
|
file File.expand_path('../icons/glimmer.png', __dir__)
|
3675
|
-
width 96
|
3676
4173
|
height 96
|
3677
4174
|
}
|
3678
4175
|
end
|
@@ -4822,9 +5319,9 @@ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/
|
|
4822
5319
|
ruby -r glimmer-dsl-libui -e "require 'examples/button_counter'"
|
4823
5320
|
```
|
4824
5321
|
|
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)
|
5322
|
+
Mac | Windows | Linux
|
5323
|
+
----|---------|------
|
5324
|
+
![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
5325
|
|
4829
5326
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
4830
5327
|
|
@@ -5540,7 +6037,7 @@ Glimmer::LibUI.timer(1) do
|
|
5540
6037
|
data[0][2] = cpu_percentage_value
|
5541
6038
|
end
|
5542
6039
|
|
5543
|
-
window('CPU Percentage', 400,
|
6040
|
+
window('CPU Percentage', 400, 50) {
|
5544
6041
|
vertical_box {
|
5545
6042
|
table {
|
5546
6043
|
text_column('Name')
|
@@ -6263,8 +6760,8 @@ window('Editable animal sounds', 300, 200) {
|
|
6263
6760
|
text_column('Animal')
|
6264
6761
|
text_column('Description')
|
6265
6762
|
|
6266
|
-
cell_rows data
|
6267
6763
|
editable true
|
6764
|
+
cell_rows data
|
6268
6765
|
|
6269
6766
|
on_changed do |row, type, row_data| # fires on all changes (even ones happening through data array)
|
6270
6767
|
puts "Row #{row} #{type}: #{row_data}"
|
@@ -6302,7 +6799,359 @@ Mac | Windows | Linux
|
|
6302
6799
|
----|---------|------
|
6303
6800
|
![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
6801
|
|
6305
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
6802
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
|
6803
|
+
|
6804
|
+
```ruby
|
6805
|
+
require 'glimmer-dsl-libui'
|
6806
|
+
|
6807
|
+
class FormTable
|
6808
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
6809
|
+
|
6810
|
+
include Glimmer
|
6811
|
+
|
6812
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
6813
|
+
|
6814
|
+
def initialize
|
6815
|
+
@contacts = [
|
6816
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
6817
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
6818
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
6819
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
6820
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
6821
|
+
]
|
6822
|
+
end
|
6823
|
+
|
6824
|
+
def launch
|
6825
|
+
window('Contacts', 600, 600) { |w|
|
6826
|
+
margined true
|
6827
|
+
|
6828
|
+
vertical_box {
|
6829
|
+
form {
|
6830
|
+
stretchy false
|
6831
|
+
|
6832
|
+
entry {
|
6833
|
+
label 'Name'
|
6834
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
6835
|
+
}
|
6836
|
+
|
6837
|
+
entry {
|
6838
|
+
label 'Email'
|
6839
|
+
text <=> [self, :email]
|
6840
|
+
}
|
6841
|
+
|
6842
|
+
entry {
|
6843
|
+
label 'Phone'
|
6844
|
+
text <=> [self, :phone]
|
6845
|
+
}
|
6846
|
+
|
6847
|
+
entry {
|
6848
|
+
label 'City'
|
6849
|
+
text <=> [self, :city]
|
6850
|
+
}
|
6851
|
+
|
6852
|
+
entry {
|
6853
|
+
label 'State'
|
6854
|
+
text <=> [self, :state]
|
6855
|
+
}
|
6856
|
+
}
|
6857
|
+
|
6858
|
+
button('Save Contact') {
|
6859
|
+
stretchy false
|
6860
|
+
|
6861
|
+
on_clicked do
|
6862
|
+
new_row = [name, email, phone, city, state]
|
6863
|
+
if new_row.include?('')
|
6864
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
6865
|
+
else
|
6866
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to explicit data-binding
|
6867
|
+
@unfiltered_contacts = @contacts.dup
|
6868
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
6869
|
+
self.email = ''
|
6870
|
+
self.phone = ''
|
6871
|
+
self.city = ''
|
6872
|
+
self.state = ''
|
6873
|
+
end
|
6874
|
+
end
|
6875
|
+
}
|
6876
|
+
|
6877
|
+
search_entry {
|
6878
|
+
stretchy false
|
6879
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
6880
|
+
text <=> [self, :filter_value,
|
6881
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
6882
|
+
@unfiltered_contacts ||= @contacts.dup
|
6883
|
+
# Unfilter first to remove any previous filters
|
6884
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
6885
|
+
# Now, apply filter if entered
|
6886
|
+
unless filter_value.empty?
|
6887
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
6888
|
+
contact.members.any? do |attribute|
|
6889
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
6890
|
+
end
|
6891
|
+
end
|
6892
|
+
end
|
6893
|
+
}
|
6894
|
+
]
|
6895
|
+
}
|
6896
|
+
|
6897
|
+
table {
|
6898
|
+
text_column('Name')
|
6899
|
+
text_column('Email')
|
6900
|
+
text_column('Phone')
|
6901
|
+
text_column('City')
|
6902
|
+
text_column('State')
|
6903
|
+
|
6904
|
+
editable true
|
6905
|
+
cell_rows <=> [self, :contacts] # explicit data-binding to self.contacts Model Array, auto-inferring model attribute names from underscored table column names by convention
|
6906
|
+
|
6907
|
+
on_changed do |row, type, row_data|
|
6908
|
+
puts "Row #{row} #{type}: #{row_data}"
|
6909
|
+
end
|
6910
|
+
}
|
6911
|
+
}
|
6912
|
+
}.show
|
6913
|
+
end
|
6914
|
+
end
|
6915
|
+
|
6916
|
+
FormTable.new.launch
|
6917
|
+
```
|
6918
|
+
|
6919
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
|
6920
|
+
|
6921
|
+
```ruby
|
6922
|
+
require 'glimmer-dsl-libui'
|
6923
|
+
|
6924
|
+
class FormTable
|
6925
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
6926
|
+
|
6927
|
+
include Glimmer
|
6928
|
+
|
6929
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
6930
|
+
|
6931
|
+
def initialize
|
6932
|
+
@contacts = [
|
6933
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
6934
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
6935
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
6936
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
6937
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
6938
|
+
]
|
6939
|
+
end
|
6940
|
+
|
6941
|
+
def launch
|
6942
|
+
window('Contacts', 600, 600) { |w|
|
6943
|
+
margined true
|
6944
|
+
|
6945
|
+
vertical_box {
|
6946
|
+
form {
|
6947
|
+
stretchy false
|
6948
|
+
|
6949
|
+
entry {
|
6950
|
+
label 'Name'
|
6951
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
6952
|
+
}
|
6953
|
+
|
6954
|
+
entry {
|
6955
|
+
label 'Email'
|
6956
|
+
text <=> [self, :email]
|
6957
|
+
}
|
6958
|
+
|
6959
|
+
entry {
|
6960
|
+
label 'Phone'
|
6961
|
+
text <=> [self, :phone]
|
6962
|
+
}
|
6963
|
+
|
6964
|
+
entry {
|
6965
|
+
label 'City'
|
6966
|
+
text <=> [self, :city]
|
6967
|
+
}
|
6968
|
+
|
6969
|
+
entry {
|
6970
|
+
label 'State'
|
6971
|
+
text <=> [self, :state]
|
6972
|
+
}
|
6973
|
+
}
|
6974
|
+
|
6975
|
+
button('Save Contact') {
|
6976
|
+
stretchy false
|
6977
|
+
|
6978
|
+
on_clicked do
|
6979
|
+
new_row = [name, email, phone, city, state]
|
6980
|
+
if new_row.include?('')
|
6981
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
6982
|
+
else
|
6983
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to implicit data-binding
|
6984
|
+
@unfiltered_contacts = @contacts.dup
|
6985
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
6986
|
+
self.email = ''
|
6987
|
+
self.phone = ''
|
6988
|
+
self.city = ''
|
6989
|
+
self.state = ''
|
6990
|
+
end
|
6991
|
+
end
|
6992
|
+
}
|
6993
|
+
|
6994
|
+
search_entry {
|
6995
|
+
stretchy false
|
6996
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
6997
|
+
text <=> [self, :filter_value,
|
6998
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
6999
|
+
@unfiltered_contacts ||= @contacts.dup
|
7000
|
+
# Unfilter first to remove any previous filters
|
7001
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
7002
|
+
# Now, apply filter if entered
|
7003
|
+
unless filter_value.empty?
|
7004
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
7005
|
+
contact.members.any? do |attribute|
|
7006
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
7007
|
+
end
|
7008
|
+
end
|
7009
|
+
end
|
7010
|
+
}
|
7011
|
+
]
|
7012
|
+
}
|
7013
|
+
|
7014
|
+
table {
|
7015
|
+
text_column('Name')
|
7016
|
+
text_column('Email')
|
7017
|
+
text_column('Phone')
|
7018
|
+
text_column('City')
|
7019
|
+
text_column('State/Province')
|
7020
|
+
|
7021
|
+
editable true
|
7022
|
+
cell_rows <=> [self, :contacts, column_attributes: {'State/Province' => :state}] # explicit data-binding to Model Array with column_attributes mapping for a specific column
|
7023
|
+
|
7024
|
+
on_changed do |row, type, row_data|
|
7025
|
+
puts "Row #{row} #{type}: #{row_data}"
|
7026
|
+
end
|
7027
|
+
}
|
7028
|
+
}
|
7029
|
+
}.show
|
7030
|
+
end
|
7031
|
+
end
|
7032
|
+
|
7033
|
+
FormTable.new.launch
|
7034
|
+
```
|
7035
|
+
|
7036
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
|
7037
|
+
|
7038
|
+
```ruby
|
7039
|
+
|
7040
|
+
require 'glimmer-dsl-libui'
|
7041
|
+
|
7042
|
+
class FormTable
|
7043
|
+
Contact = Struct.new(:full_name, :email_address, :phone_number, :city_or_town, :state_or_province)
|
7044
|
+
|
7045
|
+
include Glimmer
|
7046
|
+
|
7047
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
7048
|
+
|
7049
|
+
def initialize
|
7050
|
+
@contacts = [
|
7051
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
7052
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
7053
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
7054
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
7055
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
7056
|
+
]
|
7057
|
+
end
|
7058
|
+
|
7059
|
+
def launch
|
7060
|
+
window('Contacts', 600, 600) { |w|
|
7061
|
+
margined true
|
7062
|
+
|
7063
|
+
vertical_box {
|
7064
|
+
form {
|
7065
|
+
stretchy false
|
7066
|
+
|
7067
|
+
entry {
|
7068
|
+
label 'Name'
|
7069
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
7070
|
+
}
|
7071
|
+
|
7072
|
+
entry {
|
7073
|
+
label 'Email'
|
7074
|
+
text <=> [self, :email]
|
7075
|
+
}
|
7076
|
+
|
7077
|
+
entry {
|
7078
|
+
label 'Phone'
|
7079
|
+
text <=> [self, :phone]
|
7080
|
+
}
|
7081
|
+
|
7082
|
+
entry {
|
7083
|
+
label 'City'
|
7084
|
+
text <=> [self, :city]
|
7085
|
+
}
|
7086
|
+
|
7087
|
+
entry {
|
7088
|
+
label 'State'
|
7089
|
+
text <=> [self, :state]
|
7090
|
+
}
|
7091
|
+
}
|
7092
|
+
|
7093
|
+
button('Save Contact') {
|
7094
|
+
stretchy false
|
7095
|
+
|
7096
|
+
on_clicked do
|
7097
|
+
new_row = [name, email, phone, city, state]
|
7098
|
+
if new_row.include?('')
|
7099
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
7100
|
+
else
|
7101
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to implicit data-binding
|
7102
|
+
@unfiltered_contacts = @contacts.dup
|
7103
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
7104
|
+
self.email = ''
|
7105
|
+
self.phone = ''
|
7106
|
+
self.city = ''
|
7107
|
+
self.state = ''
|
7108
|
+
end
|
7109
|
+
end
|
7110
|
+
}
|
7111
|
+
|
7112
|
+
search_entry {
|
7113
|
+
stretchy false
|
7114
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
7115
|
+
text <=> [self, :filter_value,
|
7116
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
7117
|
+
@unfiltered_contacts ||= @contacts.dup
|
7118
|
+
# Unfilter first to remove any previous filters
|
7119
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
7120
|
+
# Now, apply filter if entered
|
7121
|
+
unless filter_value.empty?
|
7122
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
7123
|
+
contact.members.any? do |attribute|
|
7124
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
7125
|
+
end
|
7126
|
+
end
|
7127
|
+
end
|
7128
|
+
}
|
7129
|
+
]
|
7130
|
+
}
|
7131
|
+
|
7132
|
+
table {
|
7133
|
+
text_column('Name')
|
7134
|
+
text_column('Email')
|
7135
|
+
text_column('Phone')
|
7136
|
+
text_column('City')
|
7137
|
+
text_column('State')
|
7138
|
+
|
7139
|
+
editable true
|
7140
|
+
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
|
7141
|
+
|
7142
|
+
on_changed do |row, type, row_data|
|
7143
|
+
puts "Row #{row} #{type}: #{row_data}"
|
7144
|
+
end
|
7145
|
+
}
|
7146
|
+
}
|
7147
|
+
}.show
|
7148
|
+
end
|
7149
|
+
end
|
7150
|
+
|
7151
|
+
FormTable.new.launch
|
7152
|
+
```
|
7153
|
+
|
7154
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 4 (with explicit [data-binding](#data-binding) to raw data):
|
6306
7155
|
|
6307
7156
|
```ruby
|
6308
7157
|
require 'glimmer-dsl-libui'
|
@@ -6332,7 +7181,7 @@ class FormTable
|
|
6332
7181
|
|
6333
7182
|
entry {
|
6334
7183
|
label 'Name'
|
6335
|
-
text <=> [self, :name]
|
7184
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
6336
7185
|
}
|
6337
7186
|
|
6338
7187
|
entry {
|
@@ -6364,8 +7213,8 @@ class FormTable
|
|
6364
7213
|
if new_row.include?('')
|
6365
7214
|
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
6366
7215
|
else
|
6367
|
-
|
6368
|
-
@unfiltered_data =
|
7216
|
+
data << new_row # automatically inserts a row into the table due to implicit data-binding
|
7217
|
+
@unfiltered_data = data.dup
|
6369
7218
|
self.name = '' # automatically clears name entry through explicit data-binding
|
6370
7219
|
self.email = ''
|
6371
7220
|
self.phone = ''
|
@@ -6377,14 +7226,15 @@ class FormTable
|
|
6377
7226
|
|
6378
7227
|
search_entry {
|
6379
7228
|
stretchy false
|
6380
|
-
|
7229
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
7230
|
+
text <=> [self, :filter_value,
|
6381
7231
|
after_write: ->(filter_value) { # execute after write to self.filter_value
|
6382
|
-
@unfiltered_data ||=
|
7232
|
+
@unfiltered_data ||= data.dup
|
6383
7233
|
# Unfilter first to remove any previous filters
|
6384
|
-
|
7234
|
+
data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
|
6385
7235
|
# Now, apply filter if entered
|
6386
7236
|
unless filter_value.empty?
|
6387
|
-
|
7237
|
+
data.filter! do |row_data| # affects table indirectly through implicit data-binding
|
6388
7238
|
row_data.any? do |cell|
|
6389
7239
|
cell.to_s.downcase.include?(filter_value.downcase)
|
6390
7240
|
end
|
@@ -6400,8 +7250,9 @@ class FormTable
|
|
6400
7250
|
text_column('Phone')
|
6401
7251
|
text_column('City')
|
6402
7252
|
text_column('State')
|
6403
|
-
|
6404
|
-
|
7253
|
+
|
7254
|
+
editable true
|
7255
|
+
cell_rows <=> [self, :data] # explicit data-binding to raw data Array of Arrays
|
6405
7256
|
|
6406
7257
|
on_changed do |row, type, row_data|
|
6407
7258
|
puts "Row #{row} #{type}: #{row_data}"
|
@@ -6415,7 +7266,7 @@ end
|
|
6415
7266
|
FormTable.new.launch
|
6416
7267
|
```
|
6417
7268
|
|
6418
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version
|
7269
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 5 (with implicit [data-binding](#data-binding)):
|
6419
7270
|
|
6420
7271
|
```ruby
|
6421
7272
|
require 'glimmer-dsl-libui'
|
@@ -6503,7 +7354,8 @@ window('Contacts', 600, 600) { |w|
|
|
6503
7354
|
text_column('City')
|
6504
7355
|
text_column('State')
|
6505
7356
|
|
6506
|
-
|
7357
|
+
editable true
|
7358
|
+
cell_rows data # implicit data-binding to raw data Array of Arrays
|
6507
7359
|
|
6508
7360
|
on_changed do |row, type, row_data|
|
6509
7361
|
puts "Row #{row} #{type}: #{row_data}"
|