diff options
Diffstat (limited to 'Sources/WireGuardApp/UI/macOS/ViewController/ManageTunnelsRootViewController.swift')
-rw-r--r-- | Sources/WireGuardApp/UI/macOS/ViewController/ManageTunnelsRootViewController.swift | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/Sources/WireGuardApp/UI/macOS/ViewController/ManageTunnelsRootViewController.swift b/Sources/WireGuardApp/UI/macOS/ViewController/ManageTunnelsRootViewController.swift new file mode 100644 index 0000000..88eb8be --- /dev/null +++ b/Sources/WireGuardApp/UI/macOS/ViewController/ManageTunnelsRootViewController.swift @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2018-2023 WireGuard LLC. All Rights Reserved. + +import Cocoa + +class ManageTunnelsRootViewController: NSViewController { + + let tunnelsManager: TunnelsManager + var tunnelsListVC: TunnelsListTableViewController? + var tunnelDetailVC: TunnelDetailTableViewController? + let tunnelDetailContainerView = NSView() + var tunnelDetailContentVC: NSViewController? + + init(tunnelsManager: TunnelsManager) { + self.tunnelsManager = tunnelsManager + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + view = NSView() + + let horizontalSpacing: CGFloat = 20 + let verticalSpacing: CGFloat = 20 + let centralSpacing: CGFloat = 10 + + let container = NSLayoutGuide() + view.addLayoutGuide(container) + NSLayoutConstraint.activate([ + container.topAnchor.constraint(equalTo: view.topAnchor, constant: verticalSpacing), + view.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: verticalSpacing), + container.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: horizontalSpacing), + view.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: horizontalSpacing) + ]) + + tunnelsListVC = TunnelsListTableViewController(tunnelsManager: tunnelsManager) + tunnelsListVC!.delegate = self + let tunnelsListView = tunnelsListVC!.view + + addChild(tunnelsListVC!) + view.addSubview(tunnelsListView) + view.addSubview(tunnelDetailContainerView) + + tunnelsListView.translatesAutoresizingMaskIntoConstraints = false + tunnelDetailContainerView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + tunnelsListView.topAnchor.constraint(equalTo: container.topAnchor), + tunnelsListView.bottomAnchor.constraint(equalTo: container.bottomAnchor), + tunnelsListView.leadingAnchor.constraint(equalTo: container.leadingAnchor), + tunnelDetailContainerView.topAnchor.constraint(equalTo: container.topAnchor), + tunnelDetailContainerView.bottomAnchor.constraint(equalTo: container.bottomAnchor), + tunnelDetailContainerView.leadingAnchor.constraint(equalTo: tunnelsListView.trailingAnchor, constant: centralSpacing), + tunnelDetailContainerView.trailingAnchor.constraint(equalTo: container.trailingAnchor) + ]) + } + + private func setTunnelDetailContentVC(_ contentVC: NSViewController) { + if let currentContentVC = tunnelDetailContentVC { + currentContentVC.view.removeFromSuperview() + currentContentVC.removeFromParent() + } + addChild(contentVC) + tunnelDetailContainerView.addSubview(contentVC.view) + contentVC.view.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + tunnelDetailContainerView.topAnchor.constraint(equalTo: contentVC.view.topAnchor), + tunnelDetailContainerView.bottomAnchor.constraint(equalTo: contentVC.view.bottomAnchor), + tunnelDetailContainerView.leadingAnchor.constraint(equalTo: contentVC.view.leadingAnchor), + tunnelDetailContainerView.trailingAnchor.constraint(equalTo: contentVC.view.trailingAnchor) + ]) + tunnelDetailContentVC = contentVC + } +} + +extension ManageTunnelsRootViewController: TunnelsListTableViewControllerDelegate { + func tunnelsSelected(tunnelIndices: [Int]) { + assert(!tunnelIndices.isEmpty) + if tunnelIndices.count == 1 { + let tunnel = tunnelsManager.tunnel(at: tunnelIndices.first!) + if tunnel.isTunnelAvailableToUser { + let tunnelDetailVC = TunnelDetailTableViewController(tunnelsManager: tunnelsManager, tunnel: tunnel) + setTunnelDetailContentVC(tunnelDetailVC) + self.tunnelDetailVC = tunnelDetailVC + } else { + let unusableTunnelDetailVC = tunnelDetailContentVC as? UnusableTunnelDetailViewController ?? UnusableTunnelDetailViewController() + unusableTunnelDetailVC.onButtonClicked = { [weak tunnelsListVC] in + tunnelsListVC?.handleRemoveTunnelAction() + } + setTunnelDetailContentVC(unusableTunnelDetailVC) + self.tunnelDetailVC = nil + } + } else if tunnelIndices.count > 1 { + let multiSelectionVC = tunnelDetailContentVC as? ButtonedDetailViewController ?? ButtonedDetailViewController() + multiSelectionVC.setButtonTitle(tr(format: "macButtonDeleteTunnels (%d)", tunnelIndices.count)) + multiSelectionVC.onButtonClicked = { [weak tunnelsListVC] in + tunnelsListVC?.handleRemoveTunnelAction() + } + setTunnelDetailContentVC(multiSelectionVC) + self.tunnelDetailVC = nil + } + } + + func tunnelsListEmpty() { + let noTunnelsVC = ButtonedDetailViewController() + noTunnelsVC.setButtonTitle(tr("macButtonImportTunnels")) + noTunnelsVC.onButtonClicked = { [weak self] in + guard let self = self else { return } + ImportPanelPresenter.presentImportPanel(tunnelsManager: self.tunnelsManager, sourceVC: self) + } + setTunnelDetailContentVC(noTunnelsVC) + self.tunnelDetailVC = nil + } +} + +extension ManageTunnelsRootViewController { + override func supplementalTarget(forAction action: Selector, sender: Any?) -> Any? { + switch action { + case #selector(TunnelsListTableViewController.handleViewLogAction), + #selector(TunnelsListTableViewController.handleAddEmptyTunnelAction), + #selector(TunnelsListTableViewController.handleImportTunnelAction), + #selector(TunnelsListTableViewController.handleExportTunnelsAction), + #selector(TunnelsListTableViewController.handleRemoveTunnelAction): + return tunnelsListVC + case #selector(TunnelDetailTableViewController.handleToggleActiveStatusAction), + #selector(TunnelDetailTableViewController.handleEditTunnelAction): + return tunnelDetailVC + default: + return super.supplementalTarget(forAction: action, sender: sender) + } + } +} |