macOS: Tunnel edit view
Signed-off-by: Roopesh Chander <roop@roopc.net>
This commit is contained in:
@@ -119,6 +119,7 @@
|
|||||||
6FBA104621D7EBFA0051C35F /* TunnelsListTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FBA104521D7EBFA0051C35F /* TunnelsListTableViewController.swift */; };
|
6FBA104621D7EBFA0051C35F /* TunnelsListTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FBA104521D7EBFA0051C35F /* TunnelsListTableViewController.swift */; };
|
||||||
6FCD99AA21E0E14700BA4C82 /* NoTunnelsDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FCD99A821E0E0C700BA4C82 /* NoTunnelsDetailViewController.swift */; };
|
6FCD99AA21E0E14700BA4C82 /* NoTunnelsDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FCD99A821E0E0C700BA4C82 /* NoTunnelsDetailViewController.swift */; };
|
||||||
6FCD99AF21E0EA1700BA4C82 /* ImportPanelPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FCD99AE21E0EA1700BA4C82 /* ImportPanelPresenter.swift */; };
|
6FCD99AF21E0EA1700BA4C82 /* ImportPanelPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FCD99AE21E0EA1700BA4C82 /* ImportPanelPresenter.swift */; };
|
||||||
|
6FCD99B121E0EDA900BA4C82 /* TunnelEditViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FCD99B021E0EDA900BA4C82 /* TunnelEditViewController.swift */; };
|
||||||
6FDB3C3B21DCF47400A0C0BF /* TunnelDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDB3C3A21DCF47400A0C0BF /* TunnelDetailTableViewController.swift */; };
|
6FDB3C3B21DCF47400A0C0BF /* TunnelDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDB3C3A21DCF47400A0C0BF /* TunnelDetailTableViewController.swift */; };
|
||||||
6FDB3C3C21DCF6BB00A0C0BF /* TunnelViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F628C3C217F09E9003482A3 /* TunnelViewModel.swift */; };
|
6FDB3C3C21DCF6BB00A0C0BF /* TunnelViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F628C3C217F09E9003482A3 /* TunnelViewModel.swift */; };
|
||||||
6FDEF7E421846C1A00D8FBF6 /* libwg-go.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6FDEF7E321846C1A00D8FBF6 /* libwg-go.a */; };
|
6FDEF7E421846C1A00D8FBF6 /* libwg-go.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6FDEF7E321846C1A00D8FBF6 /* libwg-go.a */; };
|
||||||
@@ -274,6 +275,7 @@
|
|||||||
6FBA104521D7EBFA0051C35F /* TunnelsListTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelsListTableViewController.swift; sourceTree = "<group>"; };
|
6FBA104521D7EBFA0051C35F /* TunnelsListTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelsListTableViewController.swift; sourceTree = "<group>"; };
|
||||||
6FCD99A821E0E0C700BA4C82 /* NoTunnelsDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoTunnelsDetailViewController.swift; sourceTree = "<group>"; };
|
6FCD99A821E0E0C700BA4C82 /* NoTunnelsDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoTunnelsDetailViewController.swift; sourceTree = "<group>"; };
|
||||||
6FCD99AE21E0EA1700BA4C82 /* ImportPanelPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportPanelPresenter.swift; sourceTree = "<group>"; };
|
6FCD99AE21E0EA1700BA4C82 /* ImportPanelPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportPanelPresenter.swift; sourceTree = "<group>"; };
|
||||||
|
6FCD99B021E0EDA900BA4C82 /* TunnelEditViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelEditViewController.swift; sourceTree = "<group>"; };
|
||||||
6FDB3C3A21DCF47400A0C0BF /* TunnelDetailTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelDetailTableViewController.swift; sourceTree = "<group>"; };
|
6FDB3C3A21DCF47400A0C0BF /* TunnelDetailTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelDetailTableViewController.swift; sourceTree = "<group>"; };
|
||||||
6FDEF7E321846C1A00D8FBF6 /* libwg-go.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libwg-go.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
6FDEF7E321846C1A00D8FBF6 /* libwg-go.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libwg-go.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
6FDEF7E52185EFAF00D8FBF6 /* QRScanViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRScanViewController.swift; sourceTree = "<group>"; };
|
6FDEF7E52185EFAF00D8FBF6 /* QRScanViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRScanViewController.swift; sourceTree = "<group>"; };
|
||||||
@@ -512,6 +514,7 @@
|
|||||||
6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */,
|
6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */,
|
||||||
6FDB3C3A21DCF47400A0C0BF /* TunnelDetailTableViewController.swift */,
|
6FDB3C3A21DCF47400A0C0BF /* TunnelDetailTableViewController.swift */,
|
||||||
6FCD99A821E0E0C700BA4C82 /* NoTunnelsDetailViewController.swift */,
|
6FCD99A821E0E0C700BA4C82 /* NoTunnelsDetailViewController.swift */,
|
||||||
|
6FCD99B021E0EDA900BA4C82 /* TunnelEditViewController.swift */,
|
||||||
);
|
);
|
||||||
path = ViewController;
|
path = ViewController;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1078,6 +1081,7 @@
|
|||||||
6F4DD16C21DA558F00690EAE /* NSTableView+Reuse.swift in Sources */,
|
6F4DD16C21DA558F00690EAE /* NSTableView+Reuse.swift in Sources */,
|
||||||
6FB1BDD821D50F5300A991BF /* WireGuardResult.swift in Sources */,
|
6FB1BDD821D50F5300A991BF /* WireGuardResult.swift in Sources */,
|
||||||
6FB1BDD921D50F5300A991BF /* LocalizationHelper.swift in Sources */,
|
6FB1BDD921D50F5300A991BF /* LocalizationHelper.swift in Sources */,
|
||||||
|
6FCD99B121E0EDA900BA4C82 /* TunnelEditViewController.swift in Sources */,
|
||||||
6FB1BDCA21D50F1700A991BF /* x25519.c in Sources */,
|
6FB1BDCA21D50F1700A991BF /* x25519.c in Sources */,
|
||||||
6FB1BDCB21D50F1700A991BF /* Curve25519.swift in Sources */,
|
6FB1BDCB21D50F1700A991BF /* Curve25519.swift in Sources */,
|
||||||
6FB1BDBB21D50F0200A991BF /* Localizable.strings in Sources */,
|
6FB1BDBB21D50F0200A991BF /* Localizable.strings in Sources */,
|
||||||
|
|||||||
@@ -244,10 +244,15 @@
|
|||||||
|
|
||||||
"macButtonImportTunnels" = "Import tunnel(s) from file";
|
"macButtonImportTunnels" = "Import tunnel(s) from file";
|
||||||
|
|
||||||
// Mac detail view fields
|
// Mac detail/edit view fields
|
||||||
|
|
||||||
"macDetailFieldKey (%@)" = "%@:";
|
"macFieldKey (%@)" = "%@:";
|
||||||
|
|
||||||
// Mac status display
|
// Mac status display
|
||||||
|
|
||||||
"macStatus (%@)" = "Status: %@";
|
"macStatus (%@)" = "Status: %@";
|
||||||
|
|
||||||
|
// Mac editing config
|
||||||
|
|
||||||
|
"macEditDiscard" = "Discard";
|
||||||
|
"macEditSave" = "Save";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class KeyValueRow: NSView {
|
class EditableKeyValueRow: NSView {
|
||||||
let keyLabel: NSTextField = {
|
let keyLabel: NSTextField = {
|
||||||
let keyLabel = NSTextField()
|
let keyLabel = NSTextField()
|
||||||
keyLabel.isEditable = false
|
keyLabel.isEditable = false
|
||||||
@@ -18,12 +18,9 @@ class KeyValueRow: NSView {
|
|||||||
|
|
||||||
let valueLabel: NSTextField = {
|
let valueLabel: NSTextField = {
|
||||||
let valueLabel = NSTextField()
|
let valueLabel = NSTextField()
|
||||||
valueLabel.isEditable = false
|
|
||||||
valueLabel.isSelectable = true
|
valueLabel.isSelectable = true
|
||||||
valueLabel.isBordered = false
|
|
||||||
valueLabel.maximumNumberOfLines = 1
|
valueLabel.maximumNumberOfLines = 1
|
||||||
valueLabel.lineBreakMode = .byTruncatingTail
|
valueLabel.lineBreakMode = .byTruncatingTail
|
||||||
valueLabel.backgroundColor = .clear
|
|
||||||
return valueLabel
|
return valueLabel
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -46,6 +43,11 @@ class KeyValueRow: NSView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override var intrinsicContentSize: NSSize {
|
||||||
|
let height = max(keyLabel.intrinsicContentSize.height, valueLabel.intrinsicContentSize.height)
|
||||||
|
return NSSize(width: NSView.noIntrinsicMetric, height: height)
|
||||||
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
super.init(frame: CGRect.zero)
|
super.init(frame: CGRect.zero)
|
||||||
|
|
||||||
@@ -74,3 +76,16 @@ class KeyValueRow: NSView {
|
|||||||
isKeyInBold = false
|
isKeyInBold = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class KeyValueRow: EditableKeyValueRow {
|
||||||
|
override init() {
|
||||||
|
super.init()
|
||||||
|
valueLabel.isEditable = false
|
||||||
|
valueLabel.isBordered = false
|
||||||
|
valueLabel.backgroundColor = .clear
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder decoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -191,7 +191,8 @@ class TunnelDetailTableViewController: NSViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func editButtonClicked() {
|
@objc func editButtonClicked() {
|
||||||
print("editButtonClicked")
|
let tunnelEditVC = TunnelEditViewController(tunnelsManager: tunnelsManager, tunnel: tunnel)
|
||||||
|
presentAsSheet(tunnelEditVC)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func statusCheckboxToggled(sender: AnyObject?) {
|
@objc func statusCheckboxToggled(sender: AnyObject?) {
|
||||||
@@ -217,14 +218,14 @@ extension TunnelDetailTableViewController: NSTableViewDelegate {
|
|||||||
case .interfaceFieldRow(let field):
|
case .interfaceFieldRow(let field):
|
||||||
let cell: KeyValueRow = tableView.dequeueReusableCell()
|
let cell: KeyValueRow = tableView.dequeueReusableCell()
|
||||||
let localizedKeyString = modelRow.isTitleRow() ? modelRow.localizedSectionKeyString() : field.localizedUIString
|
let localizedKeyString = modelRow.isTitleRow() ? modelRow.localizedSectionKeyString() : field.localizedUIString
|
||||||
cell.key = tr(format: "macDetailFieldKey (%@)", localizedKeyString)
|
cell.key = tr(format: "macFieldKey (%@)", localizedKeyString)
|
||||||
cell.value = tunnelViewModel.interfaceData[field]
|
cell.value = tunnelViewModel.interfaceData[field]
|
||||||
cell.isKeyInBold = modelRow.isTitleRow()
|
cell.isKeyInBold = modelRow.isTitleRow()
|
||||||
return cell
|
return cell
|
||||||
case .peerFieldRow(let peerData, let field):
|
case .peerFieldRow(let peerData, let field):
|
||||||
let cell: KeyValueRow = tableView.dequeueReusableCell()
|
let cell: KeyValueRow = tableView.dequeueReusableCell()
|
||||||
let localizedKeyString = modelRow.isTitleRow() ? modelRow.localizedSectionKeyString() : field.localizedUIString
|
let localizedKeyString = modelRow.isTitleRow() ? modelRow.localizedSectionKeyString() : field.localizedUIString
|
||||||
cell.key = tr(format: "macDetailFieldKey (%@)", localizedKeyString)
|
cell.key = tr(format: "macFieldKey (%@)", localizedKeyString)
|
||||||
cell.value = peerData[field]
|
cell.value = peerData[field]
|
||||||
cell.isKeyInBold = modelRow.isTitleRow()
|
cell.isKeyInBold = modelRow.isTitleRow()
|
||||||
return cell
|
return cell
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class TunnelEditViewController: NSViewController {
|
||||||
|
|
||||||
|
let nameRow: EditableKeyValueRow = {
|
||||||
|
let nameRow = EditableKeyValueRow()
|
||||||
|
nameRow.key = tr(format: "macFieldKey (%@)", TunnelViewModel.InterfaceField.name.localizedUIString)
|
||||||
|
return nameRow
|
||||||
|
}()
|
||||||
|
|
||||||
|
let publicKeyRow: KeyValueRow = {
|
||||||
|
let publicKeyRow = KeyValueRow()
|
||||||
|
publicKeyRow.key = tr(format: "macFieldKey (%@)", TunnelViewModel.InterfaceField.publicKey.localizedUIString)
|
||||||
|
return publicKeyRow
|
||||||
|
}()
|
||||||
|
|
||||||
|
let textView: NSTextView = {
|
||||||
|
let textView = NSTextView()
|
||||||
|
let minWidth: CGFloat = 120
|
||||||
|
let minHeight: CGFloat = 60
|
||||||
|
textView.minSize = NSSize(width: 0, height: minHeight)
|
||||||
|
textView.maxSize = NSSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
|
||||||
|
textView.autoresizingMask = .width
|
||||||
|
textView.isHorizontallyResizable = true
|
||||||
|
if let textContainer = textView.textContainer {
|
||||||
|
textContainer.size = NSSize(width: minWidth, height: CGFloat.greatestFiniteMagnitude)
|
||||||
|
textContainer.widthTracksTextView = true
|
||||||
|
}
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
textView.widthAnchor.constraint(greaterThanOrEqualToConstant: minWidth),
|
||||||
|
textView.heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight)
|
||||||
|
])
|
||||||
|
return textView
|
||||||
|
}()
|
||||||
|
|
||||||
|
let scrollView: NSScrollView = {
|
||||||
|
let scrollView = NSScrollView()
|
||||||
|
scrollView.hasVerticalScroller = true
|
||||||
|
scrollView.borderType = .bezelBorder
|
||||||
|
return scrollView
|
||||||
|
}()
|
||||||
|
|
||||||
|
let discardButton: NSButton = {
|
||||||
|
let button = NSButton()
|
||||||
|
button.title = tr("macEditDiscard")
|
||||||
|
button.setButtonType(.momentaryPushIn)
|
||||||
|
button.bezelStyle = .rounded
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
|
let saveButton: NSButton = {
|
||||||
|
let button = NSButton()
|
||||||
|
button.title = tr("macEditSave")
|
||||||
|
button.setButtonType(.momentaryPushIn)
|
||||||
|
button.bezelStyle = .rounded
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
|
let tunnelsManager: TunnelsManager
|
||||||
|
let tunnel: TunnelContainer?
|
||||||
|
|
||||||
|
init(tunnelsManager: TunnelsManager, tunnel: TunnelContainer?) {
|
||||||
|
self.tunnelsManager = tunnelsManager
|
||||||
|
self.tunnel = tunnel
|
||||||
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func loadView() {
|
||||||
|
if let tunnel = tunnel, let tunnelConfiguration = tunnel.tunnelConfiguration {
|
||||||
|
nameRow.value = tunnel.name
|
||||||
|
publicKeyRow.value = tunnelConfiguration.interface.publicKey.base64EncodedString()
|
||||||
|
textView.string = tunnelConfiguration.asWgQuickConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollView.documentView = textView
|
||||||
|
|
||||||
|
saveButton.target = self
|
||||||
|
saveButton.action = #selector(saveButtonClicked)
|
||||||
|
|
||||||
|
discardButton.target = self
|
||||||
|
discardButton.action = #selector(discardButtonClicked)
|
||||||
|
|
||||||
|
let margin: CGFloat = 20
|
||||||
|
let internalSpacing: CGFloat = 10
|
||||||
|
|
||||||
|
let editorStackView = NSStackView(views: [nameRow, publicKeyRow, scrollView])
|
||||||
|
editorStackView.orientation = .vertical
|
||||||
|
editorStackView.setHuggingPriority(.defaultHigh, for: .horizontal)
|
||||||
|
editorStackView.spacing = internalSpacing
|
||||||
|
|
||||||
|
let buttonRowStackView = NSStackView()
|
||||||
|
buttonRowStackView.setViews([discardButton, saveButton], in: .trailing)
|
||||||
|
buttonRowStackView.orientation = .horizontal
|
||||||
|
buttonRowStackView.spacing = internalSpacing
|
||||||
|
|
||||||
|
let containerView = NSStackView(views: [editorStackView, buttonRowStackView])
|
||||||
|
containerView.orientation = .vertical
|
||||||
|
containerView.edgeInsets = NSEdgeInsets(top: margin, left: margin, bottom: margin, right: margin)
|
||||||
|
containerView.setHuggingPriority(.defaultHigh, for: .horizontal)
|
||||||
|
containerView.spacing = internalSpacing
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 180),
|
||||||
|
containerView.heightAnchor.constraint(greaterThanOrEqualToConstant: 240)
|
||||||
|
])
|
||||||
|
containerView.frame = NSRect(x: 0, y: 0, width: 480, height: 320)
|
||||||
|
|
||||||
|
self.view = containerView
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func saveButtonClicked() {
|
||||||
|
print("saveButtonClicked")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func discardButtonClicked() {
|
||||||
|
dismiss(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user