Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSTextView: how to disable single clicks but still allow selection for copy-and-paste?

I have NSTextView-based component and I would like to disable single clicks on it, so that its insertion point is not affected by these single clicks, but still to be able to select pieces of text for copy-and-paste work:

  1. single clicks do nothing
  2. copy-and-paste is possible and does not affect insertion point

What I want is exactly what we have in default Terminal app: there is insertion point and it is not possible to change it via mouse click, but still selection of text for copy-and-paste is possible.

I have tried looking at - (void)mouseDown:(NSEvent *)theEvent method but didn't find anything helpful.

like image 481
Stanislav Pankevich Avatar asked Nov 09 '22 18:11

Stanislav Pankevich


1 Answers

I have found hacky workaround to achieve this kind of behavior. I have created demo project, the relevant class there is TerminalLikeTextView. This solution works perfectly but I would still like to have a better solution: less hacky and less dependent on internal mechanics of NSTextView so if anyone has such please share.

The key steps are:

1) Set mouseDownFlag to YES before mouse down and to NO after:

@property (assign, nonatomic) BOOL mouseDownFlag;

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

    [super mouseDown:theEvent];

    self.mouseDownFlag = NO;
}

2) To prevent insertion point from updating return early from updateInsertionPointStateAndRestartTimer method:

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

    [super updateInsertionPointStateAndRestartTimer:flag];
}

3) First two steps will make insertion point to not move with mouse however the selectionRange will still be changed so we need to keep track of it:

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) Attempt to print using keys restores location if needed:

- (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)];
}
like image 131
Stanislav Pankevich Avatar answered Nov 26 '22 07:11

Stanislav Pankevich