I made a small mistake last time when I said the grid will be 5 x 7. October should have been the hint that it really needs to be 6 x 7. Any month with 31 days that starts on a Friday or Saturday will force the need for this extra row of values.
The first thing we need to decide is what kind of model object we should use for our Grid. There are two options: ObservedList or ObservedTwoDList.
When used in a Grid, an ObservedList is a collection of homogenous objects which have attributes that you can access for each column. If we use an ObservedList, we would want to create a collection of "week" objects, which answers something like "sundayDay", "mondayDay", etc.
If we use an ObservedTwoDList, each item in each cell can be a unique object, and the only requirement is that each item in a column answers to the same message.
My decision is to use an ObservedTwoDList. Each element in each row will be a date. All columns will be the same, each saying that the accessor is #dayOfMonth. All we have to do is build the collection. No matter which type of model I decided on, that is still the hardest part.
Calendar Grid
We start with a new method we'll name #addCalendarGridTo: and pass in the form:
And we add a call to this method to our calendarForm method:
CalendarAgent>>calendarForm
| form label |
form := Form new.
form frame: (FractionalFrame fractionLeft: 0 right: 1 top: 0 bottom: 1).
label := DisplayLabel string: self currentMonthYear.
label frame: AlignmentFrame topCentered.
form addComponent: label.
self addNavigationButtonsTo: form.
self addCalendarGridTo: form.
^form
Some notes: We start with a frame that is offset 5 from the left, right and bottom. We push the top down 25 to make sure it doesn't cover our navigation buttons.
Now we have to create a method to create our ObservedTwoDList with our values. We'll start with a simple ObservedTwoDList that just has a running set of numbers starting at 1:
CalendarAgent>>currentMonthModel
| model |
model := ObservedTwoDList columns: 7 rows: 6.
1 to: 42 do: [:index | model at: index put: index].
^model
We'll change the model to use actual dates next time. For now, we just want to see what's what.
Now we have to add the Column objects to display our values... Again, we'll create a separate method for that:
CalendarAgent>>addColumnsTo: aGrid
aGrid addColumn: (Column new accessPath: #(1); labelString: 'S').
aGrid addColumn: (Column new accessPath: #(2); labelString: 'M').
aGrid addColumn: (Column new accessPath: #(3); labelString: 'T').
aGrid addColumn: (Column new accessPath: #(4); labelString: 'W').
aGrid addColumn: (Column new accessPath: #(5); labelString: 'T').
aGrid addColumn: (Column new accessPath: #(6); labelString: 'F').
aGrid addColumn: (Column new accessPath: #(7); labelString: 'S')
A note here: By default, every column sends printString to it's row's value. By adding an accessPath, we tell it to first sent that message before sending the final default accessor, printString. We can use any combination of unary messages, integers or blocks to compose our accessPath, and those values are sent in turn to the result of the last value in the access path.
Now we call this method from our addCalendarGridTo: method:
If we now open our calendar test (CalendarTest new openWindowWithCalendar) and open the calendar popup, we see our grid in the popup, but.... We can only see 3 full columns and only a bit of the 4th. By default, all columns will display 50 wide. However, our window is 200 wide and our grid is 190 wide (5 from each side). So we do a bit of calculation and a bit of hand fudging, and we decide that 20 will be a good width. So, we modify the columns to be 20 wide:
We notice now when opening the popup, that all of the columns now show up. But.... The day of week header value doesn't show. Also, if we move our mouse over the column and row lines, the resize cursor turns on. There is no need to allow row or column resizing. If we try, we can also re-order the columns (drag a header from one column to another). If we click on a column, it sorts it too. So, we'll turn on the column header, and turn off the resizing, sorting and reordering.
Also we note, that we can only select by rows. So we'll turn on row selection too:
We're almost done, visually speaking, except for a few things.
The last column has a line to it's right because of our imperfect fit of the columns, so we'll turn off that line for that column and to make sure that the line doesn't stop at the right edge of the column we also tell the row lines to extend all the way to the right.
The rows don't fill the grid, there's blank space at the bottom. We'll change the default line height to fill the grid
While everything looks ok, it would be better if the numbers and the headers were centered in their cells. We'll tell them to be centered.
The columns are mostly the same, so we'll add a newColumn method, and call that to give us a default column we can manipulate.