// // KMCustomTextField.swift // PDF Reader Pro // // Created by tangchao on 2023/6/7. // // 自定义输入框 class KMCustomTextField: KMHoverView { private var _backgroundView = NSView() var backgroundView: NSView { get { return self._backgroundView } } private var _textFiled = KMTextField() var rightView: NSView? { willSet { self.rightView?.removeFromSuperview() } didSet { if let view = self.rightView { self.addSubview(view) self.layoutSubtreeIfNeeded() } self.updateRightViewStateIfNeed(editing: self.kmEnabled) } } var rightViewMode: KMSecureTextFiledViewMode = .whileEditing { didSet { self.updateRightViewStateIfNeed(editing: self.kmEnabled) } } var kmEnabled = true { didSet { self._textFiled.isEnabled = self.kmEnabled self.updateRightViewStateIfNeed(editing: self.kmEnabled) } } var placeholderString: String? { didSet { self._textFiled.placeholderString = self.placeholderString ?? "" } } var stringValue: String? { get { return self._textFiled.stringValue } set { self._textFiled.stringValue = newValue ?? "" } } var borderThickness: CGFloat = 1 { didSet { self._textFiled.borderThickness = self.borderThickness } } var offset: CGFloat = 8.0 { didSet { self._textFiled.offset = self.offset self.needsLayout = true } } weak var delegate: KMTextFieldDelegate? override init(frame frameRect: NSRect) { super.init(frame: frameRect) self.initSubViews() self.initDefaultValue() } required init?(coder: NSCoder) { super.init(coder: coder) } override func awakeFromNib() { super.awakeFromNib() self.initSubViews() self.initDefaultValue() } func initDefaultValue() { self._textFiled.isBezeled = false self._textFiled.focusRingType = .none self._textFiled.lineBreakMode = .byTruncatingTail self._textFiled.drawsBackground = false self._textFiled.delegate = self self._textFiled.firstResponderHandler = { [unowned self] result in self.updateRightViewStateIfNeed(editing: result) self.delegate?.km_didBecomeFirstResponder(textField: self) } } func initSubViews() { self.addSubview(self.backgroundView) self.addSubview(self._textFiled) } override func layout() { super.layout() let width: CGFloat = NSWidth(self.bounds) let height: CGFloat = NSHeight(self.bounds) self.backgroundView.frame = self.bounds var tfH: CGFloat = 22 if let font = self._textFiled.font { tfH = font.pointSize * 1.5 } // let tfX: CGFloat = 12 let tfX: CGFloat = self.offset if let rightView = self.rightView { let rightWidth = NSWidth(rightView.frame) rightView.frame = NSMakeRect(width-rightWidth, 0, rightWidth, height) let textFieldFrame = NSMakeRect(tfX, (height-tfH)*0.5, width-tfX*2-rightWidth-5, tfH) self._textFiled.frame = textFieldFrame } else { let textFieldFrame = NSMakeRect(tfX, (height-tfH)*0.5, width-tfX*2, tfH) self._textFiled.frame = textFieldFrame } } func clear() { self._textFiled.stringValue = "" self.delegate?.km_controlTextDidChange(textField: self) } override func becomeFirstResponder() -> Bool { self.window?.makeFirstResponder(self._textFiled) return super.becomeFirstResponder() } internal func updateRightViewStateIfNeed(editing: Bool) { guard let view = self.rightView else { return } if (self.rightViewMode == .always) { view.isHidden = false return } if (self.rightViewMode == .never) { view.isHidden = true return } if (editing) { // 开始编辑 view.isHidden = self.rightViewMode != .whileEditing } else { // 结束编辑 view.isHidden = self.rightViewMode == .whileEditing } } } extension KMCustomTextField: NSTextFieldDelegate { func control(_ control: NSControl, textShouldBeginEditing fieldEditor: NSText) -> Bool { if let should = self.delegate?.km_controlTextShouldBeginEditing(textField: self) { return should } return true } func controlTextDidBeginEditing(_ obj: Notification) { if (!self._textFiled.isEqual(to: obj.object)) { return } self.delegate?.km_controlTextDidBeginEditing(textField: self) } func controlTextDidChange(_ obj: Notification) { if (!self._textFiled.isEqual(to: obj.object)) { return } self.delegate?.km_controlTextDidChange(textField: self) } func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool { if let should = self.delegate?.km_controlTextShouldEndEditing(textField: self) { return should } return true } func controlTextDidEndEditing(_ obj: Notification) { if (!self._textFiled.isEqual(to: obj.object)) { return } self.updateRightViewStateIfNeed(editing: false) self.delegate?.km_controlTextDidEndEditing(textField: self) } func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { switch commandSelector { case #selector(NSResponder.insertNewline(_:)): if let inputView = control as? NSTextField { // //当当前TextField按下enter if inputView == self._textFiled { // guard let callBack = enterAction else { return false} // callBack() } } return true default: return false } } }