index, AV, AppleScript, Bicycle, Cinema, DTM, Dairy, Dev, Fashion, Fitness, Game, Health, Help, Lyrics, Meal, Motor, Motorcycle, Music, Objective-C, PC, PDA, Phone, Robot, S15S, Stationary, Swift, Text, Travel, V36, Watch
NSStringには複数の改行コードが混在可能だが、敢てユーザの入力するCR(\r)をLF(\n)に変換する。こうすることで、キーボードから入力した改行がコピー&ペーストやドラッグ&ドロップなどによって挿入される改行コード(\n)と同じになるため、内部処理が簡略化できる。また、複数の改行コードを扱う場合に生じるNSTextViewの不具合も回避される。
方法としては、NSTextViewのサブクラスを作成し、keyDown:メソッドをオーバーライドすることで実装する。ここでは、コードの再利用を考えて、NSEventに新しいカテゴリを導入してイベントの変換も行なっている。
// KEventUtils.h @interface NSEvent (Additions) - (NSEvent*)copyKeyEventWithCharacters:(NSString*)characters; @end // KEventUtils.m @implementation NSEvent (Additions) - (NSEvent*)copyKeyEventWithCharacters:(NSString*)characters; { return [NSEvent keyEventWithType:[self type] location:[self locationInWindow] modifierFlags:[self modifierFlags] timestamp:[self timestamp] windowNumber:[self windowNumber] context:[self context] characters:characters charactersIgnoringModifiers:characters isARepeat:[self isARepeat] keyCode:[self keyCode] ]; } @end
本当は、NSEventインスタンスそのものを直接編集できると便利なのだが… どうやらそういう機能はないらしい。
NSTextViewへのキーイベントは、NSViewのサブクラスのためkeyDown:に送られる。これをフックして、内容が\rの場合のみ\nになるよう変換してしまえばいい。
// KTextView.m - (void)keyDown:(NSEvent*)theEvent { if ([[theEvent characters] isEqualTo:@"\r"] && ![self hasMarkedText]){ [super keyDown:[theEvent copyKeyEventWithCharacters:@"\n"]]; return; } [super keyDown:theEvent]; }
keyDownはインプットメソッド(IM)より前の段階でキーイベントをフックしているため、全ての\rを\nに変換してしまうとIMが上手く機能しなくなる。リターンのプレスが問題になるのは文字の変換中に限られるため、hasMarkedTextがYESの場合のみ\rをそのまま通すようにしてある。
- (void)replaceCharactersInRange:(NSRange)aRange withRTF:(NSData*)rtfData; - (void)replaceCharactersInRange:(NSRange)aRange withRTFD:(NSData*)rtfdData; - (void)replaceCharactersInRange:(NSRange)aRange withString:(NSString*)aString;
の3つのメソッドをオーバーライドして調べてみたところ、欧文テキストの入力ではいずれも呼ばれないようだ。しかし、日本語入力でMarkedなテキストを入力する場合にはreplaceCharactersInRange:withString:が呼ばれる。
また、外部からsetString:をコールされた時にも、replaceCharactersInRange:withString:が呼ばれるようだ。
縦書きできるTextViewが公開されている。