glimmer-dsl-libui 0.4.8 → 0.4.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -0
  3. data/README.md +1361 -461
  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/snake.rb +19 -10
  25. data/examples/snake2.rb +97 -0
  26. data/examples/tic_tac_toe.rb +1 -0
  27. data/examples/tic_tac_toe2.rb +84 -0
  28. data/glimmer-dsl-libui.gemspec +0 -0
  29. data/lib/glimmer/dsl/libui/control_expression.rb +2 -1
  30. data/lib/glimmer/dsl/libui/shape_expression.rb +2 -2
  31. data/lib/glimmer/dsl/libui/string_expression.rb +2 -1
  32. data/lib/glimmer/libui/attributed_string.rb +3 -2
  33. data/lib/glimmer/libui/control_proxy/checkbox_proxy.rb +1 -2
  34. data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +1 -2
  35. data/lib/glimmer/libui/control_proxy/column/background_color_column_proxy.rb +4 -0
  36. data/lib/glimmer/libui/control_proxy/combobox_proxy.rb +1 -2
  37. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy.rb +1 -2
  38. data/lib/glimmer/libui/control_proxy/editable_combobox_proxy.rb +1 -2
  39. data/lib/glimmer/libui/control_proxy/entry_proxy.rb +1 -2
  40. data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +5 -1
  41. data/lib/glimmer/libui/control_proxy/menu_item_proxy/check_menu_item_proxy.rb +1 -2
  42. data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +1 -2
  43. data/lib/glimmer/libui/control_proxy/multiline_entry_proxy.rb +1 -2
  44. data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +1 -2
  45. data/lib/glimmer/libui/control_proxy/slider_proxy.rb +1 -2
  46. data/lib/glimmer/libui/control_proxy/spinbox_proxy.rb +1 -2
  47. data/lib/glimmer/libui/control_proxy/table_proxy.rb +95 -29
  48. data/lib/glimmer/libui/control_proxy.rb +4 -2
  49. data/lib/glimmer/libui/data_bindable.rb +34 -4
  50. data/lib/glimmer/libui/shape.rb +3 -2
  51. data/lib/glimmer/libui.rb +2 -2
  52. data/lib/glimmer-dsl-libui.rb +1 -0
  53. metadata +12 -2
@@ -0,0 +1,97 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ require_relative 'snake/presenter/grid'
4
+
5
+ class Snake
6
+ include Glimmer
7
+
8
+ CELL_SIZE = 15
9
+ SNAKE_MOVE_DELAY = 0.1
10
+
11
+ def initialize
12
+ @game = Model::Game.new
13
+ @grid = Presenter::Grid.new(@game)
14
+ @game.start
15
+ @keypress_queue = []
16
+ create_gui
17
+ register_observers
18
+ end
19
+
20
+ def launch
21
+ @main_window.show
22
+ end
23
+
24
+ def register_observers
25
+ @game.height.times do |row|
26
+ @game.width.times do |column|
27
+ observe(@grid.cells[row][column], :color) do |new_color|
28
+ @cell_grid[row][column].fill = new_color
29
+ end
30
+ end
31
+ end
32
+
33
+ observe(@game, :over) do |game_over|
34
+ Glimmer::LibUI.queue_main do
35
+ if game_over
36
+ msg_box('Game Over!', "Score: #{@game.score} | High Score: #{@game.high_score}")
37
+ @game.start
38
+ end
39
+ end
40
+ end
41
+
42
+ Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
43
+ unless @game.over?
44
+ process_queued_keypress
45
+ @game.snake.move
46
+ end
47
+ end
48
+ end
49
+
50
+ def process_queued_keypress
51
+ # 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)
52
+ key = @keypress_queue.shift
53
+ case [@game.snake.head.orientation, key]
54
+ in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
55
+ @game.snake.turn_right
56
+ in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
57
+ @game.snake.turn_left
58
+ else
59
+ # No Op
60
+ end
61
+ end
62
+
63
+ def create_gui
64
+ @cell_grid = []
65
+ @main_window = window {
66
+ # data-bind window title to game score, converting it to a title string on read from the model
67
+ title <= [@game, :score, on_read: -> (score) {"Snake (Score: #{@game.score})"}]
68
+ content_size @game.width * CELL_SIZE, @game.height * CELL_SIZE
69
+ resizable false
70
+
71
+ vertical_box {
72
+ padded false
73
+
74
+ @game.height.times do |row|
75
+ @cell_grid << []
76
+ horizontal_box {
77
+ padded false
78
+
79
+ @game.width.times do |column|
80
+ area {
81
+ @cell_grid.last << square(0, 0, CELL_SIZE) {
82
+ fill Presenter::Cell::COLOR_CLEAR
83
+ }
84
+
85
+ on_key_up do |area_key_event|
86
+ @keypress_queue << area_key_event[:ext_key]
87
+ end
88
+ }
89
+ end
90
+ }
91
+ end
92
+ }
93
+ }
94
+ end
95
+ end
96
+
97
+ Snake.new.launch
@@ -41,6 +41,7 @@ class TicTacToe
41
41
  text(23, 19) {
42
42
  string {
43
43
  font family: 'Arial', size: OS.mac? ? 20 : 16
44
+ # data-bind string property of area text attributed string to tic tac toe board cell sign
44
45
  string <= [@tic_tac_toe_board[row + 1, column + 1], :sign] # board model is 1-based
45
46
  }
46
47
  }
