Skip to content

Commit

Permalink
feat: improves PreferencesPanel UX, partially implements #49
Browse files Browse the repository at this point in the history
implements requested changes from Code-Review and contains also refactoring and more features and improvements
- Application Wide: changes all exceptions to NSError (required for error handling via sheet in panel)
- PreferencesPanel: adds display of current value of Sliders
- PreferencesPanel: adds validation with visualisation for TextFields (whitelist for KeyCode and ranges for maxScreenUsage & windowDisplayDelay) during editing, window open and close
- PreferencesPanel: adds error sheet (in-window modal) at all known possible errors to display error information and ask the user how to proceed
- PreferencesPanel: removes Restart button (and relaunch code in Application)
- PreferencesPanel: adds Preferences being now live
- PreferencesPanel: removes invisibleText workaround
- PreferencesPanel: adds TextField value save on each keyDown event (if valid and changed)
- PreferencesPanel: adds window activation/deactivation
- PreferencesPanel: changes label texts of some Preferences
- ...
  • Loading branch information
gingerr authored and lwouis committed Nov 11, 2019
1 parent 59fc712 commit 65327c2
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 138 deletions.
4 changes: 4 additions & 0 deletions alt-tab-macos.xcodeproj/project.pbxproj
Expand Up @@ -21,6 +21,7 @@
D04BA9CCE02D30C8164A552A /* SystemPermissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA2D2AD6B1CCA3F3A4DD7 /* SystemPermissions.swift */; };
D04BAD4DE538FDF7E7532EE2 /* Labels.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BAD32E130E4A061DC8332 /* Labels.swift */; };
D04BAEF78503D7A2CEFB9E9E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BAA44C837F3A67403B9DB /* main.swift */; };
F02981D5D1E1F62074801CAE /* PreferencesPanelNSTextFieldEditable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0298F4D7927608A986C320D /* PreferencesPanelNSTextFieldEditable.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand Down Expand Up @@ -72,6 +73,7 @@
D04BAF076A30A1BAFEDBEA66 /* 5 windows - 2 lines.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "5 windows - 2 lines.jpg"; sourceTree = "<group>"; };
D04BAF249324297C07E31164 /* frontpage.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = frontpage.jpg; sourceTree = "<group>"; };
D04BAFA277EAE3BDDDB61110 /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
F0298F4D7927608A986C320D /* PreferencesPanelNSTextFieldEditable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesPanelNSTextFieldEditable.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -172,6 +174,7 @@
D04BA3F15EAE8D8C39B6F2CF /* Screen.swift */,
D04BA2D2AD6B1CCA3F3A4DD7 /* SystemPermissions.swift */,
D04BA86768C6503A11ED81FC /* Extensions.swift */,
F0298F4D7927608A986C320D /* PreferencesPanelNSTextFieldEditable.swift */,
);
path = logic;
sourceTree = "<group>";
Expand Down Expand Up @@ -302,6 +305,7 @@
D04BA02DD4152997C32CF50B /* StatusItem.swift in Sources */,
D04BA0F3D46BC79544E2B930 /* Extensions.swift in Sources */,
D04BAD4DE538FDF7E7532EE2 /* Labels.swift in Sources */,
F02981D5D1E1F62074801CAE /* PreferencesPanelNSTextFieldEditable.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
28 changes: 23 additions & 5 deletions alt-tab-macos/logic/Extensions.swift
Expand Up @@ -19,15 +19,11 @@ extension Optional {
return value
case .none:
Thread.callStackSymbols.forEach { print($0) }
throw "Optional contained nil"
throw NSError.make(domain: "Optional", message: "Optional contained nil")
}
}
}

// allow String to be treated as Error (e.g. throw "explanation")
extension String: Error {
}

// add String constructor from CGFloat that round up at 1 decimal
extension String {
init?(_ cgFloat: CGFloat) {
Expand All @@ -39,3 +35,25 @@ extension String {
self.init(string)
}
}

// add recursive lookup in subviews for specific type
extension NSView {
func findNestedViews<T: NSView>(subclassOf: T.Type) -> [T] {
return recursiveSubviews.compactMap { $0 as? T }
}

var recursiveSubviews: [NSView] {
return subviews + subviews.flatMap { $0.recursiveSubviews }
}
}

// add convenience to NSError
extension NSError {
class func make(domain: String, message: String, code: Int = 9999) -> NSError {
return NSError(
domain: domain,
code: code,
userInfo: [NSLocalizedDescriptionKey: message, NSLocalizedFailureReasonErrorKey: message]
)
}
}
2 changes: 1 addition & 1 deletion alt-tab-macos/logic/Preferences.swift
Expand Up @@ -102,7 +102,7 @@ class Preferences {
let p = try showOnScreenMacro.labelToMacro[value].orThrow()
showOnScreen = p.preferences
default:
throw "Tried to update an unknown preference: '\(valueName)' = '\(value)'"
throw NSError.make(domain: "Preferences", message: "Tried to update an unknown preference: '\(valueName)' = '\(value)'")
}
rawValues[valueName] = value
}
Expand Down
34 changes: 34 additions & 0 deletions alt-tab-macos/logic/PreferencesPanelNSTextFieldEditable.swift
@@ -0,0 +1,34 @@
import Cocoa

class PreferencesPanelNSTextFieldEditable: NSTextField, NSTextFieldDelegate {

var validationHandler: ((String)->Bool)?

// protocol method
func controlTextDidChange(_ obj: Notification) {
visualizeValidationState(isValid())
let textField = obj.object as! PreferencesPanelNSTextFieldEditable
sendAction(textField.action, to: textField.target)
}

// custom method
func visualizeValidationState(_ isValid: Bool) -> Void {
if !isValid {
wantsLayer = true
layer?.borderColor = NSColor.systemRed.cgColor
layer?.borderWidth = 1
} else {
wantsLayer = false
}
}

// custom method
func isValid() -> Bool {
if let handler = validationHandler {
return handler(stringValue)
}

return true
}

}
23 changes: 11 additions & 12 deletions alt-tab-macos/ui/Application.swift
Expand Up @@ -4,7 +4,6 @@ import Cocoa
class Application: NSApplication, NSApplicationDelegate, NSWindowDelegate {
static let name = "AltTab"
var statusItem: NSStatusItem?
var backgroundView: NSVisualEffectView?
var thumbnailsPanel: ThumbnailsPanel?
var preferencesPanel: PreferencesPanel?
var selectedOpenWindow: Int = 0
Expand All @@ -28,11 +27,15 @@ class Application: NSApplication, NSApplicationDelegate, NSWindowDelegate {
SystemPermissions.ensureAccessibilityCheckboxIsChecked()
Preferences.loadFromDiskAndUpdateValues()
statusItem = StatusItem.make(self)
thumbnailsPanel = ThumbnailsPanel(self)
preferencesPanel = PreferencesPanel()
initPreferencesDependentComponents()
Keyboard.listenToGlobalEvents(self)
}

// we put application code here which should be executed on init() and Preferences change
func initPreferencesDependentComponents() {
thumbnailsPanel = ThumbnailsPanel(self)
}

func showUiOrSelectNext() {
debugPrint("showUiOrSelectNext")
showUiOrCycleSelection(1)
Expand All @@ -58,7 +61,11 @@ class Application: NSApplication, NSApplicationDelegate, NSWindowDelegate {
}
}

@objc func showPreferencesPanel() {
@objc
func showPreferencesPanel() {
if preferencesPanel == nil {
preferencesPanel = PreferencesPanel()
}
Screen.showPanel(preferencesPanel!, Screen.preferredScreen(), .appleCentered)
}

Expand Down Expand Up @@ -125,12 +132,4 @@ class Application: NSApplication, NSApplicationDelegate, NSWindowDelegate {
return openWindows.count > selectedOpenWindow ? openWindows[selectedOpenWindow] : nil
}

func relaunch(afterDelay seconds: TimeInterval = 0.5) -> Never {
let task = Process()
task.launchPath = "/bin/sh"
task.arguments = ["-c", "sleep \(seconds); open \"\(Bundle.main.bundlePath)\""]
task.launch()
self.terminate(nil)
exit(0)
}
}

0 comments on commit 65327c2

Please sign in to comment.