Skip to main content

Animating layouts part VI - welcome to the real world

Posted by kirillcool on October 30, 2006 at 11:02 PM PST

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 BorderLayout and FlowLayout.
  • The second part showed the TransitionLayout applied 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 TransitionLayout installed 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 TitledBorder, 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 installBorders method.

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).

Related Topics >>