Radiance 6.5.0

December 1st, 2022

It gives me great pleasure to announce the next major release of Radiance. Let’s get to what’s been fixed, and what’s been added. First, I’m going to use emojis to mark different parts of it like this:

💔 marks an incompatible API / binary change
🎁 marks new features
🔧 marks bug fixes and general improvements


  • 🎁 Add tri-state checkbox component
  • 🎁 Add switch component
  • 🎁 Migrate previously internal circular progress component to public API
  • 🎁💔 Revisit layout configuration of command button panels. Support fixed-column and adaptive layout spec for row fill and column fill panels.
  • 🎁 Support configurable content padding in command buttons and command button panels
  • 🎁 Add more presentation model options for command button panels
  • 🎁 Add presentation model for rich tooltips
  • 🔧 Fix crash on displaying rich tooltips under Java 17+
  • 🔧 Fix text wrap logic in command buttons under big presentation state
  • 🔧 Fix vertical positioning of command button content under tile presentation state
  • 🔧 Fix issues with command popup menus not closing in certain scenarios


  • 🎁💔 Revisit configuration of popup content. Full documentation here.
  • 🎁💔 Unify fill and highlight painters.
  • 🎁💔 Revisit how specular fill painter is configured.
  • 🔧 Fix crash in specular fill painter
  • 🔧 Fix crash in table UI delegate
  • 🔧 Fix crash in opening the window title pane menu
  • 🔧 Fix crash in update font of a tree component
  • 🔧 Fix incorrect offset of vertical scrollbars during scrolling

Kotlin extensions

  • 🎁 Add indexed access operator overload for ResourceBundle.getString

I’ve wanted to get this release out a bit earlier than anticipated to cover the functionality gaps between Radiance and Aurora, and to address some crasher bugs that snuck into the last major rewrite of Radiance’s rendering pipeline. With this release out of the door, the roadmap for 2023 remains as planned:

  • Add the ribbon / command bar component to Aurora
  • Revisit the way colors are defined and used in both Radiance and Aurora

There’s still a long road ahead to continue exploring the ever-fascinating depths of what it takes to write elegant and high-performing desktop applications in Swing. If you’re in the business of writing just such apps, I’d love for you to take this Radiance release for a spin. Click here to get the instructions on how to add Radiance to your builds. And don’t forget that all of the modules require Java 9 to build and run.

It gives me great pleasure to announce the next major release of Radiance. Let’s get to what’s been fixed, and what’s been added. First, I’m going to use emojis to mark different parts of it like this:

💔 marks an incompatible API / binary change
🎁 marks new features
🔧 marks bug fixes and general improvements

