// // KMToolbarConfigViewController.swift // PDF Reader Pro // // Created by tangchao on 2024/5/23. // import Cocoa typealias KMToolbarConfigViewControllerCallback = (NSApplication.ModalResponse) -> Void class KMToolbarConfigViewController: NSViewController { @IBOutlet weak var showLabel: NSTextField! @IBOutlet weak var collectionView: NSCollectionView! @IBOutlet weak var removedLabel: NSTextField! @IBOutlet weak var removedCollectionView: NSCollectionView! @IBOutlet weak var tipLabel: NSTextField! @IBOutlet weak var cancelButton: NSButton! @IBOutlet weak var confirmButton: NSButton! private let cellIdentifier_ = NSUserInterfaceItemIdentifier(rawValue: "ToolbarConfigCellIdentifier") var model = KMToolbarConfigModel() var callback: KMToolbarConfigViewControllerCallback? deinit { Swift.debugPrint("KMToolbarConfigViewController deinit.") } convenience init() { self.init(nibName: "KMToolbarConfigViewController", bundle: nil) } override func viewDidLoad() { super.viewDidLoad() self.initDefalutValue() } func initDefalutValue() { self.showLabel.stringValue = NSLocalizedString("Show", comment: "") self.collectionView.delegate = self self.collectionView.dataSource = self self.collectionView.register(KMToolbarConfigViewItem.self, forItemWithIdentifier: self.cellIdentifier_) (self.collectionView.collectionViewLayout as? NSCollectionViewFlowLayout)?.scrollDirection = .horizontal self.collectionView.registerForDraggedTypes([NSPasteboard.PasteboardType.string]) self.collectionView.allowsMultipleSelection = false self.collectionView.enclosingScrollView?.backgroundColor = .gridColor self.removedLabel.stringValue = NSLocalizedString("Hide", comment: "") self.removedCollectionView.delegate = self self.removedCollectionView.dataSource = self self.removedCollectionView.register(KMToolbarConfigViewItem.self, forItemWithIdentifier: self.cellIdentifier_) (self.removedCollectionView.collectionViewLayout as? NSCollectionViewFlowLayout)?.scrollDirection = .horizontal self.removedCollectionView.registerForDraggedTypes([NSPasteboard.PasteboardType.string]) self.removedCollectionView.isSelectable = true self.tipLabel.stringValue = NSLocalizedString("Drag and drop to add, remove and reorder the tools.", comment: "") self.cancelButton.title = NSLocalizedString("Cancel", comment: "") self.cancelButton.target = self self.cancelButton.action = #selector(buttonClick) self.confirmButton.title = NSLocalizedString("Confirm", comment: "") self.confirmButton.target = self self.confirmButton.action = #selector(buttonClick) } @objc func buttonClick(_ sender: NSButton) { var resp: NSApplication.ModalResponse = .cancel if self.confirmButton.isEqual(to: sender) { resp = .OK } guard let block = self.callback else { return } block(resp) } } extension KMToolbarConfigViewController: NSCollectionViewDelegate, NSCollectionViewDataSource, NSCollectionViewDelegateFlowLayout { func numberOfSections(in collectionView: NSCollectionView) -> Int { return 1 } func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int { if self.removedCollectionView.isEqual(to: collectionView) { return self.model.removedCellIdentifiers?.count ?? 0 } if let cnt = self.model.allowedCellIdentifiers?.count { return cnt + 2 } return 0 } func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem { if self.removedCollectionView.isEqual(to: collectionView) { let cell = collectionView.makeItem(withIdentifier: self.cellIdentifier_, for: indexPath) as! KMToolbarConfigViewItem let itemId = self.model.removedCellIdentifiers?[indexPath.item] ?? "" let item = KMToolbarConfigTBItemView(itemIdentifier: itemId) self.model.setupMainItem(item) cell.itemView = item return cell } let cell = collectionView.makeItem(withIdentifier: self.cellIdentifier_, for: indexPath) as! KMToolbarConfigViewItem if self.model.isFirstSegI(at: indexPath.item) { cell.itemView = nil } else if self.model.isSecondSegI(at: indexPath.item) { cell.itemView = nil } else { var idx = indexPath.item if self.model.isRight(at: idx) { idx = idx - 2 } else if self.model.isCenter(at: idx) { idx = idx - 1 } let itemId = self.model.allowedCellIdentifiers?[idx] ?? "" let item = KMToolbarConfigTBItemView(itemIdentifier: itemId) self.model.setupMainItem(item) cell.itemView = item } return cell } // Layout func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize { if self.removedCollectionView.isEqual(to: collectionView) { let itemId = self.model.removedCellIdentifiers?[indexPath.item] ?? "" let item = KMToolbarItemView(itemIdentifier: itemId) self.model.setupMainItem(item) return NSSize(width: item.itemWidth, height: 48) } if self.model.isFirstSegI(at: indexPath.item) { return NSSize(width: self.model.segItemWidth, height: 48) } else if self.model.isSecondSegI(at: indexPath.item) { return NSSize(width: self.model.segItemWidth, height: 48) } var idx = indexPath.item if self.model.isRight(at: idx) { idx = idx - 2 } else if self.model.isCenter(at: idx) { idx = idx - 1 } let itemId = self.model.allowedCellIdentifiers?[idx] ?? "" let item = KMToolbarItemView(itemIdentifier: itemId) self.model.setupMainItem(item) return NSSize(width: item.itemWidth, height: 48) } // 纵向间距 func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return 5 } // 横向间距 func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { if self.removedCollectionView.isEqual(to: collectionView) { return 12 } return 5 } func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, insetForSectionAt section: Int) -> NSEdgeInsets { return .init(top: 6, left: 10, bottom: 6, right: 10) } func collectionView(_ collectionView: NSCollectionView, shouldSelectItemsAt indexPaths: Set) -> Set { return indexPaths } // Drag & Drop func collectionView(_ collectionView: NSCollectionView, canDragItemsAt indexPaths: Set, with event: NSEvent) -> Bool { if self.removedCollectionView.isEqual(to: collectionView) { return true } guard let ip = indexPaths.first else { return false } if self.model.isSeg(at: ip.item) { return false } return true } func collectionView(_ collectionView: NSCollectionView, pasteboardWriterForItemAt indexPath: IndexPath) -> NSPasteboardWriting? { return String(indexPath.item) as NSPasteboardWriting } func collectionView(_ collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath proposedDropIndexPath: AutoreleasingUnsafeMutablePointer, dropOperation proposedDropOperation: UnsafeMutablePointer) -> NSDragOperation { let draggingSource = draggingInfo.draggingSource as? NSCollectionView if collectionView.isEqual(self.removedCollectionView) { if self.collectionView.isEqual(to: draggingSource) { return .copy } else { return .move } } if collectionView.isEqual(self.collectionView) { if self.removedCollectionView.isEqual(to: draggingSource) { return .copy } else { return .move } } return [] } func collectionView(_ collectionView: NSCollectionView, acceptDrop draggingInfo: NSDraggingInfo, indexPath: IndexPath, dropOperation: NSCollectionView.DropOperation) -> Bool { guard let draggingSource = draggingInfo.draggingSource as? NSCollectionView else { return false } if collectionView.isEqual(self.removedCollectionView) { let isIn = self.removedCollectionView.isEqual(to: draggingSource) if isIn { // 暂不支持内部拖拽排序 return false } // 外部拖拽 if let data = draggingInfo.draggingPasteboard.pasteboardItems?.first?.string(forType: .string) { if let itemIdx = Int(data) { if self.model.removeItem(at: itemIdx) { self.collectionView.reloadData() self.removedCollectionView.reloadData() return true } } } return false } // 显示区域 let isIn = self.collectionView.isEqual(to: draggingSource) if isIn { // 内部拖拽 if let data = draggingInfo.draggingPasteboard.pasteboardItems?.first?.string(forType: .string) { if let itemIdx = Int(data) { if itemIdx == indexPath.item { // 无效拖拽 return false } if self.model.moveItem(itemIdx: itemIdx, to: indexPath.item) { self.collectionView.reloadData() return true } } } return false } // 外部拖拽 if let data = draggingInfo.draggingPasteboard.pasteboardItems?.first?.string(forType: .string) { if let itemIdx = Int(data) { if let itemId = self.model.removedCellIdentifiers?.safe_element(for: itemIdx) as? String { if self.model.addItem(itemId: itemId, at: indexPath.item) { self.collectionView.reloadData() self.removedCollectionView.reloadData() return true } } } } return false } }