glimmer-dsl-libui 0.7.3 → 0.7.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c1ff2eeecd4520bb427d6a2ca0abb85ea41f5b3b92d859253b59c9574c6c899
4
- data.tar.gz: 907fa263dc715be72bf022258da6cb58aaef70e2d647ee17c29f9eda942547e5
3
+ metadata.gz: 5f7eb8d1c417de9910ff49f86592694558a3333f4840971f65be1232adf7408b
4
+ data.tar.gz: 9cdf9733b7026aae945ec183ba4551ee2bbe120454c0a094c02a99bd5338069a
5
5
  SHA512:
6
- metadata.gz: 2f8cd302076fc5e5cdced272facbd12c9af556cc0c5d1c2a3cdd271676316c80f302b496613334db749c67a8ecff2ca88bb50b3157583bce693ea7a9f77b277b
7
- data.tar.gz: f5062b028e2c4ca0eb1e804842d7940f43c4bfb8d1212b9075a0ca223c71f9730bb36c83345029e4d4eaee30d6056851621e4726d619c7f5209c0b08bde9f74d
6
+ metadata.gz: 5a6c290b75e8301cadd8c837be2612dd4e653192577ea0c8f2493d4fa4a0b7198f2f8008c99d7adeef42320e5b4d86be339b37fe9ca588ee4cc9e22ad36c0a48
7
+ data.tar.gz: b51a3e1e878b9adde67cc6670f682df5d7ab09a6678b8b16655fbcb0d5e3770b5119f867ea58c4b025a78e043ed56cd0309f86eccdec0b90d31c19b375a0007b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.7.4
4
+
5
+ - `table` `sortable` property (default: `true`) to enable automatic table sorting support when `cell_rows` is an `Array` (does not sort if `cell_rows` is a lazy enumerable)
6
+ - Fix issue with not auto-checking checkboxes for zero-or-many table selection in `examples/basic_table_selection2.rb`
7
+ - Rename `examples/basic_table_selection.rb` to `examples/basic_table_selection3.rb` and add new `examples/basic_table_selection.rb` with automated `table` selection via `sortable` property (default: `true`)
8
+ - Disable automatic sorting by default in `refined_table` (set its `table` `sortable` property to `false`) since it does not sort over the entire collection, yet the visible collection only
9
+ - Disable `sortable` sorting for `table` `button_column` since it does not make sense for it
10
+
3
11
  ## 0.7.3
4
12
 
5
13
  - `table` `selection` data-binding support
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.7.3
1
+ # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.7.4
2
2
  ## Prerequisite-Free Ruby Desktop Development GUI Library
3
3
  ### The Quickest Way From Zero To GUI
