Widgetry Sugar
Return to home page
Comments Loading...
2007-03-27

Continuing on from my previous post. If you're following Sam's blog on how to do Widgetry stuff, you'll notice that the APIs are quite rich. And with richness comes expressiveness. These are all great things. But if you are planning on saying the same thing a hundred times or so, being verbose about it can become very tiresome very quickly. That's where Widgetry-Sugar steps in.

The basic idea is to encapsulate the common UI patterns in to single method sends. For example:

myPane addButton: #buttonA labelString: 'Button A' whenClick: #actionA.

myPane addButton: #buttonB image: self bImage whenClick: #actionB.

The APIs to capture those common patterns will grow over time as I build more UIs with Widgetry. I've gone with 'option #2' from my last post. I've added a way to open up a window from any Form and you can define your UI as a subclass of a Form now - that means you can re-use the Form inside other forms or make them a distinct window on their own. I may drop this in the future but it seems like a good pattern for now.

I haven't yet talked about how you deal with frames and layout. I briefly suggested I had something to say about this in my last post on Widgetry. So time to talk about it.

Most layout in a UI falls in to two a couple of common patterns. A Menubar, a toolbar, a set of vertically or horizontally layed out widgets. These aren't hard and fast rules mind you, but the point of Widgetry-Sugar is to capture the common scenarios.

Most people, when I talked to them about horizontal and vertical layout, assumed I was going to make a ComponentPane subclass that layed things out vertically and horizontally. Unfortunately, that approach IMHO is a bit of a misnomer. For starters, they aren't really widgets.

A widget is something that has one or more of a set of properties: It can visually represent itself, it can accept interactions from the user, it takes up physical space on the screen. A vertical or horizontal layout pane would not satisfy any of these requirements.

The final nail in the coffin for me wrt to vertical and horizontal layout as a pane came when I realised that it'd be a bad way to do it if the layout algorithms got more interesting than straight vertical and horizontal. I realized I wanted an Assistant, not a Pane.

The idea of an Assistant is something that hooks in to Pane announcements and/or modifies state on Panes. So in this case, I want an Assistant that listens for bounds changing and then changes the frames of a set of panes to layout accordingly. What we're talking about here is adding a dynamic layout component to Widgetry. This is a step toward the kind of behaviour I implemented in WithStyle which would layout widgets based on CSS layout rules.

So I have VerticalLayoutGroup and HorizontalLayoutGroup in Widgetry-LayoutGroups. These guys can be called in a Widgetry-Sugar sort of style, eg:

self verticalLayout: #( paneId1 paneId2 paneId3 ) heights: #( 100 auto 50 ).

self horizontalLayout: #( ( paneId1 paneId2 paneId3 ) ) widths: #( auto ).

There are a few special things going on in here. First of all, the vertical layout will only setup y and height coordinates on the listed panes and the horizontal will only do x and width. So you need to call both to setup your layout, otherwise you'll have a squished up ui in one axis.

The second magical thing going on here is that inside the array of pane ids, you can put another array. It will then apply the same width to that whole array of ids. For example, if I want three widgets across the top of my window, but I want them to all have the same height, I can put them in an array inside the array for the vertical layout call.

The third magical thing going on here is that the entire thing is announcement driven. The widgets are not even positioned until the window gets the Mapped announcement (before the events are configured though!). When the enclosingPane resizes, they resize themselves too. Because it is event driven, you can have as many horizontal and vertical layout sets as you want.

The fourth magical thing going on here is the #auto symbol I threw in to my example above. At the moment, you can only have one of the widgets in the set be auto - the auto widget will take up the remaining free space after the other panes have occupied the space you requested them to take. This allows you to have one widget in your UI that will act as a "client" area.. it will stretch to fill up the space.

This stretching behaviour is very similar to the Delphi layout concept of a 'client' area. They would give you a layout that consisted of Above, Below, Left, Right and Client in the middle. The same thing can be found in the dojotoolkit ajax javascript library.

This isn't the most powerful layout concept - that's why we still have all our generic Widgetry Frame functionality. But it is a very common layout pattern.. so here we have it now nicely engendered in an easy to use library. It also means, you don't have to write a lick of FractionalFrame top, left, right, bottom, offset code. This saves you a lot of time and heart ache when making a Widgetry UI in code.

Keep reading Sam's blog to find out how all of this stuff works at the official API level. My job is just to satisfy the common elements of building a UI so that we can kick start the effort of making new VisualWorks tools with Widgetry.