Desktop Swing

The quarkus-desktop-swing extension makes Swing applications work in JVM mode and as GraalVM native executables: all the components, all the look and feels of the JDK (Metal with its themes, Nimbus, Synth, Windows and Windows Classic, GTK, Aqua, CDE/Motif, and the multiplexing look and feel), text (HTML, RTF, styled text), formatters and editors, the file and color choosers, option panes, internal frames, data transfer, printing, and application look and feels and UI delegates.

It includes quarkus-desktop-awt: see Desktop AWT for what AWT applications need (windows, fonts, native executables on Windows, Linux and macOS, HiDPI displays…​). Everything written there applies to Swing applications.

How it works

In JVM mode, Swing needs nothing special. The extension sets the configured look and feel at startup (see Look and feel).

Swing creates many of its classes by name, with reflection: the look and feels and their UI delegates, the Nimbus painters, the key bindings, the editor kits, the values that formatters and table editors parse…​ For native executables, the extension registers what the JDK needs on the platform of the executable (Windows, Linux or macOS), and what the application needs:

  • The look and feels of the JDK, their UI delegates, painters, lazy values, icons, sounds and resource bundles.

  • Text: the HTML and RTF editor kits, the RTF charsets, the page content types (JEditorPane.setPage).

  • The String constructors of the JDK value types (Integer, Long, Short, Byte, Float, Double, Boolean, BigDecimal, BigInteger, Date) that JFormattedTextField formatters and the JTable editors use to parse text, and their valueOf(String) methods that editable combo boxes use.

  • The bean properties of the components that are usually transferred with new TransferHandler("text"): text, icon, color, value, selected, selectedItem, selectedIndex, toolTipText, and the properties of java.awt.Component (background, foreground, font, enabled, visible, focusable, name).

  • The Synth XML support: the color types of the XML files (the beans decoder that creates the objects of the XML files comes with quarkus-desktop-awt, for XMLDecoder).

  • Optionally, the bean properties of the Swing classes, for the JavaBeans API (see JavaBeans).

  • The application classes that Swing creates by name, found in the Jandex index: UI delegates (ComponentUI subclasses, with their static createUI method), look and feels (LookAndFeel subclasses), editor kits (EditorKit subclasses) and Synth painters (SynthPainter subclasses).

  • The methods that Swing looks up with reflection in the classes of the application and of its libraries, for every class that the native image analysis reaches (in the Jandex index or not): the drawing methods of the plain text views (PlainView subclasses) and processInputMethodEvent in the text components (the Desktop AWT extension registers them, with coalesceEvents in the components).

  • The classes named in the Synth XML files of the application (<object class="…​">).

  • The look and feel class set with quarkus.desktop.swing.look-and-feel, when it is a class name.

The parts of Swing that AWT itself uses (print dialogs, input method windows, the text components of AWT on Linux) come with quarkus-desktop-awt: the run time initialization of the Swing packages, the basic and Metal UI delegates and their key bindings, the basic and Metal icons, HTML text. A class of the application or of a library (a component library such as SwingX, JFreeChart or RSyntaxTextArea) whose static initializer creates Swing objects (static final Border, static final Icon, static final Color…​) is initialized at run time, as in JVM mode.

Writing a Swing application

Observe DesktopStartupEvent to create the user interface on the event dispatch thread, once the application started and the look and feel is set. No @QuarkusMain is needed:

import io.quarkiverse.desktop.awt.DesktopStartupEvent;

@Singleton
public class MainWindow extends JFrame {

    @Inject
    Library library;

    @ConfigProperty(name = "app.title")
    String title;

    void open(@Observes DesktopStartupEvent event) {
        setTitle(title);
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        add(new JScrollPane(new JList<>(library.titles())));
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }
}

The application stops when its last window closes. Use DISPOSE_ON_CLOSE (or HIDE_ON_CLOSE), not EXIT_ON_CLOSE, which calls System.exit on the event dispatch thread: in dev mode, it ends dev mode instead of stopping the application, and the extension logs a warning when such a frame opens (in an application observing DesktopStartupEvent), see Closing windows and exit.

