glimmer-dsl-libui 0.4.7 → 0.4.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/README.md +1043 -493
- data/VERSION +1 -1
- data/examples/basic_table_button.rb +54 -30
- data/examples/basic_table_button2.rb +34 -0
- data/examples/basic_table_color.rb +1 -1
- data/examples/button_counter.rb +2 -1
- 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 +21 -8
- data/examples/midi_player.rb +1 -1
- data/examples/snake.rb +19 -10
- data/examples/snake2.rb +97 -0
- data/examples/tetris.rb +15 -18
- 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 +4 -0
- data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +1 -2
- 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 +4 -5
- 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 +4 -0
- data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +16 -4
- data/lib/glimmer/libui/control_proxy/multiline_entry_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +19 -0
- 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 +88 -24
- data/lib/glimmer/libui/control_proxy.rb +6 -4
- data/lib/glimmer/libui/data_bindable.rb +34 -4
- data/lib/glimmer/libui/shape.rb +3 -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.11
|
2
2
|
## Prerequisite-Free Ruby Desktop Development GUI Library
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/glimmer-dsl-libui.svg)](http://badge.fury.io/rb/glimmer-dsl-libui)
|
4
4
|
[![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
@@ -77,6 +77,127 @@ Mac | Windows | Linux
|
|
77
77
|
----|---------|------
|
78
78
|
![glimmer-dsl-libui-mac-basic-table-progress-bar.png](images/glimmer-dsl-libui-mac-basic-table-progress-bar.png) | ![glimmer-dsl-libui-windows-basic-table-progress-bar.png](images/glimmer-dsl-libui-windows-basic-table-progress-bar.png) | ![glimmer-dsl-libui-linux-basic-table-progress-bar.png](images/glimmer-dsl-libui-linux-basic-table-progress-bar.png)
|
79
79
|
|
80
|
+
Form Table
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
require 'glimmer-dsl-libui'
|
84
|
+
|
85
|
+
class FormTable
|
86
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
87
|
+
|
88
|
+
include Glimmer
|
89
|
+
|
90
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
91
|
+
|
92
|
+
def initialize
|
93
|
+
@contacts = [
|
94
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
95
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
96
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
97
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
98
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
99
|
+
]
|
100
|
+
end
|
101
|
+
|
102
|
+
def launch
|
103
|
+
window('Contacts', 600, 600) { |w|
|
104
|
+
margined true
|
105
|
+
|
106
|
+
vertical_box {
|
107
|
+
form {
|
108
|
+
stretchy false
|
109
|
+
|
110
|
+
entry {
|
111
|
+
label 'Name'
|
112
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
113
|
+
}
|
114
|
+
|
115
|
+
entry {
|
116
|
+
label 'Email'
|
117
|
+
text <=> [self, :email]
|
118
|
+
}
|
119
|
+
|
120
|
+
entry {
|
121
|
+
label 'Phone'
|
122
|
+
text <=> [self, :phone]
|
123
|
+
}
|
124
|
+
|
125
|
+
entry {
|
126
|
+
label 'City'
|
127
|
+
text <=> [self, :city]
|
128
|
+
}
|
129
|
+
|
130
|
+
entry {
|
131
|
+
label 'State'
|
132
|
+
text <=> [self, :state]
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
button('Save Contact') {
|
137
|
+
stretchy false
|
138
|
+
|
139
|
+
on_clicked do
|
140
|
+
new_row = [name, email, phone, city, state]
|
141
|
+
if new_row.include?('')
|
142
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
143
|
+
else
|
144
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to explicit data-binding
|
145
|
+
@unfiltered_contacts = @contacts.dup
|
146
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
147
|
+
self.email = ''
|
148
|
+
self.phone = ''
|
149
|
+
self.city = ''
|
150
|
+
self.state = ''
|
151
|
+
end
|
152
|
+
end
|
153
|
+
}
|
154
|
+
|
155
|
+
search_entry {
|
156
|
+
stretchy false
|
157
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
158
|
+
text <=> [self, :filter_value,
|
159
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
160
|
+
@unfiltered_contacts ||= @contacts.dup
|
161
|
+
# Unfilter first to remove any previous filters
|
162
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
163
|
+
# Now, apply filter if entered
|
164
|
+
unless filter_value.empty?
|
165
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
166
|
+
contact.members.any? do |attribute|
|
167
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
}
|
172
|
+
]
|
173
|
+
}
|
174
|
+
|
175
|
+
table {
|
176
|
+
text_column('Name')
|
177
|
+
text_column('Email')
|
178
|
+
text_column('Phone')
|
179
|
+
text_column('City')
|
180
|
+
text_column('State')
|
181
|
+
|
182
|
+
editable true
|
183
|
+
cell_rows <=> [self, :contacts] # explicit data-binding to Model Array
|
184
|
+
|
185
|
+
on_changed do |row, type, row_data|
|
186
|
+
puts "Row #{row} #{type}: #{row_data}"
|
187
|
+
end
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}.show
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
FormTable.new.launch
|
195
|
+
```
|
196
|
+
|
197
|
+
Mac | Windows | Linux
|
198
|
+
----|---------|------
|
199
|
+
![glimmer-dsl-libui-mac-form-table.png](images/glimmer-dsl-libui-mac-form-table.png) | ![glimmer-dsl-libui-windows-form-table.png](images/glimmer-dsl-libui-windows-form-table.png) | ![glimmer-dsl-libui-linux-form-table.png](images/glimmer-dsl-libui-linux-form-table.png)
|
200
|
+
|
80
201
|
Area Gallery
|
81
202
|
|
82
203
|
```ruby
|
@@ -267,6 +388,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
|
|
267
388
|
- [Button Counter](#button-counter)
|
268
389
|
- [Color The Circles](#color-the-circles)
|
269
390
|
- [Control Gallery](#control-gallery)
|
391
|
+
- [CPU Percentage](#cpu-percentage)
|
270
392
|
- [Custom Draw Text](#custom-draw-text)
|
271
393
|
- [Dynamic Area](#dynamic-area)
|
272
394
|
- [Editable Column Table](#editable-column-table)
|
@@ -373,10 +495,20 @@ gem install glimmer-dsl-libui
|
|
373
495
|
Or install via Bundler `Gemfile`:
|
374
496
|
|
375
497
|
```ruby
|
376
|
-
gem 'glimmer-dsl-libui', '~> 0.4.
|
498
|
+
gem 'glimmer-dsl-libui', '~> 0.4.11'
|
499
|
+
```
|
500
|
+
|
501
|
+
Test that installation worked by running the [Meta-Example](#examples):
|
502
|
+
|
503
|
+
```
|
504
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/meta_example'"
|
377
505
|
```
|
378
506
|
|
379
|
-
|
507
|
+
Mac | Windows | Linux
|
508
|
+
----|---------|------
|
509
|
+
![glimmer-dsl-libui-mac-meta-example.png](images/glimmer-dsl-libui-mac-meta-example.png) | ![glimmer-dsl-libui-windows-meta-example.png](images/glimmer-dsl-libui-windows-meta-example.png) | ![glimmer-dsl-libui-linux-meta-example.png](images/glimmer-dsl-libui-linux-meta-example.png)
|
510
|
+
|
511
|
+
Now to use [glimmer-dsl-libui](https://rubygems.org/gems/glimmer-dsl-libui), add `require 'glimmer-dsl-libui'` at the top, and then `include Glimmer` into the top-level main object for testing or into an actual class for serious usage.
|
380
512
|
|
381
513
|
Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
382
514
|
|
@@ -451,7 +583,7 @@ Keyword(Args) | Properties | Listeners
|
|
451
583
|
`about_menu_item` | None | `on_clicked`
|
452
584
|
`area` | `auto_draw_enabled` | `on_draw(area_draw_params)`, `on_mouse_event(area_mouse_event)`, `on_mouse_down(area_mouse_event)`, `on_mouse_up(area_mouse_event)`, `on_mouse_drag_started(area_mouse_event)`, `on_mouse_dragged(area_mouse_event)`, `on_mouse_dropped(area_mouse_event)`, `on_mouse_entered`, `on_mouse_exited`, `on_key_event(area_key_event)`, `on_key_down(area_key_event)`, `on_key_up(area_key_event)`
|
453
585
|
`arc(x_center as Numeric, y_center as Numeric, radius as Numeric, start_angle as Numeric, sweep as Numeric, is_negative as Boolean)` | `x_center` (`Numeric`), `y_center` (`Numeric`), `radius` (`Numeric`), `start_angle` (`Numeric`), `sweep` (`Numeric`), `is_negative` (Boolean) | None
|
454
|
-
`background_color_column
|
586
|
+
`background_color_column` | None | None
|
455
587
|
`bezier(c1_x as Numeric, c1_y as Numeric, c2_x as Numeric, c2_y as Numeric, end_x as Numeric, end_y as Numeric)` | `c1_x` (`Numeric`), `c1_y` (`Numeric`), `c2_x` (`Numeric`), `c2_y` (`Numeric`), `end_x` (`Numeric`), `end_y` (`Numeric`) | None
|
456
588
|
`button(text as String)` | `text` (`String`) | `on_clicked`
|
457
589
|
`button_column(name as String)` | `enabled` (Boolean) | None
|
@@ -615,154 +747,142 @@ Note that the `cell_rows` property declaration results in "implicit data-binding
|
|
615
747
|
- Inserting cell rows: Calling `Array#<<`, `Array#push`, `Array#prepend`, or any insertion/addition `Array` method automatically inserts rows in actual `table` control
|
616
748
|
- Changing cell rows: Calling `Array#[]=`, `Array#map!`, or any update `Array` method automatically updates rows in actual `table` control
|
617
749
|
|
618
|
-
|
619
|
-
|
620
|
-
```ruby
|
621
|
-
require 'glimmer-dsl-libui'
|
622
|
-
|
623
|
-
class FormTable
|
624
|
-
include Glimmer
|
625
|
-
|
626
|
-
attr_accessor :name, :email, :phone, :city, :state, :filter_value
|
627
|
-
|
628
|
-
def initialize
|
629
|
-
@data = [
|
630
|
-
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO', '80014'],
|
631
|
-
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA', '02101'],
|
632
|
-
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL', '60007'],
|
633
|
-
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA', '98101'],
|
634
|
-
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA', '90001'],
|
635
|
-
]
|
636
|
-
end
|
637
|
-
|
638
|
-
def launch
|
639
|
-
window('Contacts', 600, 600) { |w|
|
640
|
-
margined true
|
641
|
-
|
642
|
-
vertical_box {
|
643
|
-
form {
|
644
|
-
stretchy false
|
645
|
-
|
646
|
-
entry {
|
647
|
-
label 'Name'
|
648
|
-
text <=> [self, :name]
|
649
|
-
}
|
650
|
-
|
651
|
-
entry {
|
652
|
-
label 'Email'
|
653
|
-
text <=> [self, :email]
|
654
|
-
}
|
655
|
-
|
656
|
-
entry {
|
657
|
-
label 'Phone'
|
658
|
-
text <=> [self, :phone]
|
659
|
-
}
|
660
|
-
|
661
|
-
entry {
|
662
|
-
label 'City'
|
663
|
-
text <=> [self, :city]
|
664
|
-
}
|
665
|
-
|
666
|
-
entry {
|
667
|
-
label 'State'
|
668
|
-
text <=> [self, :state]
|
669
|
-
}
|
670
|
-
}
|
671
|
-
|
672
|
-
button('Save Contact') {
|
673
|
-
stretchy false
|
674
|
-
|
675
|
-
on_clicked do
|
676
|
-
new_row = [name, email, phone, city, state]
|
677
|
-
if new_row.include?('')
|
678
|
-
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
679
|
-
else
|
680
|
-
@data << new_row # automatically inserts a row into the table due to implicit data-binding
|
681
|
-
@unfiltered_data = @data.dup
|
682
|
-
self.name = '' # automatically clears name entry through explicit data-binding
|
683
|
-
self.email = ''
|
684
|
-
self.phone = ''
|
685
|
-
self.city = ''
|
686
|
-
self.state = ''
|
687
|
-
end
|
688
|
-
end
|
689
|
-
}
|
690
|
-
|
691
|
-
search_entry {
|
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')
|
716
|
-
|
717
|
-
cell_rows @data # implicit data-binding
|
718
|
-
|
719
|
-
on_changed do |row, type, row_data|
|
720
|
-
puts "Row #{row} #{type}: #{row_data}"
|
721
|
-
end
|
722
|
-
}
|
723
|
-
}
|
724
|
-
}.show
|
725
|
-
end
|
726
|
-
end
|
727
|
-
|
728
|
-
FormTable.new.launch
|
729
|
-
```
|
730
|
-
|
731
|
-
![glimmer-dsl-libui-linux-form-table.png](images/glimmer-dsl-libui-linux-form-table.png)
|
732
|
-
|
733
|
-
Learn more by checking out [examples](#examples).
|
734
|
-
|
735
|
-
### Area API
|
736
|
-
|
737
|
-
The `area` control is a canvas-like control for drawing paths that can be used in one of two ways:
|
738
|
-
- 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).
|
739
|
-
- 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.
|
740
|
-
|
741
|
-
Note that when nesting an `area` directly underneath `window` (without a layout control like `vertical_box`), it is automatically reparented with `vertical_box` in between the `window` and `area` since it would not show up on Linux otherwise.
|
750
|
+
([explicit data-binding](#data-binding) supports everything available with implicit data-binding too)
|
742
751
|
|
743
|
-
|
752
|
+
Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
744
753
|
|
745
754
|
```ruby
|
746
755
|
require 'glimmer-dsl-libui'
|
747
756
|
|
748
757
|
include Glimmer
|
749
758
|
|
750
|
-
|
759
|
+
data = [
|
760
|
+
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
|
761
|
+
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
|
762
|
+
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
|
763
|
+
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
|
764
|
+
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
|
765
|
+
]
|
766
|
+
|
767
|
+
window('Contacts', 600, 600) { |w|
|
751
768
|
margined true
|
752
769
|
|
753
770
|
vertical_box {
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
771
|
+
form {
|
772
|
+
stretchy false
|
773
|
+
|
774
|
+
@name_entry = entry {
|
775
|
+
label 'Name'
|
776
|
+
}
|
777
|
+
|
778
|
+
@email_entry = entry {
|
779
|
+
label 'Email'
|
780
|
+
}
|
781
|
+
|
782
|
+
@phone_entry = entry {
|
783
|
+
label 'Phone'
|
784
|
+
}
|
785
|
+
|
786
|
+
@city_entry = entry {
|
787
|
+
label 'City'
|
788
|
+
}
|
789
|
+
|
790
|
+
@state_entry = entry {
|
791
|
+
label 'State'
|
759
792
|
}
|
760
793
|
}
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
794
|
+
|
795
|
+
button('Save Contact') {
|
796
|
+
stretchy false
|
797
|
+
|
798
|
+
on_clicked do
|
799
|
+
new_row = [@name_entry.text, @email_entry.text, @phone_entry.text, @city_entry.text, @state_entry.text]
|
800
|
+
if new_row.include?('')
|
801
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
802
|
+
else
|
803
|
+
data << new_row # automatically inserts a row into the table due to implicit data-binding
|
804
|
+
@unfiltered_data = data.dup
|
805
|
+
@name_entry.text = ''
|
806
|
+
@email_entry.text = ''
|
807
|
+
@phone_entry.text = ''
|
808
|
+
@city_entry.text = ''
|
809
|
+
@state_entry.text = ''
|
810
|
+
end
|
811
|
+
end
|
812
|
+
}
|
813
|
+
|
814
|
+
search_entry { |se|
|
815
|
+
stretchy false
|
816
|
+
|
817
|
+
on_changed do
|
818
|
+
filter_value = se.text
|
819
|
+
@unfiltered_data ||= data.dup
|
820
|
+
# Unfilter first to remove any previous filters
|
821
|
+
data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
|
822
|
+
# Now, apply filter if entered
|
823
|
+
unless filter_value.empty?
|
824
|
+
data.filter! do |row_data| # affects table indirectly through implicit data-binding
|
825
|
+
row_data.any? do |cell|
|
826
|
+
cell.to_s.downcase.include?(filter_value.downcase)
|
827
|
+
end
|
828
|
+
end
|
829
|
+
end
|
830
|
+
end
|
831
|
+
}
|
832
|
+
|
833
|
+
table {
|
834
|
+
text_column('Name')
|
835
|
+
text_column('Email')
|
836
|
+
text_column('Phone')
|
837
|
+
text_column('City')
|
838
|
+
text_column('State')
|
839
|
+
|
840
|
+
editable true
|
841
|
+
cell_rows data # implicit data-binding to raw data Array of Arrays
|
842
|
+
|
843
|
+
on_changed do |row, type, row_data|
|
844
|
+
puts "Row #{row} #{type}: #{row_data}"
|
845
|
+
end
|
846
|
+
}
|
847
|
+
}
|
848
|
+
}.show
|
849
|
+
```
|
850
|
+
|
851
|
+
![glimmer-dsl-libui-linux-form-table.png](images/glimmer-dsl-libui-linux-form-table.png)
|
852
|
+
|
853
|
+
Learn more by checking out [examples](#examples).
|
854
|
+
|
855
|
+
### Area API
|
856
|
+
|
857
|
+
The `area` control is a canvas-like control for drawing paths that can be used in one of two ways:
|
858
|
+
- Declaratively via stable paths: useful for stable paths that will not change often later on. Simply nest `path` and figures like `rectangle` and all drawing logic is generated automatically. Path proxy objects are preserved across redraws assuming there would be relatively few stable paths (mostly for decorative reasons).
|
859
|
+
- Semi-declaratively via on_draw listener dynamic paths: useful for more dynamic paths that will definitely change very often. Open an `on_draw` listener block that receives an [`area_draw_params`](#area-draw-params) argument and nest `path` and figures like `rectangle` and all drawing logic is generated automatically. Path proxy objects are destroyed (thrown-away) at the end of drawing, thus having less memory overhead for drawing thousands of dynamic paths.
|
860
|
+
|
861
|
+
Note that when nesting an `area` directly underneath `window` (without a layout control like `vertical_box`), it is automatically reparented with `vertical_box` in between the `window` and `area` since it would not show up on Linux otherwise.
|
862
|
+
|
863
|
+
Here is an example of a declarative `area` with a stable path (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
864
|
+
|
865
|
+
```ruby
|
866
|
+
require 'glimmer-dsl-libui'
|
867
|
+
|
868
|
+
include Glimmer
|
869
|
+
|
870
|
+
window('Basic Area', 400, 400) {
|
871
|
+
margined true
|
872
|
+
|
873
|
+
vertical_box {
|
874
|
+
area {
|
875
|
+
path { # a stable path is added declaratively
|
876
|
+
rectangle(0, 0, 400, 400)
|
877
|
+
|
878
|
+
fill r: 102, g: 102, b: 204, a: 1.0
|
879
|
+
}
|
880
|
+
}
|
881
|
+
}
|
882
|
+
}.show
|
883
|
+
```
|
884
|
+
|
885
|
+
Mac | Windows | Linux
|
766
886
|
----|---------|------
|
767
887
|
![glimmer-dsl-libui-mac-basic-area.png](images/glimmer-dsl-libui-mac-basic-area.png) | ![glimmer-dsl-libui-windows-basic-area.png](images/glimmer-dsl-libui-windows-basic-area.png) | ![glimmer-dsl-libui-linux-basic-area.png](images/glimmer-dsl-libui-linux-basic-area.png)
|
768
888
|
|
@@ -798,9 +918,9 @@ Check [examples/dynamic_area.rb](#dynamic-area) for a more detailed semi-declara
|
|
798
918
|
- `scroll_to(x as Numeric, y as Numeric, width as Numeric = main_window.width, height as Numeric = main_window.height)`: scrolls to `x`/`y` location with `width` and `height` viewport size.
|
799
919
|
- `set_size(width as Numeric, height as Numeric)`: set size of scrolling area, which must must exceed that of visible viewport in order for scrolling to be enabled.
|
800
920
|
|
801
|
-
Mac |Linux
|
802
|
-
|
803
|
-
![glimmer-dsl-libui-mac-dynamic-area.png](images/glimmer-dsl-libui-mac-basic-scrolling-area.png) ![glimmer-dsl-libui-mac-dynamic-area-updated.png](images/glimmer-dsl-libui-mac-basic-scrolling-area-scrolled.png) | ![glimmer-dsl-libui-linux-dynamic-area.png](images/glimmer-dsl-libui-linux-basic-scrolling-area.png) ![glimmer-dsl-libui-linux-dynamic-area-updated.png](images/glimmer-dsl-libui-linux-basic-scrolling-area-scrolled.png)
|
921
|
+
Mac | Windows | Linux
|
922
|
+
----|---------|------
|
923
|
+
![glimmer-dsl-libui-mac-dynamic-area.png](images/glimmer-dsl-libui-mac-basic-scrolling-area.png) ![glimmer-dsl-libui-mac-dynamic-area-updated.png](images/glimmer-dsl-libui-mac-basic-scrolling-area-scrolled.png) | ![glimmer-dsl-libui-windows-dynamic-area.png](images/glimmer-dsl-libui-windows-basic-scrolling-area.png) ![glimmer-dsl-libui-windows-dynamic-area-updated.png](images/glimmer-dsl-libui-windows-basic-scrolling-area-scrolled.png) | ![glimmer-dsl-libui-linux-dynamic-area.png](images/glimmer-dsl-libui-linux-basic-scrolling-area.png) ![glimmer-dsl-libui-linux-dynamic-area-updated.png](images/glimmer-dsl-libui-linux-basic-scrolling-area-scrolled.png)
|
804
924
|
|
805
925
|
Check [examples/basic_scrolling_area.rb](#basic-scrolling-area) for a more detailed example.
|
806
926
|
|
@@ -1216,6 +1336,7 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
|
|
1216
1336
|
- When destroying a control nested under a `horizontal_box` or `vertical_box`, it is automatically deleted from the box's children
|
1217
1337
|
- When destroying a control nested under a `form`, it is automatically deleted from the form's children
|
1218
1338
|
- When destroying a control nested under a `window` or `group`, it is automatically unset as their child to allow successful destruction
|
1339
|
+
- When destroying a control that has a data-binding to a model attribute, the data-binding observer registration is automatically deregistered
|
1219
1340
|
- For `date_time_picker`, `date_picker`, and `time_picker`, make sure `time` hash values for `mon`, `wday`, and `yday` are 1-based instead of [libui](https://github.com/andlabs/libui) original 0-based values, and return `dst` as Boolean instead of `isdst` as `1`/`0`
|
1220
1341
|
- Smart defaults for `grid` child properties are `left` (`0`), `top` (`0`), `xspan` (`1`), `yspan` (`1`), `hexpand` (`false`), `halign` (`:fill`), `vexpand` (`false`), and `valign` (`:fill`)
|
1221
1342
|
- The `table` control automatically constructs required `TableModelHandler`, `TableModel`, and `TableParams`, calculating all their arguments from `cell_rows` and `editable` properties (e.g. `NumRows`) as well as nested columns (e.g. `text_column`)
|
@@ -1382,6 +1503,8 @@ Data-binding supports utilizing the [MVP (Model View Presenter)](https://en.wiki
|
|
1382
1503
|
![MVP](https://www.researchgate.net/profile/Gilles-Perrouin/publication/320249584/figure/fig8/AS:668260987068418@1536337243385/Model-view-presenter-architecture.png)
|
1383
1504
|
|
1384
1505
|
[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):
|
1506
|
+
- `checkbox`: `checked`
|
1507
|
+
- `check_menu_item`: `checked`
|
1385
1508
|
- `color_button`: `color`
|
1386
1509
|
- `combobox`: `selected`, `selected_item`
|
1387
1510
|
- `date_picker`: `time`
|
@@ -1391,9 +1514,12 @@ Data-binding supports utilizing the [MVP (Model View Presenter)](https://en.wiki
|
|
1391
1514
|
- `font_button`: `font`
|
1392
1515
|
- `multiline_entry`: `text`
|
1393
1516
|
- `non_wrapping_multiline_entry`: `text`
|
1517
|
+
- `radio_buttons`: `selected`
|
1518
|
+
- `radio_menu_item`: `checked`
|
1394
1519
|
- `search_entry`: `text`
|
1395
1520
|
- `slider`: `value`
|
1396
1521
|
- `spinbox`: `value`
|
1522
|
+
- `table`: `cell_rows` (explicit data-binding by using `<=>` and [implicit data-binding](#table-api) by assigning value directly)
|
1397
1523
|
- `time_picker`: `time`
|
1398
1524
|
|
1399
1525
|
Example of bidirectional data-binding:
|
@@ -1485,272 +1611,12 @@ Learn more from data-binding usage in [Login](#login) (4 data-binding versions),
|
|
1485
1611
|
- `table` `progress_bar` column on Windows cannot be updated with a positive value if it started initially with `-1` (it ignores update to avoid crashing due to an issue in [libui](https://github.com/andlabs/libui) on Windows.
|
1486
1612
|
- It seems that [libui](https://github.com/andlabs/libui) does not support nesting multiple `area` controls under a `grid` as only the first one shows up in that scenario. To workaround that limitation, use a `vertical_box` with nested `horizontal_box`s instead to include multiple `area`s in a GUI.
|
1487
1613
|
- As per the code of [examples/basic_transform.rb](#basic-transform), Windows requires different ordering of transforms than Mac and Linux.
|
1614
|
+
- `scrolling_area#scroll_to` does not seem to work on Windows and Linux, but works fine on Mac
|
1488
1615
|
|
1489
1616
|
### Original API
|
1490
1617
|
|
1491
1618
|
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):
|
1492
|
-
|
1493
|
-
- `area_begin_user_window_move`
|
1494
|
-
- `area_begin_user_window_resize`
|
1495
|
-
- `area_queue_redraw_all`
|
1496
|
-
- `area_scroll_to`
|
1497
|
-
- `area_set_size`
|
1498
|
-
- `attribute_color`
|
1499
|
-
- `attribute_family`
|
1500
|
-
- `attribute_features`
|
1501
|
-
- `attribute_get_type`
|
1502
|
-
- `attribute_italic`
|
1503
|
-
- `attribute_size`
|
1504
|
-
- `attribute_stretch`
|
1505
|
-
- `attribute_underline`
|
1506
|
-
- `attribute_underline_color`
|
1507
|
-
- `attribute_weight`
|
1508
|
-
- `attributed_string_append_unattributed`
|
1509
|
-
- `attributed_string_byte_index_to_grapheme`
|
1510
|
-
- `attributed_string_delete`
|
1511
|
-
- `attributed_string_for_each_attribute`
|
1512
|
-
- `attributed_string_grapheme_to_byte_index`
|
1513
|
-
- `attributed_string_insert_at_unattributed`
|
1514
|
-
- `attributed_string_len`
|
1515
|
-
- `attributed_string_num_graphemes`
|
1516
|
-
- `attributed_string_set_attribute`
|
1517
|
-
- `attributed_string_string`
|
1518
|
-
- `box_append`
|
1519
|
-
- `box_delete`
|
1520
|
-
- `box_padded`
|
1521
|
-
- `box_set_padded`
|
1522
|
-
- `button_on_clicked`
|
1523
|
-
- `button_set_text`
|
1524
|
-
- `button_text`
|
1525
|
-
- `checkbox_checked`
|
1526
|
-
- `checkbox_on_toggled`
|
1527
|
-
- `checkbox_set_checked`
|
1528
|
-
- `checkbox_set_text`
|
1529
|
-
- `checkbox_text`
|
1530
|
-
- `color_button_color`
|
1531
|
-
- `color_button_on_changed`
|
1532
|
-
- `color_button_set_color`
|
1533
|
-
- `combobox_append`
|
1534
|
-
- `combobox_on_selected`
|
1535
|
-
- `combobox_selected`
|
1536
|
-
- `combobox_set_selected`
|
1537
|
-
- `control_destroy`
|
1538
|
-
- `control_disable`
|
1539
|
-
- `control_enable`
|
1540
|
-
- `control_enabled`
|
1541
|
-
- `control_enabled_to_user`
|
1542
|
-
- `control_handle`
|
1543
|
-
- `control_hide`
|
1544
|
-
- `control_parent`
|
1545
|
-
- `control_set_parent`
|
1546
|
-
- `control_show`
|
1547
|
-
- `control_toplevel`
|
1548
|
-
- `control_verify_set_parent`
|
1549
|
-
- `control_visible`
|
1550
|
-
- `date_time_picker_on_changed`
|
1551
|
-
- `date_time_picker_set_time`
|
1552
|
-
- `date_time_picker_time`
|
1553
|
-
- `draw_clip`
|
1554
|
-
- `draw_fill`
|
1555
|
-
- `draw_free_path`
|
1556
|
-
- `draw_free_text_layout`
|
1557
|
-
- `draw_matrix_invert`
|
1558
|
-
- `draw_matrix_invertible`
|
1559
|
-
- `draw_matrix_multiply`
|
1560
|
-
- `draw_matrix_rotate`
|
1561
|
-
- `draw_matrix_scale`
|
1562
|
-
- `draw_matrix_set_identity`
|
1563
|
-
- `draw_matrix_skew`
|
1564
|
-
- `draw_matrix_transform_point`
|
1565
|
-
- `draw_matrix_transform_size`
|
1566
|
-
- `draw_matrix_translate`
|
1567
|
-
- `draw_new_path`
|
1568
|
-
- `draw_new_text_layout`
|
1569
|
-
- `draw_path_add_rectangle`
|
1570
|
-
- `draw_path_arc_to`
|
1571
|
-
- `draw_path_bezier_to`
|
1572
|
-
- `draw_path_close_figure`
|
1573
|
-
- `draw_path_end`
|
1574
|
-
- `draw_path_line_to`
|
1575
|
-
- `draw_path_new_figure`
|
1576
|
-
- `draw_path_new_figure_with_arc`
|
1577
|
-
- `draw_restore`
|
1578
|
-
- `draw_save`
|
1579
|
-
- `draw_stroke`
|
1580
|
-
- `draw_text`
|
1581
|
-
- `draw_text_layout_extents`
|
1582
|
-
- `draw_transform`
|
1583
|
-
- `editable_combobox_append`
|
1584
|
-
- `editable_combobox_on_changed`
|
1585
|
-
- `editable_combobox_set_text`
|
1586
|
-
- `editable_combobox_text`
|
1587
|
-
- `entry_on_changed`
|
1588
|
-
- `entry_read_only`
|
1589
|
-
- `entry_set_read_only`
|
1590
|
-
- `entry_set_text`
|
1591
|
-
- `entry_text`
|
1592
|
-
- `ffi_lib`
|
1593
|
-
- `ffi_lib=`
|
1594
|
-
- `font_button_font`
|
1595
|
-
- `font_button_on_changed`
|
1596
|
-
- `form_append`
|
1597
|
-
- `form_delete`
|
1598
|
-
- `form_padded`
|
1599
|
-
- `form_set_padded`
|
1600
|
-
- `free_attribute`
|
1601
|
-
- `free_attributed_string`
|
1602
|
-
- `free_control`
|
1603
|
-
- `free_font_button_font`
|
1604
|
-
- `free_image`
|
1605
|
-
- `free_init_error`
|
1606
|
-
- `free_open_type_features`
|
1607
|
-
- `free_table_model`
|
1608
|
-
- `free_table_value`
|
1609
|
-
- `free_text`
|
1610
|
-
- `grid_append`
|
1611
|
-
- `grid_insert_at`
|
1612
|
-
- `grid_padded`
|
1613
|
-
- `grid_set_padded`
|
1614
|
-
- `group_margined`
|
1615
|
-
- `group_set_child`
|
1616
|
-
- `group_set_margined`
|
1617
|
-
- `group_set_title`
|
1618
|
-
- `group_title`
|
1619
|
-
- `image_append`
|
1620
|
-
- `init`
|
1621
|
-
- `label_set_text`
|
1622
|
-
- `label_text`
|
1623
|
-
- `main`
|
1624
|
-
- `main_step`
|
1625
|
-
- `main_steps`
|
1626
|
-
- `menu_append_about_item`
|
1627
|
-
- `menu_append_check_item`
|
1628
|
-
- `menu_append_item`
|
1629
|
-
- `menu_append_preferences_item`
|
1630
|
-
- `menu_append_quit_item`
|
1631
|
-
- `menu_append_separator`
|
1632
|
-
- `menu_item_checked`
|
1633
|
-
- `menu_item_disable`
|
1634
|
-
- `menu_item_enable`
|
1635
|
-
- `menu_item_on_clicked`
|
1636
|
-
- `menu_item_set_checked`
|
1637
|
-
- `msg_box`
|
1638
|
-
- `msg_box_error`
|
1639
|
-
- `multiline_entry_append`
|
1640
|
-
- `multiline_entry_on_changed`
|
1641
|
-
- `multiline_entry_read_only`
|
1642
|
-
- `multiline_entry_set_read_only`
|
1643
|
-
- `multiline_entry_set_text`
|
1644
|
-
- `multiline_entry_text`
|
1645
|
-
- `new_area`
|
1646
|
-
- `new_attributed_string`
|
1647
|
-
- `new_background_attribute`
|
1648
|
-
- `new_button`
|
1649
|
-
- `new_checkbox`
|
1650
|
-
- `new_color_attribute`
|
1651
|
-
- `new_color_button`
|
1652
|
-
- `new_combobox`
|
1653
|
-
- `new_date_picker`
|
1654
|
-
- `new_date_time_picker`
|
1655
|
-
- `new_editable_combobox`
|
1656
|
-
- `new_entry`
|
1657
|
-
- `new_family_attribute`
|
1658
|
-
- `new_features_attribute`
|
1659
|
-
- `new_font_button`
|
1660
|
-
- `new_form`
|
1661
|
-
- `new_grid`
|
1662
|
-
- `new_group`
|
1663
|
-
- `new_horizontal_box`
|
1664
|
-
- `new_horizontal_separator`
|
1665
|
-
- `new_image`
|
1666
|
-
- `new_italic_attribute`
|
1667
|
-
- `new_label`
|
1668
|
-
- `new_menu`
|
1669
|
-
- `new_multiline_entry`
|
1670
|
-
- `new_non_wrapping_multiline_entry`
|
1671
|
-
- `new_open_type_features`
|
1672
|
-
- `new_password_entry`
|
1673
|
-
- `new_progress_bar`
|
1674
|
-
- `new_radio_buttons`
|
1675
|
-
- `new_scrolling_area`
|
1676
|
-
- `new_search_entry`
|
1677
|
-
- `new_size_attribute`
|
1678
|
-
- `new_slider`
|
1679
|
-
- `new_spinbox`
|
1680
|
-
- `new_stretch_attribute`
|
1681
|
-
- `new_tab`
|
1682
|
-
- `new_table`
|
1683
|
-
- `new_table_model`
|
1684
|
-
- `new_table_value_color`
|
1685
|
-
- `new_table_value_image`
|
1686
|
-
- `new_table_value_int`
|
1687
|
-
- `new_table_value_string`
|
1688
|
-
- `new_time_picker`
|
1689
|
-
- `new_underline_attribute`
|
1690
|
-
- `new_underline_color_attribute`
|
1691
|
-
- `new_vertical_box`
|
1692
|
-
- `new_vertical_separator`
|
1693
|
-
- `new_weight_attribute`
|
1694
|
-
- `new_window`
|
1695
|
-
- `on_should_quit`
|
1696
|
-
- `open_file`
|
1697
|
-
- `open_type_features_add`
|
1698
|
-
- `open_type_features_clone`
|
1699
|
-
- `open_type_features_for_each`
|
1700
|
-
- `open_type_features_get`
|
1701
|
-
- `open_type_features_remove`
|
1702
|
-
- `progress_bar_set_value`
|
1703
|
-
- `progress_bar_value`
|
1704
|
-
- `queue_main`
|
1705
|
-
- `quit`
|
1706
|
-
- `radio_buttons_append`
|
1707
|
-
- `radio_buttons_on_selected`
|
1708
|
-
- `radio_buttons_selected`
|
1709
|
-
- `radio_buttons_set_selected`
|
1710
|
-
- `save_file`
|
1711
|
-
- `slider_on_changed`
|
1712
|
-
- `slider_set_value`
|
1713
|
-
- `slider_value`
|
1714
|
-
- `spinbox_on_changed`
|
1715
|
-
- `spinbox_set_value`
|
1716
|
-
- `spinbox_value`
|
1717
|
-
- `tab_append`
|
1718
|
-
- `tab_delete`
|
1719
|
-
- `tab_insert_at`
|
1720
|
-
- `tab_margined`
|
1721
|
-
- `tab_num_pages`
|
1722
|
-
- `tab_set_margined`
|
1723
|
-
- `table_append_button_column`
|
1724
|
-
- `table_append_checkbox_column`
|
1725
|
-
- `table_append_checkbox_text_column`
|
1726
|
-
- `table_append_image_column`
|
1727
|
-
- `table_append_image_text_column`
|
1728
|
-
- `table_append_progress_bar_column`
|
1729
|
-
- `table_append_text_column`
|
1730
|
-
- `table_model_row_changed`
|
1731
|
-
- `table_model_row_deleted`
|
1732
|
-
- `table_model_row_inserted`
|
1733
|
-
- `table_value_color`
|
1734
|
-
- `table_value_get_type`
|
1735
|
-
- `table_value_image`
|
1736
|
-
- `table_value_int`
|
1737
|
-
- `table_value_string`
|
1738
|
-
- `timer`
|
1739
|
-
- `uninit`
|
1740
|
-
- `user_bug_cannot_set_parent_on_toplevel`
|
1741
|
-
- `window_borderless`
|
1742
|
-
- `window_content_size`
|
1743
|
-
- `window_fullscreen`
|
1744
|
-
- `window_margined`
|
1745
|
-
- `window_on_closing`
|
1746
|
-
- `window_on_content_size_changed`
|
1747
|
-
- `window_set_borderless`
|
1748
|
-
- `window_set_child`
|
1749
|
-
- `window_set_content_size`
|
1750
|
-
- `window_set_fullscreen`
|
1751
|
-
- `window_set_margined`
|
1752
|
-
- `window_set_title`
|
1753
|
-
- `window_title`
|
1619
|
+
`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`
|
1754
1620
|
|
1755
1621
|
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):
|
1756
1622
|
- Check out [LibUI ffi.rb](https://github.com/kojix2/LibUI/blob/main/lib/libui/ffi.rb)
|
@@ -3264,7 +3130,70 @@ Mac | Windows | Linux
|
|
3264
3130
|
----|---------|------
|
3265
3131
|
![glimmer-dsl-libui-mac-basic-table-button.png](images/glimmer-dsl-libui-mac-basic-table-button.png) ![glimmer-dsl-libui-mac-basic-table-button-deleted.png](images/glimmer-dsl-libui-mac-basic-table-button-deleted.png) | ![glimmer-dsl-libui-windows-basic-table-button.png](images/glimmer-dsl-libui-windows-basic-table-button.png) ![glimmer-dsl-libui-windows-basic-table-button-deleted.png](images/glimmer-dsl-libui-windows-basic-table-button-deleted.png) | ![glimmer-dsl-libui-linux-basic-table-button.png](images/glimmer-dsl-libui-linux-basic-table-button.png) ![glimmer-dsl-libui-linux-basic-table-button-deleted.png](images/glimmer-dsl-libui-linux-basic-table-button-deleted.png)
|
3266
3132
|
|
3267
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
3133
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
|
3134
|
+
|
3135
|
+
```ruby
|
3136
|
+
require 'glimmer-dsl-libui'
|
3137
|
+
|
3138
|
+
class BasicTableButton
|
3139
|
+
BasicAnimal = Struct.new(:name, :sound)
|
3140
|
+
|
3141
|
+
class Animal < BasicAnimal
|
3142
|
+
def action
|
3143
|
+
'delete'
|
3144
|
+
end
|
3145
|
+
end
|
3146
|
+
|
3147
|
+
include Glimmer
|
3148
|
+
|
3149
|
+
attr_accessor :animals
|
3150
|
+
|
3151
|
+
def initialize
|
3152
|
+
@animals = [
|
3153
|
+
Animal.new('cat', 'meow'),
|
3154
|
+
Animal.new('dog', 'woof'),
|
3155
|
+
Animal.new('chicken', 'cock-a-doodle-doo'),
|
3156
|
+
Animal.new('horse', 'neigh'),
|
3157
|
+
Animal.new('cow', 'moo'),
|
3158
|
+
]
|
3159
|
+
end
|
3160
|
+
|
3161
|
+
def launch
|
3162
|
+
window('Animal sounds', 400, 200) {
|
3163
|
+
horizontal_box {
|
3164
|
+
table {
|
3165
|
+
text_column('Animal')
|
3166
|
+
text_column('Description')
|
3167
|
+
button_column('Action') {
|
3168
|
+
on_clicked do |row|
|
3169
|
+
# Option 1: direct data deletion is the simpler solution
|
3170
|
+
# @animals.delete_at(row) # automatically deletes actual table row due to explicit data-binding
|
3171
|
+
|
3172
|
+
# Option 2: cloning only to demonstrate table row deletion upon explicit setting of animals attribute (cloning is not recommended beyond demonstrating this point)
|
3173
|
+
new_animals = @animals.clone
|
3174
|
+
new_animals.delete_at(row)
|
3175
|
+
self.animals = new_animals # automatically loses deleted table row due to explicit data-binding
|
3176
|
+
end
|
3177
|
+
}
|
3178
|
+
|
3179
|
+
|
3180
|
+
cell_rows <= [self, :animals, column_attributes: {'Animal' => :name, 'Description' => :sound}]
|
3181
|
+
|
3182
|
+
# explicit unidirectional data-binding of table cell_rows to self.animals
|
3183
|
+
on_changed do |row, type, row_data|
|
3184
|
+
puts "Row #{row} #{type}: #{row_data}"
|
3185
|
+
$stdout.flush
|
3186
|
+
end
|
3187
|
+
}
|
3188
|
+
}
|
3189
|
+
}.show
|
3190
|
+
end
|
3191
|
+
end
|
3192
|
+
|
3193
|
+
BasicTableButton.new.launch
|
3194
|
+
```
|
3195
|
+
|
3196
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (with implicit [data-binding](#data-binding)):
|
3268
3197
|
|
3269
3198
|
```ruby
|
3270
3199
|
require 'glimmer-dsl-libui'
|
@@ -3499,7 +3428,7 @@ window('Animals', 500, 200) {
|
|
3499
3428
|
text_color_column('Sound')
|
3500
3429
|
checkbox_text_color_column('Description')
|
3501
3430
|
image_text_color_column('GUI')
|
3502
|
-
background_color_column
|
3431
|
+
background_color_column # must be the last column
|
3503
3432
|
|
3504
3433
|
cell_rows data
|
3505
3434
|
}
|
@@ -3541,7 +3470,7 @@ window('Animals', 500, 200) {
|
|
3541
3470
|
text_color_column('Sound')
|
3542
3471
|
checkbox_text_color_column('Description')
|
3543
3472
|
image_text_color_column('GUI')
|
3544
|
-
background_color_column
|
3473
|
+
background_color_column
|
3545
3474
|
|
3546
3475
|
cell_rows data
|
3547
3476
|
}
|
@@ -3683,9 +3612,9 @@ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/
|
|
3683
3612
|
ruby -r glimmer-dsl-libui -e "require 'examples/basic_scrolling_area'"
|
3684
3613
|
```
|
3685
3614
|
|
3686
|
-
Mac | Linux
|
3687
|
-
|
3688
|
-
![glimmer-dsl-libui-mac-dynamic-area.png](images/glimmer-dsl-libui-mac-basic-scrolling-area.png) ![glimmer-dsl-libui-mac-dynamic-area-updated.png](images/glimmer-dsl-libui-mac-basic-scrolling-area-scrolled.png) | ![glimmer-dsl-libui-linux-dynamic-area.png](images/glimmer-dsl-libui-linux-basic-scrolling-area.png) ![glimmer-dsl-libui-linux-dynamic-area-updated.png](images/glimmer-dsl-libui-linux-basic-scrolling-area-scrolled.png)
|
3615
|
+
Mac | Windows | Linux
|
3616
|
+
----|---------|------
|
3617
|
+
![glimmer-dsl-libui-mac-dynamic-area.png](images/glimmer-dsl-libui-mac-basic-scrolling-area.png) ![glimmer-dsl-libui-mac-dynamic-area-updated.png](images/glimmer-dsl-libui-mac-basic-scrolling-area-scrolled.png) | ![glimmer-dsl-libui-windows-dynamic-area.png](images/glimmer-dsl-libui-windows-basic-scrolling-area.png) ![glimmer-dsl-libui-windows-dynamic-area-updated.png](images/glimmer-dsl-libui-windows-basic-scrolling-area-scrolled.png) | ![glimmer-dsl-libui-linux-dynamic-area.png](images/glimmer-dsl-libui-linux-basic-scrolling-area.png) ![glimmer-dsl-libui-linux-dynamic-area-updated.png](images/glimmer-dsl-libui-linux-basic-scrolling-area-scrolled.png)
|
3689
3618
|
|
3690
3619
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
3691
3620
|
|
@@ -3772,6 +3701,8 @@ BasicScrollingArea.new.launch
|
|
3772
3701
|
|
3773
3702
|
#### Basic Image
|
3774
3703
|
|
3704
|
+
Please note the caveats of [Area Image](#area-image) **(Alpha Feature)** with regards to this example.
|
3705
|
+
|
3775
3706
|
[examples/basic_image.rb](examples/basic_image.rb)
|
3776
3707
|
|
3777
3708
|
Run with this command from the root of the project if you cloned the project:
|
@@ -5022,9 +4953,9 @@ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/
|
|
5022
4953
|
ruby -r glimmer-dsl-libui -e "require 'examples/button_counter'"
|
5023
4954
|
```
|
5024
4955
|
|
5025
|
-
Mac | Linux
|
5026
|
-
|
5027
|
-
![glimmer-dsl-libui-mac-button-counter.png](images/glimmer-dsl-libui-mac-button-counter.png) | ![glimmer-dsl-libui-linux-button-counter.png](images/glimmer-dsl-libui-linux-button-counter.png)
|
4956
|
+
Mac | Windows | Linux
|
4957
|
+
----|---------|------
|
4958
|
+
![glimmer-dsl-libui-mac-button-counter.png](images/glimmer-dsl-libui-mac-button-counter.png) | ![glimmer-dsl-libui-windows-button-counter.png](images/glimmer-dsl-libui-windows-button-counter.png) | ![glimmer-dsl-libui-linux-button-counter.png](images/glimmer-dsl-libui-linux-button-counter.png)
|
5028
4959
|
|
5029
4960
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
5030
4961
|
|
@@ -5043,7 +4974,8 @@ class ButtonCounter
|
|
5043
4974
|
def launch
|
5044
4975
|
window('Hello, Button!') {
|
5045
4976
|
button {
|
5046
|
-
|
4977
|
+
# data-bind button text to self count, converting to string on read.
|
4978
|
+
text <= [self, :count, on_read: ->(count) {"Count: #{count}"}]
|
5047
4979
|
|
5048
4980
|
on_clicked do
|
5049
4981
|
self.count += 1
|
@@ -5687,6 +5619,71 @@ MAIN_WINDOW = window('Control Gallery', 600, 500) {
|
|
5687
5619
|
MAIN_WINDOW.show
|
5688
5620
|
```
|
5689
5621
|
|
5622
|
+
#### CPU Percentage
|
5623
|
+
|
5624
|
+
This example shows CPU usage percentage second by second.
|
5625
|
+
|
5626
|
+
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.
|
5627
|
+
|
5628
|
+
[examples/cpu_percentage.rb](examples/cpu_percentage.rb)
|
5629
|
+
|
5630
|
+
Run with this command from the root of the project if you cloned the project:
|
5631
|
+
|
5632
|
+
```
|
5633
|
+
ruby -r './lib/glimmer-dsl-libui' examples/cpu_percentage.rb
|
5634
|
+
```
|
5635
|
+
|
5636
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
5637
|
+
|
5638
|
+
```
|
5639
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/cpu_percentage'"
|
5640
|
+
```
|
5641
|
+
|
5642
|
+
Mac | Windows | Linux
|
5643
|
+
----|---------|------
|
5644
|
+
![glimmer-dsl-libui-mac-cpu-percentage.png](images/glimmer-dsl-libui-mac-cpu-percentage.png) | ![glimmer-dsl-libui-windows-cpu-percentage.png](images/glimmer-dsl-libui-windows-cpu-percentage.png) | ![glimmer-dsl-libui-linux-cpu-percentage.png](images/glimmer-dsl-libui-linux-cpu-percentage.png)
|
5645
|
+
|
5646
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
5647
|
+
|
5648
|
+
```ruby
|
5649
|
+
require 'glimmer-dsl-libui'
|
5650
|
+
require 'bigdecimal'
|
5651
|
+
|
5652
|
+
include Glimmer
|
5653
|
+
|
5654
|
+
data = [
|
5655
|
+
['CPU', '0%', 0],
|
5656
|
+
]
|
5657
|
+
|
5658
|
+
Glimmer::LibUI.timer(1) do
|
5659
|
+
cpu_percentage_value = nil
|
5660
|
+
if OS.windows?
|
5661
|
+
cpu_percentage_raw_value = `wmic cpu get loadpercentage`
|
5662
|
+
cpu_percentage_value = cpu_percentage_raw_value.split("\n")[2].to_i
|
5663
|
+
elsif OS.mac?
|
5664
|
+
cpu_percentage_value = `ps -A -o %cpu | awk '{s+=$1} END {print s}'`.to_i
|
5665
|
+
elsif OS.linux?
|
5666
|
+
stats = `top -n 1`
|
5667
|
+
idle_percentage = stats.split("\n")[2].match(/ni,.* (.*) .*id/)[1]
|
5668
|
+
cpu_percentage_value = (BigDecimal(100) - BigDecimal(idle_percentage)).to_i
|
5669
|
+
end
|
5670
|
+
data[0][1] = "#{cpu_percentage_value}%"
|
5671
|
+
data[0][2] = cpu_percentage_value
|
5672
|
+
end
|
5673
|
+
|
5674
|
+
window('CPU Percentage', 400, 200) {
|
5675
|
+
vertical_box {
|
5676
|
+
table {
|
5677
|
+
text_column('Name')
|
5678
|
+
text_column('Value')
|
5679
|
+
progress_bar_column('Percentage')
|
5680
|
+
|
5681
|
+
cell_rows data # implicit data-binding
|
5682
|
+
}
|
5683
|
+
}
|
5684
|
+
}.show
|
5685
|
+
```
|
5686
|
+
|
5690
5687
|
#### Custom Draw Text
|
5691
5688
|
|
5692
5689
|
[examples/custom_draw_text.rb](examples/custom_draw_text.rb)
|
@@ -6397,8 +6394,8 @@ window('Editable animal sounds', 300, 200) {
|
|
6397
6394
|
text_column('Animal')
|
6398
6395
|
text_column('Description')
|
6399
6396
|
|
6400
|
-
cell_rows data
|
6401
6397
|
editable true
|
6398
|
+
cell_rows data
|
6402
6399
|
|
6403
6400
|
on_changed do |row, type, row_data| # fires on all changes (even ones happening through data array)
|
6404
6401
|
puts "Row #{row} #{type}: #{row_data}"
|
@@ -6413,30 +6410,382 @@ window('Editable animal sounds', 300, 200) {
|
|
6413
6410
|
on_closing do
|
6414
6411
|
puts 'Bye Bye'
|
6415
6412
|
end
|
6416
|
-
}.show
|
6417
|
-
```
|
6418
|
-
|
6419
|
-
#### Form Table
|
6420
|
-
|
6421
|
-
[examples/form_table.rb](examples/form_table.rb)
|
6422
|
-
|
6423
|
-
Run with this command from the root of the project if you cloned the project:
|
6424
|
-
|
6425
|
-
```
|
6426
|
-
ruby -r './lib/glimmer-dsl-libui' examples/form_table.rb
|
6427
|
-
```
|
6428
|
-
|
6429
|
-
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
6413
|
+
}.show
|
6414
|
+
```
|
6415
|
+
|
6416
|
+
#### Form Table
|
6417
|
+
|
6418
|
+
[examples/form_table.rb](examples/form_table.rb)
|
6419
|
+
|
6420
|
+
Run with this command from the root of the project if you cloned the project:
|
6421
|
+
|
6422
|
+
```
|
6423
|
+
ruby -r './lib/glimmer-dsl-libui' examples/form_table.rb
|
6424
|
+
```
|
6425
|
+
|
6426
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
6427
|
+
|
6428
|
+
```
|
6429
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/form_table'"
|
6430
|
+
```
|
6431
|
+
|
6432
|
+
Mac | Windows | Linux
|
6433
|
+
----|---------|------
|
6434
|
+
![glimmer-dsl-libui-mac-form-table.png](images/glimmer-dsl-libui-mac-form-table.png) ![glimmer-dsl-libui-mac-form-table-contact-entered.png](images/glimmer-dsl-libui-mac-form-table-contact-entered.png) ![glimmer-dsl-libui-mac-form-table-filtered.png](images/glimmer-dsl-libui-mac-form-table-filtered.png) | ![glimmer-dsl-libui-windows-form-table.png](images/glimmer-dsl-libui-windows-form-table.png) ![glimmer-dsl-libui-windows-form-table-contact-entered.png](images/glimmer-dsl-libui-windows-form-table-contact-entered.png) ![glimmer-dsl-libui-windows-form-table-filtered.png](images/glimmer-dsl-libui-windows-form-table-filtered.png) | ![glimmer-dsl-libui-linux-form-table.png](images/glimmer-dsl-libui-linux-form-table.png) ![glimmer-dsl-libui-linux-form-table-contact-entered.png](images/glimmer-dsl-libui-linux-form-table-contact-entered.png) ![glimmer-dsl-libui-linux-form-table-filtered.png](images/glimmer-dsl-libui-linux-form-table-filtered.png)
|
6435
|
+
|
6436
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
|
6437
|
+
|
6438
|
+
```ruby
|
6439
|
+
require 'glimmer-dsl-libui'
|
6440
|
+
|
6441
|
+
class FormTable
|
6442
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
6443
|
+
|
6444
|
+
include Glimmer
|
6445
|
+
|
6446
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
6447
|
+
|
6448
|
+
def initialize
|
6449
|
+
@contacts = [
|
6450
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
6451
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
6452
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
6453
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
6454
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
6455
|
+
]
|
6456
|
+
end
|
6457
|
+
|
6458
|
+
def launch
|
6459
|
+
window('Contacts', 600, 600) { |w|
|
6460
|
+
margined true
|
6461
|
+
|
6462
|
+
vertical_box {
|
6463
|
+
form {
|
6464
|
+
stretchy false
|
6465
|
+
|
6466
|
+
entry {
|
6467
|
+
label 'Name'
|
6468
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
6469
|
+
}
|
6470
|
+
|
6471
|
+
entry {
|
6472
|
+
label 'Email'
|
6473
|
+
text <=> [self, :email]
|
6474
|
+
}
|
6475
|
+
|
6476
|
+
entry {
|
6477
|
+
label 'Phone'
|
6478
|
+
text <=> [self, :phone]
|
6479
|
+
}
|
6480
|
+
|
6481
|
+
entry {
|
6482
|
+
label 'City'
|
6483
|
+
text <=> [self, :city]
|
6484
|
+
}
|
6485
|
+
|
6486
|
+
entry {
|
6487
|
+
label 'State'
|
6488
|
+
text <=> [self, :state]
|
6489
|
+
}
|
6490
|
+
}
|
6491
|
+
|
6492
|
+
button('Save Contact') {
|
6493
|
+
stretchy false
|
6494
|
+
|
6495
|
+
on_clicked do
|
6496
|
+
new_row = [name, email, phone, city, state]
|
6497
|
+
if new_row.include?('')
|
6498
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
6499
|
+
else
|
6500
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to explicit data-binding
|
6501
|
+
@unfiltered_contacts = @contacts.dup
|
6502
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
6503
|
+
self.email = ''
|
6504
|
+
self.phone = ''
|
6505
|
+
self.city = ''
|
6506
|
+
self.state = ''
|
6507
|
+
end
|
6508
|
+
end
|
6509
|
+
}
|
6510
|
+
|
6511
|
+
search_entry {
|
6512
|
+
stretchy false
|
6513
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
6514
|
+
text <=> [self, :filter_value,
|
6515
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
6516
|
+
@unfiltered_contacts ||= @contacts.dup
|
6517
|
+
# Unfilter first to remove any previous filters
|
6518
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
6519
|
+
# Now, apply filter if entered
|
6520
|
+
unless filter_value.empty?
|
6521
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
6522
|
+
contact.members.any? do |attribute|
|
6523
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
6524
|
+
end
|
6525
|
+
end
|
6526
|
+
end
|
6527
|
+
}
|
6528
|
+
]
|
6529
|
+
}
|
6530
|
+
|
6531
|
+
table {
|
6532
|
+
text_column('Name')
|
6533
|
+
text_column('Email')
|
6534
|
+
text_column('Phone')
|
6535
|
+
text_column('City')
|
6536
|
+
text_column('State')
|
6537
|
+
|
6538
|
+
editable true
|
6539
|
+
cell_rows <=> [self, :contacts] # explicit data-binding to Model Array
|
6540
|
+
|
6541
|
+
on_changed do |row, type, row_data|
|
6542
|
+
puts "Row #{row} #{type}: #{row_data}"
|
6543
|
+
end
|
6544
|
+
}
|
6545
|
+
}
|
6546
|
+
}.show
|
6547
|
+
end
|
6548
|
+
end
|
6549
|
+
|
6550
|
+
FormTable.new.launch
|
6551
|
+
```
|
6552
|
+
|
6553
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
|
6554
|
+
|
6555
|
+
```ruby
|
6556
|
+
require 'glimmer-dsl-libui'
|
6557
|
+
|
6558
|
+
class FormTable
|
6559
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
6560
|
+
|
6561
|
+
include Glimmer
|
6562
|
+
|
6563
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
6564
|
+
|
6565
|
+
def initialize
|
6566
|
+
@contacts = [
|
6567
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
6568
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
6569
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
6570
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
6571
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
6572
|
+
]
|
6573
|
+
end
|
6574
|
+
|
6575
|
+
def launch
|
6576
|
+
window('Contacts', 600, 600) { |w|
|
6577
|
+
margined true
|
6578
|
+
|
6579
|
+
vertical_box {
|
6580
|
+
form {
|
6581
|
+
stretchy false
|
6582
|
+
|
6583
|
+
entry {
|
6584
|
+
label 'Name'
|
6585
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
6586
|
+
}
|
6587
|
+
|
6588
|
+
entry {
|
6589
|
+
label 'Email'
|
6590
|
+
text <=> [self, :email]
|
6591
|
+
}
|
6592
|
+
|
6593
|
+
entry {
|
6594
|
+
label 'Phone'
|
6595
|
+
text <=> [self, :phone]
|
6596
|
+
}
|
6597
|
+
|
6598
|
+
entry {
|
6599
|
+
label 'City'
|
6600
|
+
text <=> [self, :city]
|
6601
|
+
}
|
6602
|
+
|
6603
|
+
entry {
|
6604
|
+
label 'State'
|
6605
|
+
text <=> [self, :state]
|
6606
|
+
}
|
6607
|
+
}
|
6608
|
+
|
6609
|
+
button('Save Contact') {
|
6610
|
+
stretchy false
|
6611
|
+
|
6612
|
+
on_clicked do
|
6613
|
+
new_row = [name, email, phone, city, state]
|
6614
|
+
if new_row.include?('')
|
6615
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
6616
|
+
else
|
6617
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to implicit data-binding
|
6618
|
+
@unfiltered_contacts = @contacts.dup
|
6619
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
6620
|
+
self.email = ''
|
6621
|
+
self.phone = ''
|
6622
|
+
self.city = ''
|
6623
|
+
self.state = ''
|
6624
|
+
end
|
6625
|
+
end
|
6626
|
+
}
|
6627
|
+
|
6628
|
+
search_entry {
|
6629
|
+
stretchy false
|
6630
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
6631
|
+
text <=> [self, :filter_value,
|
6632
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
6633
|
+
@unfiltered_contacts ||= @contacts.dup
|
6634
|
+
# Unfilter first to remove any previous filters
|
6635
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
6636
|
+
# Now, apply filter if entered
|
6637
|
+
unless filter_value.empty?
|
6638
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
6639
|
+
contact.members.any? do |attribute|
|
6640
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
6641
|
+
end
|
6642
|
+
end
|
6643
|
+
end
|
6644
|
+
}
|
6645
|
+
]
|
6646
|
+
}
|
6647
|
+
|
6648
|
+
table {
|
6649
|
+
text_column('Name')
|
6650
|
+
text_column('Email')
|
6651
|
+
text_column('Phone')
|
6652
|
+
text_column('City')
|
6653
|
+
text_column('State/Province')
|
6654
|
+
|
6655
|
+
editable true
|
6656
|
+
cell_rows <=> [self, :contacts, column_attributes: {'State/Province' => :state}] # explicit data-binding to Model Array with column_attributes mapping for a specific column
|
6657
|
+
|
6658
|
+
on_changed do |row, type, row_data|
|
6659
|
+
puts "Row #{row} #{type}: #{row_data}"
|
6660
|
+
end
|
6661
|
+
}
|
6662
|
+
}
|
6663
|
+
}.show
|
6664
|
+
end
|
6665
|
+
end
|
6666
|
+
|
6667
|
+
FormTable.new.launch
|
6668
|
+
```
|
6669
|
+
|
6670
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
|
6671
|
+
|
6672
|
+
```ruby
|
6673
|
+
|
6674
|
+
require 'glimmer-dsl-libui'
|
6675
|
+
|
6676
|
+
class FormTable
|
6677
|
+
Contact = Struct.new(:full_name, :email_address, :phone_number, :city_or_town, :state_or_province)
|
6678
|
+
|
6679
|
+
include Glimmer
|
6680
|
+
|
6681
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
6682
|
+
|
6683
|
+
def initialize
|
6684
|
+
@contacts = [
|
6685
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
6686
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
6687
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
6688
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
6689
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
6690
|
+
]
|
6691
|
+
end
|
6692
|
+
|
6693
|
+
def launch
|
6694
|
+
window('Contacts', 600, 600) { |w|
|
6695
|
+
margined true
|
6696
|
+
|
6697
|
+
vertical_box {
|
6698
|
+
form {
|
6699
|
+
stretchy false
|
6700
|
+
|
6701
|
+
entry {
|
6702
|
+
label 'Name'
|
6703
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
6704
|
+
}
|
6705
|
+
|
6706
|
+
entry {
|
6707
|
+
label 'Email'
|
6708
|
+
text <=> [self, :email]
|
6709
|
+
}
|
6710
|
+
|
6711
|
+
entry {
|
6712
|
+
label 'Phone'
|
6713
|
+
text <=> [self, :phone]
|
6714
|
+
}
|
6715
|
+
|
6716
|
+
entry {
|
6717
|
+
label 'City'
|
6718
|
+
text <=> [self, :city]
|
6719
|
+
}
|
6720
|
+
|
6721
|
+
entry {
|
6722
|
+
label 'State'
|
6723
|
+
text <=> [self, :state]
|
6724
|
+
}
|
6725
|
+
}
|
6726
|
+
|
6727
|
+
button('Save Contact') {
|
6728
|
+
stretchy false
|
6729
|
+
|
6730
|
+
on_clicked do
|
6731
|
+
new_row = [name, email, phone, city, state]
|
6732
|
+
if new_row.include?('')
|
6733
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
6734
|
+
else
|
6735
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to implicit data-binding
|
6736
|
+
@unfiltered_contacts = @contacts.dup
|
6737
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
6738
|
+
self.email = ''
|
6739
|
+
self.phone = ''
|
6740
|
+
self.city = ''
|
6741
|
+
self.state = ''
|
6742
|
+
end
|
6743
|
+
end
|
6744
|
+
}
|
6745
|
+
|
6746
|
+
search_entry {
|
6747
|
+
stretchy false
|
6748
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
6749
|
+
text <=> [self, :filter_value,
|
6750
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
6751
|
+
@unfiltered_contacts ||= @contacts.dup
|
6752
|
+
# Unfilter first to remove any previous filters
|
6753
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
6754
|
+
# Now, apply filter if entered
|
6755
|
+
unless filter_value.empty?
|
6756
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
6757
|
+
contact.members.any? do |attribute|
|
6758
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
6759
|
+
end
|
6760
|
+
end
|
6761
|
+
end
|
6762
|
+
}
|
6763
|
+
]
|
6764
|
+
}
|
6765
|
+
|
6766
|
+
table {
|
6767
|
+
text_column('Name')
|
6768
|
+
text_column('Email')
|
6769
|
+
text_column('Phone')
|
6770
|
+
text_column('City')
|
6771
|
+
text_column('State')
|
6772
|
+
|
6773
|
+
editable true
|
6774
|
+
cell_rows <=> [self, :contacts, column_attributes: [:full_name, :email_address, :phone_number, :city_or_town, :state_or_province]] # explicit data-binding to Model Array with column_attributes mapping for all columns
|
6775
|
+
|
6776
|
+
on_changed do |row, type, row_data|
|
6777
|
+
puts "Row #{row} #{type}: #{row_data}"
|
6778
|
+
end
|
6779
|
+
}
|
6780
|
+
}
|
6781
|
+
}.show
|
6782
|
+
end
|
6783
|
+
end
|
6430
6784
|
|
6785
|
+
FormTable.new.launch
|
6431
6786
|
```
|
6432
|
-
ruby -r glimmer-dsl-libui -e "require 'examples/form_table'"
|
6433
|
-
```
|
6434
|
-
|
6435
|
-
Mac | Windows | Linux
|
6436
|
-
----|---------|------
|
6437
|
-
![glimmer-dsl-libui-mac-form-table.png](images/glimmer-dsl-libui-mac-form-table.png) ![glimmer-dsl-libui-mac-form-table-contact-entered.png](images/glimmer-dsl-libui-mac-form-table-contact-entered.png) ![glimmer-dsl-libui-mac-form-table-filtered.png](images/glimmer-dsl-libui-mac-form-table-filtered.png) | ![glimmer-dsl-libui-windows-form-table.png](images/glimmer-dsl-libui-windows-form-table.png) ![glimmer-dsl-libui-windows-form-table-contact-entered.png](images/glimmer-dsl-libui-windows-form-table-contact-entered.png) ![glimmer-dsl-libui-windows-form-table-filtered.png](images/glimmer-dsl-libui-windows-form-table-filtered.png) | ![glimmer-dsl-libui-linux-form-table.png](images/glimmer-dsl-libui-linux-form-table.png) ![glimmer-dsl-libui-linux-form-table-contact-entered.png](images/glimmer-dsl-libui-linux-form-table-contact-entered.png) ![glimmer-dsl-libui-linux-form-table-filtered.png](images/glimmer-dsl-libui-linux-form-table-filtered.png)
|
6438
6787
|
|
6439
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
6788
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 4 (with explicit [data-binding](#data-binding) to raw data):
|
6440
6789
|
|
6441
6790
|
```ruby
|
6442
6791
|
require 'glimmer-dsl-libui'
|
@@ -6444,15 +6793,15 @@ require 'glimmer-dsl-libui'
|
|
6444
6793
|
class FormTable
|
6445
6794
|
include Glimmer
|
6446
6795
|
|
6447
|
-
attr_accessor :name, :email, :phone, :city, :state, :filter_value
|
6796
|
+
attr_accessor :data, :name, :email, :phone, :city, :state, :filter_value
|
6448
6797
|
|
6449
6798
|
def initialize
|
6450
6799
|
@data = [
|
6451
|
-
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'
|
6452
|
-
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'
|
6453
|
-
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'
|
6454
|
-
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'
|
6455
|
-
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'
|
6800
|
+
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
|
6801
|
+
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
|
6802
|
+
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
|
6803
|
+
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
|
6804
|
+
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
|
6456
6805
|
]
|
6457
6806
|
end
|
6458
6807
|
|
@@ -6466,7 +6815,7 @@ class FormTable
|
|
6466
6815
|
|
6467
6816
|
entry {
|
6468
6817
|
label 'Name'
|
6469
|
-
text <=> [self, :name]
|
6818
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
6470
6819
|
}
|
6471
6820
|
|
6472
6821
|
entry {
|
@@ -6498,8 +6847,8 @@ class FormTable
|
|
6498
6847
|
if new_row.include?('')
|
6499
6848
|
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
6500
6849
|
else
|
6501
|
-
|
6502
|
-
@unfiltered_data =
|
6850
|
+
data << new_row # automatically inserts a row into the table due to implicit data-binding
|
6851
|
+
@unfiltered_data = data.dup
|
6503
6852
|
self.name = '' # automatically clears name entry through explicit data-binding
|
6504
6853
|
self.email = ''
|
6505
6854
|
self.phone = ''
|
@@ -6511,14 +6860,15 @@ class FormTable
|
|
6511
6860
|
|
6512
6861
|
search_entry {
|
6513
6862
|
stretchy false
|
6514
|
-
|
6863
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
6864
|
+
text <=> [self, :filter_value,
|
6515
6865
|
after_write: ->(filter_value) { # execute after write to self.filter_value
|
6516
|
-
@unfiltered_data ||=
|
6866
|
+
@unfiltered_data ||= data.dup
|
6517
6867
|
# Unfilter first to remove any previous filters
|
6518
|
-
|
6868
|
+
data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
|
6519
6869
|
# Now, apply filter if entered
|
6520
6870
|
unless filter_value.empty?
|
6521
|
-
|
6871
|
+
data.filter! do |row_data| # affects table indirectly through implicit data-binding
|
6522
6872
|
row_data.any? do |cell|
|
6523
6873
|
cell.to_s.downcase.include?(filter_value.downcase)
|
6524
6874
|
end
|
@@ -6534,8 +6884,9 @@ class FormTable
|
|
6534
6884
|
text_column('Phone')
|
6535
6885
|
text_column('City')
|
6536
6886
|
text_column('State')
|
6537
|
-
|
6538
|
-
|
6887
|
+
|
6888
|
+
editable true
|
6889
|
+
cell_rows <=> [self, :data] # explicit data-binding to raw data Array of Arrays
|
6539
6890
|
|
6540
6891
|
on_changed do |row, type, row_data|
|
6541
6892
|
puts "Row #{row} #{type}: #{row_data}"
|
@@ -6549,7 +6900,7 @@ end
|
|
6549
6900
|
FormTable.new.launch
|
6550
6901
|
```
|
6551
6902
|
|
6552
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version
|
6903
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 5 (with implicit [data-binding](#data-binding)):
|
6553
6904
|
|
6554
6905
|
```ruby
|
6555
6906
|
require 'glimmer-dsl-libui'
|
@@ -6557,11 +6908,11 @@ require 'glimmer-dsl-libui'
|
|
6557
6908
|
include Glimmer
|
6558
6909
|
|
6559
6910
|
data = [
|
6560
|
-
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'
|
6561
|
-
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'
|
6562
|
-
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'
|
6563
|
-
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'
|
6564
|
-
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'
|
6911
|
+
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
|
6912
|
+
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
|
6913
|
+
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
|
6914
|
+
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
|
6915
|
+
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
|
6565
6916
|
]
|
6566
6917
|
|
6567
6918
|
window('Contacts', 600, 600) { |w|
|
@@ -6637,7 +6988,8 @@ window('Contacts', 600, 600) { |w|
|
|
6637
6988
|
text_column('City')
|
6638
6989
|
text_column('State')
|
6639
6990
|
|
6640
|
-
|
6991
|
+
editable true
|
6992
|
+
cell_rows data # implicit data-binding to raw data Array of Arrays
|
6641
6993
|
|
6642
6994
|
on_changed do |row, type, row_data|
|
6643
6995
|
puts "Row #{row} #{type}: #{row_data}"
|
@@ -7965,7 +8317,7 @@ class TinyMidiPlayer
|
|
7965
8317
|
}
|
7966
8318
|
}
|
7967
8319
|
|
7968
|
-
combobox {
|
8320
|
+
combobox {
|
7969
8321
|
items @midi_files.map { |path| File.basename(path) }
|
7970
8322
|
# data-bind selected item (String) to self.selected_file with on-read/on-write converters and after_write operation
|
7971
8323
|
selected_item <=> [self, :selected_file, on_read: ->(f) {File.basename(f.to_s)}, on_write: ->(f) {File.join(@music_directory, f)}, after_write: -> { play_midi if @th&.alive? }]
|
@@ -8051,7 +8403,7 @@ class TinyMidiPlayer
|
|
8051
8403
|
}
|
8052
8404
|
}
|
8053
8405
|
|
8054
|
-
combobox {
|
8406
|
+
combobox {
|
8055
8407
|
items @midi_files.map { |path| File.basename(path) }
|
8056
8408
|
# data-bind selected index (Integer) to self.selected_file with on-read/on-write converters and after_write operation
|
8057
8409
|
selected <=> [self, :selected_file, on_read: ->(f) {@midi_files.index(f)}, on_write: ->(i) {@midi_files[i]}, after_write: -> { play_midi if @th&.alive? }]
|
@@ -8173,7 +8525,7 @@ Mac | Windows | Linux
|
|
8173
8525
|
----|---------|------
|
8174
8526
|
![glimmer-dsl-libui-mac-snake.png](images/glimmer-dsl-libui-mac-snake.png) ![glimmer-dsl-libui-mac-snake-game-over.png](images/glimmer-dsl-libui-mac-snake-game-over.png) | ![glimmer-dsl-libui-windows-snake.png](images/glimmer-dsl-libui-windows-snake.png) ![glimmer-dsl-libui-windows-snake-game-over.png](images/glimmer-dsl-libui-windows-snake-game-over.png) | ![glimmer-dsl-libui-linux-snake.png](images/glimmer-dsl-libui-linux-snake.png) ![glimmer-dsl-libui-linux-snake-game-over.png](images/glimmer-dsl-libui-linux-snake-game-over.png)
|
8175
8527
|
|
8176
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
8528
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
8177
8529
|
|
8178
8530
|
```ruby
|
8179
8531
|
require 'glimmer-dsl-libui'
|
@@ -8190,6 +8542,7 @@ class Snake
|
|
8190
8542
|
@game = Model::Game.new
|
8191
8543
|
@grid = Presenter::Grid.new(@game)
|
8192
8544
|
@game.start
|
8545
|
+
@keypress_queue = []
|
8193
8546
|
create_gui
|
8194
8547
|
register_observers
|
8195
8548
|
end
|
@@ -8209,14 +8562,30 @@ class Snake
|
|
8209
8562
|
end
|
8210
8563
|
|
8211
8564
|
Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
|
8212
|
-
|
8565
|
+
unless @game.over?
|
8566
|
+
process_queued_keypress
|
8567
|
+
@game.snake.move
|
8568
|
+
end
|
8569
|
+
end
|
8570
|
+
end
|
8571
|
+
|
8572
|
+
def process_queued_keypress
|
8573
|
+
# 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)
|
8574
|
+
key = @keypress_queue.shift
|
8575
|
+
case [@game.snake.head.orientation, key]
|
8576
|
+
in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
|
8577
|
+
@game.snake.turn_right
|
8578
|
+
in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
|
8579
|
+
@game.snake.turn_left
|
8580
|
+
else
|
8581
|
+
# No Op
|
8213
8582
|
end
|
8214
8583
|
end
|
8215
8584
|
|
8216
8585
|
def create_gui
|
8217
8586
|
@main_window = window {
|
8218
8587
|
# data-bind window title to game score, converting it to a title string on read from the model
|
8219
|
-
title <= [@game, :score, on_read: -> (score) {"
|
8588
|
+
title <= [@game, :score, on_read: -> (score) {"Snake (Score: #{@game.score})"}]
|
8220
8589
|
content_size @game.width * CELL_SIZE, @game.height * CELL_SIZE
|
8221
8590
|
resizable false
|
8222
8591
|
|
@@ -8234,15 +8603,109 @@ class Snake
|
|
8234
8603
|
}
|
8235
8604
|
|
8236
8605
|
on_key_up do |area_key_event|
|
8237
|
-
|
8238
|
-
|
8239
|
-
|
8240
|
-
|
8241
|
-
|
8242
|
-
|
8243
|
-
|
8244
|
-
|
8245
|
-
|
8606
|
+
@keypress_queue << area_key_event[:ext_key]
|
8607
|
+
end
|
8608
|
+
}
|
8609
|
+
end
|
8610
|
+
}
|
8611
|
+
end
|
8612
|
+
}
|
8613
|
+
}
|
8614
|
+
end
|
8615
|
+
end
|
8616
|
+
|
8617
|
+
Snake.new.launch
|
8618
|
+
```
|
8619
|
+
|
8620
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
8621
|
+
|
8622
|
+
```ruby
|
8623
|
+
require 'glimmer-dsl-libui'
|
8624
|
+
|
8625
|
+
require_relative 'snake/presenter/grid'
|
8626
|
+
|
8627
|
+
class Snake
|
8628
|
+
include Glimmer
|
8629
|
+
|
8630
|
+
CELL_SIZE = 15
|
8631
|
+
SNAKE_MOVE_DELAY = 0.1
|
8632
|
+
|
8633
|
+
def initialize
|
8634
|
+
@game = Model::Game.new
|
8635
|
+
@grid = Presenter::Grid.new(@game)
|
8636
|
+
@game.start
|
8637
|
+
@keypress_queue = []
|
8638
|
+
create_gui
|
8639
|
+
register_observers
|
8640
|
+
end
|
8641
|
+
|
8642
|
+
def launch
|
8643
|
+
@main_window.show
|
8644
|
+
end
|
8645
|
+
|
8646
|
+
def register_observers
|
8647
|
+
@game.height.times do |row|
|
8648
|
+
@game.width.times do |column|
|
8649
|
+
observe(@grid.cells[row][column], :color) do |new_color|
|
8650
|
+
@cell_grid[row][column].fill = new_color
|
8651
|
+
end
|
8652
|
+
end
|
8653
|
+
end
|
8654
|
+
|
8655
|
+
observe(@game, :over) do |game_over|
|
8656
|
+
Glimmer::LibUI.queue_main do
|
8657
|
+
if game_over
|
8658
|
+
msg_box('Game Over!', "Score: #{@game.score} | High Score: #{@game.high_score}")
|
8659
|
+
@game.start
|
8660
|
+
end
|
8661
|
+
end
|
8662
|
+
end
|
8663
|
+
|
8664
|
+
Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
|
8665
|
+
unless @game.over?
|
8666
|
+
process_queued_keypress
|
8667
|
+
@game.snake.move
|
8668
|
+
end
|
8669
|
+
end
|
8670
|
+
end
|
8671
|
+
|
8672
|
+
def process_queued_keypress
|
8673
|
+
# 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)
|
8674
|
+
key = @keypress_queue.shift
|
8675
|
+
case [@game.snake.head.orientation, key]
|
8676
|
+
in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
|
8677
|
+
@game.snake.turn_right
|
8678
|
+
in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
|
8679
|
+
@game.snake.turn_left
|
8680
|
+
else
|
8681
|
+
# No Op
|
8682
|
+
end
|
8683
|
+
end
|
8684
|
+
|
8685
|
+
def create_gui
|
8686
|
+
@cell_grid = []
|
8687
|
+
@main_window = window {
|
8688
|
+
# data-bind window title to game score, converting it to a title string on read from the model
|
8689
|
+
title <= [@game, :score, on_read: -> (score) {"Snake (Score: #{@game.score})"}]
|
8690
|
+
content_size @game.width * CELL_SIZE, @game.height * CELL_SIZE
|
8691
|
+
resizable false
|
8692
|
+
|
8693
|
+
vertical_box {
|
8694
|
+
padded false
|
8695
|
+
|
8696
|
+
@game.height.times do |row|
|
8697
|
+
@cell_grid << []
|
8698
|
+
horizontal_box {
|
8699
|
+
padded false
|
8700
|
+
|
8701
|
+
@game.width.times do |column|
|
8702
|
+
area {
|
8703
|
+
@cell_grid.last << square(0, 0, CELL_SIZE) {
|
8704
|
+
fill Presenter::Cell::COLOR_CLEAR
|
8705
|
+
}
|
8706
|
+
|
8707
|
+
on_key_up do |area_key_event|
|
8708
|
+
@keypress_queue << area_key_event[:ext_key]
|
8246
8709
|
end
|
8247
8710
|
}
|
8248
8711
|
end
|
@@ -8392,22 +8855,23 @@ class Tetris
|
|
8392
8855
|
menu('Game') {
|
8393
8856
|
@pause_menu_item = check_menu_item('Pause') {
|
8394
8857
|
enabled false
|
8395
|
-
|
8396
|
-
on_clicked do
|
8397
|
-
@game.paused = @pause_menu_item.checked?
|
8398
|
-
end
|
8858
|
+
checked <=> [@game, :paused]
|
8399
8859
|
}
|
8860
|
+
|
8400
8861
|
menu_item('Restart') {
|
8401
8862
|
on_clicked do
|
8402
8863
|
@game.restart!
|
8403
8864
|
end
|
8404
8865
|
}
|
8866
|
+
|
8405
8867
|
separator_menu_item
|
8868
|
+
|
8406
8869
|
menu_item('Exit') {
|
8407
8870
|
on_clicked do
|
8408
8871
|
exit(0)
|
8409
8872
|
end
|
8410
8873
|
}
|
8874
|
+
|
8411
8875
|
quit_menu_item if OS.mac?
|
8412
8876
|
}
|
8413
8877
|
|
@@ -8417,6 +8881,7 @@ class Tetris
|
|
8417
8881
|
show_high_scores
|
8418
8882
|
end
|
8419
8883
|
}
|
8884
|
+
|
8420
8885
|
menu_item('Clear High Scores') {
|
8421
8886
|
on_clicked {
|
8422
8887
|
@game.clear_high_scores!
|
@@ -8425,22 +8890,16 @@ class Tetris
|
|
8425
8890
|
}
|
8426
8891
|
|
8427
8892
|
menu('Options') {
|
8428
|
-
radio_menu_item('Instant Down on Up Arrow') {
|
8429
|
-
|
8430
|
-
@game.instant_down_on_up = true
|
8431
|
-
end
|
8893
|
+
radio_menu_item('Instant Down on Up Arrow') { |r|
|
8894
|
+
checked <=> [@game, :instant_down_on_up]
|
8432
8895
|
}
|
8433
|
-
|
8434
|
-
|
8435
|
-
|
8436
|
-
end
|
8896
|
+
|
8897
|
+
radio_menu_item('Rotate Right on Up Arrow') { |r|
|
8898
|
+
checked <=> [@game, :rotate_right_on_up]
|
8437
8899
|
}
|
8438
|
-
|
8439
|
-
|
8440
|
-
|
8441
|
-
on_clicked do
|
8442
|
-
@game.rotate_left_on_up = true
|
8443
|
-
end
|
8900
|
+
|
8901
|
+
radio_menu_item('Rotate Left on Up Arrow') { |r|
|
8902
|
+
checked <=> [@game, :rotate_left_on_up]
|
8444
8903
|
}
|
8445
8904
|
}
|
8446
8905
|
|
@@ -8452,6 +8911,7 @@ class Tetris
|
|
8452
8911
|
end
|
8453
8912
|
}
|
8454
8913
|
end
|
8914
|
+
|
8455
8915
|
menu_item('About') {
|
8456
8916
|
on_clicked do
|
8457
8917
|
show_about_dialog
|
@@ -8674,7 +9134,7 @@ Mac | Windows | Linux
|
|
8674
9134
|
----|---------|------
|
8675
9135
|
![glimmer-dsl-libui-mac-tic-tac-toe.png](images/glimmer-dsl-libui-mac-tic-tac-toe.png) ![glimmer-dsl-libui-mac-tic-tac-toe-player-o-wins.png](images/glimmer-dsl-libui-mac-tic-tac-toe-player-o-wins.png) ![glimmer-dsl-libui-mac-tic-tac-toe-player-x-wins.png](images/glimmer-dsl-libui-mac-tic-tac-toe-player-x-wins.png) ![glimmer-dsl-libui-mac-tic-tac-toe-draw.png](images/glimmer-dsl-libui-mac-tic-tac-toe-draw.png) | ![glimmer-dsl-libui-windows-tic-tac-toe.png](images/glimmer-dsl-libui-windows-tic-tac-toe.png) ![glimmer-dsl-libui-windows-tic-tac-toe-player-o-wins.png](images/glimmer-dsl-libui-windows-tic-tac-toe-player-o-wins.png) ![glimmer-dsl-libui-windows-tic-tac-toe-player-x-wins.png](images/glimmer-dsl-libui-windows-tic-tac-toe-player-x-wins.png) ![glimmer-dsl-libui-windows-tic-tac-toe-draw.png](images/glimmer-dsl-libui-windows-tic-tac-toe-draw.png) | ![glimmer-dsl-libui-linux-tic-tac-toe.png](images/glimmer-dsl-libui-linux-tic-tac-toe.png) ![glimmer-dsl-libui-linux-tic-tac-toe-player-o-wins.png](images/glimmer-dsl-libui-linux-tic-tac-toe-player-o-wins.png) ![glimmer-dsl-libui-linux-tic-tac-toe-player-x-wins.png](images/glimmer-dsl-libui-linux-tic-tac-toe-player-x-wins.png) ![glimmer-dsl-libui-linux-tic-tac-toe-draw.png](images/glimmer-dsl-libui-linux-tic-tac-toe-draw.png)
|
8676
9136
|
|
8677
|
-
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
9137
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
8678
9138
|
|
8679
9139
|
```ruby
|
8680
9140
|
require 'glimmer-dsl-libui'
|
@@ -8720,6 +9180,7 @@ class TicTacToe
|
|
8720
9180
|
text(23, 19) {
|
8721
9181
|
string {
|
8722
9182
|
font family: 'Arial', size: OS.mac? ? 20 : 16
|
9183
|
+
# data-bind string property of area text attributed string to tic tac toe board cell sign
|
8723
9184
|
string <= [@tic_tac_toe_board[row + 1, column + 1], :sign] # board model is 1-based
|
8724
9185
|
}
|
8725
9186
|
}
|
@@ -8753,6 +9214,95 @@ end
|
|
8753
9214
|
TicTacToe.new.launch
|
8754
9215
|
```
|
8755
9216
|
|
9217
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
|
9218
|
+
|
9219
|
+
```ruby
|
9220
|
+
|
9221
|
+
require 'glimmer-dsl-libui'
|
9222
|
+
|
9223
|
+
require_relative "tic_tac_toe/board"
|
9224
|
+
|
9225
|
+
class TicTacToe
|
9226
|
+
include Glimmer
|
9227
|
+
|
9228
|
+
def initialize
|
9229
|
+
@tic_tac_toe_board = Board.new
|
9230
|
+
end
|
9231
|
+
|
9232
|
+
def launch
|
9233
|
+
create_gui
|
9234
|
+
register_observers
|
9235
|
+
@main_window.show
|
9236
|
+
end
|
9237
|
+
|
9238
|
+
def register_observers
|
9239
|
+
observe(@tic_tac_toe_board, :game_status) do |game_status|
|
9240
|
+
display_win_message if game_status == Board::WIN
|
9241
|
+
display_draw_message if game_status == Board::DRAW
|
9242
|
+
end
|
9243
|
+
|
9244
|
+
3.times.map do |row|
|
9245
|
+
3.times.map do |column|
|
9246
|
+
observe(@tic_tac_toe_board[row + 1, column + 1], :sign) do |sign| # board model is 1-based
|
9247
|
+
@cells[row][column].string = sign
|
9248
|
+
end
|
9249
|
+
end
|
9250
|
+
end
|
9251
|
+
end
|
9252
|
+
|
9253
|
+
def create_gui
|
9254
|
+
@main_window = window('Tic-Tac-Toe', 180, 180) {
|
9255
|
+
resizable false
|
9256
|
+
|
9257
|
+
@cells = []
|
9258
|
+
vertical_box {
|
9259
|
+
padded false
|
9260
|
+
|
9261
|
+
3.times.map do |row|
|
9262
|
+
@cells << []
|
9263
|
+
horizontal_box {
|
9264
|
+
padded false
|
9265
|
+
|
9266
|
+
3.times.map do |column|
|
9267
|
+
area {
|
9268
|
+
square(0, 0, 60) {
|
9269
|
+
stroke :black, thickness: 2
|
9270
|
+
}
|
9271
|
+
text(23, 19) {
|
9272
|
+
@cells[row] << string('') {
|
9273
|
+
font family: 'Arial', size: OS.mac? ? 20 : 16
|
9274
|
+
}
|
9275
|
+
}
|
9276
|
+
on_mouse_up do
|
9277
|
+
@tic_tac_toe_board.mark(row + 1, column + 1) # board model is 1-based
|
9278
|
+
end
|
9279
|
+
}
|
9280
|
+
end
|
9281
|
+
}
|
9282
|
+
end
|
9283
|
+
}
|
9284
|
+
}
|
9285
|
+
end
|
9286
|
+
|
9287
|
+
def display_win_message
|
9288
|
+
display_game_over_message("Player #{@tic_tac_toe_board.winning_sign} has won!")
|
9289
|
+
end
|
9290
|
+
|
9291
|
+
def display_draw_message
|
9292
|
+
display_game_over_message("Draw!")
|
9293
|
+
end
|
9294
|
+
|
9295
|
+
def display_game_over_message(message_text)
|
9296
|
+
Glimmer::LibUI.queue_main do
|
9297
|
+
msg_box('Game Over', message_text)
|
9298
|
+
@tic_tac_toe_board.reset!
|
9299
|
+
end
|
9300
|
+
end
|
9301
|
+
end
|
9302
|
+
|
9303
|
+
TicTacToe.new.launch
|
9304
|
+
```
|
9305
|
+
|
8756
9306
|
#### Timer
|
8757
9307
|
|
8758
9308
|
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).
|
@@ -9113,7 +9663,7 @@ These features have been planned or suggested. You might see them in a future ve
|
|
9113
9663
|
is fine, but please isolate to its own commit so I can cherry-pick
|
9114
9664
|
around it.
|
9115
9665
|
|
9116
|
-
Note that the latest development sometimes takes place in [development](https://github.com/AndyObtiva/glimmer-dsl-libui/tree/development) branch (
|
9666
|
+
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)).
|
9117
9667
|
|
9118
9668
|
## Contributors
|
9119
9669
|
|