Radiance 2.0.1

March 11th, 2019  |  Desktop · Flamingo · Substance · Swing · Trident

It gives me great pleasure to announce the second major release of Radiance. It was all ready to go as 2.0.0, but what’s a release really if a blocker bug doesn’t make it in? So instead, you get to get 2.0.1 for now – pending any other blockers that would require a couple more minor re-spins. Anyway, let’s get to what’s new. 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

General

  • 💔 Java 9 is the new minimum requirement for build time and runtime of all Radiance modules

Modules

  • 💔 Removed Spoonbill (SVNKit-powered implementation of Flamingo’s breadcrumb bar
  • 😻 Added Meteor – Kotlin extensions for core Swing APIs
  • 😻 Added Ember – Kotlin extensions for SubstanceCortex APIs
  • 🤷‍♀️ Renamed Kormorant to Plasma
  • 🤷‍♀️ All core Kotlin modules (Ember, Meteor, Plasma) moved under the top-level kotlin-ext folder
  • 🤷‍♀️ Jitterbug (visual tool for editing Substance color schemes) renamed to Apollo
  • 😻 Added Ion – sample walkthroughs for replacing SwingWorker with Kotlin coroutines

Neon

  • 💔 An almost complete rewrite of NeonIcon APIs
  • 💔 Most Flamingo and Substance APIs moved off of ResizableIcon and to ResizableIcon.Factory
  • 💔 Moved some icon colorization APIs from Substance to Neon
  • 💔 Removed usage of UITable from FontPolicy API

Photon

  • 💔 Removed default public no-argument constructor from bundled templates for Java and Kotlin targets

Trident

  • 💔 Moved to builder-based construction of timelines

Substance

  • 😻 New Graphite Electric skin
  • 😻 New APIs for working with complex renderers, including built-in animations
  • 🤷‍♀️ Fix for incorrect offsets of rotated texts
  • 🤷‍♀️ Fix for inconsistent font metrics between preferred size and rendering passes
  • 🤷‍♀️ Fix for incorrect vertical position of icons in JOptionPane
  • 🤷‍♀️ Fix for crash in showing JColorChooser dialog
  • 💔 Moved all three Office 2007 skins to the extras pack

Flamingo

  • 💔 Moved all lower-level components (command button, command button strip. command popup menu, command button panel) to the new world based on content models, presentation models and projections
  • 😻 Added support for placing any ribbon content (including components, application menu links and galleries) in the taskbar
  • 😻 Added support for taskbar overflow (including built-in horizontal scrolling)
  • 💔 Keytips for taskbar content are controlled by keytip policy
  • 😻 Added support for separate keytips on action and secondary / popup areas of command buttons
  • 😻 Added support for global contextual menu on the ribbon
  • 🤷‍♀️ Added complete documentation

The first Radiance release focused on bringing all the different Swing open-source projects that I’ve been working on since 2005 under one roof. This release (code-named Beryl) is about making them work much better together. And it’s also about making it just a bit easier to use Flamingo components in general, and the ribbon in particular, in what one might call serious, if not even boring, business applications.

There’s still a long road ahead to continue exploring the never-ending 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 second Radiance release for a spin. Click here to get the instructions on how to add Radiance to your Gradle / Maven / Ivy / Leiningen / Bazel builds. And don’t forget that all of the modules require Java 9 to build and run.

Radiance 1.0.0

October 5th, 2018  |  Desktop · Flamingo · Substance · Swing · Trident

It’s been a few busy months since the announcement of Project Radiance, the new umbrella brand that unifies and streamlines the way Swing developers can integrate my libraries into their projects. Some of those projects have started all the way back in 2005, and some have joined later on along the road. Over the years, they’ve been hosted on three sites (java.net, kenai.com and github.com) in three version control systems (cvs, svn, git). Approaching the 15th year mark (with a hiatus along the way), it was clear that time has come to revisit the fundamental structure of these projects and bring them into a more modern world.

At a high-level:

  • Radiance is a single project that provides a Gradle-based build that no longer relies on knowing exactly what to check out and where the dependent projects need to be located. It also uses proper third-party project dependencies to pull those at build time.
  • Starting from the very first release, Radiance provides Maven artifacts for all core libraries – Trident (animation), Substance (look-and-feel), Flamingo (components), Photon (SVG icons) and others.
  • The Kormorant sub-project is the first exploration into using Kotlin DSLs (domain-specific languages) for more declarative way of working with Swing UIs.
  • Flamingo components only support Substance look-and-feel, no longer doing awkward and unnecessary tricks to try and support core and other third-party look-and-feels.

It gives me great pleasure to announce the very first release of Radiance, appropriately tagged 1.0.0 and code-named Antimony. Lines of code is about as meaningless a metric as it goes in our part of the world, but there are a lot of lines in Radiance. Ignoring the transcoded SVG files auto-generated by Photon, Radiance has around 208K lines of Java code, 7K lines of Kotlin code and 5K lines of build scripts.

It’s been a long road to get to where Radiance is today. And there’s a long road ahead to continue exploring the never-ending 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 very first Radiance release for a spin. You’ll find the prebuilt dependencies in the /drop/1.0.0 folder, and if you fancy a more proper dependency management mechanism, there’s an answer for that as well . All of them require Java 8 to build and run.

Converting List to Enumeration in Kotlin

August 15th, 2018  |  Kotlin · Swing

There’s a bunch of helper extension methods that the Kotlin standard library provides for working with collections. However, it would seem that at the present moment java.util.Enumeration has been left a bit behind. Here is a simple extension method to convert any List to a matching Enumeration:

And here is how you can use it to expose the local file system to the JTree component. First, we create a custom implementation of the TreeNode interface:

Note a few language shortcuts that make the code more concise than its Java counterparts:

  • Since File.listFiles() can return null, we wrap that call with a simple Elvis operator: file.listFiles() ?: arrayOf().
  • The initializer block sorts the File children in place by name.
  • To return tree node enumeration in children(), we first map each File child to the corresponding FileTreeNode and then use our extension function to convert the resulting List to Enumeration.
  • Looking up the index of the specific node is done with the existing extension indexOfFirst function from the standard library.

Now all is left to do is to create our JTree:

Replacing SwingWorker with Kotlin coroutines

August 7th, 2018  |  Kotlin · Swing

Note: code samples in this post are kept in sync with the latest stable Kotlin coroutine release. All the samples have been verified to work with release 1.2.0. Also see the official guide to UI programming with coroutines.

Kotlin 1.3 has been released, and one of the major additions to the language is the official graduation of coroutines from experimental to stable. Let’s take a look at how we can replace the old and venerable SwingWorker with something that is a bit more modern.

In the simplest use case, you do some kind of long-running work in your doInBackground() and then process the result of that work in done():

There’s a whole bunch of noise, so to speak, around the core of the logic – calling breadcrumb bar’s callback to get the leaf content for the newly set path, and then populating the folder based on the retrieved content. Let’s see how this code can look like with coroutines:

In this case, we have distilled the core of the logic flow to its essence – asynchronous loading of the leaf content, followed by updating the UI. Another option is to collapse the async / await block into a single withContext to change the execution context away from the UI thread:

Note that once you switch to coroutines, you also need a larger context for proper synchronization. In Swing, it means wrapping the entire listener with GlobalScope.launch(Dispatchers.Swing):

Now let’s take a look at something a bit more interactive – updating the UI on the ongoing progress of a long-running background task.

Let’s add a button and a label to a sample frame, and kick off a 5-second task that updates the UI every second on the progress using SwingWorker.process():

The first way to convert to coroutines would be with the help of channels:

Note the usage of Dispatchers.Swing context that is passed to the GlobalScope.launch() function and the wrapping of the emulated long-running task in another GlobalScope.launch lambda. Then, as long as that lambda keeps on sending content into the channel, the for loop iterates over the channel content on the UI thread, and then relinquishes the UI thread so that it is no longer blocked.

Now let’s make it a little bit more structured. While code samples found in documentation and tutorials run on the lighter side of things (including this article), real-life apps would have more complexity on both sides of async processing. We’re going to split this logic into two parts – background processing of data and updating the UI on the main thread. Background processing becomes a separate function that returns a data channel:

And UI code consumes the data posted to the channel and updates the relevant pieces:

Let’s make the background task cancellable so that the currently running operation can be safely canceled without subsequent erroneous UI updates. Background processing returns an object that has two parts – data channel and a job that can be canceled if needed:

And on the UI side of things, we keep track of the last job that was kicked off and cancel it:

Finally, we can move away from the existing concept of data communication “pipe” between the two parts, and start thinking in terms of passing a lambda to be invoked by the data producer when it has completed processing the next chunk of data. In this last example, the producer marks itself with the suspend keyword and uses the parent context so that cancellation is properly propagated:

And the UI side of things supplies a lambda that updates the relevant UI pieces:

If you want start playing with replacing SwingWorker with coroutines, add org.jetbrains.kotlinx:kotlinx-coroutines-swing and org.jetbrains.kotlinx:kotlinx-coroutines-jdk8 to your dependencies. The latest version for both of the modules is 1.2.0.