NSDraggingSource非形式プロトコル
ドラッグ元のオブジェクトがドラッグ操作中に受け取るメッセージはNSDraggingSource非形式プロトコルで定義されています。実装が必須とされているのはdraggingSourceOperationMaskForLocal:メソッドだけで、後はオプションです。
実装が必須とされているdraggingSourceOperationMaskForLocal:メソッドを実装していなくてもうまくいっていたのですが、これはNSTableViewが実装してくれていたものと思われます。
NVKTableViewではNSDraggingSource非形式プロトコルで規定されているメソッドのうち、以下のメソッドを実装します。
- draggingSourceOperationMaskForLocal:
- draggedImage:beganAt:
- draggedImage:movedTo:
- draggedImage:endedAt:operation:
またドラッグ操作を始めるNSViewのメソッドdragImage:at:offset:event:pasteboard:source:slideBack:をオーバーライドします。このメソッドはNSTableViewが実装してくれているので本来は実装する必要はないのですが、ドラッグ開始時のマウスポインタの座標を知るためにオーバーライドしています。
インスタンス変数を追加する
ドラッグ操作中にマウスポインタがテーブルビューの外へでているかどうかでカーソルの形状を変え、ユーザにこれから起こる現象を予告します。そこでマウスポインタがテーブルビューの外へでているかどうかを表すインスタンス変数draggingOutを追加します。
//
// NVKTableView.h
// RepeatingMotifGenerator
//
// Copyright NovemberKou 2008. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface NVKTableView : NSTableView
{
BOOL draggingOut;
NSPoint startPoint,offset;
NSArrayController *arrayController;
}
- (Class)targetClass;
- (BOOL)pasteboardHas:(NSString *)theType;
- (void)setActionName:(NSString *)actionName;
@end
ドラッグ操作をマスクする
実装が必須とされているdraggingSourceOperationMaskForLocal:を実装します。これはどんな操作を許すのかというフラグを連結したものを返せばOKです。以下のような定数が定義されているので、必要な操作を論理和で連結して返します。(ただし最新情報はデベロッパドキュメントを参照する事)
- NSDragOperationNone
- NSDragOperationCopy
- NSDragOperationLink
- NSDragOperationGeneric
- NSDragOperationPrivate
- NSDragOperationAll_Obsolete(廃止予定。代わりにNSDragOperationEveryを使う)
- NSDragOperationMove
- NSDragOperationDelete
- NSDragOperationEvery
引数は自分のアプリケーション内のドラッグ操作かどうかを表すフラグです。
NVKTableViewでは汎用性を考慮して、自分のアプリケーション内では全てのドラッグ操作を許可し、他のアプリケーションへのドラッグ操作はコピーのみ許可する事にします。
//
// NVKTableView.m
//
// 自分のアプリケーション内におけるドラッグ操作は全て許可する
// 他のアプリケーションへのドラッグ操作はコピーのみ許可する
- (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)isLocal
{
return isLocal ? NSDragOperationEvery : NSDragOperationCopy;
}
マウスクリックされた座標を覚えておく
スーパークラスのメソッドを呼びだす際にslideBackをNOにしていますが、これはスライドバックする必要がないためです。ドロップを受け入れない場合にドラッグされている画像をもとの場所まで戻すアニメーションをするのがスライドバックですが、ドロップを受け入れない場合は爆発アニメーションと共にオブジェクトを削除するわけですから、スライドバックが不要になります。
//
// NVKTableView.m
//
- (void)dragImage:(NSImage *)anImage
at:(NSPoint)imageLoc
offset:(NSSize)mouseOffset
event:(NSEvent *)theEvent
pasteboard:(NSPasteboard *)pboard
source:(id)sourceObject
slideBack:(BOOL)slideBack
{
// ドラッグ操作のメソッドで渡される座標はイメージの原点であって、マウスクリックされた座標ではない。
// そこでマウスクリックされた座標を覚えておく。
startPoint = [[self window] convertBaseToScreen:[theEvent locationInWindow]];
// スライドバックする状況はないので、スライドバックを禁止する
[super dragImage:anImage
at:imageLoc
offset:mouseOffset
event:theEvent
pasteboard:pboard
source:sourceObject
slideBack:NO];
}
ドラッグ操作の初期化処理
ドラッグされる画像が表示され、しかしまだ画像をマウスポインタに追随させる処理が始まっていない段階でdraggedImage:beganAt:メッセージが送られてきます。ここでドラッグ操作の初期化処理を行ないます。最初はドラッグアウトされている筈はありませんので、draggingOutをNOで初期化します。
引数beganAt:で渡される座標は、ドラッグされる画像の原点で、これはスクリーン座標です。この座標と先ほど保存したstartPointとの差をoffsetとして保存しておきます。
//
// NVKTableView.m
//
- (void)draggedImage:(NSImage *)anImage
beganAt:(NSPoint)aPoint
{
draggingOut = NO;
// マウスクリックされた座標とイメージの原点の差をオフセットとして持つ
offset.x = startPoint.x - aPoint.x;
offset.y = startPoint.y - aPoint.y;
}
ドラッグ中の処理
ドラッグ操作中はドラッグ元にdraggedImage:movedTo:メッセージが送られてきます。ここではマウスカーソルを変える事でユーザにこれから起こる現象を予告します。
まずmovedTo:引数で渡されるスクリーン座標をウィンドウ座標に変換します。これに先ほど計算したオフセットを足して、画像原点の座標をマウスポインタの座標に変換します。得られた座標を更に変換して、自分のビュー座標にします。この座標が自分自身のbounds内にあるかどうかで、マウスポインタがテーブルビューの中にあるか、外にあるかがわかります。
マウスポインタが外から中に入ってきた場合は、カーソルを矢印カーソルにします。マウスポインタが中から外へでた場合はカーソルを爆発カーソル(というんでしょうか???)に変えます。
最初はこれだけでよいかと思っていたのですが、マウスポインタが自分のウィンドウの外へでるとマウスカーソルが変えられてしまう問題が発生しました。ドロップを受け入れない場合は矢印カーソルに、受け入れる場合は状況に応じたカーソル(コピーの場合はプラスマークのついたカーソル)になります。問題はドロップを受け入れない場合で、この場合は爆発カーソルにしたいわけです。そこでマウスポインタがテーブルビューの外にあって、かつカーソルが矢印カーソルの場合は、カーソルを爆発カーソルに変える様にしました。
//
// NVKTableView.m
//
- (void)draggedImage:(NSImage *)draggedImage
movedTo:(NSPoint)screenPoint
{
BOOL pointInView;
NSPoint windowPoint,viewPoint;
windowPoint = [[self window] convertScreenToBase:screenPoint];
// オフセットを足す事でマウスカーソルの座標になる
windowPoint.x += offset.x;
windowPoint.y += offset.y;
viewPoint = [self convertPoint:windowPoint fromView:nil];
pointInView = NSPointInRect(viewPoint,[self bounds]);
if(draggingOut && pointInView)
{
[[NSCursor arrowCursor] set];
draggingOut = NO;
}
if(!draggingOut && !pointInView)
{
[[NSCursor disappearingItemCursor] set];
draggingOut = YES;
}
if(!pointInView && [NSCursor currentCursor] == [NSCursor arrowCursor])
[[NSCursor disappearingItemCursor] set];
}
ドラッグ終了時の処理
NSShowAnimationEffectの引数は以下の様になっています。
void NSShowAnimationEffect (
NSAnimationEffect animationEffect,
NSPoint centerLocation,
NSSize size,
id animationDelegate,
SEL didEndSelector,
void *contextInfo
);
animationEffect |
アニメーションの種類を指定する。NSAnimationEffectPoofで爆発アニメーションになる。
|
|---|---|
centerLocation |
アニメーションの中心座標をスクリーン座標で指定する。引数の座標にオフセットを足して、マウスポインタの座標としています。
|
size |
アニメーションのサイズ。NSZeroSizeにするとデフォルトのサイズになる。最初、ドラッグ画像のサイズとしましたが、複数の原図をドラッグアウトすると縦長になって見栄えが悪いので、画像の横幅を使った正方形にしました。
|
animationDelegate |
アニメーション終了後にメッセージが送られるオブジェクト。データの削除処理をデータソースに任せたいので、データソースを指定しています。
|
didEndSelector |
上記のデリゲートに送られるメッセージのセレクタ。デベロッパドキュメントで指定された通りのセレクタとしています。
|
contextInfo |
上記セレクタの引数。何か情報を渡したい時に使えます。今回は特に渡したい情報もないのでnilとしています。
|
//
// NVKTableView.m
//
- (void)draggedImage:(NSImage *)anImage
endedAt:(NSPoint)aPoint
operation:(NSDragOperation)operation
{
if(operation == NSDragOperationNone && draggingOut)
{
NSShowAnimationEffect(NSAnimationEffectPoof,
NSMakePoint(aPoint.x + offset.x, aPoint.y + offset.y),
NSMakeSize([anImage size].width, [anImage size].width),
[self dataSource],
@selector(animationEffectDidEnd:),
nil);
[[NSCursor arrowCursor] set];
}
}
データソースに実装するanimationEffectDidEnd:については次のページで説明します。

ホーム
前へ