Skip to content

Commit 36a6f3c

Browse files
authored
Merge pull request #119 from pkasila/open-quickly
Add "Open Quickly" and module to work with overlays
2 parents fe2ad8b + bbf597a commit 36a6f3c

17 files changed

+451
-82
lines changed

CodeEdit.xcodeproj/project.pbxproj

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
04660F6427E3ACAF00477777 /* Appearances.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04660F6327E3ACAF00477777 /* Appearances.swift */; };
1717
04660F6627E3ACEF00477777 /* ReopenBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04660F6527E3ACEF00477777 /* ReopenBehavior.swift */; };
1818
04660F6A27E51E5C00477777 /* CodeEditWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04660F6927E51E5C00477777 /* CodeEditWindowController.swift */; };
19+
0485EB1927E70F4900138301 /* QuickOpenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0485EB1827E70F4900138301 /* QuickOpenView.swift */; };
20+
0485EB1D27E7338100138301 /* QuickOpenItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0485EB1C27E7338100138301 /* QuickOpenItem.swift */; };
21+
0485EB1F27E7458B00138301 /* WorkspaceCodeFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0485EB1E27E7458B00138301 /* WorkspaceCodeFileView.swift */; };
22+
0485EB2327E7791400138301 /* QuickOpenPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0485EB2227E7791400138301 /* QuickOpenPreviewView.swift */; };
23+
0485EB2527E7B9C800138301 /* Overlays in Frameworks */ = {isa = PBXBuildFile; productRef = 0485EB2427E7B9C800138301 /* Overlays */; };
1924
286620A527E4AB6900E18C2B /* BreadcrumbsComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286620A427E4AB6900E18C2B /* BreadcrumbsComponent.swift */; };
2025
2875A46D27E3BE5B007805F8 /* BreadcrumbsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2875A46C27E3BE5B007805F8 /* BreadcrumbsView.swift */; };
2126
287776E727E3413200D46668 /* SideBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287776E627E3413200D46668 /* SideBar.swift */; };
@@ -68,6 +73,10 @@
6873
04660F6527E3ACEF00477777 /* ReopenBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReopenBehavior.swift; sourceTree = "<group>"; };
6974
04660F6927E51E5C00477777 /* CodeEditWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeEditWindowController.swift; sourceTree = "<group>"; };
7075
0468438427DC76E200F8E88E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
76+
0485EB1827E70F4900138301 /* QuickOpenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickOpenView.swift; sourceTree = "<group>"; };
77+
0485EB1C27E7338100138301 /* QuickOpenItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickOpenItem.swift; sourceTree = "<group>"; };
78+
0485EB1E27E7458B00138301 /* WorkspaceCodeFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceCodeFileView.swift; sourceTree = "<group>"; };
79+
0485EB2227E7791400138301 /* QuickOpenPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickOpenPreviewView.swift; sourceTree = "<group>"; };
7180
04ADA0C827E5D29000BF00B2 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
7281
04ADA0CA27E5D41F00BF00B2 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Localizable.strings; sourceTree = "<group>"; };
7382
04F2BF0E27DBB28E0024EAB1 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
@@ -106,6 +115,7 @@
106115
buildActionMask = 2147483647;
107116
files = (
108117
5C403B8F27E20F8000788241 /* WorkspaceClient in Frameworks */,
118+
0485EB2527E7B9C800138301 /* Overlays in Frameworks */,
109119
B65E614627E6765D00255275 /* Introspect in Frameworks */,
110120
D70F5E2C27E4E8CF004EE4B9 /* WelcomeModule in Frameworks */,
111121
28CE5EA027E6493D0065D29C /* StatusBar in Frameworks */,
@@ -136,6 +146,7 @@
136146
043C321327E31FF6006AE443 /* CodeEditDocumentController.swift */,
137147
043C321527E3201F006AE443 /* WorkspaceDocument.swift */,
138148
04660F6927E51E5C00477777 /* CodeEditWindowController.swift */,
149+
0485EB1E27E7458B00138301 /* WorkspaceCodeFileView.swift */,
139150
);
140151
path = Documents;
141152
sourceTree = "<group>";
@@ -150,6 +161,16 @@
150161
path = Models;
151162
sourceTree = "<group>";
152163
};
164+
0485EB1727E7016400138301 /* Quick Open */ = {
165+
isa = PBXGroup;
166+
children = (
167+
0485EB1827E70F4900138301 /* QuickOpenView.swift */,
168+
0485EB1C27E7338100138301 /* QuickOpenItem.swift */,
169+
0485EB2227E7791400138301 /* QuickOpenPreviewView.swift */,
170+
);
171+
path = "Quick Open";
172+
sourceTree = "<group>";
173+
};
153174
04F2BF1027DBB3AF0024EAB1 /* Settings */ = {
154175
isa = PBXGroup;
155176
children = (
@@ -234,6 +255,7 @@
234255
B658FB2E27DA9E0F00EA4DBD /* CodeEdit */ = {
235256
isa = PBXGroup;
236257
children = (
258+
0485EB1727E7016400138301 /* Quick Open */,
237259
04660F6027E3A68A00477777 /* Info.plist */,
238260
D72E1A8127E3B0A300EB11B9 /* Welcome */,
239261
043C321227E31FE8006AE443 /* Documents */,
@@ -305,6 +327,7 @@
305327
D70F5E2B27E4E8CF004EE4B9 /* WelcomeModule */,
306328
B65E614527E6765D00255275 /* Introspect */,
307329
28CE5E9F27E6493D0065D29C /* StatusBar */,
330+
0485EB2427E7B9C800138301 /* Overlays */,
308331
);
309332
productName = CodeEdit;
310333
productReference = B658FB2C27DA9E0F00EA4DBD /* CodeEdit.app */;
@@ -471,6 +494,7 @@
471494
files = (
472495
2B7A583527E4BA0100D25D4E /* AppDelegate.swift in Sources */,
473496
2875A46D27E3BE5B007805F8 /* BreadcrumbsView.swift in Sources */,
497+
0485EB1D27E7338100138301 /* QuickOpenItem.swift in Sources */,
474498
04540D5B27DD08C300E91B77 /* SettingsView.swift in Sources */,
475499
D72E1A8927E44D7C00EB11B9 /* WelcomeWindowView.swift in Sources */,
476500
04540D5C27DD08C300E91B77 /* GeneralSettingsView.swift in Sources */,
@@ -480,6 +504,8 @@
480504
34EE19BE27E0469C00F152CE /* BlurView.swift in Sources */,
481505
D7211D4327E066CE008F2ED7 /* Localized+Ex.swift in Sources */,
482506
287776E927E34BC700D46668 /* TabBar.swift in Sources */,
507+
0485EB1F27E7458B00138301 /* WorkspaceCodeFileView.swift in Sources */,
508+
0485EB1927E70F4900138301 /* QuickOpenView.swift in Sources */,
483509
286620A527E4AB6900E18C2B /* BreadcrumbsComponent.swift in Sources */,
484510
287776EF27E3515300D46668 /* TabBarItem.swift in Sources */,
485511
D72E1A8727E4242900EB11B9 /* RecentProjectsView.swift in Sources */,
@@ -493,6 +519,7 @@
493519
28B0A19827E385C300B73177 /* SideBarToolbarTop.swift in Sources */,
494520
345F667527DF6C180069BD69 /* FileTabRow.swift in Sources */,
495521
28FFE1BF27E3A441001939DB /* SideBarToolbarBottom.swift in Sources */,
522+
0485EB2327E7791400138301 /* QuickOpenPreviewView.swift in Sources */,
496523
);
497524
runOnlyForDeploymentPostprocessing = 0;
498525
};
@@ -870,6 +897,10 @@
870897
/* End XCRemoteSwiftPackageReference section */
871898

872899
/* Begin XCSwiftPackageProductDependency section */
900+
0485EB2427E7B9C800138301 /* Overlays */ = {
901+
isa = XCSwiftPackageProductDependency;
902+
productName = Overlays;
903+
};
873904
28CE5E9F27E6493D0065D29C /* StatusBar */ = {
874905
isa = XCSwiftPackageProductDependency;
875906
productName = StatusBar;

CodeEdit/AppDelegate.swift

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,47 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
4646
return true
4747
}
4848

49+
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
50+
if flag {
51+
return false
52+
}
53+
54+
handleOpen()
55+
56+
return false
57+
}
58+
59+
func applicationShouldOpenUntitledFile(_ sender: NSApplication) -> Bool {
60+
return false
61+
}
62+
63+
func handleOpen() {
64+
let behavior = ReopenBehavior(rawValue: UserDefaults.standard.string(forKey: ReopenBehavior.storageKey)
65+
?? ReopenBehavior.default.rawValue) ?? ReopenBehavior.default
66+
67+
switch behavior {
68+
case .welcome:
69+
openWelcome(self)
70+
case .openPanel:
71+
CodeEditDocumentController.shared.openDocument(self)
72+
case .newDocument:
73+
CodeEditDocumentController.shared.newDocument(self)
74+
}
75+
}
76+
77+
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
78+
CodeEditDocumentController.shared.documents.flatMap { doc in
79+
return doc.windowControllers
80+
}.forEach { windowContoller in
81+
if let windowContoller = windowContoller as? CodeEditWindowController {
82+
windowContoller.workspace?.close()
83+
}
84+
}
85+
return .terminateNow
86+
}
87+
88+
// MARK: - Open windows
89+
4990
@IBAction func openPreferences(_ sender: Any) {
5091
if let window = NSApp.windows.filter({ window in
5192
return (window.contentView as? NSHostingView<SettingsView>) != nil
@@ -86,43 +127,4 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
86127
window.contentView = NSHostingView(rootView: contentView)
87128
window.makeKeyAndOrderFront(self)
88129
}
89-
90-
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
91-
if flag {
92-
return false
93-
}
94-
95-
handleOpen()
96-
97-
return false
98-
}
99-
100-
func applicationShouldOpenUntitledFile(_ sender: NSApplication) -> Bool {
101-
return false
102-
}
103-
104-
func handleOpen() {
105-
let behavior = ReopenBehavior(rawValue: UserDefaults.standard.string(forKey: ReopenBehavior.storageKey)
106-
?? ReopenBehavior.default.rawValue) ?? ReopenBehavior.default
107-
108-
switch behavior {
109-
case .welcome:
110-
openWelcome(self)
111-
case .openPanel:
112-
CodeEditDocumentController.shared.openDocument(self)
113-
case .newDocument:
114-
CodeEditDocumentController.shared.newDocument(self)
115-
}
116-
}
117-
118-
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
119-
CodeEditDocumentController.shared.documents.flatMap { doc in
120-
return doc.windowControllers
121-
}.forEach { windowContoller in
122-
if let windowContoller = windowContoller as? CodeEditWindowController {
123-
windowContoller.workspace?.close()
124-
}
125-
}
126-
return .terminateNow
127-
}
128130
}