Application model and CDI describes the rest of the application model, for Swing and AWT alike:

  • Swing components are @Singleton or @Dependent beans, created on the event dispatch thread (see Components as CDI beans), and WindowBeans.get(instance) destroys a @Dependent dialog once it is disposed (see Window beans).

  • EdtExecutor continues on the event dispatch thread after background work, with CompletableFuture, Mutiny or asynchronous events, instead of SwingWorker, and @RunOnEdt runs the methods of presenters there (see The event dispatch thread).

  • The About, Preferences and Quit items of the macOS application menu, and the files opened with the application, are CDI events (see Application menu, Finder and Dock events), and the exceptions of the listeners go to the Quarkus log (category io.quarkiverse.desktop.awt.edt).

  • A @QuarkusMain starts the user interface with DesktopLifecycle.start() in the manual mode (see Manual mode), and user interface tests enable the startup event (see Testing).

Look and feel

Set the look and feel in application.properties:

quarkus.desktop.swing.look-and-feel=system

It is set on the event dispatch thread when the application starts, before it runs. The value is system (the look and feel of the platform), cross-platform (Metal), metal, nimbus, motif, windows, windows-classic, gtk, or a class name. The application can also set the look and feel itself with UIManager.setLookAndFeel, as usual.

Look and feel Windows Linux macOS Notes

Metal (cross-platform), with the Ocean (default) and Steel themes, and application themes

Yes

Yes

Yes

The default look and feel on Windows and Linux.

Nimbus

Yes

Yes

Yes

Synth

Yes

Yes

Yes

Loaded from an XML file, see Synth.

Windows (system on Windows), Windows Classic

Yes

No

No

Uses the visual styles (themes) of Windows, and the shell icons.

GTK (system on a GNOME desktop)

No

Yes

No

Needs GTK 3 (libgtk-3-0t64 on Ubuntu 24.04). The system look and feel is GTK when the desktop is GNOME (XDG_CURRENT_DESKTOP), Metal otherwise.

Aqua (system on macOS)

No

No

Yes

The default look and feel on macOS, always included (it draws the AWT components), see macOS.

CDE/Motif

Yes

Yes

Yes

Multiplexing (auxiliary look and feels, UIManager.addAuxiliaryLookAndFeel)

Yes

Yes

Yes

macOS

On macOS the default look and feel is Aqua, as in JVM mode (UIManager.getSystemLookAndFeelClassName()). To show a JMenuBar in the macOS menu bar, set apple.laf.useScreenMenuBar to true before creating the first window (-Dapple.laf.useScreenMenuBar=true on the command line of the native executable or of the JVM, or System.setProperty at the start of your QuarkusApplication). The other macOS client properties and system properties of the JDK (apple.awt.application.appearance, JButton.buttonType, JComponent.sizeVariant, apple.awt.transparentTitleBar…​) work as in JVM mode. See the macOS section of Desktop AWT for the native executables.

Native executable size

Native executables include all the look and feels of the JDK for their platform: about 10 MB of a native executable (Nimbus and the multiplexing look and feel are the largest ones). An application that uses some of them only can list them, for instance for an application that uses the system look and feel of Windows:

quarkus.desktop.swing.included-look-and-feels=metal,windows

Metal (the default look and feel) is always included. For a trivial Swing application on Windows, the native executable is 99 MB with all the look and feels, and 88.5 MB with Metal only. Setting a look and feel that is not included fails with a ClassNotFoundException, and the build logs a warning when quarkus.desktop.swing.look-and-feel names one.

Application and library look and feels

Application look and feels, themes and UI delegates work as in JVM mode when their classes are in the Jandex index of the application (the classes of the application are). A library look and feel (for instance FlatLaf) needs its own native image metadata: some libraries include it, otherwise register the classes it creates by name, for instance with @RegisterForReflection(targets = …​), or index the library with quarkus.index-dependency.

Synth

