diff --git a/.gitignore b/.gitignore index 0a78a75f..38fe947c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /.idea/ xcuserdata/ +xcshareddata/ /node_modules/ /build/ diff --git a/alt-tab-macos.xcodeproj/project.pbxproj b/alt-tab-macos.xcodeproj/project.pbxproj index e5e47652..c66d3907 100644 --- a/alt-tab-macos.xcodeproj/project.pbxproj +++ b/alt-tab-macos.xcodeproj/project.pbxproj @@ -7,41 +7,15 @@ objects = { /* Begin PBXBuildFile section */ - D04BA0B1D77CA1466BFC13BC /* 3 windows - 1 line - tall window.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D04BAD82F652C4ED5D8BF7AE /* 3 windows - 1 line - tall window.jpg */; }; - D04BA183207E20AC28BF4F20 /* dark-background.png in Resources */ = {isa = PBXBuildFile; fileRef = D04BA8DDB96B0A60C3394351 /* dark-background.png */; }; D04BA20D4A240843293B3B52 /* Cell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA56355579F78776E6D51 /* Cell.swift */; }; - D04BA21CD515ACDBCC4437E6 /* commitlint.config.js in Sources */ = {isa = PBXBuildFile; fileRef = D04BAC02D60EF22D9CC7D969 /* commitlint.config.js */; }; - D04BA21F728100990BA0D007 /* 6 windows - 1 line.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D04BA2C7B51F68651B3C60E2 /* 6 windows - 1 line.jpg */; }; D04BA278D9EFA568C8D18A4C /* WindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BAD1BED44EAEB77FED8A4 /* WindowManager.swift */; }; - D04BA2B84D062AA0EB970CFA /* CHANGELOG.md in Sources */ = {isa = PBXBuildFile; fileRef = D04BAFA277EAE3BDDDB61110 /* CHANGELOG.md */; }; D04BA3261C7DA5F48310E654 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA90C6C36DB1D65BC2B66 /* Application.swift */; }; - D04BA35E037C9E1C3B26F296 /* 5 windows - 2 lines.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D04BAF076A30A1BAFEDBEA66 /* 5 windows - 2 lines.jpg */; }; - D04BA3999A3D6A308EA41039 /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = D04BAD40CE2D3A8AAC3819D0 /* .gitignore */; }; - D04BA424348403E28A5EEB40 /* package-lock.json in Resources */ = {isa = PBXBuildFile; fileRef = D04BA92541D46EA4F6943A72 /* package-lock.json */; }; - D04BA4D8D74841AEC2601C80 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D04BA262E9A663B3A85A2A95 /* Assets.xcassets */; }; - D04BA5474180F00F583177B9 /* package.json in Resources */ = {isa = PBXBuildFile; fileRef = D04BA4336B6004A0A99849AD /* package.json */; }; D04BA57A871B7269BEBAFF84 /* Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA35456DA0DDA74F9687E /* Keyboard.swift */; }; - D04BA5DD787816BB82C4AEFF /* .travis.yml in Resources */ = {isa = PBXBuildFile; fileRef = D04BAD7D8F3CC2C36C8769C6 /* .travis.yml */; }; - D04BA6C8D3667C49F97C06AD /* 14 windows - 3 lines.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D04BAB6652494D7575057E86 /* 14 windows - 3 lines.jpg */; }; - D04BA6D15018C5164724C0D2 /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = D04BA0E1C5DBC07108AC2F54 /* README.md */; }; - D04BA7076AC73E719B733352 /* 7 windows - 2 lines - extra wide window.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D04BAE1243C9B4BE3ED1B524 /* 7 windows - 2 lines - extra wide window.jpg */; }; + D04BA70FF7262BF5F9E6E13B /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BADCB1C0F50340A6CAFC2 /* Preferences.swift */; }; D04BA86F2E687F573F920D93 /* HighInterpolationImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BAE70D6AFD2E52A71701D /* HighInterpolationImageView.swift */; }; D04BA960DDD1D32A3019C835 /* CollectionViewCenterFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA3202A2C22C347E849B3 /* CollectionViewCenterFlowLayout.swift */; }; - D04BA9A1B237364C419C3A3F /* 5 windows - 2 lines.png in Resources */ = {isa = PBXBuildFile; fileRef = D04BA5E4A1745A481CEC1EBA /* 5 windows - 2 lines.png */; }; - D04BAA6317F099FDFD8CF560 /* release.config.js in Sources */ = {isa = PBXBuildFile; fileRef = D04BA1D80F4EEF2A91BAD29C /* release.config.js */; }; - D04BAA816B0EE923C6C211DD /* 2 windows - 1 line.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D04BA9EF65B2E7AF9E3ADCA3 /* 2 windows - 1 line.jpg */; }; - D04BAAC63A3C077B1DB155B2 /* 5 windows - 1 line.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D04BA5ABFA5457A86536E2E4 /* 5 windows - 1 line.jpg */; }; - D04BAB25494E5FF86D4C2437 /* 7 windows - 2 lines - wide window.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D04BA0CE87BE264C52987ED1 /* 7 windows - 2 lines - wide window.jpg */; }; - D04BABF815E9780EAF941E88 /* 3 windows - 1 line.png in Resources */ = {isa = PBXBuildFile; fileRef = D04BA27584274BC8C2D9809F /* 3 windows - 1 line.png */; }; - D04BACB58F00CAC62A46E662 /* 6 windows - 2 lines.png in Resources */ = {isa = PBXBuildFile; fileRef = D04BA5E9CB56E27CC907F07D /* 6 windows - 2 lines.png */; }; - D04BACF6FFC99DDD990E0694 /* 3 windows - 1 line - wide window.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D04BA51D43775E57CE91154A /* 3 windows - 1 line - wide window.jpg */; }; - D04BAD4AF98BD62155749E88 /* 1 window - 1 line.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D04BA1FC9022590D7AA02486 /* 1 window - 1 line.jpg */; }; - D04BAD9763C9B59FB46B4F9B /* 4 windows - 1 line.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D04BA0F5EB832B8E142B654B /* 4 windows - 1 line.jpg */; }; - D04BADD21FFEE61E926E897F /* too many windows - 4 lines - paginated.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D04BA107C8B8FE7FF8536606 /* too many windows - 4 lines - paginated.jpg */; }; - D04BAE5CC40654B1F96E8DE9 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = D04BA7B6AAB0812631BBC7A2 /* Info.plist */; }; + D04BAE1A64F5572B19F9E2B1 /* preferences.json in Resources */ = {isa = PBXBuildFile; fileRef = D04BA8924E1644C932B8CBAA /* preferences.json */; }; D04BAEF78503D7A2CEFB9E9E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BAA44C837F3A67403B9DB /* main.swift */; }; - D04BAFACEE2C33E66B93664A /* 3 windows - 1 line.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D04BA32F25860B686DFE818A /* 3 windows - 1 line.jpg */; }; - D04BAFE95F19AD93955904DE /* 7 windows - 2 lines - tall window.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D04BA4F23325560BC0BCDDB7 /* 7 windows - 2 lines - tall window.jpg */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -51,7 +25,6 @@ D04BA107C8B8FE7FF8536606 /* too many windows - 4 lines - paginated.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "too many windows - 4 lines - paginated.jpg"; sourceTree = ""; }; D04BA1D80F4EEF2A91BAD29C /* release.config.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = release.config.js; sourceTree = ""; }; D04BA1FC9022590D7AA02486 /* 1 window - 1 line.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "1 window - 1 line.jpg"; sourceTree = ""; }; - D04BA262E9A663B3A85A2A95 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; D04BA27584274BC8C2D9809F /* 3 windows - 1 line.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "3 windows - 1 line.png"; sourceTree = ""; }; D04BA2C7B51F68651B3C60E2 /* 6 windows - 1 line.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "6 windows - 1 line.jpg"; sourceTree = ""; }; D04BA3202A2C22C347E849B3 /* CollectionViewCenterFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCenterFlowLayout.swift; sourceTree = ""; }; @@ -66,6 +39,7 @@ D04BA5E9CB56E27CC907F07D /* 6 windows - 2 lines.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "6 windows - 2 lines.png"; sourceTree = ""; }; D04BA7B6AAB0812631BBC7A2 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.info; path = Info.plist; sourceTree = ""; }; D04BA82F792DF53958D92572 /* alt-tab-macos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "alt-tab-macos.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + D04BA8924E1644C932B8CBAA /* preferences.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = preferences.json; sourceTree = ""; }; D04BA8DDB96B0A60C3394351 /* dark-background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "dark-background.png"; sourceTree = ""; }; D04BA90C6C36DB1D65BC2B66 /* Application.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = ""; }; D04BA92541D46EA4F6943A72 /* package-lock.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "package-lock.json"; sourceTree = ""; }; @@ -78,6 +52,7 @@ D04BAD40CE2D3A8AAC3819D0 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = file.gitignore; path = .gitignore; sourceTree = ""; }; D04BAD7D8F3CC2C36C8769C6 /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = file.yml; path = .travis.yml; sourceTree = ""; }; D04BAD82F652C4ED5D8BF7AE /* 3 windows - 1 line - tall window.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "3 windows - 1 line - tall window.jpg"; sourceTree = ""; }; + D04BADCB1C0F50340A6CAFC2 /* Preferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; D04BAE1243C9B4BE3ED1B524 /* 7 windows - 2 lines - extra wide window.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "7 windows - 2 lines - extra wide window.jpg"; sourceTree = ""; }; D04BAE70D6AFD2E52A71701D /* HighInterpolationImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HighInterpolationImageView.swift; sourceTree = ""; }; D04BAF076A30A1BAFEDBEA66 /* 5 windows - 2 lines.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "5 windows - 2 lines.jpg"; sourceTree = ""; }; @@ -112,6 +87,14 @@ ); sourceTree = ""; }; + D04BA18D1EDDD56CA89604B6 /* resources */ = { + isa = PBXGroup; + children = ( + D04BA8924E1644C932B8CBAA /* preferences.json */, + ); + path = resources; + sourceTree = ""; + }; D04BA1A461BBB5A9BE15E015 /* Products */ = { isa = PBXGroup; children = ( @@ -157,6 +140,7 @@ children = ( D04BA35456DA0DDA74F9687E /* Keyboard.swift */, D04BAD1BED44EAEB77FED8A4 /* WindowManager.swift */, + D04BADCB1C0F50340A6CAFC2 /* Preferences.swift */, ); path = logic; sourceTree = ""; @@ -192,12 +176,12 @@ D04BAD48B702C4B1DC543557 /* alt-tab-macos */ = { isa = PBXGroup; children = ( - D04BA262E9A663B3A85A2A95 /* Assets.xcassets */, D04BAD1C9F215BCCD3B620AC /* alt_tab_macos.entitlements */, D04BAAA28315301A906DD201 /* ui */, D04BA7B6AAB0812631BBC7A2 /* Info.plist */, D04BAA44C837F3A67403B9DB /* main.swift */, D04BAA1C553891551B903DA7 /* logic */, + D04BA18D1EDDD56CA89604B6 /* resources */, ); path = "alt-tab-macos"; sourceTree = ""; @@ -235,6 +219,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = D04BA1463D2A17038222BB84; @@ -252,30 +237,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - D04BA4D8D74841AEC2601C80 /* Assets.xcassets in Resources */, - D04BAE5CC40654B1F96E8DE9 /* Info.plist in Resources */, - D04BA3999A3D6A308EA41039 /* .gitignore in Resources */, - D04BAFACEE2C33E66B93664A /* 3 windows - 1 line.jpg in Resources */, - D04BA6C8D3667C49F97C06AD /* 14 windows - 3 lines.jpg in Resources */, - D04BA0B1D77CA1466BFC13BC /* 3 windows - 1 line - tall window.jpg in Resources */, - D04BAFE95F19AD93955904DE /* 7 windows - 2 lines - tall window.jpg in Resources */, - D04BA21F728100990BA0D007 /* 6 windows - 1 line.jpg in Resources */, - D04BADD21FFEE61E926E897F /* too many windows - 4 lines - paginated.jpg in Resources */, - D04BAAC63A3C077B1DB155B2 /* 5 windows - 1 line.jpg in Resources */, - D04BAD9763C9B59FB46B4F9B /* 4 windows - 1 line.jpg in Resources */, - D04BAD4AF98BD62155749E88 /* 1 window - 1 line.jpg in Resources */, - D04BAA816B0EE923C6C211DD /* 2 windows - 1 line.jpg in Resources */, - D04BACF6FFC99DDD990E0694 /* 3 windows - 1 line - wide window.jpg in Resources */, - D04BA35E037C9E1C3B26F296 /* 5 windows - 2 lines.jpg in Resources */, - D04BAB25494E5FF86D4C2437 /* 7 windows - 2 lines - wide window.jpg in Resources */, - D04BA7076AC73E719B733352 /* 7 windows - 2 lines - extra wide window.jpg in Resources */, - D04BA183207E20AC28BF4F20 /* dark-background.png in Resources */, - D04BACB58F00CAC62A46E662 /* 6 windows - 2 lines.png in Resources */, - D04BA9A1B237364C419C3A3F /* 5 windows - 2 lines.png in Resources */, - D04BABF815E9780EAF941E88 /* 3 windows - 1 line.png in Resources */, - D04BA5DD787816BB82C4AEFF /* .travis.yml in Resources */, - D04BA5474180F00F583177B9 /* package.json in Resources */, - D04BA424348403E28A5EEB40 /* package-lock.json in Resources */, + D04BAE1A64F5572B19F9E2B1 /* preferences.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -293,10 +255,7 @@ D04BA57A871B7269BEBAFF84 /* Keyboard.swift in Sources */, D04BA278D9EFA568C8D18A4C /* WindowManager.swift in Sources */, D04BA3261C7DA5F48310E654 /* Application.swift in Sources */, - D04BA6D15018C5164724C0D2 /* README.md in Sources */, - D04BAA6317F099FDFD8CF560 /* release.config.js in Sources */, - D04BA21CD515ACDBCC4437E6 /* commitlint.config.js in Sources */, - D04BA2B84D062AA0EB970CFA /* CHANGELOG.md in Sources */, + D04BA70FF7262BF5F9E6E13B /* Preferences.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -441,8 +400,7 @@ MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; }; @@ -456,6 +414,7 @@ D04BA49BCED00029C5289244 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; D04BA83C9495F252D87CDF0F /* Build configuration list for PBXProject "alt-tab-macos" */ = { isa = XCConfigurationList; diff --git a/alt-tab-macos/Assets.xcassets/AppIcon.appiconset/Contents.json b/alt-tab-macos/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 2db2b1c7..00000000 --- a/alt-tab-macos/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "images" : [ - { - "idiom" : "mac", - "size" : "16x16", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "16x16", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "32x32", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "32x32", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "128x128", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "128x128", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "256x256", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "256x256", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "512x512", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "512x512", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/alt-tab-macos/Assets.xcassets/Contents.json b/alt-tab-macos/Assets.xcassets/Contents.json deleted file mode 100644 index da4a164c..00000000 --- a/alt-tab-macos/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/alt-tab-macos/logic/Keyboard.swift b/alt-tab-macos/logic/Keyboard.swift index f4db2068..8feceffa 100644 --- a/alt-tab-macos/logic/Keyboard.swift +++ b/alt-tab-macos/logic/Keyboard.swift @@ -24,15 +24,15 @@ func keyboardHandler(_ cgEvent: CGEvent, _ delegate: Application) -> Unmanaged Int { + try String(contentsOf: url, encoding: .utf8).split(separator: "\n").count +} + +class Preferences: Decodable { + private static let decoded: PreferencesDecodable? = decodeFromJson() + + private static func decodeFromJson() -> PreferencesDecodable? { + do { + let defaultFile = Bundle.main.url(forResource: "preferences", withExtension: "json")! + let userFile = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask) + .first! + .appendingPathComponent("Preferences", isDirectory: true) + .appendingPathComponent("alt-tab-macos.json") + try handleNoFileOrOldFile(userFile: userFile, defaultFile: defaultFile) + return try JSONDecoder().decode(PreferencesDecodable.self, from: Data(contentsOf: userFile)) + } catch { + debugPrint("Error parsing preferences.json", error) + return nil + } + } + + private static func handleNoFileOrOldFile(userFile: URL, defaultFile: URL) throws { + if !FileManager.default.fileExists(atPath: userFile.path) { + try FileManager.default.copyItem(at: defaultFile, to: userFile) + } else if try fileLines(defaultFile) > fileLines(userFile) { + // TODO: handle upgrades in a smarter way (e.g. merge files) + let _ = try FileManager.default.replaceItemAt(defaultFile, withItemAt: userFile) + } + } + + // maximum width/height of the main window, in percentage of screen width/height + static let maxScreenUsage = decoded?.maxScreenUsage ?? 0.8 + // padding in the main window + static let windowPadding = decoded?.windowPadding ?? 20 + // padding in each cell + static let cellPadding = decoded?.cellPadding ?? 6 + // border width of each cell + static let cellBorderWidth = decoded?.cellBorderWidth ?? 2 + // maximum number of thumbnails on each row + static let maxThumbnailsPerRow = decoded?.maxThumbnailsPerRow ?? 4 + // maximum width of each thumbnail + static var thumbnailMaxWidth = decoded?.thumbnailMaxWidth ?? 0 + // maximum height of eacg thumbnail + static var thumbnailMaxHeight = decoded?.thumbnailMaxHeight ?? 0 + // width/height for each cell app icon + static let iconSize = decoded?.iconSize ?? 32 + // font height for each cell title + static let fontHeight = decoded?.fontHeight ?? 15 + // padding between cells within the main window + static let interItemPadding = decoded?.interItemPadding ?? 4 + // NSEvent.keyCode (e.g. leftCommand=55, leftOption=58, leftControl=59, rightCommand=54, rightOption=61) + static let tabKey = decoded?.tabKey ?? 48 + // NSEvent.keyCode (e.g. leftCommand=55, leftOption=58, leftControl=59, rightCommand=54, rightOption=61) + static let metaKey = decoded?.metaKey ?? 59 + // NSImageInterpolation (e.g. none=1, low=2, medium=4, high=3) + static let thumbnailQuality = NSImageInterpolation.init(rawValue: decoded?.thumbnailQuality ?? 3) ?? .high + // NSEvent.ModifierFlags (e.g. control=262144, command=1048576, shift=131072, option=524288) + static let metaModifierFlag = NSEvent.ModifierFlags(rawValue: decoded?.metaModifierFlagInt ?? 262144) + // color for the currently selected cell + static let highlightColor = NSColor(named: decoded?.highlightColorString ?? "white") ?? .white + // derived properties + static let font: NSFont = .systemFont(ofSize: Preferences.fontHeight) +} + diff --git a/alt-tab-macos/logic/WindowManager.swift b/alt-tab-macos/logic/WindowManager.swift index 987f55a4..b57b25dd 100644 --- a/alt-tab-macos/logic/WindowManager.swift +++ b/alt-tab-macos/logic/WindowManager.swift @@ -35,11 +35,11 @@ class OpenWindow { func computeDownscaledSize(_ image: NSImage) -> (Int, Int) { let imageRatio = image.size.width / image.size.height - let thumbnailWidth = Int(floor(thumbnailMaxHeight * imageRatio)) - if thumbnailWidth <= Int(thumbnailMaxWidth) { - return (thumbnailWidth, Int(thumbnailMaxHeight)) + let thumbnailWidth = Int(floor(Preferences.thumbnailMaxHeight * imageRatio)) + if thumbnailWidth <= Int(Preferences.thumbnailMaxWidth) { + return (thumbnailWidth, Int(Preferences.thumbnailMaxHeight)) } else { - return (Int(thumbnailMaxWidth), Int(floor(thumbnailMaxWidth / imageRatio))) + return (Int(Preferences.thumbnailMaxWidth), Int(floor(Preferences.thumbnailMaxWidth / imageRatio))) } } diff --git a/alt-tab-macos/resources/preferences.json b/alt-tab-macos/resources/preferences.json new file mode 100644 index 00000000..1f3b19b5 --- /dev/null +++ b/alt-tab-macos/resources/preferences.json @@ -0,0 +1,17 @@ +{ + "maxScreenUsage": 0.8, + "windowPadding": 20, + "cellPadding": 6, + "cellBorderWidth": 2, + "maxThumbnailsPerRow": 4, + "thumbnailMaxWidth": 0, + "thumbnailMaxHeight": 0, + "iconSize": 32, + "fontHeight": 15, + "interItemPadding": 4, + "tabKey": 48, + "metaKey": 59, + "metaModifierFlagInt": 262144, + "highlightColorString": "white", + "thumbnailQuality": 4 +} diff --git a/alt-tab-macos/ui/Application.swift b/alt-tab-macos/ui/Application.swift index e952d732..4bbf7ae4 100644 --- a/alt-tab-macos/ui/Application.swift +++ b/alt-tab-macos/ui/Application.swift @@ -1,27 +1,12 @@ -import Cocoa import Foundation +import Cocoa -let maxScreenUsage: CGFloat = 0.8 -let windowPadding: CGFloat = 20 -let cellPadding: CGFloat = 6 -let cellBorderWidth: CGFloat = 2 -let maxThumbnailsPerRow: CGFloat = 4 -var thumbnailMaxWidth: CGFloat = 0 -var thumbnailMaxHeight: CGFloat = 0 -let iconSize: CGFloat = 32 -let fontHeight: CGFloat = 15 -let font: NSFont = .systemFont(ofSize: fontHeight) -let interItemPadding: CGFloat = 4 -let tabKey = 48 -let metaKey = 0x3B -let metaModifierFlag: NSEvent.ModifierFlags = .control let cellId = NSUserInterfaceItemIdentifier("Cell") -let highlightColor: NSColor = .white func updateThumbnailMaxSize() -> Void { let main = NSScreen.main!.frame - thumbnailMaxWidth = (NSScreen.main!.frame.size.width * maxScreenUsage - windowPadding * 2) / maxThumbnailsPerRow - interItemPadding - thumbnailMaxHeight = thumbnailMaxWidth * (main.height / main.width) + Preferences.thumbnailMaxWidth = (NSScreen.main!.frame.size.width * Preferences.maxScreenUsage - Preferences.windowPadding * 2) / Preferences.maxThumbnailsPerRow - Preferences.interItemPadding + Preferences.thumbnailMaxHeight = Preferences.thumbnailMaxWidth * (main.height / main.width) } class Application: NSApplication, NSApplicationDelegate, NSWindowDelegate, NSCollectionViewDataSource, NSCollectionViewDelegate, NSCollectionViewDelegateFlowLayout { @@ -82,7 +67,7 @@ class Application: NSApplication, NSApplicationDelegate, NSWindowDelegate, NSCol func makeCollectionView() { let layout = CollectionViewCenterFlowLayout() - layout.estimatedItemSize = NSSize(width: thumbnailMaxWidth, height: thumbnailMaxHeight) + layout.estimatedItemSize = NSSize(width: Preferences.thumbnailMaxWidth, height: Preferences.thumbnailMaxHeight) layout.minimumInteritemSpacing = 5 layout.minimumLineSpacing = 5 collectionView_ = NSCollectionView() @@ -146,14 +131,14 @@ class Application: NSApplication, NSApplicationDelegate, NSWindowDelegate, NSCol } func computeThumbnails() { - let maxSize = NSSize(width: NSScreen.main!.frame.width * maxScreenUsage, height: NSScreen.main!.frame.height * maxScreenUsage) + let maxSize = NSSize(width: NSScreen.main!.frame.width * Preferences.maxScreenUsage, height: NSScreen.main!.frame.height * Preferences.maxScreenUsage) collectionView_!.setFrameSize(maxSize) collectionView_!.collectionViewLayout!.invalidateLayout() collectionView_!.reloadData() collectionView_!.layoutSubtreeIfNeeded() - window!.setContentSize(NSSize(width: collectionView_!.frame.size.width + windowPadding * 2, height: collectionView_!.frame.size.height + windowPadding * 2)) + window!.setContentSize(NSSize(width: collectionView_!.frame.size.width + Preferences.windowPadding * 2, height: collectionView_!.frame.size.height + Preferences.windowPadding * 2)) backgroundView!.setFrameSize(window!.frame.size) - collectionView_!.setFrameOrigin(NSPoint(x: windowPadding, y: windowPadding)) + collectionView_!.setFrameOrigin(NSPoint(x: Preferences.windowPadding, y: Preferences.windowPadding)) window!.center() } @@ -173,13 +158,13 @@ class Application: NSApplication, NSApplicationDelegate, NSWindowDelegate, NSCol debugPrint("collectionView: item size") if indexPath.item < openWindows.count { let (width, height) = computeDownscaledSize(openWindows[indexPath.item].thumbnail) - return NSSize(width: CGFloat(width) + cellPadding * 2, height: CGFloat(height) + max(fontHeight, iconSize) + interItemPadding + cellPadding * 2) + return NSSize(width: CGFloat(width) + Preferences.cellPadding * 2, height: CGFloat(height) + max(Preferences.fontHeight, Preferences.iconSize) + Preferences.interItemPadding + Preferences.cellPadding * 2) } return .zero } func cellWithStep(_ step: Int) -> Int { - return selectedOpenWindow + step < 0 ? openWindows.count - 1 : (selectedOpenWindow + step) % openWindows.count + selectedOpenWindow + step < 0 ? openWindows.count - 1 : (selectedOpenWindow + step) % openWindows.count } func selectOtherCell(_ step: Int) { @@ -203,7 +188,7 @@ class Application: NSApplication, NSApplicationDelegate, NSWindowDelegate, NSCol } func currentlySelectedWindow() -> OpenWindow? { - return openWindows.count > selectedOpenWindow ? openWindows[selectedOpenWindow] : nil + openWindows.count > selectedOpenWindow ? openWindows[selectedOpenWindow] : nil } func focusSelectedWindow(_ window: OpenWindow?) { diff --git a/alt-tab-macos/ui/Cell.swift b/alt-tab-macos/ui/Cell.swift index c99faf15..46fc42a8 100644 --- a/alt-tab-macos/ui/Cell.swift +++ b/alt-tab-macos/ui/Cell.swift @@ -14,13 +14,13 @@ class Cell: NSCollectionViewItem { override func loadView() { let vStackView = NSStackView() vStackView.wantsLayer = true - vStackView.layer!.borderWidth = cellBorderWidth + vStackView.layer!.borderWidth = Preferences.cellBorderWidth vStackView.layer!.borderColor = .clear - vStackView.edgeInsets = NSEdgeInsets(top: cellPadding, left: cellPadding, bottom: cellPadding, right: cellPadding) + vStackView.edgeInsets = NSEdgeInsets(top: Preferences.cellPadding, left: Preferences.cellPadding, bottom: Preferences.cellPadding, right: Preferences.cellPadding) vStackView.orientation = .vertical - vStackView.spacing = interItemPadding + vStackView.spacing = Preferences.interItemPadding let hStackView = NSStackView() - hStackView.spacing = interItemPadding + hStackView.spacing = Preferences.interItemPadding let textStorage = NSTextStorage() let layoutManager = NSLayoutManager() textStorage.addLayoutManager(layoutManager) @@ -33,8 +33,8 @@ class Cell: NSCollectionViewItem { text.backgroundColor = .clear text.isSelectable = false text.isEditable = false - text.font = font - text.textColor = highlightColor + text.font = Preferences.font + text.textColor = Preferences.highlightColor let shadow = NSShadow() shadow.shadowColor = .darkGray shadow.shadowOffset = NSMakeSize(0, 0) @@ -43,11 +43,11 @@ class Cell: NSCollectionViewItem { text.enabledTextCheckingTypes = 0 let paragraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle paragraphStyle.lineBreakMode = .byTruncatingTail - paragraphStyle.maximumLineHeight = fontHeight - paragraphStyle.minimumLineHeight = fontHeight + paragraphStyle.maximumLineHeight = Preferences.fontHeight + paragraphStyle.minimumLineHeight = Preferences.fontHeight paragraphStyle.allowsDefaultTighteningForTruncation = false text.defaultParagraphStyle = paragraphStyle - text.heightAnchor.constraint(equalToConstant: fontHeight).isActive = true + text.heightAnchor.constraint(equalToConstant: Preferences.fontHeight).isActive = true let shadow2 = NSShadow() shadow2.shadowColor = .gray shadow2.shadowOffset = NSMakeSize(0, 0) @@ -63,7 +63,7 @@ class Cell: NSCollectionViewItem { override var isSelected: Bool { didSet { - view.layer!.borderColor = isSelected ? highlightColor.cgColor : .clear + view.layer!.borderColor = isSelected ? Preferences.highlightColor.cgColor : .clear } } @@ -74,12 +74,12 @@ class Cell: NSCollectionViewItem { thumbnail.image!.size = NSSize(width: width, height: height) thumbnail.frame.size = NSSize(width: width, height: height) icon.image = element.icon - icon.image!.size = NSSize(width: iconSize, height: iconSize) - icon.frame.size = NSSize(width: iconSize, height: iconSize) + icon.image!.size = NSSize(width: Preferences.iconSize, height: Preferences.iconSize) + icon.frame.size = NSSize(width: Preferences.iconSize, height: Preferences.iconSize) text.string = element.cgTitle // workaround: setting string on NSTextView change the font (most likely a Cocoa bug) - text.font = font - text.textContainer!.size.width = thumbnail.frame.size.width - iconSize - interItemPadding + text.font = Preferences.font + text.textContainer!.size.width = thumbnail.frame.size.width - Preferences.iconSize - Preferences.interItemPadding mouseDownCallback = fn } } diff --git a/alt-tab-macos/ui/CollectionViewCenterFlowLayout.swift b/alt-tab-macos/ui/CollectionViewCenterFlowLayout.swift index b8f3a16e..d6a5d313 100644 --- a/alt-tab-macos/ui/CollectionViewCenterFlowLayout.swift +++ b/alt-tab-macos/ui/CollectionViewCenterFlowLayout.swift @@ -14,7 +14,7 @@ class CollectionViewCenterFlowLayout: NSCollectionViewFlowLayout { var widestRow = CGFloat(0) var totalHeight = CGFloat(0) attributes.enumerated().forEach { - let isNewRow = abs($1.frame.origin.y - currentRowY) > thumbnailMaxHeight + let isNewRow = abs($1.frame.origin.y - currentRowY) > Preferences.thumbnailMaxHeight if isNewRow { computeOriginXForAllItems(currentRowWidth - minimumInteritemSpacing, previousRowMaxY, currentRow) currentRow.removeAll() diff --git a/alt-tab-macos/ui/HighInterpolationImageView.swift b/alt-tab-macos/ui/HighInterpolationImageView.swift index 1233c55b..cb8cd3b2 100644 --- a/alt-tab-macos/ui/HighInterpolationImageView.swift +++ b/alt-tab-macos/ui/HighInterpolationImageView.swift @@ -2,7 +2,7 @@ import Cocoa class HighInterpolationImageView: NSImageView { override func draw(_ dirtyRect: NSRect) { - NSGraphicsContext.current!.imageInterpolation = .high + NSGraphicsContext.current!.imageInterpolation = Preferences.thumbnailQuality super.draw(dirtyRect) } }