@@ -30,68 +30,9 @@ struct RecentProjectsView: View {
3030 self . recentProjectPaths = UserDefaults . standard. array ( forKey: " recentProjectPaths " ) as? [ String ] ?? [ ]
3131 }
3232
33- private var emptyView : some View {
34- VStack {
35- Spacer ( )
36- Text ( NSLocalizedString ( " No Recent Projects " , comment: " " ) )
37- . font ( . system( size: 20 ) )
38- Spacer ( )
39- }
40- }
41-
42- func contextMenuShowInFinder( projectPath: String ) -> some View {
43- Group {
44- Button ( NSLocalizedString ( " Show in Finder " , comment: " " ) ) {
45- if selectedProjectPaths. contains ( projectPath) {
46- self . selectedProjectPaths. forEach { projectPath in
47- guard let url = URL ( string: " file:// \( projectPath) " ) else {
48- return
49- }
50- NSWorkspace . shared. activateFileViewerSelecting ( [ url] )
51- }
52- } else {
53- guard let url = URL ( string: " file:// \( projectPath) " ) else {
54- return
55- }
56- NSWorkspace . shared. activateFileViewerSelecting ( [ url] )
57- }
58- }
59- }
60- }
61-
62- func contextMenuCopy( path: String ) -> some View {
63- Group {
64- Button ( NSLocalizedString ( " Copy Path " , comment: " " ) ) {
65- let pasteboard = NSPasteboard . general
66- pasteboard. declareTypes ( [ . string] , owner: nil )
67- pasteboard. setString ( path, forType: . string)
68- }
69- }
70- }
71-
72- func contextMenuDelete( projectPath: String ) -> some View {
73- Group {
74- Button ( NSLocalizedString ( " Remove from Recent Projects " , comment: " " ) ) {
75- if selectedProjectPaths. contains ( projectPath) {
76- self . selectedProjectPaths. forEach { projectPath in
77- deleteFromRecent ( item: projectPath)
78- }
79- } else {
80- deleteFromRecent ( item: projectPath)
81- }
82- }
83- }
84- }
85-
8633 func deleteFromRecent( item: String ) {
87- self . recentProjectPaths. removeAll {
88- $0 == item
89- }
90-
91- UserDefaults . standard. set (
92- self . recentProjectPaths,
93- forKey: " recentProjectPaths "
94- )
34+ self . recentProjectPaths. removeAll { $0 == item }
35+ UserDefaults . standard. set ( self . recentProjectPaths, forKey: " recentProjectPaths " )
9536 }
9637
9738 func deleteProject( projectPath: String ) {
@@ -138,61 +79,10 @@ struct RecentProjectsView: View {
13879 ZStack {
13980 RecentProjectItem ( projectPath: projectPath)
14081 . frame ( width: 300 )
141- . highPriorityGesture ( TapGesture ( count: 2 ) . onEnded {
142- openProject ( projectPath: projectPath)
143- } )
144- . gesture ( TapGesture ( ) . onEnded {
145- if NSEvent . modifierFlags. contains ( . command) {
146- self . lastSelectedProjectPath = projectPath
147- selectedProjectPaths. insert ( projectPath)
148- } else if NSEvent . modifierFlags. contains ( . shift) {
149- if let lastIndex = recentProjectPaths. firstIndex ( of: lastSelectedProjectPath) ,
150- let currentIndex = recentProjectPaths. firstIndex ( of: projectPath) {
151- if currentIndex > lastIndex {
152- let projectPaths = Array ( recentProjectPaths [ lastIndex..< currentIndex+ 1 ] )
153- selectedProjectPaths = selectedProjectPaths. union ( projectPaths)
154- } else {
155- let projectPaths = Array ( recentProjectPaths [ currentIndex..< lastIndex+ 1 ] )
156- selectedProjectPaths = selectedProjectPaths. union ( projectPaths)
157- }
158- }
159- } else {
160- self . lastSelectedProjectPath = projectPath
161- selectedProjectPaths = [ projectPath]
162- }
163- } )
164- . contextMenu {
165- contextMenuShowInFinder ( projectPath: projectPath)
166-
167- if !selectedProjectPaths. contains ( projectPath) {
168- contextMenuCopy ( path: projectPath)
169- . keyboardShortcut ( mgr. named ( with: " copy " ) . keyboardShortcut)
170- }
171-
172- Divider ( )
173- contextMenuDelete ( projectPath: projectPath)
174- . keyboardShortcut ( . init( . delete) )
175- }
176-
177- Button ( " " ) {
178- deleteProject ( projectPath: projectPath)
179- }
180- . buttonStyle ( . borderless)
181- . keyboardShortcut ( . init( . delete) )
182-
183- Button ( " " ) {
184- let pasteboard = NSPasteboard . general
185- pasteboard. declareTypes ( [ . string] , owner: nil )
186- pasteboard. setString ( projectPath, forType: . string)
187- }
188- . buttonStyle ( . borderless)
189- . keyboardShortcut ( mgr. named ( with: " copy " ) . keyboardShortcut)
190-
191- Button ( " " ) {
192- openProject ( projectPath: projectPath)
193- }
194- . buttonStyle ( . borderless)
195- . keyboardShortcut ( . defaultAction)
82+ . highPriorityGesture ( doubleTapGesture ( projectPath) )
83+ . gesture ( singleTapGesture ( projectPath) )
84+ . contextMenu { contextMenu ( projectPath) }
85+ keyboardShortcutButtons ( projectPath)
19686 }
19787 }
19888 . listStyle ( . sidebar)
@@ -211,6 +101,140 @@ struct RecentProjectsView: View {
211101 // onAppear is called once, and therafter never again,
212102 // since the window is never release from memory.
213103 updateRecentProjects ( )
104+
105+ // initially select the first item
106+ if let firstProject = recentProjectPaths. first {
107+ print ( firstProject)
108+ self . lastSelectedProjectPath = firstProject
109+ self . selectedProjectPaths = [ firstProject]
110+ }
111+ }
112+ }
113+
114+ private var emptyView : some View {
115+ VStack {
116+ Spacer ( )
117+ Text ( NSLocalizedString ( " No Recent Projects " , comment: " " ) )
118+ . font ( . system( size: 20 ) )
119+ Spacer ( )
120+ }
121+ }
122+
123+ // MARK: Gestures
124+
125+ private func doubleTapGesture( _ projectPath: String ) -> some Gesture {
126+ TapGesture ( count: 2 )
127+ . onEnded {
128+ openProject ( projectPath: projectPath)
129+ }
130+ }
131+
132+ private func singleTapGesture( _ projectPath: String ) -> some Gesture {
133+ TapGesture ( )
134+ . onEnded {
135+ if NSEvent . modifierFlags. contains ( . command) {
136+ self . lastSelectedProjectPath = projectPath
137+ selectedProjectPaths. insert ( projectPath)
138+ } else if NSEvent . modifierFlags. contains ( . shift) {
139+ if let lastIndex = recentProjectPaths. firstIndex ( of: lastSelectedProjectPath) ,
140+ let currentIndex = recentProjectPaths. firstIndex ( of: projectPath) {
141+ if currentIndex > lastIndex {
142+ let projectPaths = Array ( recentProjectPaths [ lastIndex..< currentIndex+ 1 ] )
143+ selectedProjectPaths = selectedProjectPaths. union ( projectPaths)
144+ } else {
145+ let projectPaths = Array ( recentProjectPaths [ currentIndex..< lastIndex+ 1 ] )
146+ selectedProjectPaths = selectedProjectPaths. union ( projectPaths)
147+ }
148+ }
149+ } else {
150+ self . lastSelectedProjectPath = projectPath
151+ selectedProjectPaths = [ projectPath]
152+ }
153+ }
154+ }
155+
156+ // MARK: Context Menu
157+
158+ @ViewBuilder
159+ private func contextMenu( _ projectPath: String ) -> some View {
160+ contextMenuShowInFinder ( projectPath)
161+
162+ if !selectedProjectPaths. contains ( projectPath) {
163+ contextMenuCopy ( projectPath)
164+ . keyboardShortcut ( mgr. named ( with: " copy " ) . keyboardShortcut)
165+ }
166+
167+ Divider ( )
168+ contextMenuDelete ( projectPath)
169+ . keyboardShortcut ( . init( . delete) )
170+ }
171+
172+ private func contextMenuShowInFinder( _ projectPath: String ) -> some View {
173+ Group {
174+ Button ( NSLocalizedString ( " Show in Finder " , comment: " " ) ) {
175+ if selectedProjectPaths. contains ( projectPath) {
176+ self . selectedProjectPaths. forEach { projectPath in
177+ guard let url = URL ( string: " file:// \( projectPath) " ) else {
178+ return
179+ }
180+ NSWorkspace . shared. activateFileViewerSelecting ( [ url] )
181+ }
182+ } else {
183+ guard let url = URL ( string: " file:// \( projectPath) " ) else {
184+ return
185+ }
186+ NSWorkspace . shared. activateFileViewerSelecting ( [ url] )
187+ }
188+ }
189+ }
190+ }
191+
192+ private func contextMenuCopy( _ projectPath: String ) -> some View {
193+ Group {
194+ Button ( NSLocalizedString ( " Copy Path " , comment: " " ) ) {
195+ let pasteboard = NSPasteboard . general
196+ pasteboard. declareTypes ( [ . string] , owner: nil )
197+ pasteboard. setString ( projectPath, forType: . string)
198+ }
199+ }
200+ }
201+
202+ private func contextMenuDelete( _ projectPath: String ) -> some View {
203+ Group {
204+ Button ( NSLocalizedString ( " Remove from Recent Projects " , comment: " " ) ) {
205+ if selectedProjectPaths. contains ( projectPath) {
206+ self . selectedProjectPaths. forEach { projectPath in
207+ deleteFromRecent ( item: projectPath)
208+ }
209+ } else {
210+ deleteFromRecent ( item: projectPath)
211+ }
212+ }
213+ }
214+ }
215+
216+ // MARK: Keyboard Shortcuts
217+
218+ @ViewBuilder
219+ private func keyboardShortcutButtons( _ projectPath: String ) -> some View {
220+ Button ( " " ) {
221+ deleteProject ( projectPath: projectPath)
222+ }
223+ . buttonStyle ( . borderless)
224+ . keyboardShortcut ( . init( . delete) )
225+
226+ Button ( " " ) {
227+ let pasteboard = NSPasteboard . general
228+ pasteboard. declareTypes ( [ . string] , owner: nil )
229+ pasteboard. setString ( projectPath, forType: . string)
230+ }
231+ . buttonStyle ( . borderless)
232+ . keyboardShortcut ( mgr. named ( with: " copy " ) . keyboardShortcut)
233+
234+ Button ( " " ) {
235+ openProject ( projectPath: projectPath)
214236 }
237+ . buttonStyle ( . borderless)
238+ . keyboardShortcut ( . defaultAction)
215239 }
216240}
0 commit comments