Files

295 lines
11 KiB
Swift

//
// MenuController.swift
// PrivadoVPN
//
// Created by Murad Shabanov on 21.06.2021.
// Copyright © 2021 Privado LLC. All rights reserved.
//
import Foundation
protocol MenuControllerInput: AnyObject {
// func changeTitle(for index: Int)
func reloadData()
}
protocol MenuControllerOutput: AnyObject {
func menuControllerClose(_ controller: MenuControllerInput)
func menuControllerItem(_ controller: MenuControllerInput, at index: Int) -> MenuItem?
func menuControllerItem(_ controller: MenuControllerInput, selected tag: Int)
func menuControllerItemsCount(_ controller: MenuControllerInput) -> Int
func menuControllerQuit(_ controller: MenuControllerInput)
}
final class MenuController: NSViewController, MenuControllerInput, NSTableViewDelegate, NSTableViewDataSource, AdvancedTextFieldDelegate {
// swiftlint:disable nesting
private enum Constants {
static let columnIdentifier = NSUserInterfaceItemIdentifier("MenuColumn")
static let cellIdentifier = NSUserInterfaceItemIdentifier("MenuCell")
enum Geometry {
enum Close {
static let width: CGFloat = 15
static let height: CGFloat = 15
static let top: CGFloat = 15
static let trailing: CGFloat = 15
}
enum Main {
static let leading: CGFloat = 100
static let bottom: CGFloat = 200
}
enum TableView {
static let top: CGFloat = 10
static let bottom: CGFloat = 40
}
enum Bottom {
enum Quit {
static let leading: CGFloat = 25
}
enum Text {
static let centerY: CGFloat = 3.0
static let leading: CGFloat = 15
}
}
enum Table {
static let columnWidth: CGFloat = 200
static let rowHeight: CGFloat = 30
}
}
enum Image {
static let close = "closeMenu"
static let quit = "quit"
}
enum Color {
static let topBackground = NSColor(red: 32, green: 40, blue: 100)
static let bottomBackground = NSColor(red: 3, green: 15, blue: 75)
static let background = NSColor(red: 0, green: 0, blue: 0, alpha: 0.53)
static let bottomText = NSColor(red: 122 / 255, green: 134 / 255, blue: 190 / 255, alpha: 1)
static let bottomTextHover = NSColor.white
}
enum Font {
enum Size {
static let quitText: CGFloat = 12
}
}
enum Text {
static let quitString = "Quit PrivadoVPN"
}
}
// swiftlint:enable nesting
private var output: MenuControllerOutput?
private let tableView = NSTableView()
private let closeButton = MenuController.createButton(image: Constants.Image.close)
private let quitButton = MenuController.createButton(image: Constants.Image.quit)
private let mainView = NSView(frame: .zero)
private let bottomView = BottomView(backgroundColor: Constants.Color.bottomBackground)
private let bottomText = AdvancedTextField(frame: .zero)
// MARK: - Init
init(output: MenuControllerOutput) {
self.output = output
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - LifeCycle
override func loadView() {
self.view = BackgroundView(with: Constants.Color.background)
}
override func viewDidLoad() {
super.viewDidLoad()
self.congigureUI()
self.bind()
self.layoutUI()
}
override func viewDidAppear() {
super.viewDidAppear()
if let superview = self.view.superview {
NSLayoutConstraint.activate([
self.view.topAnchor.constraint(equalTo: superview.topAnchor),
self.view.leadingAnchor.constraint(equalTo: superview.leadingAnchor),
self.view.trailingAnchor.constraint(equalTo: superview.trailingAnchor),
self.view.bottomAnchor.constraint(equalTo: superview.bottomAnchor)
])
}
}
private func layoutUI() {
NSLayoutConstraint.activate([
self.mainView.topAnchor.constraint(equalTo: self.view.topAnchor),
self.mainView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -Constants.Geometry.Main.bottom),
self.mainView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: Constants.Geometry.Main.leading),
self.mainView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
//
self.closeButton.topAnchor.constraint(equalTo: self.mainView.topAnchor, constant: Constants.Geometry.Close.top),
self.closeButton.trailingAnchor.constraint(equalTo: self.mainView.trailingAnchor, constant: -Constants.Geometry.Close.trailing),
self.closeButton.widthAnchor.constraint(equalToConstant: Constants.Geometry.Close.width),
self.closeButton.heightAnchor.constraint(equalToConstant: Constants.Geometry.Close.height),
//
self.tableView.topAnchor.constraint(equalTo: self.closeButton.bottomAnchor, constant: Constants.Geometry.TableView.top),
self.tableView.leadingAnchor.constraint(equalTo: self.mainView.leadingAnchor),
self.tableView.trailingAnchor.constraint(equalTo: self.mainView.trailingAnchor),
self.tableView.bottomAnchor.constraint(equalTo: self.mainView.bottomAnchor, constant: -Constants.Geometry.TableView.bottom),
//
self.bottomView.topAnchor.constraint(equalTo: self.tableView.bottomAnchor),
self.bottomView.leadingAnchor.constraint(equalTo: self.mainView.leadingAnchor),
self.bottomView.trailingAnchor.constraint(equalTo: self.mainView.trailingAnchor),
self.bottomView.bottomAnchor.constraint(equalTo: self.mainView.bottomAnchor),
//
self.quitButton.centerYAnchor.constraint(equalTo: self.bottomView.centerYAnchor),
self.quitButton.leadingAnchor.constraint(equalTo: self.bottomView.leadingAnchor, constant: Constants.Geometry.Bottom.Quit.leading),
self.quitButton.widthAnchor.constraint(equalTo: self.quitButton.heightAnchor, multiplier: 1.0),
//
self.bottomText.centerYAnchor.constraint(equalTo: self.bottomView.centerYAnchor, constant: Constants.Geometry.Bottom.Text.centerY),
self.bottomText.leadingAnchor.constraint(equalTo: self.quitButton.trailingAnchor, constant: Constants.Geometry.Bottom.Text.leading)
])
}
private func congigureUI() {
self.mainView.translatesAutoresizingMaskIntoConstraints = false
self.mainView.wantsLayer = true
self.mainView.layer?.cornerRadius = 5
self.mainView.viewBackgroundColor = Constants.Color.topBackground
let shadow = NSShadow()
shadow.shadowColor = .black
shadow.shadowBlurRadius = 6
shadow.shadowOffset = .zero
self.mainView.shadow = shadow
self.bottomText.stringValue = Constants.Text.quitString
self.bottomText.textColor = Constants.Color.bottomText
self.bottomText.font = NSFont(name: PrivadoConstants.Font.medium, size: Constants.Font.Size.quitText)
self.bottomText.useHandCursor = true
self.bottomText.advancedDelegate = self
self.tableView.translatesAutoresizingMaskIntoConstraints = false
self.tableView.backgroundColor = .clear
self.tableView.columnAutoresizingStyle = .uniformColumnAutoresizingStyle
self.tableView.allowsColumnResizing = true
self.tableView.selectionHighlightStyle = .none
self.tableView.focusRingType = .none
if #available(OSX 11.0, *) {
self.tableView.style = .fullWidth
}
self.tableView.cornerView = NSView()
}
private func bind() {
self.view.addSubview(self.mainView)
self.quitButton.target = self
self.quitButton.action = #selector(self.onQuitAction)
self.bottomView.translatesAutoresizingMaskIntoConstraints = false
self.bottomView.addSubview(self.quitButton)
self.bottomView.addSubview(self.bottomText)
self.closeButton.target = self
self.closeButton.action = #selector(self.onCloseAction)
self.tableView.delegate = self
self.tableView.dataSource = self
let col = NSTableColumn(identifier: Constants.columnIdentifier)
col.isEditable = false
self.tableView.addTableColumn(col)
self.mainView.addSubview(self.bottomView)
self.mainView.addSubview(self.tableView)
self.mainView.addSubview(self.closeButton)
}
func reloadData() {
self.tableView.reloadData()
}
@objc
private func onCloseAction() {
self.output?.menuControllerClose(self)
}
@objc
private func onQuitAction() {
self.output?.menuControllerQuit(self)
}
// MARK: - NSTableViewDelegate
func tableView(_ tableView: NSTableView, sizeToFitWidthOfColumn column: Int) -> CGFloat {
Constants.Geometry.Table.columnWidth
}
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
Constants.Geometry.Table.rowHeight
}
func tableViewSelectionDidChange(_ notification: Notification) {
guard let output = self.output else { return }
let row = self.tableView.selectedRow
output.menuControllerClose(self)
output.menuControllerItem(self, selected: row)
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
tableView.makeView(withIdentifier: Constants.cellIdentifier, owner: nil)
guard let item = self.output?.menuControllerItem(self, at: row) else { return nil }
let cell = MenuTableViewCell()
cell.updateText(text: item.title)
return cell
}
// MARK: - NSTableViewDataSource
func numberOfRows(in tableView: NSTableView) -> Int {
return self.output?.menuControllerItemsCount(self) ?? 0
}
// MARK: - AdvancedTextFieldDelegate
func advancedTextFieldClick(on target: AdvancedTextField) {
self.onQuitAction()
}
func advancedTextFieldHover(on target: AdvancedTextField, isHover: Bool) {
target.textColor = isHover ? Constants.Color.bottomTextHover : Constants.Color.bottomText
}
}