Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSPathControl with popups for each component of the path?

From Apple's sample code and reading the docs I can see no way configuring the NSPathControl to behave similarly to e.g. the 'jump bar' in the Xcode Editor window:

Xcode Editor custom path control

I.e. have it represent a path (or other kind of hierarchy) and make each component of the path a clickable popup to navigate the hierarchy..?

Anybody having luck faking such behaviour using a NSPathControlDelegate listening to clicks and showing a menu in a temporary window?

Seems like a common design where one would even expect some OSS implementation - but no such luck yet googling for it..

like image 214
Jay Avatar asked Oct 03 '12 12:10

Jay


2 Answers

I made a subclass of NSPathControl so that I can use mouseDown: to popup the context menus of the component cells at the right position. I added also a delegate to the menu to create deeper menus on demand.

- (void)mouseDown:(NSEvent *)event {

    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];


    NSPathCell *cell = self.cell;
    NSPathComponentCell *componentCell = [cell pathComponentCellAtPoint:point
                                                              withFrame:self.bounds
                                                                 inView:self];

    NSRect componentRect = [cell rectOfPathComponentCell:componentCell
                                               withFrame:self.bounds
                                                  inView:self];

    NSMenu *menu = [componentCell menuForEvent:event
                                        inRect:componentRect
                                        ofView:self];

    if (menu.numberOfItems > 0) {
        NSUInteger selectedMenuItemIndex = 0;
        for (NSUInteger menuItemIndex = 0; menuItemIndex < menu.numberOfItems; menuItemIndex++) {
            if ([[menu itemAtIndex:menuItemIndex] state] == NSOnState) {
                selectedMenuItemIndex = menuItemIndex;
                break;
            }
        }

        NSMenuItem *selectedMenuItem = [menu itemAtIndex:selectedMenuItemIndex];
        [menu popUpMenuPositioningItem:selectedMenuItem
                            atLocation:NSMakePoint(NSMinX(componentRect) - 17, NSMinY(componentRect) + 2)
                                inView:self];
    }
}

- (NSMenu *)menuForEvent:(NSEvent *)event {
    if (event.type != NSLeftMouseDown) {
        return nil;
    }
    return [super menuForEvent:event];
}
like image 69
Stephan Michels Avatar answered Nov 09 '22 14:11

Stephan Michels


I extended Stephan's answer slightly to accommodate for lazily loading the menu items. I created a small protocol to call for the menu rather than having to build the menu's ahead of time for each cell:

NSPathControlExtended.h

@protocol NSPathControlExtendedDelegate <NSPathControlDelegate>

@required
- (NSMenu *)pathControl:(NSPathControl *)pathControl menuForCell:(NSPathComponentCell *)cell;

@end

@interface NSPathControlExtended : NSPathControl

@property (weak) id <NSPathControlExtendedDelegate> delegate;

@end

NSPathControlExtended.m

#import "NSPathControlExtended.h"

@implementation NSPathControlExtended

@synthesize delegate;

- (instancetype)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code here.
    }
    return self;
}

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    // Drawing code here.
}

- (void)mouseDown:(NSEvent *)event {

    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];


    NSPathCell *cell = self.cell;
    NSPathComponentCell *componentCell = [cell pathComponentCellAtPoint:point
                                                              withFrame:self.bounds
                                                                 inView:self];

    NSRect componentRect = [cell rectOfPathComponentCell:componentCell
                                               withFrame:self.bounds
                                                  inView:self];

    NSMenu *menu = [delegate pathControl:self menuForCell:componentCell];

    if (menu.numberOfItems > 0) {
        NSUInteger selectedMenuItemIndex = 0;
        for (NSUInteger menuItemIndex = 0; menuItemIndex < menu.numberOfItems; menuItemIndex++) {
            if ([[menu itemAtIndex:menuItemIndex] state] == NSOnState) {
                selectedMenuItemIndex = menuItemIndex;
                break;
            }
        }

        NSMenuItem *selectedMenuItem = [menu itemAtIndex:selectedMenuItemIndex];
        [menu popUpMenuPositioningItem:selectedMenuItem
                            atLocation:NSMakePoint(NSMinX(componentRect) - 17, NSMinY(componentRect) + 2)
                                inView:self];
    }
}

- (NSMenu *)menuForEvent:(NSEvent *)event {
    if (event.type != NSLeftMouseDown) {
        return nil;
    }
    return [super menuForEvent:event];
}

@end
like image 21
Stephen Donnell Avatar answered Nov 09 '22 15:11

Stephen Donnell