The Source for Java Technology Collaboration
User: Password:



Alexander Potochkin's Blog

November 2005 Archives


Debugging Swing - is it really difficult ?

Posted by alexfromsun on November 23, 2005 at 01:57 AM | Permalink | Comments (33)

Every experienced Swing developer knows that Swing components must be accessed from Event Dispatch Thread (EDT) only. Working with JComponents from any other thread may lead to unpredictable result.

Consider the following code:

import javax.swing.*;
import java.awt.*;
public class BadCode { public static void main(String args[]) { createGui(); }
private static void createGui() {
//this code must be run on EventDispatch thread!
JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().setLayout(new GridBagLayout()); JEditorPane pane = new JEditorPane(); pane.setText("Edt matters!"); pane.setSelectionEnd(pane.getText().length()); frame.getContentPane().add(pane); frame.setSize(new Dimension(200, 100)); frame.setVisible(true);
//clear selection pane.setSelectionStart(0); pane.setSelectionEnd(0); } }

Running this code I get the desirable unselected JEdiorPane, ... sometimes

Sometimes I get it like this:

Working with Swing not from EDT, you might see your components and layouts distorted or not reflected the current state.
It can work most of the times, but sometimes not, without any guarantee.

Actually the main rule is
"If you work with Swing not from EDT all sorts of strange things can happen".

It's been so many articles like this and this, so I suppose that most of Swing programmers know how it works and do the right thing.

Our previous example might be fixed this way:

public static void main(String args[]) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
             createGui();
         }
     });
    }


Pretty easy, right?

The fact that every JComponent's listener is automatically invoked on EDT makes it even more easy.

In my opinion, understanding that Swing is a single-threaded library is not difficult
(but it is not a big deal, since I am a Swing developer).

Funny thing, I took part in interviewing several java programmers who claimed to know Swing well, but at the same time some of them had no idea what EDT is.
One guy said that he'd seen Swing's strange behaviour only once, but he fixed it by changing code lines' order.
(Of course, he worked with Swing from incorrect thread)

That's why I am really interested in your opinion,
please look to this questions and give your valuable suggestions.

Set of questions

  1. Is it a common situation when developers incorrectly use Swing components from another thread and get the strange behaviour I mentioned earlier ?
  2. Is it difficult to make sure that all Swing code is invoked on EDT and find places when it is not ?
  3. Should we invent something to make Swing debugging easier?
  4. Would it be useful if we mark all EDT-only methods with special annotaion ?

Any comments would be appreciated!

I also posted new topic to javadesktop forum

Thanks
Alex



TabComponents in action

Posted by alexfromsun on November 09, 2005 at 10:51 AM | Permalink | Comments (7)

It's been a lot of publications about adding "close" button to a tabbedPane, all solutions suggested are really inventive and not easy to find. But we are gonna talk about Mustang "tabComponents feature" because it is the most preferable and clear solution for JTabbedPane customizing.

Since 1.6 three new methods added to JTabbedPane:

we can put a component to a tab with help of
public void setTabComponentAt(int index, Component component)

get this component with
public Component getTabComponentAt(int index)

and test if a component is a tabComponent for this JTabbedPane with
public int indexOfTabComponent(Component tabComponent)

With this three methods we can enrich a JTabbedPane with comboboxes, multiple labels or whatever we want.
Remember that JTabbedPane's tab area is not a real component, it is just an image so to insert a component there we can can draw a fake component in addition to the existing tab's title (it was the only solution before Mustang) or set our own tabComponent which can draw title and icon and contain any other additional components.

Please download source files and go on.

First, let's check how we can add the most wanted “close” buttons to a JTabbedPane

TabComponent with a “close” button

(source files in the blog.test1 package)


I sublcassed JPanel and added there JLabel to show tab's title and a customized button which can close this tab – and that's it !

Here how it looks:


Notice that we use a real JButton here so we can set a tooltip, attach an ActionListener and customise it in any ways

The only new thing is the close button's actionListener:

        public void actionPerformed(ActionEvent e) {
            int i = pane.indexOfTabComponent(ButtonTabComponent.this);
            if (i != -1) {
                pane.remove(i);
            }
        }

When we click to this button we find the index of the tab this button belongs to and close the tab.

The only possible problem is that this tabComponent doesn't respect actual JTabbedPane's titles so if we remove all tabComponents
(with help of "Options" menu from the test) we'll see:

JTabbedPane without tabComponents

Notice that actual tab's titles looks like tab1 not like tabComponent1.

In the next example we'll easily create better tabComponent which can show the actual titles.

JRadioButton as a tabComponent

(source files in the blog.test2 package)


Let's say we don't like JTabbedPane's focus rendering and want to replace it with something else.
For example we like JRadioButtons and want them to render font and focus for the tabs.
We can easily achieve that:


With this code:

        JRadioButton button = new JRadioButton() {
            //This way we always have up-to-date tab's title 
            public String getText() {
                int i = pane.indexOfTabComponent(this);
                if (i != -1) {
                    return pane.getTitleAt(i);
                }
                return null;
            }
        };

our radioButton will alway show the actual tab's title.

We only need to add one listener to radioButtons to select a tab when we select a JRadioButton
and one listener to JTabbedPane to do the opposite - select proper radioButton when we select a tab.

Crazy tabComponent with two icons

(source files in the blog.test3 package)


It is possible to show not only a title from a tab, but an icon as well.
Just for fun I created a big tabComponent which shows a title between to identical icons:


if we remove all tabComponents we'll see that actual title and actual icon are used:


We find the acual icon just like we find titles for the previous example

Conclusion

TabComponents make it possible to customise JTabbedPane component quite easily,
please try the latest Mustang build and create your own tabComponents !

let me give you some recommendations

  1. Keep in mind that tabs are not a real components just an image, so it is good idea to use components with setOpaque(false) to see tab's decorations underneath.
  2. Use proper spacing around your tabComponents, because big opaque component may overlap tab's decorations.
  3. To be on the safe side, test your tabComponents for all LaF's you
    use in your application (for WRAP_TAB_LAYOUT and SCROLL_TAB_LAYOUT).
  4. If you need to add a listener to your tabComponents, create the only instance of a listener and share it between all tabComponents
  5. Remember that only components without MouseListeners and MouseMotionListeners are transparent for mouse events. To be on the safe side use additional mouseListener which will select a proper tab when you click on tabComponent.
    For more information about that see my previous blog

Thanks
Alex





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds