NSTextView:如何禁用单击但仍允许选择复制和粘贴?
NSTextView: how to disable single clicks but still allow selection for copy-and-paste?
我有基于 NSTextView 的组件,我想禁用对它的单击,这样它的插入点就不会受到这些单击的影响,但仍然能够 select 段文本复制粘贴作业:
- 单击什么都不做
- 可以复制粘贴,不影响插入点
我想要的正是我们在默认终端应用程序中所拥有的:有插入点,无法通过鼠标单击来更改它,但仍然有 selection 文本用于复制和粘贴有可能。
我已经尝试查看 - (void)mouseDown:(NSEvent *)theEvent
方法,但没有找到任何有用的信息。
我找到了实现这种行为的 hacky 解决方法。我创建了 demo project, the relevant class there is TerminalLikeTextView。这个解决方案完美运行,但我仍然希望有一个更好的解决方案:更少的 hacky 和更少依赖 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
方法提前更新 return:
- (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)];
}
我有基于 NSTextView 的组件,我想禁用对它的单击,这样它的插入点就不会受到这些单击的影响,但仍然能够 select 段文本复制粘贴作业:
- 单击什么都不做
- 可以复制粘贴,不影响插入点
我想要的正是我们在默认终端应用程序中所拥有的:有插入点,无法通过鼠标单击来更改它,但仍然有 selection 文本用于复制和粘贴有可能。
我已经尝试查看 - (void)mouseDown:(NSEvent *)theEvent
方法,但没有找到任何有用的信息。
我找到了实现这种行为的 hacky 解决方法。我创建了 demo project, the relevant class there is TerminalLikeTextView。这个解决方案完美运行,但我仍然希望有一个更好的解决方案:更少的 hacky 和更少依赖 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
方法提前更新 return:
- (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)];
}