¿Cómo agregar menú contextual para senstive NSOutlineView (es decir, menú del botón derecho)

StackOverflow https://stackoverflow.com/questions/1309602

Pregunta

¿Cómo agregar la capacidad de hacer clic derecho sobre una fila en un NSOutlineView por lo que se puede decir eliminar un objeto o alguna otra actividad. (Es decir, igual que cuando hace clic derecho sobre una carpeta en el Correo de aplicaciones de Apple)

Creo que estoy a mitad de camino, tengo una subclase de NSOutlineView que me permite coger el botón derecho del ratón y mostrar un menú contextual basado en la fila seleccionada en lugar de la fila el ratón es hacer clic en.

@implementation NSContextOutlineView

    - (NSMenu *)defaultMenu {
        if([self selectedRow] < 0) return nil;
        NSMenu *theMenu = [[[NSMenu alloc] initWithTitle:@"Model browser context menu"] autorelease];
        [theMenu insertItemWithTitle:@"Add package" action:@selector(addSite:) keyEquivalent:@"" atIndex:0];
        NSString* deleteItem = [NSString stringWithFormat: @"Remove '%i'", [self selectedRow]];
        [theMenu insertItemWithTitle: deleteItem action:@selector(removeSite:) keyEquivalent:@"" atIndex:1];
        return theMenu;
    }

    - (NSMenu *)menuForEvent:(NSEvent *)theEvent {
        return [self defaultMenu];  
    }
@end

Lo siento si la respuesta es obvia simplemente no puedo encontrar ninguna ayuda en esta línea o en la documentación.

Gracias a efecto por la respuesta, que me llevó a utilizar este:

- (NSMenu *)menuForEvent:(NSEvent *)theEvent {
    NSPoint pt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
    id item = [self itemAtRow: [self rowAtPoint:pt]];
    return [self defaultMenuFor: item];
}
¿Fue útil?

Solución

En el método de menuForEvent puede averiguar qué fila se produjo el clic en. Puede pasar que como parámetro a su defaultMenu método - tal vez lo llaman defaultMenuForRow:

-(NSMenu*)menuForEvent:(NSEvent*)evt 
{
    NSPoint pt = [self convertPoint:[evt locationInWindow] fromView:nil];
    int row=[self rowAtPoint:pt];
    return [self defaultMenuForRow:row];
}

Ahora se puede crear el menú para la fila que ha encontrado en el evento ...

-(NSMenu*)defaultMenuForRow:(int)row
{
    if (row < 0) return nil;

    NSMenu *theMenu = [[[NSMenu alloc] 
                                initWithTitle:@"Model browser context menu"] 
                                autorelease];
    [theMenu insertItemWithTitle:@"Add package" 
                          action:@selector(addSite:) 
                   keyEquivalent:@"" 
                         atIndex:0];
    [theMenu insertItemWithTitle:[NSString stringWithFormat:@"Remove '%i'", row] 
                          action:@selector(removeSite:) 
                   keyEquivalent:@"" 
                         atIndex:0];
    // you'll need to find a way of getting the information about the 
    // row that is to be removed to the removeSite method
    // assuming that an ivar 'contextRow' is used for this
    contextRow = row;

    return theMenu;        
}

Además, como ya se ha mencionado en los comentarios, que realmente no debería utilizar el NS-prefix en sus propias clases. Hay una posibilidad de un enfrentamiento en el futuro además de que va a confundir a todo el mundo que está mirando a su código - incluido usted mismo:)

Espero que esto ayude ...

Otros consejos

Este es un ejemplo Swift 2.0 que utiliza una subclase y se extiende la NSOutlineDelegate predeterminado para que pueda definir sus menús en el delegado.

protocol MenuOutlineViewDelegate : NSOutlineViewDelegate {
    func outlineView(outlineView: NSOutlineView, menuForItem item: AnyObject) -> NSMenu?
}

class MenuOutlineView: NSOutlineView {

    override func menuForEvent(event: NSEvent) -> NSMenu? {
        let point = self.convertPoint(event.locationInWindow, fromView: nil)
        let row = self.rowAtPoint(point)
        let item = self.itemAtRow(row)

        if (item == nil) {
            return nil
        }

        return (self.delegate() as! MenuOutlineViewDelegate).outlineView(self, menuForItem: item!)
    }

}

mucho más tarde que la cuestión OP, pero para otros como yo pregunto, aquí está mi solución. También es necesario crear subclases NSOutlineView, que no se animó por Apple doc, de todos modos ...

En lugar de anular menuForEvent: I anular rightMouseDown:

- (void)rightMouseDown:(NSEvent *)event {
    NSPoint pt = [self convertPoint:[event locationInWindow] fromView:nil];
    NSInteger row = [self rowAtPoint:pt];
    id item = [self itemAtRow:row];
    NSMenu *menu;
    //set the menu to one you have defined either in code or IB through outlets
    self.menu = menu;
    [super rightMouseDown:event];
}

Esto tiene la ventaja de mantener delegado llamadas para actualizar el menú a partir de entonces y también mantiene la fila delineando el botón derecho del ratón.

Si lo prefiere, puede adjuntar el menú a la vista célula individual o ver fila y construirlo con constructor de interfaces:

@implementation BSMotleyOutlineView

-(NSMenu *)menuForEvent:(NSEvent *)event
{
    NSPoint pt = [self convertPoint:[event locationInWindow] fromView:nil];
    NSInteger row = [self rowAtPoint:pt];
    if (row >= 0) {
        NSTableRowView* rowView = [self rowViewAtRow:row makeIfNecessary:NO];
        if (rowView) {
            NSInteger col = [self columnAtPoint:pt];
            if (col >= 0) {
                NSTableCellView* cellView = [rowView viewAtColumn:col];
                NSMenu* cellMenu = cellView.menu;
                if(cellMenu) {
                    return cellMenu;
                }
            }
            NSMenu* rowMenu = rowView.menu;
            if (rowMenu) {
                return rowMenu;
            }
        }
    }
    return [super menuForEvent:event];
}
@end
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top