Windows の同期 TrackPopupMenu に相当するテクニックが Cocoa にありますか?
-
03-07-2019 - |
質問
rightMouse イベントに応答して、コンテキスト メニューを表示し、実行し、選択されたメニュー項目に応答する関数を呼び出したいと考えています。Windows では、TPM_RETURNCMD フラグを指定して TrackPopupMenu を使用できます。
これを Cocoa で実装する最も簡単な方法は何ですか?NSMenu:popUpContextMenu は指定された NSView にイベントを投稿したいようです。ダミー ビューを作成し、イベントを待ってから戻る必要がありますか?その場合、メインに戻らない場合、イベントを「待機」またはフラッシュするにはどうすればよいですか?
解決 2
popUpContextMenuはすでに同期しているようです。 NSViewに通知を送信せずにNSMenuを使用する方法がなかったため、一時的なNSViewをインスタンス化するスキームを思いつきました。目標は、ポップアップメニューを表示し、単一の関数呼び出しのコンテキストで選択したアイテムを返すことです。以下は、提案するソリューションのコードスニペットです。
// 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;
}
}
他のヒント
Cocoaでこれを行う「適切な」方法は、メニュー項目のターゲットとアクションに必要なメソッドを実行させることです。ただし、最初の呼び出しで行う必要がある場合は、 [NSView nextEventMatchingMask:]
を使用して、関心のある新しいイベントを継続的にフェッチし、処理し、ループすることができます。右マウスボタンが放されるまで待機する例を次に示します。おそらく、より複雑なマスク引数を使用し、必要なものが得られるまで [NSView nextEventMatchingMask:]
を継続的に呼び出します。
NSEvent *localEvent = [[self window] nextEventMatchingMask: NSRightMouseUpMask];
「適切な」方法をはるかに簡単に見つけることができると思います。
非推奨となった Carbon を除いて、直接同等のものはありません。
右クリックを検出するには、次のようにします。 これらの指示. 。これらにより、右クリックと右押しを適切に検出し、必要な場合にはメニューを表示し、表示すべきではない場合にはメニューを表示しないことが保証されます。
次のイベントについては、試してみてください [[NSRunLoop currentRunLoop] runMode:NSEventTrackingRunLoopMode untilDate:[NSDate distantFuture]]
. 。ユーザーがメニュー項目の 1 つを選択するまで、これを繰り返し呼び出す必要があります。
使用する nextEventMatchingMask:NSRightMouseUpMask
すべての場合、またはほとんどの場合でさえ機能しません。ユーザーが正しい場合、クリック数 コントロール上では、メニュー項目を選択せずに、マウスの右ボタンが下降した直後に上昇し、メニュー項目の選択はおそらく (必ずではありませんが) マウスの左ボタンによって行われます。ユーザーが何かを選択するかメニューを閉じるまで、実行ループを繰り返し実行する方がよいでしょう。
ユーザーが何も選択せずにメニューを閉じたことを確認する方法がわかりません。