// // KMHomeQuickToolsCollectionView.swift // PDF Reader Pro // // Created by lizhe on 2023/10/31. // import Cocoa typealias KMHomeQuickToolsCollectionViewDataDidChange = (_ view: KMHomeQuickToolsCollectionView, _ showData: [NSNumber]) ->Void class KMHomeQuickToolsCollectionView: KMBaseXibView { @IBOutlet weak var showBox: NSBox! @IBOutlet weak var showLabel: NSTextField! @IBOutlet weak var removeBox: NSBox! @IBOutlet weak var removeButton: NSButton! @IBOutlet weak var removeButtonLayoutConstraint: NSLayoutConstraint! @IBOutlet weak var showCollectionView: NSCollectionView! @IBOutlet weak var showScrollView: NSScrollView! @IBOutlet weak var hideBox: NSBox! @IBOutlet weak var hideLabel: NSTextField! @IBOutlet weak var addBox: NSBox! @IBOutlet weak var addButton: NSButton! @IBOutlet weak var addButtonLayoutConstraint: NSLayoutConstraint! @IBOutlet weak var hideCollectionView: NSCollectionView! @IBOutlet weak var hideScrollView: NSScrollView! @IBOutlet weak var promptLabel: NSTextField! @IBOutlet weak var showCollectionBox: NSBox! @IBOutlet weak var hideCollectionBox: NSBox! var dataChange: KMHomeQuickToolsCollectionViewDataDidChange? var indexPathsOfItemsBeingShowItemDragged: Set = [] var indexPathsOfItemsBeingHideItemDragged: Set = [] var showArray: [NSNumber] = [] var hideArray: [NSNumber] = [] var selectShowItemMutableArray: [IndexPath] = [] var selectHideItemMutableArray: [IndexPath] = [] var collectionItemWidth: CGFloat = 0 let collectionItemHeight: CGFloat = 32 override func setup() { showLabel.stringValue = NSLocalizedString("Show", comment: "") hideLabel.stringValue = NSLocalizedString("Hide", comment: "") removeButton.title = NSLocalizedString("Remove", comment: "") addButton.title = NSLocalizedString("Add", comment: "") promptLabel.stringValue = NSLocalizedString("Drag and drop to add, remove and reorder the tools.", comment: "") promptLabel.textColor = KMAppearance.Layout.h1Color() showScrollView.drawsBackground = false hideScrollView.drawsBackground = false removeBox.borderColor = NSColor.gridColor addBox.borderColor = NSColor.gridColor removeBox.fillColor = NSColor.gridColor addBox.fillColor = NSColor.gridColor removeButton.isEnabled = false addButton.isEnabled = false let removeButtonConstant = labelReturnedValue(withString: NSLocalizedString("Remove", comment: ""), height: 20, fontSize: 11.0) let addButtonConstant = labelReturnedValue(withString:NSLocalizedString("Add", comment: ""), height: 20.0, fontSize: 11.0) var constant: CGFloat = 0 if removeButtonConstant > addButtonConstant { constant = removeButtonConstant + 30 } else { constant = addButtonConstant + 30 } removeButtonLayoutConstraint.constant = constant addButtonLayoutConstraint.constant = constant showCollectionView.enclosingScrollView?.scrollerStyle = .legacy hideCollectionView.enclosingScrollView?.scrollerStyle = .legacy let shadow = NSShadow() shadow.shadowColor = KMAppearance.Status.hovColor() shadow.shadowOffset = NSMakeSize(0, 1) showCollectionBox.wantsLayer = true showCollectionBox.shadow = shadow hideCollectionBox.wantsLayer = true hideCollectionBox.shadow = shadow showCollectionView.register(KMHomeQuickToolsWindowCollectionViewItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier("KMHomeQuickToolsWindowCollectionViewItem")) hideCollectionView.register(KMHomeQuickToolsWindowCollectionViewItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier("KMHomeQuickToolsWindowCollectionViewItem")) showCollectionView.delegate = self showCollectionView.dataSource = self hideCollectionView.delegate = self hideCollectionView.dataSource = self showCollectionView.layer?.cornerRadius = 3.0 hideCollectionView.layer?.cornerRadius = 3.0 registerForCollectionViewDragAndDrop() // self.showCollectionView.reloadData() // self.hideCollectionView.reloadData() } func showData(showArr: [NSNumber], hideArr: [NSNumber]) { collectionItemWidth = self.itemReturnedWidthValue(withShowArray: showArr, hideArray: hideArr, fontSize: 12, height: collectionItemHeight) + 40 showArray = showArr hideArray = hideArr } func registerForCollectionViewDragAndDrop() { showCollectionView.registerForDraggedTypes([NSPasteboard.PasteboardType.string]) showCollectionView.setDraggingSourceOperationMask(.every, forLocal: false) showCollectionView.setDraggingSourceOperationMask(.every, forLocal: true) hideCollectionView.registerForDraggedTypes([NSPasteboard.PasteboardType.string]) hideCollectionView.setDraggingSourceOperationMask(.every, forLocal: false) hideCollectionView.setDraggingSourceOperationMask(.every, forLocal: true) } func moveCell(fromIndex: Int, toIndex: Int, collectionView: NSCollectionView) { if collectionView == showCollectionView { let fromNumber = showArray[fromIndex] var currentIndex = fromIndex if fromIndex <= toIndex { for _ in fromIndex.. toIndex { showArray.swapAt(currentIndex, currentIndex - 1) currentIndex -= 1 } } } } else if collectionView == hideCollectionView { let fromNumber = hideArray[fromIndex] var currentIndex = fromIndex if fromIndex <= toIndex { for _ in fromIndex.. toIndex { hideArray.swapAt(currentIndex, currentIndex - 1) currentIndex -= 1 } } } } } func labelReturnedValue(withString value: String, height: CGFloat, fontSize: CGFloat) -> CGFloat { if let value = value as NSString? { let rect = value.boundingRect(with: CGSize(width: .greatestFiniteMagnitude, height: height), options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: NSFont.systemFont(ofSize: fontSize)], context: nil) return rect.size.width } return 0 } } extension KMHomeQuickToolsCollectionView { @IBAction func removeButtonAction(_ sender: Any) { for indexPath in selectShowItemMutableArray { let itemCount = indexPath.item hideArray.append(showArray[itemCount]) showArray.remove(at: itemCount) } removeBox.borderColor = NSColor.gridColor removeButton.isEnabled = false addBox.borderColor = NSColor.gridColor addButton.isEnabled = false hideCollectionView.reloadData() showCollectionView.reloadData() guard let callBack = dataChange else { return } callBack(self, showArray) } @IBAction func addButtonAction(_ sender: Any) { for indexPath in selectHideItemMutableArray { let itemCount = indexPath.item showArray.append(hideArray[itemCount]) hideArray.remove(at: itemCount) } removeBox.borderColor = NSColor.gridColor addButton.isEnabled = false addBox.borderColor = NSColor.gridColor removeButton.isEnabled = false showCollectionView.reloadData() hideCollectionView.reloadData() guard let callBack = dataChange else { return } callBack(self, showArray) } } extension KMHomeQuickToolsCollectionView: NSCollectionViewDelegate, NSCollectionViewDataSource, NSCollectionViewDelegateFlowLayout { func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem { let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMHomeQuickToolsWindowCollectionViewItem"), for: indexPath) as! KMHomeQuickToolsWindowCollectionViewItem if collectionView.isEqual(showCollectionView) { item.model = KMQucikToolsModel(type: DataNavigationViewButtonActionType(rawValue: Int(truncating: showArray[indexPath.item]))) } else if collectionView.isEqual(hideCollectionView) { item.model = KMQucikToolsModel(type: DataNavigationViewButtonActionType(rawValue: Int(truncating: hideArray[indexPath.item]))) } return item } func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int { if collectionView.isEqual(showCollectionView) { return showArray.count } else if collectionView.isEqual(hideCollectionView) { return hideArray.count } return 0 } func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set) { let indexPathArr = Array(indexPaths) if collectionView.isEqual(showCollectionView) { self.selectShowItemMutableArray.removeAll() removeBox.borderColor = KMAppearance.Layout.h2Color() removeButton.isEnabled = true for indexPath in indexPathArr { self.selectShowItemMutableArray.append(indexPath) } } else if collectionView.isEqual(hideCollectionView) { self.selectHideItemMutableArray.removeAll() addBox.borderColor = KMAppearance.Layout.h2Color() addButton.isEnabled = true for indexPath in indexPathArr { self.selectHideItemMutableArray.append(indexPath) } } } func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize { return NSSize(width: collectionItemWidth, height: collectionItemHeight) } // NSCollectionViewDelegate Drag-and-Drop Methods func collectionView(_ collectionView: NSCollectionView, canDragItemsAt indexPaths: Set, with event: NSEvent) -> Bool { return true } func collectionView(_ collectionView: NSCollectionView, pasteboardWriterForItemAt indexPath: IndexPath) -> NSPasteboardWriting? { return String(indexPath.item) as NSPasteboardWriting } func collectionView(_ collectionView: NSCollectionView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItemsAt indexPaths: Set) { if collectionView.isEqual(showCollectionView) { indexPathsOfItemsBeingShowItemDragged = indexPaths } else if collectionView.isEqual(hideCollectionView) { indexPathsOfItemsBeingHideItemDragged = indexPaths } } func collectionView(_ collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath proposedDropIndexPath: AutoreleasingUnsafeMutablePointer, dropOperation proposedDropOperation: UnsafeMutablePointer) -> NSDragOperation { if collectionView.isEqual(showCollectionView) { if indexPathsOfItemsBeingShowItemDragged.count != 0 { return .move } else { return .copy } } else if collectionView.isEqual(hideCollectionView) { if indexPathsOfItemsBeingHideItemDragged.count != 0 { return .move } else { return .copy } } return [] } func collectionView(_ collectionView: NSCollectionView, acceptDrop draggingInfo: NSDraggingInfo, indexPath: IndexPath, dropOperation: NSCollectionView.DropOperation) -> Bool { var result = false if collectionView.isEqual(showCollectionView) { if indexPathsOfItemsBeingShowItemDragged.count != 0 { var toItemIndex = indexPath.item for fromIndexPath in indexPathsOfItemsBeingShowItemDragged { let fromItemIndex = fromIndexPath.item if fromItemIndex > toItemIndex { moveCell(fromIndex: fromItemIndex, toIndex: toItemIndex, collectionView: collectionView) collectionView.animator().moveItem(at: fromIndexPath, to: IndexPath(item: toItemIndex, section: indexPath.section)) toItemIndex += 1 } } var adjustedToItemIndex = indexPath.item for fromIndexPath in indexPathsOfItemsBeingShowItemDragged.reversed() { let fromItemIndex = fromIndexPath.item if fromItemIndex < adjustedToItemIndex { if adjustedToItemIndex >= showArray.count { adjustedToItemIndex = showArray.count - 1 } moveCell(fromIndex: fromItemIndex, toIndex: adjustedToItemIndex, collectionView: collectionView) let adjustedToIndexPath = IndexPath(item: adjustedToItemIndex, section: indexPath.section) collectionView.animator().moveItem(at: fromIndexPath, to: adjustedToIndexPath) adjustedToItemIndex -= 1 } } result = true } else if indexPathsOfItemsBeingHideItemDragged.count != 0 { let toItemIndex = indexPathsOfItemsBeingHideItemDragged.first!.item showArray.insert((hideArray[toItemIndex] as! Int) as NSNumber, at: indexPath.item) hideArray.remove(at: toItemIndex) result = true self.dataChange?(self, showArray) } } else if collectionView.isEqual(hideCollectionView) { if indexPathsOfItemsBeingHideItemDragged.count != 0 { var toItemIndex = indexPath.item for fromIndexPath in indexPathsOfItemsBeingHideItemDragged { let fromItemIndex = fromIndexPath.item if fromItemIndex > toItemIndex { moveCell(fromIndex: fromItemIndex, toIndex: toItemIndex, collectionView: collectionView) collectionView.animator().moveItem(at: fromIndexPath, to: IndexPath(item: toItemIndex, section: indexPath.section)) toItemIndex += 1 } } var adjustedToItemIndex = indexPath.item - 1 for fromIndexPath in indexPathsOfItemsBeingHideItemDragged.reversed() { let fromItemIndex = fromIndexPath.item if fromItemIndex < adjustedToItemIndex { if adjustedToItemIndex >= showArray.count { adjustedToItemIndex = showArray.count - 1 } moveCell(fromIndex: fromItemIndex, toIndex: adjustedToItemIndex, collectionView: collectionView) let adjustedToIndexPath = IndexPath(item: adjustedToItemIndex, section: indexPath.section) collectionView.animator().moveItem(at: fromIndexPath, to: adjustedToIndexPath) adjustedToItemIndex -= 1 } } result = true } else if indexPathsOfItemsBeingShowItemDragged.count != 0 { let toItemIndex = indexPathsOfItemsBeingShowItemDragged.first!.item hideArray.insert((showArray[toItemIndex] as! Int) as NSNumber, at: indexPath.item) showArray.remove(at: toItemIndex) result = true self.dataChange?(self, showArray) } } return result } func collectionView(_ collectionView: NSCollectionView, draggingSession session: NSDraggingSession, endedAt screenPoint: NSPoint, dragOperation operation: NSDragOperation) { if collectionView.isEqual(showCollectionView) { indexPathsOfItemsBeingShowItemDragged.removeAll() removeBox.borderColor = KMAppearance.Layout.h2Color() removeButton.isEnabled = false } else if collectionView.isEqual(hideCollectionView) { indexPathsOfItemsBeingHideItemDragged.removeAll() addBox.borderColor = KMAppearance.Layout.h2Color() addButton.isEnabled = false } showCollectionView.reloadSections(IndexSet(integer: 0)) hideCollectionView.reloadSections(IndexSet(integer: 0)) } } extension KMHomeQuickToolsCollectionView { func itemReturnedWidthValue(withShowArray showArr: [Any], hideArray hideArr: [Any], fontSize: CGFloat, height: CGFloat) -> CGFloat { var itemWidth: CGFloat = 0.0 if showArr.count != 0 { for number in showArr { let value = KMQucikToolsModel(type: DataNavigationViewButtonActionType(rawValue: Int(truncating: number as! NSNumber))).titleString() let rect = value.boundingRect(with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: height), options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: NSFont.systemFont(ofSize: fontSize)], context: nil) if itemWidth < rect.size.width { itemWidth = rect.size.width } } } if hideArr.count != 0 { for number in hideArr { let value = KMQucikToolsModel(type: DataNavigationViewButtonActionType(rawValue: Int(truncating: number as! NSNumber))).titleString() let rect = value.boundingRect(with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: height), options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: NSFont.systemFont(ofSize: fontSize)], context: nil) if itemWidth < rect.size.width { itemWidth = rect.size.width } } } return itemWidth } }