4
4
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-libui.svg)](http://badge.fury.io/rb/glimmer-dsl-libui)
@@ -544,7 +544,7 @@ gem install glimmer-dsl-libui
544
544
  Or install via Bundler `Gemfile`:
545
545
 
546
546
  ```ruby
547
- gem 'glimmer-dsl-libui', '~> 0.7.3'
547
+ gem 'glimmer-dsl-libui', '~> 0.7.4'
548
548
  ```
549
549
 
550
550
  Test that installation worked by running the [Meta-Example](#examples):
@@ -824,7 +824,7 @@ There are additional useful `Glimmer::LibUI` operations that are not found in `L
824
824
 
825
825
  The `table` control must first declare its columns via one of these column keywords (mentioned in [Supported Keywords](#supported-keywords)):
826
826
  - `background_color_column`: expects color cell values
827
- - `button_column`: expects `String` cell values
827
+ - `button_column`: expects `String` cell values and a nested `on_clicked` listener that gets triggerd when a button is clicked
828
828
  - `checkbox_column`: expects Boolean cell values
829
829
  - `checkbox_text_column`: expects dual-element `Array` of Boolean and `String` cell values
830
830
  - `checkbox_text_color_column`: expects triple-element `Array` of Boolean, `String`, and color cell values
@@ -842,6 +842,22 @@ Note that the `cell_rows` property declaration results in "implicit data-binding
842
842
  - Inserting cell rows: Calling `Array#<<`, `Array#push`, `Array#prepend`, or any insertion/addition `Array` method automatically inserts rows in actual `table` control
843
843
  - Changing cell rows: Calling `Array#[]=`, `Array#map!`, or any update `Array` method automatically updates rows in actual `table` control
844
844
 
845
+ More details about table data-binding can be found in [examples/basic_table.rb](https://github.com/AndyObtiva/glimmer-dsl-libui/blob/master/docs/examples/GLIMMER-DSL-LIBUI-BASIC-EXAMPLES.md#basic-table) or other `table` [basic examples](https://github.com/AndyObtiva/glimmer-dsl-libui/blob/master/docs/examples/GLIMMER-DSL-LIBUI-BASIC-EXAMPLES.md) and [advanced examples](https://github.com/AndyObtiva/glimmer-dsl-libui/blob/master/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md).
846
+
847
+ The `table` control supports table selection and table sorting automatically as smart defaults, which can also be configured if needed as per the options below.
848
+
849
+ There are other properties that `table` supports:
850
+ - `selection_mode` (`Symbol`) [default: `:zero_or_one`]: sets selection mode to `:one`, `:zero_or_one`, `:zero_or_many`, or `:none`
851
+ - `selection` (`Integer` or `Array` of `Integer`s): a single `Integer` row index for `:one` and `:zero_or_one` selection modes, or an `Array` of `Integer` row indexes if selection mode is `:zero_or_many`
852
+ - `header_visible` (Boolean): shows or hides column headers
853
+ - `sortable` (Boolean) [default: `true`]: enables automatic table sorting support
854
+
855
+ To handle `table` sorting manually, the following can be set inside a table column:
856
+ - `sort_indicator` (`Symbol`): sets sort indicator to ascending or descending with the value being `:ascending`, `:descending`, `:asc`, `:desc`, `:a`, or `:d`
857
+ - `on_clicked` (`Proc`): this listener is triggered when a table column is clicked
858
+
859
+ More details about table selection and table sorting can be found in [examples/basic_table_selection.rb](https://github.com/AndyObtiva/glimmer-dsl-libui/blob/master/docs/examples/GLIMMER-DSL-LIBUI-BASIC-EXAMPLES.md#basic-table-selection).
860
+
845
861
  ([explicit data-binding](#data-binding) supports everything available with implicit data-binding too)
846
862
 
847
863
  Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.3
1
+ 0.7.4
@@ -1,6 +1,45 @@
1
1
  require 'glimmer-dsl-libui'
2
2
 
3
3
  class BasicTableSelection
4
+ TableColumnPresenter = Struct.new(:name,
5
+ :column,
6
+ :table_presenter,
7
+ keyword_init: true)
8
+
9
+ TablePresenter = Struct.new(:data,
10
+ :column_names,
11
+ :selection_mode,
12
+ :selection,
13
+ :header_visible,
14
+ :sortable,
15
+ keyword_init: true) do
16
+ def selection_items
17
+ data.size.times.map { |row| "Row #{row} Selection" }
18
+ end
19
+
20
+ def toggle_header_visible
21
+ self.header_visible = !(header_visible.nil? || header_visible)
22
+ end
23
+
24
+ def toggle_sortable
25
+ self.sortable = !(sortable.nil? || sortable)
26
+ end
27
+
28
+ def column_presenters
29
+ @column_presenters ||= column_names.each_with_index.map do |column_name, column|
30
+ TableColumnPresenter.new(name: column_name, column: column, table_presenter: self)
31
+ end
32
+ end
33
+
34
+ def selected_row
35
+ selection && data[selection]
36
+ end
37
+
38
+ def selected_rows
39
+ selection && selection.is_a?(Array) && selection.map { |row| data[row] }
40
+ end
41
+ end
42
+
4
43
  include Glimmer::LibUI::Application
5
44
 
6
45
  before_body do
@@ -11,10 +50,38 @@ class BasicTableSelection
11
50
  %w[horse neigh],
12
51
  %w[cow moo]
13
52
  ]
14
- @one_table_data = data.dup
15
- @zero_or_one_table_data = data.dup
16
- @zero_or_many_table_data = data.dup
17
- @none_table_data = data.dup
53
+ @one_table_presenter = TablePresenter.new(
54
+ data: data.dup,
55
+ column_names: ['Name', 'Description'],
56
+ selection_mode: :one, # other values are :zero_or_many , :zero_or_one, :none (default is :zero_or_one if not specified)
57
+ selection: 2, # initial selection row index (could be nil too or just left off, defaulting to 0)
58
+ header_visible: nil, # defaults to true
59
+ sortable: nil, # defaults to true
60
+ )
61
+ @zero_or_one_table_presenter = TablePresenter.new(
62
+ data: data.dup,
63
+ column_names: ['Name', 'Description'],
64
+ selection_mode: :zero_or_one, # other values are :zero_or_many , :one, :none (default is :zero_or_one if not specified)
65
+ selection: nil, # initial selection row index (could be an integer too or just left off, defaulting to nil)
66
+ header_visible: nil, # defaults to true
67
+ sortable: nil, # defaults to true
68
+ )
69
+ @zero_or_many_table_presenter = TablePresenter.new(
70
+ data: data.dup,
71
+ column_names: ['Name', 'Description'],
72
+ selection_mode: :zero_or_many, # other values are :zero_or_many , :one, :none (default is :zero_or_one if not specified)
73
+ selection: [0, 2, 4], # initial selection row index (could be an integer too or just left off, defaulting to nil)
74
+ header_visible: nil, # defaults to true
75
+ sortable: nil, # defaults to true
76
+ )
77
+ @none_table_presenter = TablePresenter.new(
78
+ data: data.dup,
79
+ column_names: ['Name', 'Description'],
80
+ selection_mode: :none, # other values are :zero_or_many , :zero_or_one, :one (default is :zero_or_one if not specified)
81
+ selection: nil, # defaults to nil
82
+ header_visible: nil, # defaults to true
83
+ sortable: nil, # defaults to true
84
+ )
18
85
  end
19
86
 
20
87
  body {
@@ -26,42 +93,37 @@ class BasicTableSelection
26
93
  stretchy false
27
94
 
28
95
  @one_table_selection_radio_buttons = radio_buttons {
29
- items @one_table_data.size.times.map { |row| "Row #{row} Selection" }
30
-
31
- on_selected do |rb|
32
- @one_table.selection = [rb.selected]
33
- end
96
+ items @one_table_presenter.selection_items
97
+ selected <=> [@one_table_presenter, :selection]
34
98
  }
35
99
  }
36
-
37
- button('Toggle Table Header Visibility') {
100
+
101
+ horizontal_box {
38
102
  stretchy false
39
103
 
40
- on_clicked do
41
- @one_table.header_visible = !@one_table.header_visible
42
- end
43
- }
44
-
45
- @one_table = table {
46
- text_column('Animal') {
47
- # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
48
-
49
- on_clicked do |tc, column|
50
- sort_one_table_column(tc, column)
104
+ button('Toggle Table Header Visibility') {
105
+ on_clicked do
106
+ @one_table_presenter.toggle_header_visible
51
107
  end
52
108
  }
53
- text_column('Description') {
54
- # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
55
-
56
- on_clicked do |tc, column|
57
- sort_one_table_column(tc, column)
109
+
110
+ button('Toggle Table Sortability') {
111
+ on_clicked do
112
+ @one_table_presenter.toggle_sortable # toggles sortable attribute to false or true
58
113
  end
59
114
  }
115
+ }
116
+
117
+ @one_table = table {
118
+ @one_table_presenter.column_presenters.each do |column_presenter|
119
+ text_column(column_presenter.name)
120
+ end
60
121
 
61
- cell_rows @one_table_data
62
- selection_mode :one # other values are :zero_or_many , :zero_or_one, :none (default is :zero_or_one if not specified)
63
- selection 2 # initial selection row index (could be nil too or just left off, defaulting to 0)
64
- # header_visible true # default
122
+ cell_rows @one_table_presenter.data
123
+ selection_mode <= [@one_table_presenter, :selection_mode]
124
+ selection <=> [@one_table_presenter, :selection]
125
+ header_visible <= [@one_table_presenter, :header_visible]
126
+ sortable <= [@one_table_presenter, :sortable]
65
127
 
66
128
  on_row_clicked do |t, row|
67
129
  puts "Row Clicked: #{row}"
@@ -77,54 +139,48 @@ class BasicTableSelection
77
139
  puts "Selection Changed: #{selection.inspect}"
78
140
  puts "Added Selection: #{added_selection.inspect}"
79
141
  puts "Removed Selection: #{removed_selection.inspect}"
80
- @one_table_selection_radio_buttons.selected = selection
81
142
  end
82
143
  }
83
144
  }
84
145
  }
85
-
146
+
86
147
  tab_item('Zero-Or-One') {
87
148
  vertical_box {
88
149
  vertical_box {
89
150
  stretchy false
90
151
 
91
152
  @zero_or_one_table_selection_radio_buttons = radio_buttons {
92
- items @zero_or_one_table_data.size.times.map { |row| "Row #{row} Selection" }
93
-
94
- on_selected do |rb|
95
- @zero_or_one_table.selection = [rb.selected]
96
- end
153
+ items @zero_or_one_table_presenter.selection_items
154
+ selected <=> [@zero_or_one_table_presenter, :selection]
97
155
  }
98
156
  }
99
157
 
100
- button('Toggle Table Header Visibility') {
158
+ horizontal_box {
101
159
  stretchy false
102
160
 
103
- on_clicked do
104
- @zero_or_one_table.header_visible = !@zero_or_one_table.header_visible
105
- end
106
- }
107
-
108
- @zero_or_one_table = table {
109
- text_column('Animal') {
110
- # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
111
-
112
- on_clicked do |tc, column|
113
- sort_zero_or_one_table_column(tc, column)
161
+ button('Toggle Table Header Visibility') {
162
+ on_clicked do
163
+ @zero_or_one_table_presenter.toggle_header_visible
114
164
  end
115
165
  }
116
- text_column('Description') {
117
- # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
118
-
119
- on_clicked do |tc, column|
120
- sort_zero_or_one_table_column(tc, column)
166
+
167
+ button('Toggle Table Sortability') {
168
+ on_clicked do
169
+ @zero_or_one_table_presenter.toggle_sortable # toggles sortable attribute to false or true
121
170
  end
122
171
  }
172
+ }
173
+
174
+ @zero_or_one_table = table {
175
+ @zero_or_one_table_presenter.column_presenters.each do |column_presenter|
176
+ text_column(column_presenter.name)
177
+ end
123
178
 
124
- cell_rows @zero_or_one_table_data
125
- selection_mode :zero_or_one # other values are :zero_or_many , :one, :none (default is :zero_or_one if not specified)
126
- # selection 0 # initial selection row index (could be nil too or just left off)
127
- # header_visible true # default
179
+ cell_rows @zero_or_one_table_presenter.data
180
+ selection_mode <= [@zero_or_one_table_presenter, :selection_mode]
181
+ selection <=> [@zero_or_one_table_presenter, :selection]
182
+ header_visible <= [@zero_or_one_table_presenter, :header_visible]
183
+ sortable <= [@zero_or_one_table_presenter, :sortable]
128
184
 
129
185
  on_row_clicked do |t, row|
130
186
  puts "Row Clicked: #{row}"
@@ -140,7 +196,6 @@ class BasicTableSelection
140
196
  puts "Selection Changed: #{selection.inspect}"
141
197
  puts "Added Selection: #{added_selection.inspect}"
142
198
  puts "Removed Selection: #{removed_selection.inspect}"
143
- @zero_or_one_table_selection_radio_buttons.selected = selection
144
199
  end
145
200
  }
146
201
  }
@@ -151,49 +206,46 @@ class BasicTableSelection
151
206
  vertical_box {
152
207
  stretchy false
153
208
 
154
- @zero_or_many_table_selection_checkboxes = @zero_or_many_table_data.size.times.map do |row|
209
+ @zero_or_many_table_selection_checkboxes = @zero_or_many_table_presenter.data.size.times.map do |row|
155
210
  checkbox("Row #{row} Selection") {
156
- on_toggled do |c|
157
- table_selection = @zero_or_many_table.selection.to_a
158
- if c.checked?
159
- table_selection << row unless table_selection.include?(row)
160
- else
161
- table_selection.delete(row) if table_selection.include?(row)
162
- end
163
- @zero_or_many_table.selection = table_selection
164
- end
211
+ checked <=> [@zero_or_many_table_presenter, :selection,
212
+ on_read: ->(selection_rows) {selection_rows.to_a.include?(row)},
213
+ on_write: ->(checked_value) {
214
+ checked_value ?
215
+ (@zero_or_many_table_presenter.selection.to_a + [row]).uniq :
216
+ @zero_or_many_table_presenter.selection.to_a.reject {|v| v == row }
217
+ },
218
+ ]
165
219
  }
166
220
  end
167
221
  }
168
222
 
169
- button('Toggle Table Header Visibility') {
223
+ horizontal_box {
170
224
  stretchy false
171
225
 
172
- on_clicked do
173
- @zero_or_many_table.header_visible = !@zero_or_many_table.header_visible
174
- end
175
- }
176
-
177
- @zero_or_many_table = table {
178
- text_column('Animal') {
179
- # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
180
-
181
- on_clicked do |tc, column|
182
- sort_zero_or_many_table_column(tc, column)
226
+ button('Toggle Table Header Visibility') {
227
+ on_clicked do
228
+ @zero_or_many_table_presenter.toggle_header_visible
183
229
  end
184
230
  }
185
- text_column('Description') {
186
- # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
187
-
188
- on_clicked do |tc, column|
189
- sort_zero_or_many_table_column(tc, column)
231
+
232
+ button('Toggle Table Sortability') {
233
+ on_clicked do
234
+ @zero_or_many_table_presenter.toggle_sortable # toggles sortable attribute to false or true
190
235
  end
191
236
  }
237
+ }
238
+
239
+ @zero_or_many_table = table {
240
+ @zero_or_many_table_presenter.column_presenters.each do |column_presenter|
241
+ text_column(column_presenter.name)
242
+ end
192
243
 
193
- cell_rows @zero_or_many_table_data
194
- selection_mode :zero_or_many # other values are :none , :zero_or_one , and :one (default is :zero_or_one if not specified)
195
- selection 0, 2, 4 # initial selection row indexes (could be empty array too or just left off)
196
- # header_visible true # default
244
+ cell_rows @zero_or_many_table_presenter.data
245
+ selection_mode <= [@zero_or_many_table_presenter, :selection_mode]
246
+ selection <=> [@zero_or_many_table_presenter, :selection]
247
+ header_visible <= [@zero_or_many_table_presenter, :header_visible]
248
+ sortable <= [@zero_or_many_table_presenter, :sortable]
197
249
 
198
250
  on_row_clicked do |t, row|
199
251
  puts "Row Clicked: #{row}"
@@ -209,46 +261,39 @@ class BasicTableSelection
209
261
  puts "Selection Changed: #{selection.inspect}"
210
262
  puts "Added Selection: #{added_selection.inspect}"
211
263
  puts "Removed Selection: #{removed_selection.inspect}"
212
- removed_selection&.each do |selected_row|
213
- @zero_or_many_table_selection_checkboxes[selected_row].checked = false
214
- end
215
- added_selection&.each do |selected_row|
216
- @zero_or_many_table_selection_checkboxes[selected_row].checked = true
217
- end
218
264
  end
219
265
  }
220
266
  }
221
267
  }
222
-
268
+
223
269
  tab_item('None') {
224
270
  vertical_box {
225
- button('Toggle Table Header Visibility') {
271
+ horizontal_box {
226
272
  stretchy false
227
273
 
228
- on_clicked do
229
- @none_table.header_visible = !@none_table.header_visible
230
- end
231
- }
232
-
233
- @none_table = table {
234
- text_column('Animal') {
235
- # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
236
-
237
- on_clicked do |tc, column|
238
- sort_none_table_column(tc, column)
274
+ button('Toggle Table Header Visibility') {
275
+ on_clicked do
276
+ @none_table_presenter.toggle_header_visible
239
277
  end
240
278
  }
241
- text_column('Description') {
242
- # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
243
-
244
- on_clicked do |tc, column|
245
- sort_none_table_column(tc, column)
279
+
280
+ button('Toggle Table Sortability') {
281
+ on_clicked do
282
+ @none_table_presenter.toggle_sortable # toggles sortable attribute to false or true
246
283
  end
247
284
  }
285
+ }
286
+
287
+ @none_table = table {
288
+ @none_table_presenter.column_presenters.each do |column_presenter|
289
+ text_column(column_presenter.name)
290
+ end
248
291
 
249
- cell_rows @none_table_data
250
- selection_mode :none # other values are :zero_or_many , :zero_or_one, :one (default is :zero_or_one if not specified)
251
- # header_visible true # default
292
+ cell_rows @none_table_presenter.data
293
+ selection_mode <= [@none_table_presenter, :selection_mode]
294
+ selection <=> [@none_table_presenter, :selection]
295
+ header_visible <= [@none_table_presenter, :header_visible]
296
+ sortable <= [@none_table_presenter, :sortable]
252
297
 
253
298
  on_row_clicked do |t, row|
254
299
  puts "Row Clicked: #{row}"
@@ -264,40 +309,6 @@ class BasicTableSelection
264
309
  }
265
310
  }
266
311
  }
267
-
268
- def sort_one_table_column(tc, column)
269
- puts "Clicked column #{column}: #{tc.name}"
270
- selected_row = @one_table.selection && @one_table_data[@one_table.selection]
271
- tc.toggle_sort_indicator
272
- @one_table_data.sort_by! { |row_data| row_data[column] }
273
- @one_table_data.reverse! if tc.sort_indicator == :descending
274
- @one_table.selection = @one_table_data.index(selected_row)
275
- end
276
-
277
- def sort_zero_or_one_table_column(tc, column)
278
- puts "Clicked column #{column}: #{tc.name}"
279
- selected_row = @zero_or_one_table.selection && @zero_or_one_table_data[@zero_or_one_table.selection]
280
- tc.toggle_sort_indicator
281
- @zero_or_one_table_data.sort_by! { |row_data| row_data[column] }
282
- @zero_or_one_table_data.reverse! if tc.sort_indicator == :descending
283
- @zero_or_one_table.selection = @zero_or_one_table_data.index(selected_row)
284
- end
285
-
286
- def sort_zero_or_many_table_column(tc, column)
287
- puts "Clicked column #{column}: #{tc.name}"
288
- selected_rows = @zero_or_many_table.selection&.map { |row| @zero_or_many_table_data[row] }
289
- tc.toggle_sort_indicator
290
- @zero_or_many_table_data.sort_by! { |row_data| row_data[column] }
291
- @zero_or_many_table_data.reverse! if tc.sort_indicator == :descending
292
- @zero_or_many_table.selection = selected_rows&.map {|row_data| @zero_or_many_table_data.index(row_data) }
293
- end
294
-
295
- def sort_none_table_column(tc, column)
296
- puts "Clicked column #{column}: #{tc.name}"
297
- tc.toggle_sort_indicator
298
- @none_table_data.sort_by! { |row_data| row_data[column] }
299
- @none_table_data.reverse! if tc.sort_indicator == :descending
300
- end
301
312
  end
302
313
 
303
314
  BasicTableSelection.launch
@@ -126,6 +126,7 @@ class BasicTableSelection
126
126
  selection_mode <= [@one_table_presenter, :selection_mode]
127
127
  selection <=> [@one_table_presenter, :selection]
128
128
  header_visible <= [@one_table_presenter, :header_visible]
129
+ sortable false # disable default sorting behavior to demonstrate manual sorting
129
130
 
130
131
  on_row_clicked do |t, row|
131
132
  puts "Row Clicked: #{row}"
@@ -181,6 +182,7 @@ class BasicTableSelection
181
182
  selection_mode <= [@zero_or_one_table_presenter, :selection_mode]
182
183
  selection <=> [@zero_or_one_table_presenter, :selection]
183
184
  header_visible <= [@zero_or_one_table_presenter, :header_visible]
185
+ sortable false # disable default sorting behavior to demonstrate manual sorting
184
186
 
185
187
  on_row_clicked do |t, row|
186
188
  puts "Row Clicked: #{row}"
@@ -208,15 +210,14 @@ class BasicTableSelection
208
210
 
209
211
  @zero_or_many_table_selection_checkboxes = @zero_or_many_table_presenter.data.size.times.map do |row|
210
212
  checkbox("Row #{row} Selection") {
211
- on_toggled do |c|
212
- table_selection = @zero_or_many_table_presenter.selection.to_a
213
- if c.checked?
214
- table_selection << row unless table_selection.include?(row)
215
- else
216
- table_selection.delete(row) if table_selection.include?(row)
217
- end
218
- @zero_or_many_table_presenter.selection = table_selection
219
- end
213
+ checked <=> [@zero_or_many_table_presenter, :selection,
214
+ on_read: ->(selection_rows) {selection_rows.to_a.include?(row)},
215
+ on_write: ->(checked_value) {
216
+ checked_value ?
217
+ (@zero_or_many_table_presenter.selection.to_a + [row]).uniq :
218
+ @zero_or_many_table_presenter.selection.to_a.reject {|v| v == row }
219
+ },
220
+ ]
220
221
  }
221
222
  end
222
223
  }
@@ -245,6 +246,7 @@ class BasicTableSelection
245
246
  selection_mode <= [@zero_or_many_table_presenter, :selection_mode]
246
247
  selection <=> [@zero_or_many_table_presenter, :selection]
247
248
  header_visible <= [@zero_or_many_table_presenter, :header_visible]
249
+ sortable false # disable default sorting behavior to demonstrate manual sorting
248
250
 
249
251
  on_row_clicked do |t, row|
250
252
  puts "Row Clicked: #{row}"
@@ -291,6 +293,7 @@ class BasicTableSelection
291
293
  selection_mode <= [@none_table_presenter, :selection_mode]
292
294
  selection <=> [@none_table_presenter, :selection]
293
295
  header_visible <= [@none_table_presenter, :header_visible]
296
+ sortable false # disable default sorting behavior to demonstrate manual sorting
294
297
 
295
298
  on_row_clicked do |t, row|
296
299
  puts "Row Clicked: #{row}"
@@ -0,0 +1,307 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ class BasicTableSelection
4
+ include Glimmer::LibUI::Application
5
+
6
+ before_body do
7
+ data = [
8
+ %w[cat meow],
9
+ %w[dog woof],
10
+ %w[chicken cock-a-doodle-doo],
11
+ %w[horse neigh],
12
+ %w[cow moo]
13
+ ]
14
+ @one_table_data = data.dup
15
+ @zero_or_one_table_data = data.dup
16
+ @zero_or_many_table_data = data.dup
17
+ @none_table_data = data.dup
18
+ end
19
+
20
+ body {
21
+ window('Basic Table Selection', 400, 300) {
22
+ tab {
23
+ tab_item('One') {
24
+ vertical_box {
25
+ vertical_box {
26
+ stretchy false
27
+
28
+ @one_table_selection_radio_buttons = radio_buttons {
29
+ items @one_table_data.size.times.map { |row| "Row #{row} Selection" }
30
+
31
+ on_selected do |rb|
32
+ @one_table.selection = [rb.selected]
33
+ end
34
+ }
35
+ }
36
+
37
+ button('Toggle Table Header Visibility') {
38
+ stretchy false
39
+
40
+ on_clicked do
41
+ @one_table.header_visible = !@one_table.header_visible
42
+ end
43
+ }
44
+
45
+ @one_table = table {
46
+ text_column('Animal') {
47
+ # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
48
+
49
+ on_clicked do |tc, column|
50
+ sort_one_table_column(tc, column)
51
+ end
52
+ }
53
+ text_column('Description') {
54
+ # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
55
+
56
+ on_clicked do |tc, column|
57
+ sort_one_table_column(tc, column)
58
+ end
59
+ }
60
+
61
+ cell_rows @one_table_data
62
+ selection_mode :one # other values are :zero_or_many , :zero_or_one, :none (default is :zero_or_one if not specified)
63
+ selection 2 # initial selection row index (could be nil too or just left off, defaulting to 0)
64
+ # header_visible true # default
65
+ sortable false # disable default sorting behavior to demonstrate manual sorting
66
+
67
+ on_row_clicked do |t, row|
68
+ puts "Row Clicked: #{row}"
69
+ end
70
+
71
+ on_row_double_clicked do |t, row|
72
+ puts "Row Double Clicked: #{row}"
73
+ end
74
+
75
+ on_selection_changed do |t, selection, added_selection, removed_selection|
76
+ # selection is an array or nil if selection mode is zero_or_many
77
+ # otherwise, selection is a single index integer or nil when not selected
78
+ puts "Selection Changed: #{selection.inspect}"
79
+ puts "Added Selection: #{added_selection.inspect}"
80
+ puts "Removed Selection: #{removed_selection.inspect}"
81
+ @one_table_selection_radio_buttons.selected = selection
82
+ end
83
+ }
84
+ }
85
+ }
86
+
87
+ tab_item('Zero-Or-One') {
88
+ vertical_box {
89
+ vertical_box {
90
+ stretchy false
91
+
92
+ @zero_or_one_table_selection_radio_buttons = radio_buttons {
93
+ items @zero_or_one_table_data.size.times.map { |row| "Row #{row} Selection" }
94
+
95
+ on_selected do |rb|
96
+ @zero_or_one_table.selection = [rb.selected]
97
+ end
98
+ }
99
+ }
100
+
101
+ button('Toggle Table Header Visibility') {
102
+ stretchy false
103
+
104
+ on_clicked do
105
+ @zero_or_one_table.header_visible = !@zero_or_one_table.header_visible
106
+ end
107
+ }
108
+
109
+ @zero_or_one_table = table {
110
+ text_column('Animal') {
111
+ # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
112
+
113
+ on_clicked do |tc, column|
114
+ sort_zero_or_one_table_column(tc, column)
115
+ end
116
+ }
117
+ text_column('Description') {
118
+ # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
119
+
120
+ on_clicked do |tc, column|
121
+ sort_zero_or_one_table_column(tc, column)
122
+ end
123
+ }
124
+
125
+ cell_rows @zero_or_one_table_data
126
+ selection_mode :zero_or_one # other values are :zero_or_many , :one, :none (default is :zero_or_one if not specified)
127
+ # selection 0 # initial selection row index (could be nil too or just left off)
128
+ # header_visible true # default
129
+ sortable false # disable default sorting behavior to demonstrate manual sorting
130
+
131
+ on_row_clicked do |t, row|
132
+ puts "Row Clicked: #{row}"
133
+ end
134
+
135
+ on_row_double_clicked do |t, row|
136
+ puts "Row Double Clicked: #{row}"
137
+ end
138
+
139
+ on_selection_changed do |t, selection, added_selection, removed_selection|
140
+ # selection is an array or nil if selection mode is zero_or_many
141
+ # otherwise, selection is a single index integer or nil when not selected
142
+ puts "Selection Changed: #{selection.inspect}"
143
+ puts "Added Selection: #{added_selection.inspect}"
144
+ puts "Removed Selection: #{removed_selection.inspect}"
145
+ @zero_or_one_table_selection_radio_buttons.selected = selection
146
+ end
147
+ }
148
+ }
149
+ }
150
+
151
+ tab_item('Zero-Or-Many') {
152
+ vertical_box {
153
+ vertical_box {
154
+ stretchy false
155
+
156
+ @zero_or_many_table_selection_checkboxes = @zero_or_many_table_data.size.times.map do |row|
157
+ checkbox("Row #{row} Selection") {
158
+ on_toggled do |c|
159
+ table_selection = @zero_or_many_table.selection.to_a
160
+ if c.checked?
161
+ table_selection << row unless table_selection.include?(row)
162
+ else
163
+ table_selection.delete(row) if table_selection.include?(row)
164
+ end
165
+ @zero_or_many_table.selection = table_selection
166
+ end
167
+ }
168
+ end
169
+ }
170
+
171
+ button('Toggle Table Header Visibility') {
172
+ stretchy false
173
+
174
+ on_clicked do
175
+ @zero_or_many_table.header_visible = !@zero_or_many_table.header_visible
176
+ end
177
+ }
178
+
179
+ @zero_or_many_table = table {
180
+ text_column('Animal') {
181
+ # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
182
+
183
+ on_clicked do |tc, column|
184
+ sort_zero_or_many_table_column(tc, column)
185
+ end
186
+ }
187
+ text_column('Description') {
188
+ # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
189
+
190
+ on_clicked do |tc, column|
191
+ sort_zero_or_many_table_column(tc, column)
192
+ end
193
+ }
194
+
195
+ cell_rows @zero_or_many_table_data
196
+ selection_mode :zero_or_many # other values are :none , :zero_or_one , and :one (default is :zero_or_one if not specified)
197
+ selection 0, 2, 4 # initial selection row indexes (could be empty array too or just left off)
198
+ # header_visible true # default
199
+ sortable false # disable default sorting behavior to demonstrate manual sorting
200
+
201
+ on_row_clicked do |t, row|
202
+ puts "Row Clicked: #{row}"
203
+ end
204
+
205
+ on_row_double_clicked do |t, row|
206
+ puts "Row Double Clicked: #{row}"
207
+ end
208
+
209
+ on_selection_changed do |t, selection, added_selection, removed_selection|
210
+ # selection is an array or nil if selection mode is zero_or_many
211
+ # otherwise, selection is a single index integer or nil when not selected
212
+ puts "Selection Changed: #{selection.inspect}"
213
+ puts "Added Selection: #{added_selection.inspect}"
214
+ puts "Removed Selection: #{removed_selection.inspect}"
215
+ removed_selection&.each do |selected_row|
216
+ @zero_or_many_table_selection_checkboxes[selected_row].checked = false
217
+ end
218
+ added_selection&.each do |selected_row|
219
+ @zero_or_many_table_selection_checkboxes[selected_row].checked = true
220
+ end
221
+ end
222
+ }
223
+ }
224
+ }
225
+
226
+ tab_item('None') {
227
+ vertical_box {
228
+ button('Toggle Table Header Visibility') {
229
+ stretchy false
230
+
231
+ on_clicked do
232
+ @none_table.header_visible = !@none_table.header_visible
233
+ end
234
+ }
235
+
236
+ @none_table = table {
237
+ text_column('Animal') {
238
+ # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
239
+
240
+ on_clicked do |tc, column|
241
+ sort_none_table_column(tc, column)
242
+ end
243
+ }
244
+ text_column('Description') {
245
+ # sort_indicator :descending # (optional) can be :ascending, :descending, or nil (default)
246
+
247
+ on_clicked do |tc, column|
248
+ sort_none_table_column(tc, column)
249
+ end
250
+ }
251
+
252
+ cell_rows @none_table_data
253
+ selection_mode :none # other values are :zero_or_many , :zero_or_one, :one (default is :zero_or_one if not specified)
254
+ # header_visible true # default
255
+ sortable false # disable default sorting behavior to demonstrate manual sorting
256
+
257
+ on_row_clicked do |t, row|
258
+ puts "Row Clicked: #{row}"
259
+ end
260
+
261
+ on_row_double_clicked do |t, row|
262
+ puts "Row Double Clicked: #{row}"
263
+ end
264
+ }
265
+ }
266
+ }
267
+
268
+ }
269
+ }
270
+ }
271
+
272
+ def sort_one_table_column(tc, column)
273
+ puts "Clicked column #{column}: #{tc.name}"
274
+ selected_row = @one_table.selection && @one_table_data[@one_table.selection]
275
+ tc.toggle_sort_indicator
276
+ @one_table_data.sort_by! { |row_data| row_data[column] }
277
+ @one_table_data.reverse! if tc.sort_indicator == :descending
278
+ @one_table.selection = @one_table_data.index(selected_row)
279
+ end
280
+
281
+ def sort_zero_or_one_table_column(tc, column)
282
+ puts "Clicked column #{column}: #{tc.name}"
283
+ selected_row = @zero_or_one_table.selection && @zero_or_one_table_data[@zero_or_one_table.selection]
284
+ tc.toggle_sort_indicator
285
+ @zero_or_one_table_data.sort_by! { |row_data| row_data[column] }
286
+ @zero_or_one_table_data.reverse! if tc.sort_indicator == :descending
287
+ @zero_or_one_table.selection = @zero_or_one_table_data.index(selected_row)
288
+ end
289
+
290
+ def sort_zero_or_many_table_column(tc, column)
291
+ puts "Clicked column #{column}: #{tc.name}"
292
+ selected_rows = @zero_or_many_table.selection&.map { |row| @zero_or_many_table_data[row] }
293
+ tc.toggle_sort_indicator
294
+ @zero_or_many_table_data.sort_by! { |row_data| row_data[column] }
295
+ @zero_or_many_table_data.reverse! if tc.sort_indicator == :descending
296
+ @zero_or_many_table.selection = selected_rows&.map {|row_data| @zero_or_many_table_data.index(row_data) }
297
+ end
298
+
299
+ def sort_none_table_column(tc, column)
300
+ puts "Clicked column #{column}: #{tc.name}"
301
+ tc.toggle_sort_indicator
302
+ @none_table_data.sort_by! { |row_data| row_data[column] }
303
+ @none_table_data.reverse! if tc.sort_indicator == :descending
304
+ end
305
+ end
306
+
307
+ BasicTableSelection.launch
Binary file
@@ -101,6 +101,12 @@ module Glimmer
101
101
 
102
102
  def handle_listener(listener_name, &listener)
103
103
  column_listeners_for(listener_name) << listener
104
+ # TODO fix this by adding a `on_button_clicked` listener in the future to separate it from `on_clicked` on the column header
105
+ begin
106
+ super # attempt to handle listener natively if this column supports it (button_column)
107
+ rescue => e
108
+ # No Op
109
+ end
104
110
  end
105
111
 
106
112
  def column_listeners
@@ -37,6 +37,28 @@ module Glimmer
37
37
  include Glimmer::FiddleConsumer
38
38
 
39
39
  CUSTOM_LISTENER_NAMES = ['on_changed', 'on_edited']
40
+ DEFAULT_COLUMN_SORT_BLOCK = lambda do |table_cell_row, column, table_proxy|
41
+ if table_cell_row.is_a?(Array)
42
+ value = table_cell_row[column]
43
+ else
44
+ attribute = table_proxy.column_attributes[column]
45
+ value = table_cell_row.send(attribute)
46
+ end
47
+ if value.is_a?(Array)
48
+ # This is needed to not crash on sorting an unsortable array
49
+ value = value.map do |element|
50
+ case element
51
+ when true
52
+ 1
53
+ when false
54
+ 0
55
+ else
56
+ element
57
+ end
58
+ end
59
+ end
60
+ value
61
+ end
40
62
 
41
63
  attr_reader :model_handler, :model, :table_params, :columns
42
64
 
@@ -66,6 +88,7 @@ module Glimmer
66
88
  configure_selection
67
89
  configure_header_visible
68
90
  configure_column_sort_indicators
91
+ configure_sorting
69
92
  end
70
93
 
71
94
  def post_initialize_child(child)
@@ -183,6 +206,7 @@ module Glimmer
183
206
  result = ::LibUI.table_header_visible(@libui)
184
207
  LibUI.integer_to_boolean(result)
185
208
  end
209
+ alias header_visible? header_visible
186
210
 
187
211
  def header_visible=(value)
188
212
  @header_visible = value
@@ -194,6 +218,17 @@ module Glimmer
194
218
  end
195
219
  alias set_header_visible header_visible=
196
220
 
221
+ def sortable
222
+ @sortable = true if @sortable.nil?
223
+ @sortable
224
+ end
225
+ alias sortable? sortable
226
+
227
+ def sortable=(value)
228
+ @sortable = value
229
+ end
230
+ alias set_sortable sortable=
231
+
197
232
  def column_attributes
198
233
  @column_attributes ||= columns.select {|column| column.is_a?(Column)}.map(&:name).map(&:underscore)
199
234
  end
@@ -625,16 +660,14 @@ module Glimmer
625
660
  def register_column_listeners
626
661
  # register accumulated column listeners after table content is closed
627
662
  return if @columns.nil? || @columns.empty?
628
- if @columns.any? {|column| column.is_a?(Column) && !column.column_listeners_for('on_clicked').empty? }
663
+ if @columns.any? {|column| column.is_a?(Column)}
629
664
  ::LibUI.table_header_on_clicked(@libui) do |_, column_index|
630
665
  actual_columns = @columns.select {|column| column.is_a?(Column)}
631
666
  column = actual_columns[column_index]
632
- if column.is_a?(Column)
667
+ if column.is_a?(Column) && !column.is_a?(Column::ButtonColumnProxy)
633
668
  column_listeners = column.column_listeners_for('on_clicked')
634
- if !column_listeners.empty?
635
- column_listeners.each do |column_listener|
636
- column_listener.call(column, column_index)
637
- end
669
+ column_listeners.each do |column_listener|
670
+ column_listener.call(column, column_index)
638
671
  end
639
672
  end
640
673
  end
@@ -654,8 +687,47 @@ module Glimmer
654
687
  end
655
688
 
656
689
  def configure_column_sort_indicators
657
- column_proxies.each {|c| c.configure_sort_indicator }
690
+ column_proxies.each(&:configure_sort_indicator)
691
+ end
692
+
693
+ def configure_sorting
694
+ if sortable?
695
+ columns.each do |column_object|
696
+ next unless column_object.is_a?(Column) && !column_object.is_a?(Column::ButtonColumnProxy)
697
+ column_object.on_clicked do |column_proxy, column|
698
+ sort_by_column(column_proxy, column)
699
+ end
700
+ end
701
+ end
702
+ end
703
+
704
+ def sort_by_column(column_proxy, column)
705
+ return unless sortable? && cell_rows.is_a?(Array)
706
+ old_selection = backup_selection
707
+ column_proxy.toggle_sort_indicator
708
+ cell_rows.sort_by! {|table_cell_row| DEFAULT_COLUMN_SORT_BLOCK.call(table_cell_row, column, self) }
709
+ cell_rows.reverse! if column_proxy.sort_indicator == :descending
710
+ restore_selection(old_selection)
711
+ end
712
+
713
+ def backup_selection
714
+ if selection_mode == ::LibUI::TableSelectionModeZeroOrMany
715
+ selected_rows = selection&.map { |row| cell_rows[row] }
716
+ else
717
+ selected_row = selection && cell_rows[selection]
718
+ end
658
719
  end
720
+
721
+ def restore_selection(old_selection)
722
+ if selection_mode == ::LibUI::TableSelectionModeZeroOrMany
723
+ selected_rows = old_selection
724
+ self.selection = selected_rows&.map {|row_data| cell_rows.index(row_data) }
725
+ else
726
+ selected_row = old_selection
727
+ self.selection = cell_rows.index(selected_row)
728
+ end
729
+ end
730
+
659
731
  end
660
732
  end
661
733
  end
@@ -126,6 +126,7 @@ module Glimmer
126
126
  end
127
127
 
128
128
  editable table_editable
129
+ sortable false # TODO disabled for now until we support it correctly in the future
129
130
  cell_rows <=> [self, :refined_model_array]
130
131
  }
131
132
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer-dsl-libui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.3
4
+ version: 0.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-19 00:00:00.000000000 Z
11
+ date: 2023-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer
@@ -358,6 +358,7 @@ files:
358
358
  - examples/basic_table_progress_bar.rb
359
359
  - examples/basic_table_selection.rb
360
360
  - examples/basic_table_selection2.rb
361
+ - examples/basic_table_selection3.rb
361
362
  - examples/basic_transform.rb
362
363
  - examples/basic_transform2.rb
363
364
  - examples/basic_window.rb