CodeEdit/Breadcrumbs/BreadcrumbsView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ struct BreadcrumbsView: View {
6060
}
6161

6262
private func fileInfo() {
63-
guard let projName = workspace.folderURL?.lastPathComponent,
63+
guard let projName = workspace.fileURL?.lastPathComponent,
6464
var components = file.url.pathComponents.split(separator: projName).last else { return }
6565
components.removeLast()
6666

CodeEdit/Documents/CodeEditWindowController.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
//
77

88
import Cocoa
9+
import SwiftUI
910
import CodeFile
11+
import Overlays
1012

1113
class CodeEditWindowController: NSWindowController {
1214

@@ -31,4 +33,23 @@ class CodeEditWindowController: NSWindowController {
3133
@IBAction func saveDocument(_ sender: Any) {
3234
getSelectedCodeFile()?.save(sender)
3335
}
36+
37+
@IBAction func openQuickly(_ sender: Any) {
38+
if let workspace = workspace, let state = workspace.quickOpenState {
39+
if let window = window?.childWindows?.filter({ window in
40+
return (window.contentView as? NSHostingView<QuickOpenView>) != nil
41+
}).first {
42+
window.close()
43+
return
44+
}
45+
46+
let panel = OverlayPanel()
47+
let contentView = QuickOpenView(state: state) {
48+
panel.close()
49+
}
50+
panel.contentView = NSHostingView(rootView: contentView)
51+
window?.addChildWindow(panel, ordered: .above)
52+
panel.makeKeyAndOrderFront(self)
53+
}
54+
}
3455
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//
2+
// WorkspaceCodeFileEditor.swift
3+
// CodeEdit
4+
//
5+
// Created by Pavel Kasila on 20.03.22.
6+
//
7+
8+
import SwiftUI
9+
import CodeFile
10+
import WorkspaceClient
11+
import StatusBar
12+
13+
struct WorkspaceCodeFileView: View {
14+
var windowController: NSWindowController
15+
@ObservedObject var workspace: WorkspaceDocument
16+
17+
@ViewBuilder var body: some View {
18+
if let item = workspace.openFileItems.first(where: { file in
19+
return file.id == workspace.selectedId
20+
}) {
21+
if let codeFile = workspace.openedCodeFiles[item] {
22+
CodeFileView(codeFile: codeFile)
23+
.safeAreaInset(edge: .top, spacing: 0) {
24+
VStack(spacing: 0) {
25+
TabBar(windowController: windowController, workspace: workspace)
26+
CustomDivider()
27+
BreadcrumbsView(item, workspace: workspace)
28+
}
29+
}
30+
.safeAreaInset(edge: .bottom) {
31+
StatusBarView()
32+
}
33+
} else {
34+
Text("CodeEdit cannot open this file because its file type is not supported.")
35+
}
36+
} else {
37+
Text("Open file from sidebar")
38+
}
39+
}
40+
}

CodeEdit/Documents/WorkspaceDocument.swift

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@ import CodeFile
1414

1515
@objc(WorkspaceDocument)
1616
class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
17-
@Published var workspaceClient: WorkspaceClient?
17+
var workspaceClient: WorkspaceClient?
18+
1819
@Published var selectedId: String?
1920
@Published var openFileItems: [WorkspaceClient.FileItem] = []
2021
@Published var sortFoldersOnTop: Bool = true
2122
@Published var fileItems: [WorkspaceClient.FileItem] = []
2223

24+
var quickOpenState: QuickOpenState?
25+
2326
var openedCodeFiles: [WorkspaceClient.FileItem: CodeFileDocument] = [:]
24-
var folderURL: URL?
2527
private var cancellables = Set<AnyCancellable>()
2628

2729
deinit {
@@ -36,11 +38,10 @@ class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
3638

3739
guard let idx = openFileItems.firstIndex(of: item) else { return }
3840
let closedFileItem = openFileItems.remove(at: idx)
39-
guard closedFileItem.id == selectedId else { return }
41+
guard closedFileItem.id == item.id else { return }
4042

4143
if openFileItems.isEmpty {
4244
selectedId = nil
43-
self.windowControllers.first?.document = self
4445
} else if idx == 0 {
4546
selectedId = openFileItems.first?.id
4647
} else {
@@ -101,12 +102,12 @@ class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
101102
}
102103

103104
override func read(from url: URL, ofType typeName: String) throws {
104-
self.folderURL = url
105105
self.workspaceClient = try .default(
106106
fileManager: .default,
107107
folderURL: url,
108108
ignoredFilesAndFolders: ignoredFilesAndDirectory
109109
)
110+
self.quickOpenState = .init(self)
110111
workspaceClient?
111112
.getFiles
112113
.sink { [weak self] files in
@@ -146,3 +147,58 @@ class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
146147
super.close()
147148
}
148149
}
150+
151+
// MARK: - Quick Open
152+
153+
extension WorkspaceDocument {
154+
155+
class QuickOpenState: ObservableObject {
156+
init(_ workspace: WorkspaceDocument) {
157+
self.workspace = workspace
158+
}
159+
160+
var workspace: WorkspaceDocument
161+
162+
@Published var openQuicklyQuery: String = ""
163+
@Published var openQuicklyFiles: [WorkspaceClient.FileItem] = []
164+
@Published var isShowingOpenQuicklyFiles: Bool = false
165+
166+
func fetchOpenQuickly() {
167+
if openQuicklyQuery == "" {
168+
openQuicklyFiles = []
169+
self.isShowingOpenQuicklyFiles = !openQuicklyFiles.isEmpty
170+
return
171+
}
172+
173+
DispatchQueue(label: "austincondiff.CodeEdit.quickOpen.searchFiles").async {
174+
if let url = self.workspace.fileURL {
175+
let enumerator = FileManager.default.enumerator(at: url,
176+
includingPropertiesForKeys: [
177+
.isRegularFileKey
178+
],
179+
options: [
180+
.skipsHiddenFiles,
181+
.skipsPackageDescendants
182+
])
183+
if let filePaths = enumerator?.allObjects as? [URL] {
184+
let files = filePaths.filter { url in
185+
let state1 = url.lastPathComponent.lowercased().contains(self.openQuicklyQuery.lowercased())
186+
do {
187+
let values = try url.resourceValues(forKeys: [.isRegularFileKey])
188+
return state1 && (values.isRegularFile ?? false)
189+
} catch {
190+
return false
191+
}
192+
}.map { url in
193+
WorkspaceClient.FileItem(url: url, children: nil)
194+
}
195+
DispatchQueue.main.async {
196+
self.openQuicklyFiles = files
197+
self.isShowingOpenQuicklyFiles = !self.openQuicklyFiles.isEmpty
198+
}
199+
}
200+
}
201+
}
202+
}
203+
}
204+
}

0 commit comments

Comments
 (0)