0
# Menu System
1
2
Groovy Swing provides comprehensive menu system support with factories for creating menu bars, menus, menu items, and popup menus with full action integration.
3
4
## Menu Components
5
6
Core menu components for building application menu systems.
7
8
```groovy { .api }
9
// Menu containers
10
def menuBar(Map attributes = [:], Closure closure = null)
11
def menu(Map attributes = [:], Closure closure = null)
12
def popupMenu(Map attributes = [:], Closure closure = null)
13
14
// Menu items
15
def menuItem(Map attributes = [:], Closure closure = null)
16
def checkBoxMenuItem(Map attributes = [:], Closure closure = null)
17
def radioButtonMenuItem(Map attributes = [:], Closure closure = null)
18
19
// Separators
20
def separator(Map attributes = [:])
21
```
22
23
## Basic Menu Structure
24
25
Creating standard application menus with menu bars and menu items.
26
27
### Menu Bar Examples
28
29
```groovy
30
swing.frame(title: 'Menu Example') {
31
// Create menu bar
32
menuBar {
33
// File menu
34
menu(text: 'File', mnemonic: 'F') {
35
menuItem(text: 'New', mnemonic: 'N', accelerator: shortcut('N')) {
36
actionPerformed { println 'New file' }
37
}
38
menuItem(text: 'Open', mnemonic: 'O', accelerator: shortcut('O')) {
39
actionPerformed { println 'Open file' }
40
}
41
menuItem(text: 'Save', mnemonic: 'S', accelerator: shortcut('S')) {
42
actionPerformed { println 'Save file' }
43
}
44
separator()
45
menuItem(text: 'Exit', mnemonic: 'x') {
46
actionPerformed { System.exit(0) }
47
}
48
}
49
50
// Edit menu
51
menu(text: 'Edit', mnemonic: 'E') {
52
menuItem(text: 'Cut', accelerator: shortcut('X')) {
53
actionPerformed { println 'Cut' }
54
}
55
menuItem(text: 'Copy', accelerator: shortcut('C')) {
56
actionPerformed { println 'Copy' }
57
}
58
menuItem(text: 'Paste', accelerator: shortcut('V')) {
59
actionPerformed { println 'Paste' }
60
}
61
}
62
63
// View menu with checkboxes
64
menu(text: 'View', mnemonic: 'V') {
65
checkBoxMenuItem(text: 'Show Toolbar', selected: true) {
66
actionPerformed { e ->
67
println "Toolbar ${e.source.selected ? 'shown' : 'hidden'}"
68
}
69
}
70
checkBoxMenuItem(text: 'Show Status Bar', selected: false) {
71
actionPerformed { e ->
72
println "Status bar ${e.source.selected ? 'shown' : 'hidden'}"
73
}
74
}
75
}
76
}
77
78
// Main content
79
panel {
80
label(text: 'Application content')
81
}
82
}
83
```
84
85
## Action Integration
86
87
Using Action objects for consistent menu and toolbar integration.
88
89
```groovy { .api }
90
// Action factory for reusable actions
91
def action(Map attributes = [:])
92
93
// Action attributes:
94
// name: String - action name/text
95
// closure: Closure - action code
96
// mnemonic: String/int - keyboard mnemonic
97
// accelerator: KeyStroke - keyboard shortcut
98
// smallIcon: Icon - small icon for toolbar
99
// largeIcon: Icon - large icon
100
// shortDescription: String - tooltip text
101
// longDescription: String - detailed description
102
// enabled: boolean - whether action is enabled
103
```
104
105
### Action Examples
106
107
```groovy
108
def swing = new SwingBuilder()
109
110
// Define reusable actions
111
def newAction = swing.action(
112
name: 'New',
113
closure: { println 'Creating new document' },
114
mnemonic: 'N',
115
accelerator: swing.shortcut('N'),
116
smallIcon: swing.imageIcon('/icons/new.png'),
117
shortDescription: 'Create a new document'
118
)
119
120
def saveAction = swing.action(
121
name: 'Save',
122
closure: { println 'Saving document' },
123
mnemonic: 'S',
124
accelerator: swing.shortcut('S'),
125
smallIcon: swing.imageIcon('/icons/save.png'),
126
shortDescription: 'Save the current document'
127
)
128
129
def exitAction = swing.action(
130
name: 'Exit',
131
closure: { System.exit(0) },
132
mnemonic: 'x',
133
shortDescription: 'Exit the application'
134
)
135
136
// Use actions in both menu and toolbar
137
swing.frame(title: 'Action Integration') {
138
borderLayout()
139
140
// Menu bar using actions
141
menuBar {
142
menu(text: 'File') {
143
menuItem(action: newAction)
144
separator()
145
menuItem(action: saveAction)
146
separator()
147
menuItem(action: exitAction)
148
}
149
}
150
151
// Toolbar using same actions
152
toolBar(constraints: BorderLayout.NORTH) {
153
button(action: newAction)
154
button(action: saveAction)
155
separator()
156
button(action: exitAction)
157
}
158
159
// Content area
160
panel(constraints: BorderLayout.CENTER) {
161
label(text: 'Document content')
162
}
163
}
164
```
165
166
## Popup Menus
167
168
Context menus that appear on right-click or other triggers.
169
170
```groovy { .api }
171
def popupMenu(Map attributes = [:], Closure closure = null)
172
173
// PopupMenu is typically shown via:
174
// component.componentPopupMenu = popupMenu
175
// or programmatically with show(component, x, y)
176
```
177
178
### Popup Menu Examples
179
180
```groovy
181
// Create popup menu
182
def contextMenu = swing.popupMenu {
183
menuItem(text: 'Cut') {
184
actionPerformed { println 'Cut from context menu' }
185
}
186
menuItem(text: 'Copy') {
187
actionPerformed { println 'Copy from context menu' }
188
}
189
menuItem(text: 'Paste') {
190
actionPerformed { println 'Paste from context menu' }
191
}
192
separator()
193
menuItem(text: 'Properties') {
194
actionPerformed { println 'Show properties' }
195
}
196
}
197
198
// Attach popup to component
199
swing.textArea(
200
rows: 10,
201
columns: 40,
202
componentPopupMenu: contextMenu
203
)
204
205
// Manual popup triggering
206
swing.panel {
207
mouseClicked { e ->
208
if (e.button == MouseEvent.BUTTON3) { // Right click
209
contextMenu.show(e.component, e.x, e.y)
210
}
211
}
212
}
213
```
214
215
## Menu Item Types
216
217
Different types of menu items for various interaction patterns.
218
219
### Checkbox Menu Items
220
221
```groovy
222
def viewSettings = [
223
showToolbar: true,
224
showStatusBar: false,
225
showSidebar: true
226
]
227
228
swing.menu(text: 'View') {
229
checkBoxMenuItem(
230
text: 'Show Toolbar',
231
selected: bind(source: viewSettings, sourceProperty: 'showToolbar')
232
) {
233
actionPerformed { e ->
234
viewSettings.showToolbar = e.source.selected
235
updateToolbarVisibility(viewSettings.showToolbar)
236
}
237
}
238
239
checkBoxMenuItem(
240
text: 'Show Status Bar',
241
selected: bind(source: viewSettings, sourceProperty: 'showStatusBar')
242
) {
243
actionPerformed { e ->
244
viewSettings.showStatusBar = e.source.selected
245
updateStatusBarVisibility(viewSettings.showStatusBar)
246
}
247
}
248
}
249
```
250
251
### Radio Button Menu Items
252
253
```groovy
254
def documentMode = new ValueHolder('edit')
255
256
swing.menu(text: 'Mode') {
257
def modeGroup = buttonGroup()
258
259
radioButtonMenuItem(
260
text: 'Edit Mode',
261
buttonGroup: modeGroup,
262
selected: bind(source: documentMode, sourceProperty: 'value',
263
converter: { it == 'edit' })
264
) {
265
actionPerformed {
266
documentMode.value = 'edit'
267
switchToEditMode()
268
}
269
}
270
271
radioButtonMenuItem(
272
text: 'Preview Mode',
273
buttonGroup: modeGroup,
274
selected: bind(source: documentMode, sourceProperty: 'value',
275
converter: { it == 'preview' })
276
) {
277
actionPerformed {
278
documentMode.value = 'preview'
279
switchToPreviewMode()
280
}
281
}
282
283
radioButtonMenuItem(
284
text: 'Presentation Mode',
285
buttonGroup: modeGroup,
286
selected: bind(source: documentMode, sourceProperty: 'value',
287
converter: { it == 'presentation' })
288
) {
289
actionPerformed {
290
documentMode.value = 'presentation'
291
switchToPresentationMode()
292
}
293
}
294
}
295
```
296
297
## Dynamic Menus
298
299
Creating menus that change based on application state.
300
301
### Recent Files Menu
302
303
```groovy
304
class RecentFilesManager {
305
List<String> recentFiles = []
306
int maxFiles = 5
307
308
void addFile(String filename) {
309
recentFiles.remove(filename) // Remove if already exists
310
recentFiles.add(0, filename) // Add to beginning
311
if (recentFiles.size() > maxFiles) {
312
recentFiles = recentFiles[0..<maxFiles]
313
}
314
}
315
}
316
317
def recentFilesManager = new RecentFilesManager()
318
319
def updateRecentFilesMenu = { menu ->
320
// Clear existing recent file items
321
menu.removeAll()
322
323
if (recentFilesManager.recentFiles.empty) {
324
def item = swing.menuItem(text: 'No recent files', enabled: false)
325
menu.add(item)
326
} else {
327
recentFilesManager.recentFiles.eachWithIndex { filename, index ->
328
def item = swing.menuItem(
329
text: "&${index + 1} ${new File(filename).name}",
330
toolTipText: filename
331
) {
332
actionPerformed {
333
openFile(filename)
334
}
335
}
336
menu.add(item)
337
}
338
}
339
}
340
341
swing.frame(title: 'Dynamic Menu Example') {
342
menuBar {
343
menu(text: 'File') {
344
menuItem(text: 'New') { actionPerformed { /* new file logic */ } }
345
menuItem(text: 'Open') {
346
actionPerformed {
347
def filename = showOpenDialog()
348
if (filename) {
349
openFile(filename)
350
recentFilesManager.addFile(filename)
351
updateRecentFilesMenu(recentFilesMenu)
352
}
353
}
354
}
355
separator()
356
357
// Recent files submenu
358
def recentFilesMenu = menu(text: 'Recent Files')
359
updateRecentFilesMenu(recentFilesMenu)
360
}
361
}
362
}
363
```
364
365
## Menu Styling and Icons
366
367
Customizing menu appearance with icons, fonts, and colors.
368
369
```groovy
370
swing.menuBar {
371
menu(text: 'File', font: new Font('Arial', Font.BOLD, 12)) {
372
menuItem(
373
text: 'New Document',
374
icon: swing.imageIcon('/icons/document-new.png'),
375
accelerator: swing.shortcut('N')
376
) {
377
actionPerformed { println 'New document' }
378
}
379
380
menuItem(
381
text: 'Open Document',
382
icon: swing.imageIcon('/icons/document-open.png'),
383
accelerator: swing.shortcut('O')
384
) {
385
actionPerformed { println 'Open document' }
386
}
387
388
separator()
389
390
menuItem(
391
text: 'Recent Documents',
392
icon: swing.imageIcon('/icons/document-recent.png')
393
) {
394
// Submenu with recent files
395
}
396
}
397
398
menu(text: 'Tools') {
399
menuItem(
400
text: 'Preferences',
401
icon: swing.imageIcon('/icons/preferences.png'),
402
foreground: Color.BLUE
403
) {
404
actionPerformed { showPreferences() }
405
}
406
}
407
}
408
```
409
410
## Keyboard Navigation
411
412
Setting up proper keyboard navigation and mnemonics.
413
414
```groovy
415
swing.menuBar {
416
menu(text: 'File', mnemonic: 'F') { // Alt+F to open
417
menuItem(
418
text: 'New',
419
mnemonic: 'N', // Alt+F, N to activate
420
accelerator: swing.shortcut('N') // Ctrl+N direct shortcut
421
)
422
menuItem(
423
text: 'Open Recent',
424
mnemonic: 'R'
425
) {
426
// Submenu
427
menuItem(text: 'Document 1.txt', mnemonic: '1')
428
menuItem(text: 'Document 2.txt', mnemonic: '2')
429
}
430
}
431
432
menu(text: 'Edit', mnemonic: 'E') { // Alt+E to open
433
menuItem(
434
text: 'Undo',
435
mnemonic: 'U',
436
accelerator: swing.shortcut('Z')
437
)
438
menuItem(
439
text: 'Redo',
440
mnemonic: 'R',
441
accelerator: swing.shortcut('Y')
442
)
443
}
444
}
445
```
446
447
## Menu State Management
448
449
Managing menu item states based on application context.
450
451
```groovy
452
class MenuStateManager {
453
boolean hasSelection = false
454
boolean hasContent = false
455
boolean canUndo = false
456
boolean canRedo = false
457
458
def cutAction, copyAction, pasteAction, undoAction, redoAction
459
460
void updateMenuStates() {
461
cutAction?.enabled = hasSelection
462
copyAction?.enabled = hasSelection
463
undoAction?.enabled = canUndo
464
redoAction?.enabled = canRedo
465
}
466
}
467
468
def stateManager = new MenuStateManager()
469
470
// Create actions with initial states
471
stateManager.cutAction = swing.action(
472
name: 'Cut',
473
enabled: false,
474
closure: { performCut() }
475
)
476
477
stateManager.copyAction = swing.action(
478
name: 'Copy',
479
enabled: false,
480
closure: { performCopy() }
481
)
482
483
// Update states based on events
484
textComponent.selectionListener = { e ->
485
stateManager.hasSelection = (e.source.selectedText != null)
486
stateManager.updateMenuStates()
487
}
488
489
// Use in menu
490
swing.menuBar {
491
menu(text: 'Edit') {
492
menuItem(action: stateManager.cutAction)
493
menuItem(action: stateManager.copyAction)
494
// ... other items
495
}
496
}
497
```