Today we start the task of displaying our Calendar.
We'll start off with a test, and then go from there. First we'll add a couple of convenience methods to our test class, making it easier to write tests for this widget:
You see we're now using the window and calendar instance variables. For now, we'll use the default OriginExtentFrame, and just give it an extent.
A thing to note here is that if we write a bunch of tests with #openWindowWithCalendar, those windows will remain open when the test is over... that's not good. So, we'll write a #tearDown method to automatically clean up after ourselves:
We call super because the PollockTestCase has a extras we always want to use for setUp and tearDowns. With that done, we're ready to write our main test:
Now, let's run it! ... OOPS! It got us a red bar! That's not good. If we press the Debug button (or the "List Defects" button and select our failed test and press OK), we see we got a "My subclass should have overridden one of my messages." error. Looking one down the stack, we see the issue is #displayOn: related to CalendarArtist. Yes indeed. That's how it works, all Artists have to display their own #displayOn: method.
There is an E-Z fix for that... we'll add a #displayOn: method in CalendarArtist, with nothing in it:
displayOn: aGraphicsContext
Now lets run our test! ... GREAT! Green Bar! Run 'em all so we make sure nothing else is broken too.... 12 run, no failures, no errors.
But.... Since we didn't write any code in our #displayOn: method, nothing shows... Because our test opens and closes the window so fast, we can hardly see if it is or isn't actually displaying anything. So, we'll go to a workspace and write this:
tester := CalendarTest new openWindowWithCalendar
Now, we can do a Do-It on that, and see, indeed, the window opens, but nothing is displayed. That's ok though... We'll get to actually displaying stuff next time.
Before we finish for today, we'll add some of the boilerplate code that pretty much all displayOn: methods use:
Lets go over this. All widgets know if they are visible or not, and of course, we should never display anything if the widget isn't.
Next, we get the clippingBounds of the graphics context. The Pollock framework always sets the clipping bounds to be the smallest possible rectangle, so that we can do the next test... checking if this widget's visible rectangle intersects the place where displaying will be done. If not, we abort early. Why do we do this? Well, many times the area of the window that needs to be repainted is not anywhere within the visible rectangle of our pane. While it is totally safe to paint to a graphics context outside of the area of it's clipping bounds, it's just a waste of code.
The next thing we do is set the clipping rectangle for the graphics context so that it is just that area where the existing clipping rectangle intersects with the visible rectangle. Once we do this, we are ready to display things that are inside our pane.
However, we see that it then asks if the panes displayable rectangle intersects the old clipping rectangle, and then branches if true. Why is this? Well, as discussed elsewhere, a pane has two general areas that we care about. The visible rectangle represents the whole visible area that the pane takes on the window. The displayable rectangle represents that sub-rectangle (if it has one) that is inside any borders or decorations that our widget will have. In our case, as with Windows 98/ME/2K drop down lists, there IS an interior decoration. This is the "lowered" border that surrounds the input field part and the button part of the Calendar pane.
So, when it comes time to draw this interior decoration, we'll do it before the ifTrue test. And we'll then draw the input field and button inside the ifTrue block. We see that what it first does in that ifTrue block is set the clipping rectangle to this new area. Of course, the sub-panes will do their displaying after that.
Finally, we reset the clipping rectangle to the initial value.
So, let's run our test.... Darn... Another problem! Another subclass responsibility! If we look down the stack, we see that the default #frameVisibleRectangle method in ActionDisplayArtist calls #collectedExtents. This method is used by RadioButtons and CheckBoxes to gather up the area of the check/radio graphic plus the extent of any label part. Fact is though, we don't need to use this style of #frameVisibleRectangle for our Calendar. Artists are typically supposed to be responsible for getting this value, but for an ActionDisplay widget, it turns around and gets it from the Agent. If we look at other implementors of #frameVisibleRectangle, and in particular in DropDownListAgent, we see it is implemented as: