我有一个不可编辑的
NSTextField
,我正在填充NSTextAttachment
s,每个都被设计成一个标记。我现在希望能够将鼠标悬停在令牌上并在令牌中获得一个取消按钮,这样我就可以通过按下按钮将其从列表中删除。
我在第一个障碍就倒下了,因为我不知道如何让鼠标跟踪工作。
NSTextAttachmentCellProtocol
(由 NSTextAttachmentCell
使用)声明 wantsToTrackMouse()
,我已经实现并返回了 true
。但是文档还声称默认实现返回true
,所以我不应该那样做。无论如何,永远不会调用此方法。我已经尝试了简单的实现和更复杂的实现。
我已经实施了
trackMouse(with:in:of:untilMouseUp:)
,但这也从未被调用过。
几年前的这个答案声称在实施
trackMouse
之前你不会得到hitTest
事件,所以我尝试使用一个超级简单的实现:
override func hitTest(for event: NSEvent, in cellFrame: NSRect, of controlView: NSView) -> NSCell.HitResult {
return NSCell.HitResult.trackableArea
}
这个方法也从未被调用过。
这是我的代码,为简单起见,删除了所有令牌样式:
class TokenCellTest: NSTextAttachmentCell {
static let titleMargin: CGFloat = 0
override func cellSize() -> NSSize {
let attribs = [NSAttributedString.Key.font: font ?? NSFont.systemFont(ofSize: 13)]
let titleSize = (stringValue as NSString).size(withAttributes: attribs)
return cellSize(forTitleSize: titleSize)
}
override func titleRect(forBounds rect: NSRect) -> NSRect {
var bounds = rect
bounds.size.width = max(bounds.size.width, TokenCellTest.titleMargin * 2 + bounds.size.height)
bounds = bounds.insetBy(dx: TokenCellTest.titleMargin + bounds.size.height / 2, dy: 0)
return bounds
}
override func draw(withFrame cellFrame: NSRect, in controlView: NSView?, characterIndex charIndex: Int) {
guard let controlView = controlView else { return }
if charIndex >= 0, let textView = controlView as? NSTextView {
for rangeValue in textView.selectedRanges {
let range = rangeValue.rangeValue
guard NSLocationInRange(charIndex, range) else { continue }
if textView.window?.isKeyWindow ?? false {
break
}
}
}
drawToken(with: cellFrame, in: controlView)
}
// MARK: - private methods
private func cellSize(forTitleSize titleSize: NSSize) -> NSSize {
var size = titleSize
size.height += 1
size.width += size.height + TokenCellTest.titleMargin * 2
let rect = NSRect(origin: .zero, size: size)
return NSIntegralRect(rect).size;
}
private func drawToken(with rect: NSRect, in view: NSView) {
NSGraphicsContext.current?.saveGraphicsState()
drawTitle(with: titleRect(forBounds: rect), in: view)
NSGraphicsContext.current?.restoreGraphicsState()
}
private func drawTitle(with rect: NSRect, in view: NSView) {
(stringValue as NSString).draw(in: rect)
}
// MARK: - Mouse tracking
override func trackMouse(with theEvent: NSEvent, in cellFrame: NSRect, of controlView: NSView?, untilMouseUp flag: Bool) -> Bool {
print("trackMouse")
return true
}
override func wantsToTrackMouse() -> Bool {
print("wantsToTrackMouse")
return true
}
override func hitTest(for event: NSEvent, in cellFrame: NSRect, of controlView: NSView) -> NSCell.HitResult {
print("hit test")
return NSCell.HitResult.trackableArea
}
}
将这些标记添加到文本字段的代码:
let attachment = NSTextAttachment()
let cell = TokenCellTest(textCell: "Blue")
attachment.attachmentCell = cell
let attStr = NSAttributedString(attachment:attachment)
field.attributedStringValue = attStr
这是代币的样子:
并且没有鼠标跟踪。 :-(
我正在使用这种方法,而不是
NSTokenField
,因为我想自己绘制标记,似乎在附件单元格中完成所有这些操作似乎更简单。事实上,那部分工作得很好。但是现在我想添加交互性,我不明白为什么没有调用附件单元格的鼠标处理方法。
最终,我期望需要在附件单元格内声明一个按钮单元格,并在 draw 方法中委托按钮单元格绘制。但我希望按钮只在悬停时显示,因此令牌需要知道鼠标何时在其范围内。我有一个模糊的想法,即点击事件需要传递给按钮单元格,但我也不确定这将如何工作。
如果有更好的方法可以在不可编辑的视图中创建交互式、自定义绘制的标记,我想我已经洗耳恭听了。 此代码 看起来很有前途,但无法在当前版本的 Xcode 上打开。无论如何,它不会触及令牌绘图本身。到目前为止,我从
OEXTokenAttachmentCell
中获得了很多灵感,但这也不适用于鼠标处理。