// // KMNoteOutlineView.swift // PDF Reader Pro // // Created by tangchao on 2023/11/17. // import Cocoa protocol KMNoteOutlineViewDelegate: KMCustomOutlineViewDelegate { func outlineView(_ anOutlineView: NSOutlineView, canResizeRowByItem item: AnyObject?) -> Bool? func outlineView(_ anOutlineView: NSOutlineView, setHeight newHeight: CGFloat, ofRowByItem item: AnyObject?) func outlineView(_ anOutlineView: NSOutlineView, didChangeHiddenOfTableColumn aTableColumn: NSTableColumn) func outlineViewCommandKeyPressedDuringNavigation(_ anOutlineView: NSOutlineView) } class KMNoteOutlineView: KMCustomOutlineView { weak var noteDelegate: KMNoteOutlineViewDelegate? private let NUMBER_OF_TYPES = 9 private let PAGE_COLUMNID = "page" private let NOTE_COLUMNID = "note" private let TYPE_COLUMNID = "type" private let COLOR_COLUMNID = "color" private let AUTHOR_COLUMNID = "author" private let DATE_COLUMNID = "date" private let SMALL_COLUMN_WIDTH = 32.0 private let RESIZE_EDGE_HEIGHT = 5.0 override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) // Drawing code here. } required init?(coder: NSCoder) { super.init(coder: coder) var menu = NSMenu() for tc in self.tableColumns { let identifier = tc.identifier.rawValue let title = self._titleForTableColumnIdentifier(identifier) let menuItem = menu.addItem(title: title ?? "", action: #selector(toggleTableColumn), target: self) menuItem?.representedObject = identifier if tc.maxWidth >= SMALL_COLUMN_WIDTH { tc.headerCell.title = title ?? "" } } self.headerView?.menu = menu } override func drawRow(_ row: Int, clipRect: NSRect) { super.drawRow(row, clipRect: clipRect) if let can = self.noteDelegate?.outlineView(self, canResizeRowByItem: self.item(atRow: row) as? AnyObject), can { var isHighlighted = false if let win = self.window, win.isKeyWindow && self.isEqual(to: win.firstResponder) && self.isRowSelected(row) { isHighlighted = true } let rect = self.rect(ofRow: row) var x = ceil(NSMidX(rect)) var y = NSMaxY(rect) - 1.5 NSGraphicsContext.saveGraphicsState() NSBezierPath.defaultLineWidth = 1.0 NSColor(calibratedWhite: isHighlighted ? 1.0 : 0.5, alpha: 0.7).setStroke() NSBezierPath.strokeLine(from: NSMakePoint(x - 1.0, y), to: NSMakePoint(x + 1.0, y)) y -= 2.0 NSBezierPath.strokeLine(from: NSMakePoint(x - 3.0, y), to: NSMakePoint(x + 3.0, y)) NSGraphicsContext.restoreGraphicsState() } } override func expandItem(_ item: Any?, expandChildren: Bool) { super.expandItem(expandChildren ? nil : item, expandChildren: expandChildren) self.window?.invalidateCursorRects(for: self) } override func collapseItem(_ item: Any?, collapseChildren: Bool) { super.collapseItem(collapseChildren ? nil : item, collapseChildren: collapseChildren) self.window?.invalidateCursorRects(for: self) } /* - (void)mouseDown:(NSEvent *)theEvent { if ([theEvent clickCount] == 1 && [[self delegate] respondsToSelector:@selector(outlineView:canResizeRowByItem:)] && [[self delegate] respondsToSelector:@selector(outlineView:setHeight:ofRowByItem:)]) { NSPoint mouseLoc = [theEvent locationInView:self]; NSInteger row = [self rowAtPoint:mouseLoc]; id item = row != -1 ? [self itemAtRow:row] : nil; if (item && [[self delegate] outlineView:self canResizeRowByItem:item]) { NSRect rect = SKSliceRect([self rectOfRow:row], RESIZE_EDGE_HEIGHT, [self isFlipped] ? NSMaxYEdge : NSMinYEdge); if (NSMouseInRect(mouseLoc, rect, [self isFlipped]) && [NSApp willDragMouse]) { CGFloat startHeight = [[self delegate] outlineView:self heightOfRowByItem:item]; [[NSCursor resizeUpDownCursor] push]; while ([theEvent type] != NSLeftMouseUp) { theEvent = [[self window] nextEventMatchingMask: NSLeftMouseUpMask | NSLeftMouseDraggedMask]; if ([theEvent type] == NSLeftMouseDragged) { CGFloat currentHeight = fmax([self rowHeight], startHeight + [theEvent locationInView:self].y - mouseLoc.y); [[self delegate] outlineView:self setHeight:currentHeight ofRowByItem:item]; [self noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndex:row]]; } } [NSCursor pop]; return; } } } [super mouseDown:theEvent]; } - (void)keyDown:(NSEvent *)theEvent { unichar eventChar = [theEvent firstCharacter]; NSUInteger modifiers = [theEvent standardModifierFlags]; [super keyDown:theEvent]; if ((eventChar == NSDownArrowFunctionKey || eventChar == NSUpArrowFunctionKey) && modifiers == NSCommandKeyMask && [[self delegate] respondsToSelector:@selector(outlineViewCommandKeyPressedDuringNavigation:)]) { [[self delegate] outlineViewCommandKeyPressedDuringNavigation:self]; } } */ override func resetCursorRects() { if let _ = self.noteDelegate?.outlineView(self, canResizeRowByItem: nil) { self.discardCursorRects() super.resetCursorRects() let visibleRows = self.rows(in: self.visibleRect) // NSUInteger row; if (visibleRows.length == 0) { return } for row in visibleRows.location ..< NSMaxRange(visibleRows) { var item = self.item(atRow: row) if let can = self.noteDelegate?.outlineView(self, canResizeRowByItem: item as? AnyObject), can == false { continue } let rect = KMSliceRect(rect: self.rect(ofRow: row), amount: RESIZE_EDGE_HEIGHT, edge: self.isFlipped ? .maxY : .minY) self.addCursorRect(rect, cursor: .resizeUpDown) } } else { super.resetCursorRects() } } @objc func toggleTableColumn(_ sender: AnyObject?) { // NSTableColumn *tc = [self tableColumnWithIdentifier:[sender representedObject]]; // [tc setHidden:[tc isHidden] == NO]; // if ([self outlineTableColumn] == tc && [tc isHidden]) // [self collapseItem:nil collapseChildren:YES]; // if ([[self delegate] respondsToSelector:@selector(outlineView:didChangeHiddenOfTableColumn:)]) // [[self delegate] outlineView:self didChangeHiddenOfTableColumn:tc]; } override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { if (menuItem.action == #selector(toggleTableColumn)) { if let column = self.tableColumn(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: (menuItem.representedObject as? String) ?? "")), column.isHidden { menuItem.state = .off } else { menuItem.state = .on } return true } // else if ([[SKNoteOutlineView superclass] instancesRespondToSelector:_cmd]) { // return [super validateMenuItem:menuItem]; // } return super.validateMenuItem(menuItem) } } // MARK: - Private Methods extension KMNoteOutlineView { private func _titleForTableColumnIdentifier(_ identifier: String) -> String? { if (identifier == NOTE_COLUMNID) { return KMLocalizedString("Note", comment: "Table header title") } else if (identifier == TYPE_COLUMNID) { return KMLocalizedString("Type", comment: "Table header title") } else if (identifier == COLOR_COLUMNID) { return KMLocalizedString("Color", comment: "Table header title") } else if (identifier == PAGE_COLUMNID) { return KMLocalizedString("Page", comment: "Table header title") } else if (identifier == AUTHOR_COLUMNID) { return KMLocalizedString("Author", comment: "Table header title") } else if (identifier == DATE_COLUMNID) { return KMLocalizedString("Date", comment: "Table header title") } return nil } }