Skip to content

Commit 5e6b608

Browse files
authored
Merge pull request #4 from kdroidFilter/feat/sidebar-scroll-glass
feat(sidebar): scroll items under title bar with progressive liquid glass
2 parents 2084e9d + 83d7fba commit 5e6b608

8 files changed

Lines changed: 414 additions & 121 deletions

File tree

macosui/src/commonMain/kotlin/io/github/kdroidfilter/nucleus/ui/apple/macos/components/Scaffold.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import androidx.compose.ui.unit.dp
3434
import io.github.fletchmckee.liquid.liquefiable
3535
import io.github.fletchmckee.liquid.liquid
3636
import io.github.fletchmckee.liquid.rememberLiquidState
37+
import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.liquidGlassFade
3738
import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.MacosDuration
3839
import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.SpringPreset
3940
import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.MacosTheme
@@ -159,6 +160,7 @@ fun Scaffold(
159160
Color.Transparent
160161
}
161162

163+
CompositionLocalProvider(LocalTitleBarHeight provides topPadding) {
162164
Row(
163165
modifier = modifier
164166
.fillMaxSize()
@@ -370,12 +372,11 @@ fun Scaffold(
370372

371373
// ---- Title bar overlay (over content area only) ----
372374
if (titleBar != null || managedToggle) {
373-
val titleBarGlassModifier = Modifier.liquid(titleBarGlassState) {
374-
frost = 16.dp
375-
shape = RectangleShape
376-
tint = glassTint
377-
saturation = 1.05f
378-
}
375+
val titleBarGlassModifier = Modifier.liquidGlassFade(
376+
state = titleBarGlassState,
377+
shape = RectangleShape,
378+
tint = glassTint,
379+
)
379380

380381
CompositionLocalProvider(LocalToolbarGlassState provides rootLiquidState) {
381382
Row(
@@ -413,4 +414,5 @@ fun Scaffold(
413414
}
414415
}
415416
}
417+
}
416418
}

macosui/src/commonMain/kotlin/io/github/kdroidfilter/nucleus/ui/apple/macos/components/Scrollbar.kt

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ fun rememberScrollbarState(lazyListState: LazyListState): ScrollbarState =
134134
* [TrackClickBehavior.Seek] seeks to the click position.
135135
* Use [TrackClickBehavior.None] on touch-only platforms.
136136
* @param style Optional style override; defaults to [MacosTheme.componentStyling.scrollbar].
137+
* @param topInset Inset from the top edge before the thumb track starts.
138+
* Use this to keep the scrollbar below a fixed title bar or glass
139+
* overlay. Pass `contentPadding.calculateTopPadding()` from
140+
* [Scaffold][io.github.kdroidfilter.nucleus.ui.apple.macos.components.Scaffold]
141+
* for page-level scrollbars. Defaults to 0.dp.
137142
*/
138143
@Composable
139144
fun VerticalScrollbar(
@@ -143,6 +148,7 @@ fun VerticalScrollbar(
143148
showOnEdgeHover: Boolean = true,
144149
trackClickBehavior: TrackClickBehavior = TrackClickBehavior.Jump,
145150
style: ScrollbarStyle = MacosTheme.componentStyling.scrollbar,
151+
topInset: Dp = 0.dp,
146152
) {
147153
ScrollbarImpl(
148154
state = state,
@@ -152,6 +158,7 @@ fun VerticalScrollbar(
152158
showOnEdgeHover = showOnEdgeHover,
153159
trackClickBehavior = trackClickBehavior,
154160
style = style,
161+
topInset = topInset,
155162
)
156163
}
157164

@@ -200,6 +207,7 @@ private fun ScrollbarImpl(
200207
showOnEdgeHover: Boolean,
201208
trackClickBehavior: TrackClickBehavior,
202209
style: ScrollbarStyle,
210+
topInset: Dp = 0.dp,
203211
) {
204212
val metrics = style.metrics
205213
val colors = style.colors
@@ -361,8 +369,10 @@ private fun ScrollbarImpl(
361369
)
362370
},
363371
) { measurables, constraints ->
364-
// Track length: fill all available space on the scroll axis
365-
val trackLength = if (isVertical) constraints.maxHeight else constraints.maxWidth
372+
// Track length: fill all available space on the scroll axis, minus top inset
373+
val totalLength = if (isVertical) constraints.maxHeight else constraints.maxWidth
374+
val topInsetPx = if (isVertical) topInset.roundToPx() else 0
375+
val trackLength = (totalLength - topInsetPx).coerceAtLeast(0)
366376
trackSizePx = trackLength
367377

368378
val minPx = thumbMinLength.toPx()
@@ -386,15 +396,13 @@ private fun ScrollbarImpl(
386396
null
387397
}
388398

389-
val layoutW = if (isVertical) trackBreadthPx else trackLength
390-
val layoutH = if (isVertical) trackLength else trackBreadthPx
399+
val layoutW = if (isVertical) trackBreadthPx else totalLength
400+
val layoutH = if (isVertical) totalLength else trackBreadthPx
391401

392402
layout(layoutW, layoutH) {
393403
if (placeable != null) {
394-
val posAlong = thumbOff.roundToInt()
395-
.coerceIn(0, (trackLength - thumbLenPx).coerceAtLeast(0))
396-
// Trailing edge stays trailingPad from the container edge;
397-
// leading edge expands toward content on hover.
404+
val posAlong = (thumbOff.roundToInt() + topInsetPx)
405+
.coerceIn(topInsetPx, (totalLength - thumbLenPx).coerceAtLeast(topInsetPx))
398406
val posAcross = (trackBreadthPx - trailPadPx - thumbBreadthPx).coerceAtLeast(0)
399407

400408
if (isVertical) placeable.place(posAcross, posAlong)

0 commit comments

Comments
 (0)