This post originated from an RSS feed registered with Agile Buzz
by James Robertson.
Original Post: How To Create A Custom Widget - Pane
Feed Title: Pollock
Feed URL: http://www.cincomsmalltalk.com/rssBlog/pollock-rss.xml
Feed Description: Pollock - the next VW GUI
There are several decisions that have to be made when creating a new widget for Pollock. They are: Deciding where in the Pane hierarchy to place your new Pane widget. Where in the Agent hierarchy to place your widget's Agent, or whether to use an existing Agent. Where in the Artist hierarchy to place you widget's Artist. And finally, which standard Pollock Controller should your widget use.
Where to subclass a new Pane
There are basically five points in the Pane hierarchy that can be for a new widget. These depend on the type of widget you want to create. All are in and of themselves abstract classes.
The common ancestor for all Panes is AbstractPane. This is the most general superclass that you could choose. You would subclass from this pane if you have a widget that does not contain other panes. In Pollock, the Divider and Slider are examples of panes that do not have the ability or need to host sub-panes.
The next place would be to subclass from ComponentPane. It adds behavior to allow an arbitrary number of sub-panes to be added, and knows how to manage them. In Pollock, the Form, MenuBar and Toolbar are examples of panes that contain other panes. Also in Pollock, Button is a subclass of ComponentPane. In theory, a ComponentPane can have any number of sub-panes, of any type. While the Button's main business is to host a DisplayLabel and/or a DisplayImage, it could be made to hold any number or kind of pane.
The third place is the EditingPane. The EditingPane is where you would subclass if you wanted to do some special kind of user input editor. The two existing subclasses of EditingPane are InputField and TextEdit. This class has a lot of support for doing things with the cursor, selecting, editing and managing text entry.
The forth place is the EnumerationPane. This pane is where you would subclass if you have a pane that wants to display an enumeration or list of some kind. The TreeView, ListBox and Grid are subclasses of this pane. This pane also has all the general plumbing needed to support scrollbars for any subclass.
Finally, there is the ActionDisplay. This is a special kind of ComponentPane, and itself subclasses from ComponentPane. Where a ComponentPane in general can have any number of subpanes, the ActionDisplay pane is designed on the idea that there is a basic "Action" pane, which is not a sub component, and a "Display" pane which is a sub-component. The simplest way to explain this pane is by example. The RadioButton and CheckBox are subclasses of the ActionDisplay. The Radio part of the RadioButton is the "Action" part, as is the box of the CheckBox. The optional label, which may be on either the left or right of the box or radio area, is the "Display" part.
This design was taken a step further, and was applied to the DropDownList, the MenuButton and the SpinButton. For these, the "Button" area is the "Action" part, and the input field is the "Display" part. True, the Display part of these are actually not static labels like the RadioButton or CheckBox, but active (or read only in the case of the MenuButton) input fields. So, the name, ActionDisplay doesn't quite stand up. Vassili suggested once that it might be called ActionAction. None the less, ActionDisplay it is, and various behavior have the word action or display in their names.
Calendar Pane
Our Calendar widget is very much like a DropDownList, except that instead of a List that drops down, it will be a pane with a calendar in it. So, we'll subclass from that. As an aside, there is an argument to be made that we could just subclass from DropDownList. There are some minor pros to that. But, since we're all about learning here, we'll go with subclassing from ActionDisplay.
Before we write a single stick of code, let's be uber XP and write our first Unit Test.
You'll need PollockTesting loaded, because we're going to write our test case as a subclass of a the special Pollock test case. Also, it is best if we all agree to load the RBSUnitExtensions as well as SUnit itself. The latter will have to be loaded in any case, since PollockTesting relies on it.
So, we'll start by creating our main TestCase subclass:
If you are using the RBSUnitExtension, as I suggest, as soon as you create that class, you'll see the bottom of the browser say: Not run: 0 tests.
We added instance variables named calendar, eventValue and secondEventValue. The former is where we can put our Calendar pane when we're writing tests. The last two are for testing trigger events for our pane, when we get to writing support for them.
If you run those (press the "Run" button in the RB, because of course, you do have the RBSUnitExtension loaded, right?), you'll see that they both fail, as they should. You'll note that we use the binding reference feature of VisualWorks. We could have written the tests using Pollock at: #Calendar. However, I like this form better.
In good XP form, we never let any test cases fail before we publish/integrate our code, so: