Merge branch 'confirm-quit'

This commit is contained in:
Davide De Rosa 2021-01-26 22:08:05 +01:00
commit db3b0c6a3c
11 changed files with 181 additions and 66 deletions

View File

@ -84,12 +84,24 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
// TransientStore.shared.service.preferences.confirmsQuit = true
guard TransientStore.shared.service.preferences.confirmsQuit ?? true else {
return .terminateNow
}
let alert = Macros.warning(
L10n.App.Menu.Quit.title(GroupConstants.App.name),
L10n.App.Menu.Quit.Messages.confirm
)
guard alert.presentModally(withOK: L10n.Core.Global.ok, cancel: L10n.Core.Global.cancel) else {
switch alert.presentModallyEx(withOK: L10n.Core.Global.ok, other1: L10n.Core.Global.cancel, other2: L10n.Core.Reddit.Buttons.never) {
case .alertSecondButtonReturn:
return .terminateCancel
case .alertThirdButtonReturn:
TransientStore.shared.service.preferences.confirmsQuit = false
break
default:
break
}
return .terminateNow
}

View File

@ -206,64 +206,137 @@
<scene sceneID="Hao-Zy-3Z2">
<objects>
<viewController id="0Rd-zE-HaE" customClass="PreferencesGeneralViewController" customModule="Passepartout" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="goM-B1-s6Q">
<rect key="frame" x="0.0" y="0.0" width="500" height="140"/>
<view key="view" misplaced="YES" id="goM-B1-s6Q">
<rect key="frame" x="0.0" y="0.0" width="500" height="200"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="YAc-Jc-giQ">
<rect key="frame" x="20" y="20" width="460" height="100"/>
<rect key="frame" x="20" y="20" width="460" height="160"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="f4O-ff-563">
<rect key="frame" x="58" y="83" width="402" height="18"/>
<buttonCell key="cell" type="check" title="&lt;login&gt;" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="gGh-4N-Oiq">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="toggleLaunchesOnLogin:" target="0Rd-zE-HaE" id="Cyo-8T-0H7"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KZa-SZ-feg">
<rect key="frame" x="58" y="23" width="402" height="18"/>
<buttonCell key="cell" type="check" title="&lt;resolve&gt;" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="pvb-NJ-IsO">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="toggleResolvesHostname:" target="0Rd-zE-HaE" id="b8J-e9-9rJ"/>
</connections>
</button>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="ebe-Av-HQp">
<rect key="frame" x="58" y="0.0" width="384" height="14"/>
<textFieldCell key="cell" controlSize="small" title="&lt;resolve_desc&gt;" id="a7v-LU-QXs">
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="hVP-2c-RhI">
<rect key="frame" x="58" y="60" width="404" height="14"/>
<textFieldCell key="cell" controlSize="small" title="&lt;login_desc&gt;" id="5hr-7m-H09">
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<stackView distribution="fillProportionally" orientation="vertical" alignment="leading" spacing="10" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5o7-rk-oAy">
<rect key="frame" x="0.0" y="0.0" width="460" height="160"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Cfi-RH-nA6">
<rect key="frame" x="60" y="113" width="400" height="47"/>
<subviews>
<button verticalHuggingPriority="751" translatesAutoresizingMaskIntoConstraints="NO" id="f4O-ff-563">
<rect key="frame" x="-2" y="30" width="402" height="18"/>
<buttonCell key="cell" type="check" title="&lt;login&gt;" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="gGh-4N-Oiq">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="toggleLaunchesOnLogin:" target="0Rd-zE-HaE" id="Cyo-8T-0H7"/>
</connections>
</button>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="hVP-2c-RhI">
<rect key="frame" x="-2" y="0.0" width="404" height="21"/>
<textFieldCell key="cell" controlSize="small" title="&lt;login_desc&gt;" id="5hr-7m-H09">
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="f4O-ff-563" firstAttribute="leading" secondItem="Cfi-RH-nA6" secondAttribute="leading" id="8I3-4Q-0em"/>
<constraint firstAttribute="trailing" secondItem="hVP-2c-RhI" secondAttribute="trailing" id="ElO-Fl-z04"/>
<constraint firstAttribute="bottom" secondItem="hVP-2c-RhI" secondAttribute="bottom" id="Htp-8a-Rhz"/>
<constraint firstItem="hVP-2c-RhI" firstAttribute="top" secondItem="f4O-ff-563" secondAttribute="bottom" constant="10" id="Q55-yA-Aga"/>
<constraint firstItem="f4O-ff-563" firstAttribute="top" secondItem="Cfi-RH-nA6" secondAttribute="top" id="VPB-Cm-gd2"/>
<constraint firstItem="hVP-2c-RhI" firstAttribute="leading" secondItem="f4O-ff-563" secondAttribute="leading" id="cHr-jB-mVR"/>
<constraint firstAttribute="trailing" secondItem="f4O-ff-563" secondAttribute="trailing" id="oo3-Hh-3H5"/>
</constraints>
</customView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="jED-i6-bcB">
<rect key="frame" x="60" y="57" width="400" height="46"/>
<subviews>
<button verticalHuggingPriority="751" translatesAutoresizingMaskIntoConstraints="NO" id="omo-s0-FIM">
<rect key="frame" x="-2" y="29" width="402" height="18"/>
<buttonCell key="cell" type="check" title="&lt;quit&gt;" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="JVO-Hq-DZp">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="toggleConfirmQuit:" target="0Rd-zE-HaE" id="lWF-9G-zpa"/>
</connections>
</button>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="Ehw-oG-ego">
<rect key="frame" x="-2" y="0.0" width="404" height="20"/>
<textFieldCell key="cell" controlSize="small" title="&lt;quit_desc&gt;" id="SuT-SW-vaS">
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="Ehw-oG-ego" secondAttribute="trailing" id="LCn-5F-xYP"/>
<constraint firstItem="omo-s0-FIM" firstAttribute="top" secondItem="jED-i6-bcB" secondAttribute="top" id="W3D-Ok-HFv"/>
<constraint firstItem="Ehw-oG-ego" firstAttribute="leading" secondItem="omo-s0-FIM" secondAttribute="leading" id="bSB-7k-XMT"/>
<constraint firstItem="omo-s0-FIM" firstAttribute="leading" secondItem="jED-i6-bcB" secondAttribute="leading" id="hoc-W9-8Oy"/>
<constraint firstAttribute="trailing" secondItem="omo-s0-FIM" secondAttribute="trailing" id="l6d-Rz-gIg"/>
<constraint firstItem="Ehw-oG-ego" firstAttribute="top" secondItem="omo-s0-FIM" secondAttribute="bottom" constant="10" id="mYe-9C-V0o"/>
<constraint firstAttribute="bottom" secondItem="Ehw-oG-ego" secondAttribute="bottom" id="vZE-5w-TW5"/>
</constraints>
</customView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="rek-P9-cai">
<rect key="frame" x="60" y="0.0" width="400" height="47"/>
<subviews>
<button verticalHuggingPriority="751" translatesAutoresizingMaskIntoConstraints="NO" id="KZa-SZ-feg">
<rect key="frame" x="-2" y="30" width="402" height="18"/>
<buttonCell key="cell" type="check" title="&lt;resolve&gt;" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="pvb-NJ-IsO">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="toggleResolvesHostname:" target="0Rd-zE-HaE" id="b8J-e9-9rJ"/>
</connections>
</button>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="ebe-Av-HQp">
<rect key="frame" x="-2" y="0.0" width="384" height="21"/>
<textFieldCell key="cell" controlSize="small" title="&lt;resolve_desc&gt;" id="a7v-LU-QXs">
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="KZa-SZ-feg" secondAttribute="trailing" id="H4D-px-UFg"/>
<constraint firstAttribute="bottom" secondItem="ebe-Av-HQp" secondAttribute="bottom" id="JRl-NG-PDi"/>
<constraint firstItem="ebe-Av-HQp" firstAttribute="top" secondItem="KZa-SZ-feg" secondAttribute="bottom" constant="10" id="MmZ-6z-Dzh"/>
<constraint firstItem="KZa-SZ-feg" firstAttribute="top" secondItem="rek-P9-cai" secondAttribute="top" id="UHA-p9-dtI"/>
<constraint firstItem="KZa-SZ-feg" firstAttribute="leading" secondItem="rek-P9-cai" secondAttribute="leading" id="mQN-gn-Xrd"/>
<constraint firstItem="ebe-Av-HQp" firstAttribute="leading" secondItem="KZa-SZ-feg" secondAttribute="leading" id="wGn-il-X3T"/>
<constraint firstAttribute="trailing" secondItem="ebe-Av-HQp" secondAttribute="trailing" constant="20" symbolic="YES" id="yql-ee-w0A"/>
</constraints>
</customView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="Cfi-RH-nA6" secondAttribute="trailing" id="106-QG-puH"/>
<constraint firstItem="rek-P9-cai" firstAttribute="leading" secondItem="Cfi-RH-nA6" secondAttribute="leading" id="3bC-kT-UC3"/>
<constraint firstAttribute="trailing" secondItem="jED-i6-bcB" secondAttribute="trailing" id="GmI-JA-d2F"/>
<constraint firstItem="Cfi-RH-nA6" firstAttribute="leading" secondItem="5o7-rk-oAy" secondAttribute="leading" constant="60" id="HmP-zr-vZT"/>
<constraint firstItem="jED-i6-bcB" firstAttribute="leading" secondItem="Cfi-RH-nA6" secondAttribute="leading" id="Skr-oN-cV8"/>
</constraints>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
</subviews>
<constraints>
<constraint firstItem="hVP-2c-RhI" firstAttribute="leading" secondItem="f4O-ff-563" secondAttribute="leading" id="0fY-C7-SIE"/>
<constraint firstItem="f4O-ff-563" firstAttribute="leading" secondItem="YAc-Jc-giQ" secondAttribute="leading" constant="60" id="1yz-pv-RXL"/>
<constraint firstAttribute="trailing" secondItem="hVP-2c-RhI" secondAttribute="trailing" id="AeD-dF-hsn"/>
<constraint firstItem="ebe-Av-HQp" firstAttribute="top" secondItem="KZa-SZ-feg" secondAttribute="bottom" constant="10" id="Amg-4a-O5i"/>
<constraint firstItem="KZa-SZ-feg" firstAttribute="top" secondItem="hVP-2c-RhI" secondAttribute="bottom" constant="20" id="EXZ-73-1Fs"/>
<constraint firstItem="hVP-2c-RhI" firstAttribute="top" secondItem="f4O-ff-563" secondAttribute="bottom" constant="10" id="EYa-vv-GeM"/>
<constraint firstAttribute="trailing" secondItem="ebe-Av-HQp" secondAttribute="trailing" constant="20" symbolic="YES" id="O9Z-6t-Zyn"/>
<constraint firstItem="ebe-Av-HQp" firstAttribute="leading" secondItem="KZa-SZ-feg" secondAttribute="leading" id="eGw-4w-cj0"/>
<constraint firstAttribute="trailing" secondItem="KZa-SZ-feg" secondAttribute="trailing" id="f7h-Vt-bC3"/>
<constraint firstAttribute="trailing" secondItem="f4O-ff-563" secondAttribute="trailing" id="i0y-oH-yMb"/>
<constraint firstAttribute="bottom" secondItem="ebe-Av-HQp" secondAttribute="bottom" id="inV-3t-IGp"/>
<constraint firstItem="f4O-ff-563" firstAttribute="top" secondItem="YAc-Jc-giQ" secondAttribute="top" id="kec-0I-xzK"/>
<constraint firstItem="KZa-SZ-feg" firstAttribute="leading" secondItem="f4O-ff-563" secondAttribute="leading" id="vxY-bg-Mat"/>
<constraint firstItem="5o7-rk-oAy" firstAttribute="leading" secondItem="YAc-Jc-giQ" secondAttribute="leading" id="4Qo-xj-Bjr"/>
<constraint firstItem="5o7-rk-oAy" firstAttribute="top" secondItem="YAc-Jc-giQ" secondAttribute="top" id="Hch-47-S9C"/>
<constraint firstAttribute="bottom" secondItem="5o7-rk-oAy" secondAttribute="bottom" id="pJn-bI-w5I"/>
<constraint firstAttribute="trailing" secondItem="5o7-rk-oAy" secondAttribute="trailing" id="t5V-mw-69u"/>
</constraints>
</customView>
</subviews>
@ -275,8 +348,10 @@
</constraints>
</view>
<connections>
<outlet property="checkConfirmQuit" destination="omo-s0-FIM" id="5Xh-1H-JM5"/>
<outlet property="checkLaunchOnLogin" destination="f4O-ff-563" id="TqA-pe-SY2"/>
<outlet property="checkResolveHostname" destination="KZa-SZ-feg" id="fQP-vS-LY7"/>
<outlet property="labelConfirmQuit" destination="Ehw-oG-ego" id="KAb-BP-KXG"/>
<outlet property="labelLaunchOnLogin" destination="hVP-2c-RhI" id="ACR-5L-EHH"/>
<outlet property="labelResolveHostname" destination="ebe-Av-HQp" id="IaQ-Pn-dHd"/>
</connections>

