glimmer-dsl-libui 0.4.10 → 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.
@@ -0,0 +1,94 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ include Glimmer
4
+
5
+ data = [
6
+ ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
7
+ ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
8
+ ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
9
+ ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
10
+ ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
11
+ ]
12
+
13
+ window('Contacts', 600, 600) { |w|
14
+ margined true
15
+
16
+ vertical_box {
17
+ form {
18
+ stretchy false
19
+
20
+ @name_entry = entry {
21
+ label 'Name'
22
+ }
23
+
24
+ @email_entry = entry {
25
+ label 'Email'
26
+ }
27
+
28
+ @phone_entry = entry {
29
+ label 'Phone'
30
+ }
31
+
32
+ @city_entry = entry {
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
62
+
63
+ on_changed do
64
+ filter_value = se.text
65
+ @unfiltered_data ||= data.dup
66
+ # Unfilter first to remove any previous filters
67
+ data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
68
+ # Now, apply filter if entered
69
+ unless filter_value.empty?
70
+ data.filter! do |row_data| # affects table indirectly through implicit data-binding
71
+ row_data.any? do |cell|
72
+ cell.to_s.downcase.include?(filter_value.downcase)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ }
78
+
79
+ table {
80
+ text_column('Name')
81
+ text_column('Email')
82
+ text_column('Phone')
83
+ text_column('City')
84
+ text_column('State')
85
+
86
+ editable true
87
+ cell_rows data # implicit data-binding to raw data Array of Arrays
88
+
89
+ on_changed do |row, type, row_data|
90
+ puts "Row #{row} #{type}: #{row_data}"
91
+ end
92
+ }
93
+ }
94
+ }.show
@@ -25,11 +25,11 @@ class MetaExample
25
25
  end
26
26
 
27
27
  def basic_examples
28
- examples.select {|example| example.start_with?('Basic') || ADDITIONAL_BASIC_EXAMPLES.include?(example) }
28
+ examples.select {|example| example.start_with?('Basic') || ADDITIONAL_BASIC_EXAMPLES.include?(example) }.sort
29
29
  end
30
30
 
31
31
  def advanced_examples
32
- examples - basic_examples
32
+ (examples - basic_examples).sort
33
33
  end
34
34
 
35
35
  def examples_with_versions
@@ -69,12 +69,12 @@ class MetaExample
69
69
  command = "#{RbConfig.ruby} -r #{glimmer_dsl_libui_file} #{example} 2>&1"
70
70
  result = ''
71
71
  IO.popen(command) do |f|
72
- sleep(0.0001) # yield to main thread
72
+ sleep(0.00001) # yield to main thread
73
73
  f.each_line do |line|
74
74
  result << line
75
75
  puts line
76
76
  $stdout.flush # for Windows
77
- sleep(0.0001) # yield to main thread
77
+ sleep(0.00001) # yield to main thread
78
78
  end
79
79
  end
80
80
  Glimmer::LibUI.queue_main { msg_box('Error Running Example', result) } if result.downcase.include?('error')
@@ -104,6 +104,7 @@ class MetaExample
104
104
  example = selected_example
105
105
  self.code_text = File.read(file_path_for(example))
106
106
  @version_spinbox.value = 1
107
+ @advanced_example_radio_buttons.selected = -1
107
108
  end
108
109
  }
109
110
 
@@ -124,6 +125,7 @@ class MetaExample
124
125
  example = selected_example
125
126
  self.code_text = File.read(file_path_for(example))
126
127
  @version_spinbox.value = 1
128
+ @basic_example_radio_buttons.selected = -1
127
129
  end
128
130
  }
129
131
 
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
@@ -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.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
@@ -193,28 +239,56 @@ module Glimmer
193
239
  when Column::TextColumnProxy
194
240
  column = @columns[column].index
195
241
  @cell_rows[row] ||= []
196
- @cell_rows[row][column] = ::LibUI.table_value_string(val).to_s
242
+ if @cell_rows[row].is_a?(Array)
243
+ @cell_rows[row][column] = ::LibUI.table_value_string(val).to_s
244
+ else
245
+ attribute = @column_attributes[column]
246
+ @cell_rows[row].send("#{attribute}=", ::LibUI.table_value_string(val).to_s)
247
+ end
197
248
  when Column::TextColorColumnProxy
198
249
  column = @columns[column].index
199
250
  @cell_rows[row] ||= []
200
- @cell_rows[row][column] ||= []
201
- @cell_rows[row][column][0] = ::LibUI.table_value_string(val).to_s
251
+ if @cell_rows[row].is_a?(Array)
252
+ @cell_rows[row][column] ||= []
253
+ @cell_rows[row][column][0] = ::LibUI.table_value_string(val).to_s
254
+ else
255
+ attribute = @column_attributes[column]
256
+ @cell_rows[row].send("#{attribute}=", []) unless @cell_rows[row].send(attribute)
257
+ @cell_rows[row].send(attribute)[0] = ::LibUI.table_value_string(val).to_s
258
+ end
202
259
  when :text
203
260
  column = @columns[column - 1].index
204
261
  @cell_rows[row] ||= []
