ページ

2008年2月5日火曜日

スクラップブックその1 (4)ビュー

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

WorkView はドラッグ&ドロップの受け入れと、画像の表示を行う。
ドロップされた画像の保存は以前紹介した Pasteboard Managerを使って行っている。それ以外は MyControllerから image(NSManagedObject)のインスタンスを取得し、必要な設定を行う。


- (BOOL)performDragOperation:(id )sender {

  :

NSImage *image = [[NSImage alloc] initByReferencingURL:[NSURL URLWithString:filepath]];
NSSize size = [image size];
Item *item = [_controller createItem];

item.image = image;
item.x = [NSNumber numberWithFloat:(point.x - size.width / 2)];
item.y = [NSNumber numberWithFloat:(point.y - size.height / 2)];
item.width = [NSNumber numberWithFloat:size.width];
item.height = [NSNumber numberWithFloat:size.height];
item.filename = [filepath lastPathComponent];
  :
}



貼付けられた画像のドラッグ処理は mouseDown: 内で行う。
ADCのドキュメント Handling Mouse Dragging Operations によればドラッグ処理は2つのアプローチがあるとのこと。

 The Three-Method Approach
 The Mouse-Tracking Loop Approach

前者は mouseDown: で開始、mouseDragged: でドラッグ処理、mouseUp: で終了と、その名のとおり3つのメソッドで処理を記述する。この方法の良いところはそれぞれのイベントハンドリングを適切なメソッドで書けるところ。イベント毎の責務が分かれている為、見た目も比較的すっきりする。ただ3つのイベントを連携させる必要があるためケースによっては複雑になるかもしれない。

後者は mouseDown: 内に自前でイベントループを作る。見た目は昔っぽい?が、マウス押しから、ドラッグ、離すまでの処理が一カ所にまとまっているので分かりやすいし、一連の動きを制御しやすい。

説明が長くなったがサンプルでは後者のアプローチを採用している。コードそのものは(忘れてしまったが)どこかのソースコードを参考にして過去に書いたものを流用している。


- (void)mouseDown:(NSEvent *)theEvent
 :
 :
while (1) {
theEvent = [[self window] nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask)];
current_point = [self convertPoint:[theEvent locationInWindow]
fromView:nil];

if (!is_moving &&
((fabs(current_point.x - last_point.x) >= 2.0) ||
(fabs(current_point.y - last_point.y) >= 2.0))) {
is_moving = YES;
}

if (is_moving) {
_selected_item = nil;
moving_vector = NSMakePoint(current_point.x - last_point.x,
current_point.y - last_point.y);
[item moveWith:moving_vector];
[self setNeedsDisplay:YES];
}
last_point = current_point;

if ([theEvent type] == NSLeftMouseUp) {
break;
}
}
while (1) {
theEvent = [[self window] nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask)];
current_point = [self convertPoint:[theEvent locationInWindow]
fromView:nil];

if (!is_moving &&
((fabs(current_point.x - last_point.x) >= 2.0) ||
(fabs(current_point.y - last_point.y) >= 2.0))) {
is_moving = YES;
}

if (is_moving) {
_selected_item = nil;
moving_vector = NSMakePoint(current_point.x - last_point.x,
current_point.y - last_point.y);
[item moveWith:moving_vector];
[self setNeedsDisplay:YES];
}
last_point = current_point;

if ([theEvent type] == NSLeftMouseUp) {
break;
}
}
 :


点の移動量が 2.0以上の場合をドラッグとみなし、移動量をドラッグ対象の画像の位置に反映させて再描画させている。ドラッグ対象の画像は同じ mouseDown: の先頭でヒットテストを行い決定する。

- (void)mouseDown:(NSEvent *)theEvent
{
NSPoint start_point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
Item* item = nil;
// hit test
for (Item* item_test in [[_controller items] reverseObjectEnumerator]) {
if (NSPointInRect(start_point, [item_test rect])) {
item = item_test;
break;
}
}
 :
 :
(この後、イベントループ)