Existe-t-il une technique équivalente dans Cocoa pour le TrackPopupMenu synchrone sous Windows?

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

  •  03-07-2019
  •  | 
  •  

Question

En réponse à un événement rightMouse, je souhaite appeler une fonction qui affiche un menu contextuel, l'exécute et répond à l'élément de menu sélectionné. Sous Windows, je peux utiliser TrackPopupMenu avec l'indicateur TPM_RETURNCMD.

Quel est le moyen le plus simple de mettre en œuvre ceci dans Cocoa? Il semble que NSMenu: popUpContextMenu veuille publier un événement dans le NSView spécifié. Dois-je créer une vue fictive et attendre l'événement avant de revenir? Si oui, comment puis-je "attendre"? ou des événements flush étant donné que je ne retourne pas à mon principal?

Était-ce utile?

La solution 2

Il semble que popUpContextMenu soit déjà synchrone. Comme je ne voyais pas comment utiliser NSMenu sans l'avoir envoyé une notification à un NSView, je suis arrivé à un schéma qui instancie un NSView temporaire. L'objectif est d'afficher un menu contextuel et de renvoyer l'élément sélectionné dans le contexte d'un seul appel de fonction. Voici des extraits de code de la solution proposée:

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

Autres conseils

La meilleure façon de faire cela dans Cocoa est de faire en sorte que la cible et l’action de votre élément de menu exécutent la méthode requise. Toutefois, si vous devez le faire au cours de votre appel initial, vous pouvez utiliser [NSView nextEventMatchingMask:] pour extraire en continu les nouveaux événements qui vous intéressent, les gérer et les mettre en boucle. Voici un exemple qui attend que le bouton droit de la souris soit relâché. Vous voudrez probablement utiliser un argument de masque plus complexe et appeler continuellement [NSView nextEventMatchingMask:] jusqu'à ce que vous obteniez ce que vous voulez.

NSEvent *localEvent = [[self window] nextEventMatchingMask: NSRightMouseUpMask];

Je pense que vous trouverez la "bonne" façon de procéder beaucoup plus facile.

Il n'y a pas d'équivalent direct, sauf dans Carbon, qui est déconseillé.

Pour détecter le clic droit, suivez ces instructions . Ils garantissent que vous détecterez correctement les clics et les clics droits et que vous affichez le menu quand vous le souhaitez et que vous ne l’affichez pas lorsque vous ne devriez pas.

Pour les événements suivants, vous pouvez essayer [[NSRunLoop currentRunLoop] runMode: NSEventTrackingRunLoopMode jusqu'au Date: [NSDate lointainFuture]] . Vous devrez l'appeler plusieurs fois jusqu'à ce que l'utilisateur ait choisi l'un des éléments de menu.

L'utilisation de nextEventMatchingMask: NSRightMouseUpMask ne fonctionnera pas dans tous les cas, ni même dans la plupart des cas. Si l'utilisateur clique avec le bouton droit de la souris sur sur votre commande, le bouton droit de la souris montera immédiatement après sa chute, sans sélection d'élément de menu, et la sélection d'élément de menu se fera probablement (mais pas nécessairement) par le bouton gauche de la souris. Mieux vaut exécuter la boucle d’exécution de manière répétée jusqu’à ce que l’utilisateur sélectionne quelque chose ou ferme le menu.

Je ne sais pas comment dire que l'utilisateur a fermé le menu sans rien sélectionner.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top