]> git.ipfire.org Git - thirdparty/wireguard-apple.git/commitdiff
macOS: Support for on-demand activation
authorRoopesh Chander <roop@roopc.net>
Tue, 8 Jan 2019 22:44:08 +0000 (04:14 +0530)
committerRoopesh Chander <roop@roopc.net>
Mon, 14 Jan 2019 09:22:36 +0000 (14:52 +0530)
Signed-off-by: Roopesh Chander <roop@roopc.net>
WireGuard/WireGuard.xcodeproj/project.pbxproj
WireGuard/WireGuard/Base.lproj/Localizable.strings
WireGuard/WireGuard/UI/macOS/View/PopupRow.swift [new file with mode: 0644]
WireGuard/WireGuard/UI/macOS/ViewController/TunnelDetailTableViewController.swift
WireGuard/WireGuard/UI/macOS/ViewController/TunnelEditViewController.swift

index 0cc3579ac3048914a61ec3df9efd29b144b8a0dc..a5119b65164f14b40171eabe4b2423c2b885c234 100644 (file)
@@ -59,6 +59,7 @@
                6F919EDA218C65C50023B400 /* wireguard_doc_logo_44x58.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED6218C65C50023B400 /* wireguard_doc_logo_44x58.png */; };
                6F919EDB218C65C50023B400 /* wireguard_doc_logo_64x64.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED7218C65C50023B400 /* wireguard_doc_logo_64x64.png */; };
                6F919EDC218C65C50023B400 /* wireguard_doc_logo_320x320.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED8218C65C50023B400 /* wireguard_doc_logo_320x320.png */; };
+               6F9B582921E8D6D100544D02 /* PopupRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F9B582721E8CD4300544D02 /* PopupRow.swift */; };
                6FB1017921C57DE600766195 /* MockTunnels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB1017821C57DE600766195 /* MockTunnels.swift */; };
                6FB1BD6021D2607A00A991BF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB1BD5F21D2607A00A991BF /* AppDelegate.swift */; };
                6FB1BD6221D2607E00A991BF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FB1BD6121D2607E00A991BF /* Assets.xcassets */; };
                6F919ED6218C65C50023B400 /* wireguard_doc_logo_44x58.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_44x58.png; sourceTree = "<group>"; };
                6F919ED7218C65C50023B400 /* wireguard_doc_logo_64x64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_64x64.png; sourceTree = "<group>"; };
                6F919ED8218C65C50023B400 /* wireguard_doc_logo_320x320.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_320x320.png; sourceTree = "<group>"; };
+               6F9B582721E8CD4300544D02 /* PopupRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupRow.swift; sourceTree = "<group>"; };
                6FB1017821C57DE600766195 /* MockTunnels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTunnels.swift; sourceTree = "<group>"; };
                6FB1BD5D21D2607A00A991BF /* WireGuard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WireGuard.app; sourceTree = BUILT_PRODUCTS_DIR; };
                6FB1BD5F21D2607A00A991BF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
                                6F613D9A21DE33B8004B217A /* KeyValueRow.swift */,
                                5F52D0BA21E3781B00283CEA /* ConfTextView.swift */,
                                5F52D0BC21E3785C00283CEA /* ConfTextStorage.swift */,
+                               6F9B582721E8CD4300544D02 /* PopupRow.swift */,
                        );
                        path = View;
                        sourceTree = "<group>";
                                6FCD99B121E0EDA900BA4C82 /* TunnelEditViewController.swift in Sources */,
                                6FB1BDCA21D50F1700A991BF /* x25519.c in Sources */,
                                6FB1BDCB21D50F1700A991BF /* Curve25519.swift in Sources */,
+                               6F9B582921E8D6D100544D02 /* PopupRow.swift in Sources */,
                                6FB1BDBB21D50F0200A991BF /* Localizable.strings in Sources */,
                                6FB1BDBC21D50F0200A991BF /* ringlogger.c in Sources */,
                                6FB1BDBD21D50F0200A991BF /* ringlogger.h in Sources */,
index 369cfccd3e8066ec9854c46570e7b5ff3b11b66b..cf73bf30ceba6ed10c879ae989b856dbc2a192a5 100644 (file)
 // Mac detail/edit view fields
 
 "macFieldKey (%@)" = "%@:";
+"macFieldOnDemand" = "On-Demand:";
 
 // Mac status display
 
