NSViewではマウス移動をトラッキングすることができる。トラッキングを使えば画像の上にマウスカーソルを置いた時に反転表示したり枠線を付けたりすることができる。
トラッキングの動きを見るサンプルを作ってみた。
MouseTrackingSample.zip
マウスをWindow内の灰色の部分へ移動させると白色に反転する。
トラッキングを開始すると次のメッセージを受け取ることができるようになる。
mouseEntered:
mouseMoved:
mouseExited:
ただしデフォルトではこれらのメッセージが飛ぶことはなく、いくつかの準備が必要。やり方は ADCのドキュメントに書いてある。
Handling Mouse-Tracking Events
(1) mouseEnteredとmouseExited
まずトラッキングイベントの受け取り登録を NSView#addTrackingRect:owner:userData:assumeInside: を使って行う。ドキュメントによれば initWithFrame: ではなく、viewDidMoveToWindow: 内で行うのが良いとのこと。
- (void)viewDidMoveToWindow {
tag = [self addTrackingRect:[self bounds] owner:self userData:NULL assumeInside:NO];
}
これで mouseEntered: と mouseExited: がこのビューへ送られるようになる。
(2) mouseMoved:
mouseMoved: メッセージを受け取るにはこれらに加えてさらに親ウィンドウに setAcceptsMouseMovedEvents:YES を送る必要がある。デフォルトではイベント量が多く、余計な負荷となってしまう為にこの設定が NOになっている。mouseEntered: でマウスがトラッキング領域に入ったところでこのメッセージを送る。すると mouseMoved: メッセージが送られてくるようになる。そのままだとメッセージが送られ続けるのでマウスカーソルが外に出た時点、つまり mouseExited: が送られた時点で NOを設定する。
その他、わかったこと:
- ビューがファーストレスポンダでないと mouseMoved:が送られない。mouseEntered: の中で [[self window] makeFirstResponder:self]; としてやる。
- moueMoved: はマウスを押している(ドラッグしている)間は発生しない。これは ADCドキュメントにも書かれている。
なおビューの大きさが変わった場合はこれに合わせてトラッキング領域も変更する必要がある。setFrame: と setBounds: にトラッキング領域を再設定するコードを書いておく。
- (void)setFrame:(NSRect)frame
{
[super setFrame:frame];
[self removeTrackingRect:tag];
tag = [self addTrackingRect:[self bounds] owner:self userData:NULL assumeInside:NO];
}
- - - -
MacOSX 10.5からは NSTrackingAreaが新しく導入されている。
Using Tracking-Area Objects
この検証はまた後日。