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/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.11
|
@@ -1,34 +1,58 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
require 'glimmer-dsl-libui'
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
3
|
+
class BasicTableButton
|
4
|
+
BasicAnimal = Struct.new(:name, :sound)
|
5
|
+
|
6
|
+
class Animal < BasicAnimal
|
7
|
+
def action
|
8
|
+
'delete'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
include Glimmer
|
13
|
+
|
14
|
+
attr_accessor :animals
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@animals = [
|
18
|
+
Animal.new('cat', 'meow'),
|
19
|
+
Animal.new('dog', 'woof'),
|
20
|
+
Animal.new('chicken', 'cock-a-doodle-doo'),
|
21
|
+
Animal.new('horse', 'neigh'),
|
22
|
+
Animal.new('cow', 'moo'),
|
23
|
+
]
|
24
|
+
end
|
25
|
+
|
26
|
+
def launch
|
27
|
+
window('Animal sounds', 400, 200) {
|
28
|
+
horizontal_box {
|
29
|
+
table {
|
30
|
+
text_column('Animal')
|
31
|
+
text_column('Description')
|
32
|
+
button_column('Action') {
|
33
|
+
on_clicked do |row|
|
34
|
+
# Option 1: direct data deletion is the simpler solution
|
35
|
+
# @animals.delete_at(row) # automatically deletes actual table row due to explicit data-binding
|
36
|
+
|
37
|
+
# Option 2: cloning only to demonstrate table row deletion upon explicit setting of animals attribute (cloning is not recommended beyond demonstrating this point)
|
38
|
+
new_animals = @animals.clone
|
39
|
+
new_animals.delete_at(row)
|
40
|
+
self.animals = new_animals # automatically loses deleted table row due to explicit data-binding
|
41
|
+
end
|
42
|
+
}
|
43
|
+
|
44
|
+
|
45
|
+
cell_rows <= [self, :animals, column_attributes: {'Animal' => :name, 'Description' => :sound}]
|
46
|
+
|
47
|
+
# explicit unidirectional data-binding of table cell_rows to self.animals
|
48
|
+
on_changed do |row, type, row_data|
|
49
|
+
puts "Row #{row} #{type}: #{row_data}"
|
50
|
+
$stdout.flush
|
51
|
+
end
|
52
|
+
}
|
24
53
|
}
|
54
|
+
}.show
|
55
|
+
end
|
56
|
+
end
|
25
57
|
|
26
|
-
|
27
|
-
|
28
|
-
on_changed do |row, type, row_data|
|
29
|
-
puts "Row #{row} #{type}: #{row_data}"
|
30
|
-
$stdout.flush
|
31
|
-
end
|
32
|
-
}
|
33
|
-
}
|
34
|
-
}.show
|
58
|
+
BasicTableButton.new.launch
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'glimmer-dsl-libui'
|
4
|
+
|
5
|
+
include Glimmer
|
6
|
+
|
7
|
+
data = [
|
8
|
+
%w[cat meow delete],
|
9
|
+
%w[dog woof delete],
|
10
|
+
%w[chicken cock-a-doodle-doo delete],
|
11
|
+
%w[horse neigh delete],
|
12
|
+
%w[cow moo delete]
|
13
|
+
]
|
14
|
+
|
15
|
+
window('Animal sounds', 400, 200) {
|
16
|
+
horizontal_box {
|
17
|
+
table {
|
18
|
+
text_column('Animal')
|
19
|
+
text_column('Description')
|
20
|
+
button_column('Action') {
|
21
|
+
on_clicked do |row|
|
22
|
+
data.delete_at(row) # automatically deletes actual table row due to implicit data-binding
|
23
|
+
end
|
24
|
+
}
|
25
|
+
|
26
|
+
cell_rows data # implicit data-binding
|
27
|
+
|
28
|
+
on_changed do |row, type, row_data|
|
29
|
+
puts "Row #{row} #{type}: #{row_data}"
|
30
|
+
$stdout.flush
|
31
|
+
end
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}.show
|
data/examples/button_counter.rb
CHANGED
@@ -13,7 +13,8 @@ class ButtonCounter
|
|
13
13
|
window('Hello, Button!', 190, 20) {
|
14
14
|
vertical_box {
|
15
15
|
button {
|
16
|
-
|
16
|
+
# data-bind button text to self count, converting to string on read.
|
17
|
+
text <= [self, :count, on_read: ->(count) {"Count: #{count}"}]
|
17
18
|
|
18
19
|
on_clicked do
|
19
20
|
self.count += 1
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'glimmer-dsl-libui'
|
2
|
+
require 'bigdecimal'
|
3
|
+
|
4
|
+
include Glimmer
|
5
|
+
|
6
|
+
data = [
|
7
|
+
['CPU', '0%', 0],
|
8
|
+
]
|
9
|
+
|
10
|
+
Glimmer::LibUI.timer(1) do
|
11
|
+
cpu_percentage_value = nil
|
12
|
+
if OS.windows?
|
13
|
+
cpu_percentage_raw_value = `wmic cpu get loadpercentage`
|
14
|
+
cpu_percentage_value = cpu_percentage_raw_value.split("\n")[2].to_i
|
15
|
+
elsif OS.mac?
|
16
|
+
cpu_percentage_value = `ps -A -o %cpu | awk '{s+=$1} END {print s}'`.to_i
|
17
|
+
elsif OS.linux?
|
18
|
+
stats = `top -n 1`
|
19
|
+
idle_percentage = stats.split("\n")[2].match(/ni,.* (.*) .*id/)[1]
|
20
|
+
cpu_percentage_value = (BigDecimal(100) - BigDecimal(idle_percentage)).to_i
|
21
|
+
end
|
22
|
+
data[0][1] = "#{cpu_percentage_value}%"
|
23
|
+
data[0][2] = cpu_percentage_value
|
24
|
+
end
|
25
|
+
|
26
|
+
window('CPU Percentage', 400, 200) {
|
27
|
+
vertical_box {
|
28
|
+
table {
|
29
|
+
text_column('Name')
|
30
|
+
text_column('Value')
|
31
|
+
progress_bar_column('Percentage')
|
32
|
+
|
33
|
+
cell_rows data # implicit data-binding
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}.show
|
data/examples/editable_table.rb
CHANGED
@@ -18,8 +18,8 @@ window('Editable animal sounds', 300, 200) {
|
|
18
18
|
text_column('Animal')
|
19
19
|
text_column('Description')
|
20
20
|
|
21
|
-
cell_rows data
|
22
21
|
editable true
|
22
|
+
cell_rows data
|
23
23
|
|
24
24
|
on_changed do |row, type, row_data| # fires on all changes (even ones happening through data array)
|
25
25
|
puts "Row #{row} #{type}: #{row_data}"
|
data/examples/form_table.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
require 'glimmer-dsl-libui'
|
2
2
|
|
3
3
|
class FormTable
|
4
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
5
|
+
|
4
6
|
include Glimmer
|
5
7
|
|
6
|
-
attr_accessor :name, :email, :phone, :city, :state, :filter_value
|
8
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
7
9
|
|
8
10
|
def initialize
|
9
|
-
@
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
@contacts = [
|
12
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
13
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
14
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
15
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
16
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
15
17
|
]
|
16
18
|
end
|
17
19
|
|
@@ -25,7 +27,7 @@ class FormTable
|
|
25
27
|
|
26
28
|
entry {
|
27
29
|
label 'Name'
|
28
|
-
text <=> [self, :name]
|
30
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
29
31
|
}
|
30
32
|
|
31
33
|
entry {
|
@@ -57,8 +59,8 @@ class FormTable
|
|
57
59
|
if new_row.include?('')
|
58
60
|
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
59
61
|
else
|
60
|
-
@
|
61
|
-
@
|
62
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to explicit data-binding
|
63
|
+
@unfiltered_contacts = @contacts.dup
|
62
64
|
self.name = '' # automatically clears name entry through explicit data-binding
|
63
65
|
self.email = ''
|
64
66
|
self.phone = ''
|
@@ -70,16 +72,17 @@ class FormTable
|
|
70
72
|
|
71
73
|
search_entry {
|
72
74
|
stretchy false
|
73
|
-
|
75
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
76
|
+
text <=> [self, :filter_value,
|
74
77
|
after_write: ->(filter_value) { # execute after write to self.filter_value
|
75
|
-
@
|
78
|
+
@unfiltered_contacts ||= @contacts.dup
|
76
79
|
# Unfilter first to remove any previous filters
|
77
|
-
@
|
80
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
78
81
|
# Now, apply filter if entered
|
79
82
|
unless filter_value.empty?
|
80
|
-
@
|
81
|
-
|
82
|
-
|
83
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
84
|
+
contact.members.any? do |attribute|
|
85
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
83
86
|
end
|
84
87
|
end
|
85
88
|
end
|
@@ -94,7 +97,8 @@ class FormTable
|
|
94
97
|
text_column('City')
|
95
98
|
text_column('State')
|
96
99
|
|
97
|
-
|
100
|
+
editable true
|
101
|
+
cell_rows <=> [self, :contacts] # explicit data-binding to Model Array
|
98
102
|
|
99
103
|
on_changed do |row, type, row_data|
|
100
104
|
puts "Row #{row} #{type}: #{row_data}"
|
data/examples/form_table2.rb
CHANGED
@@ -1,93 +1,112 @@
|
|
1
1
|
require 'glimmer-dsl-libui'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
data = [
|
6
|
-
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO', '80014'],
|
7
|
-
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA', '02101'],
|
8
|
-
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL', '60007'],
|
9
|
-
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA', '98101'],
|
10
|
-
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA', '90001'],
|
11
|
-
]
|
12
|
-
|
13
|
-
window('Contacts', 600, 600) { |w|
|
14
|
-
margined true
|
3
|
+
class FormTable
|
4
|
+
Contact = Struct.new(:name, :email, :phone, :city, :state)
|
15
5
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
@
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
label 'City'
|
34
|
-
}
|
35
|
-
|
36
|
-
@state_entry = entry {
|
37
|
-
label 'State'
|
38
|
-
}
|
39
|
-
}
|
40
|
-
|
41
|
-
button('Save Contact') {
|
42
|
-
stretchy false
|
43
|
-
|
44
|
-
on_clicked do
|
45
|
-
new_row = [@name_entry.text, @email_entry.text, @phone_entry.text, @city_entry.text, @state_entry.text]
|
46
|
-
if new_row.include?('')
|
47
|
-
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
48
|
-
else
|
49
|
-
data << new_row # automatically inserts a row into the table due to implicit data-binding
|
50
|
-
@unfiltered_data = data.dup
|
51
|
-
@name_entry.text = ''
|
52
|
-
@email_entry.text = ''
|
53
|
-
@phone_entry.text = ''
|
54
|
-
@city_entry.text = ''
|
55
|
-
@state_entry.text = ''
|
56
|
-
end
|
57
|
-
end
|
58
|
-
}
|
59
|
-
|
60
|
-
search_entry { |se|
|
61
|
-
stretchy false
|
6
|
+
include Glimmer
|
7
|
+
|
8
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@contacts = [
|
12
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
13
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
14
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
15
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
16
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
def launch
|
21
|
+
window('Contacts', 600, 600) { |w|
|
22
|
+
margined true
|
62
23
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
24
|
+
vertical_box {
|
25
|
+
form {
|
26
|
+
stretchy false
|
27
|
+
|
28
|
+
entry {
|
29
|
+
label 'Name'
|
30
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
31
|
+
}
|
32
|
+
|
33
|
+
entry {
|
34
|
+
label 'Email'
|
35
|
+
text <=> [self, :email]
|
36
|
+
}
|
37
|
+
|
38
|
+
entry {
|
39
|
+
label 'Phone'
|
40
|
+
text <=> [self, :phone]
|
41
|
+
}
|
42
|
+
|
43
|
+
entry {
|
44
|
+
label 'City'
|
45
|
+
text <=> [self, :city]
|
46
|
+
}
|
47
|
+
|
48
|
+
entry {
|
49
|
+
label 'State'
|
50
|
+
text <=> [self, :state]
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
button('Save Contact') {
|
55
|
+
stretchy false
|
56
|
+
|
57
|
+
on_clicked do
|
58
|
+
new_row = [name, email, phone, city, state]
|
59
|
+
if new_row.include?('')
|
60
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
61
|
+
else
|
62
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to implicit data-binding
|
63
|
+
@unfiltered_contacts = @contacts.dup
|
64
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
65
|
+
self.email = ''
|
66
|
+
self.phone = ''
|
67
|
+
self.city = ''
|
68
|
+
self.state = ''
|
73
69
|
end
|
74
70
|
end
|
75
|
-
|
76
|
-
|
77
|
-
|
71
|
+
}
|
72
|
+
|
73
|
+
search_entry {
|
74
|
+
stretchy false
|
75
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
76
|
+
text <=> [self, :filter_value,
|
77
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
78
|
+
@unfiltered_contacts ||= @contacts.dup
|
79
|
+
# Unfilter first to remove any previous filters
|
80
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
81
|
+
# Now, apply filter if entered
|
82
|
+
unless filter_value.empty?
|
83
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
84
|
+
contact.members.any? do |attribute|
|
85
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
}
|
90
|
+
]
|
91
|
+
}
|
92
|
+
|
93
|
+
table {
|
94
|
+
text_column('Name')
|
95
|
+
text_column('Email')
|
96
|
+
text_column('Phone')
|
97
|
+
text_column('City')
|
98
|
+
text_column('State/Province')
|
78
99
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
100
|
+
editable true
|
101
|
+
cell_rows <=> [self, :contacts, column_attributes: {'State/Province' => :state}] # explicit data-binding to Model Array with column_attributes mapping for a specific column
|
102
|
+
|
103
|
+
on_changed do |row, type, row_data|
|
104
|
+
puts "Row #{row} #{type}: #{row_data}"
|
105
|
+
end
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}.show
|
109
|
+
end
|
110
|
+
end
|
85
111
|
|
86
|
-
|
87
|
-
|
88
|
-
on_changed do |row, type, row_data|
|
89
|
-
puts "Row #{row} #{type}: #{row_data}"
|
90
|
-
end
|
91
|
-
}
|
92
|
-
}
|
93
|
-
}.show
|
112
|
+
FormTable.new.launch
|
@@ -0,0 +1,113 @@
|
|
1
|
+
|
2
|
+
require 'glimmer-dsl-libui'
|
3
|
+
|
4
|
+
class FormTable
|
5
|
+
Contact = Struct.new(:full_name, :email_address, :phone_number, :city_or_town, :state_or_province)
|
6
|
+
|
7
|
+
include Glimmer
|
8
|
+
|
9
|
+
attr_accessor :contacts, :name, :email, :phone, :city, :state, :filter_value
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@contacts = [
|
13
|
+
Contact.new('Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'),
|
14
|
+
Contact.new('Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'),
|
15
|
+
Contact.new('Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'),
|
16
|
+
Contact.new('Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'),
|
17
|
+
Contact.new('Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'),
|
18
|
+
]
|
19
|
+
end
|
20
|
+
|
21
|
+
def launch
|
22
|
+
window('Contacts', 600, 600) { |w|
|
23
|
+
margined true
|
24
|
+
|
25
|
+
vertical_box {
|
26
|
+
form {
|
27
|
+
stretchy false
|
28
|
+
|
29
|
+
entry {
|
30
|
+
label 'Name'
|
31
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
32
|
+
}
|
33
|
+
|
34
|
+
entry {
|
35
|
+
label 'Email'
|
36
|
+
text <=> [self, :email]
|
37
|
+
}
|
38
|
+
|
39
|
+
entry {
|
40
|
+
label 'Phone'
|
41
|
+
text <=> [self, :phone]
|
42
|
+
}
|
43
|
+
|
44
|
+
entry {
|
45
|
+
label 'City'
|
46
|
+
text <=> [self, :city]
|
47
|
+
}
|
48
|
+
|
49
|
+
entry {
|
50
|
+
label 'State'
|
51
|
+
text <=> [self, :state]
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
button('Save Contact') {
|
56
|
+
stretchy false
|
57
|
+
|
58
|
+
on_clicked do
|
59
|
+
new_row = [name, email, phone, city, state]
|
60
|
+
if new_row.include?('')
|
61
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
62
|
+
else
|
63
|
+
@contacts << Contact.new(*new_row) # automatically inserts a row into the table due to implicit data-binding
|
64
|
+
@unfiltered_contacts = @contacts.dup
|
65
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
66
|
+
self.email = ''
|
67
|
+
self.phone = ''
|
68
|
+
self.city = ''
|
69
|
+
self.state = ''
|
70
|
+
end
|
71
|
+
end
|
72
|
+
}
|
73
|
+
|
74
|
+
search_entry {
|
75
|
+
stretchy false
|
76
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
77
|
+
text <=> [self, :filter_value,
|
78
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
79
|
+
@unfiltered_contacts ||= @contacts.dup
|
80
|
+
# Unfilter first to remove any previous filters
|
81
|
+
self.contacts = @unfiltered_contacts.dup # affects table indirectly through explicit data-binding
|
82
|
+
# Now, apply filter if entered
|
83
|
+
unless filter_value.empty?
|
84
|
+
self.contacts = @contacts.filter do |contact| # affects table indirectly through explicit data-binding
|
85
|
+
contact.members.any? do |attribute|
|
86
|
+
contact[attribute].to_s.downcase.include?(filter_value.downcase)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
}
|
91
|
+
]
|
92
|
+
}
|
93
|
+
|
94
|
+
table {
|
95
|
+
text_column('Name')
|
96
|
+
text_column('Email')
|
97
|
+
text_column('Phone')
|
98
|
+
text_column('City')
|
99
|
+
text_column('State')
|
100
|
+
|
101
|
+
editable true
|
102
|
+
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
|
103
|
+
|
104
|
+
on_changed do |row, type, row_data|
|
105
|
+
puts "Row #{row} #{type}: #{row_data}"
|
106
|
+
end
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}.show
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
FormTable.new.launch
|