The Source for Java Technology Collaboration
User: Password:



Alexander Potochkin's Blog

Alexander Potochkin Alexander Potochkin is an engineer on the Swing team. He enjoys solving puzzles, traveling to different countries and always returning to Saint Petersburg, Russia.



RepaintManager's side effect

Posted by alexfromsun on April 13, 2008 at 07:49 AM | Permalink | Comments (2)

When I worked on the lightweight disabling of compound Swing components for my JXLayer project, I tried different approaches to workaround the problems with the custom painting of double-buffered components.

The last try was using the JComponent.print() method instead of JComponent.paint(), which effectively disables the double-buffering for the whole hierarchy of components. While it seems to be a good solution, it has a flaw: print methods may be overridden to provide a specific painting logic when a component is printed rather than painting on the screen.
(Thanks to David Browne and Kirill Grouchnikov for this information)

Finally I found a correct way of disabling swing containers without recursive disabling of double buffering for the subcomponents or using print() method. I'll describe it in my next blogs
(Some great news are coming)

Anyway, you may be surprised why I spent my time on that tricks when there is a much cleaner solution for disabling double buffering in Swing. I am talking about RepaintManager.setDoubleBufferingEnabled(boolean) method. At the beginning of painting you can switch the double buffering off and switch it on again at the end. Despite the fact that it changes the global setting for the whole Swing, no one will get hurt because Swing is a single-threaded library. It makes sense, doesn't it?

I must admit that I don't use RepaintManager in my java.net projects because I am aware of one side effect it causes.

Do you remember the famous GrayRect fix for JDK 6?

The main feature of this fix is the true per-window double buffer implementation, which eliminates unnecessary painting when a window of your java application is brought to the front after it was overlapped by another window. With JDK 5 you often see a gray rectangle when the damaged area is repainted, with JDK 6 the buffered image of the window's content is immediately copied to the screen so can't notice any traces of that.

RepaintManager.currentManager(null).setDoubleBufferingEnabled(false) disables the all Swing double buffering. Unfortunately, it is a one-way street for the gray rect fix, calling setDoubleBufferingEnabled(true) after that, enables Swing double buffering, but the gray rect fix is to be disabled forever.

The system RepaintManager is created by a private constructor with a special flag which enables the true double buffering. Once you set your own implementation of RepaintManager, the gray rect fix is gone, because there is no public API to enable true double buffering for RepaintManager subclasses.

So there is the same problem when you install a custom implementation of the RepaintManager.

This demo demonstrates the effect (JDK 6 is required):

Each time the JFrame.paint() is called, the new line is added to the JTextArea. When the demo window is shown, the frame is painted once. Check out the gray rect fix by moving another window on top of the demo's frame, you'll see that JFrame.paint() is not get called, because Swing uses the per-window buffer instead. This can also be checked without another application, just move the demo's frame partly outside the window back and forth, it will not trigger frame's repainting as well.

With the Options menu you can set a new RepaintManager or switch the double buffering off and on again, any of these action will disable the gray rect fix. Choose one of the actions and move the other window on top of the demo again, you'll see that the frame starts to repaint itself and the true double buffering is gone.

The source code for this demo is available.

That's all for today,
take care and see you soon

alexp

JTrayIcon update

Posted by alexfromsun on February 14, 2008 at 11:12 AM | Permalink | Comments (18)

JDK 6 introduced plenty of cool desktop features, like famous gray-rect fix or advanced drag and drop support,
all of them made java much closer to the desktop application market and I am very exited about them.

There have been many blogs how useful new features are, and I don't want to repeat them. Among all successful and well-designed features there is one which actually has not been completed,
I am speaking about JTrayIcon.

Swing and AWT team work together (by the way, AWT team seats in 10 steps from my room), AWT is responsible for the "native" parts of the code, like topLevel frames, event processing etc,
when Swing team if focused on their lightWeight components. Actually it is AWT who isolates Swing from any native code and makes Swing cross-platform.

It wasn't a surprise that TrayIcon feature came to AWT team, and they implemented it perfectly - it works well and it is supported for various Windows, Linux and Solaris systems.
They made it customizable by providing mouse event handling support and a special method setPopupMenu() which takes java.awt.Popup (which is quite old-fashioned but remember it was the AWT feature)

JPopupMenu doesn't extend awt.Popup but it looked like it would be easy to attach JPopupMenu to the trayIcon with help of MouseListener, but it was not the case.

The fact is that there is no way (or at least easy one) to correctly attach JPopupMenu to a TrayIcon.
It means that you can't set an icon for a TrayIcon's menuItem or make it look according to the current Look and Feel and, from my point of view, it makes TrayIcon much less useful than it could be.

It is our fault, sorry about that. Now let's see how we can solve this problem.

To make JTrayIcon possible I fixed three particular bugs for the next JDK 6 update release (the famous update #10, aka 6uN) and for the JDK 7 beta

ClassCastException in Swing with TrayIcon

If JPopupMenu is shown from a trayIcon's mouseListener an ugly ClassCastException was thrown in some cases.

Keyboard navigation problems and highlighting of the first item

If popup menu lost focus it stopped processing keyboard events in some cases, the first item a popup was highlighed by default which is very different from any other GUI I've seen

Swing Popups should overlap taskbar

Finally it was impossible to implement a good looking trayicon's popup menu, because Swing popups couldn't overlap taskbar

Having integrated all three fixes I managed to write the JXTrayIcon class which extends java.awt.TrayIcon and contains setJPopuMenu(JPopupMenu) method

Here is how JXTrayIcon looks when you click on it, with the current JDK 6

JXTrayIcon with the current JDK 6

the same code is run under JDK 6 update 10 or JDK 7

JXTrayIcon with JDK 6u10 or JDK 7

The red quarter circle icon is owned by the JXTrayIcon's test.
Note that the first item is not highlighted by default when the popup menu is shown,
if you move the mouse over it or press the arrow button on your keyboard, the proper item will be highlighted as with a system popup menu.

I encourage you to download the JDK 6u10 early bits and try out the JXTrayIcon demo
The runnable jar file is also available for your convenience

I particularly interested how it works in different environments,
any comments and suggestions are welcome as usual

Thank you and sorry this took so long.I hope it was worth the wait.

alexp

April 2008
Sun Mon Tue Wed Thu Fri Sat
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30      


Search this blog:
  

Categories
Swing
Archives

April 2008
February 2008
November 2007
October 2007
September 2007
January 2007
November 2006
October 2006
September 2006
August 2006
July 2006
June 2006
April 2006
February 2006
January 2006
November 2005
October 2005

Recent Entries

RepaintManager's side effect

JTrayIcon update

Making Rainbow: Spotlight effect and soft clipping



Powered by
Movable Type 3.01D


 Feed java.net RSS Feeds