address Logo

XMouse: A Simple Sample Using Mouse Event

目次

2009/07/22
2009/08/04 追加

ここでは次のような読者を想定している。

なお XSlider から "A Simple Sample ..." が始まっているので、Xcode の起動の仕方が分からなければ XSlider から読んで欲しい。

環境

目標

ウインドウの中のボックス領域でマウス操作が発生した時のマウスイベントを捉える。
マウスイベントとしてはいろいろあるが(mouseDown, mouseUp, mouseDragged など)最初に mouseDown から始める。

マウスをクリックしたときの座標をテキストフィールドに書き出す。(下図)

fig1

方法 1

新規プロジェクトで Cocoa Application を選び、プロジェクト名を XMoude とする。新規ファイルで NSView subclass を選び、ファイル名を XMouse.m とする。この時 XMouse.m の他に XMouse.h も生成される。

XMouse.h

#import <Cocoa/Cocoa.h>
//#import <stdio.h>

@interface XMouse : NSView
{
        IBOutlet NSTextField *messageTextField;
        IBOutlet NSBox *box;
}
- (void)mouseDown:(NSEvent *)event; //Kenar
@end

この中のコメントアウトされている行

    //#import <stdio.h>

はデバッグで使われていた行である。

マウスをクリックする領域(box)、クリックした座標を表示するテキストフィールド(messageTextField)およびマウスクリック(mouseDownを捉えるメソッドが宣言されている。

XMouse.m

#import "XMouse.h"

@implementation XMouse
- (void)mouseDown:(NSEvent *)event //Kenar
{
        // mouse-down event comes here.
        NSString *s;
        NSPoint p = [event locationInWindow];
        NSRect r;
        s = [NSString stringWithFormat:@"(%f,%f)", p.x,p.y];
        //NSRunAlertPanel(@"Message", (NSString *)s, NULL, NULL, NULL);
        //if(!box) NSRunAlertPanel(@"Warning", @"box nil", NULL, NULL, NULL);
        r = [box frame];// frame is a generic method for NSView class.
        //printf("%f,%f,%f,%f\n",r.origin.x,r.origin.y,r.size.width,r.size.height);
        //printf("%f,%f,%f,%f\n",NSMinX(r),NSMinY(r),NSWidth(r),NSHeight(r));// same as above
        if(NSPointInRect(p,r) == NO)
           return;
        //if(!messageTextField) NSRunAlertPanel(@"Warning", @"messageTextField nil!", NULL, NULL, NULL);
        [messageTextField setStringValue:s];
}

// awakeFromNib is called when this object is done being unpacked from the nib file;
// at this point, we can do any needed initialization before turning app control over to the user
- (void)awakeFromNib
{
        // We don't actually need to do anything here, so it's empty
}

@end

マウスイベントの座標は

    NSPoint p = [event locationInWindow];

であるが、イベントはウインドウのパネルのどの場所をクリックしても発生し、mouseDown に届くので、ボックス(box)以外から来るイベントを捨てる必要がある。

    r = [box frame];// frame is a generic method for NSView class.
    if(NSPointInRect(p,r) == NO)
       return;

はそのためのコードである。

デバッグに使ったコードがコメントアウトして残されている。

Interface Builder

ソースコードに現れる messageTextFieldbox の実体を Interface Builder で指定し、mouseDown をウィンドウと結びつける必要がある。

ウィンドウの属性を表示する*

注*: ウインドウ(トップバーではない!)をクリックし、Interface Builder の ToolsInstector を選択する>

ウインドウのクラスを XMouse に変更する。すると Class Outlets に boxmessageTextField が表示されるはずである。

fig2

インスペクタパネルのトップバーは Mouse Identity となっている。この Mouse はオブジェクトのクラス名 XMouse から自動的に(適当に)決定される。

このままでは、boxmessageTextField の値は NULL である。これらに Interface Builder で表示されるオブジェクトの実体を結びつける必要がある。

ウインドウを controll-click すると connections panel が表示される*。この中の box をウインドウのボックスに messageTextField をウインドウのテキストフィールドに結びつける。(下図)

fig3

box をウインドウのボックスに結びつけているところ。
messageTextField は既に結びつけ終わっている。

マウスイベントで最初に戸惑うのは、イベントの発生源が Interface Builder の中で(目に見える姿で)表示されない事である。
しかし mouseDown は組み込みのメソッドなので、XMouse.m の中で上書きするだけでマウスイベントはここに来る。従って以上でプログラムは動くはずである。

注*: ウインドウのパネルを選んでインスペクタを開き、Connections に見える messageTextField をウインドウのテキストフィールドに結びつけてもよい。

他のマウスイベント

ソースコードの中の mouseDownmouseUpmouseDragged に置き換えるだけで、これらのマウスイベントに反応するプログラムになる。

デバッグ

printf() を使ったデバッグに関しては XSlider に解説されている。

ソースコード

マウスドラッグに反応するソースコード: XMouse.zip

方法 2

2009/08/04

方法 1 は無駄が含まれていたと思う。XMouse.h で定義される新しいクラスを NSBox の派生クラスとして定義し、これを Box と結びつければ幾分簡単になる。

ここではマウスドラッグに反応する Box を扱う。

XMouse.h

XMouse.h を次のようにする。

#import <Cocoa/Cocoa.h>

@interface XMouse : NSBox
{
    IBOutlet NSTextField *messageTextField;
}
- (void)mouseDragged:(NSEvent *)event; //Kenar
@end

XMouse.h

今度は

    IBOutlet NSBox *box;

がこの中に含まれていない事に注意しよう。

XMouse.m

XMouse.m

#import "XMouse.h"

@implementation XMouse
- (void)mouseDragged:(NSEvent *)event //Kenar
{
        // mouse-dragged event comes here. OK
        NSString *s;
        NSPoint p = [event locationInWindow];//OK
        NSRect r;
        s = [NSString stringWithFormat:@"(%f,%f)", p.x,p.y];
        r = [self frame];// frame is a generic method for NSView class.
        if(NSPointInRect(p,r) == NO)
                return;
        if(!messageTextField) NSRunAlertPanel(@"Warning", @"messageTextField nil!", NULL, NULL, NULL);//OK
        [messageTextField setStringValue:s];//NG
}

// awakeFromNib is called when this object is done being unpacked from the nib file;
// at this point, we can do any needed initialization before turning app control over to the user
- (void)awakeFromNib
{
        // We don't actually need to do anything here, so it's empty
}

@end

XMouse.m

方法 1 では

    r = [box frame];// frame is a generic method for NSView class.

であったが、今度は

    r = [self frame];// frame is a generic method for NSView class.

となる。また今回は余分なコメントは削除されている。

Interface Builder

ソースコードをコンパイルし、Box のインスペクタの identity で Class を XMouse にする。

fig4

Box の identity

さらに Connections で messageTextField をウインドウの中のテキストフィールドと結びつける。

fig5

Box の Connections

コンパイルして実行する。

Discussion

この第二の方法が推奨される方法であるか否かは不明である。

XMouse.m の中の

    if(NSPointInRect(p,r) == NO)
    	return;

を削除した場合にはマウスドラッグは幾分異なった振る舞いをする。この場合にはドラッグの開始点は Box の中に制限されるが、終了点には制限が付かない。この方が使いやすい事があるかも知れない。もしも mouseDown イベントだけを扱う問題であれば、このコードは不要であろう。

ソースコード

マウスドラッグに反応するソースコード: XMouse1.zip

References

以下は参考にした URL の一覧です。