【问题标题】:How to make NSTextView balance delimiters with a double-click?如何通过双击使 NSTextView 平衡分隔符?
【发布时间】:2015-12-16 21:36:02
【问题描述】:

通常有一个文本编辑器来处理代码或其他结构化内容,以平衡某种分隔符;当您双击 { 它会选择匹配的 } 或类似地选择 ( ) 对、[ ] 对等。如何在 Cocoa/Obj-C 中的 NSTextView 中实现此行为?

(我将立即发布答案,因为我在 SO 上没有发现任何关于此的内容,所以今天花了很多时间实施解决方案。欢迎提供更好的答案。)

附录:

这与this question 不同,后者是关于NSTextField,主要关注NSTextField 和字段编辑器问题。 如果通过将自定义NSTextView 子类替换为字段编辑器来解决该问题,那么该自定义子类当然可以使用此处给出的解决方案;但是可能有许多其他方法可以解决NSTextField 的问题,并且将自定义NSTextView 子类替换到字段编辑器中显然不是解决该问题的正确方法,无论如何,程序员关心@ 中的分隔符平衡987654331@(这可能是更常见的问题)可能不太关心所有这些NSTextField 和字段编辑器问题。所以这是一个不同的问题——尽管我会在这个问题上添加一个链接,作为它可能的一个方向。

这也和this question不一样,这实际上是在双击发生时改变NSTextView中“单词”的定义。根据Apple's documentation,这些是不同解决方案的不同问题;对于分隔符平衡(这个问题)Apple 特别推荐使用NSTextView 的selectionRangeForProposedRange:granularity: 方法,而对于更改单词的定义(那个问题)Apple 特别声明selectionRangeForProposedRange:granularity: 方法不应该 被使用。

【问题讨论】:

  • 您还向另一个重复的问题发布了 answer,所以我不确定您为什么要发布此问题。
  • @KenThomases,这些不是重复的。请仔细看。您标记为与此问题重复的问题是关于 NSTextField,而不是 NSTextView,主要涉及处理字段编辑器问题(与 NSTextField 一样)。那是一个不同的班级,有不同的问题。查看下面发布的答案,它使用了 Apple 为 NSTextView 专门推荐的 selectionRangeForProposedRange:granularity:。 NSTextField 问题不能使用(至少不能直接使用),因为这不是 NSTextField 方法。
  • 您在上面的评论中作为重复项提到的另一个问题也不是重复项。另一个问题是关于自定义双击选择以遵守不同的单词边界;特别是在所有情况下都将句点视为单词分隔符。根据 Apple 的文档,正如在两个答案中仔细审查的那样,这是一个不同的问题,需要不同的解决方案; Apple 声明 selectionRangeForProposedRange:granularity: 应该专门不用于解决该问题(但应该用于分隔符平衡)。
  • @KenThomases 我已经编辑了问题以使这些区别更加清晰。如果您同意,那么我们可能可以删除所有这些 cmets;我不确定 SO 上的礼仪是什么。
  • 我重新打开了它。我不确定我是否同意它确实与第一个完全不同,尽管您链接了文档,但在我看来 -selectionRangeForProposedRange:granularity: 不适合这项任务,因为您必须通过体操确定是否需要双击。但是,无论如何。

标签: objective-c macos cocoa nstextview


【解决方案1】:

在他们的 Cocoa 文本架构指南 (https://developer.apple.com/library/prerelease/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TextEditing/TextEditing.html) 中,Apple 建议子类化 NSTextView 并覆盖 selectionRangeForProposedRange:granularity: 来实现这种事情;他们甚至说“例如,在代码编辑器中,您可以提供一个委托,将双击大括号或括号字符扩展到其匹配的分隔符。”但是,目前尚不清楚如何实现这一点,因为您希望分隔符匹配仅在简单双击分隔符后发生,而不是在双击拖动甚至双击按住释放后发生.

我能想出的最佳解决方案还包括覆盖mouseDown:,并对事态进行一些记录。也许有更简单的方法。我省略了实际计算分隔符匹配的代码的核心部分;这将取决于您匹配的分隔符、可能存在的语法复杂性(字符串、cmets)等等。在我的代码中,我实际上调用了一个标记器来获取一个标记流,并使用它来查找匹配的分隔符。 YMMV。所以,这就是我得到的:

在您的 NSTextView 子类接口(或类扩展,更好)中:

// these are used in selectionRangeForProposedRange:granularity:
// to balance delimiters properly
BOOL inEligibleDoubleClick;
NSTimeInterval doubleDownTime;

在您的 NSTextView 子类实现中:

- (void)mouseDown:(NSEvent *)theEvent
{
    // Start out willing to work with a double-click for delimiter-balancing;
    // see selectionRangeForProposedRange:proposedCharRange granularity: below
    inEligibleDoubleClick = YES;

    [super mouseDown:theEvent];
}

- (NSRange)selectionRangeForProposedRange:(NSRange)proposedCharRange
    granularity:(NSSelectionGranularity)granularity
{
    if ((granularity == NSSelectByWord) && inEligibleDoubleClick)
    {
        // The proposed range has to be zero-length to qualify
        if (proposedCharRange.length == 0)
        {
            NSEvent *event = [NSApp currentEvent];
            NSEventType eventType = [event type];
            NSTimeInterval eventTime = [event timestamp];

            if (eventType == NSLeftMouseDown)
            {
                // This is the mouseDown of the double-click; we do not want
                // to modify the selection here, just log the time
                doubleDownTime = eventTime;
            }
            else if (eventType == NSLeftMouseUp)
            {
                // After the double-click interval since the second mouseDown,
                // the mouseUp is no longer eligible
                if (eventTime - doubleDownTime <= [NSEvent doubleClickInterval])
                {
                    NSString *scriptString = [[self textStorage] string];

                    ...insert delimiter-finding code here...
                    ...return the matched range, or NSBeep()...
                }
                else
                {
                    inEligibleDoubleClick = false;
                }
            }
            else
            {
                inEligibleDoubleClick = false;
            }
        }
        else
        {
            inEligibleDoubleClick = false;
        }
    }

    return [super selectionRangeForProposedRange:proposedCharRange
        granularity:granularity];
}

它有点脆弱,因为它依赖于NSTextView 的跟踪以特定方式工作并以特定方式调用selectionRangeForProposedRange:granularity:,但假设并不大;我想它非常强大。

【讨论】:

    猜你喜欢
    • 2013-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 2011-12-19
    • 2021-12-12
    • 2012-12-01
    相关资源
    最近更新 更多