Animating layouts part VI - welcome to the real world
This is the sixth part in a series about automatically animated layouts in Swing applications.
- The first part introduced the
TransitionLayout. This part showed animated versions of
- The second part showed the
TransitionLayoutapplied to a (at least partially) real-world image viewer that allows live thumbnail resizing.
- The third part described the current implementation and alternatives that were considered.
- The fourth part showed a simple demo of an application that
TransitionLayoutinstalled on different containers.
- The fifth part introduced overlapping / layered transitions over the same component.
Once i started to test the
TransitionLayout on the Substance test application (and not on a simple app shown in the fourth part of this series), i saw all sorts of visual inconsistencies. The most glaring of these were on borders and on desktop panes.
As you may already know, the borders are not really a part of the look-and-feel domain. Sure, the look-and-feel can install the borders on the component hierarchy, but only if the currently set border is not a
UIResource. Hence, if you have installed a
LineBorder or some custom border, it will remain installed and respected by all core and most third-party LAFs. When the border is painted? The answer is in a quite convoluted
JComponent.paint(Graphics) method. It is done after the component is painted (by the relevant LAF delegate) and before the component's children are painted. What does it mean? That the neither
TransitionLayout nor the look-and-feel can change the translucency of the border painting without replacing the border itself.
The answer, therefore is quite simple. After a
TransitionLayout is installed, it is called every time that the layout should be performed. When the
layoutContainer is called, we scan the entire component hierarchy and replace all the borders with the
TransitionBorder implementation which simply delegates all the logic to the original border. The only thing it adds is to set the opacity on the
Graphics in the
paintBorder implementation. When the
TransitionLayout is unset, it restores the original borders. Special care needs to be taken in order to preserve the
UIResource-ness of the installed border - see the code (scroll down to the
Is it an optimal solution? Not really, since it may interfere with the application code. The application code may have some property change listeners on the components, and it may also have some hard-coded assumptions about the installed borders (like changing the text of the
TitledBorder or the color of the
LineBorder). If you have better alternatives - add them in the comment section.
Another issue that i ran into was with the
JDesktopPane. This drove me crazy for a few good hours of debugging and perusing the code. The problem was this - whenever a transition (fade-in / fade-out) involved a panel with
JDesktopPane, the background of that pane was opaque. After losing some hair (well, that would've happened anyway), i found two very interesting pieces of code deep down in Swing
The first is the
JDesktopPane.isOpaque() which always returns
true (huh?). The second is in the
JLayeredPane.paint(Graphics) which paints the entire component in the currently set background color / light gray if it's null when the component is opaque. Since
JDesktopPane extends the
JLayeredPane, the translucency settings set in the look-and-feel delegate are simply ignored. As far as i can tell, this is the only core Swing component that has painting logic outside of the LAF delegates. It is quite strange, but i guess that the
TransitionLayout is pushing Swing into directions not thought of ten years ago :)
Thankfully, the implementation of the
JLayeredPane.paint(Graphics) left a loophole that i have been able to exploit. If a completely transparent color is set as a background, the background of
JDesktopPane is not filled. All that was left to do in the desktop pane delegate was to fill the background based on the current transition opacity and then paint the component itself.
Is it an optimal solution? Almost. The only thing left to do in the next version is to respect the original background color set on the desktop pane in the painting and restore it when the LAF is unset. Will it provide a complete solution? No - since the
getBackground method will return an incorrect (completely transparent) value.
There were some other less interesting issues encountered and fixed along the way. As always, i'm thoroughly impressed by what Swing allows us to do and as always, i learned something new about it. The code is the release candidate stage as of today and will be officially released on November 13 (unless some real big problems are found).