Apache Groovy - A powerful multi-faceted programming language for the JVM platform with comprehensive module support
—
Groovy provides powerful Swing integration through SwingBuilder, enabling declarative GUI construction with a fluent, builder-pattern API that simplifies desktop application development.
class SwingBuilder extends FactoryBuilderSupport {
SwingBuilder()
SwingBuilder(boolean init)
Object invokeMethod(String name, Object args)
// Container methods
JFrame frame(Map attributes, Closure content)
JFrame frame(Closure content)
JDialog dialog(Map attributes, Closure content)
JPanel panel(Map attributes, Closure content)
JScrollPane scrollPane(Map attributes, Closure content)
JSplitPane splitPane(Map attributes, Closure content)
JTabbedPane tabbedPane(Map attributes, Closure content)
// Control methods
JButton button(Map attributes, Closure content)
JLabel label(Map attributes, Closure content)
JTextField textField(Map attributes)
JTextArea textArea(Map attributes)
JCheckBox checkBox(Map attributes)
JRadioButton radioButton(Map attributes)
JComboBox comboBox(Map attributes)
JList list(Map attributes)
JTable table(Map attributes)
JTree tree(Map attributes)
// Menu methods
JMenuBar menuBar(Map attributes, Closure content)
JMenu menu(Map attributes, Closure content)
JMenuItem menuItem(Map attributes, Closure content)
JPopupMenu popupMenu(Map attributes, Closure content)
// Layout methods
Object borderLayout(Map attributes)
Object flowLayout(Map attributes)
Object gridLayout(Map attributes)
Object gridBagLayout(Map attributes)
Object cardLayout(Map attributes)
Object boxLayout(Map attributes)
// Action methods
Action action(Map attributes, Closure closure)
void bind(Map attributes)
// Event handling
void edt(Closure closure)
void doLater(Closure closure)
void doOutside(Closure closure)
}import groovy.swing.SwingBuilder
import javax.swing.WindowConstants
def swing = new SwingBuilder()
def frame = swing.frame(
title: 'My Application',
size: [400, 300],
locationRelativeTo: null,
defaultCloseOperation: WindowConstants.EXIT_ON_CLOSE
) {
panel {
label(text: 'Hello, Swing!')
button(text: 'Click Me', actionPerformed: {
println 'Button clicked!'
})
}
}
frame.setVisible(true)import groovy.swing.SwingBuilder
import java.awt.BorderLayout
import java.awt.FlowLayout
import java.awt.GridLayout
def swing = new SwingBuilder()
def frame = swing.frame(title: 'Layout Examples', size: [500, 400]) {
// BorderLayout example
borderLayout()
panel(constraints: BorderLayout.NORTH) {
flowLayout()
label(text: 'North Panel')
button(text: 'Button 1')
button(text: 'Button 2')
}
panel(constraints: BorderLayout.CENTER) {
gridLayout(rows: 2, cols: 2, hgap: 5, vgap: 5)
button(text: 'Grid 1')
button(text: 'Grid 2')
button(text: 'Grid 3')
button(text: 'Grid 4')
}
panel(constraints: BorderLayout.SOUTH) {
flowLayout(alignment: FlowLayout.RIGHT)
button(text: 'OK')
button(text: 'Cancel')
}
}
frame.setVisible(true)import groovy.swing.SwingBuilder
import javax.swing.border.TitledBorder
def swing = new SwingBuilder()
def model = [name: '', age: 0, subscribed: false, category: 'Bronze']
def frame = swing.frame(title: 'Input Form', size: [400, 300]) {
borderLayout()
panel(constraints: BorderLayout.CENTER, border: new TitledBorder('User Information')) {
gridBagLayout()
label(text: 'Name:', constraints: gbc(gridx: 0, gridy: 0, anchor: WEST))
textField(
columns: 20,
constraints: gbc(gridx: 1, gridy: 0, fill: HORIZONTAL),
text: bind(source: model, sourceProperty: 'name', mutual: true)
)
label(text: 'Age:', constraints: gbc(gridx: 0, gridy: 1, anchor: WEST))
spinner(
model: spinnerNumberModel(value: 18, minimum: 0, maximum: 120),
constraints: gbc(gridx: 1, gridy: 1),
stateChanged: { e -> model.age = e.source.value }
)
checkBox(
text: 'Subscribe to newsletter',
constraints: gbc(gridx: 0, gridy: 2, gridwidth: 2),
selected: bind(source: model, sourceProperty: 'subscribed', mutual: true)
)
label(text: 'Category:', constraints: gbc(gridx: 0, gridy: 3, anchor: WEST))
comboBox(
items: ['Bronze', 'Silver', 'Gold', 'Platinum'],
constraints: gbc(gridx: 1, gridy: 3),
selectedItem: bind(source: model, sourceProperty: 'category', mutual: true)
)
}
panel(constraints: BorderLayout.SOUTH) {
flowLayout()
button(text: 'Submit', actionPerformed: {
println "Name: ${model.name}"
println "Age: ${model.age}"
println "Subscribed: ${model.subscribed}"
println "Category: ${model.category}"
})
button(text: 'Reset', actionPerformed: {
model.name = ''
model.age = 18
model.subscribed = false
model.category = 'Bronze'
})
}
}
frame.setVisible(true)import groovy.swing.SwingBuilder
import javax.swing.table.DefaultTableModel
def swing = new SwingBuilder()
// Sample data
def data = [
['John Doe', 30, 'Engineering', 75000],
['Jane Smith', 25, 'Marketing', 65000],
['Bob Johnson', 35, 'Sales', 70000],
['Alice Brown', 28, 'Engineering', 80000]
]
def columnNames = ['Name', 'Age', 'Department', 'Salary']
def tableModel = new DefaultTableModel(data as Object[][], columnNames as Object[])
def frame = swing.frame(title: 'Employee Table', size: [600, 400]) {
borderLayout()
scrollPane(constraints: BorderLayout.CENTER) {
table(
model: tableModel,
selectionMode: SINGLE_SELECTION,
mouseClicked: { e ->
if (e.clickCount == 2) {
def table = e.source
def row = table.selectedRow
if (row >= 0) {
def name = tableModel.getValueAt(row, 0)
swing.optionPane().showMessageDialog(
frame,
"Selected employee: $name",
'Selection',
swing.optionPane.INFORMATION_MESSAGE
)
}
}
}
)
}
panel(constraints: BorderLayout.SOUTH) {
flowLayout()
button(text: 'Add Employee', actionPerformed: {
def name = swing.optionPane().showInputDialog('Enter name:')
if (name) {
tableModel.addRow(['New Employee', 25, 'Department', 50000] as Object[])
}
})
button(text: 'Remove Selected', actionPerformed: {
def table = frame.contentPane.getComponent(0).viewport.view
def selectedRow = table.selectedRow
if (selectedRow >= 0) {
tableModel.removeRow(selectedRow)
}
})
}
}
frame.setVisible(true)import groovy.swing.SwingBuilder
import javax.swing.KeyStroke
import java.awt.event.KeyEvent
def swing = new SwingBuilder()
def frame = swing.frame(title: 'Menu Example', size: [500, 300]) {
menuBar {
menu(text: 'File', mnemonic: 'F') {
menuItem(
text: 'New',
mnemonic: 'N',
accelerator: KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_MASK),
actionPerformed: { println 'New file' }
)
menuItem(
text: 'Open',
mnemonic: 'O',
accelerator: KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_MASK),
actionPerformed: { println 'Open file' }
)
separator()
menuItem(
text: 'Exit',
mnemonic: 'x',
actionPerformed: { System.exit(0) }
)
}
menu(text: 'Edit', mnemonic: 'E') {
menuItem(text: 'Cut', accelerator: KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.CTRL_MASK))
menuItem(text: 'Copy', accelerator: KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_MASK))
menuItem(text: 'Paste', accelerator: KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_MASK))
}
menu(text: 'View', mnemonic: 'V') {
checkBoxMenuItem(text: 'Show Toolbar', selected: true)
checkBoxMenuItem(text: 'Show Status Bar', selected: false)
separator()
// Radio button group
def group = buttonGroup()
radioButtonMenuItem(text: 'Small Icons', buttonGroup: group, selected: true)
radioButtonMenuItem(text: 'Large Icons', buttonGroup: group)
}
}
// Main content area
textArea(text: 'Main content area...', editable: false)
}
frame.setVisible(true)import groovy.swing.SwingBuilder
import groovy.beans.Bindable
class Person {
@Bindable String firstName = ''
@Bindable String lastName = ''
@Bindable int age = 0
@Bindable boolean employed = false
String getFullName() {
return "$firstName $lastName".trim()
}
}
def swing = new SwingBuilder()
def person = new Person()
def frame = swing.frame(title: 'Data Binding Example', size: [400, 250]) {
gridBagLayout()
label(text: 'First Name:', constraints: gbc(gridx: 0, gridy: 0, anchor: WEST))
textField(
columns: 15,
constraints: gbc(gridx: 1, gridy: 0, fill: HORIZONTAL),
text: bind(source: person, sourceProperty: 'firstName', mutual: true)
)
label(text: 'Last Name:', constraints: gbc(gridx: 0, gridy: 1, anchor: WEST))
textField(
columns: 15,
constraints: gbc(gridx: 1, gridy: 1, fill: HORIZONTAL),
text: bind(source: person, sourceProperty: 'lastName', mutual: true)
)
label(text: 'Age:', constraints: gbc(gridx: 0, gridy: 2, anchor: WEST))
spinner(
model: spinnerNumberModel(minimum: 0, maximum: 120),
constraints: gbc(gridx: 1, gridy: 2),
value: bind(source: person, sourceProperty: 'age', mutual: true)
)
checkBox(
text: 'Employed',
constraints: gbc(gridx: 0, gridy: 3, gridwidth: 2),
selected: bind(source: person, sourceProperty: 'employed', mutual: true)
)
separator(constraints: gbc(gridx: 0, gridy: 4, gridwidth: 2, fill: HORIZONTAL))
label(
text: bind(source: person, sourceProperty: 'fullName'),
constraints: gbc(gridx: 0, gridy: 5, gridwidth: 2, anchor: CENTER),
font: label().font.deriveFont(16f)
)
button(
text: 'Print Info',
constraints: gbc(gridx: 0, gridy: 6, gridwidth: 2),
actionPerformed: {
println "Person: ${person.fullName}, Age: ${person.age}, Employed: ${person.employed}"
}
)
}
frame.setVisible(true)import groovy.swing.SwingBuilder
import javax.swing.JPanel
import java.awt.Graphics
import java.awt.Color
class DrawingPanel extends JPanel {
private List<Point> points = []
DrawingPanel() {
background = Color.WHITE
addMouseListener([
mouseClicked: { e ->
points.add(new Point(e.x, e.y))
repaint()
}
] as java.awt.event.MouseAdapter)
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g)
g.color = Color.BLUE
points.each { point ->
g.fillOval(point.x - 3, point.y - 3, 6, 6)
}
}
void clearPoints() {
points.clear()
repaint()
}
}
def swing = new SwingBuilder()
def drawingPanel = new DrawingPanel()
def frame = swing.frame(title: 'Custom Drawing Panel', size: [500, 400]) {
borderLayout()
widget(drawingPanel, constraints: BorderLayout.CENTER)
panel(constraints: BorderLayout.SOUTH) {
flowLayout()
button(text: 'Clear', actionPerformed: {
drawingPanel.clearPoints()
})
button(text: 'Count Points', actionPerformed: {
swing.optionPane().showMessageDialog(
frame,
"Points drawn: ${drawingPanel.points.size()}",
'Point Count',
swing.optionPane.INFORMATION_MESSAGE
)
})
}
}
frame.setVisible(true)import groovy.swing.SwingBuilder
import javax.swing.SwingUtilities
import java.util.concurrent.Executors
def swing = new SwingBuilder()
def frame = swing.frame(title: 'Threading Example', size: [400, 200]) {
borderLayout()
def progressBar = progressBar(
minimum: 0,
maximum: 100,
value: 0,
stringPainted: true,
constraints: BorderLayout.CENTER
)
panel(constraints: BorderLayout.SOUTH) {
flowLayout()
button(text: 'Start Task', actionPerformed: {
// Run long task in background thread
swing.doOutside {
(1..100).each { i ->
Thread.sleep(50) // Simulate work
// Update UI on EDT
swing.edt {
progressBar.value = i
progressBar.string = "Processing ${i}%"
}
}
// Final update on EDT
swing.edt {
progressBar.string = "Complete!"
swing.optionPane().showMessageDialog(
frame,
'Task completed successfully!',
'Success',
swing.optionPane.INFORMATION_MESSAGE
)
}
}
})
button(text: 'Reset', actionPerformed: {
progressBar.value = 0
progressBar.string = null
})
}
}
frame.setVisible(true)import groovy.swing.SwingBuilder
import javax.swing.Timer
import java.text.SimpleDateFormat
def swing = new SwingBuilder()
def dateFormat = new SimpleDateFormat('yyyy-MM-dd HH:mm:ss')
def frame = swing.frame(title: 'Clock Example', size: [300, 100]) {
borderLayout()
def timeLabel = label(
text: dateFormat.format(new Date()),
constraints: BorderLayout.CENTER,
horizontalAlignment: swing.label.CENTER,
font: label().font.deriveFont(18f)
)
// Update every second
def timer = new Timer(1000) { e ->
timeLabel.text = dateFormat.format(new Date())
}
timer.start()
// Stop timer when window closes
addWindowListener([
windowClosing: { e -> timer.stop() }
] as java.awt.event.WindowAdapter)
}
frame.setVisible(true)import groovy.swing.SwingBuilder
import javax.swing.UIManager
import javax.swing.plaf.nimbus.NimbusLookAndFeel
// Set look and feel
try {
UIManager.setLookAndFeel(new NimbusLookAndFeel())
} catch (Exception e) {
println "Could not set Nimbus L&F: ${e.message}"
}
def swing = new SwingBuilder()
def frame = swing.frame(title: 'Styled Application', size: [400, 300]) {
borderLayout()
panel(constraints: BorderLayout.NORTH, background: java.awt.Color.LIGHT_GRAY) {
flowLayout()
label(text: 'Styled Header', font: label().font.deriveFont(16f))
}
tabbedPane(constraints: BorderLayout.CENTER) {
panel(name: 'Tab 1') {
gridLayout(rows: 3, cols: 2, hgap: 10, vgap: 10)
label(text: 'Name:')
textField(columns: 15)
label(text: 'Email:')
textField(columns: 15)
label(text: 'Notes:')
scrollPane {
textArea(rows: 3, columns: 15)
}
}
panel(name: 'Tab 2') {
boxLayout(axis: swing.boxLayout.Y_AXIS)
checkBox(text: 'Option 1')
checkBox(text: 'Option 2')
checkBox(text: 'Option 3')
separator()
radioButton(text: 'Choice A')
radioButton(text: 'Choice B')
}
}
panel(constraints: BorderLayout.SOUTH) {
flowLayout()
button(text: 'OK', preferredSize: [80, 30])
button(text: 'Cancel', preferredSize: [80, 30])
button(text: 'Apply', preferredSize: [80, 30])
}
}
frame.setVisible(true)This comprehensive documentation covers all the major aspects of Groovy's Swing integration, from basic components to advanced features like data binding, custom components, and threading. The SwingBuilder provides a powerful and expressive way to create desktop applications with minimal boilerplate code.
Install with Tessl CLI
npx tessl i tessl/maven-org-codehaus-groovy--groovy-all