123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- //
- // KMThumbnailView.swift
- // PDF Master
- //
- // Created by tangchao on 2023/5/4.
- //
- import Cocoa
- @objc protocol KMThumbnailViewDelegate : NSObjectProtocol {
- // layout
- @objc optional func thumbnailView(thumbanView: KMThumbnailView, minimumLineSpacingForSectionAt section: Int) -> CGFloat
- @objc optional func thumbnailView(thumbanView: KMThumbnailView, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat
- @objc optional func thumbnailView(thumbanView: KMThumbnailView, insetForSectionAt section: Int) -> NSEdgeInsets
-
- @objc optional func thumbnailView(thumbanView: KMThumbnailView, sizeForItemAt indexpath: IndexPath) -> NSSize
-
- @objc optional func thumbnailView(thumbanView: KMThumbnailView, numberOfItemsInSection section: Int) -> Int
- @objc optional func thumbnailView(thumbanView: KMThumbnailView, itemForRepresentedObjectAt indexpath: IndexPath) -> NSCollectionViewItem
-
- // Drag & Drop
- // 本地拖拽
- @objc optional func thumbnailView(thumbanView: KMThumbnailView, didDrag dragedIndexPaths: [IndexPath], indexpath: IndexPath)
- // 外部拖拽
- @objc optional func thumbnailView(thumbanView: KMThumbnailView, didDragAddFiles files: [URL], indexpath: IndexPath)
-
- @objc optional func thumbnailView(thumbanView: KMThumbnailView, didSelectItemAt indexpath: IndexPath, object: AnyObject?)
- @objc optional func thumbnailView(thumbanView: KMThumbnailView, rightMouseDidClick indexpath: IndexPath, item: NSCollectionViewItem?, object: AnyObject?)
- }
- @objc class KMThumbnailView: NSView {
-
- open weak var delegate: KMThumbnailViewDelegate?
-
- internal let localForDraggedTypes = kKMLocalForDraggedTypes
- internal var dragedIndexPaths: [IndexPath] = []
- override init(frame frameRect: NSRect) {
- super.init(frame: frameRect)
-
- self.initDefaultValue()
- self.initSubViews()
- }
-
- required public init?(coder: NSCoder) {
- super.init(coder: coder)
-
- self.initDefaultValue()
- self.initSubViews()
- }
-
- open var minimumLineSpacing: CGFloat = 0.0 {
- didSet {
- self.collectionView.reloadData()
- }
- }
- open var minimumInteritemSpacing: CGFloat = 0.0 {
- didSet {
- self.collectionView.reloadData()
- }
- }
- open var itemSize: NSSize = NSMakeSize(60, 80) {
- didSet {
- self.collectionView.reloadData()
- }
- }
- open var sectionInset: NSEdgeInsets = NSEdgeInsetsZero {
- didSet {
- self.collectionView.reloadData()
- }
- }
-
- open var numberOfSections: Int = 0 {
- didSet {
- self.collectionView.reloadData()
- }
- }
-
- var selectionIndexPaths: Set<IndexPath> {
- get {
- return self.collectionView.selectionIndexPaths
- }
- set {
- var indexpaths: Set<IndexPath> = []
- for indexpath in newValue {
- if (indexpath.section >= self.collectionView.numberOfSections) {
- continue
- }
- if (indexpath.item >= self.collectionView.numberOfItems(inSection: indexpath.section)) {
- continue
- }
-
- indexpaths.insert(indexpath)
- }
-
- self.collectionView.selectionIndexPaths = indexpaths
- }
- }
-
- // MARK: - Publick Methods
-
- public func initDefaultValue() {
- self.collectionView.registerForDraggedTypes([self.localForDraggedTypes, .fileURL,.string,.pdf])
- }
-
- public func initSubViews() {
- self.addSubview(self.scrollView)
- self.scrollView.frame = self.bounds
- self.scrollView.autoresizingMask = [.width, .height]
-
- self.scrollView.documentView = self.collectionView
- }
-
- // MARK: - register ItemClass
-
- open func register(_ itemClass: AnyClass?) {
- guard let itemClass_ = itemClass else {
- return
- }
-
- self.register(itemClass_, forItemWithIdentifier: NSStringFromClass(itemClass_))
- }
-
- open func register(_ itemClass: AnyClass?, forItemWithIdentifier identifier: String) {
- self.collectionView.register(itemClass, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: identifier))
- }
-
- // MARK: - 刷新UI
-
- public func reloadData(indexs: Set<IndexPath> = []) {
- if (indexs.count == 0) {
- if (Thread.isMainThread) {
- self.collectionView.reloadData()
- } else {
- DispatchQueue.main.async {
- self.collectionView.reloadData()
- }
- }
- } else {
- var indexpaths: Set<IndexPath> = []
- for index in indexs {
- if (index.section >= self.collectionView.numberOfSections) {
- continue
- }
- if (index.item >= self.collectionView.numberOfItems(inSection: index.section)) {
- continue
- }
-
- indexpaths.insert(index)
- }
- if (indexpaths.count == 0) {
- return
- }
-
- if (Thread.isMainThread) {
- self.collectionView.reloadItems(at: indexpaths)
- } else {
- DispatchQueue.main.async {
- self.collectionView.reloadItems(at: indexpaths)
- }
- }
- }
- }
-
- // MARK: - 属性 【懒加载】
-
- internal lazy var scrollView_: NSScrollView = {
- let view = NSScrollView()
-
- view.hasHorizontalScroller = true
- view.hasVerticalScroller = true
- view.autohidesScrollers = true
- view.minMagnification = 1.0
- view.scrollerStyle = .overlay
-
- view.wantsLayer = true
- view.layer?.backgroundColor = NSColor.clear.cgColor
-
- view.wantsLayer = true
- view.contentView.layer?.backgroundColor = .white
-
- return view
- }()
- var scrollView: NSScrollView {
- get {
- return self.scrollView_
- }
- }
-
- internal lazy var collectionView_: NSCollectionView = {
- let view = NSCollectionView()
- view.autoresizingMask = [.width, .height]
-
- let layout = NSCollectionViewFlowLayout()
- layout.sectionInset = NSEdgeInsetsMake(8, 15, 8, 15)
- layout.minimumLineSpacing = 0
- layout.minimumInteritemSpacing = 0
- view.collectionViewLayout = layout
-
- view.delegate = self
- view.dataSource = self
-
- view.register(NSCollectionViewItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMPDFThumbnailItem"))
- view.isSelectable = true
-
- view.wantsLayer = true
- view.layer?.backgroundColor = NSColor(hex: "#F7F8FA").cgColor
-
- return view
- }()
- var collectionView: NSCollectionView {
- get {
- return self.collectionView_
- }
- }
- }
- // MARK: - NSCollectionViewDataSource, NSCollectionViewDelegate
- extension KMThumbnailView: NSCollectionViewDataSource {
- public func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
- if let items = self.delegate?.thumbnailView?(thumbanView: self, numberOfItemsInSection: section) {
- return items
- }
- return self.numberOfSections
- }
-
- func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
- if let item = self.delegate?.thumbnailView?(thumbanView: self, itemForRepresentedObjectAt: indexPath) {
- return item
- }
- return NSCollectionViewItem()
- }
- }
- // MARK: - NSCollectionViewDelegate
- extension KMThumbnailView: NSCollectionViewDelegate {
- // func collectionView(_ collectionView: NSCollectionView, shouldSelectItemsAt indexPaths: Set<IndexPath>) -> Set<IndexPath> {
- // return indexPaths
- // }
-
- func collectionView(_ collectionView: NSCollectionView, shouldSelectItemsAt indexPaths: Set<IndexPath>) -> Set<IndexPath> {
- if let lastSelectedIndexPath = collectionView.selectionIndexPaths.first {
- if NSApp.currentEvent?.modifierFlags.contains(.shift) == true {
- // Shift 键按住,进行连续多选
- let selectedIndexPaths = collectionView.selectionIndexPaths
- var allIndexPaths = Set<IndexPath>(selectedIndexPaths)
- // 获取两个 IndexPath 之间的所有 IndexPath
- let startIndex = lastSelectedIndexPath.item
- let endIndex = indexPaths.first?.item ?? startIndex
- let range = startIndex < endIndex ? startIndex...endIndex : endIndex...startIndex
- for index in range {
- let indexPath = IndexPath(item: index, section: lastSelectedIndexPath.section)
- allIndexPaths.insert(indexPath)
- }
- return allIndexPaths
- }
- }
- return indexPaths
- }
-
- func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {}
-
- func collectionView(_ collectionView: NSCollectionView, shouldDeselectItemsAt indexPaths: Set<IndexPath>) -> Set<IndexPath> {
- return indexPaths
- }
-
- func collectionView(_ collectionView: NSCollectionView, didDeselectItemsAt indexPaths: Set<IndexPath>) {}
-
- func collectionView(_ collectionView: NSCollectionView, canDragItemsAt indexPaths: Set<IndexPath>, with event: NSEvent) -> Bool {
- return true
- }
-
- func collectionView(_ collectionView: NSCollectionView, writeItemsAt indexPaths: Set<IndexPath>, to pasteboard: NSPasteboard) -> Bool {
- let data: Data = try! NSKeyedArchiver.archivedData(withRootObject: indexPaths, requiringSecureCoding: true)
- pasteboard.declareTypes([self.localForDraggedTypes], owner: self)
- pasteboard.setData(data, forType: self.localForDraggedTypes)
-
- self.dragedIndexPaths.removeAll()
- for indexPath in indexPaths {
- self.dragedIndexPaths.append(indexPath)
- }
- return true
- }
-
- func collectionView(_ collectionView: NSCollectionView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItemsAt indexes: IndexSet) {}
-
- func collectionView(_ collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath proposedDropIndexPath: AutoreleasingUnsafeMutablePointer<NSIndexPath>, dropOperation proposedDropOperation: UnsafeMutablePointer<NSCollectionView.DropOperation>) -> NSDragOperation {
- let pboard = draggingInfo.draggingPasteboard
- if (pboard.availableType(from: [self.localForDraggedTypes]) != nil) {
- return .move
- }
-
- return NSDragOperation(rawValue: 0)
- }
-
- func collectionView(_ collectionView: NSCollectionView, acceptDrop draggingInfo: NSDraggingInfo, indexPath: IndexPath, dropOperation: NSCollectionView.DropOperation) -> Bool {
- let pboard = draggingInfo.draggingPasteboard
- if (pboard.availableType(from: [self.localForDraggedTypes]) != nil) {
- self.delegate?.thumbnailView?(thumbanView: self, didDrag: self.dragedIndexPaths, indexpath: indexPath)
- self.dragedIndexPaths.removeAll()
- return true
- } else if ((pboard.availableType(from: [.fileURL])) != nil) {
- var array: [URL] = []
- for item: NSPasteboardItem in pboard.pasteboardItems! {
- let string: String = item.string(forType: NSPasteboard.PasteboardType.fileURL)!
- let url = NSURL(string: string)
- array.append(url! as URL)
- }
-
- self.delegate?.thumbnailView?(thumbanView: self, didDragAddFiles: array, indexpath: indexPath)
- return true
- }
-
- return false
- }
- }
- // MARK: - NSCollectionViewDelegateFlowLayout
- extension KMThumbnailView: NSCollectionViewDelegateFlowLayout {
- func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {
- if let size_ = self.delegate?.thumbnailView?(thumbanView: self, sizeForItemAt: indexPath) {
- return size_
- }
-
- return self.itemSize
- }
-
- func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
- if let minimumLineSpacing_ = self.delegate?.thumbnailView?(thumbanView: self, minimumLineSpacingForSectionAt: section) {
- return minimumLineSpacing_
- }
- return self.minimumLineSpacing
- }
-
- func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
- if let minimumInteritemSpacing_ = self.delegate?.thumbnailView?(thumbanView: self, minimumInteritemSpacingForSectionAt: section) {
- return minimumInteritemSpacing_
- }
- return self.minimumInteritemSpacing
- }
-
- func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, insetForSectionAt section: Int) -> NSEdgeInsets {
- if let inset = self.delegate?.thumbnailView?(thumbanView: self, insetForSectionAt: section) {
- return inset
- }
- return self.sectionInset
- }
- }
|