ページ

2007年8月31日金曜日

NSView#dragImage:... を使う

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

NSView の

- (void)dragImage:(NSImage *)anImage at:(NSPoint)viewLocation offset:(NSSize)initialOffset event:(NSEvent *)event pasteboard:(NSPasteboard *)pboard source:(id)sourceObj slideBack:(BOOL)slideFlag;

を使って、アプリ内外のドラッグ&ドロップを統一的に処理する方法を調査中。


あるカスタムビュー(仮に View1とする)で上記メソッドを実行すると処理はそこでブロックされる。例えば:

[self dragImage:drag_img at:p offset:NSZeroSize
event:event pasteboard:pb source:self slideBack:NO]; --> 終了までブロック

NSLog(@"end");


を実行すると、ドラッグ&ドロップが終わるまで "end"がログに出力されない。

このドラック&ドロップ中のイベントを拾う必要があるのだが NSDraggingDestination(非形式プロトコル)を実装してやるとあっさり取ることができた。NSDraggingDestinationの主要メソッドは6つある。

– draggingEntered:
– draggingUpdated:
– draggingExited:
– prepareForDragOperation:
– performDragOperation:
– concludeDragOperation:


ところでここで疑問が生じてくる。dragImage:.. は処理が終わるまでブロックされているのだから NSDraggingDestination のメソッド群は別スレッドから呼ばれるのか?そうなると排他制御が必要になるな、と。

そこで dragImage:... と draggingEntered: の両方で下記のコードを入れて様子を見てみると:
NSLog(@" %@, %@", self, [NSThread currentThread]);


結果はどちらも同じだった。

2007-08-31 12:34:04.439 DragSample[5671] , {num = 1, threadDictionary =
{type = mutable, count = 1, capacity = 4, pairs = (
1 : {contents = "NSAppleEventManagerHandlingStack"} = {type = mutable-small, count = 0, values = (
)}
)}
}


つまり dragImage:... と同じスレッドから draggingEntered: が呼び出されていることになる。


とりあえずは一歩前進。

2007年8月30日木曜日

draggingSourceOperationMaskForLocal:

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

NSView#dragImage:(NSImage *)anImage at:(NSPoint)viewLocation offset:(NSSize)initialOffset event:(NSEvent *)event pasteboard:(NSPasteboard *)pboard source:(id)sourceObj slideBack:(BOOL)slideFlag
を使って同一ウィンドウと他アプリ両方をサポートする D&Dを検証中。
その課程で分かったことをメモ。

下記メソッドを実装しておくと、ドラッグ中に呼び出される。

NSObject(NSDraggingSource)#draggingSourceOperationMaskForLocal:

このメソッドで渡ってくる (BOOL)flag はローカルかどうか(?)を表すのではなく、NSObject(NSDraggingDestination)のメソッドが実装されているかどうかによって YES/NOが返されることが分かった。これらのメソッドが実装されている、すなわちD&Dを受け付ける場合は flag==YES、となり実装していない場合は flag==NO が返る。

2007年8月24日金曜日

ドラッグ、ドラッグ

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

Safariや他のアプリでやっているように画像をドラッグした時に:


 1. ウィンドウ内であれば移動とし、必要ならスクロールさせる
 2. ウィンドウ外であれば他のウィンドウへのコピーとする

ような動作をさせたい。


NSView の dragImage:at:offset:event:pasteboard:source:slideBack: を使うと 2 は簡単に実現できるのだが、1 はできるのだろうか。


Safari の動作を見ると次のようになっていた。

 a 表示画像をマウスでつかんでドラッグする。
  画面の端何ピクセルかの幅へ持っていくとスクロールされる。

 b 画面端でもたもたしないで、サッとウィンドウの外へ動かすとスクロールが起こらない。

 c ウィンドウズの外へドラッグすると、他ウィンドウへのコピーとなる。


感覚的な動作なのでアプリを作る時はこれをまねるべきだろう。ただ作るとなると面倒だな。

一方、Excelを見ると違うアプローチを取っていた。a, b は同じだが c のように外へは出ない。他のウィンドウへコピーするには Opt キーを押しながらドラッグする必要がある。こちらだと実装は楽だな。

スクラップブック的な観点からすると他ウィンドウへのコピーで Optキーが不要な Safariの方が使い勝手良い。
やはり面倒でもSafari方式にすべきか。うーん。

ドラッグ画像を半透明する

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

アプリ上のオブジェクトをドラッグ&ドロップして他のアプリケーションへ持っていく場合には NSView の dragImage:at:offset:event:pasteboard:source:slideBack: を使えば容易に実装できる。このメソッドへ NSImage を渡せばドラッグ中のイメージとして表示することができる。


ここまでは簡単にできたのだが、できれば Safari などが実現しているようにドラッグ中のイメージを半透明にしたい。ところが該当するような APIが見つからない。簡単にはできないのかとあきらめた矢先、NSImage に対して lock を送ると絵が描けることに気がついた。それなら画像の NSImageとは別にもう一つ NSImageを作りここへ dissolveToPoint:fraction: を使って半透明で描画してあげれば行けるのでは?

// 画像をファイルから読み込む
NSImage* image1 = [[NSImage alloc] initWithContentsOfFile:@"hello.png"];

// 表示用の画像を別途用意。サイズは image1に合わせておく
NSImage* image2 = [[NSImage alloc] initWithSize:[image1 size]];

// image1の画像を image2へ半透明で描画する
[image2 lockFocus];
[image1 dissolveToPoint:NSZeroPoint fraction:0.5];
[image2 unlockFocus];



やってみると簡単にできた。dissolveToPoint:fraction: などの描画メソッドは NSView に対して使うものと思いこんでいたためにこんな簡単なことに気がつかなかった。この方法を使えば拡大・縮小・回転、他の画像との合成などなんでもできる。シンプルながらよくできた仕組みだね。

開発開始

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

日々の隙間時間を使ってアプリを開発する。
目標はスクラップブックとノートを組み合わせたようなもの。散らかっている情報を有機的に一つにまとめあげるようなソフトを作りたい。

今現在はドラッグ&ドロップで画像の貼り付けができるところまでができた。

ブログにはこれ以降の開発を記録として残す。