home link download back number special issue

HMDT - Special Issue / Sketch BP / Sketch に見るドローオブジェクトの編集


- Case Study -

Sketch に見るドローオブジェクトの編集

ドローオブジェクトを編集する

次は、編集だ!Sketch では、ドローオブジェクトをダブルクリックすると、そのオブジェクトの編集モードに入る。このモードに入ると、マウスクリックで選択を外すまでは、そのドローオブジェクトを優先的に変更することになるんだ。

いまのところ、Sketch で編集に対応しているオブジェクトは、TextArea だけだ。その他のドローオブジェクトをダブルクリックしても、無視されるよ。

TextArea をダブルクリックすると、テキストを編集するために、NSTextView が表示されるんだ。一般的なテキスト編集はこいつでできる。フォントの変更とかも、始めから対応してるぜ。


編集可能なドローオブジェクト

SKTGraphic には isEditable っていう、そのドローオブジェクトが編集可能かどうか、調べるメソッドがあるんだ。

Sketch/DocumentMode.subproj/SKTGraphic.h
- (BOOL)isEditable;

これを呼ぶことで、SKTGraphicView は、そのドローオブジェクトが編集モードに入れるかどうかを、知ることができるんだ。デフォルトでは NO を返すように実装されている。SKTTextArea は YES を返しているぜ。

編集モードに入るには、まず startEditingWithEvent:inView: を呼ぶ。これでドローオブジェクトが編集モードになる。そこから抜けるには、endEditingInView: を呼ぶんだ。

Sketch/DocumentMode.subproj/SKTGraphic.h
- (void)startEditingWithEvent:(NSEvent *)event inView:(SKTGraphicView *)view;
- (void)endEditingInView:(SKTGraphicView *)view;

この 2 つのメソッドを実装するのが、編集対応のメインだ。


Sketch に見る編集モードへの入り方

Skethc でのドローオブジェクトの編集を調べるには、

  • 通常モードから編集モードへの入り方、または抜け方
  • 各々のオブジェクトの編集

の 2 つを調べることになる。ここでは、編集モードへの入り方、抜け方を見てみよう。対象となるのは SKTGraphicView クラスだ。

mouseDown:

もちろん、スタートポイントは mouseDown: だ。今回は、ダブルクリックの処理に注目することになる。

Sketch/SKTGraphicView.m
- (void)mouseDown:(NSEvent *)theEvent {
    Class theClass = 
        [[SKTToolPaletteController sharedToolPaletteController] 
            currentGraphicClass];
    if ([self editingGraphic]) {
        [self endEditing];
    }
    if ([theEvent clickCount] > 1) {
        NSPoint curPoint = 
            [self convertPoint:[theEvent locationInWindow] fromView:nil];
        SKTGraphic *graphic = [self graphicUnderPoint:curPoint];
        if (graphic && [graphic isEditable]) {
            [self startEditingGraphic:graphic withEvent:theEvent];
            return;
        }
    }

    ...
}

NSEvent の clickCount を調べることで、ダブルクリックと分かる。ダブルクリックで、

  • かつマウスの下にオブジェクトがあって、
  • そいつが編集可能だったら(isEditable が YES なら)、

編集モードに入るために、startEditingGraphic:withEvent: を呼び出すんだ。


startEditingGraphic:withEvent:

このメソッドは簡単。SKTGraphic の startEditingWithEvent:inView: を呼び出すだけだ。

Sketch/SKTGraphicView.m
- (void)startEditingGraphic:(SKTGraphic *)graphic 
                    withEvent:(NSEvent *)event 
{
    [graphic startEditingWithEvent:event inView:self];
}

これで編集モード突入!


endEditing:

そして編集モードから抜けるための endEditing: メソッド。これも mouseDown: から呼び出される。

Sketch/SKTGraphicView.m
- (void)endEditing {
    if (_editingGraphic) {
        [_editingGraphic endEditingInView:self];
        _editingGraphic = nil;
        _editorView = nil;
    }
}

現在編集中のドローオブジェクトの endEditingInView: を呼び出してやる。これで編集モードから、通常モードに戻ってくるんだ。


Sketch に見るテキストオブジェクトの編集

んじゃ、続いて SKTTextArea によるテキストオブジェクト編集の様子を。SKTTextArea は SKTGraphic を継承したクラスで、テキストオブジェクトを取り扱うんだ。テキストの編集には NSTextView を使う。その NSTextView は、Interface Builder であらかじめ作られるんじゃなくて、コード上で on the fly で作られる。それをやるのが newEditor() だ。

