glimmer-dsl-libui 0.4.10 → 0.4.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +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
|
[](http://badge.fury.io/rb/glimmer-dsl-libui)
|
4
4
|
[](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
@@ -77,6 +77,127 @@ Mac | Windows | Linux
|
|
77
77
|
----|---------|------
|
78
78
|
 |  | 
|
79
79
|
|
80
|
+
Form Table
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
require 'glimmer-dsl-libui'
|
84
|
+
|
85
|
+
class FormTable
|
86
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
87
|
+
|
88
|
+
include Glimmer
|
89
|
+
|
90
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
91
|
+
|
92
|
+
def initialize
|
93
|
+
@contacts = [
|
94
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
95
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
96
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
97
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
98
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
99
|
+
]
|
100
|
+
end
|
101
|
+
|
102
|
+
def launch
|
103
|
+
window('Contacts', 600, 600) { |w|
|
104
|
+
margined true
|
105
|
+
|
106
|
+
vertical_box {
|
107
|
+
form {
|
108
|
+
stretchy false
|
109
|
+
|
110
|
+
entry {
|
111
|
+
label 'Name'
|
112
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
113
|
+
}
|
114
|
+
|
115
|
+
entry {
|
116
|
+
label 'Email'
|
117
|
+
text <=> [self, :email]
|
118
|
+
}
|
119
|
+
|
120
|
+
entry {
|
121
|
+
label 'Phone'
|
122
|
+
text <=> [self, :phone]
|
123
|
+
}
|
124
|
+
|
125
|
+
entry {
|
126
|
+
label 'City'
|
127
|
+
text <=> [self, :city]
|
128
|
+
}
|
129
|
+
|
130
|
+
entry {
|
131
|
+
label 'State'
|
132
|
+
text <=> [self, :state]
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
button('Save Contact') {
|
137
|
+
stretchy false
|
138
|
+
|
139
|
+
on_clicked do
|
140
|
+
new_row = [name, email, phone, city, state]
|
141
|
+
if new_row.include?('')
|
142
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
143
|
+
else
|
144
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to explicit data-binding
|
145
|
+
@unfiltered_contacts = @contacts.dup
|
146
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
147
|
+
self.email = ''
|
148
|
+
self.phone = ''
|
149
|
+
self.city = ''
|
150
|
+
self.state = ''
|
151
|
+
end
|
152
|
+
end
|
153
|
+
}
|
154
|
+
|
155
|
+
search_entry {
|
156
|
+
stretchy false
|
157
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
158
|
+
text <=> [self, :filter_value,
|
159
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
160
|
+
@unfiltered_contacts ||= @contacts.dup
|
161
|
+
# Unfilter first to remove any previous filters
|
162
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
163
|
+
# Now, apply filter if entered
|
164
|
+
unless filter_value.empty?
|
165
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
166
|
+
contact.members.any? do |attribute|
|
167
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
}
|
172
|
+
]
|
173
|
+
}
|
174
|
+
|
175
|
+
table {
|
176
|
+
text_column('Name')
|
177
|
+
text_column('Email')
|
178
|
+
text_column('Phone')
|
179
|
+
text_column('City')
|
180
|
+
text_column('State')
|
181
|
+
|
182
|
+
editable true
|
183
|
+
cell_rows <=> [self, :contacts] # explicit data-binding to 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
|
+
 |  | 
|
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
|
+
 |  | 
|
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
|
-

|
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
|
+
 |  | 
|
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
|
+
 |  | 
|
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
|
-
  |  
|
928
|
+
Mac | Windows | Linux
|
929
|
+
----|---------|------
|
930
|
+
  |   |  
|
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
|

|
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
|
 |  | 
|
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
|
 |  | 
|
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
|
-
  |  
|
3982
|
+
Mac | Windows | Linux
|
3983
|
+
----|---------|------
|
3984
|
+
  |   |  
|
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
|
-
 | 
|
5322
|
+
Mac | Windows | Linux
|
5323
|
+
----|---------|------
|
5324
|
+
 |  | 
|
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
|
   |    |   
|
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}"
|