@@ -0,0 +1,84 @@
1
+
2
+ require 'glimmer-dsl-libui'
3
+
4
+ require_relative "tic_tac_toe/board"
5
+
6
+ class TicTacToe
7
+ include Glimmer
8
+
9
+ def initialize
10
+ @tic_tac_toe_board = Board.new
11
+ end
12
+
13
+ def launch
14
+ create_gui
15
+ register_observers
16
+ @main_window.show
17
+ end
18
+
19
+ def register_observers
20
+ observe(@tic_tac_toe_board, :game_status) do |game_status|
21
+ display_win_message if game_status == Board::WIN
22
+ display_draw_message if game_status == Board::DRAW
23
+ end
24
+
25
+ 3.times.map do |row|
26
+ 3.times.map do |column|
27
+ observe(@tic_tac_toe_board[row + 1, column + 1], :sign) do |sign| # board model is 1-based
28
+ @cells[row][column].string = sign
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ def create_gui
35
+ @main_window = window('Tic-Tac-Toe', 180, 180) {
36
+ resizable false
37
+
38
+ @cells = []
39
+ vertical_box {
40
+ padded false
41
+
42
+ 3.times.map do |row|
43
+ @cells << []
44
+ horizontal_box {
45
+ padded false
46
+
47
+ 3.times.map do |column|
48
+ area {
49
+ square(0, 0, 60) {
50
+ stroke :black, thickness: 2
51
+ }
52
+ text(23, 19) {
53
+ @cells[row] << string('') {
54
+ font family: 'Arial', size: OS.mac? ? 20 : 16
55
+ }
56
+ }
57
+ on_mouse_up do
58
+ @tic_tac_toe_board.mark(row + 1, column + 1) # board model is 1-based
59
+ end
60
+ }
61
+ end
62
+ }
63
+ end
64
+ }
65
+ }
66
+ end
67
+
68
+ def display_win_message
69
+ display_game_over_message("Player #{@tic_tac_toe_board.winning_sign} has won!")
70
+ end
71
+
72
+ def display_draw_message
73
+ display_game_over_message("Draw!")
74
+ end
75
+
76
+ def display_game_over_message(message_text)
77
+ Glimmer::LibUI.queue_main do
78
+ msg_box('Game Over', message_text)
79
+ @tic_tac_toe_board.reset!
80
+ end
81
+ end
82
+ end
83
+
84
+ TicTacToe.new.launch
Binary file
@@ -39,8 +39,9 @@ module Glimmer
39
39
  end
