¿Existe una técnica equivalente en Cocoa para el TrackPopupMenu síncrono en Windows?

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

  •  03-07-2019
  •  | 
  •  

Pregunta

En respuesta a un evento de rightMouse, quiero llamar a una función que muestra un menú contextual, lo ejecuta y responde al elemento del menú seleccionado. En Windows puedo usar TrackPopupMenu con el indicador TPM_RETURNCMD.

¿Cuál es la forma más fácil de implementar esto en Cocoa? Parece que NSMenu: popUpContextMenu quiere publicar un evento en el NSView especificado. ¿Debo crear una vista ficticia y esperar el evento antes de volver? Si es así, ¿cómo puedo " esperar " ¿O los eventos de lavado dados no vuelvo a mi principal?

¿Fue útil?

Solución 2

Parece que popUpContextMenu ya está sincronizado. Como no vi una forma de usar NSMenu sin haber enviado una notificación a un NSView, se me ocurrió un esquema que crea una instancia de un NSView temporal. El objetivo es mostrar un menú emergente y devolver el elemento seleccionado en el contexto de una llamada de función única. A continuación se encuentran fragmentos de código de mi solución propuesta:

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

Otros consejos

La forma 'correcta' de hacer esto en Cocoa es hacer que el objetivo y la acción del elemento del menú realicen el método requerido. Sin embargo, si debe hacerlo dentro de su llamada inicial, puede usar [NSView nextEventMatchingMask:] para obtener continuamente nuevos eventos que le interesen, manejarlos y hacer un ciclo. Aquí hay un ejemplo que solo espera hasta que se suelta el botón derecho del mouse. Probablemente querrá usar un argumento de máscara más complejo y llamar continuamente [NSView nextEventMatchingMask:] hasta que obtenga lo que desea.

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

Creo que encontrarás la manera 'adecuada' de hacerlo mucho más fácil.

No hay un equivalente directo, excepto en Carbon, que está en desuso.

Para detectar el clic derecho, siga estas instrucciones . Se aseguran de que detectará correctamente los clics con el botón derecho y los derechos, y mostrará el menú cuando debería y no lo mostrará cuando no debería.

Para los siguientes eventos, puede probar [[NSRunLoop currentRunLoop] runMode: NSEventTrackingRunLoopMode untilDate: [NSDate distantFuture]] . Deberá llamar a esto varias veces hasta que el usuario haya elegido uno de los elementos del menú.

El uso de nextEventMatchingMask: NSRightMouseUpMask no funcionará en todos, ni en la mayoría de los casos. Si el usuario hace clic con el botón derecho sobre en su control, el botón derecho del mouse subirá inmediatamente después de que se caiga, sin seleccionar un elemento del menú, y la selección del elemento del menú probablemente (aunque no necesariamente) se realizará el botón izquierdo del ratón. Es mejor ejecutar el bucle de ejecución varias veces hasta que el usuario seleccione algo o salga del menú.

No sé cómo decir que el usuario ha abandonado el menú sin seleccionar nada.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top