glimmer-dsl-libui 0.4.9 → 0.4.13

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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -0
  3. data/README.md +1330 -487
  4. data/VERSION +1 -1
  5. data/examples/basic_table_button.rb +54 -30
  6. data/examples/basic_table_button2.rb +34 -0
  7. data/examples/basic_table_color.rb +104 -26
  8. data/examples/basic_table_color2.rb +2 -14
  9. data/examples/basic_table_color3.rb +37 -0
  10. data/examples/basic_table_image.rb +1 -1
  11. data/examples/basic_table_image2.rb +2 -14
  12. data/examples/basic_table_image3.rb +44 -0
  13. data/examples/basic_table_image_text.rb +1 -2
  14. data/examples/basic_table_image_text2.rb +2 -13
  15. data/examples/basic_table_image_text3.rb +44 -0
  16. data/examples/cpu_percentage.rb +36 -0
  17. data/examples/editable_table.rb +1 -1
  18. data/examples/form_table.rb +21 -17
  19. data/examples/form_table2.rb +104 -85
  20. data/examples/form_table3.rb +113 -0
  21. data/examples/form_table4.rb +110 -0
  22. data/examples/form_table5.rb +94 -0
  23. data/examples/meta_example.rb +6 -4
  24. data/examples/snake2.rb +97 -0
  25. data/examples/tic_tac_toe.rb +1 -0
  26. data/examples/tic_tac_toe2.rb +84 -0
  27. data/glimmer-dsl-libui.gemspec +0 -0
  28. data/lib/glimmer/dsl/libui/control_expression.rb +2 -1
  29. data/lib/glimmer/dsl/libui/shape_expression.rb +2 -2
  30. data/lib/glimmer/dsl/libui/string_expression.rb +2 -1
  31. data/lib/glimmer/libui/attributed_string.rb +3 -2
  32. data/lib/glimmer/libui/control_proxy/column/background_color_column_proxy.rb +4 -0
  33. data/lib/glimmer/libui/control_proxy/image_proxy.rb +16 -0
  34. data/lib/glimmer/libui/control_proxy/table_proxy.rb +95 -29
  35. data/lib/glimmer/libui/control_proxy.rb +4 -2
  36. data/lib/glimmer/libui/data_bindable.rb +8 -3
  37. data/lib/glimmer/libui/shape.rb +3 -2
  38. data/lib/glimmer/libui.rb +2 -2
  39. data/lib/glimmer-dsl-libui.rb +1 -0
  40. metadata +12 -2
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.9
1
+ 0.4.13
@@ -1,34 +1,58 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'glimmer-dsl-libui'
4
2
 
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
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
- 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
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
@@ -1,29 +1,107 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'glimmer-dsl-libui'
4
2
 