40
40
 
41
41
  def add_content(parent, keyword, *args, &block)
42
+ options = args.last.is_a?(Hash) ? args.last : {post_add_content: true}
42
43
  super
43
- parent&.post_add_content
44
+ parent&.post_add_content if options[:post_add_content]
44
45
  end
45
46
  end
46
47
  end
@@ -47,10 +47,10 @@ module Glimmer
47
47
  end
48
48
 
49
49
  def add_content(parent, keyword, *args, &block)
50
+ options = args.last.is_a?(Hash) ? args.last : {post_add_content: true}
50
51
  super
51
- parent.post_add_content
52
+ parent&.post_add_content if options[:post_add_content]
52
53
  end
53
-
54
54
  end
55
55
  end
56
56
  end
@@ -46,7 +46,8 @@ module Glimmer
46
46
  end
47
47
 
48
48
  def add_content(parent, keyword, *args, &block)
49
- parent.post_add_content(block)
49
+ options = args.last.is_a?(Hash) ? args.last : {post_add_content: true}
50
+ parent&.post_add_content(block) if options[:post_add_content]
50
51
  end
51
52
  end
52
53
  end
@@ -30,8 +30,9 @@ module Glimmer
30
30
  class AttributedString
31
31
  include DataBindable
32
32
 
33
- attr_reader :keyword, :parent_proxy, :args
33
+ attr_reader :keyword, :parent_proxy, :args, :content_added
34
34
  attr_accessor :block
35
+ alias content_added? content_added
35
36
 
36
37
  def initialize(keyword, parent_proxy, args, &block)
37
38
  @keyword = keyword
@@ -205,7 +206,7 @@ module Glimmer
205
206
  end
206
207
 
207
208
  def content(&block)
208
- Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Libui::StringExpression.new, @keyword, &block)
209
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Libui::StringExpression.new, @keyword, {post_add_content: true}, &block)
209
210
  end
210
211
  end
211
212
  end
@@ -30,8 +30,7 @@ module Glimmer
30
30
  class CheckboxProxy < ControlProxy
31
31
  DEFAULT_TEXT = ''
32
32
 
33
- def data_bind(property, model_binding)
34
- super
33
+ def data_bind_write(property, model_binding)
35
34
  handle_listener('on_toggled') { model_binding.call(checked) } if property == 'checked'
36
35
  end
37
36
 
@@ -114,8 +114,7 @@ module Glimmer
114
114
  super
115
115
  end
116
116
 
117
- def data_bind(property, model_binding)
118
- super
117
+ def data_bind_write(property, model_binding)
119
118
  handle_listener('on_changed') { model_binding.call(color) } if property == 'color'
120
119
  end
121
120
  end
@@ -31,6 +31,10 @@ module Glimmer
31
31
  # Follows the Proxy Design Pattern
32
32
  class BackgroundColorColumnProxy < ControlProxy
33
33
  include Column
34
+
35
+ def name
36
+ 'Background Color'
37
+ end
34
38
  end
35
39
  end
36
40
  end
@@ -64,8 +64,7 @@ module Glimmer
64
64
  alias set_selected_item selected_item
65
65
  alias selected_item= selected_item
66
66
 
67
- def data_bind(property, model_binding)
68
- super # model to view data-binding
67
+ def data_bind_write(property, model_binding)
69
68
  case property
70
69
  when 'selected'
71
70
  handle_listener('on_selected') { model_binding.call(selected) }
@@ -65,8 +65,7 @@ module Glimmer
65
65
  super
66
66
  end
67
67
 
68
- def data_bind(property, model_binding)
69
- super
68
+ def data_bind_write(property, model_binding)
70
69
  handle_listener('on_changed') { model_binding.call(time) } if property == 'time'
71
70
  end
72
71
  end
@@ -40,8 +40,7 @@ module Glimmer
40
40
  alias set_items items
41
41
  alias items= items
42
42
 
