Old news: Swing is complicated

Swing and MVC – what are they supposed to do?

I’ve been working with Swing for two years for now. I know, most of the java programmers learned it ten years ago or so. I also know, most of the java programmers would learn now javafx or apache pivot. Anyway, I started to program Swing two years ago because we use it at work. One of the first things I learned about it that its visual components are built on the top of the MVC pattern.

Actually, it is built on the top of an MVC variant called UI delegate. It means that Swing takes care of painting and capturing user inputs. Thus the Spring programmer only needs to deal with the ‘Model’. So if you want to draw something around your component then you can add a Border. If you want to change the text on your labels then you can call setText() on your JLabel.

This granularity literary screams for more structure but let’s worry about that later. In this post I’d like to explore a certain peculiarity with JButton. This book describes in great detail how you can use an Action to specify the text and the command for a button. Moreover, they show examples how you can assign the same Action to different buttons so they’ll have the same text, same command. This construction lets the developers to enable/disable the buttons simultaneously by enabling/disabling the Action.

By the way, this is how you can do it:

Action action = new AbstractAction("Click me!") {
    @Override public void actionPerformed(ActionEvent e) {
        System.out.println("Indeed, you clicked me");
    }
};
JButton b1 = new JButton(action);
JButton b2 = new JButton(action);
action.setEnabled(false);

This sort of thing is useful when you want to enable the same functionality on different parts of the user interface. Say for instance you have different menu items with the same command. (Reminder: menu items are buttons as JMenuItem extends AbstractButton.)

What about state?

So far so good. Unfortunately, some buttons have state. E.g. checkboxes can have (or not to have) a tick inside of them. Your customer might want to have a button that has different text for the in and out state.

The previous example doesn’t work well here. Well, the buttons would still have the same text and same command. However, if you check in one of them then it wouldn’t affect the other one. If you’d write this:

Action action = new AbstractAction("Click me!") {
    @Override public void actionPerformed(ActionEvent e) {
        System.out.println("Indeed, you clicked me");
    }
};
JCheckBoxMenuItem m1 = new JCheckBoxMenuItem(action);
JCheckBoxMenuItem m2 = new JCheckBoxMenuItem(action);

Then you could end up having one checkbox with the tick on, while having the other one off. This isn’t really what your customer wants, is it?

ButtonModel for our rescue! We can copy the state of the button with the ButtonModel. This is where Swing MVC shows its true power. Or isn’t it? If you write this:

Action action = new AbstractAction("Click me!") {
    @Override public void actionPerformed(ActionEvent e) {
        System.out.println("Indeed, you clicked me");
    }
};
JCheckBoxMenuItem m1 = new JCheckBoxMenuItem(action);
JCheckBoxMenuItem m2 = new JCheckBoxMenuItem(); // no action here
m2.setModel(m1.getModel());

Then you’ll see that the menu items will have their states synchronized. However, the second menu item won’t have any text assigned with it.

There is no single object responsible for a checkbox-s state. Of course we can fix it.

Bring the action too!

There was no text on the second button in our previous example. Indeed, we didn’t add the Action in the previous example. Let’s add that and see what happens:

Action action = new AbstractAction("Click me!") {
    @Override public void actionPerformed(ActionEvent e) {
        System.out.println("Indeed, you clicked me");
    }
};
JCheckBoxMenuItem m1 = new JCheckBoxMenuItem(action);
JCheckBoxMenuItem m2 = new JCheckBoxMenuItem(action); // action here
m2.setModel(m1.getModel()); // and model too

The buttons look OK now. Unfortunately, the action gets invoked twice. How is it possible?

Apparently, the second menu item notifies both its action and its model. The model, in turn, notifies its action. Hence the duplication.

Solving it

This is how we can make it work: the subsequent buttons need a model and a text:

String text = "Click me!";
Action action = new AbstractAction(text) {
    @Override public void actionPerformed(ActionEvent e) {
        System.out.println("Indeed, you clicked me");
    }
};
JCheckBoxMenuItem m1 = new JCheckBoxMenuItem(action);
JCheckBoxMenuItem m2 = new JCheckBoxMenuItem(text); // text here
m2.setModel(m1.getModel()); // model here

Short? Yes.
Straightforward? Not really.

Conclusion

There is no single object to represent a toggle buttons ‘model state’. We need two instead: A ButtonModel and a String object. This is a bit awry. So is Swing. I guess you already knew that.
Anyway, I created an example project to explore the possibilities. The project explores the basic concepts I described in this post.

There are tons of other things I’d like to improve in Swing. I.e. the pattern of subclassing a container only to specify it – it makes me shiver every time I’m writing such code. Sadly, all the alternatives I found were waaay to complex. Feel free to give your advice on this!

Advertisements

About tamasrev

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

One Response to Old news: Swing is complicated

  1. Pingback: Focusing on Swing | tamasrev

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