ページ

2008年4月17日木曜日

RubberBand(その2)Sketchのソースを読む (1)

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

Sketch のソースコードを眺めてみる。

まずはマウスの左ボタンを押した時の処理を追って見る。

SKTGraphicView.m
mouseDown:
(1) selectAndTrackMouseWithEvent:
(2)SKTGraphic *clickedGraphic = [self graphicUnderPoint:mouseLocation index:&clickedGraphicIndex isSelected:&clickedGraphicIsSelected handle:&clickedGraphicHandle];
if (clickedGraphic) {
if (clickedGraphicHandle != SKTGraphicNoHandle) {
// ラバーバンド上のノブ(ハンドル)がクリックされた -> 拡大縮小処理
(3) [self resizeGraphic:clickedGraphic usingHandle:clickedGraphicHandle withEvent:event];
} else {
if (modifyingExistingSelection) {
if (clickedGraphicIsSelected) {
// 既に選択されていた図形をクリックした -> 未選択にする
} else {
// 未選択状態の図形をクリッックした -> 選択状態にする
}
} else {
:

mouseDown: 内で、(1) selectAndTrackMouseWithEvent: が呼ばれる。
続いてマウスカーソル下に何があったかを (2)graphicUnderPoint:index:isSelected: でチェックする。
その結果、ラバーバンド上のハンドルがクリックされていたら、(3)resizeGraphic:usingHandle:withEvent: を呼出す。

ハンドルはラバーバンド上下左右、四隅についている四角のこと。これをドラッグして拡大縮小させる。コメントではノブ (knob)と表現している個所もある。



続いて(2)(3)を詳しくみていく。

まずは (2)graphicUnderPoint:index:isSelected:
- (SKTGraphic *)graphicUnderPoint:(NSPoint)point index:(NSUInteger *)outIndex isSelected:(BOOL *)outIsSelected handle:(NSInteger *)outHandle {
for (NSUInteger index = 0; index<graphicCount; index++) {
// 図形を1つ1つチェックする

if (NSPointInRect(point, [graphic drawingBounds])) {
// マウスカーソル直下にある図形が見つかった
if (graphicIsSelected) {
// その図形は直前まで選択状態にあった
NSInteger handle = [graphic handleUnderPoint:point]; // (4)クリックされたハンドルの番号を得る
if (handle!=SKTGraphicNoHandle) {
// ハンドルがクリックされた場合...
}
}
:
}

キャンパス上に配置された図形1つ1つについてヒットテストを行っていく。図形がヒットした場合は (4)handlerUnderPoint: を使い、ハンドルの番号を得る。ハンドルがクリックされていた場合は必要な情報を設定してメソッドを終わらせる。

(4)handlerUnderPoint: を見てみよう。
SKTGraphic.m

- (NSInteger)handleUnderPoint:(NSPoint)point {

// Check handles at the corners and on the sides.
NSInteger handle = SKTGraphicNoHandle;
NSRect bounds = [self bounds];
if ([self isHandleAtPoint:NSMakePoint(NSMinX(bounds), NSMinY(bounds)) underPoint:point]) {
handle = SKTGraphicUpperLeftHandle;
} else if ([self isHandleAtPoint:NSMakePoint(NSMidX(bounds), NSMinY(bounds)) underPoint:point]) {
handle = SKTGraphicUpperMiddleHandle;
} else if ([self isHandleAtPoint:NSMakePoint(NSMaxX(bounds), NSMinY(bounds)) underPoint:point]) {
handle = SKTGraphicUpperRightHandle;
} else if ([self isHandleAtPoint:NSMakePoint(NSMinX(bounds), NSMidY(bounds)) underPoint:point]) {
handle = SKTGraphicMiddleLeftHandle;
} else if ([self isHandleAtPoint:NSMakePoint(NSMaxX(bounds), NSMidY(bounds)) underPoint:point]) {
handle = SKTGraphicMiddleRightHandle;
} else if ([self isHandleAtPoint:NSMakePoint(NSMinX(bounds), NSMaxY(bounds)) underPoint:point]) {
handle = SKTGraphicLowerLeftHandle;
} else if ([self isHandleAtPoint:NSMakePoint(NSMidX(bounds), NSMaxY(bounds)) underPoint:point]) {
handle = SKTGraphicLowerMiddleHandle;
} else if ([self isHandleAtPoint:NSMakePoint(NSMaxX(bounds), NSMaxY(bounds)) underPoint:point]) {
handle = SKTGraphicLowerRightHandle;
}
return handle;

}

合計8つのハンドル(上下左右、四隅)について単純なヒットテストを行っている。ヒットした場合はそれぞれの位置に応じて定義された値(例:左上なら SKTGraphicUpperLeftHandle)を返す。ハンドルがクリックされていない場合は SKTGraphicNoHandlerを返す。


最後にハンドルをつかんでドラッグした時の拡大縮小処理を行なう (3)resizeGraphic:usingHandle:withEvent:
SKTGraphicView.m

- (void)resizeGraphic:(SKTGraphic *)graphic usingHandle:(NSInteger)handle withEvent:(NSEvent *)event {

while ([event type]!=NSLeftMouseUp) {
// イベントループを形成、左ボタンが離されるまで繰り返す
event = [[self window] nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask)];
[self autoscroll:event];
NSPoint handleLocation = [self convertPoint:[event locationInWindow] fromView:nil];
if (_grid) {
// グリッドを使っている時は位置を強制的にグリッドに合わせる
handleLocation = [_grid constrainedPoint:handleLocation];
}
// 拡大縮小処理実行
handle = [graphic resizeByMovingHandle:handle toPoint:handleLocation];
}
}

resizeByMovingHandle:toPoint: の戻り値で handleを受け取っている。ということは、つかんでいるハンドルが入れ替わるということか?実際メソッドを見てみると左右反転、あるいは上下反転が行われている。これはハンドルをドラッグ中にハンドルが元図形の範囲を超えて反対側に行ってしまった場合を想定している。例えば、左のハンドルをドラッグすると通常は左方向の伸縮を制御することになる。左へドラッグすれば拡大、右へ行けば縮小。ところがドラッグしたまま図形の右端を超えると、これが逆転して、左へ行けば縮小、右へ行けば拡大となる。つまりこの時点では右のハンドルと同じ動作になっていることになる。

イベントハンドリングはざっとこんなところか。