KMPDFThumbnailView.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. //
  2. // KMPDFThumbnailView.swift
  3. // PDF Master
  4. //
  5. // Created by lxy on 2022/12/15.
  6. //
  7. import Cocoa
  8. @objc protocol KMPDFThumbnailViewDelegate: NSObjectProtocol {
  9. @objc optional func thumbnailView(thumbanView: KMPDFThumbnailView, didSelectPageAtIndex index: UInt, event: NSEvent)
  10. @objc optional func thumbnailView(thumbanView: KMPDFThumbnailView, item: KMPDFThumbnailItem, rightMouseDidSelect index: UInt, event: NSEvent)
  11. @objc optional func thumbnailView(thumbanView: KMPDFThumbnailView, didDragAddFiles files: [URL], indexpath: IndexPath)
  12. @objc optional func thumbnailView(thumbanView: KMPDFThumbnailView, didDragPages pages: [Int], indexpath: IndexPath)
  13. @objc optional func thumbnailView(thumbanView: KMPDFThumbnailView, minimumLineSpacingForSectionAt section: Int) -> CGFloat
  14. @objc optional func thumbnailView(thumbanView: KMPDFThumbnailView, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat
  15. @objc optional func thumbnailView(thumbanView: KMPDFThumbnailView, insetForSectionAt section: Int) -> NSEdgeInsets
  16. @objc optional func thumbnailView(thumbanView: KMPDFThumbnailView, sizeForItemAt indexpath: IndexPath) -> NSSize
  17. @objc optional func thumbnailView(thumbanView: KMPDFThumbnailView, itemForRepresentedObjectAt indexpath: IndexPath) -> NSCollectionViewItem
  18. }
  19. class KMPDFThumbnailView: NSView {
  20. open weak var delegate: KMPDFThumbnailViewDelegate?
  21. var document : CPDFDocument = CPDFDocument()
  22. var isShowPageSize : Bool = false
  23. var backgroundColor : NSColor = NSColor.white
  24. var highlightColor : NSColor = NSColor.blue
  25. var pageSelectionColor : NSColor = NSColor()
  26. var textSelectionColor : NSColor = NSColor()
  27. var thumbnailSzie : CGSize = CGSize()
  28. var scrollView : NSScrollView = NSScrollView()
  29. var collectionView : NSCollectionView = NSCollectionView()
  30. var doublePageMode : Bool! = false
  31. private let thumbnailDraggedTypes = NSPasteboard.PasteboardType(rawValue: "localForDraggedTypes")
  32. private var dragedIndexPaths: [IndexPath] = []
  33. //标记线
  34. var isNeedMarkerLine: Bool = false
  35. var markerLineView: NSView = NSView()
  36. var markBeginIndexes: IndexSet = IndexSet()
  37. //注释状态
  38. var annotationShowState: KMAnnotationViewShowType = .none {
  39. didSet {
  40. self.collectionView.reloadData()
  41. }
  42. }
  43. override func draw(_ dirtyRect: NSRect) {
  44. super.draw(dirtyRect)
  45. }
  46. required init?(coder: NSCoder) {
  47. super.init(coder: coder)
  48. self.initUI()
  49. }
  50. override init(frame frameRect: NSRect) {
  51. super.init(frame: frameRect)
  52. self.initUI()
  53. }
  54. private func initUI() {
  55. self.wantsLayer = true
  56. self.layer?.backgroundColor = NSColor(hex: "#F7F8FA").cgColor
  57. self.thumbnailSzie = CGSize(width: 120, height: 155)
  58. let layout = NSCollectionViewFlowLayout()
  59. layout.sectionInset = NSEdgeInsetsMake(8, 15, 8, 15)
  60. layout.minimumLineSpacing = 0
  61. layout.minimumInteritemSpacing = 0
  62. self.collectionView.autoresizingMask = NSView.AutoresizingMask(rawValue: 18)
  63. self.collectionView.collectionViewLayout = layout
  64. self.collectionView.dataSource = self
  65. self.collectionView.delegate = self
  66. self.collectionView.register(KMPDFThumbnailItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMPDFThumbnailItem"))
  67. self.collectionView.registerForDraggedTypes([thumbnailDraggedTypes])
  68. self.collectionView.isSelectable = true
  69. self.collectionView.wantsLayer = true
  70. self.collectionView.layer?.backgroundColor = NSColor(hex: "#F7F8FA").cgColor
  71. self.scrollView.hasHorizontalScroller = true
  72. self.scrollView.hasVerticalScroller = true
  73. self.scrollView.autohidesScrollers = true
  74. self.scrollView.minMagnification = 1.0
  75. self.scrollView.scrollerStyle = NSScroller.Style.overlay
  76. self.scrollView.documentView = self.collectionView
  77. self.scrollView.wantsLayer = true
  78. self.scrollView.layer?.backgroundColor = NSColor.clear.cgColor
  79. self.scrollView.contentView.layer?.backgroundColor = NSColor(hex: "#FFFFFF").cgColor
  80. self.scrollView.contentView.wantsLayer = true
  81. self.addSubview(self.scrollView)
  82. markerLineView.wantsLayer = true
  83. markerLineView.layer?.backgroundColor = NSColor(hex: "#1770F4").cgColor
  84. markerLineView.frame = CGRectMake(0, 0, 100, 2)
  85. self.collectionView.addSubview(markerLineView)
  86. markerLineView.isHidden = true
  87. }
  88. override func setFrameSize(_ newSize: NSSize) {
  89. super.setFrameSize(newSize)
  90. self.scrollView.frame = self.bounds
  91. }
  92. public func reloadData(indexs: Set<IndexPath> = NSSet() as! Set<IndexPath>) {
  93. if indexs.count == 0 {
  94. self.collectionView.reloadData()
  95. } else {
  96. var indexpaths: Set<IndexPath> = []
  97. for index in indexs {
  98. if (index.section >= self.collectionView.numberOfSections) {
  99. continue
  100. }
  101. if (index.item >= self.collectionView.numberOfItems(inSection: index.section)) {
  102. continue
  103. }
  104. indexpaths.insert(index)
  105. }
  106. if (indexpaths.count == 0) {
  107. return
  108. }
  109. if (Thread.isMainThread) {
  110. self.collectionView.reloadItems(at: indexpaths)
  111. } else {
  112. DispatchQueue.main.async {
  113. self.collectionView.reloadItems(at: indexpaths)
  114. }
  115. }
  116. }
  117. }
  118. }
  119. extension KMPDFThumbnailView : NSCollectionViewDataSource {
  120. func numberOfSections(in collectionView: NSCollectionView) -> Int {
  121. return 1
  122. }
  123. func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
  124. if self.document.isLocked {
  125. return 0
  126. }
  127. return Int(self.document.pageCount)
  128. }
  129. func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
  130. let view = self.delegate?.thumbnailView?(thumbanView: self, itemForRepresentedObjectAt: indexPath)
  131. if (view != nil) {
  132. return view!
  133. }
  134. let cellView: KMPDFThumbnailItem = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMPDFThumbnailItem"), for: indexPath) as! KMPDFThumbnailItem
  135. cellView.thumbnailView = self
  136. cellView.page = self.document.page(at: UInt(indexPath.item))
  137. cellView.annotationShowState = self.annotationShowState
  138. cellView.setPage(page: self.document.page(at: UInt(indexPath.item)))
  139. cellView.mouseDownAction = { [unowned self] (view, event) in
  140. self.delegate?.thumbnailView?(thumbanView: self, didSelectPageAtIndex: UInt(indexPath.item), event: event)
  141. }
  142. cellView.rightMouseDownAction = { [unowned self] (view, event) in
  143. self.delegate?.thumbnailView?(thumbanView: self, item: view, rightMouseDidSelect: UInt(indexPath.item), event: event)
  144. }
  145. return cellView
  146. }
  147. }
  148. extension KMPDFThumbnailView: NSCollectionViewDelegateFlowLayout {
  149. /**
  150. * MARK: item大小
  151. */
  152. func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {
  153. if (self.delegate != nil) {
  154. let size = self.delegate!.thumbnailView?(thumbanView: self, sizeForItemAt: indexPath)
  155. if (size != nil) {
  156. // self.thumbnailSzie = size!
  157. return size!
  158. }
  159. }
  160. let page = self.document.page(at: UInt(indexPath.item))
  161. let height = KMPDFThumbnailItem.sizeToFit(size: self.thumbnailSzie, page: page!, isShow: self.isShowPageSize)
  162. return NSMakeSize(collectionView.frame.size.width - 20, CGFloat(height))
  163. }
  164. func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
  165. if (self.delegate != nil) {
  166. let minimumLineSpacing = self.delegate!.thumbnailView?(thumbanView: self, minimumLineSpacingForSectionAt: section)
  167. if (minimumLineSpacing != nil) {
  168. return minimumLineSpacing!
  169. }
  170. }
  171. return 8
  172. }
  173. func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
  174. if (self.delegate != nil) {
  175. let minimumInteritemSpacing = self.delegate!.thumbnailView?(thumbanView: self, minimumInteritemSpacingForSectionAt: section)
  176. if (minimumInteritemSpacing != nil) {
  177. return minimumInteritemSpacing!
  178. }
  179. }
  180. return 0.01
  181. }
  182. func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, insetForSectionAt section: Int) -> NSEdgeInsets {
  183. let inset = self.delegate?.thumbnailView?(thumbanView: self, insetForSectionAt: section)
  184. if (inset != nil) {
  185. return inset!
  186. }
  187. return NSEdgeInsetsZero
  188. }
  189. }
  190. extension KMPDFThumbnailView: NSCollectionViewDelegate {
  191. func collectionView(_ collectionView: NSCollectionView, shouldSelectItemsAt indexPaths: Set<IndexPath>) -> Set<IndexPath> {
  192. return indexPaths
  193. }
  194. func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
  195. // let index: IndexPath = indexPaths.first!
  196. // self.delegate?.thumbnailView?(thumbanView: self, didSelectPageAtIndex: UInt(index.item))
  197. }
  198. func collectionView(_ collectionView: NSCollectionView, shouldDeselectItemsAt indexPaths: Set<IndexPath>) -> Set<IndexPath> {
  199. return indexPaths
  200. }
  201. func collectionView(_ collectionView: NSCollectionView, didDeselectItemsAt indexPaths: Set<IndexPath>) {
  202. }
  203. func collectionView(_ collectionView: NSCollectionView, canDragItemsAt indexPaths: Set<IndexPath>, with event: NSEvent) -> Bool {
  204. return true
  205. }
  206. func collectionView(_ collectionView: NSCollectionView, writeItemsAt indexPaths: Set<IndexPath>, to pasteboard: NSPasteboard) -> Bool {
  207. let data: Data = try! NSKeyedArchiver.archivedData(withRootObject: indexPaths, requiringSecureCoding: true)
  208. pasteboard.declareTypes([self.thumbnailDraggedTypes], owner: self)
  209. pasteboard.setData(data, forType: self.thumbnailDraggedTypes)
  210. self.dragedIndexPaths.removeAll()
  211. for indexPath in indexPaths {
  212. self.dragedIndexPaths.append(indexPath)
  213. }
  214. return true
  215. }
  216. func collectionView(_ collectionView: NSCollectionView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItemsAt indexes: IndexSet) {
  217. if self.isNeedMarkerLine {
  218. self.markBeginIndexes = self.collectionView.selectionIndexes
  219. }
  220. }
  221. func collectionView(_ collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath proposedDropIndexPath: AutoreleasingUnsafeMutablePointer<NSIndexPath>, dropOperation proposedDropOperation: UnsafeMutablePointer<NSCollectionView.DropOperation>) -> NSDragOperation {
  222. if self.isNeedMarkerLine {
  223. if self.markBeginIndexes.count != 0 {
  224. if !self.markBeginIndexes.contains(proposedDropIndexPath.pointee.item) {
  225. //标记线
  226. var rect = self.collectionView.frameForItem(at: proposedDropIndexPath.pointee.item)
  227. rect.size.height = 2
  228. self.markerLineView.frame = rect
  229. self.markerLineView.isHidden = false
  230. }
  231. }
  232. }
  233. let pboard = draggingInfo.draggingPasteboard
  234. if (pboard.availableType(from: [thumbnailDraggedTypes]) != nil) {
  235. return .move
  236. }
  237. return NSDragOperation(rawValue: 0)
  238. }
  239. func collectionView(_ collectionView: NSCollectionView, acceptDrop draggingInfo: NSDraggingInfo, indexPath: IndexPath, dropOperation: NSCollectionView.DropOperation) -> Bool {
  240. markerLineView.isHidden = true
  241. self.markBeginIndexes = IndexSet()
  242. let pboard = draggingInfo.draggingPasteboard
  243. if (pboard.availableType(from: [NSPasteboard.PasteboardType(rawValue: "localForDraggedTypes")]) != nil) {
  244. let dragIndexPath = self.dragedIndexPaths.first
  245. self.dragedIndexPaths.removeAll()
  246. if (dragIndexPath == nil) {
  247. return false
  248. }
  249. let dragIndex = dragIndexPath!.item
  250. let toIndex = max(0, indexPath.item)
  251. if dragIndex < 0 || dragIndex > self.document.pageCount {
  252. return false
  253. }
  254. if (toIndex >= self.document.pageCount) {
  255. return false
  256. }
  257. if dragIndex == toIndex {
  258. return false
  259. }
  260. var dragPages: [Int] = []
  261. for indexpath in dragedIndexPaths {
  262. dragPages.append(indexpath.item)
  263. }
  264. self.delegate?.thumbnailView?(thumbanView: self, didDragPages: dragPages, indexpath: indexPath)
  265. return true
  266. } else if ((pboard.availableType(from: [.fileURL])) != nil) {
  267. //获取url
  268. var array: [URL] = []
  269. for item: NSPasteboardItem in pboard.pasteboardItems! {
  270. let string: String = item.string(forType: NSPasteboard.PasteboardType.fileURL)!
  271. let url = NSURL(string: string)
  272. /// MARK: 暂时支持PDF格式
  273. if (url?.pathExtension?.lowercased() == "pdf") {
  274. array.append(url! as URL)
  275. }
  276. }
  277. self.delegate?.thumbnailView?(thumbanView: self, didDragAddFiles: array, indexpath: indexPath)
  278. }
  279. return false
  280. }
  281. }
  282. class MyLayoutAttributes: NSCollectionViewLayoutAttributes {
  283. var markerLocation = CGPoint.zero
  284. }