Procházet zdrojové kódy

Add light settings page

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 před 3 týdny
rodič
revize
47919fe10b
1 změnil soubory, kde provedl 192 přidání a 2 odebrání
  1. 192 2
      App for Indeed/Views/DashboardView.swift

+ 192 - 2
App for Indeed/Views/DashboardView.swift

@@ -45,6 +45,11 @@ final class DashboardView: NSView, NSTextFieldDelegate {
45
         static let selectionFillHover = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 0.2)
45
         static let selectionFillHover = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 0.2)
46
         static let neutralHoverFill = NSColor(srgbRed: 240 / 255, green: 240 / 255, blue: 240 / 255, alpha: 1)
46
         static let neutralHoverFill = NSColor(srgbRed: 240 / 255, green: 240 / 255, blue: 240 / 255, alpha: 1)
47
         static let sidebarRowHoverFill = NSColor(srgbRed: 0, green: 0, blue: 0, alpha: 0.04)
47
         static let sidebarRowHoverFill = NSColor(srgbRed: 0, green: 0, blue: 0, alpha: 0.04)
48
+        static let settingsPageBackground = NSColor(srgbRed: 1, green: 1, blue: 1, alpha: 1)
49
+        static let settingsGroupBackground = NSColor(srgbRed: 1, green: 1, blue: 1, alpha: 1)
50
+        static let settingsIconBackground = NSColor(srgbRed: 239 / 255, green: 244 / 255, blue: 252 / 255, alpha: 1)
51
+        static let settingsDivider = NSColor(srgbRed: 228 / 255, green: 228 / 255, blue: 228 / 255, alpha: 1)
52
+        static let settingsPrimaryText = NSColor(srgbRed: 45 / 255, green: 45 / 255, blue: 45 / 255, alpha: 1)
48
     }
53
     }
49
 
54
 
50
     /// Multiline `NSTextField` often ignores `alignment` for wrapped runs; explicit paragraph alignment matches the title.
55
     /// Multiline `NSTextField` often ignores `alignment` for wrapped runs; explicit paragraph alignment matches the title.
@@ -91,6 +96,8 @@ final class DashboardView: NSView, NSTextFieldDelegate {
91
     private let savedJobsScrollView = NSScrollView()
96
     private let savedJobsScrollView = NSScrollView()
92
     private let savedJobsDocumentView = JobListingsDocumentView()
97
     private let savedJobsDocumentView = JobListingsDocumentView()
93
     private let savedJobsStack = NSStackView()
98
     private let savedJobsStack = NSStackView()
99
+    private let settingsPageContainer = NSView()
100
+    private let themeControl = NSSegmentedControl(labels: ["System", "Light", "Dark"], trackingMode: .selectOne, target: nil, action: nil)
94
 
101
 
95
     private var currentSidebarItems: [SidebarItem] = []
102
     private var currentSidebarItems: [SidebarItem] = []
96
     private var selectedSidebarIndex: Int = 0
103
     private var selectedSidebarIndex: Int = 0
@@ -769,8 +776,10 @@ final class DashboardView: NSView, NSTextFieldDelegate {
769
 
776
 
770
         nonHomeGenericContainer.translatesAutoresizingMaskIntoConstraints = false
777
         nonHomeGenericContainer.translatesAutoresizingMaskIntoConstraints = false
771
         savedJobsPageContainer.translatesAutoresizingMaskIntoConstraints = false
778
         savedJobsPageContainer.translatesAutoresizingMaskIntoConstraints = false
779
+        settingsPageContainer.translatesAutoresizingMaskIntoConstraints = false
772
         nonHomeHost.addSubview(nonHomeGenericContainer)
780
         nonHomeHost.addSubview(nonHomeGenericContainer)
773
         nonHomeHost.addSubview(savedJobsPageContainer)
781
         nonHomeHost.addSubview(savedJobsPageContainer)
782
+        nonHomeHost.addSubview(settingsPageContainer)
774
 
783
 
775
         NSLayoutConstraint.activate([
784
         NSLayoutConstraint.activate([
776
             nonHomeGenericContainer.leadingAnchor.constraint(equalTo: nonHomeHost.leadingAnchor),
785
             nonHomeGenericContainer.leadingAnchor.constraint(equalTo: nonHomeHost.leadingAnchor),
@@ -781,7 +790,12 @@ final class DashboardView: NSView, NSTextFieldDelegate {
781
             savedJobsPageContainer.leadingAnchor.constraint(equalTo: nonHomeHost.leadingAnchor),
790
             savedJobsPageContainer.leadingAnchor.constraint(equalTo: nonHomeHost.leadingAnchor),
782
             savedJobsPageContainer.trailingAnchor.constraint(equalTo: nonHomeHost.trailingAnchor),
791
             savedJobsPageContainer.trailingAnchor.constraint(equalTo: nonHomeHost.trailingAnchor),
783
             savedJobsPageContainer.topAnchor.constraint(equalTo: nonHomeHost.topAnchor),
792
             savedJobsPageContainer.topAnchor.constraint(equalTo: nonHomeHost.topAnchor),
784
-            savedJobsPageContainer.bottomAnchor.constraint(equalTo: nonHomeHost.bottomAnchor)
793
+            savedJobsPageContainer.bottomAnchor.constraint(equalTo: nonHomeHost.bottomAnchor),
794
+
795
+            settingsPageContainer.leadingAnchor.constraint(equalTo: nonHomeHost.leadingAnchor),
796
+            settingsPageContainer.trailingAnchor.constraint(equalTo: nonHomeHost.trailingAnchor),
797
+            settingsPageContainer.topAnchor.constraint(equalTo: nonHomeHost.topAnchor),
798
+            settingsPageContainer.bottomAnchor.constraint(equalTo: nonHomeHost.bottomAnchor)
785
         ])
799
         ])
786
 
800
 
787
         nonHomeTitleLabel.font = .systemFont(ofSize: 22, weight: .bold)
801
         nonHomeTitleLabel.font = .systemFont(ofSize: 22, weight: .bold)
@@ -872,6 +886,162 @@ final class DashboardView: NSView, NSTextFieldDelegate {
872
             savedJobsDocumentView.leadingAnchor.constraint(equalTo: savedJobsScrollView.contentView.leadingAnchor),
886
             savedJobsDocumentView.leadingAnchor.constraint(equalTo: savedJobsScrollView.contentView.leadingAnchor),
873
             savedJobsDocumentView.widthAnchor.constraint(equalTo: savedJobsScrollView.contentView.widthAnchor)
887
             savedJobsDocumentView.widthAnchor.constraint(equalTo: savedJobsScrollView.contentView.widthAnchor)
874
         ])
888
         ])
