Saturday, December 26, 2009

TimeLine view - rollovers

Each row of the TimeLine header view has two areas that are active. One is the drawMode and has a popup - the other represents the locked status of an item. These appear as graphics in the TimeLine header. I have decided to make these active areas distinct by making them behave as rollovers. The effect that I am looking for is quite simple - as the mouse goes over the active area I want to draw a grey oval. This will also provide an opportunity to draw a popup arrow for the popup. Adding the arrow so that it always draws makes the header look cluttered - only drawing this in the mouse-over state will make this a little cleaner.

The simplest way I found to do this is to use the NSTrackingArea. My aim was to add a NSTrackingArea for each visible active area.

To do this I added an updateTrackingAreas method to the view - in here I dispose of any existing tracking areas, and then to loop through all of the visible rows and for each to add a tracking area for each of the active areas on the row. When you construct a tracking area you pass in a NSDictionary of userInfo - here I pass in details of the row and the particular active area it represents. The updateTrackingAreas method is called when the header view scrolls or is resized - for the moment this is enough.

When you establish tracking areas on a view mouseEntered and mouseExited methods of your view will be called as the mouse enters and exits the view. here you need to get the relevant areas to redraw - I affected a method setNeedsDisplayInRow that is little more that a sprinkle of sugar for setNeedsDisplayInRect.

My guess is that the procedure for getting a rollover to work for a cell in in an NSTableView would be pretty much the same - though you might need to updateTrackingAreas in more situations (for example when table rows are resized).

Currently the drawing is still handled by the header view - but I plan to move this into a NSCell subclass.
An active area with a rollover.

Friday, December 18, 2009

TimeLine view - making the header active

In the TimeLine view the header is not a passive information-only structure it is active.
  • The text is editable.
  • The "Lock" icon works as a checkbox.
  • The "Draw Mode" icon is a popup menu with four states.
This is what one of the popup menus looks in the windows version if Cello.


The way that I plan to accomplish these active areas are by using subclasses of NSCell and friends. There is, for example, a NSPopupButtonCell that handles a popup menu button.

NSCell and it's derivatives are classes that encompass everything that is needed to draw a particular object, say a popup menu, and handle it's behavior. It is everything except the view in which it is drawn. As with all things in Cocoa they are there for the taking - you can use them as is or subclass them as you please. The way you use them is really easy - if you want them to draw then you:
  • Set it up
  • Ask it to draw in a given rectangle in a given view
If you want to handle a mouse down then you:
  • Set it up
  • Ask it to handle the mouse down
All the Cocoa controls (for example NSButton) use a NSCell subclass to do the drawing etc. If you want to see how they work - check out the Cocoatron project. You can download the source and see just how simple the cocoatron NSButton is.

I have started by crudely hacking in the NSPopupButtonCell
into the TimeLine view and getting it to draw and to handle the mousedown - the menu is just a dummy. My plan is to refine this (stepwise refinement) until I get what I want.

Sunday, December 13, 2009

TimeLine view - header with icons

I have added the drawing of icons to the header row. I have taken the existing graphics from the PC version and quite quickly converted them to individual tiff images in photoshop. I am for the moment just reusing the PC images. When Cello is finished they will need reworking as they don't have that OSX feel. My thought is to hand this part of the project over to someone else - it really is something to worry about another day.

The drawing of icons is quite straight forward. I just figure out the point to draw the icon so it is centered in its rectangle and call compositeToPoint:operation and that is about it.
If you look closely you can see that there is a white bloom (square) around the icons - this is an actual transparency issue with the graphics (something wrong with my photoshop efforts).

Saturday, December 12, 2009

Cocoa and Drawing truncated text

To properly allow for long names of objects in the time-line I need to draw truncated text.
Untruncated Text

What I mean is that I want to truncate the text if the area is to small for the text and then to have some ellipses that show where the truncation occurs. This was second nature to me in my days as a ToolBox (Carbon) programmer but fathoming it out under Cocoa tool a while. Whatever I tried failed - and it seemed that the Apple docs failed me.