5
- include Glimmer
6
-
7
- img = image(File.expand_path('../icons/glimmer.png', __dir__), 24, 24)
8
-
9
- data = [
10
- [['cat', :red] , ['meow', :blue] , [true, 'mammal', :green], [img, 'Glimmer', :dark_blue], {r: 255, g: 120, b: 0, a: 0.5}],
11
- [['dog', :yellow] , ['woof', {r: 240, g: 32, b: 32}] , [true, 'mammal', :green], [img, 'Glimmer', :dark_blue], :skyblue],
12
- [['chicken', :beige], ['cock-a-doodle-doo', :blue] , [false, 'mammal', :red] , [img, 'Glimmer', :beige], {r: 5, g: 120, b: 110}],
13
- [['horse', :purple] , ['neigh', {r: 240, g: 32, b: 32}], [true, 'mammal', :green], [img, 'Glimmer', :dark_blue], '13a1fb'],
14
- [['cow', :gray] , ['moo', :blue] , [true, 'mammal', :green], [img, 'Glimmer', :brown], 0x12ff02]
15
- ]
16
-
17
- window('Animals', 500, 200) {
18
- horizontal_box {
19
- table {
20
- text_color_column('Animal')
21
- text_color_column('Sound')
22
- checkbox_text_color_column('Description')
23
- image_text_color_column('GUI')
24
- background_color_column('Mammal')
3
+ class BasicTableColor
4
+ Animal = Struct.new(:name, :sound, :mammal)
5
+
6
+ class AnimalPresenter < Animal
7
+ def name_color
8
+ color = case name
9
+ when 'cat'
10
+ :red
11
+ when 'dog'
12
+ :yellow
13
+ when 'chicken'
14
+ :beige
15
+ when 'horse'
16
+ :purple
17
+ when 'cow'
18
+ :gray
19
+ end
20
+ [name, color]
21
+ end
22
+
23
+ def sound_color
24
+ color = case name
25
+ when 'cat', 'chicken', 'cow'
26
+ :blue
27
+ when 'dog', 'horse'
28
+ {r: 240, g: 32, b: 32}
29
+ end
30
+ [sound, color]
31
+ end
32
+
33
+ def mammal_description_color
34
+ color = case name
35
+ when 'cat', 'dog', 'horse', 'cow'
36
+ :green
37
+ when 'chicken'
38
+ :red
39
+ end
40
+ [mammal, 'mammal', color]
41
+ end
42
+
43
+ def image_description_color
44
+ color = case name
45
+ when 'cat', 'dog', 'horse'
46
+ :dark_blue
47
+ when 'chicken'
48
+ :beige
49
+ when 'cow'
50
+ :brown
51
+ end
52
+ [img, 'Glimmer', color]
53
+ end
54
+
55
+ def img
56
+ # scale image to 24x24 (can be passed as file path String only instead of Array to avoid scaling)
57
+ [File.expand_path('../icons/glimmer.png', __dir__), 24, 24]
58
+ end
59
+
60
+ def background_color
61
+ case name
62
+ when 'cat'
63
+ {r: 255, g: 120, b: 0, a: 0.5}
64
+ when 'dog'
65
+ :skyblue
66
+ when 'chicken'
67
+ {r: 5, g: 120, b: 110}
68
+ when 'horse'
69
+ '#13a1fb'
70
+ when 'cow'
71
+ 0x12ff02
72
+ end
73
+ end
74
+ end
75
+
76
+ include Glimmer
77
+
78
+ attr_accessor :animals
79
+
80
+ def initialize
81
+ @animals = [
82
+ AnimalPresenter.new('cat', 'meow', true),
83
+ AnimalPresenter.new('dog', 'woof', true),
84
+ AnimalPresenter.new('chicken', 'cock-a-doodle-doo', false),
85
+ AnimalPresenter.new('horse', 'neigh', true),
86
+ AnimalPresenter.new('cow', 'moo', true),
87
+ ]
88
+ end
89
+
90
+ def launch
91
+ window('Animals', 500, 200) {
92
+ horizontal_box {
93
+ table {
94
+ text_color_column('Animal')
95
+ text_color_column('Sound')
96
+ checkbox_text_color_column('Description')
97
+ image_text_color_column('GUI')
98
+ background_color_column # must always be the last column and always expects data-binding model attribute `background_color` when binding to Array of models
99
+
100
+ cell_rows <= [self, :animals, column_attributes: {'Animal' => :name_color, 'Sound' => :sound_color, 'Description' => :mammal_description_color, 'GUI' => :image_description_color}]
101
+ }
102
+ }
103
+ }.show
104
+ end
105
+ end
25
106
 
26
- cell_rows data
27
- }
28
- }
29
- }.show
107
+ BasicTableColor.new.launch
@@ -1,20 +1,8 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'glimmer-dsl-libui'
4
- require 'chunky_png'
5
2
 
6
3
  include Glimmer
7
4
 
8
- f = File.open(File.expand_path('../icons/glimmer.png', __dir__))
9
- canvas = ChunkyPNG::Canvas.from_io(f)
10
- f.close
11
- canvas.resample_nearest_neighbor!(24, 24)
12
- data = canvas.to_rgba_stream
13
- width = canvas.width
14
- height = canvas.height
15
- img = image {
16
- image_part(data, width, height, width * 4)
17
- }
5
+ img = [File.expand_path('../icons/glimmer.png', __dir__), 24, 24] # scales image to 24x24 (can be passed as file path String only instead of Array to avoid scaling)
18
6
 