889
+
890
+        configureSettingsPage()
891
+    }
892
+
893
+    private func configureSettingsPage() {
894
+        settingsPageContainer.wantsLayer = true
895
+        settingsPageContainer.layer?.backgroundColor = Theme.settingsPageBackground.cgColor
896
+        settingsPageContainer.isHidden = true
897
+
898
+        let contentStack = NSStackView()
899
+        contentStack.orientation = .vertical
900
+        contentStack.spacing = 26
901
+        contentStack.alignment = .leading
902
+        contentStack.translatesAutoresizingMaskIntoConstraints = false
903
+
904
+        let settingsSection = makeSettingsSection(rows: [
905
+            makeSettingsRow(title: "Share App", systemImage: "square.and.arrow.up", accessory: nil),
906
+            makeSettingsRow(title: "Theme", systemImage: "circle.lefthalf.filled", accessory: makeThemeControl()),
907
+            makeSettingsRow(title: "More Apps", systemImage: "square.grid.2x2", accessory: nil)
908
+        ])
909
+
910
+        let aboutTitle = NSTextField(labelWithString: "About")
911
+        aboutTitle.font = .systemFont(ofSize: 15, weight: .bold)
912
+        aboutTitle.textColor = Theme.settingsPrimaryText
913
+        aboutTitle.alignment = .left
914
+
915
+        let aboutSection = makeSettingsSection(rows: [
916
+            makeSettingsRow(title: "Support", systemImage: "questionmark.circle", accessory: nil),
917
+            makeSettingsRow(title: "Terms of Use", systemImage: "doc.text", accessory: nil),
918
+            makeSettingsRow(title: "Privacy Policy", systemImage: "shield", accessory: nil)
919
+        ])
920
+
921
+        let aboutStack = NSStackView(views: [aboutTitle, aboutSection])
922
+        aboutStack.orientation = .vertical
923
+        aboutStack.spacing = 14
924
+        aboutStack.alignment = .leading
925
+        aboutStack.translatesAutoresizingMaskIntoConstraints = false
926
+
927
+        contentStack.addArrangedSubview(settingsSection)
928
+        contentStack.addArrangedSubview(aboutStack)
929
+        settingsPageContainer.addSubview(contentStack)
930
+
931
+        NSLayoutConstraint.activate([
932
+            contentStack.leadingAnchor.constraint(equalTo: settingsPageContainer.leadingAnchor, constant: 42),
933
+            contentStack.trailingAnchor.constraint(lessThanOrEqualTo: settingsPageContainer.trailingAnchor, constant: -42),
934
+            contentStack.topAnchor.constraint(equalTo: settingsPageContainer.topAnchor, constant: 48),
935
+            settingsSection.widthAnchor.constraint(equalTo: contentStack.widthAnchor),
936
+            aboutStack.widthAnchor.constraint(equalTo: contentStack.widthAnchor),
937
+            aboutSection.widthAnchor.constraint(equalTo: aboutStack.widthAnchor),
938
+            contentStack.widthAnchor.constraint(equalTo: settingsPageContainer.widthAnchor, constant: -84)
939
+        ])
940
+    }
941
+
942
+    private func makeThemeControl() -> NSSegmentedControl {
943
+        themeControl.target = self
944
+        themeControl.action = #selector(didChangeThemeSelection(_:))
945
+        themeControl.selectedSegment = 0
946
+        themeControl.segmentStyle = .rounded
947
+        themeControl.controlSize = .large
948
+        themeControl.font = .systemFont(ofSize: 13, weight: .semibold)
949
+        themeControl.translatesAutoresizingMaskIntoConstraints = false
950
+        themeControl.widthAnchor.constraint(equalToConstant: 204).isActive = true
951
+        themeControl.heightAnchor.constraint(equalToConstant: 30).isActive = true
952
+        return themeControl
953
+    }
954
+
955
+    private func makeSettingsSection(rows: [NSView]) -> NSView {
956
+        let section = NSStackView()
957
+        section.orientation = .vertical
958
+        section.spacing = 0
959
+        section.alignment = .leading
960
+        section.translatesAutoresizingMaskIntoConstraints = false
961
+        section.wantsLayer = true
962
+        section.layer?.backgroundColor = Theme.settingsGroupBackground.cgColor
963
+        section.layer?.cornerRadius = 14
964
+        section.layer?.borderWidth = 1
965
+        section.layer?.borderColor = Theme.border.cgColor
966
+        section.layer?.masksToBounds = true
967
+
968
+        for (index, row) in rows.enumerated() {
969
+            section.addArrangedSubview(row)
970
+            row.widthAnchor.constraint(equalTo: section.widthAnchor).isActive = true
971
+
972
+            if index < rows.count - 1 {
973
+                let divider = NSView()
974
+                divider.translatesAutoresizingMaskIntoConstraints = false
975
+                divider.wantsLayer = true
976
+                divider.layer?.backgroundColor = Theme.settingsDivider.cgColor
977
+                section.addArrangedSubview(divider)
978
+                NSLayoutConstraint.activate([
979
+                    divider.heightAnchor.constraint(equalToConstant: 1),
980
+                    divider.leadingAnchor.constraint(equalTo: section.leadingAnchor),
981
+                    divider.trailingAnchor.constraint(equalTo: section.trailingAnchor)
982
+                ])
983
+            }
984
+        }
985
+
986
+        return section
987
+    }
988
+
989
+    private func makeSettingsRow(title: String, systemImage: String, accessory: NSView?) -> NSView {
990
+        let row = NSView()
991
+        row.translatesAutoresizingMaskIntoConstraints = false
992
+        row.wantsLayer = true
993
+
994
+        let iconTile = NSView()
995
+        iconTile.translatesAutoresizingMaskIntoConstraints = false
996
+        iconTile.wantsLayer = true
997
+        iconTile.layer?.backgroundColor = Theme.settingsIconBackground.cgColor
998
+        iconTile.layer?.cornerRadius = 9
999
+
1000
+        let icon = NSImageView()
1001
+        icon.translatesAutoresizingMaskIntoConstraints = false
1002
+        icon.symbolConfiguration = NSImage.SymbolConfiguration(pointSize: 15, weight: .medium)
1003
+        icon.image = NSImage(systemSymbolName: systemImage, accessibilityDescription: title)
1004
+        icon.contentTintColor = Theme.brandBlue
1005
+
1006
+        let titleLabel = NSTextField(labelWithString: title)
1007
+        titleLabel.font = .systemFont(ofSize: 17, weight: .semibold)
1008
+        titleLabel.textColor = Theme.settingsPrimaryText
1009
+        titleLabel.alignment = .left
1010
+
1011
+        let rowStack = NSStackView()
1012
+        rowStack.orientation = .horizontal
1013
+        rowStack.spacing = 16
1014
+        rowStack.alignment = .centerY
1015
+        rowStack.translatesAutoresizingMaskIntoConstraints = false
1016
+
1017
+        let spacer = NSView()
1018
+        spacer.translatesAutoresizingMaskIntoConstraints = false
1019
+        spacer.setContentHuggingPriority(NSLayoutConstraint.Priority(1), for: .horizontal)
1020
+
1021
+        iconTile.addSubview(icon)
1022
+        rowStack.addArrangedSubview(iconTile)
1023
+        rowStack.addArrangedSubview(titleLabel)
1024
+        rowStack.addArrangedSubview(spacer)
1025
+        if let accessory {
1026
+            rowStack.addArrangedSubview(accessory)
1027
+        }
1028
+        row.addSubview(rowStack)
1029
+
1030
+        NSLayoutConstraint.activate([
1031
+            row.heightAnchor.constraint(equalToConstant: 68),
1032
+            iconTile.widthAnchor.constraint(equalToConstant: 38),
1033
+            iconTile.heightAnchor.constraint(equalToConstant: 38),
1034
+            icon.centerXAnchor.constraint(equalTo: iconTile.centerXAnchor),
1035
+            icon.centerYAnchor.constraint(equalTo: iconTile.centerYAnchor),
1036
+            icon.widthAnchor.constraint(equalToConstant: 20),
1037
+            icon.heightAnchor.constraint(equalToConstant: 20),
1038
+            rowStack.leadingAnchor.constraint(equalTo: row.leadingAnchor, constant: 16),
1039
+            rowStack.trailingAnchor.constraint(equalTo: row.trailingAnchor, constant: -16),
1040
+            rowStack.topAnchor.constraint(equalTo: row.topAnchor),
1041
+            rowStack.bottomAnchor.constraint(equalTo: row.bottomAnchor)
1042
+        ])
1043
+
1044
+        return row
875
     }