diff --git a/WireGuard/WireGuard/UI/macOS/View/PopupRow.swift b/WireGuard/WireGuard/UI/macOS/View/PopupRow.swift
new file mode 100644 (file)
index 0000000..2ef55f5
--- /dev/null
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018 WireGuard LLC. All Rights Reserved.
+
+import Cocoa
+
+class PopupRow: NSView {
+    let keyLabel: NSTextField = {
+        let keyLabel = NSTextField()
+        keyLabel.isEditable = false
+        keyLabel.isSelectable = false
+        keyLabel.isBordered = false
+        keyLabel.alignment = .right
+        keyLabel.maximumNumberOfLines = 1
+        keyLabel.lineBreakMode = .byTruncatingTail
+        keyLabel.backgroundColor = .clear
+        return keyLabel
+    }()
+
+    let valuePopup = NSPopUpButton()
+
+    var key: String {
+        get { return keyLabel.stringValue }
+        set(value) { keyLabel.stringValue = value }
+    }
+
+    var valueOptions: [String] {
+        get { return valuePopup.itemTitles }
+        set(value) {
+            valuePopup.removeAllItems()
+            valuePopup.addItems(withTitles: value)
+        }
+    }
+
+    var selectedOptionIndex: Int {
+        get { return valuePopup.indexOfSelectedItem }
+        set(value) { valuePopup.selectItem(at: value) }
+    }
+
+    override var intrinsicContentSize: NSSize {
+        let height = max(keyLabel.intrinsicContentSize.height, valuePopup.intrinsicContentSize.height)
+        return NSSize(width: NSView.noIntrinsicMetric, height: height)
+    }
+
+    init() {
+        super.init(frame: CGRect.zero)
+
+        addSubview(keyLabel)
+        addSubview(valuePopup)
+        keyLabel.translatesAutoresizingMaskIntoConstraints = false
+        valuePopup.translatesAutoresizingMaskIntoConstraints = false
+
+        NSLayoutConstraint.activate([
+            keyLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor),
+            keyLabel.firstBaselineAnchor.constraint(equalTo: valuePopup.firstBaselineAnchor),
+            self.leadingAnchor.constraint(equalTo: keyLabel.leadingAnchor),
+            keyLabel.trailingAnchor.constraint(equalTo: valuePopup.leadingAnchor, constant: -5)
+        ])
+
+        keyLabel.setContentCompressionResistancePriority(.defaultHigh + 2, for: .horizontal)
+        keyLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
+
+        let widthConstraint = keyLabel.widthAnchor.constraint(equalToConstant: 150)
+        widthConstraint.priority = .defaultHigh + 1
+        widthConstraint.isActive = true
+    }
+
+    required init?(coder decoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    override func prepareForReuse() {
+        key = ""
+        valueOptions = []
+    }
+}
index 3c070606f8a92c919f05bb46e6f0739aca41aa99..5f6c891bb0849e94a52d32bcb40d02bc681b7492 100644 (file)
@@ -8,12 +8,14 @@ class TunnelDetailTableViewController: NSViewController {
     private enum TableViewModelRow {
         case interfaceFieldRow(TunnelViewModel.InterfaceField)
         case peerFieldRow(peer: TunnelViewModel.PeerData, field: TunnelViewModel.PeerField)
+        case onDemandRow
         case spacerRow
 
         func localizedSectionKeyString() -> String {
             switch self {
             case .interfaceFieldRow: return tr("tunnelSectionTitleInterface")
             case .peerFieldRow: return tr("tunnelSectionTitlePeer")
+            case .onDemandRow: return ""
             case .spacerRow: return ""
             }
         }
@@ -22,6 +24,7 @@ class TunnelDetailTableViewController: NSViewController {
             switch self {
             case .interfaceFieldRow(let field): return field == .name
             case .peerFieldRow(_, let field): return field == .publicKey
+            case .onDemandRow: return true
             case .spacerRow: return false
             }
         }
@@ -163,6 +166,8 @@ class TunnelDetailTableViewController: NSViewController {
                 tableViewModelRows.append(.peerFieldRow(peer: peerData, field: field))
             }
         }
+        tableViewModelRows.append(.spacerRow)
+        tableViewModelRows.append(.onDemandRow)
     }
 
     func updateStatus() {
@@ -232,6 +237,12 @@ extension TunnelDetailTableViewController: NSTableViewDelegate {
             return cell
         case .spacerRow:
             return NSView()
+        case .onDemandRow:
+            let cell: KeyValueRow = tableView.dequeueReusableCell()
+            cell.key = tr("macFieldOnDemand")
+            cell.value = TunnelViewModel.activateOnDemandDetailText(for: tunnel.activateOnDemandSetting)
+            cell.isKeyInBold = true
+            return cell
         }
     }
 }
index ae95fba208cd318d40e0687e455e5bfd4bc83a3f..aecb3c91117f64413bea50d962e9970a73af4fb1 100644 (file)
@@ -41,6 +41,12 @@ class TunnelEditViewController: NSViewController {
         return textView
     }()
 
