0
# Table Support
1
2
Groovy Swing provides comprehensive table support with enhanced models, custom columns, renderers, and sorters for building data-driven table interfaces.
3
4
## Basic Table Components
5
6
Core table components for displaying tabular data.
7
8
```groovy { .api }
9
// Table component
10
def table(Map attributes = [:], Closure closure = null)
11
12
// Table structure
13
def tableModel(Map attributes = [:], Closure closure = null)
14
def columnModel(Map attributes = [:])
15
def tableColumn(Map attributes = [:])
16
17
// Column types
18
def column(Map attributes = [:])
19
def propertyColumn(Map attributes = [:])
20
def closureColumn(Map attributes = [:])
21
```
22
23
## Table Models
24
25
Enhanced table models with ValueModel integration and data binding support.
26
27
```groovy { .api }
28
// Default table model with enhanced features
29
class DefaultTableModel extends AbstractTableModel {
30
DefaultTableModel()
31
DefaultTableModel(List rowData, List columnNames)
32
33
// ValueModel integration
34
ValueModel getValueModel(int row, int column)
35
void setValueModel(ValueModel model, int row, int column)
36
}
37
38
// Table model factory attributes:
39
// list: List - row data as list of maps or objects
40
// columns: List - column definitions
41
// sortable: boolean - enable sorting support
42
```
43
44
### Basic Table Examples
45
46
```groovy
47
def swing = new SwingBuilder()
48
49
// Simple table with data
50
def tableData = [
51
[name: 'John', age: 30, city: 'New York'],
52
[name: 'Jane', age: 25, city: 'Los Angeles'],
53
[name: 'Bob', age: 35, city: 'Chicago']
54
]
55
56
swing.frame(title: 'Simple Table') {
57
scrollPane {
58
table(
59
model: tableModel(list: tableData) {
60
propertyColumn(header: 'Name', propertyName: 'name', width: 100)
61
propertyColumn(header: 'Age', propertyName: 'age', width: 50)
62
propertyColumn(header: 'City', propertyName: 'city', width: 120)
63
}
64
)
65
}
66
}
67
68
// Table with explicit column model
69
swing.scrollPane {
70
table {
71
// Define table model
72
tableModel(list: tableData)
73
74
// Define column model
75
columnModel {
76
column(header: 'Name', width: 100)
77
column(header: 'Age', width: 50)
78
column(header: 'City', width: 120)
79
}
80
}
81
}
82
```
83
84
## Column Types
85
86
Different column types for various data display and editing scenarios.
87
88
### Property Columns
89
90
```groovy { .api }
91
def propertyColumn(Map attributes = [:])
92
93
// PropertyColumn attributes:
94
// header: String - column header text
95
// propertyName: String - property name to bind to
96
// type: Class - property type for editing
97
// width: int - preferred column width
98
// editable: boolean - whether column is editable
99
// renderer: TableCellRenderer - custom renderer
100
// editor: TableCellEditor - custom editor
101
```
102
103
### Closure Columns
104
105
```groovy { .api }
106
def closureColumn(Map attributes = [:])
107
108
// ClosureColumn attributes:
109
// header: String - column header text
110
// read: Closure - closure to read value from row object
111
// write: Closure - closure to write value to row object (optional)
112
// type: Class - value type
113
// width: int - preferred column width
114
// editable: boolean - whether column is editable
115
```
116
117
### Column Examples
118
119
```groovy
120
def people = [
121
new Person(firstName: 'John', lastName: 'Doe', age: 30),
122
new Person(firstName: 'Jane', lastName: 'Smith', age: 25)
123
]
124
125
swing.scrollPane {
126
table(model: tableModel(list: people)) {
127
// Property-based columns
128
propertyColumn(
129
header: 'First Name',
130
propertyName: 'firstName',
131
width: 100,
132
editable: true
133
)
134
135
propertyColumn(
136
header: 'Last Name',
137
propertyName: 'lastName',
138
width: 100,
139
editable: true
140
)
141
142
propertyColumn(
143
header: 'Age',
144
propertyName: 'age',
145
type: Integer,
146
width: 50,
147
editable: true
148
)
149
150
// Closure-based computed column
151
closureColumn(
152
header: 'Full Name',
153
read: { row -> "${row.firstName} ${row.lastName}" },
154
width: 150,
155
editable: false
156
)
157
158
// Closure column with custom formatting
159
closureColumn(
160
header: 'Status',
161
read: { row -> row.age >= 30 ? 'Senior' : 'Junior' },
162
width: 80
163
)
164
}
165
}
166
```
167
168
## Table Cell Renderers
169
170
Custom renderers for specialized cell display.
171
172
```groovy { .api }
173
// Renderer factories
174
def tableCellRenderer(Map attributes = [:])
175
def listCellRenderer(Map attributes = [:])
176
def cellRenderer(Map attributes = [:])
177
def headerRenderer(Map attributes = [:])
178
179
// Renderer update factory
180
def onRender(Closure renderClosure)
181
```
182
183
### Renderer Examples
184
185
```groovy
186
swing.scrollPane {
187
table(model: tableModel(list: salesData)) {
188
propertyColumn(header: 'Product', propertyName: 'product')
189
190
propertyColumn(header: 'Revenue', propertyName: 'revenue') {
191
// Custom currency renderer
192
tableCellRenderer { renderer, table, value, isSelected, hasFocus, row, column ->
193
renderer.text = NumberFormat.getCurrencyInstance().format(value)
194
if (value > 10000) {
195
renderer.foreground = Color.GREEN
196
renderer.font = renderer.font.deriveFont(Font.BOLD)
197
} else {
198
renderer.foreground = Color.BLACK
199
renderer.font = renderer.font.deriveFont(Font.PLAIN)
200
}
201
return renderer
202
}
203
}
204
205
propertyColumn(header: 'Status', propertyName: 'status') {
206
// Icon renderer based on status
207
tableCellRenderer { renderer, table, value, isSelected, hasFocus, row, column ->
208
renderer.text = value
209
renderer.icon = value == 'Active' ? activeIcon : inactiveIcon
210
return renderer
211
}
212
}
213
214
// Progress bar renderer for percentage column
215
propertyColumn(header: 'Progress', propertyName: 'percentage') {
216
tableCellRenderer { renderer, table, value, isSelected, hasFocus, row, column ->
217
def progressBar = new JProgressBar(0, 100)
218
progressBar.value = value
219
progressBar.stringPainted = true
220
progressBar.string = "${value}%"
221
return progressBar
222
}
223
}
224
}
225
}
226
```
227
228
## Table Cell Editors
229
230
Custom editors for specialized cell editing.
231
232
```groovy { .api }
233
// Editor factories
234
def cellEditor(Map attributes = [:])
235
def editorValue(Closure valueClosure)
236
def prepareEditor(Closure prepareClosure)
237
```
238
239
### Editor Examples
240
241
```groovy
242
swing.scrollPane {
243
table(model: tableModel(list: employeeData)) {
244
propertyColumn(header: 'Name', propertyName: 'name')
245
246
propertyColumn(header: 'Department', propertyName: 'department') {
247
// Combo box editor
248
cellEditor {
249
comboBox(items: ['Sales', 'Marketing', 'Engineering', 'HR'])
250
}
251
}
252
253
propertyColumn(header: 'Salary', propertyName: 'salary') {
254
// Formatted text field editor
255
cellEditor {
256
formattedTextField(format: NumberFormat.getCurrencyInstance())
257
}
258
}
259
260
propertyColumn(header: 'Start Date', propertyName: 'startDate') {
261
// Custom date picker editor
262
cellEditor {
263
bean(class: 'com.toedter.calendar.JDateChooser')
264
}
265
266
// Custom value extraction
267
editorValue { editor ->
268
return editor.date
269
}
270
271
// Custom editor preparation
272
prepareEditor { editor, table, value, isSelected, row, column ->
273
editor.date = value
274
return editor
275
}
276
}
277
}
278
}
279
```
280
281
## Table Sorting
282
283
Built-in sorting support with TableSorter integration.
284
285
```groovy { .api }
286
// TableSorter class for sortable table models
287
class TableSorter extends TableMap {
288
TableSorter(TableModel model)
289
290
void setTableHeader(JTableHeader tableHeader)
291
boolean isSorting()
292
int getSortingStatus(int column)
293
void setSortingStatus(int column, int status)
294
}
295
296
// Sorting constants
297
// DESCENDING = -1
298
// NOT_SORTED = 0
299
// ASCENDING = 1
300
```
301
302
### Sorting Examples
303
304
```groovy
305
import groovy.swing.table.TableSorter
306
307
def tableData = [
308
[name: 'Alice', age: 30, salary: 50000],
309
[name: 'Bob', age: 25, salary: 45000],
310
[name: 'Charlie', age: 35, salary: 60000]
311
]
312
313
swing.frame(title: 'Sortable Table') {
314
scrollPane {
315
def tableModel = tableModel(list: tableData)
316
def sorter = new TableSorter(tableModel)
317
318
table(model: sorter) {
319
// Connect sorter to table header for click sorting
320
sorter.setTableHeader(it.tableHeader)
321
322
propertyColumn(header: 'Name', propertyName: 'name')
323
propertyColumn(header: 'Age', propertyName: 'age', type: Integer)
324
propertyColumn(header: 'Salary', propertyName: 'salary', type: Integer)
325
}
326
}
327
}
328
329
// Programmatic sorting
330
sorter.setSortingStatus(1, TableSorter.ASCENDING) // Sort by age ascending
331
```
332
333
## Table Selection and Events
334
335
Table selection handling and event processing.
336
337
```groovy { .api }
338
// Selection binding support
339
// selectedRow: int - selected row index
340
// selectedColumn: int - selected column index
341
// selectedElement: Object - selected row object
342
// selectedElements: List - multiple selected objects
343
```
344
345
### Selection Examples
346
347
```groovy
348
def selectedPerson = new ValueHolder()
349
350
swing.frame(title: 'Table Selection') {
351
borderLayout()
352
353
// Table with selection binding
354
scrollPane(constraints: BorderLayout.CENTER) {
355
table(
356
model: tableModel(list: people),
357
selectedElement: bind(source: selectedPerson, sourceProperty: 'value')
358
) {
359
propertyColumn(header: 'Name', propertyName: 'name')
360
propertyColumn(header: 'Age', propertyName: 'age')
361
}
362
}
363
364
// Detail panel showing selected person
365
panel(constraints: BorderLayout.SOUTH) {
366
label(text: bind(
367
source: selectedPerson,
368
sourceProperty: 'value',
369
converter: { person ->
370
person ? "Selected: ${person.name} (${person.age})" : "No selection"
371
}
372
))
373
}
374
}
375
376
// Multiple selection
377
swing.table(
378
selectionMode: ListSelectionModel.MULTIPLE_INTERVAL_SELECTION,
379
selectedElements: bind(source: selectedItems, sourceProperty: 'value')
380
) {
381
// Table columns...
382
}
383
```
384
385
## Advanced Table Features
386
387
### Custom Table Models
388
389
```groovy
390
// Custom table model with live data
391
class LiveDataTableModel extends DefaultTableModel {
392
void startUpdating() {
393
timer = new Timer(1000) {
394
// Update data periodically
395
fireTableDataChanged()
396
}
397
timer.start()
398
}
399
}
400
401
swing.table(model: new LiveDataTableModel()) {
402
// Column definitions...
403
}
404
```
405
406
### Table with Filtering
407
408
```groovy
409
def originalData = [/* data */]
410
def filteredData = new FilteredListModel(originalData)
411
def filterText = new ValueHolder('')
412
413
swing.frame(title: 'Filtered Table') {
414
borderLayout()
415
416
// Filter input
417
panel(constraints: BorderLayout.NORTH) {
418
label(text: 'Filter:')
419
textField(
420
columns: 20,
421
text: bind(source: filterText, sourceProperty: 'value')
422
) {
423
// Apply filter on text change
424
bind(source: filterText, sourceProperty: 'value') { newValue ->
425
filteredData.filter = { item ->
426
item.name.toLowerCase().contains(newValue.toLowerCase())
427
}
428
}
429
}
430
}
431
432
// Filtered table
433
scrollPane(constraints: BorderLayout.CENTER) {
434
table(model: tableModel(list: filteredData)) {
435
propertyColumn(header: 'Name', propertyName: 'name')
436
propertyColumn(header: 'Description', propertyName: 'description')
437
}
438
}
439
}
440
```