NSTextView: как отключить одиночные щелчки, но при этом разрешить выбор для копирования и вставки?

У меня есть компонент на основе NSTextView, и я хотел бы отключить одиночные клики по нему, чтобы на его точку вставки не влияли эти одиночные клики, но все же иметь возможность выбирать фрагменты текста для работы копирования и вставки:

  1. одиночные клики ничего не делают
  2. копирование и вставка возможны и не влияют на точку вставки

То, что я хочу, это именно то, что у нас есть в приложении Терминал по умолчанию: есть точка вставки, и ее нельзя изменить с помощью мыши, но все же возможен выбор текста для копирования и вставки.

Я пробовал смотреть на метод - (void)mouseDown:(NSEvent *)theEvent, но не нашел ничего полезного.


person Stanislav Pankevich    schedule 21.03.2016    source источник
comment
Будет ли достаточно редактируемого = НЕТ, выбираемого = ДА? или нужно что-то другое? можешь уточнить?   -  person Remizorrr    schedule 21.03.2016
comment
Этого может быть достаточно, учитывая, что insertText:replacementRange: будет работать с editable = NO, но не добавляет никакого текста.   -  person Stanislav Pankevich    schedule 21.03.2016
comment
@StanislavPankevich может быть просто установлен для редактирования на YES прямо перед вставкой, а затем вернуться к NO?   -  person m8labs    schedule 21.03.2016


Ответы (1)


Я нашел хакерский обходной путь для достижения такого поведения. Я создал демонстрационный проект, соответствующий класс есть TerminalLikeTextView. Это решение работает отлично, но я все же хотел бы иметь лучшее решение: менее хакерское и менее зависимое от внутренней механики NSTextView, поэтому, если у кого-то есть такое, поделитесь.

Ключевые шаги:

1) Установите для mouseDownFlag значение YES до нажатия кнопки мыши и значение NO после:

@property (assign, nonatomic) BOOL mouseDownFlag;

- (void)mouseDown:(NSEvent *)theEvent {
    self.mouseDownFlag = YES;

    [super mouseDown:theEvent];

    self.mouseDownFlag = NO;
}

2) Чтобы точка вставки не обновляла возврат раньше из метода updateInsertionPointStateAndRestartTimer:

- (void)updateInsertionPointStateAndRestartTimer:(BOOL)flag {
    if (self.mouseDownFlag) {
        return;
    }

    [super updateInsertionPointStateAndRestartTimer:flag];
}

3) Первые два шага заставят точку вставки не перемещаться с помощью мыши, однако selectionRange все равно будет изменено, поэтому нам нужно следить за этим:

static const NSUInteger kCursorLocationSnapshotNotExists = NSUIntegerMax;
@property (assign, nonatomic) NSUInteger cursorLocationSnapshot;

#pragma mark - <NSTextViewDelegate>

- (NSRange)textView:(NSTextView *)textView willChangeSelectionFromCharacterRange:(NSRange)oldSelectedCharRange toCharacterRange:(NSRange)newSelectedCharRange {

    if (self.mouseDownFlag && self.cursorLocationSnapshot == kCursorLocationSnapshotNotExists) {
        self.cursorLocationSnapshot = oldSelectedCharRange.location;
    }

    return newSelectedCharRange;
}

4) Попытка печати с помощью ключей восстанавливает местоположение, если это необходимо:

- (void)keyDown:(NSEvent *)event {
    NSString *characters = event.characters;

    [self insertTextToCurrentPosition:characters];
}

- (void)insertTextToCurrentPosition:(NSString *)text {
    if (self.cursorLocationSnapshot != kCursorLocationSnapshotNotExists) {
        self.selectedRange = NSMakeRange(self.cursorLocationSnapshot, 0);
        self.cursorLocationSnapshot = kCursorLocationSnapshotNotExists;
    }

    [self insertText:text replacementRange:NSMakeRange(self.selectedRange.location, 0)];
}
person Stanislav Pankevich    schedule 22.03.2016