43
- def data_bind(property, model_binding)
44
- super
43
+ def data_bind_write(property, model_binding)
45
44
  handle_listener('on_changed') { model_binding.call(text) } if property == 'text'
46
45
  end
47
46
  end
@@ -28,8 +28,7 @@ module Glimmer
28
28
  #
29
29
  # Follows the Proxy Design Pattern
30
30
  class EntryProxy < ControlProxy
31
- def data_bind(property, model_binding)
32
- super
31
+ def data_bind_write(property, model_binding)
33
32
  handle_listener('on_changed') { model_binding.call(text) } if property == 'text'
34
33
  end
35
34
 
@@ -65,7 +65,11 @@ module Glimmer
65
65
  super
66
66
  end
67
67
 
68
- def data_bind(property, model_binding)
68
+ def data_bind_read(property, model_binding)
69
+ # No Op
70
+ end
71
+
72
+ def data_bind_write(property, model_binding)
69
73
  handle_listener('on_changed') { model_binding.call(font) } if property == 'font'
70
74
  end
71
75
  end
@@ -29,8 +29,7 @@ module Glimmer
29
29
  #
30
30
  # Follows the Proxy Design Pattern
31
31
  class CheckMenuItemProxy < MenuItemProxy
32
- def data_bind(property, model_binding)
33
- super
32
+ def data_bind_write(property, model_binding)
34
33
  handle_listener('on_clicked') { model_binding.call(checked) } if property == 'checked'
35
34
  end
36
35
 
@@ -65,8 +65,7 @@ module Glimmer
65
65
  end
66
66
  end
67
67
 
68
- def data_bind(property, model_binding)
69
- super
68
+ def data_bind_write(property, model_binding)
70
69
  handle_listener('on_clicked') { model_binding.call(checked) } if property == 'checked'
71
70
  end
72
71
 
@@ -28,8 +28,7 @@ module Glimmer
28
28
  #
29
29
  # Follows the Proxy Design Pattern
30
30
  class MultilineEntryProxy < ControlProxy
31
- def data_bind(property, model_binding)
32
- super
31
+ def data_bind_write(property, model_binding)
33
32
  handle_listener('on_changed') { model_binding.call(text) } if property == 'text'
34
33
  end
35
34
 
@@ -50,8 +50,7 @@ module Glimmer
50
50
  alias set_selected_item selected_item
51
51
  alias selected_item= selected_item
52
52
 
53
- def data_bind(property, model_binding)
54
- super # model to view data-binding
53
+ def data_bind_write(property, model_binding)
55
54
  case property
56
55
  when 'selected'
57
56
  handle_listener('on_selected') { model_binding.call(selected) }
@@ -28,8 +28,7 @@ module Glimmer
28
28
  #
29
29
  # Follows the Proxy Design Pattern
30
30
  class SliderProxy < ControlProxy
31
- def data_bind(property, model_binding)
32
- super
31
+ def data_bind_write(property, model_binding)
33
32
  handle_listener('on_changed') { model_binding.call(value) } if property == 'value'
34
33
  end
35
34
  end
@@ -28,8 +28,7 @@ module Glimmer
28
28
  #
29
29
  # Follows the Proxy Design Pattern
30
30
  class SpinboxProxy < ControlProxy
31
- def data_bind(property, model_binding)
32
- super
31
+ def data_bind_write(property, model_binding)
33
32
  handle_listener('on_changed') { model_binding.call(value) } if property == 'value'
34
33
  end
35
34
  end
@@ -38,7 +38,7 @@ module Glimmer
38
38
 
39
39
  LISTENERS = ['on_changed', 'on_edited']
40
40
 
41
- attr_reader :model_handler, :model, :table_params, :columns
41
+ attr_reader :model_handler, :model, :table_params, :columns, :column_attributes
42
42
 
43
43
  def initialize(keyword, parent, args, &block)
44
44
  @keyword = keyword
@@ -75,6 +75,7 @@ module Glimmer
75
75
 
76
76
  def destroy
