Cocoa で独自の Text View を実装する

iPhone も盛り上がっていることだし、ひさしぶりに Cocoa プログラミングを始めてみる。

280Slides in Objective-J

280Slides という Web サービスがすごい。

Keynote 風のプレゼンテーションツールとしての UI も素晴らしいが、圧巻はその実装技術。

Xcode でプロジェクトの名前を変更したくなったら

Xcode でプロジェクトの名前を変えたくなった。

だが、簡単にはいかない。変更箇所が散在している。面倒くさい。以下に手順をまとめてみる。

NSView の CGContextRef に ATSUI でテキストを描画する方法

Cocoa の NSViewApple Type Services for Unicode Imaging (ATSUI) でテキストを描画する方法を紹介する。

ATSUI では描画に Quartz の CGContextRef を設定することができる。CGContextRefNSGraphicsContextgraphicsPort で取得できるので、以下のようなコードで設定できる。

CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
ATSUAttributeTag tags[] = { kATSUCGContextTag };
ByteCount sizes[] = { sizeof(CGContextRef) };
ATSUAttributeValuePtr values[] = { &context };
ATSUSetLayoutControls(layout, 1, tags, sizes, values);

以下のコードでは、NSViewdrawRect: で Hello, World を描画している。

- (void) drawRect : (NSRect) rect
{
  NSString *s = @"Hello, World!";
  UniChar *text;
  UniCharCount length;
  ATSUStyle style;
  ATSUTextLayout layout;

  // Setup
  ATSUCreateStyle(&style);
  ATSUCreateTextLayout(&layout);

  length = [s length];
  text = (UniChar *)malloc(length * sizeof(UniChar));
  [s getCharacters:text];

  ATSUSetTextPointerLocation(
    layout,
    text,
    kATSUFromTextBeginning,
    kATSUToTextEnd,
    length);
  ATSUSetRunStyle(
    layout,
    style,
    kATSUFromTextBeginning,
    kATSUToTextEnd
  );

  // CGContext
  CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
  ATSUAttributeTag tags[] = { kATSUCGContextTag };
  ByteCount sizes[] = { sizeof(CGContextRef) };
  ATSUAttributeValuePtr values[] = { &context };
  ATSUSetLayoutControls(layout, 1, tags, sizes, values);

  // Drawing
  ATSUDrawText(
    layout,
    kATSUFromTextBeginning,
    kATSUToTextEnd,
    X2Fix(10.0f),
    X2Fix(10.0f));

  free(text);
  ATSUDisposeStyle(style);
  ATSUDisposeTextLayout(layout);
}

Interface Builder の NSTextView で NSLayoutManager を置き換える

Cocoa の NSLayoutManagerNSTypesetter をサブクラス化したい場合、Apple のドキュメント "Assembling the Text System by Hand" にあるように、マニュアルで NSTextView を構築してもいいが面倒だ。それに、このままではスクロールビューでデコレートもされてないのでノーグッド。

どうせなら、Interface Builder で配置した NSTextViewNSLayoutManager を置き換えるのが手っ取り早いだろう。コードは以下のようになる。

- (void) awakeFromNib {
  _textStorage = [[NSTextStorage alloc] initWithString:@"Cocoa Programming Topics"];

  NSLayoutManager *layoutManager = [[MyLayoutManager alloc] init];
  [_textStorage addLayoutManager:layoutManager];
  [layoutManager release];

  NSTextContainer *textContainer = [_textView textContainer];
  [layoutManager addTextContainer:textContainer];
  [textContainer replaceLayoutManager:layoutManager];
}

肝になるのは NSLayoutManageraddTextContainer: で元々の NSTextContainer を追加したあとに、NSTextContainerreplaceLayoutManager: を呼ぶこと。これで NSLayoutManager を置き換えられる。

Want fries with that?

Open Source Projects