// // KMTextWithIconCell.swift // PDF Reader Pro // // Created by lizhe on 2024/2/21. // import Cocoa let SKTextWithIconStringKey = "string" let SKTextWithIconImageKey = "image" let BORDER_BETWEEN_EDGE_AND_IMAGE: CGFloat = 2.0 let BORDER_BETWEEN_IMAGE_AND_TEXT: CGFloat = 2.0 let IMAGE_OFFSET: CGFloat = 1.0 class KMTextWithIconCell: NSTextFieldCell { var imageCell: NSImageCell = NSImageCell() override init(textCell string: String) { super.init(textCell: string) commonInit() } required init(coder: NSCoder) { super.init(coder: coder) commonInit() } private func commonInit() { imageCell.imageScaling = .scaleProportionallyUpOrDown // if formatter == nil { // formatter = DictionaryFormatter(key: SKTextWithIconStringKey) // } } override func cellSize(forBounds rect: NSRect) -> NSSize { var cellSize = super.cellSize(forBounds: rect) cellSize.width += cellSize.height - 1 + BORDER_BETWEEN_EDGE_AND_IMAGE + BORDER_BETWEEN_IMAGE_AND_TEXT cellSize.width = min(cellSize.width, rect.width) return cellSize } private func textRect(forBounds rect: NSRect) -> NSRect { return KMShrinkRect(rect, NSHeight(rect) - 1 + BORDER_BETWEEN_EDGE_AND_IMAGE + BORDER_BETWEEN_IMAGE_AND_TEXT, .minX) } private func iconRect(forBounds rect: NSRect) -> NSRect { return KMSliceRect(KMShrinkRect(rect, BORDER_BETWEEN_EDGE_AND_IMAGE, .minX), NSHeight(rect) - 1, .minX) } override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) { // let super draw the text let textRect = textRect(forBounds: cellFrame) super.drawInterior(withFrame: textRect, in: controlView) // Draw the image var imageRect = iconRect(forBounds: cellFrame) imageRect = KMCenterRectVertically(rect: imageRect, height: NSWidth(imageRect), offset: IMAGE_OFFSET, flipped: controlView.isFlipped) imageCell.drawInterior(withFrame: imageRect, in: controlView) } override func edit(withFrame cellFrame: NSRect, in controlView: NSView, editor textObj: NSText, delegate anObject: Any?, event: NSEvent?) { super.edit(withFrame: textRect(forBounds: cellFrame), in: controlView, editor: textObj, delegate: anObject, event: event) } override func select(withFrame cellFrame: NSRect, in controlView: NSView, editor textObj: NSText, delegate anObject: Any?, start selStart: Int, length selLength: Int) { super.select(withFrame: textRect(forBounds: cellFrame), in: controlView, editor: textObj, delegate: anObject, start: selStart, length: selLength) } override func hitTest(for event: NSEvent, in rect: NSRect, of controlView: NSView) -> NSCell.HitResult { let textRect = textRect(forBounds: rect) let mouseLoc = event.location(inPDFListView: controlView) var hit: NSCell.HitResult = [] if NSMouseInRect(mouseLoc, textRect, controlView.isFlipped) { hit = super.hitTest(for: event, in: textRect, of: controlView) } else if NSMouseInRect(mouseLoc, iconRect(forBounds: rect), controlView.isFlipped) { hit = .contentArea } return hit } override var objectValue: Any? { didSet { if let obj = objectValue as? NSDictionary { // imageCell.image = obj[SKTextWithIconImageKey] as? NSImage self.stringValue = obj[SKTextWithIconStringKey] as! String } } } var icon: NSImage? { return imageCell.image } } extension KMTextWithIconCell { func KMSliceRect(_ rect: NSRect, _ amount: CGFloat, _ edge: NSRectEdge) -> NSRect { var rect = rect var ignored = NSRect.zero NSDivideRect(rect, &rect, &ignored, amount, edge); return rect } func KMShrinkRect(_ rect: NSRect, _ amount: CGFloat, _ edge: NSRectEdge) -> NSRect { var rect = rect var ignored = NSRect.zero NSDivideRect(rect, &ignored, &rect, amount, edge); return rect } func KMCenterRectVertically(rect: NSRect, height: CGFloat , offset: CGFloat, flipped: Bool) -> NSRect { var rect = rect rect.origin.y += 0.5 * (NSHeight(rect) - height); rect.origin.y = flipped ? ceil(rect.origin.y) - offset : floor(rect.origin.y) + offset; rect.size.height = height; return rect; } }