glimmer-dsl-opal 0.6.1 → 0.7.4
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 +54 -0
- data/README.md +489 -16
- data/VERSION +1 -1
- data/lib/display.rb +31 -0
- data/lib/file.rb +29 -0
- data/lib/glimmer-dsl-opal.rb +31 -3
- data/lib/glimmer-dsl-opal/ext/date.rb +11 -0
- data/lib/glimmer-dsl-opal/ext/struct.rb +37 -0
- data/lib/glimmer-dsl-opal/samples/hello/hello_browser.rb +1 -1
- data/lib/glimmer-dsl-opal/samples/hello/hello_button.rb +46 -0
- data/lib/glimmer-dsl-opal/samples/hello/hello_custom_shell.rb +7 -7
- data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +283 -0
- data/lib/glimmer-dsl-swt.rb +20 -35
- data/lib/glimmer/data_binding/table_items_binding.rb +32 -19
- data/lib/glimmer/dsl/opal/block_property_expression.rb +41 -0
- data/lib/glimmer/dsl/opal/custom_widget_expression.rb +5 -0
- data/lib/glimmer/dsl/opal/dsl.rb +2 -0
- data/lib/glimmer/dsl/opal/widget_expression.rb +6 -2
- data/lib/glimmer/swt/combo_proxy.rb +40 -1
- data/lib/glimmer/swt/composite_proxy.rb +5 -1
- data/lib/glimmer/swt/control_editor.rb +54 -0
- data/lib/glimmer/swt/date_time_proxy.rb +66 -1
- data/lib/glimmer/swt/display_proxy.rb +4 -0
- data/lib/glimmer/swt/font_proxy.rb +4 -4
- data/lib/glimmer/swt/grid_layout_proxy.rb +7 -8
- data/lib/glimmer/swt/label_proxy.rb +11 -3
- data/lib/glimmer/swt/layout_data_proxy.rb +9 -3
- data/lib/glimmer/swt/layout_proxy.rb +1 -1
- data/lib/glimmer/swt/message_box_proxy.rb +3 -10
- data/lib/glimmer/swt/property_owner.rb +2 -2
- data/lib/glimmer/swt/shell_proxy.rb +8 -0
- data/lib/glimmer/swt/table_column_proxy.rb +71 -12
- data/lib/glimmer/swt/table_editor.rb +65 -0
- data/lib/glimmer/swt/table_item_proxy.rb +44 -1
- data/lib/glimmer/swt/table_proxy.rb +579 -12
- data/lib/glimmer/swt/text_proxy.rb +49 -1
- data/lib/glimmer/swt/widget_proxy.rb +106 -17
- data/lib/glimmer/ui/custom_shell.rb +9 -7
- data/lib/net/http.rb +1 -5
- data/lib/os.rb +36 -0
- data/lib/uri.rb +3 -3
- metadata +31 -9
- data/lib/glimmer/data_binding/ext/observable_model.rb +0 -40
@@ -1,28 +1,294 @@
|
|
1
|
+
# Copyright (c) 2020 Andy Maleh
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
1
22
|
require 'glimmer/swt/widget_proxy'
|
2
23
|
require 'glimmer/swt/table_column_proxy'
|
24
|
+
require 'glimmer/swt/table_item_proxy'
|
25
|
+
require 'glimmer/swt/table_editor'
|
3
26
|
|
4
27
|
module Glimmer
|
5
28
|
module SWT
|
6
|
-
class TableProxy <
|
7
|
-
attr_reader :columns, :selection
|
8
|
-
|
29
|
+
class TableProxy < CompositeProxy
|
30
|
+
attr_reader :columns, :selection,
|
31
|
+
:sort_type, :sort_column, :sort_property, :sort_block, :sort_by_block, :additional_sort_properties,
|
32
|
+
:editor, :table_editor
|
33
|
+
attr_accessor :column_properties, :item_count, :data
|
9
34
|
alias items children
|
35
|
+
alias model_binding data
|
36
|
+
|
37
|
+
class << self
|
38
|
+
include Glimmer
|
39
|
+
|
40
|
+
def editors
|
41
|
+
@editors ||= {
|
42
|
+
# ensure editor can work with string keys not just symbols (leave one string in for testing)
|
43
|
+
text: {
|
44
|
+
widget_value_property: :text,
|
45
|
+
editor_gui: lambda do |args, model, property, table_proxy|
|
46
|
+
table_proxy.table_editor.minimumWidth = 90
|
47
|
+
table_proxy.table_editor.minimumHeight = 10
|
48
|
+
table_editor_widget_proxy = text(*args) {
|
49
|
+
text model.send(property)
|
50
|
+
focus true
|
51
|
+
on_focus_lost {
|
52
|
+
table_proxy.finish_edit!
|
53
|
+
}
|
54
|
+
on_key_pressed { |key_event|
|
55
|
+
if key_event.keyCode == swt(:cr)
|
56
|
+
table_proxy.finish_edit!
|
57
|
+
elsif key_event.keyCode == swt(:esc)
|
58
|
+
table_proxy.cancel_edit!
|
59
|
+
end
|
60
|
+
}
|
61
|
+
}
|
62
|
+
# table_editor_widget_proxy.swt_widget.selectAll # TODO select all
|
63
|
+
table_editor_widget_proxy
|
64
|
+
end,
|
65
|
+
},
|
66
|
+
combo: {
|
67
|
+
widget_value_property: :text,
|
68
|
+
editor_gui: lambda do |args, model, property, table_proxy|
|
69
|
+
first_time = true
|
70
|
+
table_proxy.table_editor.minimumWidth = 90
|
71
|
+
table_proxy.table_editor.minimumHeight = 18
|
72
|
+
table_editor_widget_proxy = combo(*args) {
|
73
|
+
items model.send("#{property}_options")
|
74
|
+
text model.send(property)
|
75
|
+
focus true
|
76
|
+
on_focus_lost {
|
77
|
+
table_proxy.finish_edit!
|
78
|
+
}
|
79
|
+
on_key_pressed { |key_event|
|
80
|
+
if key_event.keyCode == swt(:cr)
|
81
|
+
table_proxy.finish_edit!
|
82
|
+
elsif key_event.keyCode == swt(:esc)
|
83
|
+
table_proxy.cancel_edit!
|
84
|
+
end
|
85
|
+
}
|
86
|
+
on_widget_selected {
|
87
|
+
if !OS.windows? || !first_time || first_time && model.send(property) != table_editor_widget_proxy.text
|
88
|
+
table_proxy.finish_edit!
|
89
|
+
end
|
90
|
+
}
|
91
|
+
}
|
92
|
+
table_editor_widget_proxy
|
93
|
+
end,
|
94
|
+
},
|
95
|
+
checkbox: {
|
96
|
+
widget_value_property: :selection,
|
97
|
+
editor_gui: lambda do |args, model, property, table_proxy|
|
98
|
+
first_time = true
|
99
|
+
table_proxy.table_editor.minimumHeight = 25
|
100
|
+
checkbox(*args) {
|
101
|
+
selection model.send(property)
|
102
|
+
focus true
|
103
|
+
on_widget_selected {
|
104
|
+
table_proxy.finish_edit!
|
105
|
+
}
|
106
|
+
on_focus_lost {
|
107
|
+
table_proxy.finish_edit!
|
108
|
+
}
|
109
|
+
on_key_pressed { |key_event|
|
110
|
+
if key_event.keyCode == swt(:cr)
|
111
|
+
table_proxy.finish_edit!
|
112
|
+
elsif key_event.keyCode == swt(:esc)
|
113
|
+
table_proxy.cancel_edit!
|
114
|
+
end
|
115
|
+
}
|
116
|
+
}
|
117
|
+
end,
|
118
|
+
},
|
119
|
+
date: {
|
120
|
+
widget_value_property: :date_time,
|
121
|
+
editor_gui: lambda do |args, model, property, table_proxy|
|
122
|
+
first_time = true
|
123
|
+
table_proxy.table_editor.minimumWidth = 90
|
124
|
+
table_proxy.table_editor.minimumHeight = 15
|
125
|
+
date(*args) {
|
126
|
+
date_time model.send(property)
|
127
|
+
focus true
|
128
|
+
on_widget_selected {
|
129
|
+
table_proxy.finish_edit!
|
130
|
+
}
|
131
|
+
on_key_pressed { |key_event|
|
132
|
+
if key_event.keyCode == swt(:cr)
|
133
|
+
table_proxy.finish_edit!
|
134
|
+
elsif key_event.keyCode == swt(:esc)
|
135
|
+
table_proxy.cancel_edit!
|
136
|
+
end
|
137
|
+
}
|
138
|
+
}
|
139
|
+
end,
|
140
|
+
},
|
141
|
+
date_drop_down: {
|
142
|
+
widget_value_property: :date_time,
|
143
|
+
editor_gui: lambda do |args, model, property, table_proxy|
|
144
|
+
first_time = true
|
145
|
+
table_proxy.table_editor.minimumWidth = 80
|
146
|
+
table_proxy.table_editor.minimumHeight = 15
|
147
|
+
date_drop_down(*args) {
|
148
|
+
date_time model.send(property)
|
149
|
+
focus true
|
150
|
+
on_widget_selected {
|
151
|
+
table_proxy.finish_edit!
|
152
|
+
}
|
153
|
+
on_key_pressed { |key_event|
|
154
|
+
if key_event.keyCode == swt(:cr)
|
155
|
+
table_proxy.finish_edit!
|
156
|
+
elsif key_event.keyCode == swt(:esc)
|
157
|
+
table_proxy.cancel_edit!
|
158
|
+
end
|
159
|
+
}
|
160
|
+
}
|
161
|
+
end,
|
162
|
+
},
|
163
|
+
time: {
|
164
|
+
widget_value_property: :date_time,
|
165
|
+
editor_gui: lambda do |args, model, property, table_proxy|
|
166
|
+
first_time = true
|
167
|
+
table_proxy.table_editor.minimumWidth = 80
|
168
|
+
table_proxy.table_editor.minimumHeight = 15
|
169
|
+
time(*args) {
|
170
|
+
date_time model.send(property)
|
171
|
+
focus true
|
172
|
+
on_widget_selected {
|
173
|
+
table_proxy.finish_edit!
|
174
|
+
}
|
175
|
+
on_focus_lost {
|
176
|
+
table_proxy.finish_edit!
|
177
|
+
}
|
178
|
+
on_key_pressed { |key_event|
|
179
|
+
if key_event.keyCode == swt(:cr)
|
180
|
+
table_proxy.finish_edit!
|
181
|
+
elsif key_event.keyCode == swt(:esc)
|
182
|
+
table_proxy.cancel_edit!
|
183
|
+
end
|
184
|
+
}
|
185
|
+
}
|
186
|
+
end,
|
187
|
+
},
|
188
|
+
radio: {
|
189
|
+
widget_value_property: :selection,
|
190
|
+
editor_gui: lambda do |args, model, property, table_proxy|
|
191
|
+
first_time = true
|
192
|
+
table_proxy.table_editor.minimumHeight = 25
|
193
|
+
radio(*args) {
|
194
|
+
selection model.send(property)
|
195
|
+
focus true
|
196
|
+
on_widget_selected {
|
197
|
+
table_proxy.finish_edit!
|
198
|
+
}
|
199
|
+
on_focus_lost {
|
200
|
+
table_proxy.finish_edit!
|
201
|
+
}
|
202
|
+
on_key_pressed { |key_event|
|
203
|
+
if key_event.keyCode == swt(:cr)
|
204
|
+
table_proxy.finish_edit!
|
205
|
+
elsif key_event.keyCode == swt(:esc)
|
206
|
+
table_proxy.cancel_edit!
|
207
|
+
end
|
208
|
+
}
|
209
|
+
}
|
210
|
+
end,
|
211
|
+
},
|
212
|
+
spinner: {
|
213
|
+
widget_value_property: :selection,
|
214
|
+
editor_gui: lambda do |args, model, property, table_proxy|
|
215
|
+
first_time = true
|
216
|
+
table_proxy.table_editor.minimumHeight = 25
|
217
|
+
table_editor_widget_proxy = spinner(*args) {
|
218
|
+
selection model.send(property)
|
219
|
+
focus true
|
220
|
+
on_focus_lost {
|
221
|
+
table_proxy.finish_edit!
|
222
|
+
}
|
223
|
+
on_key_pressed { |key_event|
|
224
|
+
if key_event.keyCode == swt(:cr)
|
225
|
+
table_proxy.finish_edit!
|
226
|
+
elsif key_event.keyCode == swt(:esc)
|
227
|
+
table_proxy.cancel_edit!
|
228
|
+
end
|
229
|
+
}
|
230
|
+
}
|
231
|
+
table_editor_widget_proxy
|
232
|
+
end,
|
233
|
+
},
|
234
|
+
}
|
235
|
+
end
|
236
|
+
end
|
10
237
|
|
11
238
|
def initialize(parent, args, block)
|
12
239
|
super(parent, args, block)
|
13
240
|
@columns = []
|
14
241
|
@children = []
|
242
|
+
@editors = []
|
15
243
|
@selection = []
|
244
|
+
@table_editor = TableEditor.new(self)
|
245
|
+
@table_editor.horizontalAlignment = SWTProxy[:left]
|
246
|
+
@table_editor.grabHorizontal = true
|
247
|
+
@table_editor.minimumWidth = 90
|
248
|
+
@table_editor.minimumHeight = 20
|
249
|
+
if editable?
|
250
|
+
on_mouse_up { |event|
|
251
|
+
edit_table_item(event.table_item, event.column_index)
|
252
|
+
}
|
253
|
+
end
|
16
254
|
end
|
17
255
|
|
18
256
|
# Only table_columns may be added as children
|
19
257
|
def post_initialize_child(child)
|
20
258
|
if child.is_a?(TableColumnProxy)
|
21
259
|
@columns << child
|
22
|
-
|
260
|
+
child.render
|
261
|
+
elsif child.is_a?(TableItemProxy)
|
23
262
|
@children << child
|
263
|
+
child.render
|
264
|
+
else
|
265
|
+
@editors << child
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# Executes for the parent of a child that just got disposed
|
270
|
+
def post_dispose_child(child)
|
271
|
+
if child.is_a?(TableColumnProxy)
|
272
|
+
@columns&.delete(child)
|
273
|
+
elsif child.is_a?(TableItemProxy)
|
274
|
+
@children&.delete(child)
|
275
|
+
else
|
276
|
+
@editors&.delete(child)
|
24
277
|
end
|
25
|
-
|
278
|
+
end
|
279
|
+
|
280
|
+
def post_add_content
|
281
|
+
return if @initially_sorted
|
282
|
+
initial_sort!
|
283
|
+
@initially_sorted = true
|
284
|
+
end
|
285
|
+
|
286
|
+
def default_layout
|
287
|
+
nil
|
288
|
+
end
|
289
|
+
|
290
|
+
def get_data(key=nil)
|
291
|
+
data
|
26
292
|
end
|
27
293
|
|
28
294
|
def remove_all
|
@@ -30,17 +296,37 @@ module Glimmer
|
|
30
296
|
redraw
|
31
297
|
end
|
32
298
|
|
299
|
+
def editable?
|
300
|
+
args.include?(:editable)
|
301
|
+
end
|
302
|
+
alias editable editable?
|
303
|
+
|
304
|
+
def selection
|
305
|
+
@selection.to_a
|
306
|
+
end
|
307
|
+
|
33
308
|
def selection=(new_selection)
|
34
|
-
|
309
|
+
new_selection = new_selection.to_a
|
310
|
+
changed = (selection + new_selection) - (selection & new_selection)
|
35
311
|
@selection = new_selection
|
36
|
-
changed.each(&:
|
312
|
+
changed.each(&:redraw_selection)
|
37
313
|
end
|
38
314
|
|
39
315
|
def items=(new_items)
|
40
316
|
@children = new_items
|
317
|
+
# TODO optimize in the future by sorting elements in DOM directly when no change to elements occur other than sort
|
41
318
|
redraw
|
42
319
|
end
|
43
320
|
|
321
|
+
def item_count=(value)
|
322
|
+
@item_count = value
|
323
|
+
redraw_empty_items
|
324
|
+
end
|
325
|
+
|
326
|
+
def cells_for(model)
|
327
|
+
column_properties.map {|property| model.send(property)}
|
328
|
+
end
|
329
|
+
|
44
330
|
def search(&condition)
|
45
331
|
items.select {|item| condition.nil? || condition.call(item)}
|
46
332
|
end
|
@@ -61,8 +347,252 @@ module Glimmer
|
|
61
347
|
self.selection = new_selection
|
62
348
|
end
|
63
349
|
|
64
|
-
def
|
65
|
-
|
350
|
+
def sort_block=(comparator)
|
351
|
+
@sort_block = comparator
|
352
|
+
end
|
353
|
+
|
354
|
+
def sort_by_block=(property_picker)
|
355
|
+
@sort_by_block = property_picker
|
356
|
+
end
|
357
|
+
|
358
|
+
def sort_property=(new_sort_property)
|
359
|
+
@sort_property = new_sort_property.to_collection
|
360
|
+
end
|
361
|
+
|
362
|
+
def detect_sort_type
|
363
|
+
@sort_type = sort_property.size.times.map { String }
|
364
|
+
array = model_binding.evaluate_property
|
365
|
+
sort_property.each_with_index do |a_sort_property, i|
|
366
|
+
values = array.map { |object| object.send(a_sort_property) }
|
367
|
+
value_classes = values.map(&:class).uniq
|
368
|
+
if value_classes.size == 1
|
369
|
+
@sort_type[i] = value_classes.first
|
370
|
+
elsif value_classes.include?(Integer)
|
371
|
+
@sort_type[i] = Integer
|
372
|
+
elsif value_classes.include?(Float)
|
373
|
+
@sort_type[i] = Float
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
def column_sort_properties
|
379
|
+
column_properties.zip(columns.map(&:sort_property)).map do |pair|
|
380
|
+
pair.compact.last.to_collection
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
def sort_direction
|
385
|
+
@sort_direction == :ascending ? SWTProxy[:up] : SWTProxy[:down]
|
386
|
+
end
|
387
|
+
|
388
|
+
def sort_direction=(value)
|
389
|
+
@sort_direction = value == SWTProxy[:up] ? :ascending : :descending
|
390
|
+
end
|
391
|
+
|
392
|
+
# Sorts by specified TableColumnProxy object. If nil, it uses the table default sort instead.
|
393
|
+
def sort_by_column!(table_column_proxy=nil)
|
394
|
+
index = columns.to_a.index(table_column_proxy) unless table_column_proxy.nil?
|
395
|
+
new_sort_property = table_column_proxy.nil? ? @sort_property : table_column_proxy.sort_property || [column_properties[index]]
|
396
|
+
|
397
|
+
return if table_column_proxy.nil? && new_sort_property.nil? && @sort_block.nil? && @sort_by_block.nil?
|
398
|
+
if new_sort_property && table_column_proxy.nil? && new_sort_property.size == 1 && (index = column_sort_properties.index(new_sort_property))
|
399
|
+
table_column_proxy = columns[index]
|
400
|
+
end
|
401
|
+
if new_sort_property && new_sort_property.size == 1 && !additional_sort_properties.to_a.empty?
|
402
|
+
selected_additional_sort_properties = additional_sort_properties.clone
|
403
|
+
if selected_additional_sort_properties.include?(new_sort_property.first)
|
404
|
+
selected_additional_sort_properties.delete(new_sort_property.first)
|
405
|
+
new_sort_property += selected_additional_sort_properties
|
406
|
+
else
|
407
|
+
new_sort_property += additional_sort_properties
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
new_sort_property = new_sort_property.to_collection unless new_sort_property.is_a?(Array)
|
412
|
+
@sort_direction = @sort_direction.nil? || @sort_property.first != new_sort_property.first || @sort_direction == :descending ? :ascending : :descending
|
413
|
+
|
414
|
+
@sort_property = new_sort_property
|
415
|
+
table_column_index = column_properties.index(new_sort_property.to_s.to_sym)
|
416
|
+
table_column_proxy ||= columns[table_column_index] if table_column_index
|
417
|
+
@sort_column = table_column_proxy if table_column_proxy
|
418
|
+
|
419
|
+
if table_column_proxy
|
420
|
+
@sort_by_block = nil
|
421
|
+
@sort_block = nil
|
422
|
+
end
|
423
|
+
@sort_type = nil
|
424
|
+
if table_column_proxy&.sort_by_block
|
425
|
+
@sort_by_block = table_column_proxy.sort_by_block
|
426
|
+
elsif table_column_proxy&.sort_block
|
427
|
+
@sort_block = table_column_proxy.sort_block
|
428
|
+
else
|
429
|
+
detect_sort_type
|
430
|
+
end
|
431
|
+
|
432
|
+
sort!
|
433
|
+
end
|
434
|
+
|
435
|
+
def initial_sort!
|
436
|
+
sort_by_column!
|
437
|
+
end
|
438
|
+
|
439
|
+
def sort!
|
440
|
+
return unless sort_property && (sort_type || sort_block || sort_by_block)
|
441
|
+
array = model_binding.evaluate_property
|
442
|
+
array = array.sort_by(&:hash) # this ensures consistent subsequent sorting in case there are equivalent sorts to avoid an infinite loop
|
443
|
+
# Converting value to_s first to handle nil cases. Should work with numeric, boolean, and date fields
|
444
|
+
if sort_block
|
445
|
+
sorted_array = array.sort(&sort_block)
|
446
|
+
elsif sort_by_block
|
447
|
+
sorted_array = array.sort_by(&sort_by_block)
|
448
|
+
else
|
449
|
+
sorted_array = array.sort_by do |object|
|
450
|
+
sort_property.each_with_index.map do |a_sort_property, i|
|
451
|
+
value = object.send(a_sort_property)
|
452
|
+
# handle nil and difficult to compare types gracefully
|
453
|
+
if sort_type[i] == Integer
|
454
|
+
value = value.to_i
|
455
|
+
elsif sort_type[i] == Float
|
456
|
+
value = value.to_f
|
457
|
+
elsif sort_type[i] == String
|
458
|
+
value = value.to_s
|
459
|
+
end
|
460
|
+
value
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
sorted_array = sorted_array.reverse if @sort_direction == :descending
|
465
|
+
model_binding.call(sorted_array)
|
466
|
+
end
|
467
|
+
|
468
|
+
def additional_sort_properties=(*args)
|
469
|
+
@additional_sort_properties = args unless args.empty?
|
470
|
+
end
|
471
|
+
|
472
|
+
def editor=(args)
|
473
|
+
@editor = args
|
474
|
+
end
|
475
|
+
|
476
|
+
# Indicates if table is in edit mode, thus displaying a text widget for a table item cell
|
477
|
+
def edit_mode?
|
478
|
+
!!@edit_mode
|
479
|
+
end
|
480
|
+
|
481
|
+
def cancel_edit!
|
482
|
+
@cancel_edit&.call if @edit_mode
|
483
|
+
end
|
484
|
+
|
485
|
+
def finish_edit!
|
486
|
+
@finish_edit&.call if @edit_mode
|
487
|
+
end
|
488
|
+
|
489
|
+
# Indicates if table is editing a table item because the user hit ENTER or focused out after making a change in edit mode to a table item cell.
|
490
|
+
# It is set to false once change is saved to model
|
491
|
+
def edit_in_progress?
|
492
|
+
!!@edit_in_progress
|
493
|
+
end
|
494
|
+
|
495
|
+
def edit_selected_table_item(column_index, before_write: nil, after_write: nil, after_cancel: nil)
|
496
|
+
edit_table_item(selection.first, column_index, before_write: before_write, after_write: after_write, after_cancel: after_cancel)
|
497
|
+
end
|
498
|
+
|
499
|
+
# TODO migrate the following to the next method
|
500
|
+
# def edit_table_item(table_item, column_index)
|
501
|
+
# table_item&.edit(column_index) unless column_index.nil?
|
502
|
+
# end
|
503
|
+
|
504
|
+
def edit_table_item(table_item, column_index, before_write: nil, after_write: nil, after_cancel: nil)
|
505
|
+
return if table_item.nil? || (@edit_mode && @edit_table_item == table_item && @edit_column_index == column_index)
|
506
|
+
@edit_column_index = column_index
|
507
|
+
@edit_table_item = table_item
|
508
|
+
column_index = column_index.to_i
|
509
|
+
model = table_item.data
|
510
|
+
property = column_properties[column_index]
|
511
|
+
cancel_edit!
|
512
|
+
return unless columns[column_index].editable?
|
513
|
+
action_taken = false
|
514
|
+
@edit_mode = true
|
515
|
+
|
516
|
+
editor_config = columns[column_index].editor || editor
|
517
|
+
editor_config = editor_config.to_collection
|
518
|
+
editor_widget_options = editor_config.last.is_a?(Hash) ? editor_config.last : {}
|
519
|
+
editor_widget_arg_last_index = editor_config.last.is_a?(Hash) ? -2 : -1
|
520
|
+
editor_widget = (editor_config[0] || :text).to_sym
|
521
|
+
editor_widget_args = editor_config[1..editor_widget_arg_last_index]
|
522
|
+
model_editing_property = editor_widget_options[:property] || property
|
523
|
+
widget_value_property = TableProxy::editors.symbolize_keys[editor_widget][:widget_value_property]
|
524
|
+
|
525
|
+
@cancel_edit = lambda do |event=nil|
|
526
|
+
@cancel_in_progress = true
|
527
|
+
@table_editor.cancel!
|
528
|
+
@table_editor_widget_proxy&.dispose
|
529
|
+
@table_editor_widget_proxy = nil
|
530
|
+
after_cancel&.call
|
531
|
+
@edit_in_progress = false
|
532
|
+
@cancel_in_progress = false
|
533
|
+
@cancel_edit = nil
|
534
|
+
@edit_table_item = @edit_column_index = nil if (@edit_mode && @edit_table_item == table_item && @edit_column_index == column_index)
|
535
|
+
@edit_mode = false
|
536
|
+
end
|
537
|
+
|
538
|
+
@finish_edit = lambda do |event=nil|
|
539
|
+
new_value = @table_editor_widget_proxy&.send(widget_value_property)
|
540
|
+
if table_item.disposed?
|
541
|
+
@cancel_edit.call
|
542
|
+
elsif !new_value.nil? && !action_taken && !@edit_in_progress && !@cancel_in_progress
|
543
|
+
action_taken = true
|
544
|
+
@edit_in_progress = true
|
545
|
+
if new_value == model.send(model_editing_property)
|
546
|
+
@cancel_edit.call
|
547
|
+
else
|
548
|
+
before_write&.call
|
549
|
+
@table_editor.save!(widget_value_property: widget_value_property)
|
550
|
+
model.send("#{model_editing_property}=", new_value) # makes table update itself, so must search for selected table item again
|
551
|
+
# Table refresh happens here because of model update triggering observers, so must retrieve table item again
|
552
|
+
edited_table_item = search { |ti| ti.data == model }.first
|
553
|
+
show_item(edited_table_item)
|
554
|
+
@table_editor_widget_proxy&.dispose
|
555
|
+
@table_editor_widget_proxy = nil
|
556
|
+
after_write&.call(edited_table_item)
|
557
|
+
@edit_in_progress = false
|
558
|
+
@edit_table_item = @edit_column_index = nil
|
559
|
+
end
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
content {
|
564
|
+
@table_editor_widget_proxy = TableProxy::editors.symbolize_keys[editor_widget][:editor_gui].call(editor_widget_args, model, model_editing_property, self)
|
565
|
+
}
|
566
|
+
@table_editor.set_editor(@table_editor_widget_proxy, table_item, column_index)
|
567
|
+
rescue => e
|
568
|
+
Glimmer::Config.logger.error {e.full_message}
|
569
|
+
raise e
|
570
|
+
end
|
571
|
+
|
572
|
+
def show_item(table_item)
|
573
|
+
table_item.dom_element.focus
|
574
|
+
end
|
575
|
+
|
576
|
+
def add_listener(underscored_listener_name, &block)
|
577
|
+
enhanced_block = lambda do |event|
|
578
|
+
event.extend(TableListenerEvent)
|
579
|
+
block.call(event)
|
580
|
+
end
|
581
|
+
super(underscored_listener_name, &enhanced_block)
|
582
|
+
end
|
583
|
+
|
584
|
+
|
585
|
+
def header_visible=(value)
|
586
|
+
@header_visible = value
|
587
|
+
if @header_visible
|
588
|
+
thead_dom_element.remove_class('hide')
|
589
|
+
else
|
590
|
+
thead_dom_element.add_class('hide')
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
def header_visible
|
595
|
+
@header_visible
|
66
596
|
end
|
67
597
|
|
68
598
|
def selector
|
@@ -84,7 +614,8 @@ module Glimmer
|
|
84
614
|
event.singleton_class.send(:define_method, :column_index) do
|
85
615
|
(table_data || event.target).attr('data-column-index')
|
86
616
|
end
|
87
|
-
|
617
|
+
|
618
|
+
event_listener.call(event) unless event.table_item.nil? && event.column_index.nil?
|
88
619
|
}
|
89
620
|
}
|
90
621
|
|
@@ -96,13 +627,27 @@ module Glimmer
|
|
96
627
|
'on_mouse_up' => {
|
97
628
|
event: 'mouseup',
|
98
629
|
event_handler: mouse_handler,
|
99
|
-
}
|
630
|
+
},
|
631
|
+
'on_widget_selected' => {
|
632
|
+
event: 'mouseup',
|
633
|
+
event_handler: mouse_handler,
|
634
|
+
},
|
100
635
|
}
|
101
636
|
end
|
102
637
|
|
103
638
|
def redraw
|
104
639
|
super()
|
105
640
|
@columns.to_a.each(&:redraw)
|
641
|
+
redraw_empty_items
|
642
|
+
end
|
643
|
+
|
644
|
+
def redraw_empty_items
|
645
|
+
if @children&.size.to_i < item_count.to_i
|
646
|
+
item_count.to_i.times do
|
647
|
+
empty_columns = column_properties&.size.to_i.times.map { |i| "<td data-column-index='#{i}'></td>" }
|
648
|
+
items_dom_element.append("<tr class='table-item empty-table-item'>#{empty_columns}</tr>")
|
649
|
+
end
|
650
|
+
end
|
106
651
|
end
|
107
652
|
|
108
653
|
def element
|
@@ -136,6 +681,10 @@ module Glimmer
|
|
136
681
|
}
|
137
682
|
end
|
138
683
|
|
684
|
+
def thead_dom_element
|
685
|
+
dom_element.find('thead')
|
686
|
+
end
|
687
|
+
|
139
688
|
def items_dom
|
140
689
|
tbody {
|
141
690
|
}
|
@@ -145,7 +694,8 @@ module Glimmer
|
|
145
694
|
table_id = id
|
146
695
|
table_id_style = css
|
147
696
|
table_id_css_classes = css_classes
|
148
|
-
table_id_css_classes << 'table'
|
697
|
+
table_id_css_classes << 'table' unless table_id_css_classes.include?('table')
|
698
|
+
table_id_css_classes << 'editable' if editable? && !table_id_css_classes.include?('editable')
|
149
699
|
table_id_css_classes_string = table_id_css_classes.to_a.join(' ')
|
150
700
|
@dom ||= html {
|
151
701
|
table(id: table_id, style: table_id_style, class: table_id_css_classes_string) {
|
@@ -154,6 +704,23 @@ module Glimmer
|
|
154
704
|
}
|
155
705
|
}.to_s
|
156
706
|
end
|
707
|
+
|
708
|
+
private
|
709
|
+
|
710
|
+
def property_type_converters
|
711
|
+
super.merge({
|
712
|
+
selection: lambda do |value|
|
713
|
+
if value.is_a?(Array)
|
714
|
+
search {|ti| value.include?(ti.get_data) }
|
715
|
+
else
|
716
|
+
search {|ti| ti.get_data == value}
|
717
|
+
end
|
718
|
+
end,
|
719
|
+
})
|
720
|
+
end
|
721
|
+
|
157
722
|
end
|
723
|
+
|
158
724
|
end
|
725
|
+
|
159
726
|
end
|