205
- @cell_rows[row][column] ||= []
206
- @cell_rows[row][column][1] = ::LibUI.table_value_string(val).to_s
262
+ if @cell_rows[row].is_a?(Array)
263
+ @cell_rows[row][column] ||= []
264
+ @cell_rows[row][column][1] = ::LibUI.table_value_string(val).to_s
265
+ else
266
+ attribute = @column_attributes[column]
267
+ @cell_rows[row].send("#{attribute}=", []) unless @cell_rows[row].send(attribute)
268
+ @cell_rows[row].send(attribute)[1] = ::LibUI.table_value_string(val).to_s
269
+ end
207
270
  when Column::ButtonColumnProxy
208
271
  @columns[column].notify_listeners(:on_clicked, row)
209
272
  when Column::CheckboxColumnProxy
210
273
  column = @columns[column].index
211
274
  @cell_rows[row] ||= []
212
- @cell_rows[row][column] = ::LibUI.table_value_int(val).to_i == 1
275
+ if @cell_rows[row].is_a?(Array)
276
+ @cell_rows[row][column] = ::LibUI.table_value_int(val).to_i == 1
277
+ else
278
+ attribute = @column_attributes[column]
279
+ @cell_rows[row].send("#{attribute}=", ::LibUI.table_value_int(val).to_i == 1)
280
+ end
213
281
  when Column::CheckboxTextColumnProxy
214
282
  column = @columns[column].index
215
283
  @cell_rows[row] ||= []
216
- @cell_rows[row][column] ||= []
217
- @cell_rows[row][column][0] = ::LibUI.table_value_int(val).to_i == 1
284
+ if @cell_rows[row].is_a?(Array)
285
+ @cell_rows[row][column] ||= []
286
+ @cell_rows[row][column][0] = ::LibUI.table_value_int(val).to_i == 1
287
+ else
288
+ attribute = @column_attributes[column]
289
+ @cell_rows[row].send("#{attribute}=", []) unless @cell_rows[row].send(attribute)
290
+ @cell_rows[row].send(attribute)[0] = ::LibUI.table_value_int(val).to_i == 1
291
+ end
218
292
  end
219
293
  on_edited.each {|listener| listener.call(row, @cell_rows[row])}
220
294
  end
@@ -238,16 +312,6 @@ module Glimmer
238
312
  @next_column_index ||= -1
239
313
  @next_column_index += 1
240
314
  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
315
  end
252
316
  end
253
317
  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
@@ -30,7 +30,7 @@ module Glimmer
30
30
  #
31
31
  # classes can override data_bind_read to disable read data-binding in rare scenarios that might need it
32
32
  #
33
- # returns model attribute reading observer by default
33
+ # returns model attribute reading observer registration by default
34
34
  def data_bind(property, model_binding)
35
35
  data_bind_read(property, model_binding).tap do
36
36
  data_bind_write(property, model_binding) unless model_binding.binding_options[:read_only]
@@ -46,9 +46,10 @@ module Glimmer
46
46
  new_value = model_binding.evaluate_property
47
47
  send("#{property}=", new_value) unless send(property) == new_value
48
48
  end
49
- model_attribute_observer.observe(model_binding)
49
+ observer_registration = model_attribute_observer.observe(model_binding)
50
50
  model_attribute_observer.call # initial update
51
- model_attribute_observer
51
+ data_binding_model_attribute_observer_registrations << observer_registration
52
+ observer_registration
52
53
  end
53
54
 
54
55
  # Sets up write data-binding (writing to model from view)
@@ -59,6 +60,10 @@ module Glimmer
59
60
  def data_bind_write(property, model_binding)
60
61
  # No Op by default
61
62
  end
63
+
64
+ def data_binding_model_attribute_observer_registrations
65
+ @data_binding_model_attribute_observer_registrations ||= []
66
+ end
62
67
  end
63
68
  end
64
69
  end
@@ -67,7 +67,8 @@ module Glimmer
67
67
  include Parent
68
68
  include DataBindable
69
69
 
70
- attr_reader :parent, :args, :keyword, :block
70
+ attr_reader :parent, :args, :keyword, :block, :content_added
71
+ alias content_added? content_added
71
72
 
72
73
  def initialize(keyword, parent, args, &block)
73
74
  @keyword = keyword
@@ -97,7 +98,7 @@ module Glimmer
97
98
  end
98
99
 
99
100
  def content(&block)
100
- Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Libui::ShapeExpression.new, @keyword, &block)
101
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Libui::ShapeExpression.new, @keyword, {post_add_content: @content_added}, &block)
101
102
  request_auto_redraw
102
103
  end
103
104
 
@@ -30,6 +30,7 @@ require 'color'
30
30
  require 'os'
31
31
  require 'array_include_methods'
32
32
  require 'facets/hash/stringify_keys'
33
+ require 'facets/string/underscore'
33
34
  require 'libui'
34
35
 
35
36
  # Internal requires
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer-dsl-libui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.10
4
+ version: 0.4.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
@@ -232,6 +232,7 @@ files:
232
232
  - examples/basic_scrolling_area.rb
233
233
  - examples/basic_table.rb
234
234
  - examples/basic_table_button.rb
235
+ - examples/basic_table_button2.rb
235
236
  - examples/basic_table_checkbox.rb
236
237
  - examples/basic_table_checkbox_text.rb
237
238
  - examples/basic_table_color.rb
@@ -267,6 +268,9 @@ files:
267
268
  - examples/form2.rb
268
269
  - examples/form_table.rb
269
270
  - examples/form_table2.rb
271
+ - examples/form_table3.rb
272
+ - examples/form_table4.rb
273
+ - examples/form_table5.rb
270
274
  - examples/grid.rb
271
275
  - examples/histogram.rb
272
276
  - examples/histogram2.rb