2009-08-21 19 views
18

Come si aggiunge la possibilità di fare clic con il tasto destro su una riga in un NSOutlineView in modo da poter eliminare un oggetto o qualche altra attività. (es. come quando fai clic destro su una cartella nell'app Apple Mail)Come si aggiunge il menu sensibile al contesto a NSOutlineView (ad es. Menu di scelta rapida)

Penso di essere a metà strada, ho una sottoclasse di NSOutlineView che mi permette di catturare il tasto destro del mouse e visualizzare un menu contestuale basato sul riga selezionata anziché la riga su cui il mouse fa clic.

@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 

Scusate se la risposta è ovvia, non posso trovare alcun aiuto su questo in linea o nella documentazione.

Grazie revocatorie per la risposta, che mi portano a utilizzare questo:

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

Senza eseguirlo, sembra che dovrebbe funzionare bene. Non è così? In caso contrario, quale problema stai riscontrando? –

+4

Inoltre, non utilizzare il prefisso NS nelle proprie classi. Se Apple aggiunge una classe NSContextOutlineView a una versione futura di Cocoa, la loro classe e la tua si scontreranno e probabilmente l'app non verrà eseguita. –

+1

Penso che il problema con il codice postato sia che utilizzerà la riga selezionata e non la riga su cui è stato eseguito il tasto destro/ctrl-clic. Potrebbe essere o non essere la riga selezionata. – VoidPointer

risposta

21

nel metodo menuForEvent si può scoprire quale riga il clic si è verificato il. Si può passare che come parametro al metodo didefaultMenu - forse chiamarla defaultMenuForRow:

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

Ora è possibile costruire il menu per la riga che hai trovato nel caso in ...

-(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;   
} 

Inoltre, come già menzionato nei commenti, non dovresti usare il prefisso NS sulle tue classi. C'è un potenziale di uno scontro in futuro più esso sarà confondere tutti che sta guardando il codice - incluso te stesso :)

Spero che questo aiuti ...

+0

Grazie mille! Così tante di queste cose sono così semplici da fare, ma solo una volta che sai come! – Jacob

+0

Triste abbiamo bisogno di sottoclasse 'NSOutlineView' per raggiungere questo obiettivo. Questa funzionalità dovrebbe essere già inclusa nel protocollo dei delegati :-) –

-1

Se si preferisce, è possibile collegare il menu per la vista singola cellula o vista fila e costruire con la creazione di interfacce:

@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

Ecco un esempio 2.0 Swift che utilizza una sottoclasse e si estende il default NSOutlineDelegate in modo da poter definire i menu nel delegato.

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!) 
    } 

} 
Problemi correlati