diff --git a/Xcodes/Frontend/SignIn/PinCodeTextView.swift b/Xcodes/Frontend/SignIn/PinCodeTextView.swift index f0b1c2a0..2df36110 100644 --- a/Xcodes/Frontend/SignIn/PinCodeTextView.swift +++ b/Xcodes/Frontend/SignIn/PinCodeTextView.swift @@ -9,29 +9,24 @@ struct PinCodeTextField: NSViewRepresentable { let complete: (String) -> Void func makeNSView(context: Context) -> NSViewType { - let view = PinCodeTextView(numberOfDigits: numberOfDigits, itemSpacing: 10) + let view = PinCodeTextView(numberOfDigits: numberOfDigits) view.codeDidChange = { c in code = c } view.codeDidComplete = { complete($0) } return view } func updateNSView(_ nsView: NSViewType, context: Context) { - nsView.code = (0.. Void)? = nil private let numberOfDigits: Int - private let stackView: NSStackView = .init(frame: .zero) - private var characterViews: [PinCodeCharacterTextField] = [] - + private let textField: NSTextField + // MARK: - Initializers - init( - numberOfDigits: Int, - itemSpacing: CGFloat - ) { + init(numberOfDigits: Int) { self.numberOfDigits = numberOfDigits + self.textField = NSTextField(frame: .zero) + super.init(frame: .zero) - - stackView.translatesAutoresizingMaskIntoConstraints = false - stackView.spacing = itemSpacing - stackView.orientation = .horizontal - stackView.distribution = .fillEqually - stackView.alignment = .centerY - addSubview(stackView) - NSLayoutConstraint.activate([ - stackView.topAnchor.constraint(equalTo: self.topAnchor), - stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor), - stackView.leadingAnchor.constraint(greaterThanOrEqualTo: self.leadingAnchor), - stackView.trailingAnchor.constraint(greaterThanOrEqualTo: self.trailingAnchor), - stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor), - ]) - self.code = (0.. Bool { - if commandSelector == #selector(deleteBackward(_:)) { - // If empty, move to previous or first character view - if textView.string.isEmpty { - if let lastFieldIndexWithCharacter = code.lastIndex(where: { $0 != nil }) { - window?.makeFirstResponder(characterViews[lastFieldIndexWithCharacter]) - } else { - window?.makeFirstResponder(characterViews[0]) - } - - return true - } - } - - // Perform default behaviour - return false + private func setupLayout() { + NSLayoutConstraint.activate([ + textField.topAnchor.constraint(equalTo: topAnchor), + textField.bottomAnchor.constraint(equalTo: bottomAnchor), + textField.leadingAnchor.constraint(equalTo: leadingAnchor), + textField.trailingAnchor.constraint(equalTo: trailingAnchor), + textField.widthAnchor.constraint(greaterThanOrEqualToConstant: CGFloat(numberOfDigits * 30 + 40)), + textField.heightAnchor.constraint(greaterThanOrEqualToConstant: 50) + ]) } + // MARK: NSTextFieldDelegate + func controlTextDidChange(_ obj: Notification) { guard let field = obj.object as? NSTextField, - isEnabled, - let fieldIndex = characterViews.firstIndex(where: { $0 === field }) - else { return } + field === textField, + isEnabled + else { return } - let newFieldText = field.stringValue + let newText = field.stringValue - let lastCharacter: Character? - if newFieldText.isEmpty { - lastCharacter = nil - } else { - lastCharacter = newFieldText[newFieldText.index(before: newFieldText.endIndex)] - } - - code[fieldIndex] = lastCharacter + // Filter to only digits + let filteredText = String(newText.filter { $0.isNumber }.prefix(numberOfDigits)) - if lastCharacter != nil { - if fieldIndex >= characterViews.count - 1 { - resignFirstResponder() - } else { - window?.makeFirstResponder(characterViews[fieldIndex + 1]) - } - } else { - if let lastFieldIndexWithCharacter = code.lastIndex(where: { $0 != nil }) { - window?.makeFirstResponder(characterViews[lastFieldIndexWithCharacter]) - } else { - window?.makeFirstResponder(characterViews[0]) - } + if filteredText != newText { + field.stringValue = filteredText } + + code = filteredText } // MARK: NSResponder @@ -180,52 +151,6 @@ class PinCodeTextView: NSControl, NSTextFieldDelegate { } override func becomeFirstResponder() -> Bool { - characterViews.first?.becomeFirstResponder() ?? false - } -} - -// MARK: - PinCodeCharacterTextField - -class PinCodeCharacterTextField: NSTextField { - var character: Character? = nil { - didSet { - stringValue = character.map(String.init) ?? "" - } - } - private var lastSize: NSSize? - - init() { - super.init(frame: .zero) - - wantsLayer = true - alignment = .center - maximumNumberOfLines = 1 - font = .boldSystemFont(ofSize: 48) - - setContentHuggingPriority(.required, for: .vertical) - setContentHuggingPriority(.required, for: .horizontal) - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func textDidChange(_ notification: Notification) { - super.textDidChange(notification) - self.invalidateIntrinsicContentSize() - } - - // This is kinda cheating - // Assuming that 0 is the widest and tallest character in 0-9 - override var intrinsicContentSize: NSSize { - var size = NSAttributedString( - string: "0", - attributes: [ .font : self.font! ] - ) - .size() - // I guess the cell should probably be doing this sizing in order to take into account everything outside of simply the text's frame, but for some reason I can't find a way to do that which works... - size.width += 16 - size.height += 8 - return size + textField.becomeFirstResponder() } }