Skip to content

Commit 766eaa6

Browse files
authored
Improvements to breadcrumbs (#1067)
* Improvements to breadcrumbs Signed-off-by: Wouter01 <[email protected]> * fixed linter issue Signed-off-by: Wouter01 <[email protected]> --------- Signed-off-by: Wouter01 <[email protected]>
1 parent b27d746 commit 766eaa6

File tree

4 files changed

+86
-78
lines changed

4 files changed

+86
-78
lines changed

CodeEdit/Features/Breadcrumbs/Views/BreadcrumbsComponent.swift

Lines changed: 66 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -24,78 +24,86 @@ struct BreadcrumbsComponent: View {
2424
@State
2525
var position: NSPoint?
2626

27+
@State
28+
var selection: WorkspaceClient.FileItem
29+
2730
init(
2831
fileItem: WorkspaceClient.FileItem,
2932
tappedOpenFile: @escaping (WorkspaceClient.FileItem) -> Void
3033
) {
3134
self.fileItem = fileItem
35+
self._selection = .init(wrappedValue: fileItem)
3236
self.tappedOpenFile = tappedOpenFile
3337
}
3438

35-
private var image: String {
36-
fileItem.parent == nil ? "square.dashed.inset.filled" : fileItem.systemImage
39+
var siblings: [WorkspaceClient.FileItem] {
40+
if let siblings = fileItem.parent?.children?.sortItems(foldersOnTop: true), !siblings.isEmpty {
41+
return siblings
42+
} else {
43+
return [fileItem]
44+
}
3745
}
3846

39-
/// If current `fileItem` has no parent, it's the workspace root directory
40-
/// else if current `fileItem` has no children, it's the opened file
41-
/// else it's a folder
42-
private var color: Color {
43-
fileItem.parent == nil
44-
? .accentColor
45-
: (
46-
fileItem.isFolder
47-
? Color(hex: colorScheme == .dark ? "#61b6df" :"#27b9ff")
48-
: fileItem.iconColor
49-
)
47+
var body: some View {
48+
NSPopUpButtonView(selection: $selection) {
49+
let button = NSPopUpButton()
50+
button.menu = BreadcrumsMenu(fileItems: siblings, tappedOpenFile: tappedOpenFile)
51+
button.font = .systemFont(ofSize: NSFont.systemFontSize(for: .small))
52+
button.isBordered = false
53+
(button.cell as? NSPopUpButtonCell)?.arrowPosition = .noArrow
54+
return button
55+
}
56+
.padding(.leading, -5)
5057
}
5158

52-
var body: some View {
53-
HStack(alignment: .center, spacing: 5) {
54-
Image(systemName: image)
55-
.resizable()
56-
.aspectRatio(contentMode: .fit)
57-
.frame(width: 12)
58-
.foregroundStyle(
59-
prefs.preferences.general.fileIconStyle == .color
60-
? color
61-
: .secondary
62-
)
63-
.opacity(activeState != .inactive ? 1.0 : 0.4)
64-
Text(fileItem.fileName)
65-
.foregroundStyle(.primary)
66-
.font(.system(size: 11))
67-
.opacity(activeState != .inactive ? 1.0 : 0.2)
59+
struct NSPopUpButtonView<ItemType>: NSViewRepresentable where ItemType: Equatable {
60+
@Binding var selection: ItemType
61+
var popupCreator: () -> NSPopUpButton
62+
63+
typealias NSViewType = NSPopUpButton
64+
65+
func makeNSView(context: NSViewRepresentableContext<NSPopUpButtonView>) -> NSPopUpButton {
66+
let newPopupButton = popupCreator()
67+
setPopUpFromSelection(newPopupButton, selection: selection)
68+
return newPopupButton
6869
}
69-
/// Get location in window
70-
.background(GeometryReader { (proxy: GeometryProxy) -> Color in
71-
if position != NSPoint(
72-
x: proxy.frame(in: .global).minX,
73-
y: proxy.frame(in: .global).midY
74-
) {
75-
DispatchQueue.main.async {
76-
position = NSPoint(
77-
x: proxy.frame(in: .global).minX,
78-
y: proxy.frame(in: .global).midY
79-
)
80-
}
70+
71+
func updateNSView(_ nsView: NSPopUpButton, context: NSViewRepresentableContext<NSPopUpButtonView>) {
72+
setPopUpFromSelection(nsView, selection: selection)
73+
}
74+
75+
func setPopUpFromSelection(_ button: NSPopUpButton, selection: ItemType) {
76+
let itemsList = button.itemArray
77+
let matchedMenuItem = itemsList.filter {
78+
($0.representedObject as? ItemType) == selection
79+
}.first
80+
if matchedMenuItem != nil {
81+
button.select(matchedMenuItem)
8182
}
82-
return Color.clear
83-
})
84-
.onTapGesture {
85-
if let siblings = fileItem.parent?.children?.sortItems(foldersOnTop: true), !siblings.isEmpty {
86-
let menu = BreadcrumsMenu(
87-
fileItems: siblings
88-
) { item in
89-
tappedOpenFile(item)
90-
}
91-
if let position = position,
92-
let windowHeight = NSApp.keyWindow?.contentView?.frame.height {
93-
let pos = NSPoint(x: position.x, y: windowHeight - 72) // 72 = offset from top to breadcrumbs bar
94-
menu.popUp(
95-
positioning: menu.item(withTitle: fileItem.fileName),
96-
at: pos,
97-
in: NSApp.keyWindow?.contentView
98-
)
83+
}
84+
85+
func makeCoordinator() -> Coordinator {
86+
return Coordinator(self)
87+
}
88+
89+
class Coordinator: NSObject {
90+
var parent: NSPopUpButtonView!
91+
92+
init(_ parent: NSPopUpButtonView) {
93+
super.init()
94+
self.parent = parent
95+
NotificationCenter.default.addObserver(
96+
self,
97+
selector: #selector(dropdownItemSelected),
98+
name: NSMenu.didSendActionNotification,
99+
object: nil
100+
)
101+
}
102+
103+
@objc func dropdownItemSelected(_ notification: NSNotification) {
104+
let menuItem = (notification.userInfo?["MenuItem"])! as? NSMenuItem
105+
if let selection = menuItem?.representedObject as? ItemType {
106+
parent.selection = selection
99107
}
100108
}
101109
}

CodeEdit/Features/Breadcrumbs/Views/BreadcrumbsMenu.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,12 @@ final class BreadcrumbsMenuItem: NSMenuItem {
7575
if fileItem.children != nil {
7676
let subMenu = NSMenu()
7777
submenu = subMenu
78-
icon = "folder.fill"
79-
color = .secondary
78+
icon = fileItem.systemImage
79+
if fileItem.parent == nil {
80+
color = .accentColor
81+
} else {
82+
color = .secondary
83+
}
8084
}
8185
let image = NSImage(
8286
systemSymbolName: icon,

CodeEdit/Features/Breadcrumbs/Views/BreadcrumbsView.swift

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ struct BreadcrumbsView: View {
1818
@Environment(\.controlActiveState)
1919
private var activeState
2020

21-
@State
22-
private var fileItems: [WorkspaceClient.FileItem] = []
23-
2421
init(
2522
file: WorkspaceClient.FileItem,
2623
tappedOpenFile: @escaping (WorkspaceClient.FileItem) -> Void
@@ -29,6 +26,18 @@ struct BreadcrumbsView: View {
2926
self.tappedOpenFile = tappedOpenFile
3027
}
3128

29+
var fileItems: [WorkspaceClient.FileItem] {
30+
var treePath: [WorkspaceClient.FileItem] = []
31+
var currentFile: WorkspaceClient.FileItem? = file
32+
33+
while let currentFileLoop = currentFile {
34+
treePath.insert(currentFileLoop, at: 0)
35+
currentFile = currentFileLoop.parent
36+
}
37+
38+
return treePath
39+
}
40+
3241
var body: some View {
3342
ScrollView(.horizontal, showsIndicators: false) {
3443
HStack(spacing: 1.5) {
@@ -44,12 +53,6 @@ struct BreadcrumbsView: View {
4453
}
4554
.frame(height: 28, alignment: .center)
4655
.background(EffectView(.headerView).frame(height: 28))
47-
.onAppear {
48-
fileInfo(self.file)
49-
}
50-
.onChange(of: file) { newFile in
51-
fileInfo(newFile)
52-
}
5356
}
5457

5558
private var chevron: some View {
@@ -60,13 +63,4 @@ struct BreadcrumbsView: View {
6063
.imageScale(.large)
6164
.opacity(activeState != .inactive ? 0.8 : 0.5)
6265
}
63-
64-
private func fileInfo(_ file: WorkspaceClient.FileItem) {
65-
fileItems = []
66-
var currentFile: WorkspaceClient.FileItem? = file
67-
while let currentFileLoop = currentFile {
68-
fileItems.insert(currentFileLoop, at: 0)
69-
currentFile = currentFileLoop.parent
70-
}
71-
}
7266
}

CodeEdit/Utils/WorkspaceClient/Model/FileItem.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ extension WorkspaceClient {
116116
switch children {
117117
case nil:
118118
return FileIcon.fileIcon(fileType: fileType)
119+
case .some where parent == nil:
120+
return "square.dashed.inset.filled"
119121
case let .some(children):
120122
if self.watcher == nil && !self.activateWatcher() {
121123
return "questionmark.folder"

0 commit comments

Comments
 (0)