Thursday, July 16, 2009

Bringing Some Sense to Contextual Menu Selections

Since the release of Leopard, there has been an "additional" selection rectangle that gets shown by default when you right click (or control-click) on a table. This second selection is in addition to the current selection. I've always found this behavior to be confusing. Not helping matters is that if you use Mail, or iTunes, this behavior isn't visible, but when you use the Finder, it is:

So what's right? Well, in my opinion, there is one major problem with the "focus ring" displayed when the contextual menu pops up. When you don't click on the selection: the original selection remains and the row you clicked on to display the menu gets a focus ring. What is the actual selection that will be used when a command is chosen? Well, it's hard to say. I think a valid argument could be made for either the original, or the new one.

Now, personally, I don't like the focus ring at all, and much prefer the way that iTunes and Mail handle this situation: no focus ring, and a click on something other than the selection changes the selection to the row clicked.

Changing this behavior in your app is easy enough, you just have to use your own NSTableView subclass and override the menuForEvent: method:

- (NSMenu *)menuForEvent:(NSEvent *)theEvent
    // This adds some sanity to the default NSTableView behavior,
    // which is just damned confusing: If the user clicks to display
    // the contextual menu, instead of having two visible selections,
    // one the original and one the blue outline, the current selection
    // is either outlined, or the selection is changed, the same way
    // that Mail does it.

    // Get the current selections for the table view.
    NSIndexSet *selectedRowIndexes = [self selectedRowIndexes];

    NSPoint mousePoint = [self convertPoint:[theEvent locationInWindow]
    int row = [self rowAtPoint:mousePoint];

    // Is the row that was just clicked on currently selected?
    if ([selectedRowIndexes containsIndex:row] == NO)
        // Change the selection to the row clicked on
        [self deselectAll:self];
        [self selectRow:row byExtendingSelection:NO];

    return [self menu];
    // To display the focus ring, return [super menuForEvent:theEvent];

If you like the default focus ring, then just return the result of the call to super instead of the menu.


Post a Comment

<< Home