77
77
  super
78
+ @cell_rows_observer&.unobserve(self, :cell_rows, recursive: true)
78
79
  @destroyed = true
79
80
  end
80
81
 
@@ -82,10 +83,11 @@ module Glimmer
82
83
  if rows.nil?
83
84
  @cell_rows
84
85
  else
85
- @cell_rows = rows
86
- @cell_rows.tap do
87
- @last_cell_rows = array_deep_clone(@cell_rows)
88
- Glimmer::DataBinding::Observer.proc do |new_cell_rows|
86
+ if rows != @cell_rows
87
+ @cell_rows = rows
88
+ @cell_rows = @cell_rows.to_a if @cell_rows.is_a?(Enumerator)
89
+ @last_cell_rows ||= array_deep_clone(@cell_rows)
90
+ @cell_rows_observer ||= Glimmer::DataBinding::Observer.proc do |new_cell_rows|
89
91
  if @cell_rows.size < @last_cell_rows.size && @last_cell_rows.include_all?(*@cell_rows)
90
92
  @last_cell_rows.array_diff_indexes(@cell_rows).reverse.each do |row|
91
93
  ::LibUI.table_model_row_deleted(model, row)
@@ -106,8 +108,11 @@ module Glimmer
106
108
  end
107
109
  @last_last_cell_rows = array_deep_clone(@last_cell_rows)
108
110
  @last_cell_rows = array_deep_clone(@cell_rows)
109
- end.observe(self, :cell_rows, recursive: true)
111
+ end.tap do |cell_rows_observer|
112
+ cell_rows_observer.observe(self, :cell_rows, recursive: true)
113
+ end
110
114
  end
115
+ @cell_rows
111
116
  end
112
117
  end
113
118
  alias cell_rows= cell_rows
@@ -119,6 +124,7 @@ module Glimmer
119
124
 
120
125
  def expand(cell_rows)
121
126
  cell_rows.to_a.map do |row|
127
+ row = @column_attributes.map {|attribute| row.send(attribute) } if @column_attributes&.any? && !row.is_a?(Array)
122
128
  row.flatten(1)
123
129
  end
124
130
  end
@@ -134,6 +140,46 @@ module Glimmer
134
140
  alias set_editable editable
135
141
  alias editable? editable
136
142
 
143
+ def data_bind_read(property, model_binding)
144
+ if model_binding.binding_options[:column_attributes].is_a?(Array)
145
+ @column_attributes = model_binding.binding_options[:column_attributes]
146
+ else
147
+ column_attribute_mapping = model_binding.binding_options[:column_attributes].is_a?(Hash) ? model_binding.binding_options[:column_attributes] : {}
148
+ @column_attributes = columns.select {|column| column.is_a?(Column)}.map(&:name).map {|column_name| column_attribute_mapping[column_name] || column_name.underscore}
149
+ end
150
+ model_attribute_observer = model_attribute_observer_registration = nil
151
+ model_attribute_observer = Glimmer::DataBinding::Observer.proc do
152
+ new_value = model_binding.evaluate_property
153
+ new_value = new_value.to_a if new_value.is_a?(Enumerator)
154
+ if model_binding.binding_options[:column_attributes] || (!new_value.empty? && !new_value.first.is_a?(Array))
155
+ @model_attribute_array_observer_registration&.deregister
156
+ @model_attribute_array_observer_registration = model_attribute_observer.observe(new_value, @column_attributes)
157
+ model_attribute_observer.add_dependent(model_attribute_observer_registration => @model_attribute_array_observer_registration)
158
+ end
159
+ # TODO look if multiple notifications are happening as a result of observing array and observing model binding
160
+ send("#{property}=", new_value) unless @last_cell_rows == new_value
161
+ end
162
+ model_attribute_observer_registration = model_attribute_observer.observe(model_binding)
163
+ model_attribute_observer.call # initial update
164
+ data_binding_model_attribute_observer_registrations << model_attribute_observer_registration
165
+ model_attribute_observer
166
+ end
167
+
168
+ def data_bind_write(property, model_binding)
169
+ # TODO ensure writing is happening to models if rows are not arrays
170
+ handle_listener('on_edited') { model_binding.call(cell_rows) } if property == 'cell_rows'
171
+ end
172
+
173
+ def array_deep_clone(array_or_object)
174
+ if array_or_object.is_a?(Array)
175
+ array_or_object.map do |element|
176
+ array_deep_clone(element)
177
+ end
178
+ else
179
+ array_or_object.clone
180
+ end
181
+ end
182
+
137
183
  private
