Skip to content

Commit

Permalink
feat: add preference to start at login (closes #159)
Browse files Browse the repository at this point in the history
  • Loading branch information
lwouis committed Mar 10, 2020
1 parent bddb6fa commit 982fe6c
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 53 deletions.
8 changes: 4 additions & 4 deletions alt-tab-macos.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
D04BA084CD1236EC78D90A01 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D04BACCBE5F97BE9B6CA645B /* Localizable.strings */; };
D04BA100BD0F47828EB649FF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D04BAAEC2847830A3991F8D1 /* InfoPlist.strings */; };
D04BA14D93726795A6937832 /* LabelAndControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA2526DC6726E0F7ACF7C /* LabelAndControl.swift */; };
D04BA15A1B0C4871EA7CB899 /* ShortcutsTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BACE22DC907F03D193075 /* ShortcutsTab.swift */; };
D04BA15A1B0C4871EA7CB899 /* GeneralTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BACE22DC907F03D193075 /* GeneralTab.swift */; };
D04BA1B133D53572D7B312C2 /* CollectionViewItemFontIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA1DF8CAB2FAB7FE9244B /* CollectionViewItemFontIcon.swift */; };
D04BA1CEC6B9C8945FEC8740 /* CollectionViewItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA258B56193958D60978A /* CollectionViewItemView.swift */; };
D04BA26A691D56031FCCF00C /* Sysctl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA8DB8AA7E5570DAC568A /* Sysctl.swift */; };
Expand Down Expand Up @@ -148,7 +148,7 @@
D04BACABD048E62EBE4576CC /* DebugProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DebugProfile.swift; sourceTree = "<group>"; };
D04BACB97A5895839BCB14BD /* es */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = es; path = InfoPlist.strings; sourceTree = "<group>"; };
D04BACD976030676FD0761D5 /* Windows.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Windows.swift; sourceTree = "<group>"; };
D04BACE22DC907F03D193075 /* ShortcutsTab.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortcutsTab.swift; sourceTree = "<group>"; };
D04BACE22DC907F03D193075 /* GeneralTab.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralTab.swift; sourceTree = "<group>"; };
D04BACEE8D430B8CAAD8C4CD /* BoldLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoldLabel.swift; sourceTree = "<group>"; };
D04BAD1297730B191E96E7FE /* CollectionViewItemTitle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewItemTitle.swift; sourceTree = "<group>"; };
D04BAD241A6928F45355B315 /* es */ = {isa = PBXFileReference; fileEncoding = 2483028224; lastKnownFileType = text.plist.strings; name = es; path = Localizable.strings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -310,7 +310,7 @@
children = (
D04BA64F1F344007EA13BA05 /* AppearanceTab.swift */,
D04BA4A26987F67DD94C827F /* AboutTab.swift */,
D04BACE22DC907F03D193075 /* ShortcutsTab.swift */,
D04BACE22DC907F03D193075 /* GeneralTab.swift */,
D04BAD60C97E609A759E721E /* UpdatesTab.swift */,
);
path = tabs;
Expand Down Expand Up @@ -685,7 +685,7 @@
D04BADBCF20CD72057E7CF09 /* TabViewItem.swift in Sources */,
D04BA7BE7F3DD24D58ACE942 /* AppearanceTab.swift in Sources */,
D04BAD1BE9DC22C48C53D195 /* AboutTab.swift in Sources */,
D04BA15A1B0C4871EA7CB899 /* ShortcutsTab.swift in Sources */,
D04BA15A1B0C4871EA7CB899 /* GeneralTab.swift in Sources */,
D04BA8092885B40CE3527370 /* UpdatesTab.swift in Sources */,
D04BAD451966B43720120D2E /* Menubar.swift in Sources */,
D04BA2E64C59D96F6EB27D9D /* FeedbackWindow.swift in Sources */,
Expand Down
39 changes: 39 additions & 0 deletions src/api-wrappers/HelperExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,42 @@ extension Array {
group.wait()
}
}

// allow using a closure for NSControl action, instead of selector
class SelectorWrapper<T> {
let selector: Selector
let closure: (T) -> Void

init(withClosure closure: @escaping (T) -> Void) {
self.selector = #selector(callClosure)
self.closure = closure
}

@objc
private func callClosure(sender: AnyObject) {
closure(sender as! T)
}
}

fileprivate var handle: Int = 0

typealias ActionClosure = (NSControl) -> Void

