glimmer-dsl-libui 0.4.8 → 0.4.12
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 +32 -0
- data/README.md +1361 -461
- data/VERSION +1 -1
- data/examples/basic_table_button.rb +54 -30
- data/examples/basic_table_button2.rb +34 -0
- data/examples/basic_table_color.rb +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 +36 -0
- 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/examples/snake.rb +19 -10
- data/examples/snake2.rb +97 -0
- data/examples/tic_tac_toe.rb +1 -0
- data/examples/tic_tac_toe2.rb +84 -0
- 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/checkbox_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/column/background_color_column_proxy.rb +4 -0
- data/lib/glimmer/libui/control_proxy/combobox_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/date_time_picker_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/editable_combobox_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/entry_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +5 -1
- data/lib/glimmer/libui/control_proxy/menu_item_proxy/check_menu_item_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/multiline_entry_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/slider_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/spinbox_proxy.rb +1 -2
- 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 +34 -4
- data/lib/glimmer/libui/shape.rb +3 -2
- data/lib/glimmer/libui.rb +2 -2
- data/lib/glimmer-dsl-libui.rb +1 -0
- metadata +12 -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.12
|
2
2
|
## Prerequisite-Free Ruby Desktop Development GUI Library
|
3
3
|
[](http://badge.fury.io/rb/glimmer-dsl-libui)
|
4
4
|
[](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
@@ -77,6 +77,127 @@ Mac | Windows | Linux
|
|
77
77
|
----|---------|------
|
78
78
|
 |  | 
|
79
79
|
|
80
|
+
Form Table
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
require 'glimmer-dsl-libui'
|
84
|
+
|
85
|
+
class FormTable
|
86
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
87
|
+
|
88
|
+
include Glimmer
|
89
|
+
|
90
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
91
|
+
|
92
|
+
def initialize
|
93
|
+
@contacts = [
|
94
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
95
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
96
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
97
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
98
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
99
|
+
]
|
100
|
+
end
|
101
|
+
|
102
|
+
def launch
|
103
|
+
window('Contacts', 600, 600) { |w|
|
104
|
+
margined true
|
105
|
+
|
106
|
+
vertical_box {
|
107
|
+
form {
|
108
|
+
stretchy false
|
109
|
+
|
110
|
+
entry {
|
111
|
+
label 'Name'
|
112
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
113
|
+
}
|
114
|
+
|
115
|
+
entry {
|
116
|
+
label 'Email'
|
117
|
+
text <=> [self, :email]
|
118
|
+
}
|
119
|
+
|
120
|
+
entry {
|
121
|
+
label 'Phone'
|
122
|
+
text <=> [self, :phone]
|
123
|
+
}
|
124
|
+
|
125
|
+
entry {
|
126
|
+
label 'City'
|
127
|
+
text <=> [self, :city]
|
128
|
+
}
|
129
|
+
|
130
|
+
entry {
|
131
|
+
label 'State'
|
132
|
+
text <=> [self, :state]
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
button('Save Contact') {
|
137
|
+
stretchy false
|
138
|
+
|
139
|
+
on_clicked do
|
140
|
+
new_row = [name, email, phone, city, state]
|
141
|
+
if new_row.include?('')
|
142
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
143
|
+
else
|
144
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to explicit data-binding
|
145
|
+
@unfiltered_contacts = @contacts.dup
|
146
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
147
|
+
self.email = ''
|
148
|
+
self.phone = ''
|
149
|
+
self.city = ''
|
150
|
+
self.state = ''
|
151
|
+
end
|
152
|
+
end
|
153
|
+
}
|
154
|
+
|
155
|
+
search_entry {
|
156
|
+
stretchy false
|
157
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
158
|
+
text <=> [self, :filter_value,
|
159
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
160
|
+
@unfiltered_contacts ||= @contacts.dup
|
161
|
+
# Unfilter first to remove any previous filters
|
162
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
163
|
+
# Now, apply filter if entered
|
164
|
+
unless filter_value.empty?
|
165
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
166
|
+
contact.members.any? do |attribute|
|
167
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
}
|
172
|
+
]
|
173
|
+
}
|
174
|
+
|
175
|
+
table {
|
176
|
+
text_column('Name')
|
177
|
+
text_column('Email')
|
178
|
+
text_column('Phone')
|
179
|
+
text_column('City')
|
180
|
+
text_column('State')
|
181
|
+
|
182
|
+
editable true
|
183
|
+
cell_rows <=> [self, :contacts] # explicit data-binding to Model Array
|
184
|
+
|
185
|
+
on_changed do |row, type, row_data|
|
186
|
+
puts "Row #{row} #{type}: #{row_data}"
|
187
|
+
end
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}.show
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
FormTable.new.launch
|
195
|
+
```
|
196
|
+
|
197
|
+
Mac | Windows | Linux
|
198
|
+
----|---------|------
|
199
|
+
 |  | 
|
200
|
+
|
80
201
|
Area Gallery
|
81
202
|
|
82
203
|
```ruby
|
@@ -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)
|
@@ -267,6 +393,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
|
|
267
393
|
- [Button Counter](#button-counter)
|
268
394
|
- [Color The Circles](#color-the-circles)
|
269
395
|
- [Control Gallery](#control-gallery)
|
396
|
+
- [CPU Percentage](#cpu-percentage)
|
270
397
|
- [Custom Draw Text](#custom-draw-text)
|
271
398
|
- [Dynamic Area](#dynamic-area)
|
272
399
|
- [Editable Column Table](#editable-column-table)
|
@@ -373,10 +500,20 @@ gem install glimmer-dsl-libui
|
|
373
500
|
Or install via Bundler `Gemfile`:
|
374
501
|
|
375
502
|
```ruby
|
376
|
-
gem 'glimmer-dsl-libui', '~> 0.4.
|
503
|
+
gem 'glimmer-dsl-libui', '~> 0.4.12'
|
377
504
|
```
|
378
505
|
|
379
|
-
|
506
|
+
Test that installation worked by running the [Meta-Example](#examples):
|
507
|
+
|
508
|
+
```
|
509
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/meta_example'"
|
510
|
+
```
|
511
|
+
|
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.
|
380
517
|
|
381
518
|
Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
382
519
|
|
@@ -451,7 +588,7 @@ Keyword(Args) | Properties | Listeners
|
|
451
588
|
`about_menu_item` | None | `on_clicked`
|
452
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)`
|
453
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
|
454
|
-
`background_color_column
|
591
|
+
`background_color_column` | None | None
|
455
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
|
456
593
|
`button(text as String)` | `text` (`String`) | `on_clicked`
|
457
594
|
`button_column(name as String)` | `enabled` (Boolean) | None
|
@@ -615,120 +752,110 @@ Note that the `cell_rows` property declaration results in "implicit data-binding
|
|
615
752
|
- Inserting cell rows: Calling `Array#<<`, `Array#push`, `Array#prepend`, or any insertion/addition `Array` method automatically inserts rows in actual `table` control
|
616
753
|
- Changing cell rows: Calling `Array#[]=`, `Array#map!`, or any update `Array` method automatically updates rows in actual `table` control
|
617
754
|
|
755
|
+
([explicit data-binding](#data-binding) supports everything available with implicit data-binding too)
|
756
|
+
|
618
757
|
Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
619
758
|
|
620
759
|
```ruby
|
621
760
|
require 'glimmer-dsl-libui'
|
622
761
|
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
]
|
636
|
-
end
|
762
|
+
include Glimmer
|
763
|
+
|
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|
|
773
|
+
margined true
|
637
774
|
|
638
|
-
|
639
|
-
|
640
|
-
|
775
|
+
vertical_box {
|
776
|
+
form {
|
777
|
+
stretchy false
|
641
778
|
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
779
|
+
@name_entry = entry {
|
780
|
+
label 'Name'
|
781
|
+
}
|
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)
|
687
832
|
end
|
688
833
|
end
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
stretchy false
|
693
|
-
text <=> [self, :filter_value, # bidirectional data-binding of text to self.filter_value with after_write option
|
694
|
-
after_write: ->(filter_value) { # execute after write to self.filter_value
|
695
|
-
@unfiltered_data ||= @data.dup
|
696
|
-
# Unfilter first to remove any previous filters
|
697
|
-
@data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
|
698
|
-
# Now, apply filter if entered
|
699
|
-
unless filter_value.empty?
|
700
|
-
@data.filter! do |row_data| # affects table indirectly through implicit data-binding
|
701
|
-
row_data.any? do |cell|
|
702
|
-
cell.to_s.downcase.include?(filter_value.downcase)
|
703
|
-
end
|
704
|
-
end
|
705
|
-
end
|
706
|
-
}
|
707
|
-
]
|
708
|
-
}
|
709
|
-
|
710
|
-
table {
|
711
|
-
text_column('Name')
|
712
|
-
text_column('Email')
|
713
|
-
text_column('Phone')
|
714
|
-
text_column('City')
|
715
|
-
text_column('State')
|
834
|
+
end
|
835
|
+
end
|
836
|
+
}
|
716
837
|
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
}
|
724
|
-
}.show
|
725
|
-
end
|
726
|
-
end
|
838
|
+
table {
|
839
|
+
text_column('Name')
|
840
|
+
text_column('Email')
|
841
|
+
text_column('Phone')
|
842
|
+
text_column('City')
|
843
|
+
text_column('State')
|
727
844
|
|
728
|
-
|
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
|
729
854
|
```
|
730
855
|
|
731
|
-
|
856
|
+
Mac | Windows | Linux
|
857
|
+
----|---------|------
|
858
|
+
 |  | 
|
732
859
|
|
733
860
|
Learn more by checking out [examples](#examples).
|
734
861
|
|
@@ -798,9 +925,9 @@ Check [examples/dynamic_area.rb](#dynamic-area) for a more detailed semi-declara
|
|
798
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.
|
799
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.
|
800
927
|
|
801
|
-
Mac |Linux
|
802
|
-
|
803
|
-
  |  
|
928
|
+
Mac | Windows | Linux
|
929
|
+
----|---------|------
|
930
|
+
  |   |  
|
804
931
|
|
805
932
|
Check [examples/basic_scrolling_area.rb](#basic-scrolling-area) for a more detailed example.
|
806
933
|
|
@@ -1216,6 +1343,7 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
|
|
1216
1343
|
- When destroying a control nested under a `horizontal_box` or `vertical_box`, it is automatically deleted from the box's children
|
1217
1344
|
- When destroying a control nested under a `form`, it is automatically deleted from the form's children
|
1218
1345
|
- When destroying a control nested under a `window` or `group`, it is automatically unset as their child to allow successful destruction
|
1346
|
+
- When destroying a control that has a data-binding to a model attribute, the data-binding observer registration is automatically deregistered
|
1219
1347
|
- 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`
|
1220
1348
|
- Smart defaults for `grid` child properties are `left` (`0`), `top` (`0`), `xspan` (`1`), `yspan` (`1`), `hexpand` (`false`), `halign` (`:fill`), `vexpand` (`false`), and `valign` (`:fill`)
|
1221
1349
|
- 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`)
|
@@ -1381,6 +1509,8 @@ Data-binding supports utilizing the [MVP (Model View Presenter)](https://en.wiki
|
|
1381
1509
|
|
1382
1510
|

|
1383
1511
|
|
1512
|
+
#### Bidirectional (Two-Way) Data-Binding
|
1513
|
+
|
1384
1514
|
[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):
|
1385
1515
|
- `checkbox`: `checked`
|
1386
1516
|
- `check_menu_item`: `checked`
|
@@ -1398,27 +1528,184 @@ Data-binding supports utilizing the [MVP (Model View Presenter)](https://en.wiki
|
|
1398
1528
|
- `search_entry`: `text`
|
1399
1529
|
- `slider`: `value`
|
1400
1530
|
- `spinbox`: `value`
|
1531
|
+
- `table`: `cell_rows` (explicit data-binding by using `<=>` and [implicit data-binding](#table-api) by assigning value directly)
|
1401
1532
|
- `time_picker`: `time`
|
1402
1533
|
|
1403
|
-
Example of bidirectional data-binding:
|
1534
|
+
Example of bidirectional data-binding:
|
1535
|
+
|
1536
|
+
```ruby
|
1537
|
+
entry {
|
1538
|
+
text <=> [contract, :legal_text]
|
1539
|
+
}
|
1540
|
+
```
|
1541
|
+
|
1542
|
+
That is data-binding a contract's legal text to an `entry` `text` property.
|
1543
|
+
|
1544
|
+
Another example of bidirectional data-binding with an option:
|
1545
|
+
|
1546
|
+
```ruby
|
1547
|
+
entry {
|
1548
|
+
text <=> [self, :entered_text, after_write: ->(text) {puts text}]
|
1549
|
+
}
|
1550
|
+
```
|
1551
|
+
|
1552
|
+
That is data-binding `entered_text` attribute on `self` to `entry` `text` property and printing text after write to the model.
|
1553
|
+
|
1554
|
+
##### Table Data-Binding
|
1555
|
+
|
1556
|
+
One note about `table` `cell_rows` data-binding is that it works with either:
|
1557
|
+
- Raw data `Array` (rows) of `Array`s (column cells)
|
1558
|
+
- 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.
|
1559
|
+
|
1560
|
+
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)):
|
1561
|
+
|
1562
|
+
```ruby
|
1563
|
+
require 'glimmer-dsl-libui'
|
1564
|
+
|
1565
|
+
include Glimmer
|
1566
|
+
|
1567
|
+
data = [
|
1568
|
+
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
|
1569
|
+
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
|
1570
|
+
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
|
1571
|
+
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
|
1572
|
+
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
|
1573
|
+
]
|
1574
|
+
|
1575
|
+
window('Contacts', 600, 600) {
|
1576
|
+
table {
|
1577
|
+
text_column('Name')
|
1578
|
+
text_column('Email')
|
1579
|
+
text_column('Phone')
|
1580
|
+
text_column('City')
|
1581
|
+
text_column('State')
|
1582
|
+
|
1583
|
+
cell_rows data
|
1584
|
+
}
|
1585
|
+
}.show
|
1586
|
+
```
|
1587
|
+
|
1588
|
+
Example of `table` explicit data-binding of `cell_rows` to Model `Array` (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
1589
|
+
|
1590
|
+
```ruby
|
1591
|
+
require 'glimmer-dsl-libui'
|
1592
|
+
|
1593
|
+
class SomeTable
|
1594
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
1595
|
+
|
1596
|
+
include Glimmer
|
1597
|
+
|
1598
|
+
attr_accessor :contacts
|
1599
|
+
|
1600
|
+
def initialize
|
1601
|
+
@contacts = [
|
1602
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
1603
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
1604
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
1605
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
1606
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
1607
|
+
]
|
1608
|
+
end
|
1609
|
+
|
1610
|
+
def launch
|
1611
|
+
window('Contacts', 600, 200) {
|
1612
|
+
table {
|
1613
|
+
text_column('Name')
|
1614
|
+
text_column('Email')
|
1615
|
+
text_column('Phone')
|
1616
|
+
text_column('City')
|
1617
|
+
text_column('State')
|
1618
|
+
|
1619
|
+
cell_rows <=> [self, :contacts] # explicit data-binding to Model Array auto-inferring model attribute names from underscored table column names by convention
|
1620
|
+
}
|
1621
|
+
}.show
|
1622
|
+
end
|
1623
|
+
end
|
1624
|
+
|
1625
|
+
SomeTable.new.launch
|
1626
|
+
```
|
1627
|
+
|
1628
|
+
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)):
|
1404
1629
|
|
1405
1630
|
```ruby
|
1406
|
-
|
1407
|
-
text <=> [contract, :legal_text]
|
1408
|
-
}
|
1409
|
-
```
|
1631
|
+
require 'glimmer-dsl-libui'
|
1410
1632
|
|
1411
|
-
|
1633
|
+
class SomeTable
|
1634
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
1635
|
+
|
1636
|
+
include Glimmer
|
1637
|
+
|
1638
|
+
attr_accessor :contacts
|
1639
|
+
|
1640
|
+
def initialize
|
1641
|
+
@contacts = [
|
1642
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
1643
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
1644
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
1645
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
1646
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
1647
|
+
]
|
1648
|
+
end
|
1649
|
+
|
1650
|
+
def launch
|
1651
|
+
window('Contacts', 600, 200) {
|
1652
|
+
table {
|
1653
|
+
text_column('Name')
|
1654
|
+
text_column('Email')
|
1655
|
+
text_column('Phone')
|
1656
|
+
text_column('City/Town')
|
1657
|
+
text_column('State/Province')
|
1658
|
+
|
1659
|
+
cell_rows <=> [self, :contacts, column_attributes: {'City/Town' => :city, 'State/Province' => :state}]
|
1660
|
+
}
|
1661
|
+
}.show
|
1662
|
+
end
|
1663
|
+
end
|
1412
1664
|
|
1413
|
-
|
1665
|
+
SomeTable.new.launch
|
1666
|
+
```
|
1667
|
+
|
1668
|
+
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)):
|
1414
1669
|
|
1415
1670
|
```ruby
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1671
|
+
require 'glimmer-dsl-libui'
|
1672
|
+
|
1673
|
+
class SomeTable
|
1674
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
1675
|
+
|
1676
|
+
include Glimmer
|
1677
|
+
|
1678
|
+
attr_accessor :contacts
|
1679
|
+
|
1680
|
+
def initialize
|
1681
|
+
@contacts = [
|
1682
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
1683
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
1684
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
1685
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
1686
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
1687
|
+
]
|
1688
|
+
end
|
1689
|
+
|
1690
|
+
def launch
|
1691
|
+
window('Contacts', 600, 200) {
|
1692
|
+
table {
|
1693
|
+
text_column('Full Name')
|
1694
|
+
text_column('Email Address')
|
1695
|
+
text_column('Phone Number')
|
1696
|
+
text_column('City or Town')
|
1697
|
+
text_column('State or Province')
|
1698
|
+
|
1699
|
+
cell_rows <=> [self, :contacts, column_attributes: [:name, :email, :phone, :city, :state]]
|
1700
|
+
}
|
1701
|
+
}.show
|
1702
|
+
end
|
1703
|
+
end
|
1704
|
+
|
1705
|
+
SomeTable.new.launch
|
1419
1706
|
```
|
1420
1707
|
|
1421
|
-
|
1708
|
+
#### Unidirectional (One-Way) Data-Binding
|
1422
1709
|
|
1423
1710
|
[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).
|
1424
1711
|
|
@@ -1442,6 +1729,8 @@ window {
|
|
1442
1729
|
|
1443
1730
|
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`.
|
1444
1731
|
|
1732
|
+
#### Data-Binding API
|
1733
|
+
|
1445
1734
|
To summarize the data-binding API:
|
1446
1735
|
- `view_property <=> [model, attribute, *read_or_write_options]`: Bidirectional (two-way) data-binding to Model attribute accessor
|
1447
1736
|
- `view_property <= [model, attribute, *read_only_options]`: Unidirectional (one-way) data-binding to Model attribute reader
|
@@ -1472,12 +1761,13 @@ entry {
|
|
1472
1761
|
}
|
1473
1762
|
```
|
1474
1763
|
|
1475
|
-
|
1764
|
+
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.
|
1765
|
+
|
1766
|
+
#### Data-Binding Gotchas
|
1767
|
+
|
1476
1768
|
- 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)
|
1477
1769
|
- 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.
|
1478
1770
|
|
1479
|
-
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.
|
1480
|
-
|
1481
1771
|
### API Gotchas
|
1482
1772
|
|
1483
1773
|
- There is no proper way to destroy `grid` children due to [libui](https://github.com/andlabs/libui) not offering any API for deleting them from `grid` (no `grid_delete` similar to `box_delete` for `horizontal_box` and `vertical_box`).
|
@@ -1489,272 +1779,12 @@ Learn more from data-binding usage in [Login](#login) (4 data-binding versions),
|
|
1489
1779
|
- `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.
|
1490
1780
|
- 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.
|
1491
1781
|
- As per the code of [examples/basic_transform.rb](#basic-transform), Windows requires different ordering of transforms than Mac and Linux.
|
1782
|
+
- `scrolling_area#scroll_to` does not seem to work on Windows and Linux, but works fine on Mac
|
1492
1783
|
|
1493
1784
|
### Original API
|
1494
1785
|
|
1495
1786
|
Here are all the lower-level [LibUI](https://github.com/kojix2/LibUI) API methods utilized by [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui):
|
1496
|
-
|
1497
|
-
- `area_begin_user_window_move`
|
1498
|
-
- `area_begin_user_window_resize`
|
1499
|
-
- `area_queue_redraw_all`
|
1500
|
-
- `area_scroll_to`
|
1501
|
-
- `area_set_size`
|
1502
|
-
- `attribute_color`
|
1503
|
-
- `attribute_family`
|
1504
|
-
- `attribute_features`
|
1505
|
-
- `attribute_get_type`
|
1506
|
-
- `attribute_italic`
|
1507
|
-
- `attribute_size`
|
1508
|
-
- `attribute_stretch`
|
1509
|
-
- `attribute_underline`
|
1510
|
-
- `attribute_underline_color`
|
1511
|
-
- `attribute_weight`
|
1512
|
-
- `attributed_string_append_unattributed`
|
1513
|
-
- `attributed_string_byte_index_to_grapheme`
|
1514
|
-
- `attributed_string_delete`
|
1515
|
-
- `attributed_string_for_each_attribute`
|
1516
|
-
- `attributed_string_grapheme_to_byte_index`
|
1517
|
-
- `attributed_string_insert_at_unattributed`
|
1518
|
-
- `attributed_string_len`
|
1519
|
-
- `attributed_string_num_graphemes`
|
1520
|
-
- `attributed_string_set_attribute`
|
1521
|
-
- `attributed_string_string`
|
1522
|
-
- `box_append`
|
1523
|
-
- `box_delete`
|
1524
|
-
- `box_padded`
|
1525
|
-
- `box_set_padded`
|
1526
|
-
- `button_on_clicked`
|
1527
|
-
- `button_set_text`
|
1528
|
-
- `button_text`
|
1529
|
-
- `checkbox_checked`
|
1530
|
-
- `checkbox_on_toggled`
|
1531
|
-
- `checkbox_set_checked`
|
1532
|
-
- `checkbox_set_text`
|
1533
|
-
- `checkbox_text`
|
1534
|
-
- `color_button_color`
|
1535
|
-
- `color_button_on_changed`
|
1536
|
-
- `color_button_set_color`
|
1537
|
-
- `combobox_append`
|
1538
|
-
- `combobox_on_selected`
|
1539
|
-
- `combobox_selected`
|
1540
|
-
- `combobox_set_selected`
|
1541
|
-
- `control_destroy`
|
1542
|
-
- `control_disable`
|
1543
|
-
- `control_enable`
|
1544
|
-
- `control_enabled`
|
1545
|
-
- `control_enabled_to_user`
|
1546
|
-
- `control_handle`
|
1547
|
-
- `control_hide`
|
1548
|
-
- `control_parent`
|
1549
|
-
- `control_set_parent`
|
1550
|
-
- `control_show`
|
1551
|
-
- `control_toplevel`
|
1552
|
-
- `control_verify_set_parent`
|
1553
|
-
- `control_visible`
|
1554
|
-
- `date_time_picker_on_changed`
|
1555
|
-
- `date_time_picker_set_time`
|
1556
|
-
- `date_time_picker_time`
|
1557
|
-
- `draw_clip`
|
1558
|
-
- `draw_fill`
|
1559
|
-
- `draw_free_path`
|
1560
|
-
- `draw_free_text_layout`
|
1561
|
-
- `draw_matrix_invert`
|
1562
|
-
- `draw_matrix_invertible`
|
1563
|
-
- `draw_matrix_multiply`
|
1564
|
-
- `draw_matrix_rotate`
|
1565
|
-
- `draw_matrix_scale`
|
1566
|
-
- `draw_matrix_set_identity`
|
1567
|
-
- `draw_matrix_skew`
|
1568
|
-
- `draw_matrix_transform_point`
|
1569
|
-
- `draw_matrix_transform_size`
|
1570
|
-
- `draw_matrix_translate`
|
1571
|
-
- `draw_new_path`
|
1572
|
-
- `draw_new_text_layout`
|
1573
|
-
- `draw_path_add_rectangle`
|
1574
|
-
- `draw_path_arc_to`
|
1575
|
-
- `draw_path_bezier_to`
|
1576
|
-
- `draw_path_close_figure`
|
1577
|
-
- `draw_path_end`
|
1578
|
-
- `draw_path_line_to`
|
1579
|
-
- `draw_path_new_figure`
|
1580
|
-
- `draw_path_new_figure_with_arc`
|
1581
|
-
- `draw_restore`
|
1582
|
-
- `draw_save`
|
1583
|
-
- `draw_stroke`
|
1584
|
-
- `draw_text`
|
1585
|
-
- `draw_text_layout_extents`
|
1586
|
-
- `draw_transform`
|
1587
|
-
- `editable_combobox_append`
|
1588
|
-
- `editable_combobox_on_changed`
|
1589
|
-
- `editable_combobox_set_text`
|
1590
|
-
- `editable_combobox_text`
|
1591
|
-
- `entry_on_changed`
|
1592
|
-
- `entry_read_only`
|
1593
|
-
- `entry_set_read_only`
|
1594
|
-
- `entry_set_text`
|
1595
|
-
- `entry_text`
|
1596
|
-
- `ffi_lib`
|
1597
|
-
- `ffi_lib=`
|
1598
|
-
- `font_button_font`
|
1599
|
-
- `font_button_on_changed`
|
1600
|
-
- `form_append`
|
1601
|
-
- `form_delete`
|
1602
|
-
- `form_padded`
|
1603
|
-
- `form_set_padded`
|
1604
|
-
- `free_attribute`
|
1605
|
-
- `free_attributed_string`
|
1606
|
-
- `free_control`
|
1607
|
-
- `free_font_button_font`
|
1608
|
-
- `free_image`
|
1609
|
-
- `free_init_error`
|
1610
|
-
- `free_open_type_features`
|
1611
|
-
- `free_table_model`
|
1612
|
-
- `free_table_value`
|
1613
|
-
- `free_text`
|
1614
|
-
- `grid_append`
|
1615
|
-
- `grid_insert_at`
|
1616
|
-
- `grid_padded`
|
1617
|
-
- `grid_set_padded`
|
1618
|
-
- `group_margined`
|
1619
|
-
- `group_set_child`
|
1620
|
-
- `group_set_margined`
|
1621
|
-
- `group_set_title`
|
1622
|
-
- `group_title`
|
1623
|
-
- `image_append`
|
1624
|
-
- `init`
|
1625
|
-
- `label_set_text`
|
1626
|
-
- `label_text`
|
1627
|
-
- `main`
|
1628
|
-
- `main_step`
|
1629
|
-
- `main_steps`
|
1630
|
-
- `menu_append_about_item`
|
1631
|
-
- `menu_append_check_item`
|
1632
|
-
- `menu_append_item`
|
1633
|
-
- `menu_append_preferences_item`
|
1634
|
-
- `menu_append_quit_item`
|
1635
|
-
- `menu_append_separator`
|
1636
|
-
- `menu_item_checked`
|
1637
|
-
- `menu_item_disable`
|
1638
|
-
- `menu_item_enable`
|
1639
|
-
- `menu_item_on_clicked`
|
1640
|
-
- `menu_item_set_checked`
|
1641
|
-
- `msg_box`
|
1642
|
-
- `msg_box_error`
|
1643
|
-
- `multiline_entry_append`
|
1644
|
-
- `multiline_entry_on_changed`
|
1645
|
-
- `multiline_entry_read_only`
|
1646
|
-
- `multiline_entry_set_read_only`
|
1647
|
-
- `multiline_entry_set_text`
|
1648
|
-
- `multiline_entry_text`
|
1649
|
-
- `new_area`
|
1650
|
-
- `new_attributed_string`
|
1651
|
-
- `new_background_attribute`
|
1652
|
-
- `new_button`
|
1653
|
-
- `new_checkbox`
|
1654
|
-
- `new_color_attribute`
|
1655
|
-
- `new_color_button`
|
1656
|
-
- `new_combobox`
|
1657
|
-
- `new_date_picker`
|
1658
|
-
- `new_date_time_picker`
|
1659
|
-
- `new_editable_combobox`
|
1660
|
-
- `new_entry`
|
1661
|
-
- `new_family_attribute`
|
1662
|
-
- `new_features_attribute`
|
1663
|
-
- `new_font_button`
|
1664
|
-
- `new_form`
|
1665
|
-
- `new_grid`
|
1666
|
-
- `new_group`
|
1667
|
-
- `new_horizontal_box`
|
1668
|
-
- `new_horizontal_separator`
|
1669
|
-
- `new_image`
|
1670
|
-
- `new_italic_attribute`
|
1671
|
-
- `new_label`
|
1672
|
-
- `new_menu`
|
1673
|
-
- `new_multiline_entry`
|
1674
|
-
- `new_non_wrapping_multiline_entry`
|
1675
|
-
- `new_open_type_features`
|
1676
|
-
- `new_password_entry`
|
1677
|
-
- `new_progress_bar`
|
1678
|
-
- `new_radio_buttons`
|
1679
|
-
- `new_scrolling_area`
|
1680
|
-
- `new_search_entry`
|
1681
|
-
- `new_size_attribute`
|
1682
|
-
- `new_slider`
|
1683
|
-
- `new_spinbox`
|
1684
|
-
- `new_stretch_attribute`
|
1685
|
-
- `new_tab`
|
1686
|
-
- `new_table`
|
1687
|
-
- `new_table_model`
|
1688
|
-
- `new_table_value_color`
|
1689
|
-
- `new_table_value_image`
|
1690
|
-
- `new_table_value_int`
|
1691
|
-
- `new_table_value_string`
|
1692
|
-
- `new_time_picker`
|
1693
|
-
- `new_underline_attribute`
|
1694
|
-
- `new_underline_color_attribute`
|
1695
|
-
- `new_vertical_box`
|
1696
|
-
- `new_vertical_separator`
|
1697
|
-
- `new_weight_attribute`
|
1698
|
-
- `new_window`
|
1699
|
-
- `on_should_quit`
|
1700
|
-
- `open_file`
|
1701
|
-
- `open_type_features_add`
|
1702
|
-
- `open_type_features_clone`
|
1703
|
-
- `open_type_features_for_each`
|
1704
|
-
- `open_type_features_get`
|
1705
|
-
- `open_type_features_remove`
|
1706
|
-
- `progress_bar_set_value`
|
1707
|
-
- `progress_bar_value`
|
1708
|
-
- `queue_main`
|
1709
|
-
- `quit`
|
1710
|
-
- `radio_buttons_append`
|
1711
|
-
- `radio_buttons_on_selected`
|
1712
|
-
- `radio_buttons_selected`
|
1713
|
-
- `radio_buttons_set_selected`
|
1714
|
-
- `save_file`
|
1715
|
-
- `slider_on_changed`
|
1716
|
-
- `slider_set_value`
|
1717
|
-
- `slider_value`
|
1718
|
-
- `spinbox_on_changed`
|
1719
|
-
- `spinbox_set_value`
|
1720
|
-
- `spinbox_value`
|
1721
|
-
- `tab_append`
|
1722
|
-
- `tab_delete`
|
1723
|
-
- `tab_insert_at`
|
1724
|
-
- `tab_margined`
|
1725
|
-
- `tab_num_pages`
|
1726
|
-
- `tab_set_margined`
|
1727
|
-
- `table_append_button_column`
|
1728
|
-
- `table_append_checkbox_column`
|
1729
|
-
- `table_append_checkbox_text_column`
|
1730
|
-
- `table_append_image_column`
|
1731
|
-
- `table_append_image_text_column`
|
1732
|
-
- `table_append_progress_bar_column`
|
1733
|
-
- `table_append_text_column`
|
1734
|
-
- `table_model_row_changed`
|
1735
|
-
- `table_model_row_deleted`
|
1736
|
-
- `table_model_row_inserted`
|
1737
|
-
- `table_value_color`
|
1738
|
-
- `table_value_get_type`
|
1739
|
-
- `table_value_image`
|
1740
|
-
- `table_value_int`
|
1741
|
-
- `table_value_string`
|
1742
|
-
- `timer`
|
1743
|
-
- `uninit`
|
1744
|
-
- `user_bug_cannot_set_parent_on_toplevel`
|
1745
|
-
- `window_borderless`
|
1746
|
-
- `window_content_size`
|
1747
|
-
- `window_fullscreen`
|
1748
|
-
- `window_margined`
|
1749
|
-
- `window_on_closing`
|
1750
|
-
- `window_on_content_size_changed`
|
1751
|
-
- `window_set_borderless`
|
1752
|
-
- `window_set_child`
|
1753
|
-
- `window_set_content_size`
|
1754
|
-
- `window_set_fullscreen`
|
1755
|
-
- `window_set_margined`
|
1756
|
-
- `window_set_title`
|
1757
|
-
- `window_title`
|
1787
|
+
`alloc_control`, `append_features`, `area_begin_user_window_move`, `area_begin_user_window_resize`, `area_queue_redraw_all`, `area_scroll_to`, `area_set_size`, `attribute_color`, `attribute_family`, `attribute_features`, `attribute_get_type`, `attribute_italic`, `attribute_size`, `attribute_stretch`, `attribute_underline`, `attribute_underline_color`, `attribute_weight`, `attributed_string_append_unattributed`, `attributed_string_byte_index_to_grapheme`, `attributed_string_delete`, `attributed_string_for_each_attribute`, `attributed_string_grapheme_to_byte_index`, `attributed_string_insert_at_unattributed`, `attributed_string_len`, `attributed_string_num_graphemes`, `attributed_string_set_attribute`, `attributed_string_string`, `box_append`, `box_delete`, `box_padded`, `box_set_padded`, `button_on_clicked`, `button_set_text`, `button_text`, `checkbox_checked`, `checkbox_on_toggled`, `checkbox_set_checked`, `checkbox_set_text`, `checkbox_text`, `color_button_color`, `color_button_on_changed`, `color_button_set_color`, `combobox_append`, `combobox_on_selected`, `combobox_selected`, `combobox_set_selected`, `control_destroy`, `control_disable`, `control_enable`, `control_enabled`, `control_enabled_to_user`, `control_handle`, `control_hide`, `control_parent`, `control_set_parent`, `control_show`, `control_toplevel`, `control_verify_set_parent`, `control_visible`, `date_time_picker_on_changed`, `date_time_picker_set_time`, `date_time_picker_time`, `draw_clip`, `draw_fill`, `draw_free_path`, `draw_free_text_layout`, `draw_matrix_invert`, `draw_matrix_invertible`, `draw_matrix_multiply`, `draw_matrix_rotate`, `draw_matrix_scale`, `draw_matrix_set_identity`, `draw_matrix_skew`, `draw_matrix_transform_point`, `draw_matrix_transform_size`, `draw_matrix_translate`, `draw_new_path`, `draw_new_text_layout`, `draw_path_add_rectangle`, `draw_path_arc_to`, `draw_path_bezier_to`, `draw_path_close_figure`, `draw_path_end`, `draw_path_line_to`, `draw_path_new_figure`, `draw_path_new_figure_with_arc`, `draw_restore`, `draw_save`, `draw_stroke`, `draw_text`, `draw_text_layout_extents`, `draw_transform`, `editable_combobox_append`, `editable_combobox_on_changed`, `editable_combobox_set_text`, `editable_combobox_text`, `entry_on_changed`, `entry_read_only`, `entry_set_read_only`, `entry_set_text`, `entry_text`, `ffi_lib`, `ffi_lib=`, `font_button_font`, `font_button_on_changed`, `form_append`, `form_delete`, `form_padded`, `form_set_padded`, `free_attribute`, `free_attributed_string`, `free_control`, `free_font_button_font`, `free_image`, `free_init_error`, `free_open_type_features`, `free_table_model`, `free_table_value`, `free_text`, `grid_append`, `grid_insert_at`, `grid_padded`, `grid_set_padded`, `group_margined`, `group_set_child`, `group_set_margined`, `group_set_title`, `group_title`, `image_append`, `init`, `label_set_text`, `label_text`, `main`, `main_step`, `main_steps`, `menu_append_about_item`, `menu_append_check_item`, `menu_append_item`, `menu_append_preferences_item`, `menu_append_quit_item`, `menu_append_separator`, `menu_item_checked`, `menu_item_disable`, `menu_item_enable`, `menu_item_on_clicked`, `menu_item_set_checked`, `msg_box`, `msg_box_error`, `multiline_entry_append`, `multiline_entry_on_changed`, `multiline_entry_read_only`, `multiline_entry_set_read_only`, `multiline_entry_set_text`, `multiline_entry_text`, `new_area`, `new_attributed_string`, `new_background_attribute`, `new_button`, `new_checkbox`, `new_color_attribute`, `new_color_button`, `new_combobox`, `new_date_picker`, `new_date_time_picker`, `new_editable_combobox`, `new_entry`, `new_family_attribute`, `new_features_attribute`, `new_font_button`, `new_form`, `new_grid`, `new_group`, `new_horizontal_box`, `new_horizontal_separator`, `new_image`, `new_italic_attribute`, `new_label`, `new_menu`, `new_multiline_entry`, `new_non_wrapping_multiline_entry`, `new_open_type_features`, `new_password_entry`, `new_progress_bar`, `new_radio_buttons`, `new_scrolling_area`, `new_search_entry`, `new_size_attribute`, `new_slider`, `new_spinbox`, `new_stretch_attribute`, `new_tab`, `new_table`, `new_table_model`, `new_table_value_color`, `new_table_value_image`, `new_table_value_int`, `new_table_value_string`, `new_time_picker`, `new_underline_attribute`, `new_underline_color_attribute`, `new_vertical_box`, `new_vertical_separator`, `new_weight_attribute`, `new_window`, `on_should_quit`, `open_file`, `open_type_features_add`, `open_type_features_clone`, `open_type_features_for_each`, `open_type_features_get`, `open_type_features_remove`, `progress_bar_set_value`, `progress_bar_value`, `queue_main`, `quit`, `radio_buttons_append`, `radio_buttons_on_selected`, `radio_buttons_selected`, `radio_buttons_set_selected`, `save_file`, `slider_on_changed`, `slider_set_value`, `slider_value`, `spinbox_on_changed`, `spinbox_set_value`, `spinbox_value`, `tab_append`, `tab_delete`, `tab_insert_at`, `tab_margined`, `tab_num_pages`, `tab_set_margined`, `table_append_button_column`, `table_append_checkbox_column`, `table_append_checkbox_text_column`, `table_append_image_column`, `table_append_image_text_column`, `table_append_progress_bar_column`, `table_append_text_column`, `table_model_row_changed`, `table_model_row_deleted`, `table_model_row_inserted`, `table_value_color`, `table_value_get_type`, `table_value_image`, `table_value_int`, `table_value_string`, `timer`, `uninit`, `user_bug_cannot_set_parent_on_toplevel`, `window_borderless`, `window_content_size`, `window_fullscreen`, `window_margined`, `window_on_closing`, `window_on_content_size_changed`, `window_set_borderless`, `window_set_child`, `window_set_content_size`, `window_set_fullscreen`, `window_set_margined`, `window_set_title`, `window_title`
|
1758
1788
|
|
1759
1789
|
To learn more about the [LibUI](https://github.com/kojix2/LibUI) API exposed through [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui):
|
1760
1790
|
- Check out [LibUI ffi.rb](https://github.com/kojix2/LibUI/blob/main/lib/libui/ffi.rb)
|
@@ -3063,7 +3093,44 @@ UI.main
|
|
3063
3093
|
UI.quit
|
3064
3094
|
```
|
3065
3095
|
|
3066
|
-
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
3096
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (passing file url as image):
|
3097
|
+
|
3098
|
+
```ruby
|
3099
|
+
# frozen_string_literal: true
|
3100
|
+
|
3101
|
+
# NOTE:
|
3102
|
+
# This example displays images that can be freely downloaded from the Studio Ghibli website.
|
3103
|
+
|
3104
|
+
require 'glimmer-dsl-libui'
|
3105
|
+
|
3106
|
+
include Glimmer
|
3107
|
+
|
3108
|
+
IMAGE_ROWS = []
|
3109
|
+
|
3110
|
+
50.times do |i|
|
3111
|
+
url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', (i + 1))
|
3112
|
+
puts "Processing Image: #{url}"; $stdout.flush # for Windows
|
3113
|
+
IMAGE_ROWS << [url] # array of one column cell
|
3114
|
+
rescue StandardError => e
|
3115
|
+
warn url, e.message
|
3116
|
+
end
|
3117
|
+
|
3118
|
+
window('The Red Turtle', 310, 350, false) {
|
3119
|
+
horizontal_box {
|
3120
|
+
table {
|
3121
|
+
image_column('www.ghibli.jp/works/red-turtle')
|
3122
|
+
|
3123
|
+
cell_rows IMAGE_ROWS
|
3124
|
+
}
|
3125
|
+
}
|
3126
|
+
|
3127
|
+
on_closing do
|
3128
|
+
puts 'Bye Bye'
|
3129
|
+
end
|
3130
|
+
}.show
|
3131
|
+
```
|
3132
|
+
|
3133
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (automatic construction of `image`):
|
3067
3134
|
|
3068
3135
|
```ruby
|
3069
3136
|
# NOTE:
|
@@ -3098,7 +3165,7 @@ window('The Red Turtle', 310, 350, false) {
|
|
3098
3165
|
}.show
|
3099
3166
|
```
|
3100
3167
|
|
3101
|
-
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version
|
3168
|
+
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (manual construction of `image` from `image_part`):
|
3102
3169
|
|
3103
3170
|
```ruby
|
3104
3171
|
# NOTE:
|
@@ -3166,7 +3233,44 @@ Mac | Windows | Linux
|
|
3166
3233
|
----|---------|------
|
3167
3234
|
 |  | 
|
3168
3235
|
|
3169
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
3236
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (passing file url as image):
|
3237
|
+
|
3238
|
+
```ruby
|
3239
|
+
# frozen_string_literal: true
|
3240
|
+
|
3241
|
+
# NOTE:
|
3242
|
+
# This example displays images that can be freely downloaded from the Studio Ghibli website.
|
3243
|
+
|
3244
|
+
require 'glimmer-dsl-libui'
|
3245
|
+
|
3246
|
+
include Glimmer
|
3247
|
+
|
3248
|
+
IMAGE_ROWS = []
|
3249
|
+
|
3250
|
+
5.times do |i|
|
3251
|
+
url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', (i + 1))
|
3252
|
+
puts "Processing Image: #{url}"; $stdout.flush # for Windows
|
3253
|
+
text = url.sub('https://www.ghibli.jp/gallery/thumb-redturtle', '').sub('.png', '')
|
3254
|
+
IMAGE_ROWS << [[url, text], [url, text]] # cell values are dual-element arrays
|
3255
|
+
rescue StandardError => e
|
3256
|
+
warn url, e.message
|
3257
|
+
end
|
3258
|
+
|
3259
|
+
window('The Red Turtle', 670, 350) {
|
3260
|
+
horizontal_box {
|
3261
|
+
table {
|
3262
|
+
image_text_column('image/number')
|
3263
|
+
image_text_column('image/number (editable)') {
|
3264
|
+
editable true
|
3265
|
+
}
|
3266
|
+
|
3267
|
+
cell_rows IMAGE_ROWS
|
3268
|
+
}
|
3269
|
+
}
|
3270
|
+
}.show
|
3271
|
+
```
|
3272
|
+
|
3273
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (automatic construction of `image`):
|
3170
3274
|
|
3171
3275
|
```ruby
|
3172
3276
|
# NOTE:
|
@@ -3268,7 +3372,70 @@ Mac | Windows | Linux
|
|
3268
3372
|
----|---------|------
|
3269
3373
|
  |   |  
|
3270
3374
|
|
3271
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
3375
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
|
3376
|
+
|
3377
|
+
```ruby
|
3378
|
+
require 'glimmer-dsl-libui'
|
3379
|
+
|
3380
|
+
class BasicTableButton
|
3381
|
+
BasicAnimal = Struct.new(:name, :sound)
|
3382
|
+
|
3383
|
+
class Animal < BasicAnimal
|
3384
|
+
def action
|
3385
|
+
'delete'
|
3386
|
+
end
|
3387
|
+
end
|
3388
|
+
|
3389
|
+
include Glimmer
|
3390
|
+
|
3391
|
+
attr_accessor :animals
|
3392
|
+
|
3393
|
+
def initialize
|
3394
|
+
@animals = [
|
3395
|
+
Animal.new('cat', 'meow'),
|
3396
|
+
Animal.new('dog', 'woof'),
|
3397
|
+
Animal.new('chicken', 'cock-a-doodle-doo'),
|
3398
|
+
Animal.new('horse', 'neigh'),
|
3399
|
+
Animal.new('cow', 'moo'),
|
3400
|
+
]
|
3401
|
+
end
|
3402
|
+
|
3403
|
+
def launch
|
3404
|
+
window('Animal sounds', 400, 200) {
|
3405
|
+
horizontal_box {
|
3406
|
+
table {
|
3407
|
+
text_column('Animal')
|
3408
|
+
text_column('Description')
|
3409
|
+
button_column('Action') {
|
3410
|
+
on_clicked do |row|
|
3411
|
+
# Option 1: direct data deletion is the simpler solution
|
3412
|
+
# @animals.delete_at(row) # automatically deletes actual table row due to explicit data-binding
|
3413
|
+
|
3414
|
+
# Option 2: cloning only to demonstrate table row deletion upon explicit setting of animals attribute (cloning is not recommended beyond demonstrating this point)
|
3415
|
+
new_animals = @animals.clone
|
3416
|
+
new_animals.delete_at(row)
|
3417
|
+
self.animals = new_animals # automatically loses deleted table row due to explicit data-binding
|
3418
|
+
end
|
3419
|
+
}
|
3420
|
+
|
3421
|
+
|
3422
|
+
cell_rows <= [self, :animals, column_attributes: {'Animal' => :name, 'Description' => :sound}]
|
3423
|
+
|
3424
|
+
# explicit unidirectional data-binding of table cell_rows to self.animals
|
3425
|
+
on_changed do |row, type, row_data|
|
3426
|
+
puts "Row #{row} #{type}: #{row_data}"
|
3427
|
+
$stdout.flush
|
3428
|
+
end
|
3429
|
+
}
|
3430
|
+
}
|
3431
|
+
}.show
|
3432
|
+
end
|
3433
|
+
end
|
3434
|
+
|
3435
|
+
BasicTableButton.new.launch
|
3436
|
+
```
|
3437
|
+
|
3438
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (with implicit [data-binding](#data-binding)):
|
3272
3439
|
|
3273
3440
|
```ruby
|
3274
3441
|
require 'glimmer-dsl-libui'
|
@@ -3477,16 +3644,126 @@ Mac | Windows | Linux
|
|
3477
3644
|
----|---------|------
|
3478
3645
|
 |  | 
|
3479
3646
|
|
3480
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
3647
|
+
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):
|
3481
3648
|
|
3482
3649
|
```ruby
|
3483
|
-
|
3650
|
+
require 'glimmer-dsl-libui'
|
3651
|
+
|
3652
|
+
class BasicTableColor
|
3653
|
+
Animal = Struct.new(:name, :sound, :mammal)
|
3654
|
+
|
3655
|
+
class AnimalPresenter < Animal
|
3656
|
+
def name_color
|
3657
|
+
color = case name
|
3658
|
+
when 'cat'
|
3659
|
+
:red
|
3660
|
+
when 'dog'
|
3661
|
+
:yellow
|
3662
|
+
when 'chicken'
|
3663
|
+
:beige
|
3664
|
+
when 'horse'
|
3665
|
+
:purple
|
3666
|
+
when 'cow'
|
3667
|
+
:gray
|
3668
|
+
end
|
3669
|
+
[name, color]
|
3670
|
+
end
|
3671
|
+
|
3672
|
+
def sound_color
|
3673
|
+
color = case name
|
3674
|
+
when 'cat', 'chicken', 'cow'
|
3675
|
+
:blue
|
3676
|
+
when 'dog', 'horse'
|
3677
|
+
{r: 240, g: 32, b: 32}
|
3678
|
+
end
|
3679
|
+
[sound, color]
|
3680
|
+
end
|
3681
|
+
|
3682
|
+
def mammal_description_color
|
3683
|
+
color = case name
|
3684
|
+
when 'cat', 'dog', 'horse', 'cow'
|
3685
|
+
:green
|
3686
|
+
when 'chicken'
|
3687
|
+
:red
|
3688
|
+
end
|
3689
|
+
[mammal, 'mammal', color]
|
3690
|
+
end
|
3691
|
+
|
3692
|
+
def image_description_color
|
3693
|
+
color = case name
|
3694
|
+
when 'cat', 'dog', 'horse'
|
3695
|
+
:dark_blue
|
3696
|
+
when 'chicken'
|
3697
|
+
:beige
|
3698
|
+
when 'cow'
|
3699
|
+
:brown
|
3700
|
+
end
|
3701
|
+
[img, 'Glimmer', color]
|
3702
|
+
end
|
3703
|
+
|
3704
|
+
def img
|
3705
|
+
# scale image to 24x24 (can be passed as file path String only instead of Array to avoid scaling)
|
3706
|
+
[File.expand_path('../icons/glimmer.png', __dir__), 24, 24]
|
3707
|
+
end
|
3708
|
+
|
3709
|
+
def background_color
|
3710
|
+
case name
|
3711
|
+
when 'cat'
|
3712
|
+
{r: 255, g: 120, b: 0, a: 0.5}
|
3713
|
+
when 'dog'
|
3714
|
+
:skyblue
|
3715
|
+
when 'chicken'
|
3716
|
+
{r: 5, g: 120, b: 110}
|
3717
|
+
when 'horse'
|
3718
|
+
'#13a1fb'
|
3719
|
+
when 'cow'
|
3720
|
+
0x12ff02
|
3721
|
+
end
|
3722
|
+
end
|
3723
|
+
end
|
3724
|
+
|
3725
|
+
include Glimmer
|
3726
|
+
|
3727
|
+
attr_accessor :animals
|
3728
|
+
|
3729
|
+
def initialize
|
3730
|
+
@animals = [
|
3731
|
+
AnimalPresenter.new('cat', 'meow', true),
|
3732
|
+
AnimalPresenter.new('dog', 'woof', true),
|
3733
|
+
AnimalPresenter.new('chicken', 'cock-a-doodle-doo', false),
|
3734
|
+
AnimalPresenter.new('horse', 'neigh', true),
|
3735
|
+
AnimalPresenter.new('cow', 'moo', true),
|
3736
|
+
]
|
3737
|
+
end
|
3738
|
+
|
3739
|
+
def launch
|
3740
|
+
window('Animals', 500, 200) {
|
3741
|
+
horizontal_box {
|
3742
|
+
table {
|
3743
|
+
text_color_column('Animal')
|
3744
|
+
text_color_column('Sound')
|
3745
|
+
checkbox_text_color_column('Description')
|
3746
|
+
image_text_color_column('GUI')
|
3747
|
+
background_color_column # must always be the last column and always expects data-binding model attribute `background_color` when binding to Array of models
|
3748
|
+
|
3749
|
+
cell_rows <= [self, :animals, column_attributes: {'Animal' => :name_color, 'Sound' => :sound_color, 'Description' => :mammal_description_color, 'GUI' => :image_description_color}]
|
3750
|
+
}
|
3751
|
+
}
|
3752
|
+
}.show
|
3753
|
+
end
|
3754
|
+
end
|
3755
|
+
|
3756
|
+
BasicTableColor.new.launch
|
3757
|
+
```
|
3758
|
+
|
3759
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (with implicit [data-binding](#data-binding) to raw data rows):
|
3484
3760
|
|
3761
|
+
```ruby
|
3485
3762
|
require 'glimmer-dsl-libui'
|
3486
3763
|
|
3487
3764
|
include Glimmer
|
3488
3765
|
|
3489
|
-
img =
|
3766
|
+
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)
|
3490
3767
|
|
3491
3768
|
data = [
|
3492
3769
|
[['cat', :red] , ['meow', :blue] , [true, 'mammal', :green], [img, 'Glimmer', :dark_blue], {r: 255, g: 120, b: 0, a: 0.5}],
|
@@ -3503,7 +3780,7 @@ window('Animals', 500, 200) {
|
|
3503
3780
|
text_color_column('Sound')
|
3504
3781
|
checkbox_text_color_column('Description')
|
3505
3782
|
image_text_color_column('GUI')
|
3506
|
-
background_color_column
|
3783
|
+
background_color_column # must be the last column
|
3507
3784
|
|
3508
3785
|
cell_rows data
|
3509
3786
|
}
|
@@ -3511,7 +3788,7 @@ window('Animals', 500, 200) {
|
|
3511
3788
|
}.show
|
3512
3789
|
```
|
3513
3790
|
|
3514
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version
|
3791
|
+
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`):
|
3515
3792
|
|
3516
3793
|
```ruby
|
3517
3794
|
require 'glimmer-dsl-libui'
|
@@ -3545,7 +3822,7 @@ window('Animals', 500, 200) {
|
|
3545
3822
|
text_color_column('Sound')
|
3546
3823
|
checkbox_text_color_column('Description')
|
3547
3824
|
image_text_color_column('GUI')
|
3548
|
-
background_color_column
|
3825
|
+
background_color_column
|
3549
3826
|
|
3550
3827
|
cell_rows data
|
3551
3828
|
}
|
@@ -3687,9 +3964,9 @@ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/
|
|
3687
3964
|
ruby -r glimmer-dsl-libui -e "require 'examples/basic_scrolling_area'"
|
3688
3965
|
```
|
3689
3966
|
|
3690
|
-
Mac | Linux
|
3691
|
-
|
3692
|
-
  |  
|
3967
|
+
Mac | Windows | Linux
|
3968
|
+
----|---------|------
|
3969
|
+
  |   |  
|
3693
3970
|
|
3694
3971
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
3695
3972
|
|
@@ -3776,6 +4053,8 @@ BasicScrollingArea.new.launch
|
|
3776
4053
|
|
3777
4054
|
#### Basic Image
|
3778
4055
|
|
4056
|
+
Please note the caveats of [Area Image](#area-image) **(Alpha Feature)** with regards to this example.
|
4057
|
+
|
3779
4058
|
[examples/basic_image.rb](examples/basic_image.rb)
|
3780
4059
|
|
3781
4060
|
Run with this command from the root of the project if you cloned the project:
|
@@ -5026,9 +5305,9 @@ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/
|
|
5026
5305
|
ruby -r glimmer-dsl-libui -e "require 'examples/button_counter'"
|
5027
5306
|
```
|
5028
5307
|
|
5029
|
-
Mac | Linux
|
5030
|
-
|
5031
|
-
 | 
|
5308
|
+
Mac | Windows | Linux
|
5309
|
+
----|---------|------
|
5310
|
+
 |  | 
|
5032
5311
|
|
5033
5312
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
5034
5313
|
|
@@ -5687,9 +5966,74 @@ MAIN_WINDOW = window('Control Gallery', 600, 500) {
|
|
5687
5966
|
}
|
5688
5967
|
}
|
5689
5968
|
}
|
5690
|
-
}
|
5691
|
-
|
5692
|
-
MAIN_WINDOW.show
|
5969
|
+
}
|
5970
|
+
|
5971
|
+
MAIN_WINDOW.show
|
5972
|
+
```
|
5973
|
+
|
5974
|
+
#### CPU Percentage
|
5975
|
+
|
5976
|
+
This example shows CPU usage percentage second by second.
|
5977
|
+
|
5978
|
+
Note that it is highly dependent on low-level OS terminal commands, so if anything changes in their output formatting, the code could break. Please report any issues you might encounter.
|
5979
|
+
|
5980
|
+
[examples/cpu_percentage.rb](examples/cpu_percentage.rb)
|
5981
|
+
|
5982
|
+
Run with this command from the root of the project if you cloned the project:
|
5983
|
+
|
5984
|
+
```
|
5985
|
+
ruby -r './lib/glimmer-dsl-libui' examples/cpu_percentage.rb
|
5986
|
+
```
|
5987
|
+
|
5988
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
5989
|
+
|
5990
|
+
```
|
5991
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/cpu_percentage'"
|
5992
|
+
```
|
5993
|
+
|
5994
|
+
Mac | Windows | Linux
|
5995
|
+
----|---------|------
|
5996
|
+
 |  | 
|
5997
|
+
|
5998
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
5999
|
+
|
6000
|
+
```ruby
|
6001
|
+
require 'glimmer-dsl-libui'
|
6002
|
+
require 'bigdecimal'
|
6003
|
+
|
6004
|
+
include Glimmer
|
6005
|
+
|
6006
|
+
data = [
|
6007
|
+
['CPU', '0%', 0],
|
6008
|
+
]
|
6009
|
+
|
6010
|
+
Glimmer::LibUI.timer(1) do
|
6011
|
+
cpu_percentage_value = nil
|
6012
|
+
if OS.windows?
|
6013
|
+
cpu_percentage_raw_value = `wmic cpu get loadpercentage`
|
6014
|
+
cpu_percentage_value = cpu_percentage_raw_value.split("\n")[2].to_i
|
6015
|
+
elsif OS.mac?
|
6016
|
+
cpu_percentage_value = `ps -A -o %cpu | awk '{s+=$1} END {print s}'`.to_i
|
6017
|
+
elsif OS.linux?
|
6018
|
+
stats = `top -n 1`
|
6019
|
+
idle_percentage = stats.split("\n")[2].match(/ni,.* (.*) .*id/)[1]
|
6020
|
+
cpu_percentage_value = (BigDecimal(100) - BigDecimal(idle_percentage)).to_i
|
6021
|
+
end
|
6022
|
+
data[0][1] = "#{cpu_percentage_value}%"
|
6023
|
+
data[0][2] = cpu_percentage_value
|
6024
|
+
end
|
6025
|
+
|
6026
|
+
window('CPU Percentage', 400, 200) {
|
6027
|
+
vertical_box {
|
6028
|
+
table {
|
6029
|
+
text_column('Name')
|
6030
|
+
text_column('Value')
|
6031
|
+
progress_bar_column('Percentage')
|
6032
|
+
|
6033
|
+
cell_rows data # implicit data-binding
|
6034
|
+
}
|
6035
|
+
}
|
6036
|
+
}.show
|
5693
6037
|
```
|
5694
6038
|
|
5695
6039
|
#### Custom Draw Text
|
@@ -6402,8 +6746,8 @@ window('Editable animal sounds', 300, 200) {
|
|
6402
6746
|
text_column('Animal')
|
6403
6747
|
text_column('Description')
|
6404
6748
|
|
6405
|
-
cell_rows data
|
6406
6749
|
editable true
|
6750
|
+
cell_rows data
|
6407
6751
|
|
6408
6752
|
on_changed do |row, type, row_data| # fires on all changes (even ones happening through data array)
|
6409
6753
|
puts "Row #{row} #{type}: #{row_data}"
|
@@ -6418,30 +6762,382 @@ window('Editable animal sounds', 300, 200) {
|
|
6418
6762
|
on_closing do
|
6419
6763
|
puts 'Bye Bye'
|
6420
6764
|
end
|
6421
|
-
}.show
|
6422
|
-
```
|
6423
|
-
|
6424
|
-
#### Form Table
|
6425
|
-
|
6426
|
-
[examples/form_table.rb](examples/form_table.rb)
|
6427
|
-
|
6428
|
-
Run with this command from the root of the project if you cloned the project:
|
6429
|
-
|
6430
|
-
```
|
6431
|
-
ruby -r './lib/glimmer-dsl-libui' examples/form_table.rb
|
6432
|
-
```
|
6433
|
-
|
6434
|
-
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
6765
|
+
}.show
|
6766
|
+
```
|
6767
|
+
|
6768
|
+
#### Form Table
|
6769
|
+
|
6770
|
+
[examples/form_table.rb](examples/form_table.rb)
|
6771
|
+
|
6772
|
+
Run with this command from the root of the project if you cloned the project:
|
6773
|
+
|
6774
|
+
```
|
6775
|
+
ruby -r './lib/glimmer-dsl-libui' examples/form_table.rb
|
6776
|
+
```
|
6777
|
+
|
6778
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
6779
|
+
|
6780
|
+
```
|
6781
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/form_table'"
|
6782
|
+
```
|
6783
|
+
|
6784
|
+
Mac | Windows | Linux
|
6785
|
+
----|---------|------
|
6786
|
+
   |    |   
|
6787
|
+
|
6788
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
|
6789
|
+
|
6790
|
+
```ruby
|
6791
|
+
require 'glimmer-dsl-libui'
|
6792
|
+
|
6793
|
+
class FormTable
|
6794
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
6795
|
+
|
6796
|
+
include Glimmer
|
6797
|
+
|
6798
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
6799
|
+
|
6800
|
+
def initialize
|
6801
|
+
@contacts = [
|
6802
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
6803
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
6804
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
6805
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
6806
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
6807
|
+
]
|
6808
|
+
end
|
6809
|
+
|
6810
|
+
def launch
|
6811
|
+
window('Contacts', 600, 600) { |w|
|
6812
|
+
margined true
|
6813
|
+
|
6814
|
+
vertical_box {
|
6815
|
+
form {
|
6816
|
+
stretchy false
|
6817
|
+
|
6818
|
+
entry {
|
6819
|
+
label 'Name'
|
6820
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
6821
|
+
}
|
6822
|
+
|
6823
|
+
entry {
|
6824
|
+
label 'Email'
|
6825
|
+
text <=> [self, :email]
|
6826
|
+
}
|
6827
|
+
|
6828
|
+
entry {
|
6829
|
+
label 'Phone'
|
6830
|
+
text <=> [self, :phone]
|
6831
|
+
}
|
6832
|
+
|
6833
|
+
entry {
|
6834
|
+
label 'City'
|
6835
|
+
text <=> [self, :city]
|
6836
|
+
}
|
6837
|
+
|
6838
|
+
entry {
|
6839
|
+
label 'State'
|
6840
|
+
text <=> [self, :state]
|
6841
|
+
}
|
6842
|
+
}
|
6843
|
+
|
6844
|
+
button('Save Contact') {
|
6845
|
+
stretchy false
|
6846
|
+
|
6847
|
+
on_clicked do
|
6848
|
+
new_row = [name, email, phone, city, state]
|
6849
|
+
if new_row.include?('')
|
6850
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
6851
|
+
else
|
6852
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to explicit data-binding
|
6853
|
+
@unfiltered_contacts = @contacts.dup
|
6854
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
6855
|
+
self.email = ''
|
6856
|
+
self.phone = ''
|
6857
|
+
self.city = ''
|
6858
|
+
self.state = ''
|
6859
|
+
end
|
6860
|
+
end
|
6861
|
+
}
|
6862
|
+
|
6863
|
+
search_entry {
|
6864
|
+
stretchy false
|
6865
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
6866
|
+
text <=> [self, :filter_value,
|
6867
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
6868
|
+
@unfiltered_contacts ||= @contacts.dup
|
6869
|
+
# Unfilter first to remove any previous filters
|
6870
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
6871
|
+
# Now, apply filter if entered
|
6872
|
+
unless filter_value.empty?
|
6873
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
6874
|
+
contact.members.any? do |attribute|
|
6875
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
6876
|
+
end
|
6877
|
+
end
|
6878
|
+
end
|
6879
|
+
}
|
6880
|
+
]
|
6881
|
+
}
|
6882
|
+
|
6883
|
+
table {
|
6884
|
+
text_column('Name')
|
6885
|
+
text_column('Email')
|
6886
|
+
text_column('Phone')
|
6887
|
+
text_column('City')
|
6888
|
+
text_column('State')
|
6889
|
+
|
6890
|
+
editable true
|
6891
|
+
cell_rows <=> [self, :contacts] # explicit data-binding to Model Array auto-inferring model attribute names from underscored table column names by convention
|
6892
|
+
|
6893
|
+
on_changed do |row, type, row_data|
|
6894
|
+
puts "Row #{row} #{type}: #{row_data}"
|
6895
|
+
end
|
6896
|
+
}
|
6897
|
+
}
|
6898
|
+
}.show
|
6899
|
+
end
|
6900
|
+
end
|
6901
|
+
|
6902
|
+
FormTable.new.launch
|
6903
|
+
```
|
6904
|
+
|
6905
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
|
6906
|
+
|
6907
|
+
```ruby
|
6908
|
+
require 'glimmer-dsl-libui'
|
6909
|
+
|
6910
|
+
class FormTable
|
6911
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
6912
|
+
|
6913
|
+
include Glimmer
|
6914
|
+
|
6915
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
6916
|
+
|
6917
|
+
def initialize
|
6918
|
+
@contacts = [
|
6919
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
6920
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
6921
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
6922
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
6923
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
6924
|
+
]
|
6925
|
+
end
|
6926
|
+
|
6927
|
+
def launch
|
6928
|
+
window('Contacts', 600, 600) { |w|
|
6929
|
+
margined true
|
6930
|
+
|
6931
|
+
vertical_box {
|
6932
|
+
form {
|
6933
|
+
stretchy false
|
6934
|
+
|
6935
|
+
entry {
|
6936
|
+
label 'Name'
|
6937
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
6938
|
+
}
|
6939
|
+
|
6940
|
+
entry {
|
6941
|
+
label 'Email'
|
6942
|
+
text <=> [self, :email]
|
6943
|
+
}
|
6944
|
+
|
6945
|
+
entry {
|
6946
|
+
label 'Phone'
|
6947
|
+
text <=> [self, :phone]
|
6948
|
+
}
|
6949
|
+
|
6950
|
+
entry {
|
6951
|
+
label 'City'
|
6952
|
+
text <=> [self, :city]
|
6953
|
+
}
|
6954
|
+
|
6955
|
+
entry {
|
6956
|
+
label 'State'
|
6957
|
+
text <=> [self, :state]
|
6958
|
+
}
|
6959
|
+
}
|
6960
|
+
|
6961
|
+
button('Save Contact') {
|
6962
|
+
stretchy false
|
6963
|
+
|
6964
|
+
on_clicked do
|
6965
|
+
new_row = [name, email, phone, city, state]
|
6966
|
+
if new_row.include?('')
|
6967
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
6968
|
+
else
|
6969
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to implicit data-binding
|
6970
|
+
@unfiltered_contacts = @contacts.dup
|
6971
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
6972
|
+
self.email = ''
|
6973
|
+
self.phone = ''
|
6974
|
+
self.city = ''
|
6975
|
+
self.state = ''
|
6976
|
+
end
|
6977
|
+
end
|
6978
|
+
}
|
6979
|
+
|
6980
|
+
search_entry {
|
6981
|
+
stretchy false
|
6982
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
6983
|
+
text <=> [self, :filter_value,
|
6984
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
6985
|
+
@unfiltered_contacts ||= @contacts.dup
|
6986
|
+
# Unfilter first to remove any previous filters
|
6987
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
6988
|
+
# Now, apply filter if entered
|
6989
|
+
unless filter_value.empty?
|
6990
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
6991
|
+
contact.members.any? do |attribute|
|
6992
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
6993
|
+
end
|
6994
|
+
end
|
6995
|
+
end
|
6996
|
+
}
|
6997
|
+
]
|
6998
|
+
}
|
6999
|
+
|
7000
|
+
table {
|
7001
|
+
text_column('Name')
|
7002
|
+
text_column('Email')
|
7003
|
+
text_column('Phone')
|
7004
|
+
text_column('City')
|
7005
|
+
text_column('State/Province')
|
7006
|
+
|
7007
|
+
editable true
|
7008
|
+
cell_rows <=> [self, :contacts, column_attributes: {'State/Province' => :state}] # explicit data-binding to Model Array with column_attributes mapping for a specific column
|
7009
|
+
|
7010
|
+
on_changed do |row, type, row_data|
|
7011
|
+
puts "Row #{row} #{type}: #{row_data}"
|
7012
|
+
end
|
7013
|
+
}
|
7014
|
+
}
|
7015
|
+
}.show
|
7016
|
+
end
|
7017
|
+
end
|
7018
|
+
|
7019
|
+
FormTable.new.launch
|
7020
|
+
```
|
7021
|
+
|
7022
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
|
7023
|
+
|
7024
|
+
```ruby
|
7025
|
+
|
7026
|
+
require 'glimmer-dsl-libui'
|
7027
|
+
|
7028
|
+
class FormTable
|
7029
|
+
Contact = Struct.new(:full_name, :email_address, :phone_number, :city_or_town, :state_or_province)
|
7030
|
+
|
7031
|
+
include Glimmer
|
7032
|
+
|
7033
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
7034
|
+
|
7035
|
+
def initialize
|
7036
|
+
@contacts = [
|
7037
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
7038
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
7039
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
7040
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
7041
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
7042
|
+
]
|
7043
|
+
end
|
7044
|
+
|
7045
|
+
def launch
|
7046
|
+
window('Contacts', 600, 600) { |w|
|
7047
|
+
margined true
|
7048
|
+
|
7049
|
+
vertical_box {
|
7050
|
+
form {
|
7051
|
+
stretchy false
|
7052
|
+
|
7053
|
+
entry {
|
7054
|
+
label 'Name'
|
7055
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
7056
|
+
}
|
7057
|
+
|
7058
|
+
entry {
|
7059
|
+
label 'Email'
|
7060
|
+
text <=> [self, :email]
|
7061
|
+
}
|
7062
|
+
|
7063
|
+
entry {
|
7064
|
+
label 'Phone'
|
7065
|
+
text <=> [self, :phone]
|
7066
|
+
}
|
7067
|
+
|
7068
|
+
entry {
|
7069
|
+
label 'City'
|
7070
|
+
text <=> [self, :city]
|
7071
|
+
}
|
7072
|
+
|
7073
|
+
entry {
|
7074
|
+
label 'State'
|
7075
|
+
text <=> [self, :state]
|
7076
|
+
}
|
7077
|
+
}
|
7078
|
+
|
7079
|
+
button('Save Contact') {
|
7080
|
+
stretchy false
|
7081
|
+
|
7082
|
+
on_clicked do
|
7083
|
+
new_row = [name, email, phone, city, state]
|
7084
|
+
if new_row.include?('')
|
7085
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
7086
|
+
else
|
7087
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to implicit data-binding
|
7088
|
+
@unfiltered_contacts = @contacts.dup
|
7089
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
7090
|
+
self.email = ''
|
7091
|
+
self.phone = ''
|
7092
|
+
self.city = ''
|
7093
|
+
self.state = ''
|
7094
|
+
end
|
7095
|
+
end
|
7096
|
+
}
|
7097
|
+
|
7098
|
+
search_entry {
|
7099
|
+
stretchy false
|
7100
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
7101
|
+
text <=> [self, :filter_value,
|
7102
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
7103
|
+
@unfiltered_contacts ||= @contacts.dup
|
7104
|
+
# Unfilter first to remove any previous filters
|
7105
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
7106
|
+
# Now, apply filter if entered
|
7107
|
+
unless filter_value.empty?
|
7108
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
7109
|
+
contact.members.any? do |attribute|
|
7110
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
7111
|
+
end
|
7112
|
+
end
|
7113
|
+
end
|
7114
|
+
}
|
7115
|
+
]
|
7116
|
+
}
|
7117
|
+
|
7118
|
+
table {
|
7119
|
+
text_column('Name')
|
7120
|
+
text_column('Email')
|
7121
|
+
text_column('Phone')
|
7122
|
+
text_column('City')
|
7123
|
+
text_column('State')
|
7124
|
+
|
7125
|
+
editable true
|
7126
|
+
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
|
7127
|
+
|
7128
|
+
on_changed do |row, type, row_data|
|
7129
|
+
puts "Row #{row} #{type}: #{row_data}"
|
7130
|
+
end
|
7131
|
+
}
|
7132
|
+
}
|
7133
|
+
}.show
|
7134
|
+
end
|
7135
|
+
end
|
6435
7136
|
|
7137
|
+
FormTable.new.launch
|
6436
7138
|
```
|
6437
|
-
ruby -r glimmer-dsl-libui -e "require 'examples/form_table'"
|
6438
|
-
```
|
6439
|
-
|
6440
|
-
Mac | Windows | Linux
|
6441
|
-
----|---------|------
|
6442
|
-
   |    |   
|
6443
7139
|
|
6444
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
7140
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 4 (with explicit [data-binding](#data-binding) to raw data):
|
6445
7141
|
|
6446
7142
|
```ruby
|
6447
7143
|
require 'glimmer-dsl-libui'
|
@@ -6449,15 +7145,15 @@ require 'glimmer-dsl-libui'
|
|
6449
7145
|
class FormTable
|
6450
7146
|
include Glimmer
|
6451
7147
|
|
6452
|
-
attr_accessor :name, :email, :phone, :city, :state, :filter_value
|
7148
|
+
attr_accessor :data, :name, :email, :phone, :city, :state, :filter_value
|
6453
7149
|
|
6454
7150
|
def initialize
|
6455
7151
|
@data = [
|
6456
|
-
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'
|
6457
|
-
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'
|
6458
|
-
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'
|
6459
|
-
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'
|
6460
|
-
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'
|
7152
|
+
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
|
7153
|
+
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
|
7154
|
+
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
|
7155
|
+
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
|
7156
|
+
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
|
6461
7157
|
]
|
6462
7158
|
end
|
6463
7159
|
|
@@ -6471,7 +7167,7 @@ class FormTable
|
|
6471
7167
|
|
6472
7168
|
entry {
|
6473
7169
|
label 'Name'
|
6474
|
-
text <=> [self, :name]
|
7170
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
6475
7171
|
}
|
6476
7172
|
|
6477
7173
|
entry {
|
@@ -6503,8 +7199,8 @@ class FormTable
|
|
6503
7199
|
if new_row.include?('')
|
6504
7200
|
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
6505
7201
|
else
|
6506
|
-
|
6507
|
-
@unfiltered_data =
|
7202
|
+
data << new_row # automatically inserts a row into the table due to implicit data-binding
|
7203
|
+
@unfiltered_data = data.dup
|
6508
7204
|
self.name = '' # automatically clears name entry through explicit data-binding
|
6509
7205
|
self.email = ''
|
6510
7206
|
self.phone = ''
|
@@ -6516,14 +7212,15 @@ class FormTable
|
|
6516
7212
|
|
6517
7213
|
search_entry {
|
6518
7214
|
stretchy false
|
6519
|
-
|
7215
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
7216
|
+
text <=> [self, :filter_value,
|
6520
7217
|
after_write: ->(filter_value) { # execute after write to self.filter_value
|
6521
|
-
@unfiltered_data ||=
|
7218
|
+
@unfiltered_data ||= data.dup
|
6522
7219
|
# Unfilter first to remove any previous filters
|
6523
|
-
|
7220
|
+
data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
|
6524
7221
|
# Now, apply filter if entered
|
6525
7222
|
unless filter_value.empty?
|
6526
|
-
|
7223
|
+
data.filter! do |row_data| # affects table indirectly through implicit data-binding
|
6527
7224
|
row_data.any? do |cell|
|
6528
7225
|
cell.to_s.downcase.include?(filter_value.downcase)
|
6529
7226
|
end
|
@@ -6539,8 +7236,9 @@ class FormTable
|
|
6539
7236
|
text_column('Phone')
|
6540
7237
|
text_column('City')
|
6541
7238
|
text_column('State')
|
6542
|
-
|
6543
|
-
|
7239
|
+
|
7240
|
+
editable true
|
7241
|
+
cell_rows <=> [self, :data] # explicit data-binding to raw data Array of Arrays
|
6544
7242
|
|
6545
7243
|
on_changed do |row, type, row_data|
|
6546
7244
|
puts "Row #{row} #{type}: #{row_data}"
|
@@ -6554,7 +7252,7 @@ end
|
|
6554
7252
|
FormTable.new.launch
|
6555
7253
|
```
|
6556
7254
|
|
6557
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version
|
7255
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 5 (with implicit [data-binding](#data-binding)):
|
6558
7256
|
|
6559
7257
|
```ruby
|
6560
7258
|
require 'glimmer-dsl-libui'
|
@@ -6562,11 +7260,11 @@ require 'glimmer-dsl-libui'
|
|
6562
7260
|
include Glimmer
|
6563
7261
|
|
6564
7262
|
data = [
|
6565
|
-
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'
|
6566
|
-
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'
|
6567
|
-
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'
|
6568
|
-
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'
|
6569
|
-
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'
|
7263
|
+
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
|
7264
|
+
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
|
7265
|
+
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
|
7266
|
+
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
|
7267
|
+
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
|
6570
7268
|
]
|
6571
7269
|
|
6572
7270
|
window('Contacts', 600, 600) { |w|
|
@@ -6642,7 +7340,8 @@ window('Contacts', 600, 600) { |w|
|
|
6642
7340
|
text_column('City')
|
6643
7341
|
text_column('State')
|
6644
7342
|
|
6645
|
-
|
7343
|
+
editable true
|
7344
|
+
cell_rows data # implicit data-binding to raw data Array of Arrays
|
6646
7345
|
|
6647
7346
|
on_changed do |row, type, row_data|
|
6648
7347
|
puts "Row #{row} #{type}: #{row_data}"
|
@@ -8178,7 +8877,7 @@ Mac | Windows | Linux
|
|
8178
8877
|
----|---------|------
|
8179
8878
|
  |   |  
|
8180
8879
|
|
8181
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
8880
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
8182
8881
|
|
8183
8882
|
```ruby
|
8184
8883
|
require 'glimmer-dsl-libui'
|
@@ -8195,6 +8894,7 @@ class Snake
|
|
8195
8894
|
@game = Model::Game.new
|
8196
8895
|
@grid = Presenter::Grid.new(@game)
|
8197
8896
|
@game.start
|
8897
|
+
@keypress_queue = []
|
8198
8898
|
create_gui
|
8199
8899
|
register_observers
|
8200
8900
|
end
|
@@ -8214,14 +8914,30 @@ class Snake
|
|
8214
8914
|
end
|
8215
8915
|
|
8216
8916
|
Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
|
8217
|
-
|
8917
|
+
unless @game.over?
|
8918
|
+
process_queued_keypress
|
8919
|
+
@game.snake.move
|
8920
|
+
end
|
8921
|
+
end
|
8922
|
+
end
|
8923
|
+
|
8924
|
+
def process_queued_keypress
|
8925
|
+
# key press queue ensures one turn per snake move to avoid a double-turn resulting in instant death (due to snake illogically going back against itself)
|
8926
|
+
key = @keypress_queue.shift
|
8927
|
+
case [@game.snake.head.orientation, key]
|
8928
|
+
in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
|
8929
|
+
@game.snake.turn_right
|
8930
|
+
in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
|
8931
|
+
@game.snake.turn_left
|
8932
|
+
else
|
8933
|
+
# No Op
|
8218
8934
|
end
|
8219
8935
|
end
|
8220
8936
|
|
8221
8937
|
def create_gui
|
8222
8938
|
@main_window = window {
|
8223
8939
|
# data-bind window title to game score, converting it to a title string on read from the model
|
8224
|
-
title <= [@game, :score, on_read: -> (score) {"
|
8940
|
+
title <= [@game, :score, on_read: -> (score) {"Snake (Score: #{@game.score})"}]
|
8225
8941
|
content_size @game.width * CELL_SIZE, @game.height * CELL_SIZE
|
8226
8942
|
resizable false
|
8227
8943
|
|
@@ -8239,15 +8955,109 @@ class Snake
|
|
8239
8955
|
}
|
8240
8956
|
|
8241
8957
|
on_key_up do |area_key_event|
|
8242
|
-
|
8243
|
-
|
8244
|
-
|
8245
|
-
|
8246
|
-
|
8247
|
-
|
8248
|
-
|
8249
|
-
|
8250
|
-
|
8958
|
+
@keypress_queue << area_key_event[:ext_key]
|
8959
|
+
end
|
8960
|
+
}
|
8961
|
+
end
|
8962
|
+
}
|
8963
|
+
end
|
8964
|
+
}
|
8965
|
+
}
|
8966
|
+
end
|
8967
|
+
end
|
8968
|
+
|
8969
|
+
Snake.new.launch
|
8970
|
+
```
|
8971
|
+
|
8972
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
8973
|
+
|
8974
|
+
```ruby
|
8975
|
+
require 'glimmer-dsl-libui'
|
8976
|
+
|
8977
|
+
require_relative 'snake/presenter/grid'
|
8978
|
+
|
8979
|
+
class Snake
|
8980
|
+
include Glimmer
|
8981
|
+
|
8982
|
+
CELL_SIZE = 15
|
8983
|
+
SNAKE_MOVE_DELAY = 0.1
|
8984
|
+
|
8985
|
+
def initialize
|
8986
|
+
@game = Model::Game.new
|
8987
|
+
@grid = Presenter::Grid.new(@game)
|
8988
|
+
@game.start
|
8989
|
+
@keypress_queue = []
|
8990
|
+
create_gui
|
8991
|
+
register_observers
|
8992
|
+
end
|
8993
|
+
|
8994
|
+
def launch
|
8995
|
+
@main_window.show
|
8996
|
+
end
|
8997
|
+
|
8998
|
+
def register_observers
|
8999
|
+
@game.height.times do |row|
|
9000
|
+
@game.width.times do |column|
|
9001
|
+
observe(@grid.cells[row][column], :color) do |new_color|
|
9002
|
+
@cell_grid[row][column].fill = new_color
|
9003
|
+
end
|
9004
|
+
end
|
9005
|
+
end
|
9006
|
+
|
9007
|
+
observe(@game, :over) do |game_over|
|
9008
|
+
Glimmer::LibUI.queue_main do
|
9009
|
+
if game_over
|
9010
|
+
msg_box('Game Over!', "Score: #{@game.score} | High Score: #{@game.high_score}")
|
9011
|
+
@game.start
|
9012
|
+
end
|
9013
|
+
end
|
9014
|
+
end
|
9015
|
+
|
9016
|
+
Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
|
9017
|
+
unless @game.over?
|
9018
|
+
process_queued_keypress
|
9019
|
+
@game.snake.move
|
9020
|
+
end
|
9021
|
+
end
|
9022
|
+
end
|
9023
|
+
|
9024
|
+
def process_queued_keypress
|
9025
|
+
# key press queue ensures one turn per snake move to avoid a double-turn resulting in instant death (due to snake illogically going back against itself)
|
9026
|
+
key = @keypress_queue.shift
|
9027
|
+
case [@game.snake.head.orientation, key]
|
9028
|
+
in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
|
9029
|
+
@game.snake.turn_right
|
9030
|
+
in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
|
9031
|
+
@game.snake.turn_left
|
9032
|
+
else
|
9033
|
+
# No Op
|
9034
|
+
end
|
9035
|
+
end
|
9036
|
+
|
9037
|
+
def create_gui
|
9038
|
+
@cell_grid = []
|
9039
|
+
@main_window = window {
|
9040
|
+
# data-bind window title to game score, converting it to a title string on read from the model
|
9041
|
+
title <= [@game, :score, on_read: -> (score) {"Snake (Score: #{@game.score})"}]
|
9042
|
+
content_size @game.width * CELL_SIZE, @game.height * CELL_SIZE
|
9043
|
+
resizable false
|
9044
|
+
|
9045
|
+
vertical_box {
|
9046
|
+
padded false
|
9047
|
+
|
9048
|
+
@game.height.times do |row|
|
9049
|
+
@cell_grid << []
|
9050
|
+
horizontal_box {
|
9051
|
+
padded false
|
9052
|
+
|
9053
|
+
@game.width.times do |column|
|
9054
|
+
area {
|
9055
|
+
@cell_grid.last << square(0, 0, CELL_SIZE) {
|
9056
|
+
fill Presenter::Cell::COLOR_CLEAR
|
9057
|
+
}
|
9058
|
+
|
9059
|
+
on_key_up do |area_key_event|
|
9060
|
+
@keypress_queue << area_key_event[:ext_key]
|
8251
9061
|
end
|
8252
9062
|
}
|
8253
9063
|
end
|
@@ -8676,7 +9486,7 @@ Mac | Windows | Linux
|
|
8676
9486
|
----|---------|------
|
8677
9487
|
    |     |    
|
8678
9488
|
|
8679
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
9489
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
8680
9490
|
|
8681
9491
|
```ruby
|
8682
9492
|
require 'glimmer-dsl-libui'
|
@@ -8722,6 +9532,7 @@ class TicTacToe
|
|
8722
9532
|
text(23, 19) {
|
8723
9533
|
string {
|
8724
9534
|
font family: 'Arial', size: OS.mac? ? 20 : 16
|
9535
|
+
# data-bind string property of area text attributed string to tic tac toe board cell sign
|
8725
9536
|
string <= [@tic_tac_toe_board[row + 1, column + 1], :sign] # board model is 1-based
|
8726
9537
|
}
|
8727
9538
|
}
|
@@ -8755,6 +9566,95 @@ end
|
|
8755
9566
|
TicTacToe.new.launch
|
8756
9567
|
```
|
8757
9568
|
|
9569
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
9570
|
+
|
9571
|
+
```ruby
|
9572
|
+
|
9573
|
+
require 'glimmer-dsl-libui'
|
9574
|
+
|
9575
|
+
require_relative "tic_tac_toe/board"
|
9576
|
+
|
9577
|
+
class TicTacToe
|
9578
|
+
include Glimmer
|
9579
|
+
|
9580
|
+
def initialize
|
9581
|
+
@tic_tac_toe_board = Board.new
|
9582
|
+
end
|
9583
|
+
|
9584
|
+
def launch
|
9585
|
+
create_gui
|
9586
|
+
register_observers
|
9587
|
+
@main_window.show
|
9588
|
+
end
|
9589
|
+
|
9590
|
+
def register_observers
|
9591
|
+
observe(@tic_tac_toe_board, :game_status) do |game_status|
|
9592
|
+
display_win_message if game_status == Board::WIN
|
9593
|
+
display_draw_message if game_status == Board::DRAW
|
9594
|
+
end
|
9595
|
+
|
9596
|
+
3.times.map do |row|
|
9597
|
+
3.times.map do |column|
|
9598
|
+
observe(@tic_tac_toe_board[row + 1, column + 1], :sign) do |sign| # board model is 1-based
|
9599
|
+
@cells[row][column].string = sign
|
9600
|
+
end
|
9601
|
+
end
|
9602
|
+
end
|
9603
|
+
end
|
9604
|
+
|
9605
|
+
def create_gui
|
9606
|
+
@main_window = window('Tic-Tac-Toe', 180, 180) {
|
9607
|
+
resizable false
|
9608
|
+
|
9609
|
+
@cells = []
|
9610
|
+
vertical_box {
|
9611
|
+
padded false
|
9612
|
+
|
9613
|
+
3.times.map do |row|
|
9614
|
+
@cells << []
|
9615
|
+
horizontal_box {
|
9616
|
+
padded false
|
9617
|
+
|
9618
|
+
3.times.map do |column|
|
9619
|
+
area {
|
9620
|
+
square(0, 0, 60) {
|
9621
|
+
stroke :black, thickness: 2
|
9622
|
+
}
|
9623
|
+
text(23, 19) {
|
9624
|
+
@cells[row] << string('') {
|
9625
|
+
font family: 'Arial', size: OS.mac? ? 20 : 16
|
9626
|
+
}
|
9627
|
+
}
|
9628
|
+
on_mouse_up do
|
9629
|
+
@tic_tac_toe_board.mark(row + 1, column + 1) # board model is 1-based
|
9630
|
+
end
|
9631
|
+
}
|
9632
|
+
end
|
9633
|
+
}
|
9634
|
+
end
|
9635
|
+
}
|
9636
|
+
}
|
9637
|
+
end
|
9638
|
+
|
9639
|
+
def display_win_message
|
9640
|
+
display_game_over_message("Player #{@tic_tac_toe_board.winning_sign} has won!")
|
9641
|
+
end
|
9642
|
+
|
9643
|
+
def display_draw_message
|
9644
|
+
display_game_over_message("Draw!")
|
9645
|
+
end
|
9646
|
+
|
9647
|
+
def display_game_over_message(message_text)
|
9648
|
+
Glimmer::LibUI.queue_main do
|
9649
|
+
msg_box('Game Over', message_text)
|
9650
|
+
@tic_tac_toe_board.reset!
|
9651
|
+
end
|
9652
|
+
end
|
9653
|
+
end
|
9654
|
+
|
9655
|
+
TicTacToe.new.launch
|
9656
|
+
```
|
9657
|
+
|
8758
9658
|
#### Timer
|
8759
9659
|
|
8760
9660
|
To run this example, install [TiMidity](http://timidity.sourceforge.net) and ensure `timidity` command is in `PATH` (can be installed via [Homebrew](https://brew.sh) on Mac or [apt-get](https://help.ubuntu.com/community/AptGet/Howto) on Linux).
|
@@ -9115,7 +10015,7 @@ These features have been planned or suggested. You might see them in a future ve
|
|
9115
10015
|
is fine, but please isolate to its own commit so I can cherry-pick
|
9116
10016
|
around it.
|
9117
10017
|
|
9118
|
-
Note that the latest development sometimes takes place in [development](https://github.com/AndyObtiva/glimmer-dsl-libui/tree/development) branch (
|
10018
|
+
Note that the latest development sometimes takes place in the [development](https://github.com/AndyObtiva/glimmer-dsl-libui/tree/development) branch (usually deleted once merged back to [master](https://github.com/AndyObtiva/glimmer-dsl-libui)).
|
9119
10019
|
|
9120
10020
|
## Contributors
|
9121
10021
|
|