Jul 8 2014
Swing tips – issue #1
With a small delay, but as promised – I am starting a new series of posts about various Java and Swing tips & tricks that might help you understand it and develop awesome-looking Swing applications of any type.
Check out the first three tips under the cut.
Keep Graphics settings clean
This is not obvious but sometimes hard-to-find issue that I have come across a few times, mostly while working with custom themes for any kind of Swing containers starting with simple JPanel and ending with complex components like JTabbedPane.
Let’s jump straight to the example where I have seen this issue last time. Imagine that you want to create custom border for your panel that contains some checkboxes, so you write something like this:
But when you launch it you get an unexpected result:
All check boxes decided to use your dashed stroke without asking you!
So why that happened in the first place? Because you have cleaned up Graphics settings you’ve set. Specifically in this case – stroke settings. Swing uses the same base Graphics to paint the whole components tree within one “run” (for example one container repaint) so if you set some tricky settings like stroke, composite or transform into Graphics object – make sure you restore its previous value after you have painted what you wanted to.
So what you have to do to avoid this in our specific example:
And here we go – no more dashed check boxes:
To simplify all those settings set/restore actions I have made a few utility methids in WebLaF which can be found in GraphicsUtils class.
Heavyweight popup
You might have noticed that in v1.28 update there was a new component added named WebHeavyWeightPopup. It uses Swing PopupFactory to create heavyweight popup, retrieve its window and provide some useful options to control that window.
If you had some experience with Swing popups you already know that PopupFactory usually provides mediumweight popups which might be, depending on the case, placed on the layered pane of the JFrame/JDialog/JWindow or in a separate JWindow. But it doesn’t actually allow you to specify the popup weight directly and uses its own logic to determine this type for each case, but almost always it chooses mediumweight popups to fit any possible case and save up some resources in case new window for the popup is not created.
This approach is pretty good in case you are working with static rectangle-shaped popups like the default JComboBox popup. But hey! We are trying to create something pretty here, not just another grey rectangular menu with a poor visual representation. So this approach was kinda useless for me and started to test some options.
First of all I have tried using JWindow as a popup but had a few issues with parent frame focus blinking sometimes which is really annoying and ruins all the benefits of this approach. Everything else about JWindow is just fine – you can manipulate it directly and change/configure/move it however you want.
So I looked into the PopupFactory again and have found a pretty simple trick to create heavyweight popup:
That method is private within the PopupFactory but it allows you to specify popup type in addition to its invoker and location. So I have examined the code of PopupFactory in different JDKs and came to the conclusion that there are no possible bad consequences. So here we go – our personal heavyweight popup anytime we need it!
But popup on its own is not too easy to use so I have created WebHeavyWeightPopup class that works as popup shell and provides direct access to its window and some window settings. I have already replaced a few popup-cases with this new component and works like charm. So I will be modifying the rest of the custom popups later.
Application classpath
There was one more major feature added with WebLaF v1.28 update – PluginManager. I know that this is already a bit out-of-scope of L&F library, but WebLaF is not just about the UI – it is about features that allow you to build the whole application with ease and style!
This feature was partially pushed by our own in-company needs of a brand new PluginManager that must be agile and fully configurable. So after using this feature in a few projects I have found that loading plugins through new URLClassLoader doesn’t always work the way we wanted to. In a complex server application loaded plugin couldn’t find some of dependencies from the main application which was really odd.
So here is how the initial class loader that was loading plugin looked like:
So basically there was a new class loader created for each plugin that loaded all of its dependencies and its own JAR. It used current class loader as a parent class loader, but that didn’t really help to solve the issue. After playing around with URLClassLoader for some time I have found a simple trick to modify its classpath on fly:
This protected method basically adds a new URL into the URLClassLoader classpath, just make sure you are calling this on URLClassLoader instance.
This can be done without reflection by simply overriding URLClassLoader, but I wanted to load plugins using the initial class loader without creating additional ones so I have simply added this call to the base class loader to “integrate” loaded plugins into existing classpath. And this worked perfectly.
Of course there are different cases and you might not want to use this trick and keep using the new URLClassLoader creation – this is configurable.
Summary
These are the three issues I have met and solved recently.
Hopefully you have learned something new from those tips.
I will keep posting new tips ocasionally and will bring more interesting and useful topics.
Thanks for reading this article and stay tuned!
Walter Zinsmeister
Jul 16, 2014 @ 20:42:10
Hello Mikle! First, great work, I have been looking a VERY long time for an LAF I really enjoy and have never been satisfied til I found yours. Secondly, I did have a couple questions, off topic to this post, so I wont post them here, but I have been attempting the past 2 days to register on the forum but never receive my email for confirmation. My email is the same one entered for this post and my username I had used to register was “walazins”
I was hoping you could look and see if there was an error on the registration. When I login it tells me the account is not activated yet, so the registration did go through, I just did not get the email.
Thanks a lot!
Mikle
Jul 17, 2014 @ 16:03:08
Thanks for the feedback, there is indeed an issue with new users activation on forum, I have manually activated all inactive accounts – you can use yours now.
I might fully replace forum with GitHub issue tracker later since it is more convenient for issues/questions tracking and commenting overall.