From 45190c1b971ecb89a003c6fb616d6b5eec040ca1 Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Wed, 7 May 2025 23:22:12 -0500 Subject: [PATCH 1/2] Changed the window style to more consistent. Handling keyboard navigation to fix bug when using plain window style. Focusing list by default so it is highlighted. Made welcome action symbols a bit smaller --- .../Views/RecentProjectsListView.swift | 79 ++++++++++++++++++- .../Welcome/Views/WelcomeActionView.swift | 2 +- .../Features/Welcome/Views/WelcomeView.swift | 17 ++-- .../Welcome/Views/WelcomeWindow.swift | 44 ++++++++--- .../Welcome/Views/WelcomeWindowView.swift | 7 +- 5 files changed, 127 insertions(+), 22 deletions(-) diff --git a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift index 381b61571..181f7b5e0 100644 --- a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift +++ b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift @@ -9,9 +9,14 @@ import SwiftUI import CoreSpotlight struct RecentProjectsListView: View { + @Environment(\.colorScheme) + var colorScheme + + @FocusState private var isFocused: Bool @State private var selection: Set - @State var recentProjects: [URL] + @State private var recentProjects: [URL] + @State private var eventMonitor: Any? private let openDocument: (URL?, @escaping () -> Void) -> Void private let dismissWindow: () -> Void @@ -37,6 +42,7 @@ struct RecentProjectsListView: View { List(recentProjects, id: \.self, selection: $selection) { project in RecentProjectListItem(projectPath: project) } + .focused($isFocused) .listStyle(.sidebar) .contextMenu(forSelectionType: URL.self) { items in switch items.count { @@ -66,7 +72,15 @@ struct RecentProjectsListView: View { .onDeleteCommand { removeRecentProjects() } - .background(EffectView(.underWindowBackground, blendingMode: .behindWindow)) + .background { + if self.colorScheme == .dark { + Color(.black).opacity(0.075) + .background(.thickMaterial) + } else { + Color(.white).opacity(0.6) + .background(.regularMaterial) + } + } .background { Button("") { selection.forEach { openDocument($0, dismissWindow) } @@ -84,6 +98,22 @@ struct RecentProjectsListView: View { .onReceive(NotificationCenter.default.publisher(for: RecentProjectsStore.didUpdateNotification)) { _ in updateRecentProjects() } + .onAppear { + isFocused = true + // NOTE: workaround for FB16112506 + self.eventMonitor = NSEvent.addLocalMonitorForEvents(matching: [.keyDown]) { event in + switch event.keyCode { + case 126: // Up Arrow + return self.handleArrowUpKeyPressed() == .handled ? nil : event + case 125: // Down Arrow + return self.handleArrowDownKeyPressed() == .handled ? nil : event + case 76, 36: // Enter and Return Arrow + return self.handleReturnKeyPressed() == .handled ? nil : event + default: + return event + } + } + } } func removeRecentProjects() { @@ -93,4 +123,49 @@ struct RecentProjectsListView: View { func updateRecentProjects() { recentProjects = RecentProjectsStore.recentProjectURLs() } + + // MARK: - Key Handling + + enum KeyHandlingResult { + case handled + case notHandled + } + + @discardableResult + private func handleArrowUpKeyPressed() -> KeyHandlingResult { + guard let current = currentSelectedIndex() else { + selection = Set(recentProjects.suffix(1)) // select last if none selected + return .handled + } + if current > 0 { + selection = [recentProjects[current - 1]] + return .handled + } + return .handled + } + + @discardableResult + private func handleArrowDownKeyPressed() -> KeyHandlingResult { + guard let current = currentSelectedIndex() else { + selection = Set(recentProjects.prefix(1)) // select first if none selected + return .handled + } + if current < recentProjects.count - 1 { + selection = [recentProjects[current + 1]] + return .handled + } + return .handled + } + + @discardableResult + private func handleReturnKeyPressed() -> KeyHandlingResult { + guard let selected = selection.first else { return .notHandled } + openDocument(selected, dismissWindow) + return .handled + } + + private func currentSelectedIndex() -> Int? { + guard let selected = selection.first else { return nil } + return recentProjects.firstIndex(of: selected) + } } diff --git a/CodeEdit/Features/Welcome/Views/WelcomeActionView.swift b/CodeEdit/Features/Welcome/Views/WelcomeActionView.swift index ded382068..47d5b8959 100644 --- a/CodeEdit/Features/Welcome/Views/WelcomeActionView.swift +++ b/CodeEdit/Features/Welcome/Views/WelcomeActionView.swift @@ -24,7 +24,7 @@ struct WelcomeActionView: View { Image(systemName: iconName) .aspectRatio(contentMode: .fit) .foregroundColor(.secondary) - .font(.system(size: 20)) + .font(.system(size: 17, weight: .medium)) .frame(width: 24) Text(title) .font(.system(size: 13, weight: .semibold)) diff --git a/CodeEdit/Features/Welcome/Views/WelcomeView.swift b/CodeEdit/Features/Welcome/Views/WelcomeView.swift index a1529859e..9c9ef8298 100644 --- a/CodeEdit/Features/Welcome/Views/WelcomeView.swift +++ b/CodeEdit/Features/Welcome/Views/WelcomeView.swift @@ -173,7 +173,6 @@ struct WelcomeView: View { copyInformation() } .help("Copy System Information to Clipboard") - Spacer().frame(height: 40) HStack { VStack(alignment: .leading, spacing: 8) { @@ -207,12 +206,16 @@ struct WelcomeView: View { .padding(.horizontal, 56) .padding(.bottom, 16) .frame(width: 460) - .background( - colorScheme == .dark - ? Color(.black).opacity(0.2) - : Color(.white).opacity(controlActiveState == .inactive ? 1.0 : 0.5) - ) - .background(EffectView(.underWindowBackground, blendingMode: .behindWindow)) + .frame(maxHeight: .infinity) + .background { + if self.colorScheme == .dark { + Color(.black).opacity(0.275) + .background(.ultraThickMaterial) + } else { + Color(.white) + .background(.regularMaterial) + } + } } private var dismissButton: some View { diff --git a/CodeEdit/Features/Welcome/Views/WelcomeWindow.swift b/CodeEdit/Features/Welcome/Views/WelcomeWindow.swift index e80014e48..364105a0f 100644 --- a/CodeEdit/Features/Welcome/Views/WelcomeWindow.swift +++ b/CodeEdit/Features/Welcome/Views/WelcomeWindow.swift @@ -8,24 +8,46 @@ import SwiftUI struct WelcomeWindow: Scene { - @ObservedObject var settings = Settings.shared + var windowContent: some View { + ContentView() + .task { + if let window = NSApp.findWindow(.welcome) { + window.standardWindowButton(.closeButton)?.isHidden = true + window.standardWindowButton(.miniaturizeButton)?.isHidden = true + window.standardWindowButton(.zoomButton)?.isHidden = true + window.isMovableByWindowBackground = true + } + } + } + var body: some Scene { - Window("Welcome To CodeEdit", id: SceneID.welcome.rawValue) { - ContentView() + #if swift(>=5.9) // Needed to safely use availability in Scene builder + if #available(macOS 15, *) { + return Window("Welcome To CodeEdit", id: SceneID.welcome.rawValue) { + windowContent + .frame(width: 740, height: 460) + } + .windowStyle(.plain) + .windowResizability(.contentSize) + .defaultLaunchBehavior(.presented) + } else { + return Window("Welcome To CodeEdit", id: SceneID.welcome.rawValue) { + windowContent + .frame(width: 740, height: 432) + } + .windowStyle(.hiddenTitleBar) + .windowResizability(.contentSize) + } + #else + return Window("Welcome To CodeEdit", id: SceneID.welcome.rawValue) { + windowContent .frame(width: 740, height: 432) - .task { - if let window = NSApp.findWindow(.welcome) { - window.standardWindowButton(.closeButton)?.isHidden = true - window.standardWindowButton(.miniaturizeButton)?.isHidden = true - window.standardWindowButton(.zoomButton)?.isHidden = true - window.isMovableByWindowBackground = true - } - } } .windowStyle(.hiddenTitleBar) .windowResizability(.contentSize) + #endif } struct ContentView: View { diff --git a/CodeEdit/Features/Welcome/Views/WelcomeWindowView.swift b/CodeEdit/Features/Welcome/Views/WelcomeWindowView.swift index 378f93084..f321d6131 100644 --- a/CodeEdit/Features/Welcome/Views/WelcomeWindowView.swift +++ b/CodeEdit/Features/Welcome/Views/WelcomeWindowView.swift @@ -30,8 +30,13 @@ struct WelcomeWindowView: View { dismissWindow: dismissWindow ) RecentProjectsListView(openDocument: openDocument, dismissWindow: dismissWindow) - .frame(width: 280) } + .clipShape(.rect(cornerRadius: 8)) + .onAppear { + NSApplication.shared.windows.first?.isMovableByWindowBackground = true + NSApplication.shared.windows.first?.hasShadow = true + } + .cursor(.current) .edgesIgnoringSafeArea(.top) .onDrop(of: [.fileURL], isTargeted: .constant(true)) { providers in NSApp.activate(ignoringOtherApps: true) From 354130df914e609950fc183d8e72c5395aac04ee Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Wed, 7 May 2025 23:55:14 -0500 Subject: [PATCH 2/2] Fixed bug where removing recents via context menu was removing wrong items --- .../Welcome/Views/RecentProjectsListView.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift index 181f7b5e0..8593979f0 100644 --- a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift +++ b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift @@ -60,7 +60,7 @@ struct RecentProjectsListView: View { } Button("Remove from Recents") { - removeRecentProjects() + removeRecentProjects(items) } } } primaryAction: { items in @@ -70,7 +70,7 @@ struct RecentProjectsListView: View { selection.map { NSItemProvider(object: $0.path(percentEncoded: false) as NSString) } } .onDeleteCommand { - removeRecentProjects() + removeRecentProjects(selection) } .background { if self.colorScheme == .dark { @@ -116,12 +116,15 @@ struct RecentProjectsListView: View { } } - func removeRecentProjects() { - recentProjects = RecentProjectsStore.removeRecentProjects(selection) + func removeRecentProjects(_ items: Set) { + recentProjects = RecentProjectsStore.removeRecentProjects(items) } func updateRecentProjects() { recentProjects = RecentProjectsStore.recentProjectURLs() + if !recentProjects.isEmpty { + selection = Set(recentProjects.prefix(1)) + } } // MARK: - Key Handling