KMEditImagePropertyViewController.swift 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. //
  2. // KMEditImagePropertyViewController.swift
  3. // PDF Master
  4. //
  5. // Created by lxy on 2022/12/28.
  6. //
  7. import Cocoa
  8. public enum KMOperationState {
  9. case none
  10. case began
  11. case changed
  12. case ended
  13. }
  14. class KMEditImagePropertyViewController: NSViewController {
  15. @IBOutlet weak var titleLabel: NSTextField!
  16. @IBOutlet weak var editImageView: NSImageView!
  17. @IBOutlet weak var headerBox: NSBox!
  18. @IBOutlet weak var headerBoxHeight: NSLayoutConstraint!
  19. @IBOutlet weak var headerBoxMaginWidthConstraint: NSLayoutConstraint!
  20. @IBOutlet weak var opacityTitleLabel: NSTextField!
  21. @IBOutlet weak var opacitySlider: NSSlider!
  22. @IBOutlet weak var opacityBox: NSBox!
  23. @IBOutlet weak var opacityBoxBottomMaginConstraint: NSLayoutConstraint!
  24. @IBOutlet weak var cropBox: NSBox!
  25. @IBOutlet weak var cancelBox: NSBox!
  26. @IBOutlet weak var replaceBox: NSBox!
  27. @IBOutlet weak var exportBox: NSBox!
  28. @IBOutlet weak var cropBoxWidthConstraint: NSLayoutConstraint!
  29. // @IBOutlet weak var cutButton: NSButton!
  30. // @IBOutlet weak var replaceButton: NSButton!
  31. // @IBOutlet weak var exportButton: NSButton!
  32. // @IBOutlet weak var cancelCutButton: NSButton!
  33. // @IBOutlet weak var cutRightConstraint: NSLayoutConstraint!
  34. @IBOutlet weak var imageBox: NSBox!
  35. @IBOutlet weak var imageBoxHeight: NSLayoutConstraint!
  36. @IBOutlet weak var opacityBoxHeight: NSLayoutConstraint!
  37. @IBOutlet weak var buttonBoxHeight: NSLayoutConstraint!
  38. @IBOutlet weak var buttonBox: NSBox!
  39. @IBOutlet weak var rotateLeftBox: NSBox!
  40. @IBOutlet weak var rotateRightBox: NSBox!
  41. @IBOutlet weak var flipHorizontalBox: NSBox!
  42. @IBOutlet weak var flipVerticalBox: NSBox!
  43. var rotateLeftVC: KMDesignPropertySelector?
  44. var rotateRightVC: KMDesignPropertySelector?
  45. var flipHorizontalVC: KMDesignPropertySelector?
  46. var flipVerticalVC: KMDesignPropertySelector?
  47. var opacityVC: KMDesignSelect?
  48. var cropVC: KMDesignButton?
  49. var cancelVC: KMDesignButton?
  50. var replaceVC: KMDesignButton?
  51. var exportVC: KMDesignButton?
  52. @IBOutlet weak var alignmentView: KMEditPropertyAlignmentView!
  53. var imagesAreas : [CPDFEditImageArea] = []
  54. var listView : CPDFListView!
  55. var editingAreas: [Any] {
  56. get {
  57. return self.listView.editingAreas() ?? []
  58. }
  59. }
  60. override func viewDidLoad() {
  61. super.viewDidLoad()
  62. self.setup()
  63. self.initData()
  64. self.updateLanguage()
  65. self.updateButtonState(hidden: true)
  66. self.reloadData()
  67. }
  68. func initData() {
  69. self.updateButtonState(hidden: true)
  70. }
  71. func setup() {
  72. self.titleLabel.font = NSFont.SFProTextSemibold(14.0)
  73. self.titleLabel.textColor = NSColor(hex: "#252629")
  74. self.opacityTitleLabel.font = NSFont.SFProTextSemibold(12.0)
  75. self.opacityTitleLabel.textColor = NSColor(hex: "#616469")
  76. self.editImageView.wantsLayer = true
  77. self.editImageView.layer?.backgroundColor = NSColor.white.cgColor
  78. self.alignmentView.didChange = {[unowned self] view, areasArray, boundsArray in
  79. self.changeAreasAlign(areas: areasArray, newBounds: boundsArray)
  80. }
  81. imageBox.backgroundColor(NSColor(hex: "#FFFFFF"))
  82. opacityVC = KMDesignSelect.init(withType: .PopButton)
  83. opacityBox.contentView = opacityVC?.view
  84. opacityBox.fillColor = NSColor.clear
  85. opacityVC?.addItems(withObjectValues: ["25%","50%","75%","100%"])
  86. opacityVC?.selectItem(at: 0)
  87. opacityVC?.delete = self
  88. //alignment
  89. rotateLeftVC = KMDesignPropertySelector.init(withType: .Icon_Btn)
  90. rotateLeftBox.contentView = rotateLeftVC?.view
  91. rotateLeftBox.fillColor = NSColor.clear
  92. rotateLeftVC?.target = self
  93. rotateLeftVC?.action = #selector(leftRotationImageAction)
  94. rotateLeftVC?.image = NSImage(named: "KMImageNameEditPDFRotationLeft")!
  95. rotateLeftVC?._image_disabled = NSImage(named: "KMImageNameEditPDFRotationLeft")!
  96. rotateLeftVC?.updateUI()
  97. rotateRightVC = KMDesignPropertySelector.init(withType: .Icon_Btn)
  98. rotateRightBox.contentView = rotateRightVC?.view
  99. rotateRightBox.fillColor = NSColor.clear
  100. rotateRightVC?.target = self
  101. rotateRightVC?.action = #selector(rightRotationImageAction)
  102. rotateRightVC?.image = NSImage(named: "KMImageNameEditPDFRotationRight")!
  103. rotateRightVC?.updateUI()
  104. flipHorizontalVC = KMDesignPropertySelector.init(withType: .Icon_Btn)
  105. flipHorizontalBox.contentView = flipHorizontalVC?.view
  106. flipHorizontalBox.fillColor = NSColor.clear
  107. flipHorizontalVC?.target = self
  108. flipHorizontalVC?.action = #selector(flipHorizontalImageAction)
  109. flipHorizontalVC?.image = NSImage(named: "KMImageNameEditPDFFlipHorizontal")!
  110. flipHorizontalVC?.updateUI()
  111. flipVerticalVC = KMDesignPropertySelector.init(withType: .Icon_Btn)
  112. flipVerticalBox.contentView = flipVerticalVC?.view
  113. flipVerticalBox.fillColor = NSColor.clear
  114. flipVerticalVC?.target = self
  115. flipVerticalVC?.action = #selector(flipVerticalImageAction)
  116. flipVerticalVC?.image = NSImage(named: "KMImageNameEditPDFFlipVertical")!
  117. flipVerticalVC?.updateUI()
  118. // 设置默认状态下的滑块图像
  119. let customCell = CustomSliderCell()
  120. // 设置滑块图像
  121. customCell.knobImage = NSImage(named: "KMImageNameEditPDFSlider")
  122. // 应用自定义的NSSliderCell到NSSlider
  123. opacitySlider.cell = customCell
  124. // 添加滑动事件处理程序
  125. opacitySlider.target = self
  126. opacitySlider.action = #selector(sliderValueChanged(_:))
  127. cropVC = KMDesignButton.init(withType: .Text)
  128. cropBox.contentView = cropVC?.view
  129. cropBox.fillColor = NSColor.clear
  130. cropVC?.target = self
  131. cropVC?.action = #selector(cutImageAction)
  132. cropVC?.button(type: .Sec, size: .m)
  133. cropVC?.stringValue = NSLocalizedString("Crop", comment: "")
  134. cropVC?.updateUI()
  135. cancelVC = KMDesignButton.init(withType: .Text)
  136. cancelBox.contentView = cancelVC?.view
  137. cancelBox.fillColor = NSColor.clear
  138. cancelVC?.target = self
  139. cancelVC?.action = #selector(cancelCutImageAction)
  140. cancelVC?.button(type: .Sec, size: .m)
  141. cancelVC?.stringValue = NSLocalizedString("Cancel", comment: "")
  142. cancelVC?.updateUI()
  143. replaceVC = KMDesignButton.init(withType: .Text)
  144. replaceBox.contentView = replaceVC?.view
  145. replaceBox.fillColor = NSColor.clear
  146. replaceVC?.target = self
  147. replaceVC?.action = #selector(replaceImageAction)
  148. replaceVC?.button(type: .Sec, size: .m)
  149. replaceVC?.stringValue = NSLocalizedString("Replace", comment: "")
  150. replaceVC?.updateUI()
  151. exportVC = KMDesignButton.init(withType: .Text)
  152. exportBox.contentView = exportVC?.view
  153. exportBox.fillColor = NSColor.clear
  154. exportVC?.target = self
  155. exportVC?.action = #selector(exportImageAction)
  156. exportVC?.button(type: .Sec, size: .m)
  157. exportVC?.stringValue = NSLocalizedString("Extract", comment: "")
  158. exportVC?.updateUI()
  159. }
  160. func updateLanguage(){
  161. self.titleLabel.stringValue = NSLocalizedString("Image", comment: "")
  162. self.opacityTitleLabel.stringValue = NSLocalizedString("Opacity", comment: "")
  163. self.cropVC?.stringValue = NSLocalizedString("Crop", comment: "")
  164. self.cancelVC?.stringValue = NSLocalizedString("Cancel", comment: "")
  165. self.replaceVC?.stringValue = NSLocalizedString("Replace", comment: "")
  166. self.exportVC?.stringValue = NSLocalizedString("Extract", comment: "")
  167. }
  168. func reloadData() {
  169. imagesAreas = []
  170. if self.editingAreas.count > 0 {
  171. let areas = self.editingAreas
  172. self.alignmentView.editingAreas = areas
  173. for i in 0 ... areas.count-1 {
  174. if areas[i] is CPDFEditImageArea {
  175. imagesAreas.append(areas[i] as! CPDFEditImageArea)
  176. }
  177. }
  178. if imagesAreas.count == 1 && imagesAreas.count == areas.count{ //单个图片
  179. self.listView.selectImageAreas = imagesAreas.first
  180. self.headerBox.isHidden = false
  181. self.headerBoxHeight.constant = 176
  182. self.headerBoxMaginWidthConstraint.constant = 8
  183. self.imageBox.isHidden = false
  184. self.imageBoxHeight.constant = 88
  185. self.opacityBox.isHidden = false
  186. self.opacityBoxHeight.constant = 56
  187. self.opacityBoxBottomMaginConstraint.constant = 16
  188. self.buttonBox.isHidden = false
  189. self.cropBox.isHidden = false
  190. self.cancelBox.isHidden = false
  191. self.replaceBox.isHidden = false
  192. self.buttonBoxHeight.constant = 112
  193. self.editImageView.image = self.listView.selectImageAreas.thumbnailImage
  194. let opacity: CGFloat = self.listView.opacity(for: imagesAreas.first)
  195. self.updateImageAreasOpacity(opacity: opacity, state: .ended, needListView: false)
  196. } else if imagesAreas.count > 1 && imagesAreas.count == areas.count { //多选图片
  197. self.headerBox.isHidden = false
  198. self.headerBoxHeight.constant = 82
  199. self.headerBoxMaginWidthConstraint.constant = 0
  200. self.imageBox.isHidden = true
  201. self.imageBoxHeight.constant = 0
  202. self.opacityBox.isHidden = false
  203. self.opacityBoxHeight.constant = 56
  204. self.opacityBoxBottomMaginConstraint.constant = 0
  205. self.buttonBox.isHidden = false
  206. self.cropBox.isHidden = true
  207. self.cancelBox.isHidden = true
  208. self.replaceBox.isHidden = true
  209. self.buttonBoxHeight.constant = 0
  210. var opacity = self.listView.opacity(for: imagesAreas.first)
  211. for area in imagesAreas {
  212. let newOpacity = self.listView.opacity(for: area)
  213. if (opacity < newOpacity) {
  214. opacity = newOpacity
  215. }
  216. }
  217. self.updateImageAreasOpacity(opacity: opacity, state: .ended, needListView: false)
  218. } else if imagesAreas.count > 0 && imagesAreas.count != areas.count { //多选图片跟文字
  219. self.headerBox.isHidden = true
  220. self.headerBoxHeight.constant = 0
  221. self.imageBox.isHidden = true
  222. self.imageBoxHeight.constant = 0
  223. self.opacityBox.isHidden = true
  224. self.opacityBoxHeight.constant = 0
  225. self.buttonBox.isHidden = true
  226. self.buttonBoxHeight.constant = 0
  227. }
  228. }
  229. }
  230. func updateButtonState(hidden:Bool) {
  231. if hidden {
  232. self.cancelBox.isHidden = true
  233. self.cropBoxWidthConstraint.constant = 0
  234. self.cropBox.title = NSLocalizedString("Crop", comment: "")
  235. self.replaceVC?.state = .Norm
  236. self.exportVC?.state = .Norm
  237. self.opacitySlider.isEnabled = true
  238. self.opacityVC?.state = .Norm
  239. self.rotateLeftVC?.state = .Norm
  240. self.rotateRightVC?.state = .Norm
  241. self.flipVerticalVC?.state = .Norm
  242. self.flipHorizontalVC?.state = .Norm
  243. } else {
  244. self.cancelBox.isHidden = false
  245. self.cropBoxWidthConstraint.constant = 120
  246. self.cropBox.title = NSLocalizedString("Confirm Cut", comment: "")
  247. self.replaceVC?.state = .Disabled
  248. self.exportVC?.state = .Disabled
  249. self.opacitySlider.isEnabled = false
  250. self.opacityVC?.state = .Disabled
  251. self.rotateLeftVC?.state = .Disabled
  252. self.rotateRightVC?.state = .Disabled
  253. self.flipVerticalVC?.state = .Disabled
  254. self.flipHorizontalVC?.state = .Disabled
  255. }
  256. }
  257. func updateImageAreasOpacity(opacity: CGFloat, state: KMOperationState, needListView: Bool = true) {
  258. if self.editingAreas.count >= 1 {
  259. if needListView {
  260. for area in imagesAreas {
  261. self.listView.setOpacityEditArea(area, opacity: opacity)
  262. }
  263. }
  264. // self.listView.editingAreas()!.count == 1 &&
  265. if (self.listView.editingAreas()?.first is CPDFEditImageArea) {
  266. self.listView.selectImageAreas = self.listView.editingAreas()!.first as? CPDFEditImageArea
  267. self.editImageView.image = self.listView.selectImageAreas.thumbnailImage
  268. self.opacitySlider.objectValue = self.listView.opacity(for: self.listView.selectImageAreas)
  269. self.opacityVC?.stringValue = Int(opacity * 100).description + "%"
  270. self.editImageView.alphaValue = opacity
  271. }
  272. }
  273. }
  274. private func changeAreasAlign(areas:[Any],newBounds:[String]) {
  275. var oldBounds : [String] = []
  276. for i in 0 ... areas.count-1 {
  277. let area : CPDFEditArea = areas[i] as! CPDFEditArea
  278. oldBounds.append(NSStringFromRect(area.bounds))
  279. self.listView.setBoundsEditArea(area, withBounds: NSRectFromString(newBounds[i]))
  280. }
  281. self.listView.setNeedsDisplayForVisiblePages()
  282. }
  283. }
  284. extension KMEditImagePropertyViewController: KMSelectPopButtonDelegate {
  285. func km_comboBoxSelectionDidChange(_ obj: KMDesignSelect) {
  286. if obj == opacityVC {
  287. print("km_comboBoxSelectionDidChange")
  288. let index = obj.indexOfSelectedItem
  289. var string = obj.items[index]
  290. string = string.replacingOccurrences(of: "%", with: "")
  291. let value = CGFloat(Float(string)! * 0.01)
  292. self.updateImageAreasOpacity(opacity: value, state: .changed)
  293. }
  294. }
  295. func km_controlTextDidEndEditing(_ obj: KMDesignSelect) {
  296. if obj == opacityVC {
  297. print("km_comboBoxSelectionDidChange")
  298. let index = obj.indexOfSelectedItem
  299. var string = obj.items[index]
  300. string = string.replacingOccurrences(of: "%", with: "")
  301. let value = CGFloat(Float(string)! * 0.01)
  302. self.updateImageAreasOpacity(opacity: value, state: .ended)
  303. }
  304. }
  305. }
  306. //MARK: - Action
  307. extension KMEditImagePropertyViewController {
  308. @objc func sliderValueChanged(_ sender: NSSlider) {
  309. let sliderValue = sender.floatValue
  310. self.updateImageAreasOpacity(opacity:CGFloat(sliderValue), state: .changed)
  311. }
  312. @IBAction func rightRotationImageAction(_ sender: Any) {
  313. if self.listView.editingAreas()?.count ?? 0 < 1 {
  314. return
  315. }
  316. let areas = self.listView.editingAreas()
  317. if areas!.count == 1 && (areas!.first is CPDFEditImageArea) {
  318. let imageArea = areas!.first as! CPDFEditImageArea
  319. self.listView.rotate(with: imageArea, rotate: 90)
  320. self.listView.selectImageAreas = imageArea
  321. self.editImageView.image = self.listView.selectImageAreas.thumbnailImage
  322. } else if areas!.count > 1 {
  323. for imagesArea in imagesAreas {
  324. self.listView.rotate(with: imagesArea, rotate: 90)
  325. }
  326. }
  327. }
  328. @IBAction func leftRotationImageAction(_ sender: Any) {
  329. if self.listView.editingAreas()?.count ?? 0 < 1 {
  330. return
  331. }
  332. let areas = self.listView.editingAreas()
  333. if areas!.count == 1 && (areas!.first is CPDFEditImageArea) {
  334. self.listView.rotate(with: self.listView.selectImageAreas, rotate: -90)
  335. if self.listView.editingAreas()!.count == 1 && (self.listView.editingAreas()!.first is CPDFEditImageArea) {
  336. self.listView.selectImageAreas = self.listView.editingAreas()!.first as? CPDFEditImageArea
  337. }
  338. self.editImageView.image = self.listView.selectImageAreas.thumbnailImage
  339. } else {
  340. for imagesArea in imagesAreas {
  341. self.listView.rotate(with: imagesArea, rotate: -90)
  342. }
  343. }
  344. }
  345. @IBAction func flipHorizontalImageAction(_ sender: Any) {
  346. if self.listView.editingAreas()?.count ?? 0 < 1 {
  347. return
  348. }
  349. let areas = self.listView.editingAreas()
  350. if areas != nil {
  351. for item in areas! {
  352. if item is CPDFEditImageArea {
  353. self.listView.horizontalMirror(with: item as? CPDFEditImageArea)
  354. if areas?.count == 1 {
  355. self.editImageView.image = (item as AnyObject).thumbnailImage
  356. }
  357. } else {
  358. for imagesArea in imagesAreas {
  359. self.listView.horizontalMirror(with: imagesArea)
  360. }
  361. }
  362. }
  363. }
  364. }
  365. @IBAction func flipVerticalImageAction(_ sender: Any) {
  366. if self.listView.editingAreas()?.count ?? 0 < 1 {
  367. return
  368. }
  369. let areas = self.listView.editingAreas()
  370. if areas != nil {
  371. for item in areas! {
  372. if item is CPDFEditImageArea {
  373. self.listView.verticalMirror(with: item as? CPDFEditImageArea)
  374. self.editImageView.image = self.listView.selectImageAreas.thumbnailImage
  375. } else {
  376. for imagesArea in imagesAreas {
  377. self.listView.verticalMirror(with: imagesArea)
  378. }
  379. }
  380. }
  381. }
  382. }
  383. @IBAction func cutImageAction(_ sender: Any) {
  384. if self.listView.selectImageAreas == nil {
  385. return
  386. }
  387. if self.listView.cropAreas != nil && self.listView.selectImageAreas != nil{
  388. self.listView.cropEditImageArea(self.listView.selectImageAreas, withBounds: self.listView.cropAreas.cropRect)
  389. } else if self.listView.selectImageAreas != nil {
  390. self.listView.isEditImage = true
  391. self.listView.enterCrop(with: self.listView.selectImageAreas)
  392. self.updateButtonState(hidden: false)
  393. }
  394. }
  395. @IBAction func cancelCutImageAction(_ sender: Any) {
  396. if self.listView.selectImageAreas == nil {
  397. return
  398. }
  399. self.listView.exitCrop(with: self.listView.selectImageAreas)
  400. self.listView.cropAreas = nil
  401. self.listView.isEditImage = false
  402. self.updateButtonState(hidden: true)
  403. }
  404. @IBAction func replaceImageAction(_ sender: NSButton) {
  405. if self.listView.selectImageAreas == nil {
  406. return
  407. }
  408. let panel = NSOpenPanel()
  409. panel.allowsMultipleSelection = false
  410. panel.allowedFileTypes = ["png","jpg"]
  411. panel.beginSheetModal(for: NSApp.mainWindow!) { response in
  412. if response == .OK {
  413. let openPath = panel.url?.path
  414. // let s = self.listView.extractImage(with: self.selectImageAreas, toImagePath: openPath!)
  415. let s = self.listView.replace(self.listView.selectImageAreas, imagePath: openPath!)
  416. if s {
  417. }
  418. }
  419. }
  420. }
  421. // @available(macOS 13.0, *)
  422. @IBAction func exportImageAction(_ sender: Any) {
  423. if self.listView.selectImageAreas == nil {
  424. return
  425. }
  426. if imagesAreas.count == 1 {
  427. let panel = NSSavePanel()
  428. panel.nameFieldStringValue = "\(NSLocalizedString("Untitled", comment: "")).png"
  429. let button = NSButton.init(checkboxWithTitle: NSLocalizedString("Open the document after saving", comment: ""), target: nil, action: nil)
  430. button.state = .on
  431. panel.accessoryView = button
  432. panel.isExtensionHidden = true
  433. let response = panel.runModal()
  434. if response == .OK {
  435. let url = panel.url
  436. let result = self.listView.extractImage(with: self.listView.selectImageAreas, toImagePath: url!.path)
  437. if result {
  438. if button.state == .on { /// 开启文档
  439. NSWorkspace.shared.openFile(url!.path)
  440. } else {
  441. }
  442. }
  443. }
  444. } else if imagesAreas.count > 1 {
  445. let panel = NSOpenPanel()
  446. panel.canChooseFiles = false
  447. panel.canChooseDirectories = true
  448. panel.canCreateDirectories = true
  449. panel.allowsMultipleSelection = false
  450. panel.beginSheetModal(for: NSApp.mainWindow!) { response in
  451. if response == .OK {
  452. let outputURL = panel.url
  453. let s = self.listView.document.documentURL.lastPathComponent
  454. let folderPath = self.listView.document.documentURL.deletingPathExtension().lastPathComponent + "_extract"
  455. var filePath = outputURL?.path.stringByAppendingPathComponent(folderPath)
  456. var i = 1
  457. let testFilePath = filePath
  458. while FileManager.default.fileExists(atPath: filePath!) {
  459. filePath = testFilePath! + "\(i)"
  460. i = i + 1
  461. }
  462. try? FileManager.default.createDirectory(atPath: filePath!, withIntermediateDirectories: false, attributes: nil)
  463. var saveURLs : [URL] = []
  464. for j in 0 ... self.imagesAreas.count-1 {
  465. let documentFileName = self.listView.document.documentURL.deletingPathExtension().lastPathComponent
  466. var outPath = filePath!
  467. outPath = outPath.stringByAppendingPathComponent(documentFileName)
  468. outPath = outPath + "page \(j+1)"
  469. outPath = outPath.stringByAppendingPathExtension("png")
  470. let result = self.listView.extractImage(with: self.imagesAreas[j], toImagePath: outPath)
  471. if result {
  472. saveURLs.append(URL(fileURLWithPath: outPath))
  473. }
  474. }
  475. NSWorkspace.shared.activateFileViewerSelecting(saveURLs)
  476. }
  477. }
  478. }
  479. }
  480. }
  481. class CustomSliderCell: NSSliderCell {
  482. var knobImage: NSImage?
  483. override func drawKnob(_ knobRect: NSRect) {
  484. if let image = knobImage {
  485. let imageSize = NSSize(width: knobRect.size.height, height: knobRect.size.height)
  486. let imageRect = NSRect(origin: knobRect.origin, size: imageSize)
  487. image.draw(in: imageRect)
  488. } else {
  489. super.drawKnob(knobRect)
  490. }
  491. }
  492. }