KMToolbarItemView.swift 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. //
  2. // KMToolbarItemView.swift
  3. // PDF Master
  4. //
  5. // Created by tangchao on 2023/10/24.
  6. //
  7. import Cocoa
  8. enum KMToolbarItemViewSelectBackgroundType: Int {
  9. case none = 0
  10. case imageBox
  11. }
  12. class KMToolbarClickButton: NSButton {
  13. weak var clickObject: AnyObject?
  14. }
  15. extension NSControl.ImagePosition {
  16. static let imageExpandLeft: NSControl.ImagePosition = .init(rawValue: 100)!
  17. }
  18. private let KMPopOverClosedByWindowNotificationName = "KMPopOverClosedByWindowNotification"
  19. extension KMToolbarItemView {
  20. public static let popOverClosedNotificationName = Notification.Name(KMPopOverClosedByWindowNotificationName)
  21. }
  22. @objcMembers class KMToolbarItemView: NSView {
  23. var menuFormRepresentation: NSMenuItem?
  24. private var _itemIdentifier: String?
  25. var itemIdentifier: String? {
  26. get {
  27. return self._itemIdentifier
  28. }
  29. }
  30. lazy var clickButton: KMToolbarClickButton = {
  31. let view = KMToolbarClickButton()
  32. view.bezelStyle = .regularSquare
  33. view.isBordered = false
  34. view.imagePosition = .imageOnly
  35. view.clickObject = self
  36. return view
  37. }()
  38. var isSelected = false {
  39. didSet {
  40. if self.itemIdentifier != KMToolbarDividerItemIdentifier {
  41. if (isSelected) {
  42. if(self.image != nil && self.alternateImage != nil) {
  43. if (self.selectedImage != nil) {
  44. self.imageViewBtn.image = self.selectedImage!
  45. } else {
  46. self.imageViewBtn.image = self.alternateImage!
  47. }
  48. }
  49. if (self.nameBtn.superview != nil) {
  50. self.nameBtn.setTitleColor(color: KMAppearance.titleColor())
  51. }
  52. if(self.needExpandAction) {
  53. self.needExpandButton.image = NSImage(named: "KMImageNameUXIconBtnTriDownSel")
  54. }
  55. } else {
  56. if (self.needExpandAction) {
  57. self.needExpandButton.image = NSImage(named: "KMImageNameUXIconBtnTriDownNor")
  58. }
  59. if (self.image != nil) {
  60. self.imageViewBtn.image = self.image!
  61. }
  62. if (self.nameBtn.superview != nil) {
  63. self.nameBtn.setTitleColor(color: KMAppearance.titleColor())
  64. }
  65. }
  66. self._updateSelectBackground()
  67. }
  68. }
  69. }
  70. var unEnabled = false {
  71. didSet {
  72. self.clickButton.isEnabled = !self.unEnabled
  73. self.nameBtn.isEnabled = !self.unEnabled
  74. self.imageViewBtn.isEnabled = !self.unEnabled
  75. self.needExpandButton.isEnabled = !self.unEnabled
  76. }
  77. }
  78. var isShowCustomToolTip = false {
  79. didSet {
  80. if (self.isShowCustomToolTip) {
  81. self.clickButton.toolTip = ""
  82. }
  83. }
  84. }
  85. var boxImagePosition: NSControl.ImagePosition = .imageLeft {
  86. didSet {
  87. self._layoutView()
  88. }
  89. }
  90. var image: NSImage? {
  91. didSet {
  92. self.imageViewBtn.image = self.image
  93. }
  94. }
  95. var selectedImage: NSImage?
  96. var alternateImage: NSImage?
  97. var titleName: String? {
  98. didSet {
  99. self.nameBtn.title = self.titleName ?? " "
  100. self.nameBtn.setTitleColor(color: KMAppearance.titleColor())
  101. }
  102. }
  103. weak var target: AnyObject? {
  104. didSet {
  105. self.clickButton.target = self.target
  106. }
  107. }
  108. var btnAction: Selector? {
  109. didSet {
  110. self.clickButton.action = self.btnAction
  111. }
  112. }
  113. var needExpandAction = false
  114. var btnTag = 0 {
  115. didSet {
  116. self.clickButton.tag = self.btnTag
  117. }
  118. }
  119. var customizeView: NSView? {
  120. didSet {
  121. self._layoutView()
  122. }
  123. }
  124. var normalBackgroundColor: NSColor = .clear
  125. var selectedBackgroundColor: NSColor = KMAppearance.Status.selColor()
  126. var selectBackgroundType: KMToolbarItemViewSelectBackgroundType = .none
  127. lazy var imageViewBox: NSBox = {
  128. let view = NSBox()
  129. view.borderWidth = 0
  130. view.contentViewMargins = NSSize.zero
  131. view.boxType = .custom
  132. view.borderColor = .clear
  133. view.cornerRadius = 7.0
  134. return view
  135. }()
  136. private lazy var imageViewBtn: NSButton = {
  137. let view = NSButton()
  138. view.bezelStyle = .regularSquare
  139. view.isBordered = false
  140. view.imagePosition = .imageOnly
  141. return view
  142. }()
  143. private var nameBtn: NSButton = {
  144. let view = NSButton()
  145. view.bezelStyle = .regularSquare
  146. view.isBordered = false
  147. view.imagePosition = .imageOnly
  148. view.font = .systemFont(ofSize: 11)
  149. view.title = ""
  150. return view
  151. }()
  152. private var needExpandButton: NSButton = {
  153. let view = NSButton()
  154. view.bezelStyle = .regularSquare
  155. view.isBordered = false
  156. view.imagePosition = .imageOnly
  157. view.image = NSImage(named: "KMImageNameUXIconBtnTriDownNor")
  158. return view
  159. }()
  160. private var _popOver: NSPopover?
  161. var popOver: NSPopover? {
  162. get {
  163. return self._popOver
  164. }
  165. set {
  166. if self._popOver == nil || self._popOver!.isEqual(to: newValue) == false {
  167. self._popOver = newValue
  168. if (newValue != nil) {
  169. self.layer?.backgroundColor = self.selectedBackgroundColor.cgColor
  170. } else {
  171. self._updateSelectBackground()
  172. }
  173. }
  174. }
  175. }
  176. private var _menuViewController: KMCustomButtonPopMenuViewController?
  177. private var _kNormalImage: NSImage?
  178. private var _originalHelpTip: String?
  179. deinit {
  180. // Swift.debugPrint("KMToolbarItemView deinit")
  181. NotificationCenter.default.removeObserver(self)
  182. }
  183. convenience init(itemIdentifier: String) {
  184. self.init()
  185. self._itemIdentifier = itemIdentifier
  186. self.boxImagePosition = .imageLeft
  187. self.wantsLayer = true
  188. self.layer?.cornerRadius = 5
  189. self.layer?.masksToBounds = true
  190. self._addTrackingArea()
  191. NotificationCenter.default.addObserver(self, selector: #selector(_windowClosedPop), name: KMToolbarItemView.popOverClosedNotificationName, object: nil)
  192. }
  193. convenience init(itemIdentifier: String, postition imagePositionImagePosition: NSControl.ImagePosition, withPopMenu popMenuViewController: KMCustomButtonPopMenuViewController?) {
  194. self.init()
  195. self.boxImagePosition = imagePositionImagePosition
  196. self._menuViewController = popMenuViewController
  197. self._itemIdentifier = itemIdentifier
  198. self.wantsLayer = true
  199. self.layer?.cornerRadius = 4
  200. self.layer?.masksToBounds = true
  201. self._addTrackingArea()
  202. self._layoutView()
  203. if (popMenuViewController != nil) {
  204. NotificationCenter.default.addObserver(self, selector: #selector(_windowClosedPop), name: KMToolbarItemView.popOverClosedNotificationName, object: nil)
  205. }
  206. }
  207. override func draw(_ dirtyRect: NSRect) {
  208. if (self.itemIdentifier == KMToolbarDividerItemIdentifier) {
  209. let context = NSGraphicsContext.current?.cgContext
  210. KMContextSaveGState(context)
  211. KMContextTranslateCTM(context, CGRectGetWidth(dirtyRect)/2.0, CGRectGetHeight(dirtyRect)/2.0-10)
  212. KMContextMoveToPoint(context, 0, 0)
  213. KMContextAddLineToPoint(context, 0, 20)
  214. KMContextSetStrokeColorWithColor(context, NSColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor)
  215. KMContextStrokePath(context)
  216. KMContextRestoreGState(context)
  217. }
  218. }
  219. override var toolTip: String? {
  220. get {
  221. return self._originalHelpTip
  222. }
  223. set {
  224. self.clickButton.toolTip = newValue ?? ""
  225. self._originalHelpTip = self.clickButton.toolTip
  226. if(self.isShowCustomToolTip) {
  227. self.clickButton.toolTip = ""
  228. }
  229. }
  230. }
  231. override func mouseEntered(with event: NSEvent) {
  232. super.mouseEntered(with: event)
  233. guard let _window = self.window else {
  234. return
  235. }
  236. if (!_window.isKeyWindow) {
  237. return
  238. }
  239. if (self.itemIdentifier == KMToolbarDividerItemIdentifier || self.customizeView != nil || self.image == nil) {
  240. return
  241. }
  242. if (!self.isSelected) {
  243. if self.selectBackgroundType == .none {
  244. self.layer?.backgroundColor = self.selectedBackgroundColor.cgColor
  245. } else {
  246. self.imageViewBox.fillColor = self.selectedBackgroundColor
  247. }
  248. if(self.image != nil && self.alternateImage != nil) {
  249. self._kNormalImage = self.image
  250. self.imageViewBtn.image = self.alternateImage
  251. if(self.nameBtn.superview != nil) {
  252. self.nameBtn.setTitleColor(color: KMAppearance.titleColor())
  253. }
  254. }
  255. }
  256. if (self.needExpandAction) {
  257. // KMImageNameUXIconBtnTriDownSel
  258. self.needExpandButton.image = NSImage(named: "KMImageNameUXIconBtnTriDownNor")
  259. self._showPop(self)
  260. }else if (self.isShowCustomToolTip) {
  261. self.perform(#selector(_showHUDHint), with: nil, afterDelay: 0.1)
  262. }
  263. }
  264. override func mouseExited(with event: NSEvent) {
  265. super.mouseExited(with: event)
  266. if (!self.isSelected && !self.needExpandAction) {
  267. if self.selectBackgroundType == .none {
  268. self.layer?.backgroundColor = self.normalBackgroundColor.cgColor
  269. } else {
  270. self.imageViewBox.fillColor = self.normalBackgroundColor
  271. }
  272. if(self.image != nil && self.alternateImage != nil) {
  273. self.imageViewBtn.image = self._kNormalImage ?? self.image!
  274. }
  275. }
  276. if(self.needExpandAction && !self.isSelected) {
  277. if self.selectBackgroundType == .none {
  278. self.layer?.backgroundColor = self.normalBackgroundColor.cgColor
  279. } else {
  280. self.imageViewBox.fillColor = self.normalBackgroundColor
  281. }
  282. if(self.image != nil && self.alternateImage != nil) {
  283. self.imageViewBtn.image = self._kNormalImage ?? self.image!
  284. }
  285. self.needExpandButton.image = NSImage(named: "KMImageNameUXIconBtnTriDownNor")
  286. }
  287. guard let _window = self.window else {
  288. return
  289. }
  290. if (!_window.isKeyWindow) {
  291. return
  292. }
  293. if(self.nameBtn.superview != nil && !self.isSelected) {
  294. self.nameBtn.setTitleColor(color: KMAppearance.titleColor())
  295. }
  296. if (self.isShowCustomToolTip && !self.needExpandAction) {
  297. NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(_showHUDHint), object: nil)
  298. self._closePop()
  299. }
  300. }
  301. private func _updateSelectBackground() {
  302. if self.selectBackgroundType == .none {
  303. if self.isSelected {
  304. self.layer?.backgroundColor = self.selectedBackgroundColor.cgColor
  305. } else {
  306. self.layer?.backgroundColor = self.normalBackgroundColor.cgColor
  307. }
  308. } else if self.selectBackgroundType == .imageBox {
  309. if self.isSelected {
  310. self.imageViewBox.fillColor = self.selectedBackgroundColor
  311. } else {
  312. self.imageViewBox.fillColor = self.normalBackgroundColor
  313. }
  314. }
  315. }
  316. }
  317. // MARK: - Private Methods
  318. extension KMToolbarItemView {
  319. private func _addTrackingArea() {
  320. let trackingArea = NSTrackingArea(rect: self.bounds, options: [.mouseEnteredAndExited, .inVisibleRect, .activeInKeyWindow], owner: self)
  321. self.addTrackingArea(trackingArea)
  322. }
  323. private func _showPop(_ sender: NSView) {
  324. if (self._popOver != nil) {
  325. return
  326. }
  327. let menuViewController = KMCustomButtonPopMenuViewController()
  328. menuViewController.delegate = self
  329. menuViewController.dataSources = self
  330. self.popOver = NSPopover()
  331. self.popOver?.delegate = self
  332. self.popOver?.contentViewController = menuViewController
  333. self.popOver?.animates = false
  334. self.popOver?.behavior = .semitransient
  335. self.popOver?.contentSize = menuViewController.view.frame.size
  336. var sourcesRect = sender.bounds
  337. sourcesRect = sender.convert(sourcesRect, to: nil)
  338. // sourcesRect.size = sender.convert(sourcesRect.size, to: nil)
  339. sourcesRect.origin.y += 20
  340. sourcesRect.size.height += 20
  341. self.window?.popover = self.popOver
  342. self.window?.sourcesRect = sourcesRect
  343. self.popOver?.show(relativeTo: CGRectInset(self.bounds, 0, 5), of: self, preferredEdge: .minY)
  344. }
  345. private func _closePop() {
  346. self.popOver?.close()
  347. self.popOver = nil
  348. }
  349. @objc private func _showHUDHint() {
  350. // KMToolbarItemPopViewController *popViewController = [[[KMToolbarItemPopViewController alloc] init] autorelease];
  351. // self.popOver = [[[NSPopover alloc] init] autorelease];
  352. // self.popOver.contentViewController = popViewController;
  353. // self.popOver.animates = NO;
  354. // self.popOver.behavior = NSPopoverBehaviorSemitransient;
  355. // self.popOver.backgroundColor = [KMAppearance KMBluegrey01Color];
  356. //
  357. // self.popOver.contentSize = popViewController.view.frame.size;
  358. // [popViewController updateWithHelpTip:self.originalHelpTip];
  359. // [self.popOver showRelativeToRect:self.bounds ofView:self preferredEdge:NSRectEdgeMinY];
  360. }
  361. @objc private func _windowClosedPop(sender: Notification) {
  362. if let data = self._popOver?.isEqual(to: sender.object), data {
  363. self.popOver = nil
  364. }
  365. }
  366. private func _layoutView() {
  367. if self.nameBtn.superview != nil {
  368. self.nameBtn.removeFromSuperview()
  369. }
  370. if self.imageViewBox.superview != nil {
  371. self.imageViewBox.removeFromSuperview()
  372. }
  373. if self.imageViewBtn.superview != nil {
  374. self.imageViewBtn.removeFromSuperview()
  375. }
  376. if let view = self.customizeView {
  377. if view.superview != nil {
  378. view.removeFromSuperview()
  379. }
  380. self.addSubview(view)
  381. view.km_add_leading_constraint()
  382. view.km_add_trailing_constraint()
  383. view.km_add_centerY_constraint()
  384. view.km_add_width_constraint(constant: NSWidth(view.bounds))
  385. view.km_add_height_constraint(constant: NSHeight(view.bounds))
  386. return
  387. } else if (self.itemIdentifier == KMToolbarDividerItemIdentifier) {
  388. self.addSubview(self.imageViewBox)
  389. self.imageViewBox.km_add_inset_constraint(inset: NSEdgeInsetsZero)
  390. self.imageViewBox.km_add_width_constraint(constant: 8)
  391. return
  392. }
  393. let offset = 4.0
  394. let offsetY = 2.0
  395. let offsetX = 4.0
  396. if self.boxImagePosition == .imageOnly {
  397. self.addSubview(self.imageViewBox)
  398. self.imageViewBox.km_add_inset_constraint()
  399. self.imageViewBox.contentView?.addSubview(self.imageViewBtn)
  400. self.imageViewBtn.km_add_inset_constraint(equalTo: self.imageViewBox, inset: NSEdgeInsets(top: offsetY, left: offsetX, bottom: offsetY, right: offsetX))
  401. } else if (self.boxImagePosition == .imageLeft) {
  402. self.addSubview(self.imageViewBox)
  403. self.imageViewBox.km_add_leading_constraint()
  404. self.imageViewBox.km_add_top_constraint()
  405. self.imageViewBox.km_add_bottom_constraint()
  406. self.imageViewBox.contentView?.addSubview(self.imageViewBtn)
  407. self.imageViewBtn.km_add_inset_constraint(equalTo: self.imageViewBox, inset: NSEdgeInsets(top: offsetY, left: 2*offsetX, bottom: offsetY, right: 0))
  408. self.addSubview(self.nameBtn)
  409. self.nameBtn.km_add_centerY_constraint()
  410. self.nameBtn.km_add_leading_constraint(equalTo: self.imageViewBox, attribute: .trailing)
  411. if (self.needExpandAction) {
  412. self.nameBtn.km_add_right_constraint(constant: -2*offsetX-8)
  413. } else {
  414. self.nameBtn.km_add_right_constraint(constant: -2*offsetX)
  415. }
  416. if(self.needExpandAction) {
  417. self.needExpandButton.image = NSImage(named: "KMImageNameUXIconBtnTriDownNor")
  418. self.addSubview(self.needExpandButton)
  419. self.needExpandButton.km_add_centerY_constraint()
  420. self.needExpandButton.km_add_width_constraint(constant: 8)
  421. self.needExpandButton.km_add_right_constraint(constant: -offset)
  422. }
  423. self.layer?.cornerRadius = 6
  424. } else if (self.boxImagePosition == .imageExpandLeft) {
  425. self.addSubview(self.imageViewBox)
  426. self.imageViewBox.km_add_leading_constraint()
  427. self.imageViewBox.km_add_top_constraint()
  428. self.imageViewBox.km_add_bottom_constraint()
  429. self.imageViewBox.contentView?.addSubview(self.imageViewBtn)
  430. self.imageViewBtn.km_add_inset_constraint(equalTo: self.imageViewBox, inset: NSEdgeInsets(top: offsetY, left: offsetX, bottom: offsetY, right: 0))
  431. self.needExpandButton.image = NSImage(named: "KMImageNameUXIconBtnTriDownNor")
  432. self.addSubview(self.needExpandButton)
  433. self.needExpandButton.km_add_centerY_constraint()
  434. self.needExpandButton.km_add_width_constraint(constant: 8)
  435. self.needExpandButton.km_add_right_constraint(constant: -offset)
  436. self.addSubview(self.nameBtn)
  437. self.nameBtn.km_add_centerY_constraint()
  438. self.nameBtn.km_add_leading_constraint(equalTo: self.imageViewBox, attribute: .trailing)
  439. self.nameBtn.km_add_trailing_constraint(equalTo: self.needExpandButton, attribute: .leading)
  440. } else if (self.boxImagePosition == .imageAbove) {
  441. self.addSubview(self.nameBtn)
  442. self.nameBtn.alignment = .center
  443. self.nameBtn.mas_makeConstraints { make in
  444. make?.left.right().equalTo()(0)
  445. make?.width.greaterThanOrEqualTo()(32)
  446. make?.bottom.equalTo()(self.mas_bottom)?.offset()(0)
  447. }
  448. // self.nameBtn.km_add_leading_constraint()
  449. // self.nameBtn.km_add_trailing_constraint()
  450. // self.nameBtn.km_add_bottom_constraint()
  451. self.addSubview(self.imageViewBox)
  452. self.imageViewBox.km_add_top_constraint()
  453. self.imageViewBox.km_add_width_constraint(constant: 32)
  454. self.imageViewBox.km_add_centerX_constraint()
  455. self.imageViewBox.km_add_bottom_constraint(equalTo: self.nameBtn, attribute: .top, constant: 0)
  456. self.imageViewBox.contentView?.addSubview(self.imageViewBtn)
  457. self.imageViewBtn.km_add_inset_constraint(inset: .init(top: 0, left: offset, bottom: 0, right: offset))
  458. }
  459. self.imageViewBox.borderWidth = 1.0
  460. self.addSubview(self.clickButton)
  461. self.clickButton.km_add_inset_constraint()
  462. }
  463. }
  464. extension KMToolbarItemView: NSPopoverDelegate {
  465. func popoverDidClose(_ notification: Notification) {
  466. if let data = self.popOver?.isEqual(to: notification.object), data {
  467. self._closePop()
  468. }
  469. }
  470. }
  471. extension KMToolbarItemView: KMCustomButtonPopMenuViewControllerDelegate, KMCustomButtonPopMenuViewControllerDataSources {
  472. func customViewButtonPopDidSelectIndex(_ index: Int) {
  473. self._closePop()
  474. // if (self.itemIdentifier == KMToolbarPageEditPageRangeItemIdentifier) {
  475. if let items = self.menuFormRepresentation?.submenu?.items {
  476. let item = items[index]
  477. _ = item.target?.perform(item.action, with: item)
  478. }
  479. // }
  480. }
  481. func numberOfLine() -> Int {
  482. // if (self.itemIdentifier == KMToolbarPageEditPageRangeItemIdentifier) {
  483. if let items = self.menuFormRepresentation?.submenu?.items {
  484. return items.count
  485. }
  486. // }
  487. return 0
  488. }
  489. func stringForLine(at index: Int) -> String? {
  490. // if (self.itemIdentifier == KMToolbarPageEditPageRangeItemIdentifier) {
  491. if let items = self.menuFormRepresentation?.submenu?.items {
  492. return items[index].title
  493. }
  494. // }
  495. return nil
  496. }
  497. func needInsertSeperateLine(at index: Int) -> Bool {
  498. if let items = self.menuFormRepresentation?.submenu?.items {
  499. return items[index].isSeparatorItem
  500. }
  501. return false
  502. }
  503. func needHightLightLine(at index: Int) -> Bool {
  504. return false
  505. }
  506. func imageForLine(at index: Int) -> NSImage? {
  507. if let items = self.menuFormRepresentation?.submenu?.items {
  508. return items[index].image
  509. }
  510. return nil
  511. }
  512. func itemEnable(at index: Int) -> Bool {
  513. return true
  514. }
  515. }