140 lines
4.2 KiB
Swift
140 lines
4.2 KiB
Swift
//
|
|
// UnderlineSecuredTextField.swift
|
|
// PrivadoVPN
|
|
//
|
|
// Created by Juraldinio on 1/21/21.
|
|
// Copyright © 2021 Privado LLC. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import AppKit
|
|
|
|
final class UnderlineSecuredTextField: DSFSecureTextField, NSTextFieldDelegate {
|
|
|
|
typealias EnterHandlerClosure = () -> Bool
|
|
|
|
enum Constants {
|
|
|
|
static let lineHeight: CGFloat = 2.0
|
|
|
|
enum Font {
|
|
static let name = PrivadoConstants.Font.regular
|
|
static let size: CGFloat = 14.0
|
|
}
|
|
|
|
enum Color {
|
|
static let text = NSColor.white
|
|
static let placeholder = NSColor(calibratedRed: 173.0 / 255, green: 179.0 / 255, blue: 210.0 / 255, alpha: 1.0)
|
|
static let neutral = NSColor(calibratedRed: 45.0 / 255, green: 51.0 / 255, blue: 82.0 / 255, alpha: 1.0)
|
|
static let beginEditing = NSColor(red: 40, green: 215, blue: 153)
|
|
}
|
|
}
|
|
|
|
// MARK: - Properties
|
|
|
|
var enterHandler: EnterHandlerClosure?
|
|
|
|
private let bottomLineView: NSView
|
|
|
|
var coloredPlaceHolder: String? {
|
|
get {
|
|
return self.placeholderAttributedString?.string
|
|
}
|
|
set {
|
|
if let value = newValue {
|
|
let attrs = [NSAttributedString.Key.foregroundColor: Constants.Color.placeholder,
|
|
NSAttributedString.Key.font: NSFont(name: Constants.Font.name, size: Constants.Font.size)]
|
|
self.placeholderAttributedString = NSAttributedString(string: value, attributes: attrs)
|
|
} else {
|
|
self.placeholderAttributedString = nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func hightlight(with color: NSColor?) {
|
|
self.bottomLineView.viewBackgroundColor = color ?? Constants.Color.neutral
|
|
}
|
|
|
|
// MARK: - Initializers
|
|
|
|
override init(frame frameRect: NSRect) {
|
|
|
|
self.bottomLineView = NSView()
|
|
|
|
super.init(frame: frameRect)
|
|
self.cell?.isScrollable = true
|
|
self.setupUI()
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
private func setupUI() {
|
|
|
|
self.isBezeled = false
|
|
self.drawsBackground = false
|
|
self.translatesAutoresizingMaskIntoConstraints = false
|
|
self.focusRingType = .none
|
|
self.usesSingleLineMode = true
|
|
self.textColor = Constants.Color.text
|
|
if #available(macOS 11.0, *) {
|
|
self.contentType = .oneTimeCode
|
|
}
|
|
|
|
self.delegate = self
|
|
self.font = NSFont(name: Constants.Font.name, size: Constants.Font.size)
|
|
|
|
self.bottomLineView.translatesAutoresizingMaskIntoConstraints = false
|
|
self.addSubview(self.bottomLineView)
|
|
|
|
NSLayoutConstraint.activate([
|
|
self.bottomLineView.heightAnchor.constraint(equalToConstant: Constants.lineHeight),
|
|
self.bottomLineView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
|
|
self.bottomLineView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
|
|
self.bottomLineView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
|
|
])
|
|
|
|
self.hightlight(with: nil)
|
|
|
|
}
|
|
|
|
// MARK: - Lifecycle
|
|
|
|
// Show edit pointer
|
|
override func viewDidMoveToWindow() {
|
|
if let textView = self.window?.fieldEditor(true, for: self) as? NSTextView {
|
|
textView.insertionPointColor = Constants.Color.text
|
|
textView.alignment = .center
|
|
}
|
|
}
|
|
|
|
// MARK: - NSTextFieldDelegate
|
|
|
|
func controlTextDidChange(_ obj: Notification) {
|
|
// self.hightlight(with: nil)
|
|
}
|
|
|
|
func controlTextDidBeginEditing(_ obj: Notification) {
|
|
self.hightlight(with: Constants.Color.beginEditing)
|
|
}
|
|
|
|
func controlTextDidEndEditing(_ obj: Notification) {
|
|
self.hightlight(with: nil)
|
|
}
|
|
|
|
func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
|
|
|
|
if commandSelector == #selector(NSResponder.insertNewline(_:)) {
|
|
|
|
guard let handler = self.enterHandler else { return true }
|
|
return handler()
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
}
|