The Artima Developer Community
Sponsored Link

Agile Buzz Forum
How To Create A Custom Widget - Drop Down - Calendar Grid

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
James Robertson

Posts: 29924
Nickname: jarober61
Registered: Jun, 2003

David Buck, Smalltalker at large
How To Create A Custom Widget - Drop Down - Calendar Grid Posted: Nov 3, 2004 3:05 PM
Reply to this message Reply

This post originated from an RSS feed registered with Agile Buzz by James Robertson.
Original Post: How To Create A Custom Widget - Drop Down - Calendar Grid
Feed Title: Pollock
Feed URL: http://www.cincomsmalltalk.com/rssBlog/pollock-rss.xml
Feed Description: Pollock - the next VW GUI
Latest Agile Buzz Posts
Latest Agile Buzz Posts by James Robertson
Latest Posts From Pollock

Advertisement

Today we create the Calendar grid.


Grid Design

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:

	CalendarAgent>>addCalendarGridTo: aForm 
		| grid |
		grid := Grid new.
		grid frame: (FractionalFrame fractionLeft: 0 right: 1 top: 0 bottom: 1).
		grid frame
			leftOffset: 5;
			rightOffset: -5;
			bottomOffset: -5;
			topOffset: 25.
		aForm addComponent: grid

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 tell our grid to use this as it's model:

	CalendarAgent>>addCalendarGridTo: aForm 
		| grid |
		grid := Grid new.
		grid frame: (FractionalFrame fractionLeft: 0 right: 1 top: 0 bottom: 1).
		grid frame
			leftOffset: 5;
			rightOffset: -5;
			bottomOffset: -5;
			topOffset: 25.
		grid list: self currentMonthModel.
		aForm addComponent: grid

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:

	CalendarAgent>>addCalendarGridTo: aForm 
		| grid |
		grid := Grid new.
		grid frame: (FractionalFrame fractionLeft: 0 right: 1 top: 0 bottom: 1).
		grid frame
			leftOffset: 5;
			rightOffset: -5;
			bottomOffset: -5;
			topOffset: 25.
		grid list: self currentMonthModel.
		self addColumnsTo: grid.
		aForm addComponent: grid

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:

	CalendarAgent>>addColumnsTo: aGrid 
		aGrid addColumn: ((Column new)
			accessPath: #(1);
			width: 20;
			labelString: 'S').
		aGrid addColumn: ((Column new)
			accessPath: #(2);
			width: 20;
			labelString: 'M').
		aGrid addColumn: ((Column new)
			accessPath: #(3);
			width: 20;
			labelString: 'T').
		aGrid addColumn: ((Column new)
			accessPath: #(4);
			width: 20;
			labelString: 'W').
		aGrid addColumn: ((Column new)
			accessPath: #(5);
			width: 20;
			labelString: 'T').
		aGrid addColumn: ((Column new)
			accessPath: #(6);
			width: 20;
			labelString: 'F').
		aGrid addColumn: ((Column new)
			accessPath: #(7);
			width: 20;
			labelString: 'S')

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:

	CalendarAgent>>addCalendarGridTo: aForm 
		| grid |
		grid := Grid new.
		grid frame: (FractionalFrame fractionLeft: 0 right: 1 top: 0 bottom: 1).
		grid frame
			leftOffset: 5;
			rightOffset: -5;
			bottomOffset: -5;
			topOffset: 25.
		grid list: self currentMonthModel.
		self addColumnsTo: grid.
		grid selectByCells.
		grid 
			columnHeader: true;
			allowColumnResizing: false;
			allowColumnReordering: false;
			allowRowResizing: false;
			allowSorting: false.
		aForm addComponent: grid

Visual Details

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.

Without further comment, here are those changes:

	CalendarAgent>>addColumnsTo: aGrid 
		aGrid addColumn: (self defaultColumn labelString: 'S'; accessPath: #(1)).
		aGrid addColumn: (self defaultColumn labelString: 'M'; accessPath: #(2)).
		aGrid addColumn: (self defaultColumn labelString: 'T'; accessPath: #(3)).
		aGrid addColumn: (self defaultColumn labelString: 'W'; accessPath: #(4)).
		aGrid addColumn: (self defaultColumn labelString: 'T'; accessPath: #(5)).
		aGrid addColumn: (self defaultColumn labelString: 'F'; accessPath: #(6)).
		aGrid addColumn: (self defaultColumn labelString: 'S'; accessPath: #(7); displayColumnLine: false)

-+-+-

	CalendarAgent>>defaultColumn
		^(Column new)
			width: 20;
			defaultHeight: 20;
			headerJustification: #centered;
			displayJustification: #centered

-+-+-

	CalendarAgent>>addCalendarGridTo: aForm 
		| grid |
		grid := Grid new.
		grid frame: (FractionalFrame fractionLeft: 0 right: 1 top: 0 bottom: 1).
		grid frame
			leftOffset: 5;
			rightOffset: -5;
			bottomOffset: -5;
			topOffset: 25.
		grid list: self currentMonthModel.
		self addColumnsTo: grid.
		grid selectByCells.
		grid 
			columnHeader: true;
			allowColumnResizing: false;
			allowColumnReordering: false;
			allowRowResizing: false;
			allowSorting: false;
			displayExtendedRowLines: true.
		aForm addComponent: grid

The above is published as version 1.13 in the Package named "Pollock-Calendar" on the Cincom public repository.

Next time we'll populate our model with actual dates


And So It Goes
Sames

Read: How To Create A Custom Widget - Drop Down - Calendar Grid

Topic: Frustrating connectivity Previous Topic   Next Topic Topic: Is it just me?

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use