// // KMEditPDFPopToolBarWindow.swift // PDF Reader Pro // // Created by tangchao on 2024/6/25. // import Cocoa @objcMembers class KMEditPDFPopToolBarWindow: NSWindow { static let shared = KMEditPDFPopToolBarWindow() var style: KMEditPDFToolbarStyle = .text var isMultiple: Bool = false let model = KMEditPDFModel() var itemClick: ((KMEditPDFToolbarItemKey, Any?)->Void)? convenience init() { let rect = NSRect(x: 0, y: 0, width: 400, height: 44) let styleMask: NSWindow.StyleMask = [.fullSizeContentView] self.init(contentRect: rect, styleMask: styleMask, backing: .buffered, defer: false) } override init(contentRect: NSRect, styleMask style: NSWindow.StyleMask, backing backingStoreType: NSWindow.BackingStoreType, defer flag: Bool) { super.init(contentRect: contentRect, styleMask: style, backing: backingStoreType, defer: flag) let contentViewC = KMEditPDFPopToolBarController() self.contentViewController = contentViewC self.titlebarAppearsTransparent = true self.titleVisibility = .hidden // self.level = .popUpMenu // self.isMovableByWindowBackground = false self.isMovable = false self.contentView?.wantsLayer = true self.contentView?.layer?.cornerRadius = 4 self.contentView?.layer?.masksToBounds = true self.backgroundColor = .clear contentViewC.itemClick = { [weak self] itemKey, obj in self?.itemClick?(itemKey, obj) } } func show(relativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge) { let contentViewC = (self.contentViewController as? KMEditPDFPopToolBarController) contentViewC?.fontColor = self.model.fontColors.last ?? .black contentViewC?.areaCount = self.model.editingAreas?.count ?? 0 // contentViewC?.toolbarView?.reloadData() var width: CGFloat = 392 if self.style.contains(.text) { if self.style.contains(.image) { // text + image contentViewC?.itemKeys = [.alignmentLeft, .alignmentCenterX, .alignmentRight, .alignmentjustifiedX, .alignmentTop, .alignmentCenterY, .alignmentBottom, .alignmentjustifiedY] var datas: [KMEditPDFToolbarModel] = [] for key in contentViewC?.itemKeys ?? [] { let model = KMEditPDFToolbarModel() model.itemKey = key if key == .alignmentjustifiedX || key == .alignmentjustifiedY { let areas = self.model.editingAreas ?? [] model.isEnabled = areas.count > 2 } datas.append(model) } contentViewC?.datas = datas width = 320-36 } else { // text if self.isMultiple { width = 478 contentViewC?.itemKeys = [.color, .fontStyle, .fontAdd, .fontReduce, .fontBold, .fontItalic, .textAlignment, .separator, .alignmentLeft, .alignmentTop] var datas: [KMEditPDFToolbarModel] = [] for key in contentViewC?.itemKeys ?? [] { let model = KMEditPDFToolbarModel() model.itemKey = key if key == .color { model.isEnabled = self.model.editAreasFontColorIsEqual() } else if key == .fontStyle { if self.model.editAreasFontNameIsEqual() { model.isEnabled = true model.fontName = self.model.fontNames.first } else { model.isEnabled = false model.fontName = nil } } else if key == .fontAdd { model.isEnabled = self._fontSizeItemIsEnabled() } else if key == .fontReduce { model.isEnabled = self._fontSizeItemIsEnabled() } else if key == .fontBold { model.isEnabled = self.model.editAreasFontBoldIsEqual() if self.model.editAreasFontBoldIsEqual() { model.isSelected = self.model.fontBolds.first ?? false } } else if key == .fontItalic { model.isEnabled = self.model.editAreasFontItalicIsEqual() if self.model.editAreasFontItalicIsEqual() { model.isSelected = self.model.fontItalics.first ?? false } } else if key == .textAlignment { // model.isEnabled = self.model.editAreasTextAlignmentIsEqual() if let data = self._fetchTextAlign() { model.textAlign = data } // model.isSelected = true } datas.append(model) } contentViewC?.datas = datas } else { var needUpdateData = false width = 392 contentViewC?.itemKeys = [.color, .fontStyle, .fontAdd, .fontReduce, .fontBold, .fontItalic, .textAlignment] let keyCnt = contentViewC?.itemKeys.count ?? 0 let dataCnt = contentViewC?.datas.count ?? 0 if dataCnt == 0 || dataCnt != keyCnt { needUpdateData = true } var datas: [KMEditPDFToolbarModel] = [] for key in contentViewC?.itemKeys ?? [] { let model = KMEditPDFToolbarModel() model.itemKey = key if key == .color { model.isEnabled = self.model.editAreasFontColorIsEqual() } else if key == .fontStyle { if self.model.editAreasFontNameIsEqual() { model.isEnabled = true model.fontName = self.model.fontNames.first if model.fontName != self._fetchCurrentFontName() { needUpdateData = true } } else { model.isEnabled = false // model.fontName = "Helvetica" model.fontName = "--" } } else if key == .fontAdd { model.isEnabled = self._fontSizeItemIsEnabled() } else if key == .fontReduce { model.isEnabled = self._fontSizeItemIsEnabled() } else if key == .fontBold { model.isEnabled = self.model.editAreasFontBoldIsEqual() model.isSelected = self.model.fontBolds.first ?? false model.isChanged = model.isSelected != self._fetchCurrentBoldIsSelected() if model.isSelected != self._fetchCurrentBoldIsSelected() { needUpdateData = true } } else if key == .fontItalic { model.isEnabled = self.model.editAreasFontItalicIsEqual() model.isSelected = self.model.fontItalics.first ?? false if model.isSelected != self._fetchCurrentItalicIsSelected() { needUpdateData = true } } else if key == .textAlignment { // model.isEnabled = self.model.editAreasTextAlignmentIsEqual() if let data = self._fetchTextAlign() { model.textAlign = data } // model.isSelected = true if model.textAlign != self._fetchCurrentTextAlign() { needUpdateData = true } } datas.append(model) } if needUpdateData { contentViewC?.datas = datas } } } } else { if self.style.contains(.image) { // image if self.isMultiple { width = 396-20 contentViewC?.itemKeys = [.leftRotate, .rightRotate, .separator, .reverseX, .reverseY, .separator, .crop, .replace, .export, .separator, .alignmentLeft, .alignmentTop] var datas: [KMEditPDFToolbarModel] = [] for key in contentViewC?.itemKeys ?? [] { let model = KMEditPDFToolbarModel() model.itemKey = key if key == .crop { model.isEnabled = !self.isMultiple } else if key == .replace { model.isEnabled = !self.isMultiple } datas.append(model) } contentViewC?.datas = datas } else { width = 304-16 contentViewC?.itemKeys = [.leftRotate, .rightRotate, .separator, .reverseX, .reverseY, .separator, .crop, .replace, .export] var datas: [KMEditPDFToolbarModel] = [] for key in contentViewC?.itemKeys ?? [] { let model = KMEditPDFToolbarModel() model.itemKey = key if key == .crop { model.isEnabled = !self.isMultiple } datas.append(model) } contentViewC?.datas = datas } } else { // none } } let winFrame = positioningView.window?.frame ?? .zero var position = positioningView.convert(positioningRect.origin, to: nil) position.x += winFrame.origin.x position.y += winFrame.origin.y position.y += positioningRect.size.height position.y += 26 position.x += (positioningRect.size.width*0.5-width*0.5) // var x = max(0, position.x) var x = max(winFrame.origin.x, position.x) let offsetX = x + width - NSMaxX(winFrame) if offsetX > 0 { // 超出右编辑 x -= offsetX } var y = max(0, position.y) let screenFrame = NSScreen.main?.frame ?? .zero if y + 44 + 40 >= screenFrame.size.height { y = screenFrame.size.height - 44 - 40 } let frame = NSMakeRect(x, y, width, 44) self.setFrame(frame, display: true) self.contentViewController?.view.frame = NSMakeRect(0, 0, width, 44) self.orderFront(nil) // self.makeKeyAndOrderFront(nil) } override var isMainWindow: Bool { return true } override var isKeyWindow: Bool { return true } func updateFontColor(fontColor: NSColor?) { if self.isVisible { let contentViewC = self.contentViewController as? KMEditPDFPopToolBarController contentViewC?.fontColor = fontColor for model in contentViewC?.datas ?? [] { if model.itemKey == .color { model.isEnabled = fontColor != nil contentViewC?.toolbarView?.reloadData() break; } } } } func updateTextAlign(align: NSTextAlignment) { if self.isVisible { let contentViewC = self.contentViewController as? KMEditPDFPopToolBarController for model in contentViewC?.datas ?? [] { if model.itemKey == .textAlignment { if model.textAlign == align { break; } model.textAlign = align contentViewC?.toolbarView?.reloadData() break; } } } } func updateFontSizeButtons(enable: Bool) { if self.isVisible { let contentViewC = self.contentViewController as? KMEditPDFPopToolBarController for model in contentViewC?.datas ?? [] { if model.itemKey == .fontAdd || model.itemKey == .fontReduce { model.isEnabled = enable } } contentViewC?.toolbarView?.reloadData() } } // MARK: - Private Methods private func _fontSizeItemIsEnabled() -> Bool { if self.model.editAreasFontSizeIsEqual() { if let fontSize = self.model.fontSizes.first, fontSize == -1 { return false } else { return true } } else { return false } } private func _fetchTextAlign() -> NSTextAlignment? { if self.model.editAreasTextAlignmentIsEqual() { return self.model.textAlignments.first } return nil } private func _fetchCurrentBoldIsSelected() -> Bool { guard let contentC = self.contentViewController as? KMEditPDFPopToolBarController else { return false } for model in contentC.datas { if model.itemKey == .fontBold { return model.isSelected } } return false } private func _fetchCurrentItalicIsSelected() -> Bool { guard let contentC = self.contentViewController as? KMEditPDFPopToolBarController else { return false } for model in contentC.datas { if model.itemKey == .fontItalic { return model.isSelected } } return false } private func _fetchCurrentTextAlign() -> NSTextAlignment? { guard let contentC = self.contentViewController as? KMEditPDFPopToolBarController else { return nil } for model in contentC.datas { if model.itemKey == .textAlignment { return model.textAlign } } return nil } private func _fetchCurrentFontName() -> String? { guard let contentC = self.contentViewController as? KMEditPDFPopToolBarController else { return nil } for model in contentC.datas { if model.itemKey == .fontStyle { return model.fontName } } return nil } } extension KMEditPDFPopToolBarWindow: KMInterfaceThemeChangedProtocol { func interfaceThemeDidChanged(_ appearance: NSAppearance.Name) { self.appearance = .init(named: appearance) self.contentViewController?.interfaceThemeDidChanged(appearance) } }