Cocoa で独自の Text View を実装する
iPhone も盛り上がっていることだし、ひさしぶりに Cocoa プログラミングを始めてみる。
Mac Dev Center をつらつらと眺めてみると、Managing Concurrency with NSOperation という記事に興味が湧く。NSOperation と NSOperationQueue(優先順位付きキュー)を使うことで、タスクのスケジューリングと並行処理が手軽に行えるようになったようだ。Java の concurrency パッケージに感銘を受けた身としては、こういう拡張は嬉しい。今後、マルチスレッドが必要になったときに、改めて詳しく調べてみよう。
テキストシステム周りも、ひところに比べると充実してきている。Mac OS X 10.3 では NSATSTypesetter が公開された。更に Mac OS X 10.5 では NSTextInputClient プロトコルと Input Method Kit が追加されている(他にもあるかも)。
さて、Cocoa プログラミングのリハビリとして、今回は独自の Text View を実装してみようと思う。
ここでいう Text View とは NSTextView のように、IM からの入力を受け取り、テキストを表示、編集できるクラスのことだ。エディタやワープロといったアプリケーションを開発するときには必要になってくるだろう。
NSView のサブクラス
ドキュメントによると、独自の Text View を実装する場合は NSTextView のサブクラスか、NSView のサブクラスを作るようなので、今回は NSView のサブクラスとして作成することにする。
NSView
のサブクラスを作るとして、名前は単純に MyTextView
としよう。まずは MyTextView.h
で、クラスの @interface
を書く。
#import <Cocoa/Cocoa.h>
@interface MyTextView : NSView {
NSMutableAttributedString *_text;
}
@end
インスタンス変数として NSMutableAttributedString をひとつ持つ。ここに表示、編集するテキストの内容を保持するわけだ(MVC も糞もない設計だが、今回のはあくまで例なので単純さを優先する)。
First Responder
最初にすることは、このクラスがキー入力を受け取れるようにすることだ。そのためには、キー入力やマウスクリックなどのイベントを受け取るれるように、acceptsFirstResponder をオーバーライドする必要がある。
- (BOOL) acceptsFirstResponder {
return YES;
}
さて、これでキー入力イベントを受け取れるようになったので、今度は keyDown: を実装しよう。
- (void) keyDown: (NSEvent *) theEvent {
[self interpretKeyEvents: [NSArray arrayWithObject: theEvent]];
}
これだけ。そのまま interpretKeyEvents: に投げている。
interpretKeyEvents:
interpretKeyEvents:
は NSInputManager を通して、最終的に、ビューの insertText: や insertNewline: などを呼び出すので、あとはこれらのメソッドを実装すればいい。
今回は改行、タブ、削除、通常のテキスト入力など、最低限必要なメソッドだけを実装している。
- (void) deleteBackward: (id) sender {
const NSUInteger length = [_text length];
if (length > 0) {
[_text deleteCharactersInRange: NSMakeRange(length - 1, 1)];
[self setNeedsDisplay: YES];
}
}
- (void) insertNewline: (id) sender {
[self insertText: @"\n"];
}
- (void) insertTab: (id) sender {
[self insertText: @"\t"];
}
- (void) insertText: (id) aString {
[[_text mutableString] appendString: aString];
[_text addAttribute: NSFontAttributeName
value: [NSFont userFontOfSize: 18.0f]
range: NSMakeRange(0, [_text length])];
[self setNeedsDisplay: YES];
}
なお、insertText:
に渡される aString
は NSString
とは限らず、NSAttributedString
の可能性もあるので、本来はそれも考慮したコードにするべきだ。
テキストの表示
最後に、描画部分を実装しよう。テキストのレイアウトと描画は NSAttributedString
の drawInRect: に任せてしまう。
- (void) drawRect: (NSRect) theRect {
[[NSColor whiteColor] set];
NSFrameRect(theRect);
NSRectFill(theRect);
[[NSColor grayColor] set];
NSFrameRect(theRect);
[_text drawInRect: NSMakeRect(
theRect.origin.x + 5.0f, theRect.origin.y,
theRect.size.width, theRect.size.height)];
}
以上で、簡単ではあるが、テキストの入力と削除が可能な Text View が出来た。
日本語変換、入力など、更に複雑な操作をサポートするためには、NSTextInput や、それを拡張した NSTextInputClient を実装する必要がある。更に、独自のレイアウトも実装することになるだろう。実用的な Text View を開発するのはかなりの労力がいりそうだ。
なお、今回のソースコードは gist:23017 に置いてある。