如果NSTextView
包含以下内容:
SELECT someTable.someColumn FROM someTable
用户双击someTable.someColumn
,整个内容都会被选中(期间的两边)。在这种特定情况下(查询编辑器),选择someTable
或someColumn
会更有意义。
我试过四处看看是否可以找到一种方法来自定义选择,但到目前为止我还没有。
目前我正在考虑做的是继承NSTextView
并执行以下操作:
- (void)mouseDown:(NSEvent *)theEvent
{
if(theEvent.clickCount == 2)
{
// TODO: Handle double click selection.
}
else
{
[super mouseDown:theEvent];
}
}
有没有人对此有任何想法或替代方案? (我还缺少另一种可能更适合覆盖的方法吗?)
答案 0 :(得分:1)
在NSTextView
的子类中,您应该覆盖-selectionRangeForProposedRange:granularity:
,例如:
-(NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange granularity:(NSSelectionGranularity)granularity
{
if (granularity == NSSelectByWord)
{
NSRange doubleRange = [[self textStorage] doubleClickAtIndex:proposedSelRange.location];
if (doubleRange.location != NSNotFound)
{
NSRange dotRange = [[[self textStorage] string] rangeOfString:@"." options:0 range:doubleRange];
if (dotRange.location != NSNotFound)
{
// double click after '.' ?
if (dotRange.location < proposedSelRange.location)
return NSMakeRange(dotRange.location + 1, doubleRange.length - (dotRange.location-doubleRange.location) - 1);
else
return NSMakeRange(doubleRange.location, dotRange.location-doubleRange.location);
}
}
}
return [super selectionRangeForProposedRange:proposedSelRange granularity:granularity];
}
答案 1 :(得分:0)
这是@bhaller代码在Swift 5中的自定义实现,非常感谢!
请注意,由于内存效率的原因,它不使用string
或NSMutableAttributedString
,最好使用另一个NSTextStorage
。更多信息here
final class MyTextStorage: NSTextStorage {
private var storage = NSTextStorage()
// MARK: - Required overrides for NSTextStorage
override var string: String {
return storage.string
}
override func attributes(at location: Int, effectiveRange range: NSRangePointer?) -> [NSAttributedString.Key : Any] {
return storage.attributes(at: location, effectiveRange: range)
}
override func replaceCharacters(in range: NSRange, with str: String) {
beginEditing()
storage.replaceCharacters(in: range, with: str)
edited(.editedCharacters, range: range, changeInLength: (str as NSString).length - range.length)
endEditing()
}
override func setAttributes(_ attrs: [NSAttributedString.Key : Any]?, range: NSRange) {
beginEditing()
storage.setAttributes(attrs, range: range)
edited(.editedAttributes, range: range, changeInLength: 0)
endEditing()
}
// MARK: - DOuble click functionality
override func doubleClick(at location: Int) -> NSRange {
// Call super to get location of the double click
var range = super.doubleClick(at: location)
let stringCopy = self.string
// If the user double-clicked a period, just return the range of the period
let locationIndex = stringCopy.index(stringCopy.startIndex, offsetBy: location)
guard stringCopy[locationIndex] != "." else {
return NSMakeRange(location, 1)
}
// The case where super's behavior is wrong involves the dot operator; x.y should not be considered a word.
// So we check for a period before or after the anchor position, and trim away the periods and everything
// past them on both sides. This will correctly handle longer sequences like foo.bar.baz.is.a.test.
let candidateRangeBeforeLocation = NSMakeRange(range.location, location - range.location)
let candidateRangeAfterLocation = NSMakeRange(location + 1, NSMaxRange(range) - (location + 1))
let periodBeforeRange = (stringCopy as NSString).range(of: ".", options: .backwards, range: candidateRangeBeforeLocation)
let periodAfterRange = (stringCopy as NSString).range(of: ".", options: [], range: candidateRangeAfterLocation)
if periodBeforeRange.location != NSNotFound {
// Change range to start after the preceding period; fix its length so its end remains unchanged
range.length -= (periodBeforeRange.location + 1 - range.location)
range.location = periodBeforeRange.location + 1
}
if periodAfterRange.location != NSNotFound {
// Change range to end before the following period
range.length -= (NSMaxRange(range) - periodAfterRange.location);
}
return range
}
}