KMHomePopViewController.swift 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. //
  2. // KMHomePopViewController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by wanjun on 2022/10/17.
  6. //
  7. import Cocoa
  8. typealias popCellViewDownCallback = (_ downEntered: Bool, _ count: String) -> Void
  9. @objcMembers class KMHomePopViewController: NSViewController {
  10. @IBOutlet weak var customBox: NSBox!
  11. @IBOutlet weak var customBoxWidthLayoutConstraint: NSLayoutConstraint!
  12. @IBOutlet weak var customBoxHeightLayoutConstraint: NSLayoutConstraint!
  13. var downCallback: popCellViewDownCallback?
  14. var popCellViewDownString: String?
  15. var popCellCount: Int?
  16. var dataArr: [String]?
  17. var KMHorizontalLine: String = "KMHorizontalLine"
  18. var enterFillColor : NSColor = KMDesignToken.shared.fill(withToken: "dropdown.m.bg.hov")
  19. var textColor : NSColor = .black // 背景颜色
  20. var background : NSColor = .white // 背景颜色
  21. var background_hov : NSColor = .clear // 背景颜色
  22. var background_sel : NSColor = .clear // 背景颜色
  23. var background_disabled : NSColor = .clear // 背景颜色
  24. var cornerRadius : Float = 0.0 // 边框圆角
  25. var cornerRadius_hov : Float = 0.0 // 边框圆角
  26. var cornerRadius_sel : Float = 0.0 // 边框圆角
  27. var cornerRadius_disabled : Float = 0.0 // 边框圆角
  28. var lineHeight : CGFloat = 20.0 // 默认 内容行高
  29. var lineHeight_hov : CGFloat = 20.0 // 默认 内容行高
  30. var lineHeight_sel : CGFloat = 20.0 // 默认 内容行高
  31. var lineHeight_disabled : CGFloat = 20.0 // 默认 内容行高
  32. var font : NSFont = NSFont.systemFont(ofSize: 14.0) // 内容字体
  33. var font_hov : NSFont = NSFont.systemFont(ofSize: 14.0) // 内容字体
  34. var font_sel : NSFont = NSFont.systemFont(ofSize: 14.0) // 内容字体
  35. var font_disabled : NSFont = NSFont.systemFont(ofSize: 14.0) // 内容字体
  36. var _state: KMDesignTokenState = .Norm
  37. var enabled: Bool = true // 是否可点击
  38. var canHover: Bool = true // 是否可悬浮
  39. var disItems: [String] = []
  40. var selectedItems: [String] = []
  41. func initWithPopViewDataArr(_ popViewDataArr: [String]) -> Self {
  42. // self.dataArr = popViewDataArr.reverseObjectEnumerator().allObjects as NSArray
  43. self.dataArr = popViewDataArr.reversed()
  44. return self
  45. }
  46. override func viewDidLoad() {
  47. super.viewDidLoad()
  48. customBox.fillColor = background//NSColor.km_init(hex: "#FFFFFF")
  49. // self.updateUI()
  50. }
  51. override func viewDidAppear() {
  52. super.viewDidAppear()
  53. self.updateUI()
  54. }
  55. // MARK: Private
  56. func updateUI() {
  57. customBox.fillColor = background
  58. var widthMax: Float = 0;
  59. let subViews: [NSView] = self.customBox.contentView!.subviews
  60. for subView in subViews {
  61. subView.removeFromSuperview()
  62. }
  63. for string in self.dataArr ?? [] {
  64. if !(string as AnyObject).isEqual(to: KMHorizontalLine) {
  65. let width = self.cellContentAdaptiveWidth(string)
  66. if widthMax < width {
  67. widthMax = width
  68. }
  69. }
  70. }
  71. var formTopFloat: Float = 4.0
  72. // for i in (0..<dataArr!.count).reversed() {
  73. for string in dataArr?.reversed() ?? [] {
  74. if (string as AnyObject).isEqual(to: KMHorizontalLine) {
  75. self.createHonrizontalLineWithFrame(CGRect(x: 12.0, y: CGFloat(formTopFloat), width: CGFloat(widthMax)+23, height: 11))
  76. formTopFloat += 11
  77. } else {
  78. popCellViewDownString = string
  79. // self.createPopViewCellLabelWithFrame(CGRect(x: 4.0, y: CGFloat(formTopFloat), width: CGFloat(widthMax)+47, height: 32), stringValue: string)
  80. createPopViewCellLabelWithFrame(formTopFloat, stringValue: string)
  81. formTopFloat += 32;
  82. }
  83. }
  84. customBoxWidthLayoutConstraint.constant = CGFloat(widthMax+47)
  85. customBoxHeightLayoutConstraint.constant = CGFloat(formTopFloat+4.0)
  86. }
  87. func createHonrizontalLineWithFrame(_ frame: CGRect) {
  88. let box: NSBox = NSBox.init(frame: frame)
  89. box.boxType = .custom
  90. box.borderWidth = 0.0
  91. box.contentViewMargins = NSMakeSize(0, 0)
  92. customBox.contentView?.addSubview(box)
  93. let lineBox = NSBox.init(frame: CGRect(x: 0, y: 6, width: frame.width, height: 1))
  94. lineBox.boxType = .separator
  95. box.addSubview(lineBox)
  96. }
  97. // func createPopViewCellLabelWithFrame(_ frame: CGRect, stringValue: String) {
  98. // let box: KMBox = KMBox(frame: frame)
  99. // box.boxType = .custom
  100. // box.borderWidth = 0.0
  101. // box.contentViewMargins = NSMakeSize(0, 0)
  102. // customBox.contentView?.addSubview(box)
  103. //
  104. // let boxLabel: NSTextField = NSTextField.init()
  105. // boxLabel.isEditable = false
  106. // boxLabel.isBordered = false
  107. // boxLabel.stringValue = stringValue
  108. // boxLabel.font = NSFont.systemFont(ofSize: 14.0)
  109. // boxLabel.translatesAutoresizingMaskIntoConstraints = false
  110. // boxLabel.backgroundColor = NSColor.clear
  111. // boxLabel.textColor = NSColor.labelColor
  112. // box.contentView?.addSubview(boxLabel)
  113. //
  114. // box.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-21-[boxLabel]-21-|", metrics: nil, views:["boxLabel": boxLabel]))
  115. // box.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-1-[boxLabel]-1-|", metrics: nil, views:["boxLabel": boxLabel]))
  116. //
  117. // box.contentView?.addConstraint(NSLayoutConstraint(item: boxLabel, attribute:.centerY , relatedBy: .equal, toItem: box.contentView, attribute: .centerY, multiplier: 1, constant: 0))
  118. // box.moveCallback = {(mouseEntered: Bool, mouseBox: KMBox) -> Void in
  119. // if mouseEntered {
  120. // if #available(macOS 10.14, *) {
  121. // box.fillColor = NSColor.controlAccentColor
  122. // boxLabel.textColor = NSColor.white
  123. // } else {
  124. // box.fillColor = NSColor.init(red: 71/255.0, green: 126/255.0, blue: 222/255.0, alpha: 1.0)
  125. // boxLabel.textColor = NSColor.white
  126. // }
  127. // } else {
  128. // box.fillColor = NSColor.clear
  129. // boxLabel.textColor = NSColor.labelColor
  130. // }
  131. // }
  132. // box.downCallback = {(downEntered: Bool, mouseBox: KMBox) -> Void in
  133. // if downEntered {
  134. // if let callback = self.downCallback {
  135. // callback(true, stringValue)
  136. // }
  137. // }
  138. // }
  139. // }
  140. func createPopViewCellLabelWithFrame(_ mas_top: Float, stringValue: String) {
  141. var isDisabled = false
  142. if disItems.contains(stringValue) {
  143. isDisabled = true
  144. }
  145. var isSelected = false
  146. if (isDisabled == false && self.selectedItems.contains(stringValue)) {
  147. isSelected = true
  148. }
  149. let box: KMBox = KMBox(frame: NSZeroRect)
  150. box.boxType = .custom
  151. box.borderWidth = 0.0
  152. box.contentViewMargins = NSMakeSize(0, 0)
  153. box.cornerRadius = 4.0
  154. customBox.contentView?.addSubview(box)
  155. box.mas_makeConstraints { (make) in
  156. make?.left.equalTo()(4.0)
  157. make?.right.equalTo()(-4.0)
  158. make?.height.equalTo()(32.0)
  159. make?.top.equalTo()(customBox.mas_top)?.offset()(CGFloat(mas_top))
  160. }
  161. // let dropdownVC = KMDesignDropdown.init(nibName: "KMDesignDropdown", bundle: nil)
  162. // box.contentView = dropdownVC.view
  163. // dropdownVC.dropdown(bg: "dropdown.m.bg.norm", text: "dropdown.m.mac-text.def")
  164. // dropdownVC.dropdown(bg: "dropdown.m.bg.hov", text: "dropdown.m.mac-text.def", state: .Hov)
  165. // dropdownVC.dropdown(bg: "dropdown.m.bg.sel", text: "dropdown.m.mac-text.sel", state: .Sel)
  166. // dropdownVC.dropdown(bg: "dropdown.m.bg.dis", text: "dropdown.m.mac-text.dis", state: .Disabled)
  167. // dropdownVC.stringValue = str
  168. let boxLabel: NSTextField = NSTextField.init()
  169. boxLabel.isEditable = false
  170. boxLabel.isBordered = false
  171. boxLabel.stringValue = stringValue
  172. boxLabel.font = NSFont.systemFont(ofSize: 14.0)
  173. boxLabel.translatesAutoresizingMaskIntoConstraints = false
  174. boxLabel.backgroundColor = NSColor.clear
  175. boxLabel.textColor = textColor//NSColor.km_init(hex: "#252629")
  176. box.contentView?.addSubview(boxLabel)
  177. boxLabel.mas_makeConstraints { (make) in
  178. make?.centerY.equalTo()(0.0)
  179. make?.left.equalTo()(8.0)
  180. }
  181. let textTypography = KMDesignToken.shared.typography(withToken: "dropdown.m.mac-text.def")
  182. var fontFamily: String = textTypography.fontFamily
  183. let fontWeight: String = textTypography.fontWeight
  184. if fontFamily.contains(" ") {
  185. fontFamily = fontFamily.replacingOccurrences(of: " ", with: "")
  186. }
  187. if fontWeight != "" {
  188. fontFamily = String(format: "%@-%@", fontFamily, fontWeight)
  189. }
  190. boxLabel.font = NSFont(name: fontFamily, size: textTypography.fontSize.stringToCGFloat()) ?? NSFont.systemFont(ofSize: textTypography.fontSize.stringToCGFloat())
  191. let paragraphStyle = NSMutableParagraphStyle()
  192. paragraphStyle.lineSpacing = textTypography.lineHeight.stringToCGFloat()
  193. boxLabel.attributedStringValue = NSAttributedString(string: stringValue, attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle])
  194. box.moveCallback = {(mouseEntered: Bool, mouseBox: KMBox) -> Void in
  195. if !isDisabled {
  196. if isSelected { // 选中没有 hover 效果
  197. return
  198. }
  199. if mouseEntered {
  200. mouseBox.fillColor = self.enterFillColor
  201. } else {
  202. mouseBox.fillColor = NSColor.clear
  203. }
  204. }
  205. }
  206. box.downCallback = {(downEntered, mouseBox, event) -> Void in
  207. if !isDisabled {
  208. if downEntered {
  209. mouseBox.fillColor = KMDesignToken.shared.fill(withToken: "dropdown.m.bg.sel")
  210. boxLabel.textColor = KMDesignToken.shared.fill(withToken: "dropdown.m.mac-text.sel")
  211. if let callback = self.downCallback {
  212. callback(true, stringValue)
  213. }
  214. } else {
  215. mouseBox.fillColor = KMDesignToken.shared.fill(withToken: "dropdown.m.bg.norm")
  216. boxLabel.textColor = KMDesignToken.shared.fill(withToken: "dropdown.m.mac-text.def")
  217. }
  218. }
  219. }
  220. if isDisabled {
  221. box.fillColor = KMDesignToken.shared.fill(withToken: "dropdown.m.bg.dis")
  222. boxLabel.textColor = KMDesignToken.shared.fill(withToken: "dropdown.m.mac-text.dis")
  223. } else if (isSelected) {
  224. box.fillColor = KMDesignToken.shared.fill(withToken: "dropdown.m.bg.sel")
  225. boxLabel.textColor = KMDesignToken.shared.fill(withToken: "dropdown.m.mac-text.sel")
  226. }
  227. }
  228. func cellContentAdaptiveWidth(_ content: String) -> Float {
  229. if content.isEmpty {
  230. return 0
  231. }
  232. let attributes = [NSAttributedString.Key.font : NSFont.systemFont(ofSize: 14.0)]
  233. let rect : CGRect = content.boundingRect(with: CGSize(width: 0, height: 18),options: .usesLineFragmentOrigin, attributes: attributes,context:nil)
  234. return Float(rect.size.width)
  235. }
  236. func changePopViewCellData(_ count: Int, content: String) {
  237. let boxArray: Array<NSView> = customBox.contentView!.subviews
  238. for subView in boxArray {
  239. subView.removeFromSuperview()
  240. }
  241. var dataMutableArr: [String] = Array((dataArr?.reversed())!)
  242. dataMutableArr[count] = content
  243. dataArr = Array(dataMutableArr.reversed())
  244. self.updateUI()
  245. }
  246. // MARK: - Init Views
  247. fileprivate func initBoxLabel() -> NSTextField {
  248. let label = NSTextField.init()
  249. label.isEditable = false
  250. label.isBordered = false
  251. label.font = NSFont.systemFont(ofSize: 14.0)
  252. label.translatesAutoresizingMaskIntoConstraints = false
  253. label.backgroundColor = NSColor.clear
  254. label.textColor = NSColor.km_init(hex: "#252629")
  255. return label
  256. }
  257. }
  258. class KMScrollPopViewController: KMHomePopViewController {
  259. private var scrollView = NSScrollView()
  260. private var contentView = NSView()
  261. private var currentItemPosition: NSPoint = .zero
  262. convenience init() {
  263. self.init(nibName: "KMHomePopViewController", bundle: nil)
  264. }
  265. override func viewDidLoad() {
  266. super.viewDidLoad()
  267. self.view.addSubview(self.scrollView)
  268. self.scrollView.documentView = self.contentView
  269. self.scrollView.documentView?.backgroundColor(NSColor.km_init(hex: "#FFFFFF"))
  270. // customBox.fillColor = NSColor.km_init(hex: "#FFFFFF")
  271. }
  272. override func viewDidAppear() {
  273. super.viewDidAppear()
  274. DispatchQueue.main.async {
  275. let contentView = self.scrollView.contentView
  276. let pageH = NSHeight(self.scrollView.bounds)
  277. // KMPrint(NSHeight(self.contentView.bounds))
  278. // KMPrint(pageH)
  279. // KMPrint(self.currentItemPosition)
  280. var numberPages: Int = 0
  281. var currentPage: Int = 0
  282. var scrollY: CGFloat = 0
  283. if (pageH > 0) {
  284. numberPages = Int(NSHeight(self.contentView.bounds) / pageH) + 1
  285. currentPage = Int((self.currentItemPosition.y + 32 + 4 * 2) / pageH)
  286. // KMPrint(numberPages)
  287. // KMPrint(currentPage)
  288. // let _currentPage = numberPages - currentPage - 1
  289. // KMPrint(_currentPage)
  290. if (currentPage == (numberPages - 1)) {
  291. scrollY = 0
  292. } else {
  293. scrollY = NSHeight(self.contentView.bounds) - pageH * CGFloat(currentPage+1)
  294. }
  295. }
  296. // KMPrint(scrollY)
  297. contentView.scroll(to: NSPoint(x: 0, y: scrollY))
  298. }
  299. }
  300. override func viewDidLayout() {
  301. super.viewDidLayout()
  302. self.scrollView.frame = NSMakeRect(0, 0, NSWidth(self.view.frame), NSHeight(self.view.window!.frame))
  303. self.contentView.frame = NSMakeRect(0, 0, NSWidth(self.customBox.frame), NSHeight(self.customBox.frame)+30)
  304. // self.contentView.frame = self.customBox.bounds
  305. }
  306. override func updateUI() {
  307. var widthMax: Float = 0
  308. for subView in self.contentView.subviews {
  309. subView.removeFromSuperview()
  310. }
  311. for string in self.dataArr ?? [] {
  312. if ((string as AnyObject).isEqual(to: KMHorizontalLine)) {
  313. continue
  314. }
  315. let width = self.cellContentAdaptiveWidth(string)
  316. if (widthMax < width) {
  317. widthMax = width
  318. }
  319. }
  320. var formTopFloat: Float = 4.0
  321. for string in dataArr?.reversed() ?? [] {
  322. if (string as AnyObject).isEqual(to: KMHorizontalLine) {
  323. self.createHonrizontalLineWithFrame(CGRect(x: 12.0, y: CGFloat(formTopFloat), width: CGFloat(widthMax)+23, height: 11))
  324. formTopFloat += 11
  325. } else {
  326. self.popCellViewDownString = string
  327. self.createPopViewCellLabelWithFrame(formTopFloat, stringValue: string)
  328. formTopFloat += 32
  329. }
  330. }
  331. self.customBoxWidthLayoutConstraint.constant = CGFloat(widthMax+47)
  332. self.customBoxHeightLayoutConstraint.constant = CGFloat(formTopFloat+4.0)
  333. }
  334. override func createHonrizontalLineWithFrame(_ frame: CGRect) {
  335. let box: NSBox = NSBox.init(frame: frame)
  336. box.boxType = .custom
  337. box.borderWidth = 0.0
  338. box.contentViewMargins = NSMakeSize(0, 0)
  339. self.contentView.addSubview(box)
  340. let lineBox = NSBox.init(frame: CGRect(x: 0, y: 6, width: frame.width, height: 1))
  341. lineBox.boxType = .separator
  342. box.addSubview(lineBox)
  343. }
  344. override func createPopViewCellLabelWithFrame(_ mas_top: Float, stringValue: String) {
  345. var isDisabled = false
  346. if (self.disItems.contains(stringValue)) {
  347. isDisabled = true
  348. }
  349. var isSelected = false
  350. if (isDisabled == false && self.selectedItems.contains(stringValue)) {
  351. isSelected = true
  352. }
  353. if (isSelected && self.selectedItems.first == stringValue) {
  354. self.currentItemPosition = NSPoint(x: 0, y: Int(mas_top))
  355. }
  356. let box: KMBox = KMBox(frame: NSZeroRect)
  357. box.boxType = .custom
  358. box.borderWidth = 0.0
  359. box.contentViewMargins = NSMakeSize(0, 0)
  360. box.cornerRadius = 4.0
  361. self.contentView.addSubview(box)
  362. box.mas_makeConstraints { (make) in
  363. make?.left.equalTo()(4.0)
  364. make?.right.equalTo()(-4.0)
  365. make?.height.equalTo()(32.0)
  366. make?.top.equalTo()(self.customBox.mas_top)?.offset()(CGFloat(mas_top))
  367. }
  368. let boxLabel = self.initBoxLabel()
  369. boxLabel.stringValue = stringValue
  370. box.contentView?.addSubview(boxLabel)
  371. boxLabel.mas_makeConstraints { (make) in
  372. make?.centerY.equalTo()(0.0)
  373. make?.left.equalTo()(8.0)
  374. }
  375. let textTypography = KMDesignToken.shared.typography(withToken: "dropdown.m.mac-text.def")
  376. var fontFamily: String = textTypography.fontFamily
  377. let fontWeight: String = textTypography.fontWeight
  378. if (fontFamily.contains(" ")) {
  379. fontFamily = fontFamily.replacingOccurrences(of: " ", with: "")
  380. }
  381. if (fontWeight != "") {
  382. fontFamily = String(format: "%@-%@", fontFamily, fontWeight)
  383. }
  384. if NSFont(name: stringValue, size: 12) != nil {
  385. fontFamily = stringValue
  386. } else {
  387. debugPrint("不支持字体" + stringValue)
  388. }
  389. boxLabel.font = NSFont(name: fontFamily, size: textTypography.fontSize.stringToCGFloat()) ?? NSFont.systemFont(ofSize: textTypography.fontSize.stringToCGFloat())
  390. let paragraphStyle = NSMutableParagraphStyle()
  391. paragraphStyle.lineSpacing = textTypography.lineHeight.stringToCGFloat()
  392. boxLabel.attributedStringValue = NSAttributedString(string: stringValue, attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle])
  393. if (isDisabled) {
  394. box.fillColor = KMDesignToken.shared.fill(withToken: "dropdown.m.bg.dis")
  395. boxLabel.textColor = KMDesignToken.shared.fill(withToken: "dropdown.m.mac-text.dis")
  396. } else if (isSelected) {
  397. box.fillColor = KMDesignToken.shared.fill(withToken: "dropdown.m.bg.sel")
  398. boxLabel.textColor = KMDesignToken.shared.fill(withToken: "dropdown.m.mac-text.sel")
  399. }
  400. box.moveCallback = { mouseEntered, mouseBox in
  401. if (isDisabled) {
  402. return
  403. }
  404. if (isSelected) { // 选中没有 hover 效果
  405. return
  406. }
  407. if (mouseEntered) {
  408. mouseBox.fillColor = KMDesignToken.shared.fill(withToken: "dropdown.m.bg.hov")
  409. } else {
  410. mouseBox.fillColor = NSColor.clear
  411. }
  412. }
  413. box.downCallback = { [unowned self] downEntered, mouseBox, _ in
  414. if (isDisabled) {
  415. return
  416. }
  417. if (downEntered) {
  418. mouseBox.fillColor = KMDesignToken.shared.fill(withToken: "dropdown.m.bg.sel")
  419. boxLabel.textColor = KMDesignToken.shared.fill(withToken: "dropdown.m.mac-text.sel")
  420. guard let callback = self.downCallback else {
  421. return
  422. }
  423. callback(true, stringValue)
  424. } else {
  425. mouseBox.fillColor = KMDesignToken.shared.fill(withToken: "dropdown.m.bg.norm")
  426. boxLabel.textColor = KMDesignToken.shared.fill(withToken: "dropdown.m.mac-text.def")
  427. }
  428. }
  429. }
  430. }