138
184
 
139
185
  def build_control
@@ -162,11 +208,13 @@ module Glimmer
162
208
  when Column::TextColumnProxy, Column::ButtonColumnProxy, Column::TextColorColumnProxy, :text
163
209
  ::LibUI.new_table_value_string((expanded_cell_rows[row] && expanded_cell_rows[row][column]).to_s)
164
210
  when Column::ImageColumnProxy, Column::ImageTextColumnProxy, Column::ImageTextColorColumnProxy
165
- if OS.windows? && row == cell_rows.count
166
- ::LibUI.new_table_value_image((expanded_cell_rows[row - 1] && (expanded_cell_rows[row - 1][column].respond_to?(:libui) ? expanded_cell_rows[row - 1][column].libui : expanded_cell_rows[row - 1][column])))
167
- else
168
- ::LibUI.new_table_value_image((expanded_cell_rows[row] && (expanded_cell_rows[row][column].respond_to?(:libui) ? expanded_cell_rows[row][column].libui : expanded_cell_rows[row][column])))
169
- end
211
+ # TODO refactor to eliminate redundancy and share similar code
212
+ row = row - 1 if OS.windows? && row == cell_rows.count
213
+ img = expanded_cell_rows[row][column]
214
+ img = ControlProxy::ImageProxy.new('image', nil, img) if img.is_a?(Array)
215
+ img = ControlProxy::ImageProxy.new('image', nil, [img]) if img.is_a?(String)
216
+ img = img.respond_to?(:libui) ? img.libui : img
217
+ ::LibUI.new_table_value_image(img)
170
218
  when Column::CheckboxColumnProxy, Column::CheckboxTextColumnProxy, Column::CheckboxTextColorColumnProxy
171
219
  ::LibUI.new_table_value_int(((expanded_cell_rows[row] && (expanded_cell_rows[row][column] == 1 || expanded_cell_rows[row][column].to_s.strip.downcase == 'true' ? 1 : 0))) || 0)
172
220
  when Column::ProgressBarColumnProxy
@@ -193,28 +241,56 @@ module Glimmer
193
241
  when Column::TextColumnProxy
194
242
  column = @columns[column].index
195
243
  @cell_rows[row] ||= []
196
- @cell_rows[row][column] = ::LibUI.table_value_string(val).to_s
244
+ if @cell_rows[row].is_a?(Array)
245
+ @cell_rows[row][column] = ::LibUI.table_value_string(val).to_s
246
+ else
247
+ attribute = @column_attributes[column]
248
+ @cell_rows[row].send("#{attribute}=", ::LibUI.table_value_string(val).to_s)
249
+ end
197
250
  when Column::TextColorColumnProxy
198
251
  column = @columns[column].index
199
252
  @cell_rows[row] ||= []
200
- @cell_rows[row][column] ||= []
201
- @cell_rows[row][column][0] = ::LibUI.table_value_string(val).to_s
253
+ if @cell_rows[row].is_a?(Array)
254
+ @cell_rows[row][column] ||= []
255
+ @cell_rows[row][column][0] = ::LibUI.table_value_string(val).to_s
256
+ else
257
+ attribute = @column_attributes[column]
258
+ @cell_rows[row].send("#{attribute}=", []) unless @cell_rows[row].send(attribute)
259
+ @cell_rows[row].send(attribute)[0] = ::LibUI.table_value_string(val).to_s
260
+ end
202
261
  when :text
