KMCustomStepperView.swift 9.5 KB

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