Skip to content

Commit ade340b

Browse files
authored
Merge pull request #5 from sweetmans/feature/add_page_control
Add paging
2 parents 839f4da + 2c6b447 commit ade340b

File tree

13 files changed

+184
-110
lines changed

13 files changed

+184
-110
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
## User settings
66
xcuserdata/
7-
7+
FuckAppleLaunchpad.xcodeproj/xcshareddata/
8+
FuckAppleLaunchpad.xcodeproj/xcuserdata/
9+
*.xcodeproj/xcuserdata/
810
## compatibility with Xcode 8 and earlier (ignoring documentation and other minor files)
911
*.xcscmblueprint
1012
*.xccheckout

FuckAppleLaunchpad.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@
396396
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
397397
CODE_SIGN_STYLE = Automatic;
398398
COMBINE_HIDPI_IMAGES = YES;
399-
CURRENT_PROJECT_VERSION = 3;
399+
CURRENT_PROJECT_VERSION = 14.6.2025;
400400
DEVELOPMENT_TEAM = 92A277ZDK2;
401401
ENABLE_APP_SANDBOX = YES;
402402
ENABLE_HARDENED_RUNTIME = YES;
@@ -411,7 +411,7 @@
411411
"@executable_path/../Frameworks",
412412
);
413413
MACOSX_DEPLOYMENT_TARGET = 26.0;
414-
MARKETING_VERSION = 1.0.0;
414+
MARKETING_VERSION = 1.0.1;
415415
PRODUCT_BUNDLE_IDENTIFIER = com.davinci.FuckAppleLaunchpad;
416416
PRODUCT_NAME = "$(TARGET_NAME)";
417417
REGISTER_APP_GROUPS = YES;
@@ -430,7 +430,7 @@
430430
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
431431
CODE_SIGN_STYLE = Automatic;
432432
COMBINE_HIDPI_IMAGES = YES;
433-
CURRENT_PROJECT_VERSION = 3;
433+
CURRENT_PROJECT_VERSION = 14.6.2025;
434434
DEVELOPMENT_TEAM = 92A277ZDK2;
435435
ENABLE_APP_SANDBOX = YES;
436436
ENABLE_HARDENED_RUNTIME = YES;
@@ -445,7 +445,7 @@
445445
"@executable_path/../Frameworks",
446446
);
447447
MACOSX_DEPLOYMENT_TARGET = 26.0;
448-
MARKETING_VERSION = 1.0.0;
448+
MARKETING_VERSION = 1.0.1;
449449
PRODUCT_BUNDLE_IDENTIFIER = com.davinci.FuckAppleLaunchpad;
450450
PRODUCT_NAME = "$(TARGET_NAME)";
451451
REGISTER_APP_GROUPS = YES;

FuckAppleLaunchpad.xcodeproj/xcuserdata/andy.xcuserdatad/xcschemes/xcschememanagement.plist

Lines changed: 0 additions & 14 deletions
This file was deleted.

FuckAppleLaunchpad/ApplicationsViewModel.swift

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,41 +14,72 @@ struct ApplicationPage: Identifiable {
1414
let apps: [Application]
1515
}
1616

