on-demand: macOS: Integrate Ethernet and Wi-Fi controls in one row

This commit is contained in:
Roopesh Chander 2019-03-17 20:10:26 +05:30 committed by Jason A. Donenfeld
parent f8c1837f1e
commit 094ab4fed7
2 changed files with 82 additions and 45 deletions

View File

@ -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) }
} }

View File

@ -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)