在NSTextView中进行硬包装

时间:2012-06-08 23:47:22

标签: cocoa word-wrap nstextview

我正在尝试创建一个行为类似于一系列打字机页面的NSTextView - 每个页面都有固定数量的行和列可用。

底层表示理想地匹配显示,而显示又匹配文件格式。页面以换页符'\f'结尾。例如,两个4x4页面在保存时看起来像这样(字符之间的空格以便于阅读):

T  H  I  S  \n
P  A  G  E  \n
I  S  \n
F  U  L  L  \n
\f
N  O  T  \n
T  H  I  S  \n
O  N  E  \n
\n
\f

需要注意的一些事项:

  • 新行和换页符不计入行/列总数
  • 短于列数的行不会用尾随空格填充(尽管编辑器在覆盖模式下工作时可能会这样)
  • 比行数短的页面用换行符填充

我尝试了一些方法,比如将文本转换为NSArray行并在每个textDidChange通知上拆分长行,然后将文本设置为连接行,但这样做效率很低,如果键入发生在除文档末尾之外的任何地方,则丢失光标位置。

最后,我认为我希望这个行为是一个填充了空白的大页面,并且只能在覆盖模式下进行打字,但会在行尾包装。我不知道从哪里开始这种方法。有什么建议吗?

1 个答案:

答案 0 :(得分:0)

通过子类化NSTextStorage,我找到了一个可接受的(如果不是完美的)解包方案。

在将文本添加到文档末尾(无论是粘贴还是键入)时,它的行为完全符合预期。它在删除或替换文本时也有效。唯一有点奇怪的地方是将文本插入已经完整的行的中间。这将导致在插入文本之前换行。它工作正常,但这意味着继续在该位置键入将产生许多短线。

例如,下面的文字包含20列。原始文字为x,插入的文字为o。插入的文本从第二行开始,第8个字符。

xxxxxxxxxxxxxxxxxxxx
xxxxxxx
ooooooo
ooooooo
oooooooxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx

以下代码:

- (void)breakLinesInArray:(NSMutableArray *)lines atLength:(NSUInteger)length
charsPreceding:(NSUInteger)preceding charsFollowing:(NSUInteger)following
{
    NSUInteger line = 0;
    while (line < [lines count]) {
        NSUInteger maxLineLength = length;
        if (line == 0) {
            maxLineLength = length - preceding;
        } else if (line == [lines count] - 1) {
            maxLineLength = length - following;
        }
        NSString *original = [lines objectAtIndex:line];
        if (maxLineLength > 0 && [original length] > maxLineLength) {
            NSString *first = [original substringToIndex:maxLineLength];
            NSString *second = [original substringFromIndex:maxLineLength];
            [lines replaceObjectAtIndex:line withObject:first];
            [lines insertObject:second atIndex:line + 1];
        }
        ++line;
    }
}

- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str
{
    static const NSUInteger kMaxLineLength = 20;
    NSString *text = [text_ string];
    NSUInteger firstLineStart = 0;
    NSUInteger lastLineEnd = 0;
    NSMutableArray *lines = [NSMutableArray arrayWithArray:[str componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]];
    [text getLineStart:&firstLineStart end:nil contentsEnd:&lastLineEnd forRange:range];
    NSUInteger firstLineStartFragmentLength = range.location - firstLineStart;
    NSUInteger lastLineEndFragmentLength = lastLineEnd - (range.location + range.length);
    [self breakLinesInArray:lines atLength:kMaxLineLength charsPreceding:firstLineStartFragmentLength charsFollowing:lastLineEndFragmentLength];
    NSString *newString = [lines componentsJoinedByString:@"\n"];
    NSInteger changeInLength = [newString length] - range.length;
    NSInteger newLastLineLength = firstLineStartFragmentLength + lastLineEndFragmentLength + changeInLength;
    if ((firstLineStartFragmentLength || lastLineEndFragmentLength) && newLastLineLength > (NSInteger)kMaxLineLength) {
        newString = [@"\n" stringByAppendingString:newString];
        ++changeInLength;
    }
    [text_ replaceCharactersInRange:range withString:newString];
    [self edited:NSTextStorageEditedCharacters range:range changeInLength:changeInLength];
}