SynthLookAndFeel.load(InputStream, Class) and load(URL) work in native executables:

  • Include the XML file and the images it references in the native executable: quarkus.native.resources.includes=com/example/laf/**. Paths in the XML file are relative to the package of the class given to load (the resource base).

  • The classes of the objects that the XML file creates (<object class="…​">, for instance a painter or a ColorUIResource) are registered for reflection by the extension, which reads the Synth XML files of the application (the XML files with a <synth> root element).

Text

JEditorPane, JTextPane and the other text components work as in JVM mode: the HTML editor kit (CSS, tables, lists, forms, images), the RTF editor kit (reading and writing, with the RTF charsets), styled text, undo and redo, and the application editor kits registered by class name (JEditorPane.registerEditorKitForContentType).

In a native executable, class path resources have resource: URLs (jar: or file: URLs in JVM mode): JEditorPane.setPage(getClass().getResource("page.html")), new ImageIcon(getClass().getResource("icon.png")) and the images of HTML pages work with them. Include the resources of the application in the native executable with quarkus.native.resources.includes.

JFormattedTextField formatters (DefaultFormatter, NumberFormatter, InternationalFormatter) and the JTable editors create the values from text with the String constructor of their class: the JDK types listed in How it works are registered. Register the application value classes that use a formatter or a table editor for reflection (@RegisterForReflection).

Data transfer

Clipboard and drag and drop of text, images and files work as in AWT. A TransferHandler created with a property name (new TransferHandler("text")) reads and writes the property with the bean introspector: the usual properties of the components are registered (see How it works). Register the accessors of other properties for reflection.

JavaBeans

The core of the JavaBeans API (property editors, XMLEncoder and XMLDecoder with the persistence delegates of the JDK, EventHandler) comes with quarkus-desktop-awt: see JavaBeans.

The bean properties of the Swing classes are optional (disabled by default): quarkus.desktop.swing.java-beans.jdk-classes=true registers the public constructors, methods and fields of the public classes of javax.swing, javax.swing.border, javax.swing.event, javax.swing.table and javax.swing.tree, of the text components, documents and formatters of javax.swing.text, and of the UI resources of javax.swing.plaf, with the AWT classes that they extend (as quarkus.desktop.awt.java-beans.jdk-classes=true does), and the icons of their bean infos (BeanInfo.getIcon). The Introspector then finds the properties and event sets of the Swing components, and XMLEncoder and XMLDecoder write and read Swing forms and frames as in JVM mode: components with their borders, layouts and models, JTabbedPane tabs, Box layouts, combo box and list models, trees with their nodes, menus with their accelerators (KeyStroke).

Without it, the properties of the components that are usually transferred with new TransferHandler("text") are still registered (see Data transfer).

It makes a native executable 3 to 4 MB larger, since many public methods of the Swing classes are not used otherwise: 3.2 MB for the showcase application (127.6 MB without it), 3.9 MB for the Swing integration test application (109.8 MB without it). Applications that only use the JavaBeans API with their own classes do not need it.

Printing

JTable.print, JTextComponent.print (text, HTML, RTF) and the Printable objects of JTable.getPrintable and JTextComponent.getPrintable print as in JVM mode, to printers or to a StreamPrintService (PostScript). The print and page dialogs are the ones of AWT: native dialogs on Windows, the Cocoa print and page layout panels on macOS, Swing dialogs (ServiceDialog) on Linux.

Locales

The texts of Swing (the buttons of the option panes, the file and color choosers…​) come from resource bundles. A native executable includes the locales of the application: set quarkus.locales (and quarkus.default-locale). The JDK translates Swing to German, Japanese and Simplified Chinese on Windows, and to more languages on Linux; the other languages get English texts.

Dev mode

In dev mode (and in tests running several applications in the same JVM), the AWT event dispatch thread outlives the application. The Desktop AWT extension makes it dispatch the events with the class loader of the running application, so that the classes that Swing loads by name on the event dispatch thread (look and feels, UI delegates, editor kits) are the ones of the running application after a live reload. Global Swing state stays from one run to the next (the look and feel, UIManager.put values): set it when the application starts. See Dev mode for the restarts and what they dispose.

Limitations

  • DefaultFormatter and the JTable editors of application value classes, the properties of a TransferHandler other than the registered ones (unless quarkus.desktop.swing.java-beans.jdk-classes is enabled), third party look and feels and objects created by name (UIDefaults.ProxyLazyValue, application classes of java.beans.XMLDecoder documents, HTML <object> elements with swing.html.object=true) need their own native image metadata, for instance with @RegisterForReflection.

  • The native image tracing agent gives the metadata that an application needs: java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/<group>/<artifact> -jar target/quarkus-app/quarkus-run.jar.

  • The native executables of Swing applications have the limitations of AWT applications (see Limitations).

Exact reachability metadata

With --exact-reachability-metadata (see Exact reachability metadata), the Swing extension registers the lookups of Swing expected to fail: the class names that Nimbus probes for the regions of its skin, processInputMethodEvent in the text components of the JDK that do not declare it (and in those of the application and of its libraries, see How it works), the .properties files next to the resource bundles of the look and feels, the icons of the look and feels that are looked up in the package of an application look and feel first (and those of the basic look and feel, looked up in the packages of Nimbus and Synth first), and the internal values of the Swing components that the JavaBeans API queries when it writes them (with their BeanInfo, Customizer and PersistenceDelegate probes). It also registers what the JavaBeans API queries without the JavaBeans registration of the Swing classes: the components whose properties a property transfer handler reads (new TransferHandler("text"), with the listener interfaces of their event sets), and the classes named in the Synth XML files of the application, whose constructors, methods and properties the Synth parser looks up.

Verification

The integration tests of the extension run a gallery of the components with every look and feel of the platform (in JVM mode and as a native executable, on Windows, Linux and macOS), and check that the native executable gets the same look and feel defaults (UI delegates, icons, painters, borders, colors, fonts, texts), the same key bindings, the same font metrics, the same rendering (pixel by pixel) and the same PostScript output as JVM mode. The Quarkus Desktop showcase compares JVM mode and native executables page by page, with its Swing components, containers, text, look and feel, data transfer, printing and JavaBeans pages (see Verification).

Configuration

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

Type

Default

The look and feels of the JDK included in native executables : all, or a list of metal, nimbus, synth, motif, windows (with Windows Classic), gtk and multi (the multiplexing look and feel of the auxiliary look and feels).

By default, native executables include all the look and feels of the JDK for their platform. They make the native executable larger (about 10 MB for all of them : Nimbus and the multiplexing look and feel about 3 MB each), so an application can list only those it uses. Metal, the default look and feel, is always included (and Aqua on macOS, which draws the AWT components there); Nimbus and GTK include the Synth look and feel they extend, but not the loading of Synth XML files (synth). Setting a look and feel that is not included fails with a ClassNotFoundException.

This property has no effect in JVM mode.

Environment variable: QUARKUS_DESKTOP_SWING_INCLUDED_LOOK_AND_FEELS

list of all, metal, nimbus, synth, motif, windows, gtk, multi

all

Whether the JDK Swing classes support the JavaBeans API in native executables : their public constructors, methods and fields are registered for reflection, so that the Introspector finds their bean properties and event sets, and that XMLEncoder, XMLDecoder, Statement, Expression, EventHandler and Beans.instantiate work with them, as in JVM mode. The AWT classes that they extend are registered too (as with quarkus.desktop.awt.java-beans.jdk-classes=true), and the icons of the bean infos of the Swing components are included (BeanInfo.getIcon).

The classes are the public classes of javax.swing, javax.swing.border, javax.swing.event, javax.swing.table and javax.swing.tree (components, models, layouts, borders, actions, key strokes, icons, renderers and editors, events and listeners), the text components, documents and formatters of javax.swing.text, and the UI resources of javax.swing.plaf. For instance XMLEncoder writes a JPanel with its border, layout and components, a JTabbedPane, a JTree with its nodes, and XMLDecoder reads them.

It is disabled by default because it makes a native executable 3 to 4 MB larger (many public methods of the Swing classes are not used otherwise). When disabled, the Introspector finds no bean property of these classes (other than the properties usually transferred with new TransferHandler("text"), which are always registered) and XMLEncoder cannot write them.

Environment variable: QUARKUS_DESKTOP_SWING_JAVA_BEANS_JDK_CLASSES

boolean

false

The look and feel set when the application starts, before it runs (on the event dispatch thread, in JVM mode and in native executables).

system (the look and feel of the platform : Windows on Windows, Aqua on macOS, GTK on a GNOME desktop, Metal otherwise), cross-platform (Metal), metal, nimbus, motif, windows, windows-classic, gtk, or the class name of a look and feel (an application look and feel, or a library one such as FlatLaf). When not set, Swing uses its default look and feel (Aqua on macOS, Metal elsewhere, unless the swing.defaultlaf system property sets another one), and the application can set the look and feel itself with UIManager.setLookAndFeel.

A look and feel that this platform does not support (for instance windows on Linux) or that cannot be created is reported as a warning, and the default look and feel is kept. The look and feel classes of the JDK are included in native executables as quarkus.desktop.swing.included-look-and-feels configures them (all of them by default ; the build warns when this property names one that is not included) ; an application look and feel class is registered for reflection when it is in the Jandex index of the application, or when this property names it at build time.

Environment variable: QUARKUS_DESKTOP_SWING_LOOK_AND_FEEL

string