// // KilSwitchPanelView.swift // PrivadoVPN // // Created by Juraldinio on 1/31/21. // Copyright © 2021 Privado LLC. All rights reserved. // import Foundation import AppKit protocol KilSwitchPanelViewInput: AnyObject { func show(enabled: Bool) func killSwitch(enabled: Bool) } protocol KilSwitchPanelViewOutput: AnyObject { func viewIsReady(view: KilSwitchPanelViewInput) func viewWillDisappear() // func killSwitch(in view: KilSwitchPanelViewInput, enabled: Bool) func requirePopoverMessage() -> (String, String) } final class KilSwitchPanelView: NSView { private enum Constants { enum LocalizedString { static let killSwitchText = "connection.killswitch.text" } enum Image { static let info = "info" } enum Color { static let title = NSColor.white static let fill = NSColor(red: 122, green: 134, blue: 190) static let background = NSColor(red: 21, green: 25, blue: 53) } enum Font { // swiftlint:disable nesting enum Name { static let medium = PrivadoConstants.Font.semiBold } enum Size { static let title: CGFloat = 14.0 } // swiftlint:enable nesting } enum Geometry { static let height: CGFloat = 45 static let titleLeading: CGFloat = 30 static let titleHeight: CGFloat = 19 static let titleWidth: CGFloat = 90 static let titleTrailingInfo: CGFloat = 3 static let infoImageWidth: CGFloat = 18 static let infoImageHeight: CGFloat = 18 static let switchWidth: CGFloat = 56 static let switchHeight: CGFloat = 26 static let switchTrailing: CGFloat = 32 } } private let output: KilSwitchPanelViewOutput private var titleField: NSTextField? private var infoView: ImageView? private var tooltip: NSPopover? private var switchToggle: Switch? // MARK: - Init init(output: KilSwitchPanelViewOutput) { self.output = output super.init(frame: .zero) self.translatesAutoresizingMaskIntoConstraints = false self.isHidden = true self.createUI() self.activateLayout() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - Lyfecycle override func viewDidMoveToSuperview() { super.viewDidMoveToSuperview() if self.superview.isExist { self.output.viewIsReady(view: self) } } override func removeFromSuperview() { self.output.viewWillDisappear() super.removeFromSuperview() } // MARK: - Private private func createUI() { let titleFont = NSFont(name: PrivadoConstants.Font.bold, size: Constants.Font.Size.title) let titleField = self.initializeTextField(color: Constants.Color.title, font: titleFont) titleField.stringValue = NSLocalizedString(Constants.LocalizedString.killSwitchText, comment: "") // self.titleField = titleField self.addSubview(titleField) let infoView = self.initializeImageView(resource: Constants.Image.info) self.infoView = infoView if #available(OSX 10.14, *) { infoView.mouseOverTracker = { [weak self] _, entered in guard let self = self , let infoView = self.infoView else { return } if entered { guard !self.tooltip.isExist else { return } let messages = self.output.requirePopoverMessage() let tooltip = self.createPopover(on: infoView, title: messages.0, description: messages.1) self.tooltip = tooltip } else { guard let tooltip = self.tooltip else { return } tooltip.performClose(nil) self.tooltip = nil } } } self.addSubview(infoView) let switchToggle = self.initializeSwitchToggle(height: Constants.Geometry.switchHeight, fillColor: Constants.Color.fill, backColor: Constants.Color.background) switchToggle.target = self switchToggle.action = #selector(self.switchAction) // switchToggle.callback = { [weak self] isOn in // guard let self = self else { return } // self.output.killSwitch(in: self, enabled: isOn) // } self.switchToggle = switchToggle self.addSubview(switchToggle) } @objc private func switchAction() { guard let switchToggle = self.switchToggle else { return } self.output.killSwitch(in: self, enabled: switchToggle.isOn) } private func activateLayout() { guard let titleField = self.titleField , let infoView = self.infoView , let switchToggle = self.switchToggle else { return } NSLayoutConstraint.activate([ self.heightAnchor.constraint(equalToConstant: Constants.Geometry.height), // titleField.centerYAnchor.constraint(equalTo: self.centerYAnchor), titleField.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: Constants.Geometry.titleLeading), titleField.widthAnchor.constraint(equalToConstant: Constants.Geometry.titleWidth), titleField.heightAnchor.constraint(equalToConstant: Constants.Geometry.titleHeight), // infoView.centerYAnchor.constraint(equalTo: self.centerYAnchor), infoView.leadingAnchor.constraint(equalTo: titleField.trailingAnchor, constant: Constants.Geometry.titleTrailingInfo), infoView.widthAnchor.constraint(equalToConstant: Constants.Geometry.infoImageWidth), infoView.heightAnchor.constraint(equalToConstant: Constants.Geometry.infoImageHeight), // switchToggle.centerYAnchor.constraint(equalTo: self.centerYAnchor), switchToggle.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -Constants.Geometry.switchTrailing), switchToggle.widthAnchor.constraint(equalToConstant: Constants.Geometry.switchWidth), switchToggle.heightAnchor.constraint(equalToConstant: Constants.Geometry.switchHeight) ]) } private func createPopover(on view: NSView, title: String, description: String) -> NSPopover { let popover = NSPopover() popover.behavior = .semitransient popover.appearance = NSAppearance(named: NSAppearance.Name.vibrantDark) popover.contentViewController = KillSwitchHelpViewController(title: title, description: description) popover.show(relativeTo: .zero, of: view, preferredEdge: .minY) // popover.delegate = self return popover } } // MARK: - KilSwitchPanelViewInput extension KilSwitchPanelView: KilSwitchPanelViewInput { func show(enabled: Bool) { self.isHidden = !enabled } func killSwitch(enabled: Bool) { self.switchToggle?.isOn = enabled } }