Focusing on Swing

I’m working on software archaeology: I’m working with Swing on a day-to-day basis. Here I  explained why it’s complicated. Now we’re taking a look on how to customize the focus traversal.

Officially

If you want to override the default focus traversal then, officially, you have a very easy job: You provide your customized FocusTraversalPolicy, then you set the setFocusCyleRoot to true on your Container, and voilà.

JPanel panel = new JPanel();
//... customization
panel.setFocusCycleRoot(true);
panel.setFocusTraversalPolicy(new CustomFocusTraversalPolicy());

Well, yeah, this works well as long as you want to customize a single form, as seen on the official tutorial.

Little customization

Let’s suppose we don’t need this strict control over the focus traversal. This might be so because we’re manipulating the focus for something bigger than a simple form.

Suppose, you have an entire frame full of controls. All you want to specify is that the default focus should go to the default item on the “CENTER” element. After the “CENTER” it should go “NORTH”, then “SOUTH”. Inside of those containers we want to keep the default traversal policy. In this case, providing custom rules all over the place takes too much effort. Moreover, you don’t want to rewrite the rules each time you add a combo box, do you?

Our example application will be a simple frame that has 3×3 buttons. It doesn’t illustrate the complexity that we want to avoid here. But, it demonstrates whether the hack is working or not. This is how the GUI looks like without the hack:

Focus_experiment_default

And, this is how it’ll look like after the hack:

Focus_experiment_tweaked

The right comparator

So, how do we do that? A little debugging tells us that the default focus traversal policy called LayoutFocusTraversalPolicy. This is a subclass of SortingFocusTraversalPolicy. At first you might think, all you need to do is to provide a comparator to the SortingFocusTraversalPolicy that uses the default comparator as a fallback.

That would be great, but Swing doesn’t allow you to do that. LayoutFocusTraversalPolicy has some logic itself, it’s not only the comparator. If this weren’t enough, the LayoutFocusTraversalPolicy  uses LayoutComparator – and that’s a non-public final class. So we cannot either subclass or create it. Luckily, there is a way to obtain an instance: Subclass LayoutFocusTraversalPolicy and call getComparator().

This is how you can do it:

public class LittleCustomization extends LayoutFocusTraversalPolicy {
  public LittleCustomization() {
    customize();
  }

  private void customize() {
    Comparator<!--? super Component--> layoutComparator = getComparator();
    Comparator<!--? super Component--> modifiedComparator = wrapComparator(layoutComparator); // add your focus logic here
    setComparator(modifiedComparator);
  }

}

This is the full example application. To see the modified layout you need to set the -Dpredefined.layout=true system property.

Conclusion

Swing is not only complicated, but confusing too. On the one hand it prohibits subclassing LayoutComparator. On the other hand it is obligatory to subclass LayoutFocusTraversalPolicy.

Advertisements

About tamasrev

A software developer, specialized in Java, addressing himself as generalist. A proud daddy.
This entry was posted in programming, Uncategorized and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s