Fix blur and make window size be remembered

This commit is contained in:
2026-07-26 22:30:00 +03:00
Unverified
parent 16a14da642
commit 5952e1a9c5
9 changed files with 99 additions and 54 deletions
@@ -0,0 +1,35 @@
package ru.fromchat.desktop
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import java.util.prefs.Preferences
/**
* Desktop main-window size prefs.
*
* Stored in the same JVM prefs node as PlatformSettings (`ru.fromchat.settings`),
* keys `desktop_window_width` / `desktop_window_height` (float dp).
*/
internal object DesktopWindowPrefs {
private const val WIDTH_KEY = "desktop_window_width"
private const val HEIGHT_KEY = "desktop_window_height"
private val prefs: Preferences =
Preferences.userRoot().node("ru.fromchat.settings")
fun loadSize(): DpSize {
val width = prefs.getFloat(WIDTH_KEY, 0f)
val height = prefs.getFloat(HEIGHT_KEY, 0f)
return if (width <= 0f || height <= 0f) {
DpSize(800.dp, 600.dp)
} else {
DpSize(width.dp, height.dp)
}
}
fun saveSize(size: DpSize) {
prefs.putFloat(WIDTH_KEY, size.width.value)
prefs.putFloat(HEIGHT_KEY, size.height.value)
runCatching { prefs.flush() }
}
}
@@ -52,6 +52,7 @@ import kotlin.concurrent.thread
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.resources.stringResource
import org.jetbrains.skia.Image import org.jetbrains.skia.Image
@@ -232,7 +233,7 @@ fun main(args: Array<String>) {
UtilsLibrary.init() UtilsLibrary.init()
DesktopApplicationBootstrap.launchOnApplicationStart() DesktopApplicationBootstrap.launchOnApplicationStart()
val windowState = rememberWindowState() val windowState = rememberWindowState(size = remember { DesktopWindowPrefs.loadSize() })
val aboutWindowState = rememberWindowState( val aboutWindowState = rememberWindowState(
position = WindowPosition.Aligned(Alignment.Center), position = WindowPosition.Aligned(Alignment.Center),
size = DpSize(440.dp, 640.dp), size = DpSize(440.dp, 640.dp),
@@ -249,6 +250,11 @@ fun main(args: Array<String>) {
val trayShow = stringResource(Res.string.desktop_tray_show) val trayShow = stringResource(Res.string.desktop_tray_show)
val quit = stringResource(Res.string.desktop_quit) val quit = stringResource(Res.string.desktop_quit)
LaunchedEffect(windowState.size) {
delay(3_000)
DesktopWindowPrefs.saveSize(windowState.size)
}
DisposableEffect(trayState) { DisposableEffect(trayState) {
DesktopNotifier.sink = { title, body -> DesktopNotifier.sink = { title, body ->
trayState.sendNotification( trayState.sendNotification(
@@ -67,6 +67,7 @@ import dev.chrisbanes.haze.blur.HazeBlurStyle
import dev.chrisbanes.haze.blur.HazeColorEffect import dev.chrisbanes.haze.blur.HazeColorEffect
import dev.chrisbanes.haze.blur.HazeProgressive import dev.chrisbanes.haze.blur.HazeProgressive
import dev.chrisbanes.haze.blur.blurEffect import dev.chrisbanes.haze.blur.blurEffect
import dev.chrisbanes.haze.blur.materials.HazeMaterials
import dev.chrisbanes.haze.hazeEffect import dev.chrisbanes.haze.hazeEffect
import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.resources.stringResource
import ru.fromchat.Res import ru.fromchat.Res
@@ -425,8 +426,9 @@ fun ChatTopBar(
/** /**
* Two-pane chat chrome: separate back pill + title pill. Matches [ChatInput] composer chrome * Two-pane chat chrome: separate back pill + title pill. Matches [ChatInput] composer chrome
* ([rememberChatSurfaceContainerHazeStyle] + [androidx.compose.material3.ColorScheme.surfaceContainer]). * ([rememberChatSurfaceContainerHazeStyle] + [androidx.compose.material3.ColorScheme.surfaceContainer]).
* Full-bleed progressive haze strip behind the pills (high at top → fade toward bottom); pills keep * Full-bleed progressive haze strip behind the pills uses [HazeMaterials.thin] (high at top → fade
* their own surfaceContainer + [hazeEffect]. Horizontal inset matches [ChatInput]. * toward bottom); pills keep surfaceContainer + [rememberChatSurfaceContainerHazeStyle].
* Horizontal inset matches [ChatInput].
*/ */
@Composable @Composable
private fun ChatTopBarPill( private fun ChatTopBarPill(
@@ -441,6 +443,7 @@ private fun ChatTopBarPill(
hazeBlurEnabled: Boolean = true, hazeBlurEnabled: Boolean = true,
) { ) {
val hazeStyle = rememberChatSurfaceContainerHazeStyle() val hazeStyle = rememberChatSurfaceContainerHazeStyle()
val progressiveStripHazeStyle = HazeMaterials.thin()
val chromeColor = MaterialTheme.colorScheme.surfaceContainer val chromeColor = MaterialTheme.colorScheme.surfaceContainer
val pillShape = RoundedCornerShape(28.dp) val pillShape = RoundedCornerShape(28.dp)
@@ -452,7 +455,7 @@ private fun ChatTopBarPill(
.hazeEffect(state = hazeState) { .hazeEffect(state = hazeState) {
blurEffect { blurEffect {
blurEnabled = hazeBlurEnabled blurEnabled = hazeBlurEnabled
style = hazeStyle style = progressiveStripHazeStyle
progressive = HazeProgressive.verticalGradient( progressive = HazeProgressive.verticalGradient(
startIntensity = 1f, startIntensity = 1f,
endIntensity = 0f, endIntensity = 0f,
@@ -442,11 +442,15 @@ private fun ChatContextMenuBlurLayer(
blurProgress: Float, blurProgress: Float,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val blurRadius = 12.dp * blurProgress val progress = blurProgress.coerceIn(0f, 1f)
val blurRadius = 12.dp * progress
if (blurRadius <= 0.dp) return if (blurRadius <= 0.dp) return
// Opaque enough background so rounded chrome corners don't stay razor-sharp while
// interiors look frosted (Transparent + no tint left original AA edges unblurred).
// In-tree under the overlay (zIndex). Never use a Popup here: platform popups stack above // In-tree under the overlay (zIndex). Never use a Popup here: platform popups stack above
// the sharp row/menu, blur them, and intercept dismiss taps. // the sharp row/menu, blur them, and intercept dismiss taps.
val surface = MaterialTheme.colorScheme.surfaceContainerLowest
Box( Box(
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
@@ -454,10 +458,12 @@ private fun ChatContextMenuBlurLayer(
blurEffect { blurEffect {
style = HazeBlurStyle( style = HazeBlurStyle(
blurRadius = blurRadius, blurRadius = blurRadius,
colorEffects = emptyList(), backgroundColor = surface,
backgroundColor = Color.Transparent, colorEffects = listOf(
HazeColorEffect.tint(surface.copy(alpha = 0.4f * progress)),
),
noiseFactor = 0f, noiseFactor = 0f,
fallbackColorEffect = HazeColorEffect.tint(Color.Transparent), fallbackColorEffect = HazeColorEffect.tint(surface.copy(alpha = 0.4f * progress)),
) )
} }
}, },
@@ -268,7 +268,6 @@ internal fun ChatConversationsList(
publicChatSelected: Boolean, publicChatSelected: Boolean,
selectedOtherUserIds: Set<Int>, selectedOtherUserIds: Set<Int>,
contextMenuState: ChatContextMenuState, contextMenuState: ChatContextMenuState,
overlayCloneReady: Boolean,
rowRevealProgress: Float, rowRevealProgress: Float,
listContentPadding: PaddingValues = PaddingValues(0.dp), listContentPadding: PaddingValues = PaddingValues(0.dp),
showSearchBar: Boolean = false, showSearchBar: Boolean = false,
@@ -380,11 +379,11 @@ internal fun ChatConversationsList(
selectionTransitionProgress = selectionTransitionProgress, selectionTransitionProgress = selectionTransitionProgress,
isSelected = publicChatSelected, isSelected = publicChatSelected,
isHiddenForOverlay = contextMenuState.listIndex == ChatListLayout.PUBLIC_CHAT_ROW && isHiddenForOverlay = contextMenuState.listIndex == ChatListLayout.PUBLIC_CHAT_ROW &&
contextMenuState.isOverlayReplicaActive && contextMenuState.isOverlayReplicaActive,
overlayCloneReady,
isPressingForContextMenu = contextMenuState.listIndex == ChatListLayout.PUBLIC_CHAT_ROW && isPressingForContextMenu = contextMenuState.listIndex == ChatListLayout.PUBLIC_CHAT_ROW &&
contextMenuState.phase == ChatContextMenuPhase.Pressing, contextMenuState.phase == ChatContextMenuPhase.Pressing,
contextMenuPressScaleActive = contextMenuState.phase == ChatContextMenuPhase.Animating && contextMenuPressScaleActive = contextMenuState.listIndex == ChatListLayout.PUBLIC_CHAT_ROW &&
contextMenuState.phase == ChatContextMenuPhase.Animating &&
!contextMenuState.animatingOut, !contextMenuState.animatingOut,
rowRevealProgress = if (contextMenuState.listIndex == ChatListLayout.PUBLIC_CHAT_ROW) { rowRevealProgress = if (contextMenuState.listIndex == ChatListLayout.PUBLIC_CHAT_ROW) {
rowRevealProgress rowRevealProgress
@@ -461,11 +460,11 @@ internal fun ChatConversationsList(
selectionTransitionProgress = selectionTransitionProgress, selectionTransitionProgress = selectionTransitionProgress,
isSelected = conversation.otherUserId in selectedOtherUserIds, isSelected = conversation.otherUserId in selectedOtherUserIds,
isHiddenForOverlay = contextMenuState.listIndex == lazyIndex && isHiddenForOverlay = contextMenuState.listIndex == lazyIndex &&
contextMenuState.isOverlayReplicaActive && contextMenuState.isOverlayReplicaActive,
overlayCloneReady,
isPressingForContextMenu = contextMenuState.listIndex == lazyIndex && isPressingForContextMenu = contextMenuState.listIndex == lazyIndex &&
contextMenuState.phase == ChatContextMenuPhase.Pressing, contextMenuState.phase == ChatContextMenuPhase.Pressing,
contextMenuPressScaleActive = contextMenuState.phase == ChatContextMenuPhase.Animating && contextMenuPressScaleActive = contextMenuState.listIndex == lazyIndex &&
contextMenuState.phase == ChatContextMenuPhase.Animating &&
!contextMenuState.animatingOut, !contextMenuState.animatingOut,
rowRevealProgress = if (contextMenuState.listIndex == lazyIndex) { rowRevealProgress = if (contextMenuState.listIndex == lazyIndex) {
rowRevealProgress rowRevealProgress
@@ -103,8 +103,6 @@ class ChatContextMenuOverlayController {
var blurProgress by mutableFloatStateOf(0f) var blurProgress by mutableFloatStateOf(0f)
/** Shared row scale progress (0 = pressed, 1 = full) for overlay ↔ list handoff. */ /** Shared row scale progress (0 = pressed, 1 = full) for overlay ↔ list handoff. */
var rowRevealProgress by mutableFloatStateOf(0f) var rowRevealProgress by mutableFloatStateOf(0f)
/** True once the overlay row clone has been composed and positioned. */
var overlayCloneReady by mutableStateOf(false)
var onStateChange: (ChatContextMenuState) -> Unit = {} var onStateChange: (ChatContextMenuState) -> Unit = {}
var onDismiss: () -> Unit = {} var onDismiss: () -> Unit = {}
var onMessage: () -> Unit = {} var onMessage: () -> Unit = {}
@@ -114,12 +112,10 @@ class ChatContextMenuOverlayController {
var onMarkPublicRead: () -> Unit = {} var onMarkPublicRead: () -> Unit = {}
var onDelete: (Int) -> Unit = {} var onDelete: (Int) -> Unit = {}
var onSelect: () -> Unit = {} var onSelect: () -> Unit = {}
var onOverlayCloneReady: () -> Unit = {}
fun clear() { fun clear() {
uiState = null uiState = null
blurProgress = 0f blurProgress = 0f
rowRevealProgress = 0f rowRevealProgress = 0f
overlayCloneReady = false
} }
} }
@@ -829,7 +829,6 @@ fun ChatsTab(
), ),
) )
val rowRevealProgress = chatContextMenuOverlay.rowRevealProgress val rowRevealProgress = chatContextMenuOverlay.rowRevealProgress
val overlayCloneReady = chatContextMenuOverlay.overlayCloneReady
val callsEnabled = ServerConfig.callsEnabled val callsEnabled = ServerConfig.callsEnabled
fun markSelectedChatsRead() { fun markSelectedChatsRead() {
@@ -968,7 +967,6 @@ fun ChatsTab(
publicChatSelected = publicChatSelected, publicChatSelected = publicChatSelected,
selectedOtherUserIds = selectedOtherUserIds, selectedOtherUserIds = selectedOtherUserIds,
contextMenuState = contextMenuState, contextMenuState = contextMenuState,
overlayCloneReady = overlayCloneReady,
rowRevealProgress = rowRevealProgress, rowRevealProgress = rowRevealProgress,
listContentPadding = PaddingValues(top = fixedTopBarHeight), listContentPadding = PaddingValues(top = fixedTopBarHeight),
showSearchBar = true, showSearchBar = true,
@@ -1033,7 +1031,6 @@ fun ChatsTab(
return@ChatConversationsList return@ChatConversationsList
} }
haptic(HapticFeedbackEvent.ContextMenuOpened) haptic(HapticFeedbackEvent.ContextMenuOpened)
chatContextMenuOverlay.overlayCloneReady = false
contextMenuState = ChatContextMenuState( contextMenuState = ChatContextMenuState(
phase = ChatContextMenuPhase.Animating, phase = ChatContextMenuPhase.Animating,
target = target, target = target,
@@ -1049,7 +1046,6 @@ fun ChatsTab(
if (suspensionState.isSuspended) return@ChatConversationsList if (suspensionState.isSuspended) return@ChatConversationsList
haptic(HapticFeedbackEvent.ContextMenuOpened) haptic(HapticFeedbackEvent.ContextMenuOpened)
avatarPressMark = null avatarPressMark = null
chatContextMenuOverlay.overlayCloneReady = false
contextMenuState = ChatContextMenuState( contextMenuState = ChatContextMenuState(
phase = ChatContextMenuPhase.Animating, phase = ChatContextMenuPhase.Animating,
target = target, target = target,
@@ -1185,9 +1181,6 @@ fun ChatsTab(
animatingOut = true, animatingOut = true,
) )
} }
chatContextMenuOverlay.onOverlayCloneReady = {
chatContextMenuOverlay.overlayCloneReady = true
}
val shouldPublishOverlay = isVisible && val shouldPublishOverlay = isVisible &&
contextMenuState.isOverlayReplicaActive && contextMenuState.isOverlayReplicaActive &&
@@ -1215,19 +1208,10 @@ fun ChatsTab(
null null
} }
if (chatContextMenuOverlay.uiState != overlayUiState) { if (chatContextMenuOverlay.uiState != overlayUiState) {
val previousOverlay = chatContextMenuOverlay.uiState
chatContextMenuOverlay.uiState = overlayUiState chatContextMenuOverlay.uiState = overlayUiState
if (overlayUiState == null) { if (overlayUiState == null) {
chatContextMenuOverlay.blurProgress = 0f chatContextMenuOverlay.blurProgress = 0f
chatContextMenuOverlay.rowRevealProgress = 0f chatContextMenuOverlay.rowRevealProgress = 0f
chatContextMenuOverlay.overlayCloneReady = false
} else {
val isNewOverlaySession = previousOverlay == null ||
previousOverlay.contextMenuState.listIndex != overlayUiState.contextMenuState.listIndex ||
!previousOverlay.contextMenuState.isOverlayReplicaActive
if (isNewOverlaySession) {
chatContextMenuOverlay.overlayCloneReady = false
}
} }
} }
@@ -1277,7 +1261,6 @@ internal fun ChatContextMenuOverlayHost(
uiState.contextMenuState.otherUserId?.let(controller.onDelete) uiState.contextMenuState.otherUserId?.let(controller.onDelete)
}, },
onSelect = controller.onSelect, onSelect = controller.onSelect,
onOverlayCloneReady = controller.onOverlayCloneReady,
modifier = modifier, modifier = modifier,
) )
} }
@@ -1297,7 +1280,6 @@ private fun ChatContextMenuOverlay(
onMarkRead: () -> Unit, onMarkRead: () -> Unit,
onDelete: () -> Unit, onDelete: () -> Unit,
onSelect: () -> Unit, onSelect: () -> Unit,
onOverlayCloneReady: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val contextMenuState = uiState.contextMenuState val contextMenuState = uiState.contextMenuState
@@ -1503,17 +1485,6 @@ private fun ChatContextMenuOverlay(
} }
} }
LaunchedEffect(
layoutReady,
contextMenuState.listIndex,
contextMenuState.target,
contextMenuState.otherUserId,
) {
if (layoutReady && contextMenuState.isOverlayReplicaActive) {
onOverlayCloneReady()
}
}
fun requestDismiss() { fun requestDismiss() {
when (contextMenuState.phase) { when (contextMenuState.phase) {
ChatContextMenuPhase.Open -> { ChatContextMenuPhase.Open -> {
@@ -1,23 +1,26 @@
package ru.fromchat.ui.main.settings package ru.fromchat.ui.main.settings
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Devices import androidx.compose.material.icons.filled.Devices
import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.Notifications import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.outlined.BugReport
import androidx.compose.material.icons.filled.Palette import androidx.compose.material.icons.filled.Palette
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Storage import androidx.compose.material.icons.filled.Storage
import androidx.compose.material.icons.outlined.BugReport
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import ru.fromchat.ui.components.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -31,12 +34,17 @@ import com.pr0gramm3r101.utils.WindowWidthSizeClass
import com.pr0gramm3r101.utils.currentWindowAdaptiveInfo import com.pr0gramm3r101.utils.currentWindowAdaptiveInfo
import com.pr0gramm3r101.utils.verticalScroll import com.pr0gramm3r101.utils.verticalScroll
import com.pr0gramm3r101.utils.widthSizeClass import com.pr0gramm3r101.utils.widthSizeClass
import dev.chrisbanes.haze.blur.blurEffect
import dev.chrisbanes.haze.hazeEffect
import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.rememberHazeState
import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.resources.stringResource
import ru.fromchat.Res import ru.fromchat.Res
import ru.fromchat.about import ru.fromchat.about
import ru.fromchat.api.ApiClient import ru.fromchat.api.ApiClient
import ru.fromchat.change_server import ru.fromchat.change_server
import ru.fromchat.change_server_d import ru.fromchat.change_server_d
import ru.fromchat.logs_title
import ru.fromchat.profile import ru.fromchat.profile
import ru.fromchat.settings import ru.fromchat.settings
import ru.fromchat.settings_category_account import ru.fromchat.settings_category_account
@@ -47,11 +55,12 @@ import ru.fromchat.settings_category_devices
import ru.fromchat.settings_category_devices_d import ru.fromchat.settings_category_devices_d
import ru.fromchat.settings_category_notifications import ru.fromchat.settings_category_notifications
import ru.fromchat.settings_category_notifications_d import ru.fromchat.settings_category_notifications_d
import ru.fromchat.logs_title
import ru.fromchat.settings_hub_about_sub import ru.fromchat.settings_hub_about_sub
import ru.fromchat.settings_hub_logs_sub import ru.fromchat.settings_hub_logs_sub
import ru.fromchat.settings_hub_profile_sub import ru.fromchat.settings_hub_profile_sub
import ru.fromchat.ui.LocalNavController import ru.fromchat.ui.LocalNavController
import ru.fromchat.ui.chat.rememberChatSurfaceContainerHazeStyle
import ru.fromchat.ui.components.Text
import ru.fromchat.ui.extraStatusBars import ru.fromchat.ui.extraStatusBars
import ru.fromchat.ui.main.LocalMainChromeInsets import ru.fromchat.ui.main.LocalMainChromeInsets
import ru.fromchat.ui.main.mainPagerBottomInset import ru.fromchat.ui.main.mainPagerBottomInset
@@ -64,6 +73,9 @@ val SettingsStepHorizontalPadding = 24.dp
fun SettingsTab() { fun SettingsTab() {
val navController = LocalNavController.current val navController = LocalNavController.current
val isTwoPane = currentWindowAdaptiveInfo().widthSizeClass != WindowWidthSizeClass.COMPACT val isTwoPane = currentWindowAdaptiveInfo().widthSizeClass != WindowWidthSizeClass.COMPACT
val topChromeHazeState = rememberHazeState()
// Same material as MainScreen bottom nav (not HazeMaterials.thin).
val topChromeHazeStyle = rememberChatSurfaceContainerHazeStyle()
fun openDetail(route: String) { fun openDetail(route: String) {
navController.navigateReplacingMainDetail( navController.navigateReplacingMainDetail(
@@ -75,6 +87,7 @@ fun SettingsTab() {
Scaffold( Scaffold(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
containerColor = Color.Transparent, containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.onSurface,
contentWindowInsets = WindowInsets(0, 0, 0, 0), contentWindowInsets = WindowInsets(0, 0, 0, 0),
topBar = { topBar = {
TopAppBar( TopAppBar(
@@ -89,16 +102,27 @@ fun SettingsTab() {
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
containerColor = Color.Transparent, containerColor = Color.Transparent,
), ),
// Match MainScreen bottom chrome: surfaceContainer + hazeEffect/blurEffect.
modifier = Modifier
.background(MaterialTheme.colorScheme.surfaceContainer)
.hazeEffect(state = topChromeHazeState) {
blurEffect {
style = topChromeHazeStyle
}
},
) )
} }
) { innerPadding -> ) { innerPadding ->
Column( Column(
Modifier Modifier
.fillMaxSize() .fillMaxSize()
.hazeSource(topChromeHazeState)
.verticalScroll() .verticalScroll()
.mainPagerBottomInset() .mainPagerBottomInset()
.padding(innerPadding)
) { ) {
// Scrollable top inset so rows can pass under the frosted top chrome.
Spacer(Modifier.height(innerPadding.calculateTopPadding()))
if (isTwoPane) { if (isTwoPane) {
Category(Modifier.padding(top = 16.dp)) { Category(Modifier.padding(top = 16.dp)) {
ListItem( ListItem(
@@ -680,10 +680,15 @@ fun ListItem(
bottom = 8.dp bottom = 8.dp
), ),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) {
// Material list leadingIconColor default: onSurfaceVariant
CompositionLocalProvider(
LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant,
) { ) {
leadingContent() leadingContent()
} }
} }
}
ListItem( ListItem(
headlineContent = headlineSlot ?: { Text(headline) }, headlineContent = headlineSlot ?: { Text(headline) },