I found a hacky workaround to achieve this behavior. I created the corresponding TerminalLikeTextView class. This solution works fine, but I would still like to have a better solution: less hacker and less dependent on the internal mechanics of NSTextView, so if anyone has such a request, please share.
Key steps are:
1) Set mouseDownFlag to YES before mouse click and NO after:
@property (assign, nonatomic) BOOL mouseDownFlag; - (void)mouseDown:(NSEvent *)theEvent { self.mouseDownFlag = YES; [super mouseDown:theEvent]; self.mouseDownFlag = NO; }
2) To prevent the insertion point from being added early from the updateInsertionPointStateAndRestartTimer method:
- (void)updateInsertionPointStateAndRestartTimer:(BOOL)flag { if (self.mouseDownFlag) { return; } [super updateInsertionPointStateAndRestartTimer:flag]; }
3) The first two steps will make the input point not move with the mouse, however the selectionRange will still change, so we need to track 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) Attempting to print using the keys restores the location if necessary:
- (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)]; }
source share