|
- Application Kit-
NSResponder
Application Kit - NSResponder
マウスクリック、ドラッグイベントをつかまえる
Keywords: mouseDown, mouseUp, mouseDragged
マウスを使って、ある Responder をクリックしたり、ドラッグしたりすると、イベントが発生するんだ。それらをつかまえるには mouseDown:、mouseUp:、mouseDragged: メソッドをオーバーライドする。
Application Kit/NSResponder.h
- (void)mouseDown:(NSEvent*)theEvent;
- (void)mouseUp:(NSEvent*)theEvent;
- (void)mouseDragged:(NSEvent*)theEvent;
これらを使えば、簡単にマウスイベントを利用することができるよ。
MouseEventViewm (sample)
- (void)mouseDown:(NSEvent*)theEvent
{
NSLog("Mouse is pressed at %@¥n",
[event locationInWindow]);
}
- (void)mouseUp:(NSEvent*)theEvent
{
NSlog("Mouse is released at %@¥n",
[event locationInWindow]);
}
- (void)mouseDragged:(NSEvent*)theEvent
{
NSlog("Mouse is dragged to %@¥n",
[event locationInWindow]);
}
Application Kit - NSResponder
ダブルクリックをつかまえる
Keywords: mouseUp, clickCount
ユーザがダブルクリックしたかどうか、知るにはどうしたらいいか?それは、mouseUp: のところで、NSEvent の clickCount を調べるんだ。
DoubleClickView.m (sample)
- (void)mouseUp:(NSEvent*)event
{
if([event clickCount] == 2) {
// Double click
NSLog(@"I got double click!¥n");
}
}
ちなみに、クリックカウントは何回まで追跡できるのか?どうも、何回でもできるらしい。ダブルクリックや、トリプルクリックだけでなく、100th クリックも利用できるわけだ。だれも使わないと思うが。
Application Kit - NSResponder
コンテキストメニューを設定する
Keywords: mennu, setMenu
コンテキストメニューってあるよな。ctrl キーといっしょにクリックするか、右クリックで出てくるメニュー。Cocoa では、コンテキストメニューをサポートしている。NSResponder のレベルで管理することになるんだ。
コンテキストメニューの設定、取り出しには menu, setMenu を使う。
Application Kit/NSResponder.h
- (void)setMenu:(NSMenu *)menu;
- (NSMenu *)menu;
実は、いちばん簡単にコンテキストメニューを設定する方法は、Interface Builder を使う方法だ。IB でオブジェクトを貼付けると、ほとんどのやつは、その outlet に menu という項目があるはずだ。それに IB で作ったメニューを指定してやればいい。それだけで、ctrl + クリックでメニューが出るようになる。ただし、メニューのアイテムに target を指定してやらないと enable にならないので注意。
■関連リンク:
コンテキストメニューを動的に変化させる (NSView)
Application Kit - NSResponder
ファースト・レスポンダが変更されるときの流れ
Keywords: acceptsFirstResponder, resignFirstResponder, becomeFirstResponder
ファースト・レスポンダが変更されるとき、どういう流れになっているのか調べてみるよ。
例として、TextFiled が 2 つ(A と B)がある状態を考えてみよう。いま、A にフォーカスが当たっていて、キーボードから文字を入力すると A に入れられる。それを、Tab を使って B にフォーカスを移そうとするんだ。
使われるメソッドは、次の 3 つ。acceptsFirstResponder、resignFirstResponder、そして becomeResponder だ。
Application it/NSResponder.h
- (BOOL)acceptsFirstResponder;
- (BOOL)becomeFirstResponder;
- (BOOL)resignFirstResponder;
まず、ウィンドウが TextFiled B に、お前はファースト・レスポンダになれるのか?って聞くんだ。そのために acceptsFirstResponder を送るんだ。この場合、TextField B は、なれるので YES を返す。
次に、ウィンドウは、今度は TextField A(現在のファースト・レスポンダ)に、ファースト・レスポンダを変えたいんだけど、準備できてるか?って聞くわけだ。それには resignFirstResponder が使われる。TextField A は、別にかまわないなら YES を返す。だめなら、たとえば、いまかな漢字変換している最中だから、後にしてくれ!っていうときは NO を返すんだ。
TextField A が、ファースト・レスポンダ変えてもいいぜ、っていったら、いよいよファースト・レスポンダを変える。まず TextField B に becomResponder を送って、お知らせする。そして、ウィンドウの outlet であるファースト・レスポンダを TextField B に変えて、おしまいだ。
自分で作ったビューで挙動を変えたいときは、上の 3 つのメソッドをオーバーライトすれば OK だ。
Application Kit - NSResponder
.nib の中にある First responder とは
Keywords: First Responder
Cocoa アプリケーションの .nib ファイルを Interface Builder で開くと、First Responder っていうアイコンが、最初っからできてるよな。これに対して、Copy とか Paste とかのアクションはつながってるでしょ。でも、これって何?インスタンスなの?誰かの outlet なの?どこに実体があるの?そもそも実体があるの?薄いグレーで名前が表示されてるのが気になるよな、、、
結論から言ってしまうと、こいつは nil だ。いままで nil にアクションを送っていたのだ。へ?何それ?この仕組みを解き明かしてみよう。
(注意:ここでいってる "First Responder" は、.nib のウィンドウの中に出てくるアイコンのことだ。NSWindow の outlet である firstResponder とは違うからね。そこんとこ、勘違いしないよーに!)
AppKit では、NSControl のサブクラスはアクションを送ることができるんだ。たとえば、NSButton ならボタンが押されときにアクションを送る、とかね。アクションは、NSApplication の sendAction:to:from: を経由して、ターゲットに送られるんだ。
Application Kit/NSApplication.h
- (BOOL)sendAction:(SEL)theAction to:(id)theTarget from:(id)sender;
sender に送り手となるオブジェクト、theTarget に送る相手のオブジェクト、theAction にアクションを指定するんだ。Interface Builder で、普通にオブジェクト同士をつなげた場合は、sender と theTarget に、それぞれ指定したオブジェクトが入る。
じゃ、なげる相手として First Responder を指定した場合は?この場合、sender はアクションをなげるオブジェクトだけど、theTarget に nil が指定されるんだ。そして、NSApplication は、アクションを送る相手を探す。どうやって探すかって?responder chain を使うんだ。responder chain に対して、どんどんアクションをなげていく。誰かが受け取ったら、そこでおしまいだ。
Application Kit - NSResponder
レスポンダ・チェインをたぐる
Keywords: responder chain
responder chain の話をする前に、ちょっと思い出しておこう。まず、NSApplication は keyWindow を持っている。さらに Window のリストも持っている。そして NSWindow は、firstResponder を持っている。各 View は nextResponder を持っている。ここまで OK?
まず、NSApplication は keyWindow にアクションをなげる。NSWindow はアクションをもらったら、firstResponder に対してアクションをなげて、そいつがアクションを受け取るかどうか評価する。受け取ったら、そこでおしまい。
受け取らなかったら、そいつの nextResponder にアクションをなげる。
ぜんぶの responder を走査したら、次は NSWindow 自分自身になげる。
その次は Window の delegate。
keyWindow で受け取られなかったら、次は main window になげる。そこでも同じことが行われる。
main window の評価が終わったら、次は NSApplication 自身。
それでもだめなら、application の delegate。そこで、おしまい。
こういう流れね。responder chain は、target に nil が指定されたときに使われる。つまり、アクションの相手が不明なときや、動的に変化するとき(first responder とか)に使われるんだ。相手が決まっているときは、Interface Builder でつないでやって、直接なげればいい。
Application Kit - NSResponder
レスポンダ・チェインを表示する
Keywords: responder chain
Cocoa では、イベントが発生したら、NSWindow から NSResponder に、順々に投げられるんだ。まず最初に、ファースト・レスポンダに投げる。ファースト・レスポンダがそのイベントに対して処理をしないならば、次のレスポンダに投げる。このレスポンダの連なりをレスポンダ・チェインって呼ぶんだ。こいつを表示して見よう。実際にアプリケーションを作るときは、表示させる必要はまったくない。単に、実験として、どういう chain を作っているのか、確かめるのが目的だ。
NSResponder から、次の responder を得るには、nextResponder を使う。
Application Kit/NSResponder.h
- (NSResponder *)nextResponder;
これを使って、次のような関数、showResponderChain() を作ってみた。
FirstResponderWindow.m (sample)
void showResponderChain(NSResponder* responder)
{
NSLog(@"- Show responder chain? ");
while(true) {
NSLog(@"%@", NSStringFromClass([responder class]));
responder = [responder nextResponder];
if(responder == nil) {
break;
}
printf("-> ");
}
}
この関数に、responder chain の先頭を渡してやる(途中でもいいけど)。そこから連なる responder chain を表示するんだ。たとえば、ウィンドウに貼付けてある、NSTextView が first responder だった場合は、次のような responder chain ができあがってるんだ。
Result
- Show responder chain
NSTextView
-> _NSKeyboardFocusClipView
-> NSTextField
-> NSView
-> NSWindow
という感じで、イベントが NSTextView を先頭に、次々と渡っていくわけだ。で、てきとうなところで、処理されるわけね。
■関連リンク:
ファースト・レスポンダの変更をつかまえる (NSWindow)
■サンプルダウンロード:
ResponderChain.tar.gz
|