Esiste una tecnica equivalente in Cocoa per il TrackPopupMenu sincrono in Windows?
-
03-07-2019 - |
Domanda
In risposta a un evento rightMouse, desidero chiamare una funzione che visualizza un menu di scelta rapida, lo esegue e risponde alla voce di menu selezionata. In Windows posso usare TrackPopupMenu con il flag TPM_RETURNCMD.
Qual è il modo più semplice per implementarlo in Cocoa? Sembra NSMenu: popUpContextMenu vuole pubblicare un evento nel NSView specificato. Devo creare una vista fittizia e attendere l'evento prima di tornare? In tal caso, come posso "attendere" o scaricare eventi dato che non torno al mio principale?
Soluzione 2
Sembra che popUpContextMenu sia già sincrono. Dato che non vedevo un modo per usare NSMenu senza che fosse necessario inviare una notifica a un NSView, mi è venuto in mente uno schema che crea un'istanza di un NSView temporaneo. L'obiettivo è visualizzare un menu popup e restituire l'elemento selezionato nel contesto di una singola chiamata di funzione. Di seguito sono riportati frammenti di codice della mia soluzione proposta:
// Dummy View class used to receive Menu Events
@interface DVFBaseView : NSView
{
NSMenuItem* nsMenuItem;
}
- (void) OnMenuSelection:(id)sender;
- (NSMenuItem*)MenuItem;
@end
@implementation DVFBaseView
- (NSMenuItem*)MenuItem
{
return nsMenuItem;
}
- (void)OnMenuSelection:(id)sender
{
nsMenuItem = sender;
}
@end
// Calling Code (in response to rightMouseDown event in my main NSView
void HandleRButtonDown (NSPoint pt)
{
NSRect graphicsRect; // contains an origin, width, height
graphicsRect = NSMakeRect(200, 200, 50, 100);
//-----------------------------
// Create Menu and Dummy View
//-----------------------------
nsMenu = [[[NSMenu alloc] initWithTitle:@"Contextual Menu"] autorelease];
nsView = [[[DVFBaseView alloc] initWithFrame:graphicsRect] autorelease];
NSMenuItem* item = [nsMenu addItemWithTitle:@"Menu Item# 1" action:@selector(OnMenuSelection:) keyEquivalent:@""];
[item setTag:ID_FIRST];
item = [nsMenu addItemWithTitle:@"Menu Item #2" action:@selector(OnMenuSelection:) keyEquivalent:@""];
[item setTag:ID_SECOND];
//---------------------------------------------------------------------------------------------
// Providing a valid windowNumber is key in getting the Menu to display in the proper location
//---------------------------------------------------------------------------------------------
int windowNumber = [(NSWindow*)myWindow windowNumber];
NSRect frame = [(NSWindow*)myWindow frame];
NSPoint wp = {pt.x, frame.size.height - pt.y}; // Origin in lower left
NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined
location:wp
modifierFlags:NSApplicationDefined
timestamp: (NSTimeInterval) 0
windowNumber: windowNumber
context: [NSGraphicsContext currentContext]
subtype:0
data1: 0
data2: 0];
[NSMenu popUpContextMenu:nsMenu withEvent:event forView:nsView];
NSMenuItem* MenuItem = [nsView MenuItem];
switch ([MenuItem tag])
{
case ID_FIRST: HandleFirstCommand(); break;
case ID_SECOND: HandleSecondCommand(); break;
}
}
Altri suggerimenti
Il modo "corretto" per eseguire questa operazione in Cocoa consiste nel fare in modo che l'obiettivo e l'azione della voce di menu eseguano il metodo richiesto. Tuttavia, se è necessario farlo durante la chiamata iniziale, è possibile utilizzare [NSView nextEventMatchingMask:]
per recuperare continuamente nuovi eventi che ti interessano, gestirli e eseguire il loop. Ecco un esempio che attende solo il rilascio del pulsante destro del mouse. Probabilmente vorrai utilizzare un argomento maschera più complesso e chiamare continuamente [NSView nextEventMatchingMask:]
fino a ottenere ciò che desideri.
NSEvent *localEvent = [[self window] nextEventMatchingMask: NSRightMouseUpMask];
Penso che troverai il modo "corretto" per andare molto più facilmente.
Non esiste un equivalente diretto, tranne in Carbon, che è deprecato.
Per rilevare il clic con il tasto destro, seguire queste istruzioni . Assicurano che rileverai correttamente i clic con il tasto destro e quelli a destra e visualizzerai il menu quando dovresti e non visualizzarlo quando non dovresti.
Per i seguenti eventi, puoi provare [[NSRunLoop currentRunLoop] runMode: NSEventTrackingRunLoopMode fino alla data: [NSDate distanceFuture]]
. Dovrai chiamarlo ripetutamente fino a quando l'utente non avrà scelto una delle voci di menu.
L'uso di nextEventMatchingMask: NSRightMouseUpMask
non funzionerà in tutti o persino nella maggior parte dei casi. Se l'utente fa clic con il pulsante destro del mouse su sul controllo, il pulsante destro del mouse salirà immediatamente dopo la discesa, senza selezionare una voce di menu e la selezione delle voci di menu probabilmente (sebbene non necessariamente) avverrà attraverso il tasto sinistro del mouse. Meglio semplicemente eseguire il ciclo di esecuzione ripetutamente fino a quando l'utente non seleziona qualcosa o chiude il menu.
Non so come dire che l'utente ha chiuso il menu senza selezionare nulla.