A great man once said: “I love it when a plan comes together”.
It’s time to explain the final pieces in this little pet project of mine, and release the whole as a (already rather usable) preview.
Swing has many advantages, let’s agree on that, but on the other hand it has many, so many, things that just make me want to jump out my 1st floor window when I use it. None of those are deal-breakers per se, but it does feel a lot like ‘death by a thousand paper cuts’. This is my attempt at correcting those pain points, and making using Swing a bit easier.
The problems I’m trying to solve
Without a doubt, the biggest pain point in using Swing is its API. First of all, components have a huge number of methods, it’s really frightening. A lot of them are useless AWT methods. To me, the most important qualities of a good API are related to the user experience: conciseness, and discoverability.
As a user, I want to write short amounts of code, and I don’t want to need to remember a lot about how the API is used, or read a ton of docs to do simple things. Swing fails on both of these points.
I could go on. The whole event system requires a lot of classes and methods. There’s no way to group components in a logical group, only in a physical group. So if you want to say hide 5 components, your choices are 5 setVisible() method calls, or group the components in a panel, and hide that one instead. The latter offers a nicer way to hide the components in one call, but comes with performance and layout problems related to adding another component in your UI.
And so on.
jQuery’s mantra is ‘Write Less, Do more‘, and that’s a motto I can get behind, no matter the programming language. How does jQuery achieve clean and concise code, and how would the concepts it’s built on look in Java, and Swing ?
The answer to that has multiple parts, and by now you’ve seen most of them. A query engine (hQuery + Shizzle) will help you select one or more components in your UI – which allows you, if you want, to not have to create a member variable for each of your components. You’ll then interact with the results of your query using a composite – reducing boilerplate code – or your member variable components as usual, using Swing wrappers/builders (HTk) implementing a hopefully easier API. Let’s start from the top of the stack and build our way down.
HTk
There’s multiple ways to make the Swing API easier to use. I could modify the Swing classes directly, but that would require too much work. I could make new Swing components performing the functions of existing ones, but that would force me to extend JComponent at the very least, and having a lot of methods I don’t want in my API. The last of the most obvious methods is I could have builder or wrapper classes that create or configure an existing JComponent instance, and only interact with the builder.
The latter allows you to start with a clean slate, and offers the best work/reward ratio.
HTk is a small set of builders for the most common components and methods I use (the version I’ll release today only targets the most basic components, but the version I use contains builder for more advanced components like tables and list. I want to wait to have more experience using and improving them before releasing those). I already talked a very tiny bit about them last time when we talked about Shizzle, and today I’ll describe them in more detail.
For “each” of the JComponent classes, there would be a HComponent class that would provide the builder for the associated JComponent. HButton builds a JButton, HLabel builds a JLabel, and so on. The code still looks like you’re using Swing components, but the HComponents are never added to the UI – In turn, it also creates a problem, since you don’t have any HComponent in your panels, when you want to query them (say, disable all the buttons that apply to a table row when there’s no row selected in the table, this should be one line of code, and now it can be), so each HComponent adds style classes so you can still query them, as we’ll describe more in the next section.
So when you create an HComponent, it’ll create a delegate JComponent to configure, or you can pass it an existing JComponent instance – since you might want to interact with the components using the same API regardless of whether you’ve already added them to your UI or not. This delegate JComponent can be accessed with the asComponent() method. This is useful to know because as I’ve said the point is to reduce the number of methods in components to most useful ones, and you need to be able to access the underlying JComponent to call the less common ones. HTk is not a new toolkit whose goal is to replace Swing, the goal is to help you for the most common cases you’ll encounter (right now it’s of course biased towards the most common cases me and the friends at my company encounter – in our own projects and the client projects we use it in; but releasing it will hopefully start a discussion on what your needs are, dear reader).
The other methods are either just getters/setters and methods from the underlying wrapped JComponent class, or new methods to simplify the existing API (eg, events) or new features such (eg, style classes).
For conciseness, the HComponents support method chaining and I wanted to talk a bit about that. Since setters usually return void, and chaining setters actually looks weird – see component.setX (2).setY (0) – I started having with* methods too, a fairly traditional way of having a setter returning this. However, that made having 3 methods for each property instead of just 2, which went directly against the design principle of limiting the number of methods. So I decided not to use the JavaBeans notation and I used the name of the property for the getter and the chainable setter, mimicking named arguments in a sense – component.x (2).y (0). As I don’t need to use reflection on those components, it has not created any problems yet. Nothing is set in stone, we can talk about it, maybe you have a better solution.
Since the goal was just to wrap Swing objects, just to have a smaller API, from blank or existing instances, I chose to have stateless components, and then little by little we thought we could add some features that required state (say a suffix in a JLabel), and then also wanted to create custom components. I don’t have the complete answer on this yet, things can be improved greatly to make it easier to add features and create custom components. Until now, the focus has mostly been put on using existing Swing APIs, rather than creating new components, so it’s understandable – How this is done right now, is the component’s state, when it needs one, is stored using client properties in the delegate JComponent. It’s a little bit tedious (for people who write HComponents, so you’re safe here) and we’ll talk about this later in the ‘What can be improved’ section.
The more common new methods in HComponent are used for queries. Naming a component with as (“aName”), and handling its style classes (addStyle and removeStyle, hasStyle, toggleStyle, etc). Then there’s also a number (actually too big a number) of methods dealing with the component’s border some dealing with sizes, bounds and location, property change listeners, etc.
Since I just mentioned property change listeners, I’d like to take a moment to describe what the plan is for events. Still a work-in-progress, but I added some example code in this preview, and I think we can do the same thing other toolkits have done, and just use one method call to add a listener, and one to remove it, whether it’s a regular Swing event or a new one (for when custom component creation is more fully fleshed out). You have a generic EventSource interface that binds the event type to its listener type (say ActionListener to ActionEvent), which would be used when registering listeners. HComponents have a connect() method, which takes an EventSource and an event listener. You’d then have an EventSource object for Swing events (in the preview code you can see ActionEvents, MouseEvents, MouseMotionEvents, MouseWheelEvents) and another class for custom event sources (based on composites of course, which would then use another generic listener interface EventListener). For Swing listeners, it’d look like this: component.connect (Events.mouse, aRegularAWTMouseListener) or component.connect (MouseEvents.click, aRegularAWTMouseListener). I guess you could also have component.click.connect (listener) if you wanted to. The HComponent will then call the appropriate addListenerType method.
Enough about components – and specifics can be seen in the code – and let’s turn to the container, the HPanel class.
At its core it’s very basic, providing standard methods to add and remove JComponents or HComponents, or setting the layout manager, etc. But there’s really two things of interest here. The first is the logical grouping. I’ve mentioned style classes before, and they’re part of the answer to the lack of logical groups (querying for components matching a set of rules in general, and most of the time having a specific style class, allows for an easy way to get a group of components regardless of where they are in the component tree). The HPanel offers a way to automatically add a style class to a whole bunch of components without adding the style class to each of them manually, it’s a little helper that can be useful. The group method returns a StyleGroup object which’ll just have add() methods. By calling them you’ll add your component to the panel as usual, and also at the same time the group’s style class will be added to the component’s existing style classes. Like so panel.group (“myStyle”).add (component1).add (component2). I decided not to use varargs because you can add both JComponents and HComponents to an HPanel and thus to a StyleGroup, and so you couldn’t have one method that’d take a variable set of JComponents and HComponents.
The second thing of interest is the most novel one, it’s the link with the next layer, the query engine. So there’s methods taking a String selector query and returning the handle to the result, an HQuery instance.
hQuery
Now that HTk provides a smaller API over Swing, we can follow jQuery’s path and provide an object that would help with conciseness over multiple similar method calls, or as a way to remove the need for having a member variable for every component in your screen/page (as the default Netbeans/Matisse settings generate).
A long time ago we saw that composites allow for concise code, we still need a way to fill the composite with values, to loop over and do its proxy job. Last time we saw Shizzle, a query engine that worked on Java and Swing objects using the CSS selector syntax, this will give us our components. This is where hQuery comes into play, as the glue code between a query and the results’ composite.
Again, hQuery is ridiculously small, you only need to know about 1 class (actually 1 interface; there’s also two implementations, both of which you’ll never need unless you want to extend the library. As I said very small).
When you call the query method on a panel, it’ll return an instance of the HQuery interface so you can interact with the results. Its implementation uses Shizzle, to query the panel component tree, and fills a composite with the results, really simple. I like to create a HQuery $ (String selector) { return query (selector);} method on my page/screen class (extending HPanel) so I can also have a short way of calling it.
So now $ (“aQuery”) returns an HQuery instance, but what can be done with it ? Mostly three things, the fist one being calling some methods defined at the HComponent level. For instance, returning to our early example, if I have a table and a set of buttons launching different actions, some of which only apply to the selected row (for those I would have just used a StyleGroup or individual addStyle calls on those specific buttons). In my selection changed listener, all I’d need to do is call $ (“.buttonsNeedingSelection”).enabled (false) if the selection was empty and all those buttons would be disabled.
Similarly, you can control the visibility of the results, manage the style classes, add the components to another panel, or detach them from their current parent container, or connect/disconnect an event listener, etc.
The second things an HQuery instance offers is to do some other query, using the results as a starting point. For instance you could use find() and search for some of the results’ descendants, or use the parents() methods, this time to query the component tree upwards.
And lastly, but the most useful in practice is getting some kind of handle on the results (a composite most of the time). Actually all the first set of methods, including the $ (“.style”).enabled (false) example we saw, create a composite on which they call a HComponent method, enabled() in this case.
As there’s different ways to use the results, there is going to be different methods depending on whether you want to loop over the results yourself or use a composite. In the first case you could use contents() to get the List of JComponents, but there’s another way. asJComponent() (if you have only one result) and asJComponents() (if you have 1 or more results) both take a class that extends JComponent, so you could do for (JButton b : $ (“JButton”).asJComponents (JButton.class)) {}. The same method exists for HComponents too, and since we’re querying inside a live UI containing only JComponents, every JComponent will be wrapped by an instance of the HComponent class you specifiy.
So these methods give you a List, but this is not the way I prefer to use hQuery. I usually want a composite of the results, and these are the last of the important methods this object provides. The asComposite() method takes a Class extending HComponent, and obviously returns a composite of the results accessible with a proxy of the class you specified (the parameter-less asComposite() method returns an HComponent composite, which is the most basic one, the ones used by the HComponent methods at the HQuery level). And there’s also 2 similar methods for composites on JComponents.
Most of the time I use the as() method, which just calls asComposite(). I’ll show you an example just after I describe one last trick. Since I’m using quite a similar API, I decided to apply Fabrizio Giudici’s cute class literal trick and I added in each of the HComponent classes two public Class variables for the component itself and the JComponent class it wraps. With a simple static import, you could select the text of one or more JTextFields with such a line $(“.HTextField”).as (HTextField).selectAll(); (Tip: If you’re using Eclipse, static imports can be added on demand, type $(“.HTextField”).as (HTextField.HTextField), select the HTextField variable, and then ‘add import‘).
Quite readable, short and clean, IMO. And since the query goes straight to Shizzle, of course all of its syntax is supported, which is pretty powerful.
The last method is a work-in-progress, and you can’t really use it just yet. Like jQuery, hQuery is really small, but the point is to have a tiny core that can be extended with plugins. The with() method takes a class extending the last of the 2 classes contained in hQuery (the DelegatedHQuery class), there’s some useless examples in the code, we’ll see what happens when we cross that bridge but the first plugins that came to mind were related to JXLayer, effects, animations and transitions, form validations, etc.
Shizzle and Composites
Since hQuery binds Shizzle and Composites together, under a nice simple interface, I’ve updated both versions. While Shizzle saw some little modifications, a couple more tests, and so on, the composites have changed quite a bit (even if I surely intend on recoding them from scratch once again, so they’re better tested, right now, it’s pretty well, organic).
The composites I’ve already described allow you to have any number of objects behave like one object (within certain technical restrictions). The version I released was based on the JDK’s dynamic proxy. The biggest advantage to this is you don’t need another library to use them. The big problem is it only works with interfaces. Since then I’ve switched to cglib to create composites of classes (this is where the technical restrictions come into play, as final classes and methods can’t be easily used in a proxy). It also comes with the perk that the proxies are faster than the vanilla JDK ones. The downsides are, there’s another dependency, and since we’re generating proxy classes, the PermGen could become polluted (even if in practice this has never happened to me, I have not looked at it that closely since today’s project only creates a low number of proxy classes. If that problem ever surfaces, Google’s Guice already contains solutions which can be looked at, and Guice also uses cglib, so it’d be fairly easy to integrate).
Composites are at the lowest level, you don’t create them or need to think about them, except in those specific cases where they technically can’t work: creating a proxy on final classes, calling final methods on a proxy, trying to use getters returning classes that can’t be put into another composite.
Another modification since last time is of special interest in the HTk method chaining. If you’re calling a method that returns something, a composite of the results of those method calls will be created (with the regular composite limitations) so you can call another method if you want to. (You can also extract the individual values if you want, look at the code)
What can be improved
Even if this preview is quite usable, and we do use it, there’s still some rough parts that can be improved. As I mentioned before, there’s little support right now to create custom components based on the HTk APIs. It should be much easier. Maybe instead of using client properties for each of the custom components properties, the full HComponent wrapper could be stored inside a client property. Maybe other self referring generics tricks could be used to help in creating classes extending from existing HComponents, like HPanel. Right now, you’d need to override a lot of methods just to return your own type to allow transparent method chaining mixing all the classes.
The different borders methods in HComponent are a little too numerous, I’d like to reduce them to one or two, and move the rest to an inner builder object – maybe component.border().top (5).bottom (2) – or an outer builder – component.border (Borders.empty().top(5).bottom(2)). You guys probably have better ideas.
What needs to be done or would be nice to have
The whole event mechanism is only at its beginnings. All the Swing event classes need EventSources. A consistent good way to use and create custom events should be decided.
Another nice one related to eventing could be live events. Something like $ (“JButton”).live (MouseEvents.click, listener). Basically, you insert your event listener as an AWTEventListener directly to EDT, and this listener would use Shizzle to see if the source matches the query you specified. So whether the UI component tree changed, if new JButtons were added or removed from the UI, you wouldn’t need to add another click listener. It seems cute and nice, but I’m not really sure it’d be as useful as in the HTML DOM (where events bubble and come back down already, which is not the case in Swing when there’s no registered listener for a specific event type)
And of course last but not least, the plugin system would also be interesting to have.
Move to github ?
Downloads
As always, the license in this preview is BSD.
Here’s the zipped Eclipse projects, containing both the source and the binary jars.
Conclusion
I hope this preview can start an discussion, be it about Swing pain points we could solve or even maybe JavaFX 2 (as it seems from the little slides I’ve seen they intend to keep the JavaBeans get/set rule; the only thing that seem different was about properties and events, where it seemed they would use a very low number of methods; I hope the awesome Alex Ruiz is being listened to up there, because he’s the man when it comes to small, powerful, concise and expressive APIs, just look at FEST and dare say I’m wrong).
I hope you’ll find this useful. To me, the combination of small and discoverable set of common methods HTK offers, coupled with composites and CSS selectors make a powerful set of tools.
I’d love to get feedback on this, you can find me at twitter.com/lqd.