View File

@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Country flags in provider infrastructure menu.
- Support DNS over HTTPS/TLS in "Network settings". [#91](https://github.com/passepartoutvpn/passepartout-apple/issues/91)
- Menu tooltip describing active profile and status.
- Make "Confirm quit" a preference.
### Changed

View File

@ -74,14 +74,18 @@ extension NSAlert {
}
func presentModally(withOK okTitle: String, cancel cancelTitle: String?, dummy dummyTitle: String? = nil) -> Bool {
return presentModallyEx(withOK: okTitle, other1: cancelTitle, other2: dummyTitle) == .alertFirstButtonReturn
}
func presentModallyEx(withOK okTitle: String, other1 other1Title: String?, other2 other2Title: String? = nil) -> NSApplication.ModalResponse {
addButton(withTitle: okTitle)
if let cancelTitle = cancelTitle {
addButton(withTitle: cancelTitle)
if let other1Title = other1Title {
addButton(withTitle: other1Title)
}
if let dummyTitle = dummyTitle {
addButton(withTitle: dummyTitle)
if let other2Title = other2Title {
addButton(withTitle: other2Title)
}
return runModal() == .alertFirstButtonReturn
return runModal()
}
}

View File

@ -136,11 +136,15 @@ internal enum L10n {
}
}
internal enum Preferences {
/// %@ Preferences
internal static func title(_ p1: Any) -> String {
return L10n.tr("App", "preferences.title", String(describing: p1))
}
/// Preferences
internal static let title = L10n.tr("App", "preferences.title")
internal enum Cells {
internal enum ConfirmQuit {
/// Confirm quit
internal static let caption = L10n.tr("App", "preferences.cells.confirm_quit.caption")
/// Check to present a quit confirmation alert.
internal static let footer = L10n.tr("App", "preferences.cells.confirm_quit.footer")
}
internal enum LaunchesOnLogin {
/// Launch on login
internal static let caption = L10n.tr("App", "preferences.cells.launches_on_login.caption")

View File

@ -45,7 +45,7 @@ class WindowManager: NSObject {
// preferences = presentWindowController(
// StoryboardScene.Preferences.initialScene.instantiate(),
// existing: preferences,
// title: L10n.App.Preferences.title(GroupConstants.App.name)
// title: L10n.App.Preferences.title
// )
// return preferences
// }

View File

@ -32,6 +32,10 @@ class PreferencesGeneralViewController: NSViewController {
@IBOutlet private weak var labelLaunchOnLogin: NSTextField!
@IBOutlet private weak var checkConfirmQuit: NSButton!
@IBOutlet private weak var labelConfirmQuit: NSTextField!
@IBOutlet private weak var checkResolveHostname: NSButton!
@IBOutlet private weak var labelResolveHostname: NSTextField!
@ -43,10 +47,13 @@ class PreferencesGeneralViewController: NSViewController {
checkLaunchOnLogin.title = L10n.App.Preferences.Cells.LaunchesOnLogin.caption
labelLaunchOnLogin.stringValue = L10n.App.Preferences.Cells.LaunchesOnLogin.footer
checkConfirmQuit.title = L10n.App.Preferences.Cells.ConfirmQuit.caption
labelConfirmQuit.stringValue = L10n.App.Preferences.Cells.ConfirmQuit.footer
checkResolveHostname.title = L10n.Core.Service.Cells.VpnResolvesHostname.caption
labelResolveHostname.stringValue = L10n.Core.Service.Sections.VpnResolvesHostname.footer
checkLaunchOnLogin.state = (service.preferences.launchesOnLogin ?? true) ? .on : .off
checkConfirmQuit.state = (service.preferences.confirmsQuit ?? true) ? .on : .off
checkResolveHostname.state = service.preferences.resolvesHostname ? .on : .off
}
@ -61,6 +68,10 @@ class PreferencesGeneralViewController: NSViewController {
SMLoginItemSetEnabled(AppConstants.App.appLauncherId as CFString, service.preferences.launchesOnLogin ?? true)
}
@IBAction private func toggleConfirmQuit(_ sender: Any?) {
service.preferences.confirmsQuit = (checkConfirmQuit.state == .on)
}
@IBAction private func toggleResolvesHostname(_ sender: Any?) {
service.preferences.resolvesHostname = (checkResolveHostname.state == .on)
cycleVPNIfNeeded()

View File

@ -32,7 +32,7 @@ class PreferencesViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
title = L10n.App.Preferences.title(GroupConstants.App.name)
title = L10n.App.Preferences.title
let labels = [
L10n.App.Preferences.Sections.General.header,
L10n.Core.Service.Sections.Diagnostics.header

View File

@ -50,10 +50,12 @@
"network_settings.dns.cells.domains.title" = "Domains";
"network_settings.proxy.cells.bypass_domains.title" = "Bypass domains";
"preferences.title" = "%@ Preferences";
"preferences.title" = "Preferences";
"preferences.sections.general.header" = "General";
"preferences.cells.launches_on_login.caption" = "Launch on login";
"preferences.cells.launches_on_login.footer" = "Check to automatically launch the app on boot or login.";
"preferences.cells.confirm_quit.caption" = "Confirm quit";
"preferences.cells.confirm_quit.footer" = "Check to present a quit confirmation alert.";
"trusted.columns.trust.title" = "Trust";
"trusted.ethernet.title" = "Trust wired connections";

View File

@ -50,10 +50,12 @@
"network_settings.dns.cells.addresses.title" = "Server";
"network_settings.proxy.cells.bypass_domains.title" = "Dominii ignorati";
"preferences.title" = "Preferenze %@";
"preferences.title" = "Preferenze";
"preferences.sections.general.header" = "Generale";
"preferences.cells.launches_on_login.caption" = "Apri al login";
"preferences.cells.launches_on_login.footer" = "Seleziona per aprire automaticamente l'app all'avvio o al login.";
"preferences.cells.confirm_quit.caption" = "Conferma uscita";
"preferences.cells.confirm_quit.footer" = "Seleziona per confermare l'uscita dall'applicazione.";
"trusted.columns.trust.title" = "Sicura";
"trusted.ethernet.title" = "Connessioni cablate sicure";

View File

@ -28,6 +28,8 @@ import Foundation
public protocol Preferences {
var launchesOnLogin: Bool? { get }
var confirmsQuit: Bool? { get }
var resolvesHostname: Bool { get }
var disconnectsOnSleep: Bool { get }
@ -35,6 +37,8 @@ public protocol Preferences {
public class EditablePreferences: Preferences, Codable {
public var launchesOnLogin: Bool? = false
public var confirmsQuit: Bool? = true
public var resolvesHostname: Bool = true