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
Stringconstructors of the JDK value types (Integer,Long,Short,Byte,Float,Double,Boolean,BigDecimal,BigInteger,Date) thatJFormattedTextFieldformatters and theJTableeditors use to parse text, and theirvalueOf(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 ofjava.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, forXMLDecoder). -
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 (
ComponentUIsubclasses, with their staticcreateUImethod), look and feels (LookAndFeelsubclasses), editor kits (EditorKitsubclasses) and Synth painters (SynthPaintersubclasses). -
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 (
PlainViewsubclasses) andprocessInputMethodEventin the text components (the Desktop AWT extension registers them, withcoalesceEventsin 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
@Singletonor@Dependentbeans, created on the event dispatch thread (see Components as CDI beans), andWindowBeans.get(instance)destroys a@Dependentdialog once it is disposed (see Window beans). -
EdtExecutorcontinues on the event dispatch thread after background work, withCompletableFuture, Mutiny or asynchronous events, instead ofSwingWorker, and@RunOnEdtruns 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
@QuarkusMainstarts the user interface withDesktopLifecycle.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 ( |
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 ( |
Yes |
No |
No |
Uses the visual styles (themes) of Windows, and the shell icons. |
GTK ( |
No |
Yes |
No |
Needs GTK 3 ( |
Aqua ( |
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, |
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 toload(the resource base). -
The classes of the objects that the XML file creates (
<object class="…">, for instance a painter or aColorUIResource) 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
-
DefaultFormatterand theJTableeditors of application value classes, the properties of aTransferHandlerother than the registered ones (unlessquarkus.desktop.swing.java-beans.jdk-classesis enabled), third party look and feels and objects created by name (UIDefaults.ProxyLazyValue, application classes ofjava.beans.XMLDecoderdocuments, HTML<object>elements withswing.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 : 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 ( This property has no effect in JVM mode. Environment variable: |
list of |
|
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 The classes are the public classes of 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 Environment variable: |
boolean |
|
The look and feel set when the application starts, before it runs (on the event dispatch thread, in JVM mode and in native executables).
A look and feel that this platform does not support (for instance Environment variable: |
string |