ルーム情報 : "droom" (291件)
最近更新されたメモ一覧 :
docs
被リンクメモ一覧 : (1件)
[Tips]
[Home] [List] [Recent] [Orphan] [Help] [Edit] [Remove]

Objective-C : NSText & NSTextView

ユーザの入力した改行を\nに変換

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をそのまま通すようにしてある。

NSTextのフック

- (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

縦書きできるTextViewが公開されている。

[Home] [List] [Recent] [Orphan] [Help] [Edit] [Remove]
-->