extension NSControl {
var onAction: ActionClosure? {
get {
return nil
}
set {
if let newValue = newValue {
let selectorWrapper = SelectorWrapper<NSControl>(withClosure: newValue)
objc_setAssociatedObject(self, &handle, selectorWrapper, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
action = selectorWrapper.selector
target = selectorWrapper
} else {
action = nil
target = nil
}
}
}
}
2 changes: 2 additions & 0 deletions src/logic/Preferences.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class Preferences {
"theme": MacroPreferences.themeList.keys.first!,
"showOnScreen": MacroPreferences.showOnScreenList.keys.first!,
"hideSpaceNumberLabels": false,
"startAtLogin": true,
]

// constant values
Expand All @@ -39,6 +40,7 @@ class Preferences {
static var tabKeyCode: UInt16 { UInt16(defaults.integer(forKey: "tabKeyCode")) }
static var windowDisplayDelay: DispatchTimeInterval { DispatchTimeInterval.milliseconds(defaults.integer(forKey: "windowDisplayDelay")) }
static var hideSpaceNumberLabels: Bool { defaults.bool(forKey: "hideSpaceNumberLabels") }
static var startAtLogin: Bool { defaults.bool(forKey: "startAtLogin") }

// macro values
static var theme: Theme { MacroPreferences.themeList[defaults.string(forKey: "theme")!]! }
Expand Down
1 change: 1 addition & 0 deletions src/ui/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class App: NSApplication, NSApplicationDelegate {
static let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as! String
static let licence = Bundle.main.object(forInfoDictionaryKey: "NSHumanReadableCopyright") as! String
static let repository = "https://github.com/lwouis/alt-tab-macos"
static let url = URL(fileURLWithPath: Bundle.main.bundlePath) as CFURL
var statusItem: NSStatusItem?
var thumbnailsPanel: ThumbnailsPanel?
var preferencesWindow: PreferencesWindow?
Expand Down
19 changes: 7 additions & 12 deletions src/ui/preferences-window/LabelAndControl.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import Cocoa

class LabelAndControl: NSObject {
static var callbackTarget: PreferencesWindow!

static func makeLabelWithInput(_ labelText: String, _ rawName: String, _ width: CGFloat, _ suffixText: String? = nil, _ suffixUrl: String? = nil, _ validator: ((String) -> Bool)? = nil) -> [NSView] {
let input = TextField(Preferences.getAsString(rawName)!)
input.validationHandler = validator
Expand All @@ -13,10 +11,10 @@ class LabelAndControl: NSObject {
return [views[0], NSStackView(views: [views[1], makeSuffix(rawName, suffixText!, suffixUrl)])]
}

static func makeLabelWithCheckbox(_ labelText: String, _ rawName: String) -> [NSView] {
static func makeLabelWithCheckbox(_ labelText: String, _ rawName: String, extraAction: ActionClosure? = nil) -> [NSView] {
let checkbox = NSButton(checkboxWithTitle: "", target: nil, action: nil)
setControlValue(checkbox, Preferences.getAsString(rawName)!)
return makeLabelWithProvidedControl(labelText, rawName, checkbox)
return makeLabelWithProvidedControl(labelText, rawName, checkbox, extraAction: extraAction)
}

static func makeLabelWithDropdown(_ labelText: String, _ rawName: String, _ values: [String], _ suffixText: String? = nil) -> [NSView] {
Expand All @@ -40,19 +38,16 @@ class LabelAndControl: NSObject {
return makeLabelWithProvidedControl(labelText, rawName, slider, suffixText)
}

static func makeLabelWithProvidedControl(_ labelText: String?, _ rawName: String, _ control: NSControl, _ suffixText: String? = nil, _ suffixUrl: String? = nil) -> [NSView] {
static func makeLabelWithProvidedControl(_ labelText: String?, _ rawName: String, _ control: NSControl, _ suffixText: String? = nil, _ suffixUrl: String? = nil, extraAction: ActionClosure? = nil) -> [NSView] {
let label = makeLabel(labelText, rawName)
control.identifier = NSUserInterfaceItemIdentifier(rawName)
control.target = self
control.action = #selector(controlWasChanged)
control.onAction = {
PreferencesWindow.controlWasChanged($0)
extraAction?($0)
}
return [label, control, suffixText != nil ? makeSuffix(rawName, suffixText!, suffixUrl) : NSView()]
}

@objc
static func controlWasChanged(senderControl: NSControl) {
callbackTarget.controlWasChanged(senderControl)
}

private static func makeLabel(_ labelText: String?, _ rawName: String) -> NSTextField {
let label = NSTextField(wrappingLabelWithString: labelText != nil ? labelText! + ": " : "")
label.fit()
Expand Down
5 changes: 2 additions & 3 deletions src/ui/preferences-window/PreferencesWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ class PreferencesWindow: NSWindow {

override init(contentRect: NSRect, styleMask style: StyleMask, backing backingStoreType: BackingStoreType, defer flag: Bool) {
super.init(contentRect: .zero, styleMask: style, backing: backingStoreType, defer: flag)
LabelAndControl.callbackTarget = self
setupWindow()
setupTabViews()
}
Expand All @@ -15,7 +14,7 @@ class PreferencesWindow: NSWindow {
makeKeyAndOrderFront(nil)
}

func controlWasChanged(_ senderControl: NSControl) {
@objc static func controlWasChanged(_ senderControl: NSControl) {
let newValue = LabelAndControl.getControlValue(senderControl)
LabelAndControl.updateControlExtras(senderControl, newValue)
Preferences.set(senderControl.identifier!.rawValue, newValue)
Expand All @@ -31,7 +30,7 @@ class PreferencesWindow: NSWindow {
private func setupTabViews() {
contentViewController = tabViewController
tabViewController.tabStyle = .toolbar
tabViewController.addTabViewItem(ShortcutsTab.make())
tabViewController.addTabViewItem(GeneralTab.make())
tabViewController.addTabViewItem(AppearanceTab.make())
tabViewController.addTabViewItem(UpdatesTab.make())
tabViewController.addTabViewItem(AboutTab.make())
Expand Down
60 changes: 60 additions & 0 deletions src/ui/preferences-window/tabs/GeneralTab.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Cocoa

class GeneralTab {
private static let rowHeight = CGFloat(22) // height of the "Tab key" input

static func make() -> NSTabViewItem {
return TabViewItem.make(NSLocalizedString("General", comment: ""), NSImage.preferencesGeneralName, makeView())
}

private static func makeView() -> NSGridView {
// TODO: make the validators be a part of each Preference
let tabKeyCodeValidator: ((String) -> Bool) = {
guard let int = Int($0) else {
return false
}
// non-special keys (mac & pc keyboards): https://eastmanreference.com/complete-list-of-applescript-key-codes
var whitelistedKeycodes: [Int] = Array(0...53)
whitelistedKeycodes.append(contentsOf: [65, 67, 69, 75, 76, 78, ])
whitelistedKeycodes.append(contentsOf: Array(81...89))
whitelistedKeycodes.append(contentsOf: [91, 92, 115, 116, 117, 119, 121])
whitelistedKeycodes.append(contentsOf: Array(123...126))
return whitelistedKeycodes.contains(int)
}

let startAtLogin = LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Start at login", comment: ""), "startAtLogin", extraAction: startAtLoginCallback)
let view = GridView.make([
startAtLogin,
LabelAndControl.makeLabelWithDropdown(NSLocalizedString("Alt key", comment: ""), "metaKey", MacroPreferences.metaKeyList.values.map { $0.label }),
LabelAndControl.makeLabelWithInput(NSLocalizedString("Tab key", comment: ""), "tabKeyCode", 33, NSLocalizedString("KeyCodes Reference", comment: ""), "https://eastmanreference.com/complete-list-of-applescript-key-codes", tabKeyCodeValidator),
])
view.column(at: 0).xPlacement = .trailing
view.rowAlignment = .lastBaseline
view.setRowsHeight(rowHeight)
view.fit()
setLoginItemIfCheckboxIsOn(startAtLogin[1] as! NSButton)
return view
}

private static func setLoginItemIfCheckboxIsOn(_ startAtLoginCheckbox: NSButton) {
if startAtLoginCheckbox.state == .on {
startAtLoginCallback(startAtLoginCheckbox)
}
}

// adding/removing login item depending on the checkbox state
@available(OSX, deprecated: 10.11)
@objc static func startAtLoginCallback(_ sender: NSControl) {
let loginItems = LSSharedFileListCreate(nil, kLSSharedFileListSessionLoginItems.takeRetainedValue(), nil).takeRetainedValue()
let loginItemsSnapshot = LSSharedFileListCopySnapshot(loginItems, nil).takeRetainedValue() as! [LSSharedFileListItem]
if (sender as! NSButton).state == .on {
LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst.takeRetainedValue(), nil, nil, App.url, nil, nil)
} else {
loginItemsSnapshot.forEach {
if CFEqual(LSSharedFileListItemCopyResolvedURL($0, 0, nil).takeRetainedValue(), App.url) {
LSSharedFileListItemRemove(loginItems, $0)
}
}
}
}
}
34 changes: 0 additions & 34 deletions src/ui/preferences-window/tabs/ShortcutsTab.swift

This file was deleted.

0 comments on commit 982fe6c

Please sign in to comment.