// // KMEditImagePropertyViewController.swift // PDF Reader Pro // // Created by lxy on 2022/12/28. // import Cocoa public enum KMOperationState { case none case began case changed case ended } enum KMEditImagePropertyViewControllerChangeType { case rotate case flip case add case move } protocol KMEditImagePropertyViewControllerDelegate: NSObject { func editImagePropertyViewControllerDidChanged(controller: KMEditImagePropertyViewController, type: KMEditImagePropertyViewControllerChangeType) } class KMEditImagePropertyViewController: NSViewController { @IBOutlet weak var backgroundView: NSView! @IBOutlet weak var titleLabel: NSTextField! @IBOutlet weak var headerBox: NSBox! @IBOutlet weak var headerBoxHeight: NSLayoutConstraint! @IBOutlet weak var whTitleLabel: NSTextField! @IBOutlet weak var wTextFieldItemView: KMCustomTextField! @IBOutlet weak var whBox: NSBox! @IBOutlet weak var hTextFieldItemView: KMCustomTextField! var whVC: KMDesignButton? private var whState_: KMDesignTokenState = .None @IBOutlet weak var rotateTitleLabel: NSTextField! @IBOutlet weak var rotateTextFieldItemView: KMDesignTextField! @IBOutlet weak var leftRotateBox: NSBox! @IBOutlet weak var rightRotateBox: NSBox! var leftRotateVC: KMDesignButton? var rightRotateVC: KMDesignButton? @IBOutlet weak var opacityTitleLabel: NSTextField! @IBOutlet weak var opacitySlider: NSSlider! @IBOutlet weak var opacityBox: NSBox! @IBOutlet weak var opacityBoxBottomMaginConstraint: NSLayoutConstraint! @IBOutlet weak var opacityBoxTopConstraint: NSLayoutConstraint! @IBOutlet weak var turnOverBox: NSBox! @IBOutlet weak var turnOverTitleLabel: NSTextField! @IBOutlet weak var reverseXBox: NSView! @IBOutlet weak var reverseYBox: NSView! var reverseXVC: KMDesignButton? var reverseYVC: KMDesignButton? @IBOutlet weak var cropBox: NSBox! @IBOutlet weak var confirmBox: NSBox! @IBOutlet weak var cancelBox: NSBox! @IBOutlet weak var replaceBox: NSBox! @IBOutlet weak var exportBox: NSBox! @IBOutlet weak var opacityBoxHeight: NSLayoutConstraint! @IBOutlet weak var buttonBoxHeight: NSLayoutConstraint! @IBOutlet weak var buttonBox: NSBox! @IBOutlet weak var alignmentView: KMEditPropertyAlignmentView! @IBOutlet weak var alignmentViewTopConstraint: NSLayoutConstraint! //NO Image @IBOutlet weak var noImageView: NSImageView! @IBOutlet weak var noTitleLabel: NSTextField! @IBOutlet weak var noSubtitleLabel: NSTextField! @IBOutlet weak var noBackgroundView: NSView! var rotateLeftVC: KMDesignPropertySelector? var rotateRightVC: KMDesignPropertySelector? var flipHorizontalVC: KMDesignPropertySelector? var flipVerticalVC: KMDesignPropertySelector? var opacityVC: KMDesignSelect? @IBOutlet weak var toolsTitleLabel: NSTextField! var cropVC: KMDesignButton? var confirmVC: KMDesignButton? var cancelVC: KMDesignButton? var replaceVC: KMDesignButton? var exportVC: KMDesignButton? var imagesAreas : [CPDFEditImageArea] = [] var listView : CPDFListView! var editingAreas: [Any] { get { return self.listView.editingAreas() ?? [] } } weak var delegate: KMEditImagePropertyViewControllerDelegate? weak var handdler: KMEditPDfHanddler? static let keepProportionsKey = "KMEditPDFKeepProportionsKey" deinit { KMPrint("KMEditImagePropertyViewController deinit.") self.removeNotification() } override func viewDidLoad() { super.viewDidLoad() self.setup() self.initData() self.updateLanguage() self.updateButtonState(hidden: true) self.reloadData() self.addNotification() self.updateUI() } func initData() { self.updateButtonState(hidden: true) } func setup() { self.titleLabel.font = NSFont.SFProTextSemiboldFont(14.0) self.titleLabel.textColor = KMAppearance.Layout.h0Color() self.opacityTitleLabel.font = NSFont.SFProTextSemiboldFont(12.0) self.opacityTitleLabel.textColor = KMAppearance.Layout.h0Color() self.whTitleLabel.stringValue = NSLocalizedString("Size", comment: "") self.wTextFieldItemView.backgroundView.wantsLayer = true self.wTextFieldItemView.backgroundView.layer?.backgroundColor = .white self.wTextFieldItemView.backgroundView.layer?.borderWidth = 1 self.wTextFieldItemView.backgroundView.layer?.borderColor = NSColor(hex: "#DFE1E5").cgColor self.wTextFieldItemView.backgroundView.layer?.cornerRadius = 4 self.wTextFieldItemView.delegate = self self.wTextFieldItemView.offset = 8 self.wTextFieldItemView.rightViewMode = .always let wRightView = NSView() wRightView.frame = NSMakeRect(0, 0, 24, 32) let wIv = NSImageView() wIv.frame = NSMakeRect(0, 8, 16, 16) wIv.image = NSImage(named: "KMImageNameEditPDFWidthIcon") wRightView.addSubview(wIv) self.wTextFieldItemView.rightView = wRightView var formatter = NumberFormatter() formatter.numberStyle = .decimal // formatter.maximum = NSNumber(value: 999) // formatter.minimum = NSNumber(value: 1) self.wTextFieldItemView.textFiled.formatter = formatter self.hTextFieldItemView.backgroundView.wantsLayer = true self.hTextFieldItemView.backgroundView.layer?.backgroundColor = .white self.hTextFieldItemView.backgroundView.layer?.borderWidth = 1 self.hTextFieldItemView.backgroundView.layer?.borderColor = NSColor(hex: "#DFE1E5").cgColor self.hTextFieldItemView.backgroundView.layer?.cornerRadius = 4 self.hTextFieldItemView.delegate = self self.hTextFieldItemView.textFiled.formatter = formatter let hRightView = NSView() hRightView.frame = NSMakeRect(0, 0, 24, 32) let hIv = NSImageView() hIv.frame = NSMakeRect(0, 8, 16, 16) hIv.image = NSImage(named: "KMImageNameEditPDFHeightIcon") hRightView.addSubview(hIv) self.hTextFieldItemView.rightView = hRightView self.hTextFieldItemView.rightViewMode = .always self.alignmentView.didChange = {[unowned self] view, areasArray, boundsArray in self._trackEvent() let textAreas = self.handdler?.editingTextAreas ?? [] let imageAreas = self.handdler?.editingImageAreas ?? [] if textAreas.count > 0 && imageAreas.count > 0 { self._trackAlignEvent() } self.changeAreasAlign(areas: areasArray, newBounds: boundsArray) } self.whVC = KMDesignButton(withType: .Image) self.whBox.contentView = self.whVC?.view self.whBox.borderWidth = 0 self.whVC?.target = self self.whVC?.action = #selector(whAction) self.whVC?.image = NSImage(named: "KMImageNameEditPDFWhExchange")! self.whVC?.image_act = NSImage(named: "KMImageNameEditPDFWhExchangeSelected")! self.whVC?.pagination() whVC?.cornerRadius = 4 whVC?.cornerRadius_hov = 4 whVC?.cornerRadius_disabled = 4 self.whVC?.toolTip = NSLocalizedString("Keep Proportions", comment: "") // self.whVC?.state = self._getKeepProportionsButtonState() ? .Act : .Norm let whState: KMDesignTokenState = self._getKeepProportionsButtonState() ? .Act : .Norm self.whVC?.state = .Norm self._setWHButtonState(whState) self.rotateTitleLabel.stringValue = NSLocalizedString("Rotate", comment: "") self.rotateTextFieldItemView.delegate = self self.rotateTextFieldItemView.enterKeyEndEdit = true self.rotateTextFieldItemView.useToken = false self.leftRotateVC = KMDesignButton(withType: .Image) self.leftRotateBox.contentView = self.leftRotateVC?.view self.leftRotateBox.borderWidth = 0 self.leftRotateVC?.target = self self.leftRotateVC?.action = #selector(leftRotationImageAction) self.leftRotateVC?.image = NSImage(named: "KMImageNameEditPDFRotationLeft")! self.leftRotateVC?.pagination() self.rightRotateVC = KMDesignButton(withType: .Image) self.rightRotateBox.contentView = self.rightRotateVC?.view self.rightRotateBox.borderWidth = 0 self.rightRotateVC?.target = self self.rightRotateVC?.action = #selector(rightRotationImageAction) self.rightRotateVC?.image = NSImage(named: "KMImageNameEditPDFRotationRight")! self.rightRotateVC?.pagination() opacityVC = KMDesignSelect.init(withType: .PopButton) opacityBox.contentView = opacityVC?.view opacityBox.fillColor = KMAppearance.Layout.bgDrakColor() opacityVC?.addItems(withObjectValues: ["25%","50%","75%","100%"]) opacityVC?.selectItem(at: 0) opacityVC?.delete = self opacityVC?.borderColor = KMAppearance.Interactive.s0Color() opacityVC?.borderColor_disabled = KMAppearance.Interactive.s0Color() opacityVC?.background = KMAppearance.Layout.l1Color() opacityVC?.background_hov = KMAppearance.Layout.l1Color() opacityVC?.background_focus = KMAppearance.Layout.l1Color() opacityVC?.background_disabled = KMAppearance.Layout.l1Color() opacityVC?.textColor = KMAppearance.Layout.h1Color() opacityVC?.textColor_hov = KMAppearance.Layout.h1Color() opacityVC?.textColor_focus = KMAppearance.Layout.h1Color() opacityVC?.popViewControllerBackground = KMAppearance.Layout.bgColor() opacityVC?.popViewControllerTextColor = KMAppearance.Layout.h0Color() opacityVC?.popViewControllerEnterFillColor = KMAppearance.Interactive.s0Color() opacityVC?.updateUI() let customCell = CustomSliderCell() customCell.knobImage = NSImage(named: "KMImageNameEditPDFSlider") opacitySlider.cell = customCell opacitySlider.target = self opacitySlider.action = #selector(sliderValueChanged(_:)) opacitySlider.isContinuous = false self.turnOverTitleLabel.stringValue = NSLocalizedString("Flip", comment: "") self.reverseXVC = KMDesignButton(withType: .Image) self.reverseXBox.addSubview(self.reverseXVC!.view) self.reverseXVC?.view.frame = self.reverseXBox.bounds self.reverseXVC?.view.autoresizingMask = [.width, .height] self.reverseXVC?.target = self self.reverseXVC?.action = #selector(flipHorizontalImageAction) self.reverseXVC?.image = NSImage(named: "KMImageNameEditPDFReverseX")! self.reverseXVC?.pagination() self.reverseYVC = KMDesignButton(withType: .Image) self.reverseYBox.addSubview(self.reverseYVC!.view) self.reverseYVC?.view.frame = self.reverseXBox.bounds self.reverseYVC?.view.autoresizingMask = [.width, .height] self.reverseYVC?.target = self self.reverseYVC?.action = #selector(flipVerticalImageAction) self.reverseYVC?.image = NSImage(named: "KMImageNameEditPDFReverseY")! self.reverseYVC?.pagination() self.toolsTitleLabel.stringValue = NSLocalizedString("Tools", comment: "") cropVC = KMDesignButton.init(withType: .Text) cropBox.contentView = cropVC?.view cropBox.fillColor = NSColor.clear cropVC?.target = self cropVC?.action = #selector(cutImageAction) cropVC?.button(type: .Sec, size: .m) cropVC?.stringValue = NSLocalizedString("Crop", comment: "") cropVC?.borderColor = KMAppearance.Interactive.s0Color() cropVC?.background = KMAppearance.Layout.l1Color() cropVC?.background_hov = KMAppearance.Layout.l1Color() cropVC?.background_focus = KMAppearance.Layout.l1Color() cropVC?.textColor = KMAppearance.Layout.h1Color() cropVC?.textColor_hov = KMAppearance.Layout.h1Color() cropVC?.textColor_focus = KMAppearance.Layout.h1Color() cropVC?.updateUI() confirmVC = KMDesignButton.init(withType: .Text) confirmBox.contentView = confirmVC?.view confirmBox.fillColor = NSColor.clear confirmVC?.target = self confirmVC?.action = #selector(confirmVCImageAction) confirmVC?.button(type: .Cta, size: .m) confirmVC?.stringValue = NSLocalizedString("Confirm", comment: "") confirmVC?.updateUI() cancelVC = KMDesignButton.init(withType: .Text) cancelBox.contentView = cancelVC?.view cancelBox.fillColor = NSColor.clear cancelVC?.target = self cancelVC?.action = #selector(cancelCutImageAction) cancelVC?.button(type: .Sec, size: .m) cancelVC?.stringValue = NSLocalizedString("Cancel", comment: "") cancelVC?.borderColor = KMAppearance.Interactive.s0Color() cancelVC?.background = KMAppearance.Layout.l1Color() cancelVC?.background_hov = KMAppearance.Layout.l1Color() cancelVC?.background_focus = KMAppearance.Layout.l1Color() cancelVC?.textColor = KMAppearance.Layout.h1Color() cancelVC?.textColor_hov = KMAppearance.Layout.h1Color() cancelVC?.textColor_focus = KMAppearance.Layout.h1Color() cancelVC?.updateUI() replaceVC = KMDesignButton.init(withType: .Text) replaceBox.contentView = replaceVC?.view replaceBox.fillColor = NSColor.clear replaceVC?.target = self replaceVC?.action = #selector(replaceImageAction) replaceVC?.button(type: .Sec, size: .m) replaceVC?.stringValue = NSLocalizedString("Replace", comment: "") replaceVC?.borderColor = KMAppearance.Interactive.s0Color() replaceVC?.borderColor_hov = KMAppearance.Interactive.s0Color() replaceVC?.borderColor_disabled = KMAppearance.Interactive.s0Color() replaceVC?.background = KMAppearance.Layout.l1Color() replaceVC?.background_hov = KMAppearance.Layout.l1Color() replaceVC?.background_focus = KMAppearance.Layout.l1Color() replaceVC?.background_disabled = KMAppearance.Layout.l1Color() replaceVC?.textColor = KMAppearance.Layout.h1Color() replaceVC?.textColor_hov = KMAppearance.Layout.h1Color() replaceVC?.textColor_focus = KMAppearance.Layout.h1Color() replaceVC?.updateUI() exportVC = KMDesignButton.init(withType: .Text) exportBox.contentView = exportVC?.view exportBox.fillColor = NSColor.clear exportVC?.target = self exportVC?.action = #selector(exportImageAction) exportVC?.button(type: .Sec, size: .m) exportVC?.stringValue = NSLocalizedString("Export", comment: "") exportVC?.borderColor = KMAppearance.Interactive.s0Color() exportVC?.borderColor_hov = KMAppearance.Interactive.s0Color() exportVC?.borderColor_disabled = KMAppearance.Interactive.s0Color() exportVC?.background = KMAppearance.Layout.l1Color() exportVC?.background_hov = KMAppearance.Layout.l1Color() exportVC?.background_focus = KMAppearance.Layout.l1Color() exportVC?.background_disabled = KMAppearance.Layout.l1Color() exportVC?.textColor = KMAppearance.Layout.h1Color() exportVC?.textColor_hov = KMAppearance.Layout.h1Color() exportVC?.textColor_focus = KMAppearance.Layout.h1Color() exportVC?.updateUI() self.noTitleLabel.textColor = KMAppearance.Layout.h1Color() self.noTitleLabel.font = NSFont.SFProTextRegularFont(14) self.noImageView.image = NSImage(named: "No_addimage") self.noImageView.alphaValue = 0.4 let string = self.noSubtitleLabel.stringValue let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineHeightMultiple = 1.32 paragraphStyle.alignment = .center self.noSubtitleLabel.attributedStringValue = NSMutableAttributedString(string: NSLocalizedString(string, comment: ""), attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle, .foregroundColor : NSColor.km_init(hex: "#94989C"), NSAttributedString.Key.font: NSFont.SFProTextRegularFont(12.0)]) self.noBackgroundView.backgroundColor(NSColor.km_init(hex: "#F7F8FA")) } func addNotification() { NotificationCenter.default.addObserver(self, selector: #selector(changeEffectiveAppearance), name: NSNotification.Name(rawValue: "kEffectiveAppearance"), object: nil) } func removeNotification() { NotificationCenter.default.removeObserver(self) DistributedNotificationCenter.default().removeObserver(self) } @objc func changeEffectiveAppearance() { let isDarkModel = KMAdvertisementConfig.isDarkModel() if isDarkModel { self.view.appearance = NSAppearance(named: .darkAqua) } else { self.view.appearance = NSAppearance(named: .aqua) } self.updateUI() } func updateUI() { let isDarkModel = KMAdvertisementConfig.isDarkModel() if isDarkModel { self.backgroundView.backgroundColor(NSColor.km_init(hex: "#252526")) self.noBackgroundView.backgroundColor(NSColor.km_init(hex: "#252526")) whVC?.background_hov = NSColor(red: 71/255, green: 72/255, blue: 75/255, alpha: 1) // whVC?.background_focus = KMAppearance.Layout.l1Color() whVC?.background_disabled = KMAppearance.buttonDisabledColor() leftRotateVC?.borderColor = KMAppearance.Interactive.s0Color() leftRotateVC?.background = KMAppearance.Layout.l1Color() leftRotateVC?.background_hov = NSColor(red: 71/255, green: 72/255, blue: 75/255, alpha: 1) leftRotateVC?.background_focus = KMAppearance.Layout.l1Color() leftRotateVC?.textColor = KMAppearance.Layout.h1Color() leftRotateVC?.textColor_hov = KMAppearance.Layout.h1Color() leftRotateVC?.textColor_focus = KMAppearance.Layout.h1Color() leftRotateVC?.borderColor_hov = KMAppearance.Interactive.s0Color() leftRotateVC?.updateUI() rightRotateVC?.borderColor = KMAppearance.Interactive.s0Color() rightRotateVC?.background = KMAppearance.Layout.l1Color() rightRotateVC?.background_hov = NSColor(red: 71/255, green: 72/255, blue: 75/255, alpha: 1) rightRotateVC?.background_focus = KMAppearance.Layout.l1Color() rightRotateVC?.textColor = KMAppearance.Layout.h1Color() rightRotateVC?.textColor_hov = KMAppearance.Layout.h1Color() rightRotateVC?.textColor_focus = KMAppearance.Layout.h1Color() rightRotateVC?.borderColor_hov = KMAppearance.Interactive.s0Color() rightRotateVC?.updateUI() reverseXVC?.borderColor = KMAppearance.Interactive.s0Color() reverseXVC?.background = KMAppearance.Layout.l1Color() reverseXVC?.background_hov = NSColor(red: 71/255, green: 72/255, blue: 75/255, alpha: 1) reverseXVC?.background_focus = KMAppearance.Layout.l1Color() reverseXVC?.textColor = KMAppearance.Layout.h1Color() reverseXVC?.textColor_hov = KMAppearance.Layout.h1Color() reverseXVC?.textColor_focus = KMAppearance.Interactive.a0Color() reverseXVC?.borderColor_hov = KMAppearance.Interactive.s0Color() reverseXVC?.updateUI() reverseYVC?.borderColor = KMAppearance.Interactive.s0Color() reverseYVC?.background = KMAppearance.Layout.l1Color() reverseYVC?.background_hov = NSColor(red: 71/255, green: 72/255, blue: 75/255, alpha: 1) reverseYVC?.background_focus = KMAppearance.Layout.l1Color() reverseYVC?.textColor = KMAppearance.Layout.h1Color() reverseYVC?.textColor_hov = KMAppearance.Layout.h1Color() reverseYVC?.textColor_focus = KMAppearance.Interactive.a0Color() reverseYVC?.borderColor_hov = KMAppearance.Interactive.s0Color() reverseYVC?.updateUI() self.wTextFieldItemView.backgroundView.layer?.backgroundColor = KMAppearance.Layout.w15Color().cgColor self.wTextFieldItemView.backgroundView.layer?.borderColor = KMAppearance.Layout.w15Color().cgColor self.hTextFieldItemView.backgroundView.layer?.backgroundColor = KMAppearance.Layout.w15Color().cgColor self.hTextFieldItemView.backgroundView.layer?.borderColor = KMAppearance.Layout.w15Color().cgColor self.rotateTextFieldItemView.fillColor = KMAppearance.Layout.w15Color() self.rotateTextFieldItemView.textF?.backgroundView.layer?.backgroundColor = KMAppearance.Layout.w15Color().cgColor // self.rotateTextFieldItemView.updateUI(state: self.rotateTextFieldItemView.state) if self.rotateTextFieldItemView.state == .Focus { let borderColor = NSColor(hex: "68ACF8") self.rotateTextFieldItemView.textF?.backgroundView.layer?.borderColor = borderColor.cgColor } else { let borderColor = NSColor(hex: "56585A") self.rotateTextFieldItemView.textF?.backgroundView.layer?.borderColor = borderColor.cgColor } } else { self.backgroundView.backgroundColor(NSColor.km_init(hex: "#FAFAFA")) self.noBackgroundView.backgroundColor(NSColor.km_init(hex: "#F7F8FA")) leftRotateVC?.borderColor = KMAppearance.Interactive.s0Color() leftRotateVC?.background = KMAppearance.Layout.l1Color() leftRotateVC?.background_hov = KMAppearance.view_bg_dis_color() leftRotateVC?.background_focus = KMAppearance.Layout.l1Color() leftRotateVC?.textColor = KMAppearance.Layout.h1Color() leftRotateVC?.textColor_hov = KMAppearance.Layout.h1Color() leftRotateVC?.textColor_focus = KMAppearance.Layout.h1Color() leftRotateVC?.borderColor_hov = KMAppearance.Interactive.s0Color() leftRotateVC?.updateUI() whVC?.background_hov = KMAppearance.view_bg_dis_color() // whVC?.background_focus = KMAppearance.Layout.l1Color() whVC?.background_disabled = KMAppearance.buttonDisabledColor() reverseXVC?.borderColor = KMAppearance.Interactive.s0Color() reverseXVC?.background = KMAppearance.Layout.l1Color() reverseXVC?.background_hov = KMAppearance.view_bg_dis_color() reverseXVC?.background_focus = KMAppearance.Layout.l1Color() reverseXVC?.textColor = KMAppearance.Layout.h1Color() reverseXVC?.textColor_hov = KMAppearance.Layout.h1Color() reverseXVC?.textColor_focus = KMAppearance.Layout.h1Color() reverseXVC?.borderColor_hov = KMAppearance.Interactive.s0Color() reverseXVC?.updateUI() reverseYVC?.borderColor = KMAppearance.Interactive.s0Color() reverseYVC?.background = KMAppearance.Layout.l1Color() reverseYVC?.background_hov = KMAppearance.view_bg_dis_color() reverseYVC?.background_focus = KMAppearance.Layout.l1Color() reverseYVC?.textColor = KMAppearance.Layout.h1Color() reverseYVC?.textColor_hov = KMAppearance.Layout.h1Color() reverseYVC?.textColor_focus = KMAppearance.Layout.h1Color() reverseYVC?.borderColor_hov = KMAppearance.Interactive.s0Color() reverseYVC?.updateUI() self.wTextFieldItemView.backgroundView.layer?.backgroundColor = .white self.wTextFieldItemView.backgroundView.layer?.borderColor = NSColor(hex: "#DFE1E5").cgColor self.hTextFieldItemView.backgroundView.layer?.backgroundColor = .white self.hTextFieldItemView.backgroundView.layer?.borderColor = NSColor(hex: "#DFE1E5").cgColor self.rightRotateVC?.initDefaultValue() self.leftRotateVC?.initDefaultValue() self.rotateTextFieldItemView.fillColor = .white // self.rotateTextFieldItemView.updateUI(state: self.rotateTextFieldItemView.state) self.rotateTextFieldItemView.textF?.backgroundView.layer?.backgroundColor = .white if self.rotateTextFieldItemView.state == .Focus { let borderColor = NSColor(hex: "68ACF8") self.rotateTextFieldItemView.textF?.backgroundView.layer?.borderColor = borderColor.cgColor } else { let borderColor = NSColor(hex: "DFE1E5") self.rotateTextFieldItemView.textF?.backgroundView.layer?.borderColor = borderColor.cgColor } } for label in [self.whTitleLabel, self.opacityTitleLabel, self.turnOverTitleLabel] { label?.font = NSFont.SFProTextSemiboldFont(12.0) label?.textColor = NSColor.km_init(hex: "#616469") } } func updateLanguage() { let areas = self.editingAreas if imagesAreas.count > 0 && imagesAreas.count != areas.count { //多选图片跟文字 self.titleLabel.stringValue = NSLocalizedString("General Properties", comment: "") } else { self.titleLabel.stringValue = NSLocalizedString("Image", comment: "") } self.noTitleLabel.stringValue = NSLocalizedString("Add Image", comment: "") self.opacityTitleLabel.stringValue = NSLocalizedString("Opacity", comment: "") self.cropVC?.stringValue = NSLocalizedString("Crop", comment: "") self.confirmVC?.stringValue = NSLocalizedString("Confirm", comment: "") self.cancelVC?.stringValue = NSLocalizedString("Cancel", comment: "") self.replaceVC?.stringValue = NSLocalizedString("Replace", comment: "") self.exportVC?.stringValue = NSLocalizedString("Export", comment: "") } func reloadData() { imagesAreas = [] if self.editingAreas.count > 0 { self.noBackgroundView.isHidden = true let areas = self.editingAreas self.alignmentView.editingAreas = areas for i in 0 ... areas.count-1 { if areas[i] is CPDFEditImageArea { imagesAreas.append(areas[i] as! CPDFEditImageArea) } } if imagesAreas.count == 1 && imagesAreas.count == areas.count{ //单个图片 self.listView.selectImageAreas = imagesAreas.first self.headerBox.isHidden = false self.headerBoxHeight.constant = 176 self.opacityBox.isHidden = false self.opacityBoxHeight.constant = 56 self.opacityBoxBottomMaginConstraint.constant = 66 self.opacityBoxTopConstraint.constant = 16 self.buttonBox.isHidden = false self.buttonBoxHeight.constant = 112+16+8 self.alignmentViewTopConstraint.constant = 16 let opacity: CGFloat = self.listView.opacity(for: imagesAreas.first) self.updateImageAreasOpacity(opacity: opacity, state: .ended, needListView: false) self.turnOverBox.isHidden = false self.opacityTitleLabel.isHidden = false // self.whVC?.state = self._getKeepProportionsButtonState() ? .Act : .Norm let whSta: KMDesignTokenState = self._getKeepProportionsButtonState() ? .Act : .Norm self.whVC?.state = .Norm self._setWHButtonState(whSta) self.cropVC?.state = .Norm let isCropMode = self.listView.isCropMode self.replaceVC?.state = isCropMode ? .Disabled : .Norm } else if imagesAreas.count > 1 && imagesAreas.count == areas.count { //多选图片 self.headerBox.isHidden = false self.headerBoxHeight.constant = 176 self.opacityBox.isHidden = false self.opacityBoxHeight.constant = 56 self.opacityBoxBottomMaginConstraint.constant = 66 self.opacityBoxTopConstraint.constant = 0 self.buttonBox.isHidden = false self.buttonBoxHeight.constant = 112+16+8 self.alignmentViewTopConstraint.constant = 16 var opacity = self.listView.opacity(for: imagesAreas.first) for area in imagesAreas { let newOpacity = self.listView.opacity(for: area) if (opacity < newOpacity) { opacity = newOpacity } } self.updateImageAreasOpacity(opacity: opacity, state: .ended, needListView: false) self.turnOverBox.isHidden = false self.opacityTitleLabel.isHidden = false self._updateWHProportionButtonEnableState() self.cropVC?.state = .Disabled self.replaceVC?.state = .Disabled if let data = self.handdler?.editAreasOpacityIsEqual(), data { self.opacitySlider.isEnabled = true self.opacityVC?.state = .Norm } else { self.opacitySlider.isEnabled = true self.opacityVC?.state = .Norm self.opacitySlider.doubleValue = 0 self.opacityVC?.stringValue = "--"; } } else if imagesAreas.count > 0 && imagesAreas.count != areas.count { //多选图片跟文字 self.opacityBoxBottomMaginConstraint.constant = 8 self.alignmentViewTopConstraint.constant = 0 self.opacityBoxTopConstraint.constant = 0 self.headerBoxHeight.constant = 40 self.opacityBox.isHidden = true self.opacityBoxHeight.constant = 0 self.buttonBox.isHidden = true self.buttonBoxHeight.constant = 0 self.turnOverBox.isHidden = true self.opacityTitleLabel.isHidden = true } self.updateLanguage() } else { self.noBackgroundView.isHidden = false } self.updateWHData() self.updateRotateData() } func updateButtonState(hidden: Bool) { if hidden { self.cancelBox.isHidden = true self.confirmBox.isHidden = true self.cropBox.isHidden = false self.replaceVC?.enabled = true self.replaceVC?.state = .Norm self.exportVC?.enabled = true self.opacitySlider.isEnabled = true self.opacityVC?.enabled = true self.rotateLeftVC?.button.isEnabled = true self.rotateLeftVC?.view.alphaValue = 1.0 self.rotateRightVC?.button.isEnabled = true self.rotateRightVC?.view.alphaValue = 1.0 self.flipVerticalVC?.button.isEnabled = true self.flipVerticalVC?.view.alphaValue = 1.0 self.flipHorizontalVC?.button.isEnabled = true self.flipHorizontalVC?.view.alphaValue = 1.0 self.wTextFieldItemView.textFiled.isEnabled = true self.hTextFieldItemView.textFiled.isEnabled = true // self.whVC?.state = self._getKeepProportionsButtonState() ? .Act : .Norm let whSta: KMDesignTokenState = self._getKeepProportionsButtonState() ? .Act : .Norm self.whVC?.state = .Norm self._setWHButtonState(whSta) self.rotateTextFieldItemView.state = .Norm self.rotateTextFieldItemView.kmEnabled = true self.leftRotateVC?.state = .Norm self.rightRotateVC?.state = .Norm self.reverseXVC?.state = .Norm self.reverseYVC?.state = .Norm self.leftRotateVC?.button.isEnabled = true self.rightRotateVC?.button.isEnabled = true self.reverseXVC?.button.isEnabled = true self.reverseYVC?.button.isEnabled = true } else { self.cancelBox.isHidden = false self.confirmBox.isHidden = false self.cropBox.isHidden = true self.replaceVC?.enabled = false self.replaceVC?.state = .Disabled self.exportVC?.enabled = false self.opacitySlider.isEnabled = false self.opacityVC?.enabled = false self.rotateLeftVC?.button.isEnabled = false self.rotateLeftVC?.view.alphaValue = 0.5 self.rotateRightVC?.button.isEnabled = false self.rotateRightVC?.view.alphaValue = 0.5 self.flipVerticalVC?.button.isEnabled = false self.flipVerticalVC?.view.alphaValue = 0.5 self.flipHorizontalVC?.button.isEnabled = false self.flipHorizontalVC?.view.alphaValue = 0.5 self.wTextFieldItemView.textFiled.isEnabled = false self.hTextFieldItemView.textFiled.isEnabled = false self.whVC?.state = .Disabled self._setWHButtonState(.Disabled) self.rotateTextFieldItemView.state = .Disabled self.rotateTextFieldItemView.kmEnabled = false self.leftRotateVC?.state = .Disabled self.rightRotateVC?.state = .Disabled self.reverseXVC?.state = .Disabled self.reverseYVC?.state = .Disabled self.leftRotateVC?.button.isEnabled = false self.rightRotateVC?.button.isEnabled = false self.reverseXVC?.button.isEnabled = false self.reverseYVC?.button.isEnabled = false } self.reloadData() } func updateWHData() { if let data = self.handdler?.editAreasBoundsIsEqualForWidth(), data { let areaFrame = self.handdler?.editingAreas.first?.bounds ?? .zero self.wTextFieldItemView.stringValue = String(format: "%.1f", areaFrame.size.width) } else { self.wTextFieldItemView.stringValue = "--" } if let data = self.handdler?.editAreasBoundsIsEqualForHeight(), data { let areaFrame = self.handdler?.editingAreas.first?.bounds ?? .zero self.hTextFieldItemView.stringValue = String(format: "%.1f", areaFrame.size.height) } else { self.hTextFieldItemView.stringValue = "--" } } func updateRotateData() { if let data = self.handdler?.editAreasRotateIsEqual(), data { let area = self.handdler?.editingImageAreas.first let rotate = self.listView.getRotateWith(area) let stringValue = self.rotateTextFieldItemView.stringValue?.replacingOccurrences(of: "°", with: "") let number = NSNumber(value: rotate) let formatter = NumberFormatter() let string = formatter.string(for: number) self.rotateTextFieldItemView.stringValue = "\(string ?? "0") °" } else { self.rotateTextFieldItemView.stringValue = "--" } } func updateImageAreasOpacity(opacity: CGFloat, state: KMOperationState, needListView: Bool = true) { if self.editingAreas.count >= 1 { if needListView { for area in imagesAreas { self.listView.setOpacityEditArea(area, opacity: opacity) } } // self.listView.editingAreas()!.count == 1 && if (self.listView.editingAreas()?.first is CPDFEditImageArea) { self.listView.selectImageAreas = self.listView.editingAreas()!.first as? CPDFEditImageArea // self.editImageView.image = self.listView.selectImageAreas.thumbnailImage self.opacitySlider.objectValue = self.listView.opacity(for: self.listView.selectImageAreas) self.opacityVC?.stringValue = Int(opacity * 100).description + "%" // self.editImageView.alphaValue = opacity } } } // MARK: - Private Methods private func changeAreasAlign(areas:[Any],newBounds:[String]) { var oldBounds : [String] = [] for i in 0 ... areas.count-1 { let area : CPDFEditArea = areas[i] as! CPDFEditArea oldBounds.append(NSStringFromRect(area.bounds)) self.listView.setBoundsEditArea(area, withBounds: NSRectFromString(newBounds[i])) } self.listView.setNeedsDisplayForVisiblePages() } private func _setKeepProportionsButtonState(state: Bool) { KMDataManager.ud_set(state, forKey: Self.keepProportionsKey) } private func _getKeepProportionsButtonState() -> Bool { let defaultV = true guard let state = KMDataManager.ud_object(forKey: Self.keepProportionsKey) else { return defaultV } return state as? Bool ?? defaultV } private func _updateWHProportionButtonEnableState() { let wEqual = self.handdler?.editAreasBoundsIsEqualForWidth() ?? false let hEqual = self.handdler?.editAreasBoundsIsEqualForHeight() ?? false if (wEqual && hEqual) { // self.whVC?.state = self._getKeepProportionsButtonState() ? .Act : .Norm let state: KMDesignTokenState = self._getKeepProportionsButtonState() ? .Act : .Norm self.whVC?.state = .Norm self._setWHButtonState(state) } else { self.whVC?.state = .Disabled self._setWHButtonState(.Disabled) } } private func _setWHButtonState(_ state: KMDesignTokenState) { self.whState_ = state if state == .Act { self.whVC?.image = NSImage(named: "KMImageNameEditPDFWhExchangeSelected")! } else if state == .Norm { self.whVC?.image = NSImage(named: "KMImageNameEditPDFWhExchange")! } } private func _trackEvent() { kTrackEventManager.trackEvent(event: "SubTbr_EditPDF", withProperties: ["SubTbr_EditImage":"SubTbr_EditImage_PropertiesPanel_All"]) } private func _trackAlignEvent() { kTrackEventManager.trackEvent(event: "SubTbr_EditPDF", withProperties: ["SubTbr_EditText":"SubTbr_EditContent_AlignContent"]) } //MARK: - MouseEvent override func mouseDown(with event: NSEvent) { super.mouseDown(with: event) self.view.window?.makeFirstResponder(nil) } } //MARK: - Action extension KMEditImagePropertyViewController { @objc func sliderValueChanged(_ sender: NSSlider) { let sliderValue = sender.floatValue self.updateImageAreasOpacity(opacity:CGFloat(sliderValue), state: .changed) } @objc func whAction(_ sender: AnyObject) { // let state = self.whVC?.state ?? .None let state = self.whState_ if state == .Disabled { return } if state == .Act { // self.whVC?.state = .Norm self.whVC?.state = .Norm self._setWHButtonState(.Norm) self._setKeepProportionsButtonState(state: false) } else { // self.whVC?.state = .Act self.whVC?.state = .Norm self._setWHButtonState(.Act) self._setKeepProportionsButtonState(state: true) } } @IBAction func rightRotationImageAction(_ sender: Any) { if self.listView.editingAreas()?.count ?? 0 < 1 { return } FMTrackEventManager.defaultManager.trackEvent(event: "SubTbr_PageEdit", withProperties: ["SubTbr_Btn": "Btn_SubTbr_PageEdit_Rotate"]) self._trackEvent() let areas = self.listView.editingAreas() if areas!.count == 1 && (areas!.first is CPDFEditImageArea) { let imageArea = areas!.first as! CPDFEditImageArea self.listView.rotate(with: imageArea, rotate: 90) self.listView.selectImageAreas = imageArea // self.editImageView.image = self.listView.selectImageAreas.thumbnailImage } else if areas!.count > 1 { for imagesArea in imagesAreas { self.listView.rotate(with: imagesArea, rotate: 90) } } self.delegate?.editImagePropertyViewControllerDidChanged(controller: self, type: .rotate) } @IBAction func leftRotationImageAction(_ sender: Any) { if self.listView.editingAreas()?.count ?? 0 < 1 { return } FMTrackEventManager.defaultManager.trackEvent(event: "SubTbr_PageEdit", withProperties: ["SubTbr_Btn": "Btn_SubTbr_PageEdit_Rotate"]) self._trackEvent() let areas = self.listView.editingAreas() if areas!.count == 1 && (areas!.first is CPDFEditImageArea) { self.listView.rotate(with: self.listView.selectImageAreas, rotate: -90) if self.listView.editingAreas()!.count == 1 && (self.listView.editingAreas()!.first is CPDFEditImageArea) { self.listView.selectImageAreas = self.listView.editingAreas()!.first as? CPDFEditImageArea } // self.editImageView.image = self.listView.selectImageAreas.thumbnailImage } else { for imagesArea in imagesAreas { self.listView.rotate(with: imagesArea, rotate: -90) } } self.delegate?.editImagePropertyViewControllerDidChanged(controller: self, type: .rotate) } @IBAction func flipHorizontalImageAction(_ sender: Any) { if self.listView.editingAreas()?.count ?? 0 < 1 { return } FMTrackEventManager.defaultManager.trackEvent(event: "SubTbr_PageEdit", withProperties: ["SubTbr_Btn": "Btn_SubTbr_PageEdit_Reverse"]) self._trackEvent() let areas = self.listView.editingAreas() if areas != nil { for item in areas! { if item is CPDFEditImageArea { self.listView.horizontalMirror(with: item as? CPDFEditImageArea) if areas?.count == 1 { // self.editImageView.image = (item as AnyObject).thumbnailImage } } else { for imagesArea in imagesAreas { self.listView.horizontalMirror(with: imagesArea) } } } } self.delegate?.editImagePropertyViewControllerDidChanged(controller: self, type: .flip) } @IBAction func flipVerticalImageAction(_ sender: Any) { if self.listView.editingAreas()?.count ?? 0 < 1 { return } FMTrackEventManager.defaultManager.trackEvent(event: "SubTbr_PageEdit", withProperties: ["SubTbr_Btn": "Btn_SubTbr_PageEdit_Reverse"]) self._trackEvent() let areas = self.listView.editingAreas() if areas != nil { for item in areas! { if item is CPDFEditImageArea { self.listView.verticalMirror(with: item as? CPDFEditImageArea) // self.editImageView.image = self.listView.selectImageAreas.thumbnailImage } else { for imagesArea in imagesAreas { self.listView.verticalMirror(with: imagesArea) } } } } self.delegate?.editImagePropertyViewControllerDidChanged(controller: self, type: .flip) } @IBAction func cutImageAction(_ sender: Any) { let state = self.cropVC?.state ?? .None if state == .Disabled { return } self._trackEvent() self.handdler?.cropAction() } @IBAction func confirmVCImageAction(_ sender: Any) { self.handdler?.cropComfirmAction() } @IBAction func cancelCutImageAction(_ sender: Any) { self.handdler?.cropCancelAction() } @IBAction func restoreCutImageAction(_ sender: Any) { if self.listView.selectImageAreas == nil { return } self.listView.resetCrop(with: self.listView.selectImageAreas) } @IBAction func replaceImageAction(_ sender: NSButton) { let state = self.replaceVC?.state ?? .None if state == .Disabled { return } if self.listView.selectImageAreas == nil { return } FMTrackEventManager.defaultManager.trackEvent(event: "SubTbr_PageEdit", withProperties: ["SubTbr_Btn": "Btn_SubTbr_PageEdit_Replace"]) self._trackEvent() self.handdler?.replaceAction() } @IBAction func exportImageAction(_ sender: Any) { self._trackEvent() if let data = sender as? NSView { self.handdler?.showExportMenu(data) } } } // MARK: - KMSelectPopButtonDelegate extension KMEditImagePropertyViewController: KMSelectPopButtonDelegate { func km_comboBoxSelectionDidChange(_ obj: KMDesignSelect) { if obj == opacityVC { let index = obj.indexOfSelectedItem var string = obj.items[index] string = string.replacingOccurrences(of: "%", with: "") let value = CGFloat(Float(string)! * 0.01) self.updateImageAreasOpacity(opacity: value, state: .changed) } } func km_controlTextDidEndEditing(_ obj: KMDesignSelect) { if obj == opacityVC { let index = obj.indexOfSelectedItem var string = obj.items[index] string = string.replacingOccurrences(of: "%", with: "") let value = CGFloat(Float(string)! * 0.01) self.updateImageAreasOpacity(opacity: value, state: .ended) } } } // MARK: - KMTextFieldDelegate extension KMEditImagePropertyViewController: KMTextFieldDelegate { func km_didBecomeFirstResponder(textField: AnyObject) { if self.wTextFieldItemView.isEqual(to: textField) { if KMAppearance.isDarkMode() { self.wTextFieldItemView.backgroundView.layer?.backgroundColor = KMAppearance.Layout.w15Color().cgColor self.wTextFieldItemView.backgroundView.layer?.borderColor = KMAppearance.Interactive.a0Color().cgColor } else { self.wTextFieldItemView.backgroundView.layer?.backgroundColor = .white self.wTextFieldItemView.backgroundView.layer?.borderColor = NSColor(hex: "#4982E6").cgColor } self.wTextFieldItemView.backgroundView.layer?.borderWidth = 1 self.wTextFieldItemView.backgroundView.layer?.cornerRadius = 4 } else if self.hTextFieldItemView.isEqual(to: textField) { if KMAppearance.isDarkMode() { self.hTextFieldItemView.backgroundView.layer?.backgroundColor = KMAppearance.Layout.w15Color().cgColor self.hTextFieldItemView.backgroundView.layer?.borderColor = KMAppearance.Interactive.a0Color().cgColor } else { self.hTextFieldItemView.backgroundView.layer?.backgroundColor = .white self.hTextFieldItemView.backgroundView.layer?.borderColor = NSColor(hex: "#4982E6").cgColor } } } func km_controlTextDidEndEditing(textField: AnyObject) { if self.wTextFieldItemView.isEqual(to: textField) { if KMAppearance.isDarkMode() { self.wTextFieldItemView.backgroundView.layer?.backgroundColor = KMAppearance.Layout.w15Color().cgColor self.wTextFieldItemView.backgroundView.layer?.borderColor = KMAppearance.Layout.w15Color().cgColor } else { self.wTextFieldItemView.backgroundView.layer?.backgroundColor = .white self.wTextFieldItemView.backgroundView.layer?.borderColor = NSColor(hex: "#DFE1E5").cgColor } self.wTextFieldItemView.backgroundView.layer?.borderWidth = 1 self.wTextFieldItemView.backgroundView.layer?.cornerRadius = 4 let value = self.wTextFieldItemView.stringValue?.stringToCGFloat() ?? 0 let ss = String(format: "%.1f", value) self.wTextFieldItemView.stringValue = ss } else if self.hTextFieldItemView.isEqual(to: textField) { if KMAppearance.isDarkMode() { self.hTextFieldItemView.backgroundView.layer?.backgroundColor = KMAppearance.Layout.w15Color().cgColor self.hTextFieldItemView.backgroundView.layer?.borderColor = KMAppearance.Layout.w15Color().cgColor } else { self.hTextFieldItemView.backgroundView.layer?.backgroundColor = .white self.hTextFieldItemView.backgroundView.layer?.borderColor = NSColor(hex: "#DFE1E5").cgColor } let value = self.hTextFieldItemView.stringValue?.stringToCGFloat() ?? 0 let ss = String(format: "%.1f", value) self.hTextFieldItemView.stringValue = ss } else if self.rotateTextFieldItemView.isEqual(to: textField) { let stringValue = self.rotateTextFieldItemView.stringValue?.replacingOccurrences(of: "°", with: "") let formatter = NumberFormatter() let number = NSNumber(value: Int(stringValue ?? "") ?? 0) let string = formatter.string(for: number) self.rotateTextFieldItemView.stringValue = "\(string ?? "0") °" let areas = self.handdler?.editingImageAreas ?? [] for area in areas { let rotate = self.listView.getRotateWith(area) let rotate2 = string?.stringToCGFloat() ?? 0 let offset = rotate2-rotate if offset != 0 { self.listView.rotate(with: area, rotate: offset) kTrackEventManager.trackEvent(event: "SubTbr_EditPDF", withProperties: ["SubTbr_EditImage":"SubTbr_EditImage_PropertiesPanel_CustomRotate"]) } } } } func km_controlTextDidChange(textField: AnyObject) { if self.wTextFieldItemView.isEqual(to: textField) { let number = NSNumber(value: Int(self.wTextFieldItemView.stringValue ?? "") ?? 0) let string = self.wTextFieldItemView.textFiled.formatter?.string(for: number) self.wTextFieldItemView.stringValue = string ?? "" let areas = self.handdler?.editingImageAreas ?? [] self.listView.editAreaBoundUpdating = true for area in areas { // let state = self.whVC?.state ?? .None let state = self.whState_ var bounds = area.bounds var width = string?.stringToCGFloat() ?? 0 if width == 0 { return } let pageBounds = area.page?.bounds ?? .zero if width > NSWidth(pageBounds) { width = NSWidth(pageBounds) let value = String(format: "%.1f", width) self.wTextFieldItemView.stringValue = value } let wOffset = width-bounds.size.width // bounds.origin.x = max(0, (pageBounds.size.width-width)*0.5) bounds.origin.x = max(0, bounds.origin.x-wOffset * 0.5) bounds.origin.x = min(bounds.origin.x, pageBounds.size.width-width) if state == .Act { // 寛高约束 var scale: CGFloat = 0 if bounds.size.width != 0 { scale = width / bounds.size.width } bounds.size.height *= scale bounds.size.width = width let value = String(format: "%.1f", bounds.size.height) self.hTextFieldItemView.stringValue = value } else { bounds.size.width = width } self.listView.setBoundsEditArea(area, withBounds: bounds) } DispatchQueue.main.async { self._updateWHProportionButtonEnableState() } } else if self.hTextFieldItemView.isEqual(to: textField) { let number = NSNumber(value: Int(self.hTextFieldItemView.stringValue ?? "") ?? 0) let string = self.hTextFieldItemView.textFiled.formatter?.string(for: number) self.hTextFieldItemView.stringValue = string ?? "" // let areas = self.listView?.editingAreas() as? [CPDFEditArea] ?? [] let areas = self.handdler?.editingImageAreas ?? [] self.listView.editAreaBoundUpdating = true for area in areas { // if let area = areas.first as? CPDFEditImageArea { // let state = self.whVC?.state ?? .None let state = self.whState_ var bounds = area.bounds var height = string?.stringToCGFloat() ?? 0 if height == 0 { return } let pageBounds = area.page?.bounds ?? .zero if height > NSHeight(pageBounds) { height = NSHeight(pageBounds) let value = String(format: "%.1f", height) self.hTextFieldItemView.stringValue = value } let hOffset = height-bounds.size.height // bounds.origin.y = max(0, (pageBounds.size.height-height)*0.5) bounds.origin.y = max(0, bounds.origin.y-hOffset * 0.5) bounds.origin.y = min(bounds.origin.y, pageBounds.size.height-height) if state == .Act { // 寛高约束 var scale: CGFloat = 0 if bounds.size.height != 0 { scale = height / bounds.size.height } bounds.size.width *= scale bounds.size.height = height let value = String(format: "%.1f", bounds.size.width) self.wTextFieldItemView.stringValue = value } else { bounds.size.height = height } self.listView.setBoundsEditArea(area, withBounds: bounds) } DispatchQueue.main.async { self._updateWHProportionButtonEnableState() } } else if self.rotateTextFieldItemView.isEqual(to: textField) { // let stringValue = self.rotateTextFieldItemView.stringValue // let number = NSNumber(value: Int(stringValue ?? "") ?? 0) // let formatter = NumberFormatter() // let string = formatter.string(for: number) // let string = self.rotateTextFieldItemView.kFormatter?.string(for: number) // self.rotateTextFieldItemView.stringValue = string } } } class CustomSliderCell: NSSliderCell { var knobImage: NSImage? override func drawKnob(_ knobRect: NSRect) { if let image = knobImage { let imageSize = NSSize(width: knobRect.size.height, height: knobRect.size.height) let imageRect = NSRect(origin: knobRect.origin, size: imageSize) image.draw(in: imageRect) } else { super.drawKnob(knobRect) } } }