KMCustomStepperView.swift 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. //
  2. // KMCustomStepperView.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by tangchao on 2023/6/8.
  6. //
  7. // 自定义步进器
  8. class KMCustomStepperView: KMHoverView {
  9. // private lazy var content_stackView: NSStackView = {
  10. // let view = NSStackView()
  11. // // 排列方向 默认为水平(horizontal)
  12. // view.orientation = .horizontal
  13. //// view.distribution = .fillProportionally
  14. // // 对齐方式
  15. //// view.alignment = .centerY
  16. // view.spacing = 1
  17. //
  18. // return view
  19. // }()
  20. private var _contentView = NSView()
  21. private var _backgroundView = NSView()
  22. var backgroundView: NSView {
  23. get {
  24. return self._backgroundView
  25. }
  26. }
  27. private var _textField = KMTextField()
  28. var textField: KMTextField {
  29. get {
  30. return self._textField
  31. }
  32. }
  33. private var _right_v_line = NSView()
  34. private var _right_up_button = NSButton()
  35. private var _right_h_line = NSView()
  36. private var _right_down_button = NSButton()
  37. private var _right_up_hover = KMHoverView()
  38. private var _right_down_hover = KMHoverView()
  39. var contentInset: NSEdgeInsets = NSEdgeInsetsZero {
  40. didSet {
  41. self.needsLayout = true
  42. }
  43. }
  44. var contentOffset: CGFloat = 8 {
  45. didSet {
  46. self.needsLayout = true
  47. }
  48. }
  49. var upItemImage: NSImage? {
  50. didSet {
  51. self._right_up_button.image = self.upItemImage
  52. }
  53. }
  54. var downItemImage: NSImage? {
  55. didSet {
  56. self._right_down_button.image = self.downItemImage
  57. }
  58. }
  59. var itemWidth: CGFloat = 20 {
  60. didSet {
  61. self.needsLayout = true
  62. }
  63. }
  64. var itemHeight: CGFloat = 14 {
  65. didSet {
  66. self.needsLayout = true
  67. }
  68. }
  69. var horizontalDividerHeight: CGFloat = 1 {
  70. didSet {
  71. self.needsLayout = true
  72. }
  73. }
  74. var horizontalDividerColor: NSColor = .lightGray {
  75. didSet {
  76. self._right_h_line.wantsLayer = true
  77. self._right_h_line.layer?.backgroundColor = self.horizontalDividerColor.cgColor
  78. }
  79. }
  80. var verticalDividerWidth: CGFloat = 1 {
  81. didSet {
  82. self.needsLayout = true
  83. }
  84. }
  85. var verticalDividerColor: NSColor = .lightGray {
  86. didSet {
  87. self._right_v_line.wantsLayer = true
  88. self._right_v_line.layer?.backgroundColor = self.verticalDividerColor.cgColor
  89. }
  90. }
  91. var kmEnabled = true {
  92. didSet {
  93. self.textField.isEnabled = self.kmEnabled
  94. self._right_up_button.isEnabled = self.kmEnabled
  95. self._right_down_button.isEnabled = self.kmEnabled
  96. }
  97. }
  98. var minValue: Double = CGFLOAT_MIN
  99. var maxValue: Double = CGFLOAT_MAX
  100. var increment: Double = 1
  101. var autorepeat = true
  102. // var valueWraps = false
  103. private var _kmValue: Double = 0
  104. var kmValue: Double {
  105. get {
  106. return self._kmValue
  107. }
  108. set {
  109. self._kmValue = newValue
  110. self.textField.stringValue = String(format: "%.f", self._kmValue)
  111. }
  112. }
  113. var rightHoverAction: ((NSButton, KMHoverAction)->Void)?
  114. weak var delegate: KMTextFieldDelegate?
  115. override var isFlipped: Bool {
  116. return true
  117. }
  118. override init(frame frameRect: NSRect) {
  119. super.init(frame: frameRect)
  120. self.initSubViews()
  121. self.initDefaultValue()
  122. }
  123. required init?(coder: NSCoder) {
  124. super.init(coder: coder)
  125. self.initSubViews()
  126. self.initDefaultValue()
  127. }
  128. func initSubViews() {
  129. self.addSubview(self._contentView)
  130. self._contentView.addSubview(self._backgroundView)
  131. self._contentView.addSubview(self._textField)
  132. self._contentView.addSubview(self._right_v_line)
  133. self._contentView.addSubview(self._right_up_button)
  134. self._contentView.addSubview(self._right_h_line)
  135. self._contentView.addSubview(self._right_down_button)
  136. self._right_up_button.target = self
  137. self._right_up_button.action = #selector(_right_up_button_action)
  138. self._right_down_button.target = self
  139. self._right_down_button.action = #selector(_right_down_button_action)
  140. self._textField.firstResponderHandler = { [unowned self] result in
  141. self.delegate?.km_didBecomeFirstResponder(textField: self)
  142. }
  143. self._contentView.addSubview(self._right_up_hover, positioned: .below, relativeTo: self._right_up_button)
  144. self._contentView.addSubview(self._right_down_hover, positioned: .below, relativeTo: self._right_down_button)
  145. self._right_up_hover.hoverAction = { [unowned self] _, action in
  146. guard let callback = self.rightHoverAction else {
  147. return
  148. }
  149. callback(self._right_up_button, action)
  150. }
  151. self._right_down_hover.hoverAction = { [unowned self] _, action in
  152. guard let callback = self.rightHoverAction else {
  153. return
  154. }
  155. callback(self._right_down_button, action)
  156. }
  157. }
  158. func initDefaultValue() {
  159. self.horizontalDividerColor = .lightGray
  160. self.verticalDividerColor = .lightGray
  161. self._textField.isBezeled = false
  162. self._textField.focusRingType = .none
  163. self._textField.lineBreakMode = .byTruncatingTail
  164. self._textField.drawsBackground = false
  165. self._textField.delegate = self
  166. self._textField.formatter = TextFieldFormatter()
  167. self._right_up_button.isBordered = false
  168. self._right_up_button.title = ""
  169. self._right_down_button.isBordered = false
  170. self._right_down_button.title = ""
  171. if let _image = NSImage(named: "btn_arrow_gray_up_s_norm_on") {
  172. self.upItemImage = _image
  173. }
  174. if let _image = NSImage(named: "btn_arrow_gray_down_s_norm_on") {
  175. self.downItemImage = _image
  176. }
  177. }
  178. override func layout() {
  179. super.layout()
  180. let width: CGFloat = NSWidth(self.bounds)
  181. let height: CGFloat = NSHeight(self.bounds)
  182. let leftMargin: CGFloat = self.contentInset.left
  183. let topMargin: CGFloat = self.contentInset.top
  184. let rightMargin: CGFloat = self.contentInset.right
  185. let bottomMargin: CGFloat = self.contentInset.bottom
  186. let contentW: CGFloat = width - leftMargin - rightMargin
  187. let contentH: CGFloat = height - topMargin - bottomMargin
  188. self._contentView.frame = self.bounds
  189. self._backgroundView.frame = NSMakeRect(leftMargin, topMargin, contentW, contentH)
  190. let itemW = self.itemWidth
  191. let itemH = self.itemHeight
  192. var tfH: CGFloat = 22
  193. if let font = self._textField.font {
  194. tfH = font.pointSize * 1.5
  195. }
  196. let tfX = leftMargin + self.contentOffset
  197. self._textField.frame = NSMakeRect(tfX, (height-tfH)*0.5, width-tfX-rightMargin-itemW-self.verticalDividerWidth-self.contentOffset, tfH)
  198. let itemX: CGFloat = width-rightMargin-itemW-1
  199. self._right_v_line.frame = NSMakeRect(itemX-self.verticalDividerWidth*0.5, 0, self.verticalDividerWidth, contentH)
  200. self._right_up_button.frame = NSMakeRect(itemX, height*0.5+self.horizontalDividerHeight*0.5, itemW, itemH)
  201. self._right_h_line.frame = NSMakeRect(itemX, (height-self.horizontalDividerHeight)*0.5, itemW, self.horizontalDividerHeight)
  202. self._right_down_button.frame = NSMakeRect(itemX, 1.5, itemW, itemH)
  203. self._right_up_hover.frame = self._right_up_button.frame
  204. self._right_down_hover.frame = self._right_down_button.frame
  205. }
  206. @objc private func _right_up_button_action() {
  207. let value = self.kmValue + self.increment
  208. if (value > self.maxValue) {
  209. self.kmValue = self.autorepeat ? self.minValue : self.maxValue
  210. } else {
  211. self.kmValue = value
  212. }
  213. self.delegate?.km_controlTextDidChange(textField: self)
  214. }
  215. @objc private func _right_down_button_action() {
  216. let value = self.kmValue - self.increment
  217. if (value < self.minValue) {
  218. self.kmValue = self.autorepeat ? self.maxValue : self.minValue
  219. } else {
  220. self.kmValue = value
  221. }
  222. self.delegate?.km_controlTextDidChange(textField: self)
  223. }
  224. }
  225. extension KMCustomStepperView: NSTextFieldDelegate {
  226. func control(_ control: NSControl, textShouldBeginEditing fieldEditor: NSText) -> Bool {
  227. if let should = self.delegate?.km_controlTextShouldBeginEditing(textField: self) {
  228. return should
  229. }
  230. return true
  231. }
  232. func controlTextDidBeginEditing(_ obj: Notification) {
  233. if (!self._textField.isEqual(to: obj.object)) {
  234. return
  235. }
  236. self.delegate?.km_controlTextDidBeginEditing(textField: self)
  237. }
  238. func controlTextDidChange(_ obj: Notification) {
  239. if (!self._textField.isEqual(to: obj.object)) {
  240. return
  241. }
  242. if (self._textField.doubleValue > self.maxValue) {
  243. self.kmValue = self.maxValue
  244. } else if (self._textField.doubleValue < self.minValue) {
  245. self.kmValue = self.minValue
  246. } else {
  247. self.kmValue = self.textField.doubleValue
  248. }
  249. self.delegate?.km_controlTextDidChange(textField: self)
  250. }
  251. func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool {
  252. if let should = self.delegate?.km_controlTextShouldEndEditing(textField: self) {
  253. return should
  254. }
  255. return true
  256. }
  257. func controlTextDidEndEditing(_ obj: Notification) {
  258. if (!self._textField.isEqual(to: obj.object)) {
  259. return
  260. }
  261. self.delegate?.km_controlTextDidEndEditing(textField: self)
  262. }
  263. }