feat: preferences can be changed through JSON file

* removed unused Resources from the bundle
* thumbnails quality is now a preference
This commit is contained in:
Louis Pontoise committed 2019-10-16 21:34:58 +09:00
1 parent f56fd59042
commit 64cb6f067d
12 files changed
+158 -174

No files matched your search

+1
View File
@@ -1,5 +1,6 @@
/.idea/
xcuserdata/
xcshareddata/
/node_modules/
/build/
+19 -60
View File
@@ -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 = "<group>"; };
D04BA1D80F4EEF2A91BAD29C /* release.config.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = release.config.js; sourceTree = "<group>"; };
D04BA1FC9022590D7AA02486 /* 1 window - 1 line.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "1 window - 1 line.jpg"; sourceTree = "<group>"; };
D04BA262E9A663B3A85A2A95 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
D04BA27584274BC8C2D9809F /* 3 windows - 1 line.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "3 windows - 1 line.png"; sourceTree = "<group>"; };
D04BA2C7B51F68651B3C60E2 /* 6 windows - 1 line.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "6 windows - 1 line.jpg"; sourceTree = "<group>"; };
D04BA3202A2C22C347E849B3 /* CollectionViewCenterFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCenterFlowLayout.swift; sourceTree = "<group>"; };
@@ -66,6 +39,7 @@
D04BA5E9CB56E27CC907F07D /* 6 windows - 2 lines.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "6 windows - 2 lines.png"; sourceTree = "<group>"; };
D04BA7B6AAB0812631BBC7A2 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.info; path = Info.plist; sourceTree = "<group>"; };
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 = "<group>"; };
D04BA8DDB96B0A60C3394351 /* dark-background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "dark-background.png"; sourceTree = "<group>"; };
D04BA90C6C36DB1D65BC2B66 /* Application.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
D04BA92541D46EA4F6943A72 /* package-lock.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "package-lock.json"; sourceTree = "<group>"; };
@@ -78,6 +52,7 @@
D04BAD40CE2D3A8AAC3819D0 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = file.gitignore; path = .gitignore; sourceTree = "<group>"; };
D04BAD7D8F3CC2C36C8769C6 /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = file.yml; path = .travis.yml; sourceTree = "<group>"; };
D04BAD82F652C4ED5D8BF7AE /* 3 windows - 1 line - tall window.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "3 windows - 1 line - tall window.jpg"; sourceTree = "<group>"; };
D04BADCB1C0F50340A6CAFC2 /* Preferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
D04BAE1243C9B4BE3ED1B524 /* 7 windows - 2 lines - extra wide window.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "7 windows - 2 lines - extra wide window.jpg"; sourceTree = "<group>"; };
D04BAE70D6AFD2E52A71701D /* HighInterpolationImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HighInterpolationImageView.swift; sourceTree = "<group>"; };
D04BAF076A30A1BAFEDBEA66 /* 5 windows - 2 lines.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "5 windows - 2 lines.jpg"; sourceTree = "<group>"; };
@@ -112,6 +87,14 @@
);
sourceTree = "<group>";
};
D04BA18D1EDDD56CA89604B6 /* resources */ = {
isa = PBXGroup;
children = (
D04BA8924E1644C932B8CBAA /* preferences.json */,
);
path = resources;
sourceTree = "<group>";
};
D04BA1A461BBB5A9BE15E015 /* Products */ = {
isa = PBXGroup;
children = (
@@ -157,6 +140,7 @@
children = (
D04BA35456DA0DDA74F9687E /* Keyboard.swift */,
D04BAD1BED44EAEB77FED8A4 /* WindowManager.swift */,
D04BADCB1C0F50340A6CAFC2 /* Preferences.swift */,
);
path = logic;
sourceTree = "<group>";
@@ -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 = "<group>";
@@ -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;
@@ -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"
}
}
@@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
+5 -5
View File
@@ -24,15 +24,15 @@ func keyboardHandler(_ cgEvent: CGEvent, _ delegate: Application) -> Unmanaged<C
if cgEvent.type == .keyDown || cgEvent.type == .keyUp || cgEvent.type == .flagsChanged {
if let event = NSEvent.init(cgEvent: cgEvent) {
let keyDown = event.type == .keyDown
let optionKeyEvent = event.keyCode == metaKey
let tabKeyEvent = event.keyCode == tabKey
if optionKeyEvent && event.modifiersDown([metaModifierFlag]) {
let optionKeyEvent = event.keyCode == Preferences.metaKey
let tabKeyEvent = event.keyCode == Preferences.tabKey
if optionKeyEvent && event.modifiersDown([Preferences.metaModifierFlag]) {
delegate.keyDownMeta()
} else if tabKeyEvent && event.modifiersDown([metaModifierFlag]) && keyDown {
} else if tabKeyEvent && event.modifiersDown([Preferences.metaModifierFlag]) && keyDown {
delegate.keyDownMetaTab()
// focused app will not receive the event (will not press tab key in that app)
return nil
} else if tabKeyEvent && event.modifiersDown([metaModifierFlag, .shift]) && keyDown {
} else if tabKeyEvent && event.modifiersDown([Preferences.metaModifierFlag, .shift]) && keyDown {
delegate.keyDownMetaShiftTab()
// focused app will not receive the event (will not press tab key in that app)
return nil
+86
View File
@@ -0,0 +1,86 @@
import Foundation
import Cocoa
struct PreferencesDecodable: Decodable {
let maxScreenUsage: CGFloat?
let windowPadding: CGFloat?
let cellPadding: CGFloat?
let cellBorderWidth: CGFloat?
let maxThumbnailsPerRow: CGFloat?
let thumbnailMaxWidth: CGFloat?
let thumbnailMaxHeight: CGFloat?
let iconSize: CGFloat?
let fontHeight: CGFloat?
let interItemPadding: CGFloat?
let tabKey: UInt16?
let metaKey: UInt16?
let metaModifierFlagInt: UInt?
let highlightColorString : String?
let thumbnailQuality : UInt?
}
func fileLines(_ url: URL) throws -> 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)
}
+4 -4
View File
@@ -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)))
}
}
+17
View File
@@ -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
}
+10 -25
View File
@@ -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?) {
+14 -14
View File
@@ -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
}
}
@@ -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()
@@ -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)
}
}