KMSecureTextFiled.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. //
  2. // KMSecureTextFiled.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by tangchao on 2022/11/28.
  6. //
  7. import Cocoa
  8. enum KMSecureTextFiledMode: Int {
  9. case ciphertext = 0 /// 密文
  10. case plaintext = 1 /// 明文
  11. }
  12. enum KMSecureTextFiledViewMode: Int {
  13. case never = 0
  14. case whileEditing
  15. case unlessEditing
  16. case always
  17. }
  18. typealias KMSecureTextFiledFirstResponderHandler = (Bool)->()
  19. private class KMSecureTextFiled_TextFiled: NSTextField {
  20. var firstResponderHandler: KMSecureTextFiledFirstResponderHandler?
  21. override func becomeFirstResponder() -> Bool {
  22. let result = super.becomeFirstResponder()
  23. guard let callback = firstResponderHandler else {
  24. return result
  25. }
  26. callback(result)
  27. return result
  28. }
  29. }
  30. private class KMSecureTextFiled_SecureTextFiled: NSSecureTextField {
  31. var firstResponderHandler: KMSecureTextFiledFirstResponderHandler?
  32. override func becomeFirstResponder() -> Bool {
  33. let result = super.becomeFirstResponder()
  34. guard let callback = firstResponderHandler else {
  35. return result
  36. }
  37. callback(result)
  38. return result
  39. }
  40. }
  41. typealias KMSecureTextFiledValueDidChange = (_ view: KMSecureTextFiled, _ string: String) -> Void
  42. typealias KMSecureTextFiledBecomeFirstResponderHandler = (NSView)->()
  43. typealias KMSecureTextFiledEnterAction = () -> ()
  44. class KMSecureTextFiled: NSView {
  45. var enterAction: KMSecureTextFiledEnterAction?
  46. var backgroundView = NSView()
  47. private var textFiled = KMSecureTextFiled_TextFiled()
  48. private var secureTextField = KMSecureTextFiled_SecureTextFiled()
  49. var mode: KMSecureTextFiledMode = .ciphertext
  50. private var _rightView: NSView?
  51. var rightView: NSView? {
  52. get {
  53. return self._rightView
  54. }
  55. set {
  56. if self._rightView != newValue {
  57. if let view = self._rightView {
  58. view.removeFromSuperview()
  59. }
  60. self._rightView = newValue
  61. }
  62. if let view = self._rightView {
  63. self.addSubview(view)
  64. self.layoutSubtreeIfNeeded()
  65. }
  66. self.updateRightViewStateIfNeed(editing: self.kmEnabled)
  67. }
  68. }
  69. private var _rightViewMode = KMSecureTextFiledViewMode.whileEditing
  70. var rightViewMode: KMSecureTextFiledViewMode {
  71. get {
  72. return self._rightViewMode
  73. }
  74. set {
  75. self._rightViewMode = newValue
  76. self.updateRightViewStateIfNeed(editing: self.kmEnabled)
  77. }
  78. }
  79. private var _enabled = true
  80. var kmEnabled: Bool {
  81. get {
  82. return self._enabled
  83. }
  84. set {
  85. self._enabled = newValue
  86. textFiled.isEnabled = newValue
  87. secureTextField.isEnabled = newValue
  88. self.updateRightViewStateIfNeed(editing: newValue)
  89. }
  90. }
  91. var placeholderString: String {
  92. get {
  93. if (self.mode == .plaintext) {
  94. return self.textFiled.placeholderString ?? ""
  95. } else {
  96. return self.secureTextField.placeholderString ?? ""
  97. }
  98. }
  99. set {
  100. if (self.mode == .plaintext) {
  101. self.textFiled.placeholderString = newValue
  102. } else {
  103. self.secureTextField.placeholderString = newValue
  104. }
  105. }
  106. }
  107. var valueDidChange: KMSecureTextFiledValueDidChange?
  108. var becomeFirstResponderHandler: KMSecureTextFiledBecomeFirstResponderHandler?
  109. var didEndEditHandler: ((String?)->())?
  110. override init(frame frameRect: NSRect) {
  111. super.init(frame: frameRect)
  112. initDefaultValue()
  113. initSubViews()
  114. }
  115. required init?(coder: NSCoder) {
  116. super.init(coder: coder)
  117. }
  118. override func awakeFromNib() {
  119. super.awakeFromNib()
  120. initDefaultValue()
  121. initSubViews()
  122. }
  123. func initDefaultValue() {
  124. mode = .ciphertext
  125. self.secureTextField.isHidden = false
  126. self.secureTextField.isBezeled = false
  127. self.secureTextField.focusRingType = .none
  128. self.secureTextField.delegate = self
  129. self.textFiled.isHidden = true
  130. self.textFiled.isBezeled = false
  131. self.textFiled.focusRingType = .none
  132. self.textFiled.delegate = self
  133. let cell = self.textFiled.cell as? NSTextFieldCell
  134. cell?.allowedInputSourceLocales = [NSAllRomanInputSourcesLocaleIdentifier]
  135. self.textFiled.firstResponderHandler = { [unowned self] result in
  136. self.updateRightViewStateIfNeed(editing: result)
  137. guard let callback = self.becomeFirstResponderHandler else {
  138. return
  139. }
  140. callback(self)
  141. }
  142. self.secureTextField.firstResponderHandler = { [unowned self] result in
  143. self.updateRightViewStateIfNeed(editing: result)
  144. guard let callback = self.becomeFirstResponderHandler else {
  145. return
  146. }
  147. callback(self)
  148. }
  149. }
  150. func initSubViews() {
  151. self.addSubview(self.backgroundView)
  152. self.addSubview(self.secureTextField)
  153. self.addSubview(self.textFiled)
  154. }
  155. override func layout() {
  156. super.layout()
  157. let width: CGFloat = NSWidth(self.bounds)
  158. let height: CGFloat = NSHeight(self.bounds)
  159. self.backgroundView.frame = self.bounds
  160. var textFieldHeight: CGFloat = 22
  161. if let font = self.textFiled.font {
  162. textFieldHeight = font.pointSize * 1.5
  163. }
  164. let textFieldX: CGFloat = 12
  165. if let rightView = self.rightView {
  166. let rightWidth = NSWidth(rightView.frame)
  167. rightView.frame = NSMakeRect(width-rightWidth, 0, rightWidth, height)
  168. let textFieldFrame = NSMakeRect(textFieldX, (height-textFieldHeight)*0.5, width-textFieldX*2-rightWidth-5, textFieldHeight)
  169. self.secureTextField.frame = textFieldFrame
  170. self.textFiled.frame = textFieldFrame
  171. } else {
  172. let textFieldFrame = NSMakeRect(textFieldX, (height-textFieldHeight)*0.5, width-textFieldX*2, textFieldHeight)
  173. self.secureTextField.frame = textFieldFrame
  174. self.textFiled.frame = textFieldFrame
  175. }
  176. }
  177. func switchMode(mode: KMSecureTextFiledMode) {
  178. self.mode = mode
  179. if (mode == .ciphertext) { /// 密文
  180. self.secureTextField.isHidden = false
  181. self.secureTextField.stringValue = self.textFiled.stringValue
  182. self.window?.makeFirstResponder(self.secureTextField)
  183. self.textFiled.isHidden = true
  184. } else { /// 明文
  185. self.textFiled.isHidden = false
  186. self.textFiled.stringValue = self.secureTextField.stringValue
  187. self.window?.makeFirstResponder(self.textFiled)
  188. self.secureTextField.isHidden = true
  189. }
  190. }
  191. func clear() {
  192. self.secureTextField.stringValue = ""
  193. self.textFiled.stringValue = ""
  194. guard let callback = valueDidChange else {
  195. return
  196. }
  197. callback(self, "")
  198. }
  199. func password() -> String {
  200. return self.mode == .ciphertext ? self.secureTextField.stringValue : self.textFiled.stringValue
  201. }
  202. override func becomeFirstResponder() -> Bool {
  203. self.window?.makeFirstResponder(self.mode == .ciphertext ? self.secureTextField : self.textFiled)
  204. return super.becomeFirstResponder()
  205. }
  206. internal func updateRightViewStateIfNeed(editing: Bool) {
  207. guard let view = self.rightView else {
  208. return
  209. }
  210. if (self.rightViewMode == .always) {
  211. view.isHidden = false
  212. return
  213. }
  214. if (self.rightViewMode == .never) {
  215. view.isHidden = true
  216. return
  217. }
  218. if (editing) { // 开始编辑
  219. view.isHidden = self.rightViewMode != .whileEditing
  220. } else { // 结束编辑
  221. view.isHidden = self.rightViewMode == .whileEditing
  222. }
  223. }
  224. }
  225. extension KMSecureTextFiled: NSTextFieldDelegate {
  226. func controlTextDidChange(_ obj: Notification) {
  227. if (self.mode == .ciphertext) {
  228. if (self.secureTextField.isEqual(to: obj.object)) {
  229. guard let callback = valueDidChange else {
  230. return
  231. }
  232. callback(self, self.secureTextField.stringValue)
  233. }
  234. } else {
  235. if (self.textFiled.isEqual(to: obj.object)) {
  236. guard let callback = valueDidChange else {
  237. return
  238. }
  239. callback(self, self.secureTextField.stringValue)
  240. }
  241. }
  242. }
  243. func controlTextDidEndEditing(_ obj: Notification) {
  244. if (!self.secureTextField.isEqual(to: obj.object) && !self.textFiled.isEqual(to: obj.object)) {
  245. return
  246. }
  247. self.updateRightViewStateIfNeed(editing: false)
  248. guard let callback = self.didEndEditHandler else {
  249. return
  250. }
  251. callback(self.password())
  252. }
  253. func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
  254. switch commandSelector {
  255. case #selector(NSResponder.insertNewline(_:)):
  256. if let inputView = control as? NSTextField {
  257. // //当当前TextField按下enter
  258. if inputView == self.textFiled ||
  259. inputView == self.secureTextField {
  260. guard let callBack = enterAction else { return false}
  261. callBack()
  262. }
  263. }
  264. return true
  265. default:
  266. return false
  267. }
  268. }
  269. }