203
262
  column = @columns[column - 1].index
204
263
  @cell_rows[row] ||= []
205
- @cell_rows[row][column] ||= []
206
- @cell_rows[row][column][1] = ::LibUI.table_value_string(val).to_s
264
+ if @cell_rows[row].is_a?(Array)
265
+ @cell_rows[row][column] ||= []
266
+ @cell_rows[row][column][1] = ::LibUI.table_value_string(val).to_s
267
+ else
268
+ attribute = @column_attributes[column]
269
+ @cell_rows[row].send("#{attribute}=", []) unless @cell_rows[row].send(attribute)
270
+ @cell_rows[row].send(attribute)[1] = ::LibUI.table_value_string(val).to_s
271
+ end
207
272
  when Column::ButtonColumnProxy
208
273
  @columns[column].notify_listeners(:on_clicked, row)
209
274
  when Column::CheckboxColumnProxy
210
275
  column = @columns[column].index
211
276
  @cell_rows[row] ||= []
212
- @cell_rows[row][column] = ::LibUI.table_value_int(val).to_i == 1
277
+ if @cell_rows[row].is_a?(Array)
278
+ @cell_rows[row][column] = ::LibUI.table_value_int(val).to_i == 1
279
+ else
280
+ attribute = @column_attributes[column]
281
+ @cell_rows[row].send("#{attribute}=", ::LibUI.table_value_int(val).to_i == 1)
282
+ end
213
283
  when Column::CheckboxTextColumnProxy
214
284
  column = @columns[column].index
215
285
  @cell_rows[row] ||= []
216
- @cell_rows[row][column] ||= []
217
- @cell_rows[row][column][0] = ::LibUI.table_value_int(val).to_i == 1
286
+ if @cell_rows[row].is_a?(Array)
287
+ @cell_rows[row][column] ||= []
288
+ @cell_rows[row][column][0] = ::LibUI.table_value_int(val).to_i == 1
289
+ else
290
+ attribute = @column_attributes[column]
291
+ @cell_rows[row].send("#{attribute}=", []) unless @cell_rows[row].send(attribute)
292
+ @cell_rows[row].send(attribute)[0] = ::LibUI.table_value_int(val).to_i == 1
293
+ end
218
294
  end
219
295
  on_edited.each {|listener| listener.call(row, @cell_rows[row])}
220
296
  end
@@ -238,16 +314,6 @@ module Glimmer
238
314
  @next_column_index ||= -1
239
315
  @next_column_index += 1
240
316
  end
241
-
242
- def array_deep_clone(array_or_object)
243
- if array_or_object.is_a?(Array)
244
- array_or_object.map do |element|
245
- array_deep_clone(element)
246
- end
247
- else
248
- array_or_object.clone
249
- end
250
- end
251
317
  end
252
318
  end
253
319
  end
@@ -126,7 +126,8 @@ module Glimmer
126
126
  ]
127
127
 
128
128
  # libui returns the contained LibUI object
129
- attr_reader :parent_proxy, :libui, :args, :keyword, :block
129
+ attr_reader :parent_proxy, :libui, :args, :keyword, :block, :content_added
130
+ alias content_added? content_added
130
131
 
131
132
  def initialize(keyword, parent, args, &block)
132
133
  @keyword = keyword
@@ -288,6 +289,7 @@ module Glimmer
288
289
  end
289
290
 
290
291
  def destroy
292
+ data_binding_model_attribute_observer_registrations.each(&:deregister)
291
293
  if parent_proxy.nil?
292
294
  default_destroy
293
295
  else
@@ -337,7 +339,7 @@ module Glimmer
337
339
  alias visible= visible
338
340
 
339
341
  def content(&block)
340
- Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Libui::ControlExpression.new, @keyword, &block)
342
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Libui::ControlExpression.new, @keyword, {post_add_content: @content_added}, &block)
341
343
  end
342
344
 
343
345
  private