// // KMRectangleController.swift // PDF Reader Pro // // Created by Niehaoyu on 2024/11/26. // import Cocoa import KMComponentLibrary class KMRectangleController: NSViewController { //Color @IBOutlet var colorBGView: NSView! @IBOutlet var colorLabel: NSTextField! @IBOutlet var colorGroup: ComponentCColorGroup! @IBOutlet var colorSlider: ComponentSlider! @IBOutlet var colorOpacitySelect: ComponentSelect! @IBOutlet var fillColorGroup: ComponentCColorGroup! //Line @IBOutlet var lineBGView: NSView! @IBOutlet var lineLabel: NSTextField! @IBOutlet var lineTypeSelector: ComponentCSelectorGroup! @IBOutlet var lineWidthSlider: ComponentSlider! @IBOutlet var lineWidthSelect: ComponentSelect! @IBOutlet var linedashInfoView: NSView! @IBOutlet var lineDashSlider: ComponentSlider! @IBOutlet var lineDashSelect: ComponentSelect! private let solidProperty = ComponentCSelectorProperty.init(size: .s, state: .normal, text: "", iconImage: NSImage(named: "lineStyle_solid")) private let dashProperty = ComponentCSelectorProperty.init(size: .s, state: .normal, text: "", iconImage: NSImage(named: "lineStyle_dash")) private var annotations: [CPDFAnnotation] = [] //CPDFCircleAnnotation, CPDFSquareAnnotation private var circleAnnotations: [CPDFCircleAnnotation] { get { var validAnnotations: [CPDFCircleAnnotation] = [] for annotation in annotations { if annotation is CPDFCircleAnnotation { validAnnotations.append((annotation as! CPDFCircleAnnotation)) } } return validAnnotations } } private var squareAnnotations: [CPDFSquareAnnotation] { get { var validAnnotations: [CPDFSquareAnnotation] = [] for annotation in annotations { if annotation is CPDFSquareAnnotation { validAnnotations.append((annotation as! CPDFSquareAnnotation)) } } return validAnnotations } } var pdfView: CPDFListView? var viewManager: KMPDFViewManager? //MARK: - func override func viewDidAppear() { super.viewDidAppear() colorSlider.reloadData() lineWidthSlider.reloadData() lineDashSlider.reloadData() } override func viewDidLoad() { super.viewDidLoad() // Do view setup here. setupProperty() } func setupProperty() { //Color colorLabel.stringValue = KMLocalizedString("Color") colorLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") colorLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium") colorGroup.delegate = self fillColorGroup.delegate = self colorSlider.properties = ComponentSliderProperty(size: .m, percent: 1) colorSlider.delegate = self colorOpacitySelect.properties = ComponentSelectProperties(size: .s, state: .normal, creatable: true, text: "100%", textUnit: "%", regexString: "0123456789%") if true { var opacityItems: [ComponentMenuitemProperty] = [] for string in ["25%", "50%", "75%", "100%"] { let item = ComponentMenuitemProperty(type: .normal, text: string) opacityItems.append(item) } colorOpacitySelect.updateMenuItemsArr(opacityItems) } colorOpacitySelect.delegate = self //Line lineLabel.stringValue = KMLocalizedString("Line") lineLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") lineLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium") lineTypeSelector.updateItemProperty([solidProperty, dashProperty]) lineTypeSelector.delegate = self lineWidthSlider.properties = ComponentSliderProperty(size: .m, percent: 1) lineWidthSlider.delegate = self lineWidthSelect.properties = ComponentSelectProperties(size: .s, state: .normal, creatable: true, text: "2", textUnit: " pt", regexString: "0123456789 pt") if true { var opacityItems: [ComponentMenuitemProperty] = [] for string in ["1 pt", "2 pt", "4 pt", "6 pt", "8 pt", "10 pt"] { let item = ComponentMenuitemProperty(type: .normal, text: string) opacityItems.append(item) } lineWidthSelect.updateMenuItemsArr(opacityItems) } lineWidthSelect.delegate = self lineDashSlider.properties = ComponentSliderProperty(size: .m, percent: 1) lineDashSlider.delegate = self lineDashSelect.properties = ComponentSelectProperties(size: .s, state: .normal, creatable: true, text: "2", textUnit: " pt", regexString: "0123456789 pt") if true { var opacityItems: [ComponentMenuitemProperty] = [] for string in ["1 pt", "2 pt", "4 pt", "6 pt", "8 pt", "10 pt"] { let item = ComponentMenuitemProperty(type: .normal, text: string) opacityItems.append(item) } lineDashSelect.updateMenuItemsArr(opacityItems) } lineDashSelect.delegate = self } func reloadData() { guard let pdfView = self.pdfView else { return } var firstAnnotation: CPDFAnnotation? self.annotations.removeAll() let allAnnotations: [CPDFAnnotation] = pdfView.activeAnnotations as? [CPDFAnnotation] ?? [] for annotation in allAnnotations { if annotation is CPDFCircleAnnotation { annotations.append((annotation as! CPDFCircleAnnotation)) } else if annotation is CPDFSquareAnnotation { annotations.append((annotation as! CPDFSquareAnnotation)) } } if annotations.count > 0 { firstAnnotation = annotations.first } if true { var colors: [NSColor] = [] var fillColors: [NSColor] = [] if let annotation = firstAnnotation { if annotation is CPDFSquareAnnotation { colors = KMAnnotationPropertiesColorManager.manager.rectangle_Colors fillColors = KMAnnotationPropertiesColorManager.manager.rectangle_Fill_Colors } else if annotation is CPDFCircleAnnotation { colors = KMAnnotationPropertiesColorManager.manager.circle_Colors fillColors = KMAnnotationPropertiesColorManager.manager.circle_Fill_Colors } } else if viewManager?.subToolMode == .Rectangle { colors = KMAnnotationPropertiesColorManager.manager.rectangle_Colors fillColors = KMAnnotationPropertiesColorManager.manager.rectangle_Fill_Colors } else if viewManager?.subToolMode == .Circle { colors = KMAnnotationPropertiesColorManager.manager.circle_Colors fillColors = KMAnnotationPropertiesColorManager.manager.circle_Fill_Colors } if colors.count > 4 { let colorAProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[0]) let colorBProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[1]) let colorCProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[2]) let colorDProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[3]) let colorEProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: true, color: colors[4]) colorGroup.setUpWithColorPropertys([colorAProperty, colorBProperty, colorCProperty, colorDProperty], customItemProperty: colorEProperty) } if fillColors.count > 4 { let colorAProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: fillColors[0]) let colorBProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: fillColors[1]) let colorCProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: fillColors[2]) let colorDProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: fillColors[3]) let colorEProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: true, color: fillColors[4]) fillColorGroup.setUpWithColorPropertys([colorAProperty, colorBProperty, colorCProperty, colorDProperty], customItemProperty: colorEProperty) } } var _borderColor: NSColor? var _fillColor: NSColor? var _opacity: CGFloat? var _borderStyle: CPDFBorderStyle? var _borderWidth: CGFloat? var _borderDashWidth: CGFloat? if let annotation = firstAnnotation { _borderColor = annotation.color if annotation is CPDFCircleAnnotation { _fillColor = (annotation as! CPDFCircleAnnotation).interiorColor } else if annotation is CPDFSquareAnnotation { _fillColor = (annotation as! CPDFSquareAnnotation).interiorColor } _opacity = annotation.opacity let border: CPDFBorder = annotation.border ?? CPDFBorder() _borderStyle = border.style _borderWidth = border.lineWidth if border.style == .dashed { var dash = 1.0 for dashPattern in border.dashPattern { if let value = dashPattern as? CGFloat { dash = value break } } _borderDashWidth = dash } if annotations.count > 1 { if CPDFListView.isAnnotationsContainMultiType(annotations, withType: .color) == true { _borderColor = nil } if CPDFListView.isAnnotationsContainMultiType(annotations, withType: .interiorColor) == true { _fillColor = nil } if CPDFListView.isAnnotationsContainMultiType(annotations, withType: .opacity) { _opacity = nil } if CPDFListView.isAnnotationsContainMultiType(annotations, withType: .border_Style) { _borderStyle = nil } if CPDFListView.isAnnotationsContainMultiType(annotations, withType: .line_Width) { _borderWidth = nil } if CPDFListView.isAnnotationsContainMultiType(annotations, withType: .dash_Pattern) { _borderDashWidth = nil } } } else { if viewManager?.subToolMode == .Rectangle { _borderColor = CPDFSquareAnnotation.defaultColor() _fillColor = CPDFSquareAnnotation.defaultInteriorColor() _opacity = CPDFSquareAnnotation.defaultOpacity() let border: CPDFBorder = CPDFSquareAnnotation.defaultBorder() _borderStyle = border.style _borderWidth = border.lineWidth if border.style == .dashed { var dash = 1.0 for dashPattern in border.dashPattern { if let value = dashPattern as? CGFloat { dash = value break } } _borderDashWidth = dash } } else if viewManager?.subToolMode == .Circle { _borderColor = CPDFCircleAnnotation.defaultColor() _fillColor = CPDFCircleAnnotation.defaultInteriorColor() _opacity = CPDFCircleAnnotation.defaultOpacity() let border: CPDFBorder = CPDFCircleAnnotation.defaultBorder() _borderStyle = border.style _borderWidth = border.lineWidth if border.style == .dashed { var dash = 1.0 for dashPattern in border.dashPattern { if let value = dashPattern as? CGFloat { dash = value break } } _borderDashWidth = dash } } } colorGroup.currentColor = _borderColor colorGroup.refreshUI() fillColorGroup.currentColor = _fillColor fillColorGroup.refreshUI() if let value = _opacity { colorSlider.properties.percent = value colorOpacitySelect.properties.text = String(format: "%.0f%@", value*100, "%") } else { colorSlider.properties.percent = 0 colorOpacitySelect.properties.text = "-" } colorSlider.reloadData() colorOpacitySelect.reloadData() linedashInfoView.isHidden = true dashProperty.state = .normal solidProperty.state = .normal if let value = _borderStyle { if value == .dashed { linedashInfoView.isHidden = false dashProperty.state = .pressed } else if value == .solid { solidProperty.state = .pressed } } lineTypeSelector.reloadData() if let value = _borderWidth { let percent = value/18 lineWidthSlider.properties.percent = percent lineWidthSelect.properties.text = String(format: "%.0f%@", value, " pt") } else { lineWidthSlider.properties.percent = 0 lineWidthSelect.properties.text = "-" } lineWidthSlider.reloadData() lineWidthSelect.reloadData() if let value = _borderDashWidth { let percent = value / 18 lineDashSlider.properties.percent = percent lineDashSelect.properties.text = String(format: "%.0f%@", value, " pt") } else { lineDashSlider.properties.percent = 0 lineDashSelect.properties.text = "-" } lineDashSlider.reloadData() lineDashSelect.reloadData() } //MARK: - Mouse override func mouseDown(with event: NSEvent) { super.mouseDown(with: event) view.window?.makeFirstResponder(nil) } } //MARK: - ComponentCColorDelegate extension KMRectangleController: ComponentCColorDelegate { func componentCColorDidChooseColor(_ view: NSView, _ color: NSColor?) { if view == colorGroup { CPDFAnnotation.updateAnnotations(annotations, newColor: color, withPDFView: pdfView) if circleAnnotations.count > 0 || viewManager?.subToolMode == .Circle { CPDFCircleAnnotation.updateDefault_Color(color) } if squareAnnotations.count > 0 || viewManager?.subToolMode == .Rectangle { CPDFSquareAnnotation.updateDefault_Color(color) } } else if view == fillColorGroup { CPDFAnnotation.updateAnnotations(annotations, newInteriorColor: color, withPDFView: pdfView) if circleAnnotations.count > 0 || viewManager?.subToolMode == .Circle { CPDFCircleAnnotation.updateDefault_InteriorColor(color) } if squareAnnotations.count > 0 || viewManager?.subToolMode == .Rectangle { CPDFSquareAnnotation.updateDefault_InteriorColor(color) } } reloadData() } } //MARK: - ComponentSliderDelegate extension KMRectangleController: ComponentSliderDelegate { func componentSliderDidUpdate(_ view: ComponentSlider) { if view == colorSlider { let opacity = view.properties.percent CPDFAnnotation.updateAnnotations(annotations, newOpacity: opacity, withPDFView: pdfView) if circleAnnotations.count > 0 || viewManager?.subToolMode == .Circle { CPDFCircleAnnotation.updateDefault_Opacity(opacity) } if squareAnnotations.count > 0 || viewManager?.subToolMode == .Rectangle { CPDFSquareAnnotation.updateDefault_Opacity(opacity) } } else if view == lineWidthSlider { let value = view.properties.percent * 18 CPDFAnnotation.updateAnnotations(annotations, newLineWidth: value, withPDFView: pdfView) if circleAnnotations.count > 0 || viewManager?.subToolMode == .Circle { CPDFCircleAnnotation.updateDefault_LineWidth(value) } if squareAnnotations.count > 0 || viewManager?.subToolMode == .Rectangle { CPDFSquareAnnotation.updateDefault_LineWidth(value) } } else if view == lineDashSlider { let value = view.properties.percent * 18 CPDFAnnotation.updateAnnotations(annotations, newDashPattern: value, withPDFView: pdfView) if circleAnnotations.count > 0 || viewManager?.subToolMode == .Circle{ CPDFCircleAnnotation.updateDefault_DashPattern(value) } if squareAnnotations.count > 0 || viewManager?.subToolMode == .Rectangle { CPDFSquareAnnotation.updateDefault_DashPattern(value) } } reloadData() } } //MARK: - ComponentSelectDelegate extension KMRectangleController: ComponentSelectDelegate { func componentSelectTextDidEndEditing(_ view: ComponentSelect, removeUnit text: String?) { if let result = text { if view == colorOpacitySelect { let opacity = max(0, min(1, result.stringToCGFloat()/100)) CPDFAnnotation.updateAnnotations(annotations, newOpacity: opacity, withPDFView: pdfView) if circleAnnotations.count > 0 || viewManager?.subToolMode == .Circle { CPDFCircleAnnotation.updateDefault_Opacity(opacity) } if squareAnnotations.count > 0 || viewManager?.subToolMode == .Rectangle { CPDFSquareAnnotation.updateDefault_Opacity(opacity) } } else if view == lineWidthSelect { var value = result.stringToCGFloat() if value > 18 { value = 18 } else if value < 0 { value = 0 } CPDFAnnotation.updateAnnotations(annotations, newLineWidth: value, withPDFView: pdfView) if circleAnnotations.count > 0 || viewManager?.subToolMode == .Circle { CPDFCircleAnnotation.updateDefault_LineWidth(value) } if squareAnnotations.count > 0 || viewManager?.subToolMode == .Rectangle { CPDFSquareAnnotation.updateDefault_LineWidth(value) } } else if view == lineDashSelect { var value = result.stringToCGFloat() if value > 18 { value = 18 } else if value < 0 { value = 0 } CPDFAnnotation.updateAnnotations(annotations, newDashPattern: value, withPDFView: pdfView) if circleAnnotations.count > 0 || viewManager?.subToolMode == .Circle{ CPDFCircleAnnotation.updateDefault_DashPattern(value) } if squareAnnotations.count > 0 || viewManager?.subToolMode == .Rectangle { CPDFSquareAnnotation.updateDefault_DashPattern(value) } } reloadData() } } func componentSelectDidSelect(view: ComponentSelect?, menuItemProperty: ComponentMenuitemProperty?) { if var result = menuItemProperty?.text { if let textUnit = view?.properties.textUnit { result = result.stringByDeleteCharString(textUnit) } if view == colorOpacitySelect { let opacity = max(0, min(1, result.stringToCGFloat()/100)) CPDFAnnotation.updateAnnotations(annotations, newOpacity: opacity, withPDFView: pdfView) if circleAnnotations.count > 0 || viewManager?.subToolMode == .Circle { CPDFCircleAnnotation.updateDefault_Opacity(opacity) } if squareAnnotations.count > 0 || viewManager?.subToolMode == .Rectangle { CPDFSquareAnnotation.updateDefault_Opacity(opacity) } } else if view == lineWidthSelect { var value = result.stringToCGFloat() if value > 18 { value = 18 } else if value < 0 { value = 0 } CPDFAnnotation.updateAnnotations(annotations, newLineWidth: value, withPDFView: pdfView) if circleAnnotations.count > 0 || viewManager?.subToolMode == .Circle { CPDFCircleAnnotation.updateDefault_LineWidth(value) } if squareAnnotations.count > 0 || viewManager?.subToolMode == .Rectangle { CPDFSquareAnnotation.updateDefault_LineWidth(value) } } else if view == lineDashSelect { var value = result.stringToCGFloat() if value > 18 { value = 18 } else if value < 0 { value = 0 } CPDFAnnotation.updateAnnotations(annotations, newDashPattern: value, withPDFView: pdfView) if circleAnnotations.count > 0 || viewManager?.subToolMode == .Circle{ CPDFCircleAnnotation.updateDefault_DashPattern(value) } if squareAnnotations.count > 0 || viewManager?.subToolMode == .Rectangle { CPDFSquareAnnotation.updateDefault_DashPattern(value) } } reloadData() } } } //MARK: - ComponentCSelectorGroupDelegate extension KMRectangleController: ComponentCSelectorGroupDelegate { func componentCSelectorGroupDidChoose(_ view: ComponentCSelectorGroup, _ item: ComponentCSelectorItem) { if item.properties == solidProperty { CPDFAnnotation.updateAnnotations(annotations, newBorderStyle: .solid, withPDFView: pdfView) if circleAnnotations.count > 0 || viewManager?.subToolMode == .Circle { CPDFCircleAnnotation.updateDefault_Style(.solid) } if squareAnnotations.count > 0 || viewManager?.subToolMode == .Rectangle { CPDFSquareAnnotation.updateDefault_Style(.solid) } } else if item.properties == dashProperty { CPDFAnnotation.updateAnnotations(annotations, newBorderStyle: .dashed, withPDFView: pdfView) if circleAnnotations.count > 0 || viewManager?.subToolMode == .Circle { CPDFCircleAnnotation.updateDefault_Style(.dashed) } if squareAnnotations.count > 0 || viewManager?.subToolMode == .Rectangle { CPDFSquareAnnotation.updateDefault_Style(.dashed) } } reloadData() } }