Monday, November 1, 2010

Side Project

I started working in a calendar app. Right now you can think of it mainly as a prototype, but it's already got some interesting features. It's implemented in python, and while it relies on goocanvas at the moment, the calendar view is actually one big canvas item and could easily be ported to a stand-alone widget.



Interface

At first glance it looks like the week view used in most timelines. However, the view is an infinite scrolling area. There is no horizontal scroll bar, as this is a ranged widget. You move in time by clicking and dragging on the day heading. If you flick the heading, the calendar will continute to 'coast', slowing down gradually until it stops (or immediately if click on it).


I've found that I rarely look at the month view in a calendar application, so I sought to eliminate it. My calendars tend to have a lot of daily repeating events, making the month view seem cluttered. I mainly use it for skipping forward or backward to a specific week without loosing context, or to see a group of days that span a weekend.

With infinite scrolling I found I really don't need the month view. I don't loose context skipping around because of the smoothness of the animation. Moreover, it's no trouble to position the calendar so that Friday is in the middle of the window (the coloration on the heading serves as a reference). Expanding the window will reveal more days, not make the days larger. If you want to look two weeks ahead, you can -- even on an eee 901. I do plan to add a zoom feature, however.

Selections

Another feature I've added as a reaction against current calendars: When you click and drag on the calendar itself, it doesn't create a new event automatically. I've always found this to be annoying. When I make a selection, I am usually in some kind of tentative state of mind. Moreover, in thunderbird and evolution, it's possible to accidentally create events that are too small to be selected, forcing you to go into another view to delete them. Again, really annoying. I don't want the application taking some action based on my half-baked ideas. I much prefer it to wait until I'm sure of myself.

Undo / Redo

This applet is the first time I've implemented the Command Pattern in a program, and I'm curious to see how it pans out. The idea is that rather than employing low-level tricks to capture state for undo, you explicitly code the do and undo procedure for each action that your application supports. So far, New, delete, move, resize, and select can all be undone.

I think that selection is too often left out as an undo action. Doubly annoying because selections are ephemeral, disappearing the minute you focus on something else. Often I select things without thinking. I've put a lot of work into creating selections, only to have them evaporate due to an errant click.

On the implementation side, I distinguish between MenuCommands, from which menu and toolbar items can be created automatically, and MouseCommands (invoked repeatedly with new mouse coordinates before being committed to the UndoStack in their final state). Both types support a class-methods indicating whether they are currently available. This allows toolbar state to be managed, and for the controller to distinguish between one of several commands it might support.

One thing I found is that with this approach, the Controller becomes a general-purpose object which mostly creates and commits commands to an UndoStack in response to low-level events. The controller implements the logic for detecting drag, click, double-click detection. The commands implement more concrete actions, such as moving an event in the calendar based on given mouse coordinates. The Controller only needs to be specialized to add new input events. For example, flicking the calendar is currently implemented with a custom controller.

In the future I plan to make navigation undoable. I'm debating whether navigation commands should be posted to the main UndoStack, or into a separate stack that would be exposed as "Forward / Back".  I'll have to try and see.

Development Status

If I follow this out to its conclusion, it would need at least the following features before I'd call it "Done".

  • Ability to edit the names of events
    • tricky because the whole thing is done in raw cairo
  • Some system for handling repeating events
  • convenience navigation buttons, such as "Today", "Last {Week, Month, Year}", "Next {Week, Month, Year}" etc.
  • Alarm notification system, and ability to handle alarms
  • Integration with google calendar (probably using libgdata)
  • some cosmetic improvements
The implementation of the alarm notification system worries me most. I have only a vague idea of how I would implement that, and it would need to be absolutely reliable before I'd consider using this program myself. I do have some interesting ideas for how to create alarms and repeating events in the UI, however.

3 comments:

Anonymous said...

If you can get this thing to integrate with the Gnome calendar, I would be so happy because I could finally remove Evolution.

Xavi said...

And if you can integrate the calendar with pitivi, then we can have a gstreamer playout automation aplication! hehe

brandon lewis said...

I forgot to post a link to the code:

https://github.com/emdash/calendar