Skip to content

Welcome Window Improvements #2040

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 84 additions & 6 deletions CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<URL>
@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
Expand All @@ -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 {
Expand All @@ -54,7 +60,7 @@ struct RecentProjectsListView: View {
}

Button("Remove from Recents") {
removeRecentProjects()
removeRecentProjects(items)
}
}
} primaryAction: { items in
Expand All @@ -64,9 +70,17 @@ struct RecentProjectsListView: View {
selection.map { NSItemProvider(object: $0.path(percentEncoded: false) as NSString) }
}
.onDeleteCommand {
removeRecentProjects()
removeRecentProjects(selection)
}
.background {
if self.colorScheme == .dark {
Color(.black).opacity(0.075)
.background(.thickMaterial)
} else {
Color(.white).opacity(0.6)
.background(.regularMaterial)
}
}
.background(EffectView(.underWindowBackground, blendingMode: .behindWindow))
.background {
Button("") {
selection.forEach { openDocument($0, dismissWindow) }
Expand All @@ -84,13 +98,77 @@ 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() {
recentProjects = RecentProjectsStore.removeRecentProjects(selection)
func removeRecentProjects(_ items: Set<URL>) {
recentProjects = RecentProjectsStore.removeRecentProjects(items)
}

func updateRecentProjects() {
recentProjects = RecentProjectsStore.recentProjectURLs()
if !recentProjects.isEmpty {
selection = Set(recentProjects.prefix(1))
}
}

// 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)
}
}
2 changes: 1 addition & 1 deletion CodeEdit/Features/Welcome/Views/WelcomeActionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
17 changes: 10 additions & 7 deletions CodeEdit/Features/Welcome/Views/WelcomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ struct WelcomeView: View {
copyInformation()
}
.help("Copy System Information to Clipboard")

Spacer().frame(height: 40)
HStack {
VStack(alignment: .leading, spacing: 8) {
Expand Down Expand Up @@ -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 {
Expand Down
44 changes: 33 additions & 11 deletions CodeEdit/Features/Welcome/Views/WelcomeWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 6 additions & 1 deletion CodeEdit/Features/Welcome/Views/WelcomeWindowView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading