cocoa - NSTextView 中的硬包装

标签 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 通知上分割长行,然后将文本设置为连接的行,但它确实效率低下,如果在文档末尾以外的任何地方打字,就会丢失光标位置。

最后,我想我希望它表现为一个填充有空格的大页面,并且键入仅发生在覆盖模式下,但在行尾换行。我不知道从哪里开始这种方法。有什么建议吗?

最佳答案

我找到了一个可以接受的(即使不是完美的)解决方案,通过子类化 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];
}

关于cocoa - NSTextView 中的硬包装,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10957243/

相关文章:

html - ie7 中指定宽度的按钮自动换行?

swift - 替换 NSTextStorage 中的 NSAttributedString 移动 NSTextView 光标

objective-c - 行间距在 Core Text 中是如何工作的? (为什么它与 NSLayoutManager 不同?)

objective-c - 在 Objective-C 中不链接初始化器可以吗?

objective-c - 使用 Sparkle 更新后,应用程序不再处于事件状态

emacs - 如何删除 emacs 垂直边框内的换行符

cocoa - 如何禁用 NSTextView 的自动换行?

cocoa - 解码未知的 NSData

objective-c - 从 NSTableView 获取选定值

objective-c - 如何在 NSTextView 中获取特定字体文本的字符串?