glimmer-dsl-libui 0.4.7 → 0.4.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/README.md +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
|
[](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
|
@@ -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
|
+
 |  | 
|
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
|
-

|
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
|
+

|
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
|
 |  | 
|
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
|
-
  |  
|
921
|
+
Mac | Windows | Linux
|
922
|
+
----|---------|------
|
923
|
+
  |   |  
|
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
|

|
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
|
  |   |  
|
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
|
-
  |  
|
3615
|
+
Mac | Windows | Linux
|
3616
|
+
----|---------|------
|
3617
|
+
  |   |  
|
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
|
-
 | 
|
4956
|
+
Mac | Windows | Linux
|
4957
|
+
----|---------|------
|
4958
|
+
 |  | 
|
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
|
+
 |  | 
|
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
|
+
   |    |   
|
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
|
-
   |    |   
|
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
|
  |   |  
|
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
|
    |     |    
|
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
|
|