Dependencies for core libraries

  • Gradle: 7.2 ➡ 7.5.1
  • Kotlin: 1.5.31 ➡ 1.7.10
  • Kotlin coroutines: 1.5.2 ➡ 1.6.4


  • 🔧💔 A new direct rendering model for all core and custom components in Radiance
    • Instead of rendering components as multi-layer combinations of cached offscreen images, Radiance now uses direct rendering to the Graphics objects passed to the relevant UI delegates and painting methods
    • Use RadianceCommonCortex.paintAtScale1x for visuals that need to “fall” on exact pixels, line single-pixel borders, separators, etc
  • 🔧 Remove all usages of java.security APIs (that are deprecated in Java 17 going forward)


  • 🎁 New default animation pulse source that is based on the display refresh rate


  • 🎁💔 Unify fire action trigger logic for command buttons by replacing CommandButtonPresentationModel.isFireActionOnRollover and CommandButtonPresentationModel.isFireActionOnPress with a single actionFireTrigger enum that has three values:
    • OnRollover to fire action on rollover
    • OnPressed to fire action on press
    • OnPressReleased to fire action on press release (the default)
  • 🎁💔 Unify text action/popup click logic for command buttons by replacing CommandButtonPresentationModel.isTextClickAction and CommandButtonPresentationModel.isTextClickPopup with a single textClick enum field that has two values:
    • Action to activate action on text click
    • Popup to activate secondary content on text click
  • 🎁💔 Revisit breadcrumb bar APIs
    • Remove exception propagation APIs (they were no-op in any case since it wasn’t wired)
    • Remove index tracking in BreadcrumbItem (not wired to anything)
    • Switch BreadcrumbBarCallBack APIs from StringValuePair to BreadcrumbItem
    • Also rename getLeafs to getLeaves
    • Rename BreadcrumbBarCallBack to BreadcrumbBarContentProvider`
    • Rename BreadcrumbBarModel to BreadcrumbBarContentModel
    • Add BreadcrumbBarPresentationModel and support icon filtering
    • Remove StringValuePair from the API surface altogether
    • Revisit the API surface of BreadcrumbItem
  • 🎁💔 Switch presentation models to use BackgroundAppearanceStrategy across all components. This applies to
    • CommandButtonPresentationModel.setFlat
    • CommandButtonPresentationModel.Overlay.setFlat
    • CommandStripPresentationModel.setFlat
    • CommandPresentationModel.setFlat
  • 🎁 Add single row resize policy to ribbon flow bands
  • 🔧 Fix lost breadcrumb bar path after skin change
  • 🔧 Fix separator drawing over the last text character in MEDIUM command buttons that don’t display icons
  • 🔧 Command menus now toggle open and close on clicks
  • 🔧 Fix issues with command popup menus not closing in certain scenarios


  • 💔 Simplified visuals of tabbed panes
    • Remove SINGLE_FULL and DOUBLE_FULL from TabContentPaneBorderKind. Apps that wish to draw border around the content area will need to do so explicitly.
    • Remove RadianceSkin.setTabFadeStart and RadianceSkin.setTabFadeEnd and do consistent indication for the selected / rollover tab with no alpha fade gradient.
    • Consistent corner radius of tabs across all skins.
  • 💔 Clean up the signature of fill painters, removing isFocused (not used anywhere, and shouldn’t be since the focus indication is painted separately) and hasShine (specific to StandardFillPainter visuals).
  • 🔧 Fix issues with various color chooser panels, including the correct wiring of the “Reset” button across all the panels
  • 🔧 Fix incorrect bounds of maximized decorated frames on Windows
  • 🔧 Fix inverted logic of ComponentOrParentChainScope.setExtraWidgetsPresence
  • 🔧 Fix null pointer exception in rollover button listeners

SVG transcoder

  • 🔧 Simplify generated code by not emitting identity affine transforms
  • 💔 Remove plain templates

As always, I’d love for you to take this Radiance release for a spin. Click here to get the instructions on how to add Radiance to your builds. And don’t forget that all of the modules require Java 9 to build and run.

And now for the next big thing or two.

This release took almost a year to complete. I needed this time to figure out how to continue evolving Radiance in a meaningful way over the next decade or so. The considerations for what went into this work were laid out last October in this post. The two major areas I wanted to focus on are direct rendering and API consistency.

Direct rendering has touched the UI delegates for every single core Swing component, and almost every custom Radiance component, from command buttons all the way up to the ribbon. API consistency has been driven by the ongoing work in Aurora, as well as the drive to clean up the API surfaces that have been misaligned across the codebase for a while.

Making meaningful changes also means making hard choices about backwards compatibility. Deprecating existing APIs but leaving them available leads to a confusing API surface and increases the cost of maintaining and evolving the codebase. Leaving existing APIs in place, and trying to redirect them under the hood to a “v2” variant places noticeable constraints on what is feasible to do. If I want Radiance to be here in the next 10-15 years, the only practical way forward is to cut out APIs that have not aged well, remove them from the codebase and introduce new ones as necessary. I understand that it causes friction during dependency upgrades on the application side of things, but the only other alternative is abandoning any new development altogether.

With all this in mind, what is next, for 2023 and beyond?

The first major change in Radiance is going to be around defining and using colors. Code-named Chroma, this effort aims to bring more clarity and control over working with colors in core and custom Radiance skins, inspired by the ongoing evolution of design systems such as Material and others.

This change will also find its way into Aurora, as these two projects are twins, in a sense. Once Compose for Desktop hits its official 1.2 release, Aurora will go to 1.2 as well. Afterwards, I will work on window APIs, and will start the long-planned work to port the ribbon component to Aurora.

And last but most definitely not the least, are the plans to explore the third twin to Radiance and Aurora, and bring the theming layer and all the components to the world of Flutter.

As I said last October, it’s going to be a long road, and it may take a bit of time again until the next major release of Radiance. The current goal is to fully complete the color work across both Radiance and Aurora, and have them released at the same time. This will probably happen after the ribbon component is added to Aurora. As for the Flutter twin, it is going to be an exciting, and yet completely unpredictable adventure. I may or may not have something for you to play with in 2023. Time will tell.

Native fidelity

January 19th, 2022

I’ve been marinating in the world of Swing for about 17 years now, and one thing that I will say for certain is that trying to achieve native fidelity (not even the feel, but just the look of components) is a herculean task that requires constant work.

Swing engineers tried to do that at some point by delegating all the rendering to native APIs. That worked to some extent. But not the animations – since these controls were not native ones. And over the years the gap between the visuals of Swing controls under that cobbled-together house of cards and the real native controls keeps on growing larger and larger, as Microsoft keeps on tweaking and changing what native look and feel is.

The same goes for macOS – every release changes things for individual controls, as well as the overall structure of the window and the content in it. Even if somehow you managed to get aligned with the absolute latest macOS visuals (including light/dark and accent automatically respecting the user setting), if you don’t do a window layout that matches the platform guidelines, you’re still off.

And again, every year, every major platform changes things. So whoever it is that provides the UI kit that aims for native fidelity, needs to make a hard decision. Do they follow the latest native look and keep on switching to the latest native APIs (effectively abandoning the older versions of those platforms), or do they create a monstrosity of backwards compatibility, that eventually requires so much testing and hand-holding, that it collapses under its own weight?

And of course, the moment that person / organization stop maintaining that library is the moment it simply stops working on new versions of those major desktop OSes. That’s a hard guarantee.

If anything, the beautiful thing about the web expansion in the last 6-8 years is that it has shown that the end users simply do not care about native fidelity en masse. Sure, there is a small, dedicated, vocal cohort of die-hard aficionados that are willing to die on that hill, but the 99.9999% of users do not care. They want to get their thing done, and move on.

It gives me great pleasure to announce the seventh major release of Radiance. Let’s get to what’s been fixed, and what’s been added. First, I’m going to use emojis to mark different parts of it like this:

💔 marks an incompatible API / binary change
🎁 marks new features
🔧 marks bug fixes and general improvements

Dependencies for core libraries

  • Gradle from 7.1 to 7.2
  • Kotlin from 1.5.10 to 1.5.31
  • Kotlin coroutines from 1.5.0 to 1.5.2




  • 🎁💔 Add reference to the ribbon as a parameter to all OnShowContextualMenuListener methods
  • 🎁💔 Align icon theming across all Flamingo components
  • 🔧 Fix layout of command buttons in TILE layout under RTL
  • 🔧 Fix visuals of horizontal command button strips under RTL
  • 🔧 Fix layout of anchored command buttons under RTL
  • 🔧 Fix layout of command button popup content under RTL
  • 🔧 Fix issues with updating ribbon gallery content


  • 💔 Remove SvgBatikIcon and SvgBatikNeonIcon
  • 💔 Move Photon to be under tools


As with the earlier release 4.0.0, this release has mostly been focused on stabilizing and improving the overall API surface of the various Radiance modules. As always, I’d love for you to take this Radiance release for a spin. Click here to get the instructions on how to add Radiance to your builds. And don’t forget that all of the modules require Java 9 to build and run.

And now for the next big thing or two.

I will take the next two weeks to fix any bugs or regressions that are reported on the 4.5.0 release. On the week of October 18th, all Radiance modules are going to undergo a major refactoring. While Radiance unified all of the Swing projects that I’ve been working on since around 2004, this unification was rather superficial. It made it easier to have inter-module dependencies. It made it easier to write documentation. It made it easier to schedule coordinated releases. But it didn’t make it easier to see what Radiance is.

In the last year or so I kept on asking myself the same questions over and over again.

If I started with these libraries today, will they still be using these disjointed codenames (Neon, Trident, Substance, Flamingo, not to talk about Torch, Apollo, Zodiac, Meteor, Ember, Plasma, Spyglass, Beacon, etc)? For somebody who wants to deep dive into the implementation details, are there places that are internally inconsistent? For an app developer who wants to get the most out of these libraries, does Radiance provide an externally approachable and consistent set of APIs?

The first step I’m taking to answer at least some of these questions is moving away from the codenames, and renaming everything based on the functional boundaries. And by everything I mean everything – modules, classes, methods, fields, variables, etc. It’s going to be a huge breaking change. But it’s something that I feel is way overdue for a project of this complexity. More specifically:

  • org.pushingpixels.trident -> org.pushingpixels.radiance.animation
  • org.pushingpixels.neon -> org.pushingpixels.radiance.common
  • org.pushingpixels.substance -> org.pushingpixels.radiance.theming
  • org.pushingpixels.flamingo -> org.pushingpixels.radiance.component
  • org.pushingpixels.substance.extras -> org.pushingpixels.radiance.theming.extras
  • org.pushingpixels.ember -> org.pushingpixels.radiance.theming.ktx
  • org.pushingpixels.meteor -> org.pushingpixels.radiance.swing.ktx
  • org.pushingpixels.plasma -> org.pushingpixels.radiance.component.ktx
  • org.pushingpixels.torch -> org.pushingpixels.radiance.animation.ktx
  • org.pushingpixels.tools.apollo -> org.pushingpixels.radiance.tools.schemeeditor
  • org.pushingpixels.tools.beacon -> org.pushingpixels.radiance.tools.themingdebugger
  • org.pushingpixels.tools.hyperion -> org.pushingpixels.radiance.tools.shapereditor
  • org.pushingpixels.tools.ignite -> org.pushingpixels.radiance.tools.svgtranscoder.gradle
  • org.pushingpixels.tools.lightbeam -> org.pushingpixels.radiance.tools.lafbenchmark
  • org.pushingpixels.tools.photon -> org.pushingpixels.radiance.tools.svgtranscoder
  • org.pushingpixels.tools.zodiac -> org.pushingpixels.radiance.tools.screenshot

Classes that used codenames, such as SubstanceLookAndFeel, TridentConfig etc will be renamed to follow the functionality of the matching API sub-surface. For example:

  • SubstanceCortex -> RadianceLafCortex
  • TridentCortex -> RadianceAnimationCortex
  • SubstanceButtonUI -> RadianceButtonUI

This first round of refactoring will be the next Radiance release. It will not move classes between modules. It will not add or remove modules, classes or methods. Migrating from 4.5 to 5.0 will require a lot of import refactoring, and some amount of refactoring – wherever you are calling Radiance APIs in your code. Once 5.0 is out, the next release will have follow-up refactorings for cleaning up places that have not aged well.

What’s the other big thing that I alluded to earlier? I want to provide support for consistent application of custom visuals across all supported Swing components. In Substance, this is done with painters. Due to a complicated nature of some of these painters, pretty much since the very beginning Substance has been using cached off-screen bitmaps to maintain a good performance footprint. The very first time a component needs to be rendered in a certain visual state, Substance renders those visuals to an offscreen bitmap. Next time, if we already have a cached bitmap that matches the current state, we reuse it by rendering that bitmap on the screen.

While this model has served Substance (and, by extension, the Flamingo components) rather well, it has started to show significant cracks over the last few years. You can see more information in this bug tracker on the underlying issues, but the gist of it is rather simple – screens with fractional DPI settings (125% or 150%, for example) do not play well with rendering offscreen bitmaps. The end result is that rendering a hairline (one-pixel wide) element can be fuzzy, distorted, or not there at all on the screen.

It is going to be a long road, and it might mean that it might take longer than usual to get the next Radiance release out the door. My current goal is that by the end of it, Radiance does not use any offscreen bitmaps for any of its rendering, and that everything is rendered directly onto the passed graphics object. Lightbeam will certainly come in handy all through that process. Wait, excuse me, Lightbeam will be no more in a couple weeks. It’s going to be Radiance LAF Benchmark instead.