1045
     }
876
 
1046
 
877
     private func reloadSavedJobsListings() {
1047
     private func reloadSavedJobsListings() {
@@ -909,16 +1079,25 @@ final class DashboardView: NSView, NSTextFieldDelegate {
909
         return currentSidebarItems[index].title == "Home"
1079
         return currentSidebarItems[index].title == "Home"
910
     }
1080
     }
911
 
1081
 
1082
+    private func isSettingsSidebarIndex(_ index: Int) -> Bool {
1083
+        guard index >= 0, index < currentSidebarItems.count else { return false }
1084
+        return currentSidebarItems[index].title == "Settings"
1085
+    }
1086
+
912
     private func updateMainContentVisibility() {
1087
     private func updateMainContentVisibility() {
913
         let home = isHomeSidebarIndex(selectedSidebarIndex)
1088
         let home = isHomeSidebarIndex(selectedSidebarIndex)
914
         let savedJobs = isSavedJobsSidebarIndex(selectedSidebarIndex)
1089
         let savedJobs = isSavedJobsSidebarIndex(selectedSidebarIndex)
1090
+        let settings = isSettingsSidebarIndex(selectedSidebarIndex)
915
         mainOverlay.isHidden = !home
1091
         mainOverlay.isHidden = !home
916
         nonHomeHost.isHidden = home
1092
         nonHomeHost.isHidden = home
917
-        nonHomeGenericContainer.isHidden = savedJobs
1093
+        nonHomeGenericContainer.isHidden = savedJobs || settings
918
         savedJobsPageContainer.isHidden = !savedJobs
1094
         savedJobsPageContainer.isHidden = !savedJobs
1095
+        settingsPageContainer.isHidden = !settings
919
         if !home, selectedSidebarIndex < currentSidebarItems.count {
1096
         if !home, selectedSidebarIndex < currentSidebarItems.count {
920
             if savedJobs {
1097
             if savedJobs {
921
                 reloadSavedJobsListings()
1098
                 reloadSavedJobsListings()
1099
+            } else if settings {
1100
+                window?.makeFirstResponder(nil)
922
             } else {
1101
             } else {
923
                 nonHomeTitleLabel.stringValue = currentSidebarItems[selectedSidebarIndex].title
1102
                 nonHomeTitleLabel.stringValue = currentSidebarItems[selectedSidebarIndex].title
924
             }
1103
             }
@@ -1186,6 +1365,17 @@ final class DashboardView: NSView, NSTextFieldDelegate {
1186
         NSWorkspace.shared.open(url)
1365
         NSWorkspace.shared.open(url)
1187
     }
1366
     }
1188
 
1367
 
1368
+    @objc private func didChangeThemeSelection(_ sender: NSSegmentedControl) {
1369
+        switch sender.selectedSegment {
1370
+        case 1:
1371
+            NSApp.appearance = NSAppearance(named: .aqua)
1372
+        case 2:
1373
+            NSApp.appearance = NSAppearance(named: .darkAqua)
1374
+        default:
1375
+            NSApp.appearance = nil
1376
+        }
1377
+    }
1378
+
1189
     private func selectSidebarItem(at index: Int) {
1379
     private func selectSidebarItem(at index: Int) {
1190
         guard index >= 0, index < currentSidebarItems.count else { return }
1380
         guard index >= 0, index < currentSidebarItems.count else { return }
1191
         let selectingHome = isHomeSidebarIndex(index)
1381
         let selectingHome = isHomeSidebarIndex(index)