From 514c820fab3e11677750e0b0aab9bdf361505d27 Mon Sep 17 00:00:00 2001 From: Pushpinder Pal Singh Date: Tue, 13 May 2025 20:41:59 -0700 Subject: [PATCH 1/6] Add collapsing jumpbar items --- .../Editor/JumpBar/Views/EditorJumpBarComponent.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift index 130fc6098..c801c6e3b 100644 --- a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift +++ b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift @@ -65,6 +65,11 @@ struct EditorJumpBarComponent: View { return button } + .frame( + maxWidth: isHovering || isLastItem ? nil : 20, + alignment: .leading + ) + .clipped() .padding(.trailing, 11) .background { Color(nsColor: colorScheme == .dark ? .white : .black) From b8a9bff5a9c514dcdc966f4cca20954d0f8fa8b7 Mon Sep 17 00:00:00 2001 From: Pushpinder Pal Singh Date: Wed, 14 May 2025 11:34:26 -0700 Subject: [PATCH 2/6] Add animation --- .../Editor/JumpBar/Views/EditorJumpBarComponent.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift index c801c6e3b..bac21dec7 100644 --- a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift +++ b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift @@ -88,7 +88,9 @@ struct EditorJumpBarComponent: View { } .padding(.vertical, 3) .onHover { hover in - isHovering = hover + withAnimation(.easeInOut(duration: 0.2)) { + isHovering = hover + } } .onLongPressGesture(minimumDuration: 0) { button.performClick(nil) From e412f0d7b15d77a0cc822aaaf23cb7523d96f616 Mon Sep 17 00:00:00 2001 From: Pushpinder Pal Singh Date: Wed, 14 May 2025 13:22:18 -0700 Subject: [PATCH 3/6] Add truncated only while collapsed --- .../Views/EditorJumpBarComponent.swift | 7 +- .../JumpBar/Views/EditorJumpBarView.swift | 92 +++++++++++++++---- 2 files changed, 80 insertions(+), 19 deletions(-) diff --git a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift index bac21dec7..ab49b012d 100644 --- a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift +++ b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift @@ -26,16 +26,19 @@ struct EditorJumpBarComponent: View { @State var selection: CEWorkspaceFile @State var isHovering: Bool = false @State var button = NSPopUpButton() + @Binding var truncatedCrumbWidth: CGFloat? init( fileItem: CEWorkspaceFile, tappedOpenFile: @escaping (CEWorkspaceFile) -> Void, - isLastItem: Bool + isLastItem: Bool, + isTruncated: Binding ) { self.fileItem = fileItem self._selection = .init(wrappedValue: fileItem) self.tappedOpenFile = tappedOpenFile self.isLastItem = isLastItem + self._truncatedCrumbWidth = isTruncated } var siblings: [CEWorkspaceFile] { @@ -66,7 +69,7 @@ struct EditorJumpBarComponent: View { return button } .frame( - maxWidth: isHovering || isLastItem ? nil : 20, + maxWidth: isHovering || isLastItem ? nil : truncatedCrumbWidth, alignment: .leading ) .clipped() diff --git a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift index ce53d07b8..f48298bca 100644 --- a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift +++ b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift @@ -25,6 +25,11 @@ struct EditorJumpBarView: View { static let height = 28.0 + @State private var textWidth: CGFloat = 0 + @State private var containerWidth: CGFloat = 0 + @State private var isTruncated: Bool = false + @State private var crumbWidth: CGFloat? = nil + init( file: CEWorkspaceFile?, shouldShowTabBar: Bool, @@ -50,24 +55,59 @@ struct EditorJumpBarView: View { } var body: some View { - ScrollView(.horizontal, showsIndicators: false) { - HStack(spacing: 0) { - if file == nil { - Text("No Selection") - .font(.system(size: 11, weight: .regular)) - .foregroundColor( - activeState != .inactive - ? isActiveEditor ? .primary : .secondary - : Color(nsColor: .tertiaryLabelColor) - ) - } else { - ForEach(fileItems, id: \.self) { fileItem in - EditorJumpBarComponent( - fileItem: fileItem, - tappedOpenFile: tappedOpenFile, - isLastItem: fileItems.last == fileItem - ) + GeometryReader { containerProxy in + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 0) { + if file == nil { + Text("No Selection") + .font(.system(size: 11, weight: .regular)) + .foregroundColor( + activeState != .inactive + ? isActiveEditor ? .primary : .secondary + : Color(nsColor: .tertiaryLabelColor) + ) + } else { + ForEach(fileItems, id: \.self) { fileItem in + EditorJumpBarComponent( + fileItem: fileItem, + tappedOpenFile: tappedOpenFile, + isLastItem: fileItems.last == fileItem, + isTruncated: $crumbWidth + ) + } + + } + } + .background( + GeometryReader { proxy in + Color.clear + .onAppear { + if crumbWidth == nil { + textWidth = proxy.size.width + } + } + .onChange(of: proxy.size.width) { newValue in + if crumbWidth == nil { + textWidth = newValue + } + } } + ) + } + .onAppear { + containerWidth = containerProxy.size.width + } + .onChange(of: containerProxy.size.width) { newValue in + containerWidth = newValue + } + .onChange(of: textWidth) { _ in + withAnimation(.easeInOut(duration: 0.2)) { + resize() + } + } + .onChange(of: containerWidth) { _ in + withAnimation(.easeInOut(duration: 0.2)) { + resize() } } } @@ -86,4 +126,22 @@ struct EditorJumpBarView: View { .opacity(activeState == .inactive ? 0.8 : 1.0) .grayscale(isActiveEditor ? 0.0 : 1.0) } + + private func resize() { + let minWidth: CGFloat = 20 + let snapThreshold: CGFloat = 30 + let maxWidth: CGFloat = textWidth / CGFloat(fileItems.count) + let exponent: CGFloat = 5.0 + + if textWidth >= containerWidth { + let scale = max(0, min(1, containerWidth / textWidth)) + var width = floor((minWidth + (maxWidth - minWidth) * pow(scale, exponent))) + if width < snapThreshold { + width = minWidth + } + crumbWidth = width + } else { + crumbWidth = nil + } + } } From 54be86b87e0d0380ea44b89871dafc091a84acab Mon Sep 17 00:00:00 2001 From: Pushpinder Pal Singh Date: Tue, 20 May 2025 14:27:29 +0530 Subject: [PATCH 4/6] Add blur to the end of bar --- .../JumpBar/Views/EditorJumpBarView.swift | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift index f48298bca..a9e4444b7 100644 --- a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift +++ b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift @@ -28,7 +28,7 @@ struct EditorJumpBarView: View { @State private var textWidth: CGFloat = 0 @State private var containerWidth: CGFloat = 0 @State private var isTruncated: Bool = false - @State private var crumbWidth: CGFloat? = nil + @State private var crumbWidth: CGFloat? init( file: CEWorkspaceFile?, @@ -78,6 +78,23 @@ struct EditorJumpBarView: View { } } + .mask( + LinearGradient( + gradient: Gradient( + stops: crumbWidth != nil ? + [ + .init(color: .black, location: 0), + .init(color: .black, location: 0.8), + .init(color: .clear, location: 1) + ] : [ + .init(color: .black, location: 0), + .init(color: .black, location: 1) + ] + ), + startPoint: .leading, + endPoint: .trailing + ) + ) .background( GeometryReader { proxy in Color.clear From 6734806f82080467beee65a86940f4ed44a8c6f0 Mon Sep 17 00:00:00 2001 From: Pushpinder Pal Singh Date: Thu, 22 May 2025 06:11:49 +0530 Subject: [PATCH 5/6] Update for first crumb --- .../JumpBar/Views/EditorJumpBarView.swift | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift index a9e4444b7..30eca81fd 100644 --- a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift +++ b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift @@ -29,6 +29,7 @@ struct EditorJumpBarView: View { @State private var containerWidth: CGFloat = 0 @State private var isTruncated: Bool = false @State private var crumbWidth: CGFloat? + @State private var firstCrumbWidth: CGFloat? init( file: CEWorkspaceFile?, @@ -72,7 +73,7 @@ struct EditorJumpBarView: View { fileItem: fileItem, tappedOpenFile: tappedOpenFile, isLastItem: fileItems.last == fileItem, - isTruncated: $crumbWidth + isTruncated: fileItems.first == fileItem ? $firstCrumbWidth : $crumbWidth ) } @@ -149,16 +150,34 @@ struct EditorJumpBarView: View { let snapThreshold: CGFloat = 30 let maxWidth: CGFloat = textWidth / CGFloat(fileItems.count) let exponent: CGFloat = 5.0 + var betweenWidth: CGFloat = 0.0 if textWidth >= containerWidth { let scale = max(0, min(1, containerWidth / textWidth)) - var width = floor((minWidth + (maxWidth - minWidth) * pow(scale, exponent))) - if width < snapThreshold { - width = minWidth + betweenWidth = floor((minWidth + (maxWidth - minWidth) * pow(scale, exponent))) + if betweenWidth < snapThreshold { + betweenWidth = minWidth } - crumbWidth = width + crumbWidth = betweenWidth } else { crumbWidth = nil } + + if betweenWidth > snapThreshold { + firstCrumbWidth = nil + } else { + let otherCrumbs = CGFloat(max(fileItems.count - 1, 1)) + let usedWidth = otherCrumbs * snapThreshold + + // Multiplier to reserve extra space for other crumbs in the jump bar. + // Increasing this value causes the first crumb to truncate sooner. + let crumbSpacingMultiplier: CGFloat = 1.5 + let availableForFirst = containerWidth - usedWidth * crumbSpacingMultiplier + if availableForFirst < snapThreshold { + firstCrumbWidth = minWidth + } else { + firstCrumbWidth = availableForFirst + } + } } } From 6076b038f978b25aca87cfc2f0ac9c9503422810 Mon Sep 17 00:00:00 2001 From: Pushpinder Pal Singh Date: Thu, 22 May 2025 06:24:25 +0530 Subject: [PATCH 6/6] Update mask --- .../JumpBar/Views/EditorJumpBarComponent.swift | 17 +++++++++++++++++ .../JumpBar/Views/EditorJumpBarView.swift | 17 ----------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift index ab49b012d..c6663721b 100644 --- a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift +++ b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarComponent.swift @@ -72,6 +72,23 @@ struct EditorJumpBarComponent: View { maxWidth: isHovering || isLastItem ? nil : truncatedCrumbWidth, alignment: .leading ) + .mask( + LinearGradient( + gradient: Gradient( + stops: truncatedCrumbWidth == nil ? + [ + .init(color: .black, location: 0), + .init(color: .black, location: 1) + ] : [ + .init(color: .black, location: 0), + .init(color: .black, location: 0.8), + .init(color: .clear, location: 1) + ] + ), + startPoint: .leading, + endPoint: .trailing + ) + ) .clipped() .padding(.trailing, 11) .background { diff --git a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift index 30eca81fd..5c70aef98 100644 --- a/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift +++ b/CodeEdit/Features/Editor/JumpBar/Views/EditorJumpBarView.swift @@ -79,23 +79,6 @@ struct EditorJumpBarView: View { } } - .mask( - LinearGradient( - gradient: Gradient( - stops: crumbWidth != nil ? - [ - .init(color: .black, location: 0), - .init(color: .black, location: 0.8), - .init(color: .clear, location: 1) - ] : [ - .init(color: .black, location: 0), - .init(color: .black, location: 1) - ] - ), - startPoint: .leading, - endPoint: .trailing - ) - ) .background( GeometryReader { proxy in Color.clear