KMDesignSelect.swift 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. //
  2. // KMDesignSelect.swift
  3. // PDF Office
  4. //
  5. // Created by wanjun on 2023/2/22.
  6. //
  7. import Cocoa
  8. @objc protocol KMSelectPopButtonDelegate: NSObjectProtocol {
  9. @objc optional func km_controlTextDidEndEditing(_ obj: KMDesignSelect)
  10. @objc optional func km_controlTextDidChange(_ obj: KMDesignSelect)
  11. func km_comboBoxSelectionDidChange(_ obj: KMDesignSelect)
  12. }
  13. @objc enum SelectType : Int {
  14. case PopButton = 0
  15. case Combox
  16. }
  17. class KMSelectCell: NSTextFieldCell {
  18. var borderThickness: CGFloat = 1
  19. var offset: CGFloat = 0.0
  20. override func drawingRect(forBounds theRect: NSRect) -> NSRect {
  21. var newRect:NSRect = super.drawingRect(forBounds: theRect)
  22. let textSize:NSSize = self.cellSize(forBounds: theRect)
  23. // let heightDelta:CGFloat = newRect.size.height - textSize.height
  24. // if heightDelta > 0 {
  25. // newRect.size.height = textSize.height
  26. // newRect.origin.y += heightDelta * 0.5
  27. // } else {
  28. // newRect.size.height = textSize.height
  29. // newRect.origin.y += heightDelta
  30. // }
  31. newRect.size.height = textSize.height
  32. newRect.origin.y += 2.0
  33. newRect.origin.x += offset
  34. newRect.size.width = theRect.width - offset*2
  35. // newRect.fill()
  36. return newRect
  37. }
  38. override func draw(withFrame cellFrame: NSRect, in controlView: NSView) {
  39. // Area that covers the NSTextField itself. That is the total height minus our custom border size.
  40. let interiorFrame = NSRect(x: 0, y: 0, width: cellFrame.width, height: cellFrame.height - borderThickness)
  41. let path = NSBezierPath()
  42. path.lineWidth = borderThickness
  43. // Line width is at the center of the line.
  44. path.move(to: NSPoint(x: 0, y: cellFrame.height))
  45. path.line(to: NSPoint(x: cellFrame.width, y: cellFrame.height))
  46. path.line(to: NSPoint(x: cellFrame.width, y: 0))
  47. path.line(to: NSPoint(x: 0, y: 0))
  48. NSColor.clear.setStroke()
  49. path.stroke()
  50. // Pass in area minus the border thickness in the height
  51. drawInterior(withFrame: interiorFrame, in: controlView)
  52. }
  53. }
  54. @objcMembers class KMDesignSelect: NSViewController {
  55. @IBOutlet weak var mainBox: KMBox!
  56. @IBOutlet weak var selectBox: NSBox!
  57. @IBOutlet weak var textField: NSTextField!
  58. @IBOutlet weak var imageView: NSImageView!
  59. @IBOutlet weak var horizontalPadding_spacing: NSLayoutConstraint!
  60. @IBOutlet weak var itemSpacing_spacing: NSLayoutConstraint!
  61. @IBOutlet weak var imageViewWidth_spacing: NSLayoutConstraint!
  62. @IBOutlet weak var imageViewHeight_spacing: NSLayoutConstraint!
  63. var imageWidth: Float = 12.0 // 图片宽度
  64. var imageHeight: Float = 12.0 // 图片高度
  65. var horizontalPadding: Float = 8.0
  66. var itemSpacing: Float = 8.0
  67. var borderColor: NSColor = .clear// 边框颜色
  68. var borderColor_hov: NSColor = .clear// 边框颜色
  69. var borderColor_focus: NSColor = .clear// 边框颜色
  70. var borderColor_disabled: NSColor = .clear// 边框颜色
  71. var borderColor_errordef: NSColor = .clear// 边框颜色
  72. var borderColor_errorfocus: NSColor = .clear// 边框颜色
  73. var cornerRadius: Float = 0.0// 边框圆角
  74. var cornerRadius_hov: Float = 0.0// 边框圆角
  75. var cornerRadius_focus: Float = 0.0// 边框圆角
  76. var cornerRadius_disabled: Float = 0.0// 边框圆角
  77. var cornerRadius_errordef: Float = 0.0// 边框圆角
  78. var cornerRadius_errorfocus: Float = 0.0// 边框圆角
  79. var borderWidth: Float = 1.0// 边框宽度
  80. var borderWidth_hov: Float = 1.0// 边框宽度
  81. var borderWidth_focus: Float = 1.0// 边框宽度
  82. var borderWidth_disabled: Float = 1.0// 边框宽度
  83. var borderWidth_errordef: Float = 1.0// 边框宽度
  84. var borderWidth_errorfocus: Float = 1.0// 边框宽度
  85. var background: NSColor = .clear// 背景颜色
  86. var background_hov: NSColor = .clear// 背景颜色
  87. var background_focus: NSColor = .clear// 背景颜色
  88. var background_disabled: NSColor = .clear// 背景颜色
  89. var background_errordef: NSColor = .clear// 背景颜色
  90. var background_errorfocus: NSColor = .clear// 背景颜色
  91. var textColor: NSColor = .black // 内容颜色
  92. var textColor_hov: NSColor = .black // 内容颜色
  93. var textColor_focus: NSColor = .black // 内容颜色
  94. var textColor_disabled: NSColor = .black // 内容颜色
  95. var textColor_errordef: NSColor = .black // 内容颜色
  96. var textColor_errorfocus: NSColor = .black // 内容颜色
  97. var lineHeight: CGFloat = 20.0 // 默认 内容行高
  98. var lineHeight_hov: CGFloat = 20.0 // 默认 内容行高
  99. var lineHeight_focus: CGFloat = 20.0 // 默认 内容行高
  100. var lineHeight_disabled: CGFloat = 20.0 // 默认 内容行高
  101. var lineHeight_errordef: CGFloat = 20.0 // 默认 内容行高
  102. var lineHeight_errorfocus: CGFloat = 20.0 // 默认 内容行高
  103. var font: NSFont = NSFont.systemFont(ofSize: 14.0) // 内容字体
  104. var font_hov: NSFont = NSFont.systemFont(ofSize: 14.0) // 内容字体
  105. var font_focus: NSFont = NSFont.systemFont(ofSize: 14.0) // 内容字体
  106. var font_disabled: NSFont = NSFont.systemFont(ofSize: 14.0) // 内容字体
  107. var font_errordef: NSFont = NSFont.systemFont(ofSize: 14.0) // 内容字体
  108. var font_errorfocus: NSFont = NSFont.systemFont(ofSize: 14.0) // 内容字体
  109. var textbg_errorfocus: NSColor = .clear // 正在输入高亮
  110. var _image: NSImage = NSImage(named: "icon_btn_arrow_gray_down_s_norm_false")!
  111. var _stringValue: String = ""// 内容
  112. var toolTip: String = "" // 提示文字
  113. // button 通用属性
  114. var action: Selector? // 点击事件
  115. var target: AnyObject? // 对象目标
  116. var _enabled: Bool = true // 是否可点击
  117. var _state: KMDesignTokenState = .Norm
  118. var canHover: Bool = true // 是否可悬浮
  119. var _isHidden: Bool = false // 是否隐藏
  120. var _editable: Bool = false // 是否允许编辑
  121. var alignment: NSTextAlignment = .left //对齐
  122. var placeholderString: String = "" // 预设文字
  123. var lineBreakMode: NSLineBreakMode = .byTruncatingTail //文字超出显示
  124. var buttonType: SelectType = .PopButton
  125. var items: [String] = []
  126. open weak var delete: KMSelectPopButtonDelegate?
  127. var _indexOfSelectedItem: Int = 0
  128. var _numberOfItems: Int = 0
  129. var createFilePopover: NSPopover = NSPopover.init()
  130. init(withType type: SelectType) {
  131. super.init(nibName: "KMDesignSelect", bundle: nil)
  132. self.buttonType = type
  133. }
  134. required init?(coder: NSCoder) {
  135. fatalError("init(coder:) has not been implemented")
  136. }
  137. override func viewDidLoad() {
  138. super.viewDidLoad()
  139. // Do view setup here.
  140. if (buttonType == .PopButton) {
  141. self.mainBox.contentView = selectBox
  142. textField.delegate = self
  143. self.select(bg: "select.m.bg.norm", text: "select.m.mac-text.act")
  144. self.select(bg: "select.m.bg.hov", text: "select.m.mac-text.act", state: .Hov)
  145. self.select(bg: "select.m.bg.focus", text: "select.m.mac-text.act", state: .Focus)
  146. self.select(bg: "select.m.bg.dis", text: "select.m.mac-text.dis", state: .Disabled)
  147. self.select(bg: "select.m.bg.error-def", text: "select.m.mac-text.act", state: .Error_def)
  148. self.select(bg: "select.m.bg.error-focus", text: "select.m.mac-text.act", textbg: "select.m.bg-text", state: .Error_focus)
  149. } else if (buttonType == .Combox) {
  150. self.mainBox.contentView = selectBox
  151. textField.delegate = self
  152. self.select(bg: "select.m.bg.norm", text: "select.m.mac-text.act")
  153. self.select(bg: "select.m.bg.hov", text: "select.m.mac-text.act", state: .Hov)
  154. self.select(bg: "select.m.bg.focus", text: "select.m.mac-text.act", state: .Focus)
  155. self.select(bg: "select.m.bg.dis", text: "select.m.mac-text.dis", state: .Disabled)
  156. self.select(bg: "select.m.bg.error-def", text: "select.m.mac-text.act", state: .Error_def)
  157. self.select(bg: "select.m.bg.error-focus", text: "select.m.mac-text.act", textbg: "select.m.bg-text", state: .Error_focus)
  158. }
  159. self.mainBox.canHover = true
  160. self.mainBox.canClick = true
  161. self.mainBox.downCallback = { [self](downEntered: Bool, mouseBox: KMBox) -> Void in
  162. if enabled {
  163. if downEntered {
  164. self.canHover = false
  165. self.mainBoxAction(mouseBox)
  166. }
  167. }
  168. }
  169. self.mainBox.moveCallback = { [self](mouseEntered: Bool, mouseBox: KMBox) -> Void in
  170. if !self.createFilePopover.isShown {
  171. self.canHover = true
  172. if mouseEntered {
  173. state = .Hov
  174. } else {
  175. state = .Norm
  176. }
  177. updateUI()
  178. }
  179. }
  180. createFilePopover.delegate = self
  181. }
  182. // MARK: Get、Set
  183. var stringValue: String {
  184. get {
  185. return _stringValue
  186. }
  187. set {
  188. _stringValue = newValue
  189. let paragraphStyle = NSMutableParagraphStyle()
  190. if (state == .Norm) {
  191. paragraphStyle.lineSpacing = lineHeight
  192. } else if (state == .Hov) {
  193. paragraphStyle.lineSpacing = lineHeight_hov
  194. } else if (state == .Focus) {
  195. paragraphStyle.lineSpacing = lineHeight_focus
  196. } else if (state == .Disabled) {
  197. paragraphStyle.lineSpacing = lineHeight_disabled
  198. } else if (state == .Error_def) {
  199. paragraphStyle.lineSpacing = lineHeight_errordef
  200. } else if (state == .Error_focus) {
  201. paragraphStyle.lineSpacing = lineHeight_errorfocus
  202. }
  203. textField.attributedStringValue = NSAttributedString(string: _stringValue, attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle])
  204. }
  205. }
  206. var enabled: Bool {
  207. get {
  208. return _enabled
  209. }
  210. set {
  211. _enabled = newValue
  212. if _enabled {
  213. _state = .Norm
  214. } else {
  215. _state = .Disabled
  216. }
  217. updateUI()
  218. }
  219. }
  220. var state: KMDesignTokenState {
  221. get {
  222. return _state
  223. }
  224. set {
  225. _state = newValue
  226. updateUI()
  227. }
  228. }
  229. var isHidden: Bool {
  230. get {
  231. return _isHidden
  232. }
  233. set {
  234. _isHidden = newValue
  235. self.view.isHidden = _isHidden
  236. }
  237. }
  238. var editable: Bool {
  239. get {
  240. return _editable
  241. }
  242. set {
  243. _editable = newValue
  244. textField.isEditable = _editable
  245. }
  246. }
  247. var indexOfSelectedItem: Int {
  248. get {
  249. return _indexOfSelectedItem
  250. }
  251. set {
  252. _indexOfSelectedItem = newValue
  253. }
  254. }
  255. var numberOfItems: Int {
  256. get {
  257. return items.count
  258. }
  259. }
  260. var image: NSImage {
  261. get {
  262. return _image
  263. }
  264. set {
  265. _image = newValue
  266. imageView.image = _image
  267. }
  268. }
  269. // MARK: Private Methods
  270. func updateUI() -> Void {
  271. if (state == .Norm) {
  272. selectBox.fillColor = background
  273. selectBox.borderWidth = CGFloat(borderWidth)
  274. selectBox.cornerRadius = CGFloat(cornerRadius)
  275. selectBox.borderColor = borderColor
  276. textField.textColor = textColor
  277. textField.font = font
  278. } else if (state == .Hov) {
  279. selectBox.fillColor = background_hov
  280. selectBox.borderWidth = CGFloat(borderWidth_hov)
  281. selectBox.cornerRadius = CGFloat(cornerRadius_hov)
  282. selectBox.borderColor = borderColor_hov
  283. textField.textColor = textColor_hov
  284. textField.font = font_hov
  285. } else if (state == .Focus) {
  286. selectBox.fillColor = background_focus
  287. selectBox.borderWidth = CGFloat(borderWidth_focus)
  288. selectBox.cornerRadius = CGFloat(cornerRadius_focus)
  289. selectBox.borderColor = borderColor_focus
  290. textField.textColor = textColor_focus
  291. textField.font = font_focus
  292. } else if (state == .Disabled) {
  293. selectBox.fillColor = background_disabled
  294. selectBox.borderWidth = CGFloat(borderWidth_disabled)
  295. selectBox.cornerRadius = CGFloat(cornerRadius_disabled)
  296. selectBox.borderColor = borderColor_disabled
  297. textField.textColor = textColor_disabled
  298. textField.font = font_disabled
  299. } else if (state == .Error_def) {
  300. selectBox.fillColor = background_errordef
  301. selectBox.borderWidth = CGFloat(borderWidth_errordef)
  302. selectBox.cornerRadius = CGFloat(cornerRadius_errordef)
  303. selectBox.borderColor = borderColor_errordef
  304. textField.textColor = textColor_errordef
  305. textField.font = font_errordef
  306. } else if (state == .Error_focus) {
  307. selectBox.fillColor = background_errorfocus
  308. selectBox.borderWidth = CGFloat(borderWidth_errorfocus)
  309. selectBox.cornerRadius = CGFloat(cornerRadius_errorfocus)
  310. selectBox.borderColor = borderColor_errorfocus
  311. textField.textColor = textColor_errorfocus
  312. textField.font = font_errorfocus
  313. }
  314. imageViewWidth_spacing.constant = CGFloat(imageWidth)
  315. imageViewHeight_spacing.constant = CGFloat(imageHeight)
  316. horizontalPadding_spacing.constant = CGFloat(horizontalPadding)
  317. itemSpacing_spacing.constant = CGFloat(itemSpacing)
  318. if action != nil {
  319. // button.action = action
  320. // button.target = target
  321. }
  322. // button.isEnabled = enabled
  323. if toolTip != "" {
  324. mainBox.toolTip = toolTip
  325. }
  326. textField.isEditable = editable
  327. textField.alignment = alignment
  328. textField.placeholderString = placeholderString
  329. textField.lineBreakMode = lineBreakMode
  330. }
  331. func removeAllItems() {
  332. items = []
  333. }
  334. func addItems(withObjectValues objects: [String]) {
  335. items = objects
  336. }
  337. func selectItem(at index: Int) {
  338. if items.count > 0 {
  339. stringValue = items[index]
  340. }
  341. }
  342. // MARK: Action
  343. func mainBoxAction(_ sender: Any) -> Void {
  344. let vc: KMHomePopViewController = KMHomePopViewController().initWithPopViewDataArr(items)
  345. createFilePopover.contentViewController = vc
  346. createFilePopover.animates = true
  347. createFilePopover.behavior = .semitransient
  348. createFilePopover.setValue(true, forKey: "shouldHideAnchor")
  349. createFilePopover.show(relativeTo: CGRect(x: view.bounds.origin.x, y: 10, width: view.bounds.size.width, height: view.bounds.size.height), of: view, preferredEdge: .minY)
  350. vc.customBoxWidthLayoutConstraint.constant = mainBox.frame.width
  351. vc.downCallback = {(downEntered: Bool, count: String) -> Void in
  352. if downEntered {
  353. self.stringValue = count
  354. let current = self.items.firstIndex(of: count) ?? 0
  355. self.indexOfSelectedItem = current
  356. self.delete?.km_comboBoxSelectionDidChange(self)
  357. self.updateUI()
  358. self.createFilePopover.close()
  359. }
  360. }
  361. }
  362. }
  363. extension KMDesignSelect: NSTextFieldDelegate {
  364. func controlTextDidChange(_ obj: Notification) {
  365. let textfield = obj.object as! NSTextField
  366. self.stringValue = textfield.stringValue
  367. self.delete?.km_controlTextDidChange!(self)
  368. }
  369. func controlTextDidEndEditing(_ obj: Notification) {
  370. let textfield = obj.object as! NSTextField
  371. self.stringValue = textfield.stringValue
  372. self.delete?.km_controlTextDidEndEditing!(self)
  373. }
  374. }
  375. extension KMDesignSelect: NSPopoverDelegate {
  376. func popoverWillShow(_ notification: Notification) {
  377. let popover = notification.object as! NSPopover
  378. if (createFilePopover == popover) {
  379. self.state = .Focus
  380. }
  381. }
  382. func popoverWillClose(_ notification: Notification) {
  383. let popover = notification.object as! NSPopover
  384. if (createFilePopover == popover) {
  385. self.state = .Norm
  386. }
  387. }
  388. }