如何在 NSTextAttachmentCell 上获取鼠标跟踪和单击事件?

问题描述 投票:0回答:0

我有一个不可编辑的

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
中获得了很多灵感,但这也不适用于鼠标处理。

swift cocoa nsattributedstring nstextattachment nstokenfield
© www.soinside.com 2019 - 2024. All rights reserved.