Cocoa右クリックメニュー
今回のお遊び実験
目的
- NSViewを継承しているクラスで、右クリックした時にポップアップメニューを表示する。
- NSMenuを少しいじくる
NSViewを継承しているクラスってたくさんあります。
というか、GUIに設置にするものはほとんど(といか全部?)がそうでしょう。
今回はあえてNSButtonでやってみたいと思います。
誰もそんなことしたいと思わないだろうと?
お遊びだからいいのです。
実は実装は簡単です。
+ (NSMenu *)defaultMenu;
をオーバライドすれば、完了です。
その他にも、
- (NSMenu*)menuForEvent:(NSEvent*)event;
をオーバーライドする方法があります。
後者はクリック情報を内部処理で使いたい場合に使います。
例えば、指定した範囲ないでのみポップアップメニューを表示するとか。
@interface PJPopUpMenuButton : NSButton @end @implementation PJPopUpMenuButton + (NSMenu*)defaultMenu { NSMenu * menu = [[[NSMenu alloc] initWithTitle:@"PJPopUpMenu"] autorelease]; [menu addItemWithTitle:@"menu1" action:nil keyEquivalent:@""]; return menu; } @end
なんとも簡単ですね。
NSMenuをいじってみる
次はNSMenuをいじってこんなことをしてみます。
これは、NSMenuItemのsetView:メソッドを使えば実現できます。
ソース
+ (NSMenu*)defaultMenu { NSMenu * menu = [[[NSMenu alloc] initWithTitle:@"PJPopUpMenu"] autorelease]; [menu addItemWithTitle:@"menu1" action:nil keyEquivalent:@""]; // // メニュー内にNSButtonを入れてみる // NSMenuItem *buttonItem = [[[NSMenuItem alloc] initWithTitle:@"buttonItem" action:nil keyEquivalent:@""] autorelease]; NSButton *button = [[[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 20)] autorelease]; { [button setTitle:@"buttonItem"]; [button setBezelStyle:NSTexturedSquareBezelStyle]; } [buttonItem setView:button]; [menu addItem:buttonItem]; // // メニュー内にNSSliderを入れてみる // NSMenuItem *sliderItem = [[[NSMenuItem alloc] initWithTitle:@"sliderItem" action:nil keyEquivalent:@""] autorelease]; NSSlider *slider = [[[NSSlider alloc] initWithFrame:NSMakeRect(0, 0, 100, 20)] autorelease]; { [slider setMinValue:0.0f]; [slider setMaxValue:100.0f]; [slider setFloatValue:50.0f]; } [sliderItem setView:slider]; [menu addItem:sliderItem]; return menu; }
とまぁ、結構遊べるものです。
NSMenuに関しては別記事にて、もう少し色々してみたい思います。
メニューの表示位置を変えてみる
では、最後に、ポップアップメニューの表示位置を変えてみます。
まずポップアップメニューが表示されるフローについて簡単に説明します。
右クリックが検知された際の動作は
- - (void)rightMouseDown:(NSEvent*)theEvent
- - (NSMenu*)menuForEvent:(NSEvent*)theEvent
- + (NSMenu*)defaultMenu
右クリックが検知されると1が呼び出されます。
1のデフォルト動作は、 2を呼び出し、その返り値がnilでなければ、 クリックされた場所にメニューを表示する 2のデフォルト動作は、3を呼び出す 3のデフォルト動作はnilを返す
という様になっています。
(2のデフォルト動作がもしかしたら違うかもしれませんが、ここではこれで差し支えないので、こうしておきます。中途半端ですみません)
つまり、表示位置をコントロールするには、1までさかのぼる必要があります。
また、表示される位置についてです。
絵にするとこんな感じになっています。
座標系が左下基準というのに、注意する必要があります。
そして、クリックされた場所の情報は、
rightMouseDown:の引数であるNSEventに含まれています。
こいつをいじくってやれば、表示位置を変更できるわけです。NSEventについては割愛します。
というわけで、コードはこんな感じになります。
- (void)rightMouseDown:(NSEvent *)theEvent { // // クリック位置をずらす // NSPoint displayPoint = NSMakePoint([theEvent locationInWindow].x-100, [theEvent locationInWindow].y+100); // // 新しいNSEventを作る // これをもとにポップアップメニューを表示させる // NSEvent * newEvent = [NSEvent mouseEventWithType:[theEvent type] location:displayPoint modifierFlags:[theEvent modifierFlags] timestamp:[theEvent timestamp] windowNumber:[theEvent windowNumber] context:[theEvent context] eventNumber:[theEvent eventNumber] clickCount:[theEvent clickCount] pressure:[theEvent pressure]]; // // 新しく作ったNSEventを使ってポップアップメニューを表示 // デフォルトでは、theEventを使っているのでしょう // [NSMenu popUpContextMenu:[[self class] defaultMenu] withEvent:newEvent forView:self]; }
さぁ、どんな使い道があるのか・・・
僕にはさっぱりです。