diff --git a/WireGuard/WireGuard/UI/macOS/View/OnDemandWiFiControls.swift b/WireGuard/WireGuard/UI/macOS/View/OnDemandWiFiControls.swift index 194075d..2e3de48 100644 --- a/WireGuard/WireGuard/UI/macOS/View/OnDemandWiFiControls.swift +++ b/WireGuard/WireGuard/UI/macOS/View/OnDemandWiFiControls.swift @@ -4,7 +4,27 @@ import Cocoa import CoreWLAN -class OnDemandWiFiControls: NSStackView { +class OnDemandControlsRow: NSView { + let keyLabel: NSTextField = { + let keyLabel = NSTextField() + keyLabel.stringValue = tr("macFieldOnDemand") + keyLabel.isEditable = false + keyLabel.isSelectable = false + keyLabel.isBordered = false + keyLabel.alignment = .right + keyLabel.maximumNumberOfLines = 1 + keyLabel.lineBreakMode = .byTruncatingTail + keyLabel.backgroundColor = .clear + return keyLabel + }() + + let onDemandEthernetCheckbox: NSButton = { + let checkbox = NSButton() + checkbox.title = tr("tunnelOnDemandEthernet") + checkbox.setButtonType(.switch) + checkbox.state = .off + return checkbox + }() let onDemandWiFiCheckbox: NSButton = { let checkbox = NSButton() @@ -23,20 +43,23 @@ class OnDemandWiFiControls: NSStackView { let onDemandSSIDsField: NSTokenField = { let tokenField = NSTokenField() tokenField.tokenizingCharacterSet = CharacterSet([]) + tokenField.tokenStyle = .squared NSLayoutConstraint.activate([ - tokenField.widthAnchor.constraint(greaterThanOrEqualToConstant: 150) + tokenField.widthAnchor.constraint(greaterThanOrEqualToConstant: 180) ]) return tokenField }() override var intrinsicContentSize: NSSize { let minHeight: CGFloat = 22 - let height = max(minHeight, onDemandWiFiCheckbox.intrinsicContentSize.height, onDemandSSIDOptionsPopup.intrinsicContentSize.height, onDemandSSIDsField.intrinsicContentSize.height) + let height = max(minHeight, keyLabel.intrinsicContentSize.height, + onDemandEthernetCheckbox.intrinsicContentSize.height, onDemandWiFiCheckbox.intrinsicContentSize.height, + onDemandSSIDOptionsPopup.intrinsicContentSize.height, onDemandSSIDsField.intrinsicContentSize.height) return NSSize(width: NSView.noIntrinsicMetric, height: height) } var onDemandViewModel: ActivateOnDemandViewModel? { - didSet { updateSSIDControls() } + didSet { updateControls() } } var currentSSIDs: [String] @@ -44,16 +67,45 @@ class OnDemandWiFiControls: NSStackView { init() { currentSSIDs = getCurrentSSIDs() super.init(frame: CGRect.zero) - onDemandSSIDOptionsPopup.addItems(withTitles: OnDemandWiFiControls.onDemandSSIDOptions.map { $0.localizedUIString }) - setViews([onDemandWiFiCheckbox, onDemandSSIDOptionsPopup, onDemandSSIDsField], in: .leading) - orientation = .horizontal + + onDemandSSIDOptionsPopup.addItems(withTitles: OnDemandControlsRow.onDemandSSIDOptions.map { $0.localizedUIString }) + + let stackView = NSStackView() + stackView.setViews([onDemandEthernetCheckbox, onDemandWiFiCheckbox, onDemandSSIDOptionsPopup, onDemandSSIDsField], in: .leading) + stackView.orientation = .horizontal + + addSubview(keyLabel) + addSubview(stackView) + keyLabel.translatesAutoresizingMaskIntoConstraints = false + stackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - onDemandWiFiCheckbox.centerYAnchor.constraint(equalTo: centerYAnchor), - onDemandSSIDOptionsPopup.lastBaselineAnchor.constraint(equalTo: onDemandWiFiCheckbox.lastBaselineAnchor), - onDemandSSIDsField.lastBaselineAnchor.constraint(equalTo: onDemandWiFiCheckbox.lastBaselineAnchor) + keyLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor), + stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor), + self.leadingAnchor.constraint(equalTo: keyLabel.leadingAnchor), + stackView.leadingAnchor.constraint(equalTo: keyLabel.trailingAnchor, constant: 5), + stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor) ]) + 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 + + NSLayoutConstraint.activate([ + onDemandEthernetCheckbox.centerYAnchor.constraint(equalTo: stackView.centerYAnchor), + onDemandWiFiCheckbox.lastBaselineAnchor.constraint(equalTo: onDemandEthernetCheckbox.lastBaselineAnchor), + onDemandSSIDOptionsPopup.lastBaselineAnchor.constraint(equalTo: onDemandEthernetCheckbox.lastBaselineAnchor), + onDemandSSIDsField.lastBaselineAnchor.constraint(equalTo: onDemandEthernetCheckbox.lastBaselineAnchor) + ]) + + onDemandSSIDsField.setContentHuggingPriority(.defaultLow, for: .horizontal) + + onDemandEthernetCheckbox.target = self + onDemandEthernetCheckbox.action = #selector(ethernetCheckboxToggled) + onDemandWiFiCheckbox.target = self onDemandWiFiCheckbox.action = #selector(wiFiCheckboxToggled) @@ -62,7 +114,7 @@ class OnDemandWiFiControls: NSStackView { onDemandSSIDsField.delegate = self - updateSSIDControls() + updateControls() } required init?(coder decoder: NSCoder) { @@ -71,38 +123,44 @@ class OnDemandWiFiControls: NSStackView { func saveToViewModel() { guard let onDemandViewModel = onDemandViewModel else { return } + onDemandViewModel.isNonWiFiInterfaceEnabled = onDemandEthernetCheckbox.state == .on onDemandViewModel.isWiFiInterfaceEnabled = onDemandWiFiCheckbox.state == .on - onDemandViewModel.ssidOption = OnDemandWiFiControls.onDemandSSIDOptions[onDemandSSIDOptionsPopup.indexOfSelectedItem] + onDemandViewModel.ssidOption = OnDemandControlsRow.onDemandSSIDOptions[onDemandSSIDOptionsPopup.indexOfSelectedItem] onDemandViewModel.selectedSSIDs = (onDemandSSIDsField.objectValue as? [String]) ?? [] } - func updateSSIDControls() { + func updateControls() { guard let onDemandViewModel = onDemandViewModel else { return } + onDemandEthernetCheckbox.state = onDemandViewModel.isNonWiFiInterfaceEnabled ? .on : .off onDemandWiFiCheckbox.state = onDemandViewModel.isWiFiInterfaceEnabled ? .on : .off - let optionIndex = OnDemandWiFiControls.onDemandSSIDOptions.firstIndex(of: onDemandViewModel.ssidOption) + let optionIndex = OnDemandControlsRow.onDemandSSIDOptions.firstIndex(of: onDemandViewModel.ssidOption) onDemandSSIDOptionsPopup.selectItem(at: optionIndex ?? 0) onDemandSSIDsField.objectValue = onDemandViewModel.selectedSSIDs onDemandSSIDOptionsPopup.isHidden = !onDemandViewModel.isWiFiInterfaceEnabled onDemandSSIDsField.isHidden = !onDemandViewModel.isWiFiInterfaceEnabled || onDemandViewModel.ssidOption == .anySSID } + @objc func ethernetCheckboxToggled() { + onDemandViewModel?.isNonWiFiInterfaceEnabled = onDemandEthernetCheckbox.state == .on + } + @objc func wiFiCheckboxToggled() { onDemandViewModel?.isWiFiInterfaceEnabled = onDemandWiFiCheckbox.state == .on - updateSSIDControls() + updateControls() } @objc func ssidOptionsPopupValueChanged() { let selectedIndex = onDemandSSIDOptionsPopup.indexOfSelectedItem - onDemandViewModel?.ssidOption = OnDemandWiFiControls.onDemandSSIDOptions[selectedIndex] + onDemandViewModel?.ssidOption = OnDemandControlsRow.onDemandSSIDOptions[selectedIndex] onDemandViewModel?.selectedSSIDs = (onDemandSSIDsField.objectValue as? [String]) ?? [] - updateSSIDControls() + updateControls() if !onDemandSSIDsField.isHidden { onDemandSSIDsField.becomeFirstResponder() } } } -extension OnDemandWiFiControls: NSTokenFieldDelegate { +extension OnDemandControlsRow: NSTokenFieldDelegate { func tokenField(_ tokenField: NSTokenField, completionsForSubstring substring: String, indexOfToken tokenIndex: Int, indexOfSelectedItem selectedIndex: UnsafeMutablePointer?) -> [Any]? { return currentSSIDs.filter { $0.hasPrefix(substring) } } diff --git a/WireGuard/WireGuard/UI/macOS/ViewController/TunnelEditViewController.swift b/WireGuard/WireGuard/UI/macOS/ViewController/TunnelEditViewController.swift index 51420c4..3d959cf 100644 --- a/WireGuard/WireGuard/UI/macOS/ViewController/TunnelEditViewController.swift +++ b/WireGuard/WireGuard/UI/macOS/ViewController/TunnelEditViewController.swift @@ -42,15 +42,7 @@ class TunnelEditViewController: NSViewController { return textView }() - let onDemandEthernetCheckbox: NSButton = { - let checkbox = NSButton() - checkbox.title = tr("tunnelOnDemandEthernet") - checkbox.setButtonType(.switch) - checkbox.state = .off - return checkbox - }() - - let onDemandWiFiControls = OnDemandWiFiControls() + let onDemandControlsRow = OnDemandControlsRow() let scrollView: NSScrollView = { let scrollView = NSScrollView() @@ -147,20 +139,6 @@ class TunnelEditViewController: NSViewController { override func loadView() { populateFields() - let onDemandEthernetRow = ControlRow(controlView: onDemandEthernetCheckbox) - onDemandEthernetRow.key = tr("macFieldOnDemand") - onDemandEthernetCheckbox.state = onDemandViewModel.isNonWiFiInterfaceEnabled ? .on : .off - - let onDemandWiFiRow = ControlRow(controlView: onDemandWiFiControls) - onDemandWiFiRow.key = "" - onDemandWiFiControls.onDemandViewModel = onDemandViewModel - - NSLayoutConstraint.activate([ - onDemandEthernetRow.keyLabel.firstBaselineAnchor.constraint(equalTo: onDemandEthernetRow.controlView.firstBaselineAnchor), - onDemandWiFiRow.controlView.centerYAnchor.constraint(equalTo: onDemandWiFiRow.centerYAnchor), - onDemandWiFiRow.trailingAnchor.constraint(equalTo: onDemandWiFiControls.trailingAnchor) - ]) - scrollView.documentView = textView saveButton.target = self @@ -172,10 +150,12 @@ class TunnelEditViewController: NSViewController { excludePrivateIPsCheckbox.target = self excludePrivateIPsCheckbox.action = #selector(excludePrivateIPsCheckboxToggled(sender:)) + onDemandControlsRow.onDemandViewModel = onDemandViewModel + let margin: CGFloat = 20 let internalSpacing: CGFloat = 10 - let editorStackView = NSStackView(views: [nameRow, publicKeyRow, onDemandEthernetRow, onDemandWiFiRow, scrollView]) + let editorStackView = NSStackView(views: [nameRow, publicKeyRow, onDemandControlsRow, scrollView]) editorStackView.orientation = .vertical editorStackView.setHuggingPriority(.defaultHigh, for: .horizontal) editorStackView.spacing = internalSpacing @@ -205,7 +185,7 @@ class TunnelEditViewController: NSViewController { view.window?.ignoresMouseEvents = !enabled nameRow.valueLabel.isEditable = enabled textView.isEditable = enabled - onDemandWiFiControls.onDemandSSIDsField.isEnabled = enabled + onDemandControlsRow.onDemandSSIDsField.isEnabled = enabled } @objc func handleSaveAction() { @@ -215,8 +195,7 @@ class TunnelEditViewController: NSViewController { return } - onDemandViewModel.isNonWiFiInterfaceEnabled = onDemandEthernetCheckbox.state == .on - onDemandWiFiControls.saveToViewModel() + onDemandControlsRow.saveToViewModel() let onDemandOption = onDemandViewModel.toOnDemandOption() let isTunnelModifiedWithoutChangingName = (tunnel != nil && tunnel!.name == name)