280Slides in Objective-J
280Slides という Web サービスがすごい。
- Dru’s Blog » New Web Site in Objective-C via Javascript
- New 280 Slides Web Site translates Objective-C to Javascript | reddit
Keynote 風のプレゼンテーションツールとしての UI も素晴らしいが、圧巻はその実装技術。
NSView の CGContextRef に ATSUI でテキストを描画する方法
Cocoa の NSView に Apple Type Services for Unicode Imaging (ATSUI) でテキストを描画する方法を紹介する。
ATSUI では描画に Quartz の CGContextRef を設定することができる。CGContextRef は NSGraphicsContext の graphicsPort で取得できるので、以下のようなコードで設定できる。
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
ATSUAttributeTag tags[] = { kATSUCGContextTag };
ByteCount sizes[] = { sizeof(CGContextRef) };
ATSUAttributeValuePtr values[] = { &context };
ATSUSetLayoutControls(layout, 1, tags, sizes, values);
以下のコードでは、NSView の drawRect: で 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 の NSLayoutManager や NSTypesetter をサブクラス化したい場合、Apple のドキュメント "Assembling the Text System by Hand" にあるように、マニュアルで NSTextView を構築してもいいが面倒だ。それに、このままではスクロールビューでデコレートもされてないのでノーグッド。
どうせなら、Interface Builder で配置した NSTextView の NSLayoutManager を置き換えるのが手っ取り早いだろう。コードは以下のようになる。
- (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];
}肝になるのは NSLayoutManager の addTextContainer: で元々の NSTextContainer を追加したあとに、NSTextContainer の replaceLayoutManager: を呼ぶこと。これで NSLayoutManager を置き換えられる。