2009-08-21 26 views
18

Cómo agregar la capacidad de hacer clic derecho en una fila en un NSOutlineView para poder decir eliminar un objeto o alguna otra actividad. (es decir, como cuando hace clic derecho en una carpeta en la aplicación Apple Mail)Cómo agregar menú contextual al menú contextual de NSOutlineView (

Creo que estoy a mitad de camino, tengo una subclase de NSOutlineView que me permite hacer clic derecho y mostrar un menú contextual basado en el fila seleccionada en lugar de la fila en la que está haciendo clic el mouse.

@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. No puedo encontrar ayuda sobre esto en línea o en la documentación.

Gracias al 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]; 
} 
+0

Sin ejecutarlo, parece que debería funcionar bien. ¿No es así? Si no, ¿qué problema tienes con eso? –

+4

Además, no use el prefijo NS en sus propias clases. Si Apple agrega una clase NSContextOutlineView a una versión futura de Cocoa, su clase y la suya colisionarán, y probablemente su aplicación no se ejecutará. –

+1

Creo que el problema con el código publicado es que usará la Fila seleccionada y no la fila en la que se realizó el clic derecho/ctrl. Esa puede ser o no la fila seleccionada. – VoidPointer

Respuesta

21

En el método de menuForEvent puede averiguar qué fila se produjo el clic en. Puede pasar que como parámetro para el métododefaultMenu - tal vez llamarlo defaultMenuForRow:

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

Ahora 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 mencionó en los comentarios, realmente no debería usar el prefijo NS 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 ...

+0

¡Muchas gracias! ¡Muchas de estas cosas son tan simples de hacer, pero solo una vez sabes cómo! – Jacob

+0

Triste, necesitamos subclase 'NSOutlineView' para lograr esto. Esta funcionalidad debería incluirse en el protocolo delegado ya :-) –

-1

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

@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 
8

Aquí es un ejemplo Swift 2.0 que utiliza una subclase y se extiende por defecto NSOutlineDelegate 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!) 
    } 

} 
Cuestiones relacionadas