19
7
  data = [
20
8
  [['cat', :red] , ['meow', :blue] , [true, 'mammal', :green], [img, 'Glimmer', :dark_blue], {r: 255, g: 120, b: 0, a: 0.5}],
@@ -31,7 +19,7 @@ window('Animals', 500, 200) {
31
19
  text_color_column('Sound')
32
20
  checkbox_text_color_column('Description')
33
21
  image_text_color_column('GUI')
34
- background_color_column('Mammal')
22
+ background_color_column # must be the last column
35
23
 
36
24
  cell_rows data
37
25
  }
@@ -0,0 +1,37 @@
1
+ require 'glimmer-dsl-libui'
2
+ require 'chunky_png'
3
+
4
+ include Glimmer
5
+
6
+ f = File.open(File.expand_path('../icons/glimmer.png', __dir__))
7
+ canvas = ChunkyPNG::Canvas.from_io(f)
8
+ f.close
9
+ canvas.resample_nearest_neighbor!(24, 24)
10
+ data = canvas.to_rgba_stream
11
+ width = canvas.width
12
+ height = canvas.height
13
+ img = image {
14
+ image_part(data, width, height, width * 4)
15
+ }
16
+
17
+ data = [
18
+ [['cat', :red] , ['meow', :blue] , [true, 'mammal', :green], [img, 'Glimmer', :dark_blue], {r: 255, g: 120, b: 0, a: 0.5}],
19
+ [['dog', :yellow] , ['woof', {r: 240, g: 32, b: 32}] , [true, 'mammal', :green], [img, 'Glimmer', :dark_blue], :skyblue],
20
+ [['chicken', :beige], ['cock-a-doodle-doo', :blue] , [false, 'mammal', :red] , [img, 'Glimmer', :beige], {r: 5, g: 120, b: 110}],
21
+ [['horse', :purple] , ['neigh', {r: 240, g: 32, b: 32}], [true, 'mammal', :green], [img, 'Glimmer', :dark_blue], '13a1fb'],
22
+ [['cow', :gray] , ['moo', :blue] , [true, 'mammal', :green], [img, 'Glimmer', :brown], 0x12ff02]
23
+ ]
24
+
25
+ window('Animals', 500, 200) {
26
+ horizontal_box {
27
+ table {
28
+ text_color_column('Animal')
29
+ text_color_column('Sound')
30
+ checkbox_text_color_column('Description')
31
+ image_text_color_column('GUI')
32
+ background_color_column('Mammal')
33
+
34
+ cell_rows data
35
+ }
36
+ }
37
+ }.show
@@ -12,7 +12,7 @@ IMAGE_ROWS = []
12
12
  50.times do |i|
13
13
  url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', (i + 1))
14
14
  puts "Processing Image: #{url}"; $stdout.flush # for Windows
15
- IMAGE_ROWS << [image(url)] # array of one column cell
15
+ IMAGE_ROWS << [url] # array of one column cell
16
16
  rescue StandardError => e
17
17
  warn url, e.message
18
18
  end
@@ -4,8 +4,6 @@
4
4
  # This example displays images that can be freely downloaded from the Studio Ghibli website.
5
5
 
6
6
  require 'glimmer-dsl-libui'
7
- require 'chunky_png'
8
- require 'open-uri'
9
7
 
10
8
  include Glimmer
11
9
 
@@ -13,18 +11,8 @@ IMAGE_ROWS = []
13
11
 
14
12
  50.times do |i|
15
13
  url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', (i + 1))
16
- puts "Processing Image: #{url}"
17
- $stdout.flush # for Windows
18
- f = URI.open(url)
19
- canvas = ChunkyPNG::Canvas.from_io(f)
20
- f.close
21
- data = canvas.to_rgba_stream
22
- width = canvas.width
23
- height = canvas.height
24
- img = image {
25
- image_part(data, width, height, width * 4)
26
- }
27
- IMAGE_ROWS << [img] # array of one column cell
14
+ puts "Processing Image: #{url}"; $stdout.flush # for Windows
15
+ IMAGE_ROWS << [image(url)] # array of one column cell
28
16
  rescue StandardError => e
29
17
  warn url, e.message
30
18
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # NOTE:
4
+ # This example displays images that can be freely downloaded from the Studio Ghibli website.
5
+
6
+ require 'glimmer-dsl-libui'
7
+ require 'chunky_png'
8
+ require 'open-uri'
9
+
10
+ include Glimmer
11
+
12
+ IMAGE_ROWS = []
13
+
14
+ 50.times do |i|
15
+ url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', (i + 1))
16
+ puts "Processing Image: #{url}"
17
+ $stdout.flush # for Windows
18
+ f = URI.open(url)
19
+ canvas = ChunkyPNG::Canvas.from_io(f)
20
+ f.close
21
+ data = canvas.to_rgba_stream
22
+ width = canvas.width
23
+ height = canvas.height
24
+ img = image {
25
+ image_part(data, width, height, width * 4)
26
+ }
27
+ IMAGE_ROWS << [img] # array of one column cell
28
+ rescue StandardError => e
29
+ warn url, e.message
30
+ end
31
+
32
+ window('The Red Turtle', 310, 350, false) {
33
+ horizontal_box {
34
+ table {
35
+ image_column('www.ghibli.jp/works/red-turtle')
36
+
37
+ cell_rows IMAGE_ROWS
38
+ }
39
+ }
40
+
41
+ on_closing do
42
+ puts 'Bye Bye'
43
+ end
44
+ }.show
@@ -13,8 +13,7 @@ IMAGE_ROWS = []
13
13
  url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', (i + 1))
14
14
  puts "Processing Image: #{url}"; $stdout.flush # for Windows
15
15
  text = url.sub('https://www.ghibli.jp/gallery/thumb-redturtle', '').sub('.png', '')
16
- img = image(url)
17
- IMAGE_ROWS << [[img, text], [img, text]] # cell values are dual-element arrays
16
+ IMAGE_ROWS << [[url, text], [url, text]] # cell values are dual-element arrays
18
17
  rescue StandardError => e
19
18
  warn url, e.message
20
19
  end
@@ -4,8 +4,6 @@
4
4
  # This example displays images that can be freely downloaded from the Studio Ghibli website.
5
5
 
6
6
  require 'glimmer-dsl-libui'
7
- require 'chunky_png'
8
- require 'open-uri'
9
7
 
10
8
  include Glimmer
11
9
 
@@ -13,18 +11,9 @@ IMAGE_ROWS = []
13
11
 
14
12
  5.times do |i|
15
13
  url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', (i + 1))
16
- puts "Processing Image: #{url}"
17
- $stdout.flush # for Windows
18
- f = URI.open(url)
19
- canvas = ChunkyPNG::Canvas.from_io(f)
20
- f.close
21
- data = canvas.to_rgba_stream
22
- width = canvas.width
23
- height = canvas.height
24
- img = image {
25
- image_part(data, width, height, width * 4)
26
- }
14
+ puts "Processing Image: #{url}"; $stdout.flush # for Windows
27
15
  text = url.sub('https://www.ghibli.jp/gallery/thumb-redturtle', '').sub('.png', '')
16
+ img = image(url)
28
17
  IMAGE_ROWS << [[img, text], [img, text]] # cell values are dual-element arrays
29
18
  rescue StandardError => e
30
19
  warn url, e.message
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # NOTE:
4
+ # This example displays images that can be freely downloaded from the Studio Ghibli website.
5
+
6
+ require 'glimmer-dsl-libui'
7
+ require 'chunky_png'
8
+ require 'open-uri'
9
+
10
+ include Glimmer
11
+
12
+ IMAGE_ROWS = []
13
+
14
+ 5.times do |i|
15
+ url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', (i + 1))
16
+ puts "Processing Image: #{url}"
17
+ $stdout.flush # for Windows
18
+ f = URI.open(url)
19
+ canvas = ChunkyPNG::Canvas.from_io(f)
20
+ f.close
21
+ data = canvas.to_rgba_stream
22
+ width = canvas.width
23
+ height = canvas.height
24
+ img = image {
25
+ image_part(data, width, height, width * 4)
26
+ }
27
+ text = url.sub('https://www.ghibli.jp/gallery/thumb-redturtle', '').sub('.png', '')
28
+ IMAGE_ROWS << [[img, text], [img, text]] # cell values are dual-element arrays
29
+ rescue StandardError => e
30
+ warn url, e.message
31
+ end
32
+
33
+ window('The Red Turtle', 670, 350) {
34
+ horizontal_box {
35
+ table {
36
+ image_text_column('image/number')
37
+ image_text_column('image/number (editable)') {
38
+ editable true
39
+ }
40
+
41
+ cell_rows IMAGE_ROWS
42
+ }
43
+ }
44
+ }.show
@@ -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, 50) {
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
@@ -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}"
@@ -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
- @data = [
10
- ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO', '80014'],
11
- ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA', '02101'],
12
- ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL', '60007'],
13
- ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA', '98101'],
14
- ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA', '90001'],
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
- @data << new_row # automatically inserts a row into the table due to implicit data-binding
61
- @unfiltered_data = @data.dup
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
- text <=> [self, :filter_value, # bidirectional data-binding of text to self.filter_value with after_write option
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
- @unfiltered_data ||= @data.dup
78
+ @unfiltered_contacts ||= @contacts.dup
76
79
  # Unfilter first to remove any previous filters
77
- @data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
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
- @data.filter! do |row_data| # affects table indirectly through implicit data-binding
81
- row_data.any? do |cell|
82
- cell.to_s.downcase.include?(filter_value.downcase)
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
- cell_rows @data # implicit data-binding
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}"