on-demand: macOS: Integrate Ethernet and Wi-Fi controls in one row
This commit is contained in:
parent
f8c1837f1e
commit
094ab4fed7
|
@ -4,7 +4,27 @@
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import CoreWLAN
|
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 onDemandWiFiCheckbox: NSButton = {
|
||||||
let checkbox = NSButton()
|
let checkbox = NSButton()
|
||||||
|
@ -23,20 +43,23 @@ class OnDemandWiFiControls: NSStackView {
|
||||||
let onDemandSSIDsField: NSTokenField = {
|
let onDemandSSIDsField: NSTokenField = {
|
||||||
let tokenField = NSTokenField()
|
let tokenField = NSTokenField()
|
||||||
tokenField.tokenizingCharacterSet = CharacterSet([])
|
tokenField.tokenizingCharacterSet = CharacterSet([])
|
||||||
|
tokenField.tokenStyle = .squared
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
tokenField.widthAnchor.constraint(greaterThanOrEqualToConstant: 150)
|
tokenField.widthAnchor.constraint(greaterThanOrEqualToConstant: 180)
|
||||||
])
|
])
|
||||||
return tokenField
|
return tokenField
|
||||||
}()
|
}()
|
||||||
|
|
||||||
override var intrinsicContentSize: NSSize {
|
override var intrinsicContentSize: NSSize {
|
||||||
let minHeight: CGFloat = 22
|
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)
|
return NSSize(width: NSView.noIntrinsicMetric, height: height)
|
||||||
}
|
}
|
||||||
|
|
||||||
var onDemandViewModel: ActivateOnDemandViewModel? {
|
var onDemandViewModel: ActivateOnDemandViewModel? {
|
||||||
didSet { updateSSIDControls() }
|
didSet { updateControls() }
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentSSIDs: [String]
|
var currentSSIDs: [String]
|
||||||
|
@ -44,16 +67,45 @@ class OnDemandWiFiControls: NSStackView {
|
||||||
init() {
|
init() {
|
||||||
currentSSIDs = getCurrentSSIDs()
|
currentSSIDs = getCurrentSSIDs()
|
||||||
super.init(frame: CGRect.zero)
|
super.init(frame: CGRect.zero)
|
||||||
onDemandSSIDOptionsPopup.addItems(withTitles: OnDemandWiFiControls.onDemandSSIDOptions.map { $0.localizedUIString })
|
|
||||||
setViews([onDemandWiFiCheckbox, onDemandSSIDOptionsPopup, onDemandSSIDsField], in: .leading)
|
onDemandSSIDOptionsPopup.addItems(withTitles: OnDemandControlsRow.onDemandSSIDOptions.map { $0.localizedUIString })
|
||||||
orientation = .horizontal
|
|
||||||
|
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([
|
NSLayoutConstraint.activate([
|
||||||
onDemandWiFiCheckbox.centerYAnchor.constraint(equalTo: centerYAnchor),
|
keyLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor),
|
||||||
onDemandSSIDOptionsPopup.lastBaselineAnchor.constraint(equalTo: onDemandWiFiCheckbox.lastBaselineAnchor),
|
stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor),
|
||||||
onDemandSSIDsField.lastBaselineAnchor.constraint(equalTo: onDemandWiFiCheckbox.lastBaselineAnchor)
|
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.target = self
|
||||||
onDemandWiFiCheckbox.action = #selector(wiFiCheckboxToggled)
|
onDemandWiFiCheckbox.action = #selector(wiFiCheckboxToggled)
|
||||||
|
|
||||||
|
@ -62,7 +114,7 @@ class OnDemandWiFiControls: NSStackView {
|
||||||
|
|
||||||
onDemandSSIDsField.delegate = self
|
onDemandSSIDsField.delegate = self
|
||||||
|
|
||||||
updateSSIDControls()
|
updateControls()
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder decoder: NSCoder) {
|
required init?(coder decoder: NSCoder) {
|
||||||
|
@ -71,38 +123,44 @@ class OnDemandWiFiControls: NSStackView {
|
||||||
|
|
||||||
func saveToViewModel() {
|
func saveToViewModel() {
|
||||||
guard let onDemandViewModel = onDemandViewModel else { return }
|
guard let onDemandViewModel = onDemandViewModel else { return }
|
||||||
|
onDemandViewModel.isNonWiFiInterfaceEnabled = onDemandEthernetCheckbox.state == .on
|
||||||
onDemandViewModel.isWiFiInterfaceEnabled = onDemandWiFiCheckbox.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]) ?? []
|
onDemandViewModel.selectedSSIDs = (onDemandSSIDsField.objectValue as? [String]) ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateSSIDControls() {
|
func updateControls() {
|
||||||
guard let onDemandViewModel = onDemandViewModel else { return }
|
guard let onDemandViewModel = onDemandViewModel else { return }
|
||||||
|
onDemandEthernetCheckbox.state = onDemandViewModel.isNonWiFiInterfaceEnabled ? .on : .off
|
||||||
onDemandWiFiCheckbox.state = onDemandViewModel.isWiFiInterfaceEnabled ? .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)
|
onDemandSSIDOptionsPopup.selectItem(at: optionIndex ?? 0)
|
||||||
onDemandSSIDsField.objectValue = onDemandViewModel.selectedSSIDs
|
onDemandSSIDsField.objectValue = onDemandViewModel.selectedSSIDs
|
||||||
onDemandSSIDOptionsPopup.isHidden = !onDemandViewModel.isWiFiInterfaceEnabled
|
onDemandSSIDOptionsPopup.isHidden = !onDemandViewModel.isWiFiInterfaceEnabled
|
||||||
onDemandSSIDsField.isHidden = !onDemandViewModel.isWiFiInterfaceEnabled || onDemandViewModel.ssidOption == .anySSID
|
onDemandSSIDsField.isHidden = !onDemandViewModel.isWiFiInterfaceEnabled || onDemandViewModel.ssidOption == .anySSID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func ethernetCheckboxToggled() {
|
||||||
|
onDemandViewModel?.isNonWiFiInterfaceEnabled = onDemandEthernetCheckbox.state == .on
|
||||||
|
}
|
||||||
|
|
||||||
@objc func wiFiCheckboxToggled() {
|
@objc func wiFiCheckboxToggled() {
|
||||||
onDemandViewModel?.isWiFiInterfaceEnabled = onDemandWiFiCheckbox.state == .on
|
onDemandViewModel?.isWiFiInterfaceEnabled = onDemandWiFiCheckbox.state == .on
|
||||||
updateSSIDControls()
|
updateControls()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func ssidOptionsPopupValueChanged() {
|
@objc func ssidOptionsPopupValueChanged() {
|
||||||
let selectedIndex = onDemandSSIDOptionsPopup.indexOfSelectedItem
|
let selectedIndex = onDemandSSIDOptionsPopup.indexOfSelectedItem
|
||||||
onDemandViewModel?.ssidOption = OnDemandWiFiControls.onDemandSSIDOptions[selectedIndex]
|
onDemandViewModel?.ssidOption = OnDemandControlsRow.onDemandSSIDOptions[selectedIndex]
|
||||||
onDemandViewModel?.selectedSSIDs = (onDemandSSIDsField.objectValue as? [String]) ?? []
|
onDemandViewModel?.selectedSSIDs = (onDemandSSIDsField.objectValue as? [String]) ?? []
|
||||||
updateSSIDControls()
|
updateControls()
|
||||||
if !onDemandSSIDsField.isHidden {
|
if !onDemandSSIDsField.isHidden {
|
||||||
onDemandSSIDsField.becomeFirstResponder()
|
onDemandSSIDsField.becomeFirstResponder()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension OnDemandWiFiControls: NSTokenFieldDelegate {
|
extension OnDemandControlsRow: NSTokenFieldDelegate {
|
||||||
func tokenField(_ tokenField: NSTokenField, completionsForSubstring substring: String, indexOfToken tokenIndex: Int, indexOfSelectedItem selectedIndex: UnsafeMutablePointer<Int>?) -> [Any]? {
|
func tokenField(_ tokenField: NSTokenField, completionsForSubstring substring: String, indexOfToken tokenIndex: Int, indexOfSelectedItem selectedIndex: UnsafeMutablePointer<Int>?) -> [Any]? {
|
||||||
return currentSSIDs.filter { $0.hasPrefix(substring) }
|
return currentSSIDs.filter { $0.hasPrefix(substring) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,15 +42,7 @@ class TunnelEditViewController: NSViewController {
|
||||||
return textView
|
return textView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
let onDemandEthernetCheckbox: NSButton = {
|
let onDemandControlsRow = OnDemandControlsRow()
|
||||||
let checkbox = NSButton()
|
|
||||||
checkbox.title = tr("tunnelOnDemandEthernet")
|
|
||||||
checkbox.setButtonType(.switch)
|
|
||||||
checkbox.state = .off
|
|
||||||
return checkbox
|
|
||||||
}()
|
|
||||||
|
|
||||||
let onDemandWiFiControls = OnDemandWiFiControls()
|
|
||||||
|
|
||||||
let scrollView: NSScrollView = {
|
let scrollView: NSScrollView = {
|
||||||
let scrollView = NSScrollView()
|
let scrollView = NSScrollView()
|
||||||
|
@ -147,20 +139,6 @@ class TunnelEditViewController: NSViewController {
|
||||||
override func loadView() {
|
override func loadView() {
|
||||||
populateFields()
|
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
|
scrollView.documentView = textView
|
||||||
|
|
||||||
saveButton.target = self
|
saveButton.target = self
|
||||||
|
@ -172,10 +150,12 @@ class TunnelEditViewController: NSViewController {
|
||||||
excludePrivateIPsCheckbox.target = self
|
excludePrivateIPsCheckbox.target = self
|
||||||
excludePrivateIPsCheckbox.action = #selector(excludePrivateIPsCheckboxToggled(sender:))
|
excludePrivateIPsCheckbox.action = #selector(excludePrivateIPsCheckboxToggled(sender:))
|
||||||
|
|
||||||
|
onDemandControlsRow.onDemandViewModel = onDemandViewModel
|
||||||
|
|
||||||
let margin: CGFloat = 20
|
let margin: CGFloat = 20
|
||||||
let internalSpacing: CGFloat = 10
|
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.orientation = .vertical
|
||||||
editorStackView.setHuggingPriority(.defaultHigh, for: .horizontal)
|
editorStackView.setHuggingPriority(.defaultHigh, for: .horizontal)
|
||||||
editorStackView.spacing = internalSpacing
|
editorStackView.spacing = internalSpacing
|
||||||
|
@ -205,7 +185,7 @@ class TunnelEditViewController: NSViewController {
|
||||||
view.window?.ignoresMouseEvents = !enabled
|
view.window?.ignoresMouseEvents = !enabled
|
||||||
nameRow.valueLabel.isEditable = enabled
|
nameRow.valueLabel.isEditable = enabled
|
||||||
textView.isEditable = enabled
|
textView.isEditable = enabled
|
||||||
onDemandWiFiControls.onDemandSSIDsField.isEnabled = enabled
|
onDemandControlsRow.onDemandSSIDsField.isEnabled = enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func handleSaveAction() {
|
@objc func handleSaveAction() {
|
||||||
|
@ -215,8 +195,7 @@ class TunnelEditViewController: NSViewController {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
onDemandViewModel.isNonWiFiInterfaceEnabled = onDemandEthernetCheckbox.state == .on
|
onDemandControlsRow.saveToViewModel()
|
||||||
onDemandWiFiControls.saveToViewModel()
|
|
||||||
let onDemandOption = onDemandViewModel.toOnDemandOption()
|
let onDemandOption = onDemandViewModel.toOnDemandOption()
|
||||||
|
|
||||||
let isTunnelModifiedWithoutChangingName = (tunnel != nil && tunnel!.name == name)
|
let isTunnelModifiedWithoutChangingName = (tunnel != nil && tunnel!.name == name)
|
||||||
|
|
Loading…
Reference in New Issue