If we run this, it will fail because there is no #interiorDecoration method. Before we go ahead and create this method, we need to discuss where things go and why. In the case where we are creating or placing an object in the world that is basically a display thing, we give it to the artist. That is where we'll create our instance variable for this internal decoration. But we need to go further. A Pane is always the public interface to all that is going on in a widget. Interior decorations, like most things in Pollock, can be changed at runtime. That means we need to give the Pane, our Calendar, an interface to the interior decoration. So, we'll add setters and getters to the Calendar also:
In general, a drop down widget has an interior decoration by default. We can write a testing method on Calendar to tell us if there is one. First the test case method:
This will blow up with an error, because there is no #hasInteriorDeocration method, much less one that answers true or false. So, let's fix that... we'll write the #hasInteriorDeocration method:
Now if we run our test, we still get a red bar, but it now says it's a failure. We need to have our Calendar create the interior decoration for us. The easiest way, and it turns out the Pollock way, is to just add an initialize method. We don't have to play with special class side overrides of new, all base GUI classes in Pollock already do that, and call initialize.
About Borders & Decorations
The question now is, what do we use for an interior decoration? Pollock provides a whole host of ready to use BorderDecoration classes, and BorderDecorationArtists to go with them, which take care of almost any kind of border like thing you can imagine. Looking in the "Pollock-Pane-Decoration" package, you'll see our vast selection. Most of these border decoration objects subclass from BorderDecoration. Others, such as ScrollbarDecoration and TabControlDecoration are more complex, and have both border decorations in them, as well as other things, such as, well, scrollbars, tabs and or grid headers.
Border decorations are in a way second class Panes. They have artists, which do the drawing for them, but don't have their own frames or agents. Being able to have a distinct artist, as well as the general capability for changing things at runtime, allows us to build many different kind of borders by combining a basic border with a border artist. In a Windows9x/2k look, the interior decoration of a drop down widget is two pixels wide. Each side of the border has two colors, one for the inside line, one for the outside line. Fortunately, Pollock has run into this before, and has a BorderDecoration class and artist all ready to go. The class is TwoToneRaisedDecoration. I admit freely that the name is wrong, it shouldn't have the word "Raised" in it, and we'll fix that in 2005.
Basically, this is a border, of any width for each side, which can have two colors one each for the inner and outer lines. The default line width for each side is 2 pixels. Creating a border is a one step, two part thing. We send #on: to the border class, and send in an instantiated border artist for it to use. The border artist we'll use is TwoTonedRaisedDecorationArtist. The artists job in a border / border artist pair, is to supply the colors for line drawing, and to call back (a bit of double dispatch) to the decoration to do the individual drawing of lines by whatever means it decides is right. In turn, the border artist may (and in our case does) call back into the pane's artist that is hosting the decoration to get colors.
Before we go ahead and create our initialize method we should comment on one Pollockism. We could just write our initialize method with a direct setting using the classes hard coded in the initialize method. That is pretty much a no-no in Pollock. So, we'll create a private #getInteriorDecoration method, which will return our interior decoration object, all created and ready to go, and then assign it:
Calendar>>getInteriorDecoration
^TwoToneRaisedDecoration on: TwoTonedRaisedDecorationArtist new
-+-+-
Calendar>>initialize
super initialize.
self interiorDecoration: self getInteriorDecoration
Now if we run our test, everything is fine, and we get a green bar. But if we open our window with the Calendar, we still see nothing:
tester := CalendarTest new openWindowWithCalendar
Our next step is to display the interior decoration. This is pretty simple. In our #displayOn: method in the artist, we tell the interior decoration (if there is one) to display itself. Decorations take a graphics context and a rectangle as their parameters. In our case, the rectangle we want to display it within is the visible rectangle of the pane:
Now we run our test... and all is well.. but... if we retry to open our window, nothing shows! Well, #hasInteriorDecoraton in AbstractArtist answers false by default. So we need to override it in our artist:
Now, we run our test again, and everything is ok, and when we open our example, it shows a nice little lowered border, just like Poppa Gates bakes in his billion dollar oven. We'll finish this round with an "Ounce Of Prevention" tests for the class of the decoration and the artist... this way we can see if we accidentally screw something up in our later work... Note, the four tests we created today cover the behavior of the initialize method, the getInteriorDecoration method, the setters and getters in the Calendar and CalendarArtist as well as if we got the correct decoration and decoration artist:
CalendarTest>>testInteriorDecorationClass
self openWindowWithCalendar.
self should: [calendar interiorDecoration class == TwoToneRaisedDecoration]
-+-+-
CalendarTest>>testInteriorDecorationArtistClass
self openWindowWithCalendar.
self should: [calendar interiorDecoration artist class == TwoTonedRaisedDecorationArtist]
Now if we run all of our CalendarTest cases, we get a full green bar, and 16 tests.
The above is published as version 1.5 in the Package named "Pollock-Calendar" on the Cincom public repository.
Next time, we'll create the "action part", which is the button which will eventually show the calendar navigation and selection pane.