glimmer-dsl-libui 0.4.10 → 0.4.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -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