Repository navigation
Scale sidebar workspace group header with sidebar font size (#5398) #5401
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
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| {"sessionId":"9c9943de-bc79-4f2c-9b82-cbd58c3a1d51","pid":32319,"procStart":"Sun May 31 00:20:09 2026","acquiredAt":1780187614705} | ||
| {"sessionId":"5c2a62d0-99c2-42c0-9452-f67b6fbaacda","pid":75304,"procStart":"Thu Jun 4 23:34:54 2026","acquiredAt":1780616564932} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import CoreGraphics | ||
|
|
||
| /// Font sizes, icon/control frames, and badge padding for | ||
| /// ``SidebarWorkspaceGroupHeaderView``, derived from the sidebar font scale. | ||
| /// | ||
| /// The collapsible workspace group/folder header must grow proportionally with | ||
| /// the configurable sidebar font size, just like the workspace rows below it. | ||
| /// `TabItemView` already scales its subviews by `settings.sidebarFontScale` | ||
| /// (see ``SidebarTabItemFontScale``); this type is the single place that | ||
| /// applies the same scale to the group header so the two grow at the same rate | ||
| /// from one scaling path. | ||
| /// | ||
| /// The `base*` constants are the design sizes at the default sidebar font size, | ||
| /// where ``SidebarTabItemFontScale/scale(for:)`` returns `1.0`. Every metric is | ||
| /// the corresponding base value multiplied by ``fontScale``. | ||
| /// | ||
| /// ```swift | ||
| /// let metrics = SidebarWorkspaceGroupHeaderMetrics(fontScale: settings.sidebarFontScale) | ||
| /// Image(systemName: "chevron.down") | ||
| /// .font(.system(size: metrics.chevronFontSize, weight: .semibold)) | ||
| /// .frame(width: metrics.chevronFrame, height: metrics.chevronFrame) | ||
| /// ``` | ||
| struct SidebarWorkspaceGroupHeaderMetrics: Equatable { | ||
| /// The sidebar font scale; `1.0` at the default sidebar font size and | ||
| /// proportionally larger as the configured `sidebar-font-size` grows. | ||
| let fontScale: CGFloat | ||
|
|
||
| /// Creates header metrics for the given sidebar font scale. | ||
| /// - Parameter fontScale: The scale from ``SidebarTabItemFontScale/scale(for:)``; | ||
| /// `1.0` at the default sidebar font size. | ||
| init(fontScale: CGFloat) { | ||
| self.fontScale = fontScale | ||
| } | ||
|
|
||
| /// Chevron glyph point size at the default sidebar font size. | ||
| static let baseChevronFontSize: CGFloat = 9 | ||
| /// Chevron tap-target frame edge at the default sidebar font size. | ||
| static let baseChevronFrame: CGFloat = 14 | ||
| /// Folder/group icon point size at the default sidebar font size. | ||
| static let baseIconFontSize: CGFloat = 11 | ||
| /// Folder/group icon frame edge at the default sidebar font size. | ||
| static let baseIconFrame: CGFloat = 14 | ||
| /// Group name point size at the default sidebar font size. | ||
| static let baseNameFontSize: CGFloat = 11 | ||
| /// Unread badge point size at the default sidebar font size. | ||
| static let baseUnreadFontSize: CGFloat = 10 | ||
| /// Unread badge horizontal padding at the default sidebar font size. | ||
| static let baseUnreadHorizontalPadding: CGFloat = 5 | ||
| /// Unread badge vertical padding at the default sidebar font size. | ||
| static let baseUnreadVerticalPadding: CGFloat = 1 | ||
| /// Plus-button glyph point size at the default sidebar font size. | ||
| static let basePlusFontSize: CGFloat = 11 | ||
| /// Plus-button frame edge at the default sidebar font size. | ||
| static let basePlusFrame: CGFloat = 18 | ||
|
|
||
| /// Scaled chevron glyph point size. | ||
| var chevronFontSize: CGFloat { Self.baseChevronFontSize * fontScale } | ||
| /// Scaled chevron tap-target frame edge. | ||
| var chevronFrame: CGFloat { Self.baseChevronFrame * fontScale } | ||
| /// Scaled folder/group icon point size. | ||
| var iconFontSize: CGFloat { Self.baseIconFontSize * fontScale } | ||
| /// Scaled folder/group icon frame edge. | ||
| var iconFrame: CGFloat { Self.baseIconFrame * fontScale } | ||
| /// Scaled group name point size. | ||
| var nameFontSize: CGFloat { Self.baseNameFontSize * fontScale } | ||
| /// Scaled unread badge point size. | ||
| var unreadFontSize: CGFloat { Self.baseUnreadFontSize * fontScale } | ||
| /// Scaled unread badge horizontal padding. | ||
| var unreadHorizontalPadding: CGFloat { Self.baseUnreadHorizontalPadding * fontScale } | ||
| /// Scaled unread badge vertical padding. | ||
| var unreadVerticalPadding: CGFloat { Self.baseUnreadVerticalPadding * fontScale } | ||
| /// Scaled plus-button glyph point size. | ||
| var plusFontSize: CGFloat { Self.basePlusFontSize * fontScale } | ||
| /// Scaled plus-button frame edge. | ||
| var plusFrame: CGFloat { Self.basePlusFrame * fontScale } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import CoreGraphics | ||
| import Testing | ||
|
|
||
| #if canImport(cmux_DEV) | ||
| @testable import cmux_DEV | ||
| #elseif canImport(cmux) | ||
| @testable import cmux | ||
| #endif | ||
|
|
||
| @Suite struct SidebarWorkspaceGroupHeaderMetricsTests { | ||
| @Test func metricsMatchBaseSizesAtDefaultScale() { | ||
| let metrics = SidebarWorkspaceGroupHeaderMetrics(fontScale: 1) | ||
|
|
||
| #expect(metrics.chevronFontSize == SidebarWorkspaceGroupHeaderMetrics.baseChevronFontSize) | ||
| #expect(metrics.chevronFrame == SidebarWorkspaceGroupHeaderMetrics.baseChevronFrame) | ||
| #expect(metrics.iconFontSize == SidebarWorkspaceGroupHeaderMetrics.baseIconFontSize) | ||
| #expect(metrics.iconFrame == SidebarWorkspaceGroupHeaderMetrics.baseIconFrame) | ||
| #expect(metrics.nameFontSize == SidebarWorkspaceGroupHeaderMetrics.baseNameFontSize) | ||
| #expect(metrics.unreadFontSize == SidebarWorkspaceGroupHeaderMetrics.baseUnreadFontSize) | ||
| #expect(metrics.unreadHorizontalPadding == SidebarWorkspaceGroupHeaderMetrics.baseUnreadHorizontalPadding) | ||
| #expect(metrics.unreadVerticalPadding == SidebarWorkspaceGroupHeaderMetrics.baseUnreadVerticalPadding) | ||
| #expect(metrics.plusFontSize == SidebarWorkspaceGroupHeaderMetrics.basePlusFontSize) | ||
| #expect(metrics.plusFrame == SidebarWorkspaceGroupHeaderMetrics.basePlusFrame) | ||
| } | ||
|
|
||
| @Test func metricsScaleProportionallyWhenSidebarFontEnlarged() { | ||
| let scale: CGFloat = 2 | ||
| let metrics = SidebarWorkspaceGroupHeaderMetrics(fontScale: scale) | ||
|
|
||
| #expect(metrics.chevronFontSize == SidebarWorkspaceGroupHeaderMetrics.baseChevronFontSize * scale) | ||
| #expect(metrics.chevronFrame == SidebarWorkspaceGroupHeaderMetrics.baseChevronFrame * scale) | ||
| #expect(metrics.iconFontSize == SidebarWorkspaceGroupHeaderMetrics.baseIconFontSize * scale) | ||
| #expect(metrics.iconFrame == SidebarWorkspaceGroupHeaderMetrics.baseIconFrame * scale) | ||
| #expect(metrics.nameFontSize == SidebarWorkspaceGroupHeaderMetrics.baseNameFontSize * scale) | ||
| #expect(metrics.unreadFontSize == SidebarWorkspaceGroupHeaderMetrics.baseUnreadFontSize * scale) | ||
| #expect(metrics.unreadHorizontalPadding == SidebarWorkspaceGroupHeaderMetrics.baseUnreadHorizontalPadding * scale) | ||
| #expect(metrics.unreadVerticalPadding == SidebarWorkspaceGroupHeaderMetrics.baseUnreadVerticalPadding * scale) | ||
| #expect(metrics.plusFontSize == SidebarWorkspaceGroupHeaderMetrics.basePlusFontSize * scale) | ||
| #expect(metrics.plusFrame == SidebarWorkspaceGroupHeaderMetrics.basePlusFrame * scale) | ||
| } | ||
|
|
||
| @Test func metricsScaleProportionallyWhenSidebarFontShrunk() { | ||
| let scale: CGFloat = 0.5 | ||
| let metrics = SidebarWorkspaceGroupHeaderMetrics(fontScale: scale) | ||
|
|
||
| #expect(metrics.chevronFontSize == SidebarWorkspaceGroupHeaderMetrics.baseChevronFontSize * scale) | ||
| #expect(metrics.chevronFrame == SidebarWorkspaceGroupHeaderMetrics.baseChevronFrame * scale) | ||
| #expect(metrics.iconFontSize == SidebarWorkspaceGroupHeaderMetrics.baseIconFontSize * scale) | ||
| #expect(metrics.iconFrame == SidebarWorkspaceGroupHeaderMetrics.baseIconFrame * scale) | ||
| #expect(metrics.nameFontSize == SidebarWorkspaceGroupHeaderMetrics.baseNameFontSize * scale) | ||
| #expect(metrics.unreadFontSize == SidebarWorkspaceGroupHeaderMetrics.baseUnreadFontSize * scale) | ||
| #expect(metrics.unreadHorizontalPadding == SidebarWorkspaceGroupHeaderMetrics.baseUnreadHorizontalPadding * scale) | ||
| #expect(metrics.unreadVerticalPadding == SidebarWorkspaceGroupHeaderMetrics.baseUnreadVerticalPadding * scale) | ||
| #expect(metrics.plusFontSize == SidebarWorkspaceGroupHeaderMetrics.basePlusFontSize * scale) | ||
| #expect(metrics.plusFrame == SidebarWorkspaceGroupHeaderMetrics.basePlusFrame * scale) | ||
| } | ||
|
|
||
| @Test func headerAndRowFontScaleShareOneScalingPath() { | ||
| // The header must grow at the same rate as the workspace rows, which | ||
| // derive their scale from SidebarTabItemFontScale. Reusing that scale | ||
| // keeps the two in lockstep instead of introducing a second path. | ||
| let enlargedSize = GhosttyConfig.defaultSidebarFontSize * 1.6 | ||
| let rowScale = SidebarTabItemFontScale.scale(for: enlargedSize) | ||
| let metrics = SidebarWorkspaceGroupHeaderMetrics(fontScale: rowScale) | ||
|
|
||
| #expect(metrics.nameFontSize == SidebarWorkspaceGroupHeaderMetrics.baseNameFontSize * rowScale) | ||
| #expect(rowScale > 1) | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.