+    let onDemandRow: PopupRow = {
+        let popupRow = PopupRow()
+        popupRow.key = tr("macFieldOnDemand")
+        return popupRow
+    }()
+
     let scrollView: NSScrollView = {
         let scrollView = NSScrollView()
         scrollView.hasVerticalScroller = true
@@ -64,6 +70,13 @@ class TunnelEditViewController: NSViewController {
         return button
     }()
 
+    let activateOnDemandOptions: [ActivateOnDemandOption] = [
+        .none,
+        .useOnDemandOverWiFiOrEthernet,
+        .useOnDemandOverWiFiOnly,
+        .useOnDemandOverEthernetOnly
+    ]
+
     let tunnelsManager: TunnelsManager
     let tunnel: TunnelContainer?
 
@@ -82,6 +95,7 @@ class TunnelEditViewController: NSViewController {
     }
 
     func populateTextFields() {
+        let selectedActivateOnDemandOption: ActivateOnDemandOption
         if let tunnel = tunnel {
             // Editing an existing tunnel
             let tunnelConfiguration = tunnel.tunnelConfiguration!
@@ -99,6 +113,11 @@ class TunnelEditViewController: NSViewController {
                     publicKeyRow?.value = ""
                 }
             }
+            if tunnel.activateOnDemandSetting.isActivateOnDemandEnabled {
+                selectedActivateOnDemandOption = tunnel.activateOnDemandSetting.activateOnDemandOption
+            } else {
+                selectedActivateOnDemandOption = .none
+            }
         } else {
             // Creating a new tunnel
             let privateKey = Curve25519.generatePrivateKey()
@@ -109,7 +128,13 @@ class TunnelEditViewController: NSViewController {
             """
             publicKeyRow.value = publicKey.base64EncodedString()
             textView.string = bootstrappingText
+            selectedActivateOnDemandOption = .none
         }
+
+        onDemandRow.valueOptions = activateOnDemandOptions.map {
+            return TunnelViewModel.activateOnDemandOptionText(for: $0)
+        }
+        onDemandRow.selectedOptionIndex = activateOnDemandOptions.firstIndex(of: selectedActivateOnDemandOption)!
     }
 
     override func loadView() {
@@ -126,7 +151,7 @@ class TunnelEditViewController: NSViewController {
         let margin: CGFloat = 20
         let internalSpacing: CGFloat = 10
 
-        let editorStackView = NSStackView(views: [nameRow, publicKeyRow, scrollView])
+        let editorStackView = NSStackView(views: [nameRow, publicKeyRow, onDemandRow, scrollView])
         editorStackView.orientation = .vertical
         editorStackView.setHuggingPriority(.defaultHigh, for: .horizontal)
         editorStackView.spacing = internalSpacing
@@ -157,6 +182,13 @@ class TunnelEditViewController: NSViewController {
             ErrorPresenter.showErrorAlert(title: tr("macAlertNameIsEmpty"), message: "", from: self)
             return
         }
+        let onDemandSetting: ActivateOnDemandSetting
+        let onDemandOption = activateOnDemandOptions[onDemandRow.selectedOptionIndex]
+        if onDemandOption == .none {
+            onDemandSetting = ActivateOnDemandSetting.defaultSetting
+        } else {
+            onDemandSetting = ActivateOnDemandSetting(isActivateOnDemandEnabled: true, activateOnDemandOption: onDemandOption)
+        }
         if let tunnel = tunnel {
             // We're modifying an existing tunnel
             if name != tunnel.name && tunnelsManager.tunnel(named: name) != nil {
@@ -165,7 +197,6 @@ class TunnelEditViewController: NSViewController {
             }
             do {
                 let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: textView.string, called: nameRow.value)
-                let onDemandSetting = ActivateOnDemandSetting.defaultSetting
                 tunnelsManager.modify(tunnel: tunnel, tunnelConfiguration: tunnelConfiguration, activateOnDemandSetting: onDemandSetting) { [weak self] error in
                     if let error = error {
                         ErrorPresenter.showErrorAlert(error: error, from: self)
@@ -187,7 +218,6 @@ class TunnelEditViewController: NSViewController {
             }
             do {
                 let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: textView.string, called: nameRow.value)
-                let onDemandSetting = ActivateOnDemandSetting.defaultSetting
                 tunnelsManager.add(tunnelConfiguration: tunnelConfiguration, activateOnDemandSetting: onDemandSetting) { [weak self] result in
                     if let error = result.error {
                         ErrorPresenter.showErrorAlert(error: error, from: self)