Existe uma técnica equivalente em cacau para o trackpopupmenu síncrono no Windows?
-
03-07-2019 - |
Pergunta
Em resposta a um evento RightMouse, quero chamar uma função que exibe um menu de contexto, o executa e responde ao item de menu selecionado. No Windows, posso usar o trackpopupMenu com o sinalizador TPM_RETURNNCMD.
Qual é a maneira mais fácil de implementar isso no cacau? Parece que NSMENU: PopupContextMenu quer postar um evento no NSView especificado. Devo criar uma visão fictícia e esperar o evento antes de retornar? Em caso afirmativo, como faço para "esperar" ou liberar eventos, já que não estou voltando ao meu principal?
Solução 2
Parece que o PopUpContextMenu já é síncrono. Como não vi uma maneira de usar o NSMENU sem que ele envie uma notificação para uma NSView, criei um esquema que instancia uma NSView temporária. O objetivo é exibir um menu pop -up e retornar o item selecionado no contexto de uma única chamada de função. A seguir, os trechos de código da minha solução 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;
}
}
Outras dicas
A maneira 'adequada' de fazer isso no cacau é fazer com que o alvo e a ação do item de menu executem o método necessário. No entanto, se você precisar fazer isso dentro da sua chamada inicial, poderá usar [NSView nextEventMatchingMask:]
Para buscar continuamente novos eventos que lhe interessam, lidam com eles e loop. Aqui está um exemplo que apenas espera até que o botão certo do mouse seja lançado. Você provavelmente vai querer usar um argumento de máscara mais complexo e ligar continuamente [NSView nextEventMatchingMask:]
até você conseguir o que deseja.
NSEvent *localEvent = [[self window] nextEventMatchingMask: NSRightMouseUpMask];
Eu acho que você encontrará o caminho 'adequado' para ir muito mais fácil.
Não há equivalente direto, exceto em carbono, que é preterido.
Para detectar o clique com o botão direito, siga estas instruções. Eles garantem que você detecte corretamente o botão direito do mouse e os seguintes e exibirá o menu quando deve e não exibi-lo quando não deve.
Para os seguintes eventos, você pode tentar [[NSRunLoop currentRunLoop] runMode:NSEventTrackingRunLoopMode untilDate:[NSDate distantFuture]]
. Você precisará ligar para isso repetidamente até que o usuário tenha escolhido um dos itens do menu.
Usando nextEventMatchingMask:NSRightMouseUpMask
Não funcionará no total, ou mesmo na maioria dos casos. Se o usuário corretamente-cliques No seu controle, o botão direito do mouse aumentará imediatamente após o descendo, sem selecionar um item de menu, e a seleção do item de menu provavelmente (embora não necessariamente) acontecerá através do botão esquerdo do mouse. Melhor apenas executar o loop de corrida repetidamente até que o usuário selecione algo ou descarte o menu.
Não sei como dizer que o usuário descartou o menu sem selecionar nada.