17-
class ApplicationsViewModel: ObservableObject {
17+
class LaunchpadViewViewModel: ObservableObject {
1818
@Published var applications: [Application] = []
1919
var applicationPages: [ApplicationPage] {
2020
stride(from: 0, to: applications.count, by: 35).map { index in
2121
ApplicationPage(apps: Array(applications[index..<min(index + 35, applications.count)]))
2222
}
2323
}
24-
24+
2525
func loadApplications() {
26+
let systemApplicationUrl = URL(fileURLWithPath: "/System/Applications")
27+
let applicationUrl = URL(fileURLWithPath: "/Applications")
28+
var applications = loadApplications(from: systemApplicationUrl, isLoadingDirectory: true)
29+
applications.append(contentsOf: loadApplications(from: applicationUrl, isLoadingDirectory: true))
30+
if let userApplicationsPath = getUserApplicationsPath() {
31+
let userApplicationUrl = URL(fileURLWithPath: userApplicationsPath)
32+
applications.append(contentsOf: loadApplications(from: userApplicationUrl, isLoadingDirectory: true))
33+
}
34+
DispatchQueue.main.async {
35+
self.applications = applications.sorted { $0.name < $1.name }
36+
print("total apps loaded:", applications.count)
37+
}
38+
}
39+
40+
// TODO: Fix issue on Error accessing User Applications
41+
private func getUserApplicationsPath() -> String? {
42+
let userHomePath = FileManager.default.homeDirectoryForCurrentUser.path
43+
let components = userHomePath.components(separatedBy: "/")
44+
if components.count >= 3,
45+
components[1] == "Users" {
46+
let homePath = "/\(components[1])/\(components[2])/Applications"
47+
return homePath
48+
} else {
49+
return nil
50+
}
51+
}
52+
53+
private func loadApplications(from folderUrl: URL, isLoadingDirectory: Bool) -> [Application] {
2654
let fileManager = FileManager.default
27-
let applicationsURL = URL(fileURLWithPath: "/Applications")
28-
55+
var applications: [Application] = []
2956
do {
30-
let contents = try fileManager.contentsOfDirectory(
31-
at: applicationsURL,
32-
includingPropertiesForKeys: [.isApplicationKey]
33-
)
34-
35-
let apps = contents
36-
.filter { url in
37-
let resourceValues = try? url.resourceValues(forKeys: [.isApplicationKey])
38-
return resourceValues?.isApplication == true
39-
}
40-
.map { url in
57+
let resourceKeys: [URLResourceKey] = [.isApplicationKey, .isDirectoryKey]
58+
guard let enumerator = fileManager.enumerator(at: folderUrl,
59+
includingPropertiesForKeys: resourceKeys,
60+
options: [.skipsHiddenFiles, .skipsSubdirectoryDescendants],
61+
errorHandler: { url, error in
62+
print("Error accessing \(url): \(error)")
63+
return true
64+
}) else {
65+
return []
66+
}
67+
while let url = enumerator.nextObject() as? URL {
68+
if url.lastPathComponent.contains("app") {
4169
let name = url.deletingPathExtension().lastPathComponent
4270
let icon = NSWorkspace.shared.icon(forFile: url.path)
43-
return Application(name: name, path: url, icon: icon)
71+
applications.append(Application(name: name, path: url, icon: icon))
72+
continue
73+
}
74+
guard isLoadingDirectory else { continue }
75+
let resourceValues = try url.resourceValues(forKeys: Set(resourceKeys))
76+
if resourceValues.isDirectory == true {
77+
applications.append(contentsOf: loadApplications(from: url, isLoadingDirectory: false))
4478
}
45-
.sorted { $0.name < $1.name }
46-
47-
DispatchQueue.main.async {
48-
self.applications = apps
4979
}
50-
} catch {
80+
} catch let error {
5181
print("Error loading applications: \(error)")
5282
}
83+
return applications
5384
}
5485
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"images" : [
3+
{
4+
"idiom" : "universal",
5+
"scale" : "1x"
6+
},
7+
{
8+
"filename" : "[email protected]",
9+
"idiom" : "universal",
10+
"scale" : "2x"
11+
},
12+
{
13+
"idiom" : "universal",
14+
"scale" : "3x"
15+
}
16+
],
17+
"info" : {
18+
"author" : "xcode",
19+
"version" : 1
20+
}
21+
}
314 KB
Loading

FuckAppleLaunchpad/FuckAppleLaunchpadApp.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ struct FuckAppleLaunchpadApp: App {
77

88
var body: some Scene {
99
WindowGroup {
10-
ContentView()
10+
LaunchpadView()
1111
.frame(minWidth: NSScreen.main?.visibleFrame.width ?? 1280, minHeight: NSScreen.main?.visibleFrame.height ?? 800) // Minimum size for readability
1212
}
1313
.defaultSize(width: NSScreen.main?.visibleFrame.width ?? 1280, height: NSScreen.main?.visibleFrame.height ?? 800)

FuckAppleLaunchpad/View/AppGridView.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,10 @@ struct AppGridView: View {
1414
ForEach(0..<7) { columnIndex in
1515
if rowIndex * 7 + columnIndex < apps.count {
1616
AppItemView(app: apps[rowIndex * 7 + columnIndex])
17-
.frame(width: appItemViewWidth,
18-
height: appItemViewHeight)
17+
.frame(width: appItemViewWidth, height: appItemViewHeight)
1918
} else {
2019
Color.clear
21-
.frame(width: appItemViewWidth,
22-
height: appItemViewHeight)
20+
.frame(width: appItemViewWidth, height: appItemViewHeight)
2321
}
2422
}
2523
}
@@ -35,6 +33,11 @@ struct AppGridView: View {
3533
let screenWidth = NSScreen.main?.visibleFrame.width else {
3634
return
3735
}
36+
if screenWidth <= 3456.0 / 2 {
37+
appGridViewHorizontalPadding = 100.0
38+
appGridViewVerticalPadding = 40.0
39+
}
40+
print("screenHeight", screenHeight, "screenWidth", screenWidth)
3841
appItemViewWidth = (screenWidth - appGridViewHorizontalPadding * 2 ) / 7.0
3942
appItemViewHeight = (screenHeight - appGridViewVerticalPadding * 2 ) / 5.0
4043
}

FuckAppleLaunchpad/View/AppItemView.swift

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
import SwiftUI
2+
import AppKit
23

34
struct AppItemView: View {
45
let app: Application
56

67
var body: some View {
7-
VStack(spacing: 16) {
8+
VStack(spacing: 12) {
89
if let icon = app.icon {
910
Image(nsImage: icon)
1011
.resizable()
11-
.scaledToFit()
1212
.frame(width: 104, height: 104)
13+
.scaledToFit()
1314
.clipShape(RoundedRectangle(cornerRadius: 12))
14-
.shadow(color: .black.opacity(0.3), radius: 3, x: 0, y: 2)
1515
} else {
1616
Image(systemName: "app.fill")
1717
.resizable()
18-
.scaledToFit()
1918
.frame(width: 104, height: 104)
19+
.scaledToFit()
2020
.clipShape(RoundedRectangle(cornerRadius: 12))
2121
.foregroundColor(.gray)
2222
.shadow(color: .black.opacity(0.3), radius: 3, x: 0, y: 2)
@@ -34,3 +34,13 @@ struct AppItemView: View {
3434
}
3535
}
3636
}
37+
38+
struct AppItemView_Previews: PreviewProvider {
39+
static var previews: some View {
40+
AppItemView(app: .init(name: "Xcode",
41+
path: URL(string: "abc")!,
42+
icon: NSImage(named: "launchIcon")))
43+
.frame(width: 400, height: 400)
44+
.background(Color.gray)
45+
}
46+
}

FuckAppleLaunchpad/View/ContentView.swift

Lines changed: 0 additions & 58 deletions
This file was deleted.

0 commit comments

Comments
 (0)