A declarative Swing GUI builder for Groovy applications that provides a concise and maintainable way to create complex Swing user interfaces
—
Simplified Look and Feel management with support for system LAFs and third-party themes, providing easy access to platform-specific appearance and custom styling.
The central helper class for managing Look and Feel configuration with built-in support for popular LAFs.
/**
* Singleton helper for Look and Feel management
*/
class LookAndFeelHelper {
/** Get singleton instance */
static LookAndFeelHelper getInstance()
/** Configure Look and Feel with attributes and initialization code */
LookAndFeel lookAndFeel(Object value, Map attributes = [:], Closure initClosure = null)
/** Register alias for Look and Feel class name */
String addLookAndFeelAlias(String alias, String className)
/** Add custom attribute handler for specific LAF */
String addLookAndFeelAttributeHandler(String className, String attr, Closure handler)
/** Check if this is a leaf node in builder pattern */
boolean isLeaf()
}Pre-configured aliases for common Look and Feel implementations.
// System Look and Feels
'system' // UIManager.getSystemLookAndFeelClassName()
'crossPlatform' // UIManager.getCrossPlatformLookAndFeelClassName()
// Platform-specific LAFs
'metal' // javax.swing.plaf.metal.MetalLookAndFeel
'nimbus' // com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel (if available)
'windows' // com.sun.java.swing.plaf.windows.WindowsLookAndFeel
'win2k' // com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel
'mac' // com.apple.laf.AquaLookAndFeel (if available)
'motif' // com.sun.java.swing.plaf.motif.MotifLookAndFeel
'gtk' // com.sun.java.swing.plaf.gtk.GTKLookAndFeel
'synth' // javax.swing.plaf.synth.SynthLookAndFeel
// Third-party LAFs (requires external libraries)
'plastic' // com.jgoodies.looks.plastic.PlasticLookAndFeel
'plastic3D' // com.jgoodies.looks.plastic.Plastic3DLookAndFeel
'plasticXP' // com.jgoodies.looks.plastic.PlasticXPLookAndFeel
'substance' // org.pushingpixels.substance.api.SubstanceLookAndFeel
'napkin' // net.sourceforge.napkinlaf.NapkinLookAndFeelUsage Examples:
import groovy.swing.SwingBuilder
// Set system Look and Feel
SwingBuilder.lookAndFeel('system')
// Try multiple LAFs in fallback order
SwingBuilder.lookAndFeel('nimbus', 'system', 'metal')
// Set LAF with configuration
SwingBuilder.lookAndFeel('metal', [
theme: 'ocean',
boldFonts: false
])
// Use LAF with initialization closure
SwingBuilder.lookAndFeel('nimbus') { laf ->
// Custom initialization code
UIManager.put('control', new Color(240, 240, 240))
}Look and Feel methods integrated into SwingBuilder for convenient usage.
/**
* SwingBuilder static methods for LAF management
*/
class SwingBuilder {
/** Configure LAF with single parameter */
static LookAndFeel lookAndFeel(Object laf)
/** Configure LAF with initialization closure */
static LookAndFeel lookAndFeel(Object laf, Closure initCode)
/** Configure LAF with attributes and optional closure */
static LookAndFeel lookAndFeel(Map attributes = [:], Object laf = null, Closure initCode = null)
/** Try multiple LAFs in order until one succeeds */
static LookAndFeel lookAndFeel(Object... lafs)
}Usage Examples:
// Simple LAF setting
SwingBuilder.lookAndFeel('nimbus')
// LAF with initialization
SwingBuilder.lookAndFeel('metal') { laf ->
MetalLookAndFeel.setCurrentTheme(new OceanTheme())
}
// LAF with attributes
SwingBuilder.lookAndFeel([theme: 'steel'], 'metal')
// Fallback LAF selection
SwingBuilder.lookAndFeel('substance', 'nimbus', 'system')Special support for Metal LAF themes and configuration.
// Metal LAF extended attributes
[
theme: 'ocean' | 'steel' | MetalTheme, // Predefined or custom theme
boldFonts: boolean, // Enable/disable bold fonts
noxp: boolean // Disable XP styling
]Usage Examples:
// Ocean theme with bold fonts disabled
SwingBuilder.lookAndFeel('metal', [
theme: 'ocean',
boldFonts: false
])
// Steel theme (classic Metal)
SwingBuilder.lookAndFeel('metal', [theme: 'steel'])
// Custom Metal theme
import javax.swing.plaf.metal.*
class CustomMetalTheme extends DefaultMetalTheme {
String getName() { 'Custom Theme' }
ColorUIResource getPrimary1() { new ColorUIResource(66, 33, 66) }
}
SwingBuilder.lookAndFeel('metal', [theme: new CustomMetalTheme()])Support for Substance LAF skins and themes (requires external library).
// Substance LAF extended attributes
[
theme: Object, // Substance theme object
skin: Object, // Substance skin object
watermark: Object // Substance watermark object
]Usage Examples:
// Substance with skin (requires Substance library)
try {
SwingBuilder.lookAndFeel('substance', [
skin: 'org.pushingpixels.substance.api.skin.BusinessSkin'
])
} catch (Exception e) {
// Fallback to system LAF
SwingBuilder.lookAndFeel('system')
}Methods for registering custom LAF aliases and attribute handlers.
/**
* Register custom LAF alias
*/
String addLookAndFeelAlias(String alias, String className)
/**
* Add custom attribute handler for specific LAF
*/
String addLookAndFeelAttributeHandler(String className, String attr, Closure handler)Usage Examples:
def helper = LookAndFeelHelper.getInstance()
// Register custom LAF alias
helper.addLookAndFeelAlias('myLaf', 'com.example.MyLookAndFeel')
// Add custom attribute handler
helper.addLookAndFeelAttributeHandler(
'com.example.MyLookAndFeel',
'customProperty'
) { laf, value ->
laf.setCustomProperty(value)
}
// Use custom LAF
SwingBuilder.lookAndFeel('myLaf', [customProperty: 'value'])Utility methods for LAF detection and management.
/**
* Static utility methods for LAF detection
*/
class LookAndFeelHelper {
/** Get Nimbus LAF class name if available */
static String getNimbusLAFName()
/** Get Aqua LAF class name if available (Mac) */
static String getAquaLAFName()
/** Get Substance LAF class name if available */
static String getSubstanceLAFName()
}
// UIManager utilities
UIManager.getSystemLookAndFeelClassName()
UIManager.getCrossPlatformLookAndFeelClassName()
UIManager.getInstalledLookAndFeels()
UIManager.getLookAndFeel()Usage Examples:
// Check available LAFs
def installedLafs = UIManager.getInstalledLookAndFeels()
println "Available LAFs:"
installedLafs.each { lafInfo ->
println " ${lafInfo.name}: ${lafInfo.className}"
}
// Conditional LAF selection based on platform
def platformLaf = System.getProperty('os.name').toLowerCase().contains('mac') ? 'mac' : 'system'
SwingBuilder.lookAndFeel(platformLaf)
// Get current LAF information
def currentLaf = UIManager.getLookAndFeel()
println "Current LAF: ${currentLaf.name} (${currentLaf.class.name})"Best practices for integrating LAF management into applications.
// Application startup LAF configuration
class Application {
static void initializeLookAndFeel() {
// Set LAF before creating any Swing components
SwingBuilder.lookAndFeel('nimbus', 'system')
// Configure global UI properties
UIManager.put('OptionPane.messageFont', new Font('Dialog', Font.PLAIN, 12))
UIManager.put('OptionPane.buttonFont', new Font('Dialog', Font.PLAIN, 12))
}
static void main(String[] args) {
SwingUtilities.invokeLater {
initializeLookAndFeel()
createAndShowGUI()
}
}
}Usage Examples:
// Complete application LAF setup
import groovy.swing.SwingBuilder
import javax.swing.*
class MyApplication {
static void main(String[] args) {
// Initialize LAF early
SwingUtilities.invokeLater {
// Try preferred LAFs with fallback
SwingBuilder.lookAndFeel('nimbus', 'system', 'metal')
// Create main GUI
def swing = new SwingBuilder()
def frame = swing.frame(
title: 'My Application',
defaultCloseOperation: JFrame.EXIT_ON_CLOSE,
size: [800, 600]
) {
menuBar {
menu(text: 'View') {
menu(text: 'Look and Feel') {
UIManager.installedLookAndFeels.each { lafInfo ->
menuItem(text: lafInfo.name) {
actionPerformed { e ->
try {
UIManager.setLookAndFeel(lafInfo.className)
SwingUtilities.updateComponentTreeUI(frame)
} catch (Exception ex) {
JOptionPane.showMessageDialog(frame,
"Failed to set LAF: ${ex.message}")
}
}
}
}
}
}
}
panel {
// main content
}
}
frame.visible = true
}
}
}Proper error handling for LAF operations.
// LAF setting with error handling
try {
SwingBuilder.lookAndFeel('preferredLaf')
} catch (UnsupportedLookAndFeelException e) {
println "LAF not supported: ${e.message}"
SwingBuilder.lookAndFeel('system') // fallback
} catch (ClassNotFoundException e) {
println "LAF class not found: ${e.message}"
SwingBuilder.lookAndFeel('metal') // fallback
} catch (Exception e) {
println "LAF error: ${e.message}"
// Keep current LAF
}Usage Examples:
// Robust LAF selection with logging
def selectLookAndFeel() {
def candidates = ['nimbus', 'system', 'metal']
for (laf in candidates) {
try {
SwingBuilder.lookAndFeel(laf)
println "Successfully set LAF: $laf"
return
} catch (Exception e) {
println "Failed to set LAF $laf: ${e.message}"
}
}
println "Warning: Using default LAF"
}
// Dynamic LAF switching in running application
def switchLookAndFeel(String lafName, Component rootComponent) {
try {
def oldLaf = UIManager.getLookAndFeel()
SwingBuilder.lookAndFeel(lafName)
SwingUtilities.updateComponentTreeUI(rootComponent)
println "Switched from ${oldLaf.name} to ${UIManager.getLookAndFeel().name}"
} catch (Exception e) {
JOptionPane.showMessageDialog(rootComponent,
"Could not switch to $lafName: ${e.message}",
'Look and Feel Error',
JOptionPane.ERROR_MESSAGE)
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-codehaus-groovy--groovy-swing