newEidtor()

新しい NSTextView を作るための関数だ。

Sketch/DocumentMode.subproj/SKTTextArea.m
static NSTextView *newEditor() {
    NSLayoutManager *lm = [[NSLayoutManager allocWithZone:NULL] init];
    NSTextContainer *tc = [[NSTextContainer allocWithZone:NULL] 
        initWithContainerSize:NSMakeSize(1.0e6, 1.0e6)];
    NSTextView *tv = [[NSTextView allocWithZone:NULL] 
        initWithFrame:NSMakeRect(0.0, 0.0, 100.0, 100.0) textContainer:nil];

    [lm addTextContainer:tc];
    [tc release];

    [tv setTextContainerInset:NSMakeSize(0.0, 0.0)];
    [tv setDrawsBackground:NO];
    [tv setAllowsUndo:YES];
    [tc setTextView:tv];
    [tv release];

    return tv;
}

この中で NSLayoutManager、 NSTextContainer、NSTextView が作られているんだ。返り値は NSTextView へのポインタ。注意しないといけないのは、NSLayoutManager が release されていないこと。Ref count は 1 になっている。だから、使い終わったら、自分で release してやらないといけないんだ。


startEditingWithEvent:inView:

そして SKTTextArea を編集モードにするためのメソッド。ここでやることはたくさんあるぞ!

Sketch/DocumentMode.subproj/SKTTextArea.m
- (void)startEditingWithEvent:(NSEvent *)event 
                         inView:(SKTGraphicView *)view 
{
    NSTextView *editor;
    NSTextStorage *contents = [self contents];
    NSSize maxSize = [self maxSize];
    NSSize minSize = [self minSize];
    NSRect bounds = [self bounds];
    
    if (!sharedEditorInUse) {
        if (!sharedEditor) {
            sharedEditor = newEditor();
        }
        sharedEditorInUse = YES;
        editor = sharedEditor;
    } else {
        editor = newEditor();
    }

    ...

    [contents addLayoutManager:[editor layoutManager]];
    [view addSubview:editor];
    [view setEditingGraphic:self editorView:editor];
    [editor setSelectedRange:NSMakeRange(0, [contents length])];
    [editor setDelegate:self];

    // Make sure we redisplay
    [self didChange];

    [[view window] makeFirstResponder:editor];
    if (event) {
        [editor mouseDown:event];
    }
}

まず、newEditor() を使って、NSTextView を取り出す。そして、

  • SKTTextArea は、テキストを保持するために NSTextStorage を持っているんだけど、そいつに NSLayoutManager を addLayoutManager: してやる。これで、テキストの変更が反映される
  • 引数の view は SKTGraphicView なんだ。これのサブビューとして、NSTextView を addSubView: してやる。これで画面に NSTextView が登場する
  • NSTextView の delegate として、SKTTextArea を設定する。これで NSTextView からの notification を受け取れる
  • NSTextView を firstResponder にしてやる。これで、キー入力が NSTextView に流れる

と、これだけの処理をしてやることによって、NSTextView を使えるようになるのだ。こんなにやることがある、と言うべきか、これしかない、と言うべきか?


endEditingInView:

こっちは編集モードから抜けるときのメソッド。やることは、さっきの逆だね。

Sketch/DocumentMode.subproj/SKTTextArea.m
- (void)endEditingInView:(SKTGraphicView *)view {
    if ([view editingGraphic] == self) {
        NSTextView *editor = (NSTextView *)[view editorView];
        [editor setDelegate:nil];
        [editor removeFromSuperview];
        [[self contents] removeLayoutManager:[editor layoutManager]];
        if (editor == sharedEditor) {
            sharedEditorInUse = NO;
        } else {
            [[editor layoutManager] release];
        }
        [view setEditingGraphic:nil editorView:nil];
    }
}

順に追っていくと、

  • NSTextView の delegate を nil にしてやることで、SKTTextArea を delegate から外す
  • NSTextView を親の view から外す
  • SKTTextArea が持っている NSTextStorage から、NSLayoutManager を外す
  • NSLayoutManager を release してやる

と、いうわけ。さっきときれに逆でしょ。これで後片付けが終わって、我々には、編集された NSTextStorage だけが残るんだ。


Home | Link | Download | Back Number | Speciall Issue

Sketch BP

mailto: mkino@xd5.so-net.ne.jp

HMDT