123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- //
- // KMNewUserGuideWindowController.swift
- // PDF Reader Pro
- //
- // Created by User-Tangchao on 2024/12/18.
- //
- import Cocoa
- @objc enum KMNewUserGuideType: Int {
- case none = 0
- case ai
- case note
- case editPDF
- case pageEdit
- case convert
- case ocr
- case fillForms
- }
- // 新手引导 Layout 模块
- @objcMembers class KMNewUserGuideWindowController: KMBaseWindowController {
- @IBOutlet weak var contentBox: NSBox!
- @IBOutlet weak var backgroundIv: NSImageView!
- @IBOutlet weak var titleLabel: NSTextField!
- @IBOutlet weak var scrollView: NSScrollView!
- @IBOutlet weak var tableView: NSTableView!
- @IBOutlet weak var rightBox: NSBox!
-
- static let shared = KMNewUserGuideWindowController(windowNibName: "KMNewUserGuideWindowController")
-
- private lazy var displayView_ = KMNewUserGuideDisplayView()
-
- private var datas_: [KMNewUserGuideModel] = []
-
- var showType: KMNewUserGuideType = .ai {
- didSet {
- selectType(showType)
- }
- }
-
- // 28+8
- private var defaultViewH_: CGFloat = 36
-
- private let hasShowKey_ = "newUserGuidehasShow"
-
- override func windowDidLoad() {
- super.windowDidLoad()
-
- window?.title = ""
- window?.standardWindowButton(.zoomButton)?.isHidden = true
- window?.standardWindowButton(.miniaturizeButton)?.isHidden = true
- let closeButton = window?.standardWindowButton(.closeButton)
- closeButton?.target = self
- closeButton?.action = #selector(closeAction)
- backgroundIv.image = NSImage(named: "KMImageNameNewUserGuideBg")
- backgroundIv.image?.size = window?.frame.size ?? .zero
- titleLabel.font = .SFProTextSemiboldFont(20)
- titleLabel.stringValue = NSLocalizedString("The Beginner's Guide", comment: "")
-
- tableView.delegate = self
- tableView.dataSource = self
- tableView.enclosingScrollView?.borderType = .noBorder
- tableView.backgroundColor = .clear
- tableView.enclosingScrollView?.drawsBackground = false
-
- rightBox.borderWidth = 0
- rightBox.contentView = displayView_
- displayView_.contentInset = .init(top: 0, left: 10, bottom: 34, right: 18)
-
- interfaceThemeDidChanged(self.window?.appearance?.name ?? .vibrantLight)
-
- _initData()
- tableView.reloadData()
- }
-
- // MARK: - Private Methods
-
- private func _initData() {
- datas_.removeAll()
- let types: [KMNewUserGuideType] = [.ai, .editPDF, .note, .pageEdit, .convert, .ocr, .fillForms]
- for type in types {
- datas_.append(_initModel(type: type))
- }
- }
-
- private func _initModel(type: KMNewUserGuideType) -> KMNewUserGuideModel {
- let model = KMNewUserGuideModel()
- model.type = type
- let maxWidth: CGFloat = 224
- var listDespColor = NSColor(hex: "#42464D")
- if KMAppearance.isDarkMode() {
- listDespColor = .white
- }
-
- if type == .ai {
- #if VERSION_FREE
- model.name = NSLocalizedString("AI Credits Giveaway", comment: "")
- let attri = NSAttributedString(string: NSLocalizedString("Free 40 AI Credits for 7-day VIP Free Trial Users", comment: ""), attributes: [
- .font : NSFont.SFProTextRegularFont(11),
- .foregroundColor : listDespColor
- ])
- #else
- model.name = NSLocalizedString("Edit with AI Robot", comment: "")
- let attri = NSAttributedString(string: NSLocalizedString("AI Translate, Summarize, Proofread, Rewrite", comment: ""), attributes: [
- .font : NSFont.SFProTextRegularFont(11),
- .foregroundColor : listDespColor
- ])
- #endif
- model.imageName = "newUserGuideAITools"
- model.despAttri = attri
- model.viewH = _calculateViewHeight(model: model, maxWidth: maxWidth)
- } else if type == .note {
- model.name = NSLocalizedString("Annotate PDF", comment: "")
- model.imageName = "newUserGuideAnnotation"
- let attri = NSAttributedString(string: NSLocalizedString("Add Annotations, Images, and Stamps to PDFs", comment: ""), attributes: [
- .font : NSFont.SFProTextRegularFont(11),
- .foregroundColor : listDespColor
- ])
- model.despAttri = attri
- model.viewH = _calculateViewHeight(model: model, maxWidth: maxWidth)
- } else if type == .editPDF {
- model.name = NSLocalizedString("Edit PDF", comment: "")
- model.imageName = "newUserGuideEdit"
- let attri = NSAttributedString(string: NSLocalizedString("Edit PDF Text, Images, and Watermarks", comment: ""), attributes: [
- .font : NSFont.SFProTextRegularFont(11),
- .foregroundColor : listDespColor
- ])
- model.despAttri = attri
- model.viewH = _calculateViewHeight(model: model, maxWidth: maxWidth)
- } else if type == .pageEdit {
- model.name = NSLocalizedString("Page Edit", comment: "")
- model.imageName = "KMImageNameNewUserGuidePageEdit"
- let attri = NSAttributedString(string: NSLocalizedString("Insert, Delete, Rotate, and Extract the Pages in PDFs", comment: ""), attributes: [
- .font : NSFont.SFProTextRegularFont(11),
- .foregroundColor : listDespColor
- ])
- model.despAttri = attri
- model.viewH = _calculateViewHeight(model: model, maxWidth: maxWidth)
- } else if type == .convert {
- model.name = NSLocalizedString("PDF Converter", comment: "")
- model.imageName = "KMImageNameNewUserGuideConvert"
- let attri = NSAttributedString(string: NSLocalizedString("Convert to/from MS Office, Images, and More", comment: ""), attributes: [
- .font : NSFont.SFProTextRegularFont(11),
- .foregroundColor : listDespColor
- ])
- model.despAttri = attri
- model.viewH = _calculateViewHeight(model: model, maxWidth: maxWidth)
- } else if type == .ocr {
- model.name = NSLocalizedString("OCR", comment: "")
- model.imageName = "KMImageNameNewUserGuideOCR"
- let attri = NSAttributedString(string: NSLocalizedString("Scanned PDFs to Editable & Searchable PDFs in 90+ Languages", comment: ""), attributes: [
- .font : NSFont.SFProTextRegularFont(11),
- .foregroundColor : listDespColor
- ])
- model.despAttri = attri
- model.viewH = _calculateViewHeight(model: model, maxWidth: maxWidth)
- } else if type == .fillForms {
- model.name = NSLocalizedString("Create & Fill Forms", comment: "")
- model.imageName = "KMImageNameNewUserGuideFillForms"
- let attri = NSAttributedString(string: NSLocalizedString("Support to Create & Fill Interactive PDF Forms", comment: ""), attributes: [
- .font : NSFont.SFProTextRegularFont(11),
- .foregroundColor : listDespColor
- ])
- model.despAttri = attri
- model.viewH = _calculateViewHeight(model: model, maxWidth: maxWidth)
- }
- return model
- }
-
- private func _calculateViewHeight(model: KMNewUserGuideModel, maxWidth: CGFloat) -> CGFloat {
- guard let attri = model.despAttri else {
- return defaultViewH_
- }
- let size = attri.boundingRect(with: .init(width: maxWidth, height: CGFLOAT_MAX), options: .usesLineFragmentOrigin).size
- return (4+20+8+size.height+4)+4+4
- }
-
- private func _trackEvent(type: KMNewUserGuideType) {
- if type == .ai {
- trackEvent(eventName: "PUW", params: ["PUW_Btn" : "FunctionIntro_AI"], platform: .AppCenter)
- } else if type == .note {
- trackEvent(eventName: "PUW", params: ["PUW_Btn" : "FunctionIntro_AnnotatePDF"], platform: .AppCenter)
- } else if type == .editPDF {
- trackEvent(eventName: "PUW", params: ["PUW_Btn" : "FunctionIntro_EditPDF"], platform: .AppCenter)
- } else if type == .pageEdit {
- trackEvent(eventName: "PUW", params: ["PUW_Btn" : "FunctionIntro_PageEditor"], platform: .AppCenter)
- } else if type == .convert {
- trackEvent(eventName: "PUW", params: ["PUW_Btn" : "FunctionIntro_Convert"], platform: .AppCenter)
- } else if type == .ocr {
- trackEvent(eventName: "PUW", params: ["PUW_Btn" : "FunctionIntro_OCR"], platform: .AppCenter)
- } else if type == .fillForms {
- trackEvent(eventName: "PUW", params: ["PUW_Btn" : "FunctionIntro_Form"], platform: .AppCenter)
- }
- }
-
- private func _fetchBrowserWindowC() -> KMBrowserWindowController? {
- if let win = NSApp.mainWindow as? CTBrowserWindow {
- return win.windowController as? KMBrowserWindowController
- }
- if let win = NSApp.keyWindow as? CTBrowserWindow {
- return win.windowController as? KMBrowserWindowController
- }
-
- for win in NSApp.windows {
- if win.isVisible == false {
- continue
- }
- guard let winC = win.windowController as? KMBrowserWindowController else {
- continue
- }
- if winC.browser == nil {
- continue
- }
- return winC
- }
- return nil
- }
-
- private func _showPopGuide() {
- if KMMemberInfo.shared.isLogin == false { // 未登陆
- return
- }
- if KMMemberInfo.shared.isMemberAllFunction { // 有权益
- return
- }
- guard let winC = _fetchBrowserWindowC() else { // 没找到有效的窗口
- return
- }
- guard let itemV = winC.rightMessageVC?.discountItemView, itemV.isHidden == false else {
- return
- }
- guard let doc = winC.browser.activeTabContents() as? KMMainDocument else {
- return
- }
- if doc.isHome || doc.isNewTab {
- doc.homeViewController?.loadOpenFileFunctionGuide(.messageDiscount)
- return
- }
- doc.mainViewController?.loadOpenFileFunctionGuide(.messageDiscount)
- }
-
- override func interfaceThemeDidChanged(_ appearance: NSAppearance.Name) {
- super.interfaceThemeDidChanged(appearance)
-
- KMMainThreadExecute {
- var listDespColor = NSColor(hex: "#42464D")
- if KMAppearance.isDarkMode() {
- self.titleLabel.textColor = .white
- listDespColor = .white
- } else {
- self.titleLabel.textColor = NSColor(hex: "#002143")
- }
-
- let maxWidth: CGFloat = 224
- for model in self.datas_ {
- if let data = model.despAttri?.string {
- let attri = NSAttributedString(string: data, attributes: [
- .font : NSFont.SFProTextRegularFont(11),
- .foregroundColor : listDespColor
- ])
- model.despAttri = attri
- model.viewH = self._calculateViewHeight(model: model, maxWidth: maxWidth)
- }
- }
-
- self.tableView.reloadData()
- }
- }
-
- // MARK: - Actions
-
- @objc func closeAction() {
- _showPopGuide()
-
- closeWindow()
- }
-
- // MARK: - public Methods
-
- public func openWindow() {
- self.showWindow(nil)
-
- self._showCenter(animate: true)
-
- self.window?.makeKeyAndOrderFront(nil)
-
- selectType(showType)
- saveShowRecord()
-
- trackEvent(eventName: "PUW", params: ["PUW_Exposure" : "PUW_FunctionIntro"], platform: .AppCenter)
- }
-
- public func selectType(_ type: KMNewUserGuideType) {
- var selectedModel: KMNewUserGuideModel?
- for model in datas_ {
- model.isSelected = (model.type == type)
- if model.isSelected {
- selectedModel = model
- }
- }
-
- KMMainThreadExecute {
- // self.tableView.reloadData()
- self.tableView.animator().reloadData()
-
- if let data = selectedModel?.imageName {
- self.displayView_.imageView.image = NSImage(named: data)
- self.displayView_.imageView.image?.size = NSMakeSize(480, 320)
- }
- }
- }
-
- public func closeWindow() {
- window?.close()
- }
-
- public func saveShowRecord() {
- KMDataManager.ud_set(true, forKey: hasShowKey_)
- }
-
- public func needShow() -> Bool {
- // 已经显示
- if KMDataManager.ud_bool(forKey: hasShowKey_) {
- return false
- }
- #if VERSION_FREE
- if KMMemberInfo.shared.isMemberAllFunction { // 有权益(本地 或 账号)
- return false
- }
- #endif
- // 不是新用户
- if KMMemberInfo.shared.isFreeAccount == false {
- return false
- }
- // 新用户首次显示
- return true
- }
-
- private func _showCenter(animate: Bool){
- guard let screenFrame = NSScreen.main?.frame else {
- return
- }
-
- var frame: CGRect = self.window!.frame
- frame.origin.y = (screenFrame.size.height-frame.size.height)*0.5
- frame.origin.x = (screenFrame.size.width-frame.size.width)*0.5
- self.window?.setFrame(frame, display: true, animate: animate)
- }
- }
- // MARK: - NSTableViewDelegate, NSTableViewDataSource
- extension KMNewUserGuideWindowController: NSTableViewDelegate, NSTableViewDataSource {
- func numberOfRows(in tableView: NSTableView) -> Int {
- return datas_.count
- }
-
- func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
- let model = datas_.safe_element(for: row) as? KMNewUserGuideModel
- let isSelected = model?.isSelected ?? false
- if isSelected == false {
- return defaultViewH_
- }
- return model?.viewH ?? defaultViewH_
- }
-
- func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
- let rowView = KMTableRowView()
- rowView.selectionBackgroundColorBlock = {
- return .clear
- }
- return rowView
- }
-
- func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
- var cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CellId"), owner: self) as? KMNewUserGuideCellView
- if cell == nil {
- cell = KMNewUserGuideCellView()
- }
-
- cell?.contentInset = .init(top: 4, left: 0, bottom: 4, right: 0)
-
- guard let model = datas_.safe_element(for: row) as? KMNewUserGuideModel else {
- return cell
- }
-
- cell?.titleLabel.font = .SFProTextBoldFont(14)
-
- cell?.titleLabel.stringValue = model.name
- if let data = model.despAttri {
- cell?.subTitleLabel.attributedStringValue = data
- }
-
- let isSelected = model.isSelected
- cell?.subTitleLabel.isHidden = !isSelected
- cell?.leftLine.isHidden = !isSelected
- cell?.backgroundView.layer?.cornerRadius = 4
- if KMAppearance.isDarkMode() {
- cell?.titleLabel.textColor = .white
- cell?.leftLine.layer?.backgroundColor = NSColor(hex: "#227AFF").cgColor
-
- if isSelected {
- cell?.backgroundView.layer?.backgroundColor = NSColor(hex: "#869297").withAlphaComponent(0.4).cgColor
- } else {
- cell?.backgroundView.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.8).cgColor
- }
- } else {
- cell?.titleLabel.textColor = NSColor(hex: "#42464D")
- cell?.leftLine.layer?.backgroundColor = NSColor(hex: "#4982E6").cgColor
-
- if isSelected {
- cell?.backgroundView.layer?.backgroundColor = NSColor(hex: "#DCF4FF").withAlphaComponent(0.4).cgColor
- } else {
- cell?.backgroundView.layer?.backgroundColor = .white
- }
- }
-
- return cell
- }
-
- func tableViewSelectionDidChange(_ notification: Notification) {
- if tableView.isEqual(to: notification.object) {
- let row = tableView.selectedRow
- guard let model = datas_.safe_element(for: row) as? KMNewUserGuideModel else {
- return
- }
-
- selectType(model.type)
- _trackEvent(type: model.type)
- }
- }
- }
|