In a tight corner Google is a good friend and I found the blog-post from Preston Jackson. It is an example of the kind of blog-post I find most useful. A problem and a solution - with a code snippet. Thanks Preston.

I copy/pasted his code and it worked. Then I integrated it into the code that should have worked but didn't and it failed. It took (unbelievably) an hour to get to the bottom of the problem. In full numpty horror I realized that I was calculating the width of the text I was and changing my destination rectangle so it would be that width.

With the truncation the text looks like this:

TimeLine view - header regions

Working on the TimeLine view I have started getting the row headers to draw. I am stepwise refining the row header drawing until I have the row headers drawing correctly. The PC version of cello draws row headers like this:
So my start is to take the single rectangle for a row and to divide it into four parts. The text part will grow and shrink horizontally - the other parts are fixed (they will display icons). I did this by writing a small C++ class that just calculates the dimensions. I could have done this in C, or Cocoa but sometimes C++ is closest to hand - and it fell out very simply. I altered the drawing to reflect the calculated rectangles. It is a temporary debugging measure I just frame the rectangles. - so the evolving headers now look like this:

Friday, December 4, 2009

NSScrollView - problems

I have been having a strange problem scrolling in the TimeLine view. The issue is down to my understanding of coordinate spaces under "normal" (not flipped) circumstances in cocoa. The coordinate space is much as I expect - the bottom left corner is (0, 0). However when you come to NSScrollView things are a little strange. If the position of the content view also starts from the bottom left corner. This is 100% logical. What I did not count on was the effect when the content view is smaller than the NSScrollView. So, of your list, is smaller than the NSScrollView you can easily have this situation. What happens is that the bottom of the content view is placed as (0, 0) within the NSScrollView.The problem can be seen quite clearly if I expanded my evolving TimeLine so that the NSScrollView is larger than the content area. It looks as follows:

It took me a while to figure out the fix. MBTableView dealt with the problem forcing all the views to be flipped - so that (0, 0) is the top-left so it did not provide any kind of example. To get an example I downloaded the source code to CocoaTron. CocoaTron is an open source implementation of the cocoa frameworks. It is a cool project (don't let the web-site fool you it is actively developed) and the source code is there for you. You can find it here.

I pulled the CocoaTron source and had a look at their implementation of NSTableView. The solution is to grow the content view so that it is never smaller. Each time you resize the NSScrollView the NSTableView recalculates and repositions its self.

The solution:Add an override to resizeWithOldSuperviewSize that makes the content at least the size of it's superview. My resizeWithOldSuperviewSize would up looking like this:

-(void)resizeWithOldSuperviewSize:(NSSize)oldSize {

[super resizeWithOldSuperviewSize:oldSize];

[self reloadData];

}


My reloadData has also changed:

- (void)reloadData

{

.... stuff

NSRect contentRect = NSMakeRect(0, 0, _numberOfColumns * _columnWidth, _numberOfRows *_rowHeight);

contentRect.size.height = MAX(contentRect.size.height, [[self superview] bounds].size.height);

[contentView setFrameSize:contentRect.size];

.... stuff

}



Thursday, December 3, 2009

TimeLine - sketching out more

I am slowly shapping up the timeline. I am working with a scratch target in the main project and doing occasional Git commits. The commits are little more than line in the sand that I can undo to. My approach is to start simple and to refine slowly.

Basic Topology (header areas)

Starting with the basic ytopology I refined it by adding NSScrollViews.

Basic topology (with scrolling)

The final shot of this post shows some dummy content. The beginings of what will happen in the row headers is there. I have coppied the draw code of the rows into the main content view so that I can debug the drawing. I have some strange problems that I guess are related to the coordinate system that is prelevant in cocoa - which to me (cooming from QuickDraw coordinates) still feels a litte odd.


Some dummy drawing of row headers and rows