// // UnderlineTextField.swift // PrivadoVPN // // Created by Juraldinio on 1/21/21. // Copyright © 2021 Privado LLC. All rights reserved. // import Foundation import AppKit final class UnderlineTextField: NSTextField, NSTextFieldDelegate { 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 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 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) } private func performEditingKeyEquivalent(with event: NSEvent) -> Bool { guard event.type == NSEvent.EventType.keyDown else { return false } let commandKey = NSEvent.ModifierFlags.command.rawValue let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue let key = event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue switch key { case commandKey: return self.handleCommandKey(keyCode: event.keyCode) case commandShiftKey: return self.handleShiftKey(keyCode: event.keyCode) default: break } return false } private func handleCommandKey(keyCode: UInt16) -> Bool { switch keyCode { case KeyCode.x: if NSApp.sendAction(#selector(NSText.cut(_:)), to: nil, from: self) { return true } case KeyCode.c: if NSApp.sendAction(#selector(NSText.copy(_:)), to: nil, from: self) { return true } case KeyCode.v: if NSApp.sendAction(#selector(NSText.paste(_:)), to: nil, from: self) { return true } case KeyCode.z: if NSApp.sendAction(Selector(("undo:")), to: nil, from: self) { return true } case KeyCode.a: if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to: nil, from: self) { return true } default: break } return false } private func handleShiftKey(keyCode: UInt16) -> Bool { if keyCode == KeyCode.z { if NSApp.sendAction(Selector(("redo:")), to: nil, from: self) { return true } } return false } // 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 } } // Allow hotkeys in field override func performKeyEquivalent(with event: NSEvent) -> Bool { if self.performEditingKeyEquivalent(with: event) { return true } return super.performKeyEquivalent(with: event) } // 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) } }