Found the missing piece of the Cacio puzzle
May 8, 2009 2 Comments
The last couple of weeks, we’ve been quite stuck with Caciocavallo progress, especially with the complex components as TextArea, Choice, ScrollPane, etc. The problem was in the way the Swing components (those that back the AWT widgets) were connected into the peers. Let me explain how we used to do this.
So far, we had one specially modified Swing component for each peer. For example, the CacioButtonPeer used a subclass of JButton. In this subclass we overrode a couple of methods to make the JButton think it is actually part of some component hierarchy (remember that lightweight components cannot do anything by themselves, they need a heavyweight ancestor). For example, we needed to override getGraphics() to provide it a graphics to draw with, getParent() to make it think it has a parent etc. Those overridden method then hooked directly into the peer or the backing AWT component. getGraphics() would call getGraphics() of the peer, getParent() would return the parent of the backing AWT component, etc. As you can imagine, this can become quite aweful. In the case of button it’s still fairly easy, but for complex components it’s totally ridiculous. Stuff like ScrollPane and Choice are practically impossible to implement, or at least awefully complex. Event handling is also a pain. AWT events coming from the peer need to be forwarded somehow to the Swing component. In the case of the button it’s easy, just take the event, retarget and dispatch to the button. But for complex widgets it’s a bit more, well, complex. Since the swing component doesn’t really have a heavyweight parent, all the event handling and dispatching doesn’t quite work as usual, and events don’t get dispatched correctly down the hierarchy (complex Swing components like JComboBox actually have their own hierarchy!). And last but not least, the code is ugly, because it needs duplication all over the place. The problem boils down to the fact that the Swing components don’t have a heavyweight parent and need a lot of faking to make it think it has one.
Yesterday, after some talking with Mario and James (my still-boss) we had a light going on in our heads. What if we actually DO provide the Swing component a real heavyweight ancestor? Why not let it live inside its own Window? The idea is this: for each peer Swing component, we create a proxy Window object (a special subclass of Window, called ProxyWindow), and put in the actual Swing component as (the only) child component. Then, when creating the peer for the ProxyWindow, we don’t create the usual CacioWindowPeer, but instead a ProxyWindowPeer. This guy is then responsible for providing the ProxyWindow with things like Graphics objects and all the infrastructure it needs for serving the ProxyWindow and thus the Swing component. It does so by simply forwarding the requests to the underlying CacioComponentPeer. Event o handling is similarily easy: all events that show up in the CacioComponentPeer are now simply dispatched to the ProxyWindow, and there the usual lightweight dispatching takes place. From the point of view of the Swing component, it now lives in a real heavyweight window. No more hacks required to get anything to work, even the complex widgets now become fairly straightforward. We can use plain Swing components now, instead of custom subclasses. We don’t need to duplicate lots of code anymore (I guess I just shrinked Cacio code size by 50%).
A quick test shows that TextArea now works mostly as expected, with Scrollbars and everything. For comparison, before it took us _weeks_ (really) to get anything remotely useful with TextArea, now it took just a couple of _minutes_ (really, no kidding here!). I suppose we can show some cool stuff @JavaOne 🙂
In other news, Mario implemented multihead support in Cacio, yay! But I let him blog about this himself.