KMDesignSelect.swift 15 KB

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