diff --git a/common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/EntryGrid.kt b/common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/EntryGrid.kt index 781e0749b0..8cff964a01 100644 --- a/common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/EntryGrid.kt +++ b/common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/EntryGrid.kt @@ -90,7 +90,7 @@ fun EntryGrid( } } - TiviScaffold( + HazeScaffold( topBar = { EntryGridAppBar( title = title, diff --git a/common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/HazeScaffold.kt b/common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/HazeScaffold.kt new file mode 100644 index 0000000000..3b63ca0e58 --- /dev/null +++ b/common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/HazeScaffold.kt @@ -0,0 +1,67 @@ +// Copyright 2023, Christopher Banes and the Tivi project contributors +// SPDX-License-Identifier: Apache-2.0 + +package app.tivi.common.compose + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.material3.FabPosition +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ScaffoldDefaults +import androidx.compose.material3.contentColorFor +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import dev.chrisbanes.haze.HazeState +import dev.chrisbanes.haze.haze +import dev.chrisbanes.haze.hazeChild + +@Composable +fun HazeScaffold( + modifier: Modifier = Modifier, + topBar: @Composable () -> Unit = {}, + bottomBar: @Composable () -> Unit = {}, + snackbarHost: @Composable () -> Unit = {}, + floatingActionButton: @Composable () -> Unit = {}, + floatingActionButtonPosition: FabPosition = FabPosition.End, + containerColor: Color = MaterialTheme.colorScheme.background, + contentColor: Color = contentColorFor(containerColor), + contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets, + blurTopBar: Boolean = false, + blurBottomBar: Boolean = false, + content: @Composable (PaddingValues) -> Unit, +) { + val hazeState = remember { HazeState() } + + NestedScaffold( + modifier = modifier, + topBar = { + Box( + modifier = Modifier.thenIf(blurTopBar) { hazeChild(hazeState) }, + content = { topBar() }, + ) + }, + bottomBar = { + Box( + modifier = Modifier.thenIf(blurBottomBar) { hazeChild(hazeState) }, + content = { bottomBar() }, + ) + }, + snackbarHost = snackbarHost, + floatingActionButton = floatingActionButton, + floatingActionButtonPosition = floatingActionButtonPosition, + containerColor = containerColor, + contentColor = contentColor, + contentWindowInsets = contentWindowInsets, + ) { contentPadding -> + Box( + modifier = Modifier.haze( + state = hazeState, + backgroundColor = containerColor, + ), + content = { content(contentPadding) }, + ) + } +} diff --git a/common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/TiviScaffold.kt b/common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/NestedScaffold.kt similarity index 83% rename from common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/TiviScaffold.kt rename to common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/NestedScaffold.kt index 69debec638..1fb6fb128c 100644 --- a/common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/TiviScaffold.kt +++ b/common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/NestedScaffold.kt @@ -3,7 +3,6 @@ package app.tivi.common.compose -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.asPaddingValues @@ -24,14 +23,12 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.Immutable import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.SubcomposeLayout import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.offset import app.tivi.common.compose.ui.plus -import dev.chrisbanes.haze.haze private val LocalScaffoldContentPadding = staticCompositionLocalOf { PaddingValues(0.dp) } @@ -39,11 +36,9 @@ private val LocalScaffoldContentPadding = staticCompositionLocalOf { PaddingValu * A copy of Material 3's [Scaffold] composable, but with a few tweaks: * * - Supports being used nested. The `contentPadding` is compounded on each level. - * - Supports automatic blurring of content over [topBar] and [bottomBar], via - * [blurTopBar] and [blurBottomBar]. */ @Composable -fun TiviScaffold( +internal fun NestedScaffold( modifier: Modifier = Modifier, topBar: @Composable () -> Unit = {}, bottomBar: @Composable () -> Unit = {}, @@ -53,8 +48,6 @@ fun TiviScaffold( containerColor: Color = MaterialTheme.colorScheme.background, contentColor: Color = contentColorFor(containerColor), contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets, - blurTopBar: Boolean = false, - blurBottomBar: Boolean = false, content: @Composable (PaddingValues) -> Unit, ) { Surface(modifier = modifier, color = containerColor, contentColor = contentColor) { @@ -66,8 +59,6 @@ fun TiviScaffold( snackbar = snackbarHost, contentWindowInsets = contentWindowInsets, fab = floatingActionButton, - blurTopBar = blurTopBar, - blurBottomBar = blurBottomBar, incomingContentPadding = LocalScaffoldContentPadding.current, ) } @@ -95,8 +86,6 @@ private fun NestedScaffoldLayout( contentWindowInsets: WindowInsets, incomingContentPadding: PaddingValues, bottomBar: @Composable () -> Unit, - blurTopBar: Boolean, - blurBottomBar: Boolean, ) { SubcomposeLayout { constraints -> val layoutWidth = constraints.maxWidth @@ -193,40 +182,10 @@ private fun NestedScaffoldLayout( end = contentInsets.calculateEndPadding((this@SubcomposeLayout).layoutDirection), ) - val blurAreas = listOfNotNull( - if (blurTopBar) { - Rect( - left = 0f, - top = 0f, - right = layoutWidth.toFloat(), - bottom = innerPadding.calculateTopPadding().toPx(), - ).takeUnless { it.isEmpty } - } else { - null - }, - if (blurBottomBar) { - Rect( - left = 0f, - top = layoutHeight.toFloat() - innerPadding.calculateBottomPadding().toPx(), - right = layoutWidth.toFloat(), - bottom = layoutHeight.toFloat(), - ).takeUnless { it.isEmpty } - } else { - null - }, - ) - - Box( - modifier = Modifier.haze( - *blurAreas.toTypedArray(), - backgroundColor = MaterialTheme.colorScheme.surface, - ), - ) { - // Scaffold always applies the insets, so we only want to pass down the content padding - // without the insets (i.e. padding from the bottom bar, etc) - CompositionLocalProvider(LocalScaffoldContentPadding provides innerPadding) { - content(innerPadding) - } + // Scaffold always applies the insets, so we only want to pass down the content padding + // without the insets (i.e. padding from the bottom bar, etc) + CompositionLocalProvider(LocalScaffoldContentPadding provides innerPadding) { + content(innerPadding) } }.map { it.measure(looseConstraints) } @@ -276,7 +235,7 @@ internal class FabPlacement( /** * CompositionLocal containing a [FabPlacement] that is used to calculate the FAB bottom offset. */ -internal val LocalFabPlacement = staticCompositionLocalOf { null } +internal val LocalFabPlacement = staticCompositionLocalOf { null } // FAB spacing above the bottom bar / bottom of the Scaffold private val FabSpacing = 16.dp diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fae1827391..3f1c54f2c3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -72,7 +72,7 @@ compose-material3-windowsizeclass = "dev.chrisbanes.material3:material3-window-s crashkios-crashlytics = "co.touchlab.crashkios:crashlytics:0.8.5" -haze = "dev.chrisbanes.haze:haze:0.3.1" +haze = "dev.chrisbanes.haze:haze:0.4.0" tools-desugarjdklibs = "com.android.tools:desugar_jdk_libs:2.0.4" diff --git a/ui/developer/log/src/commonMain/kotlin/app/tivi/developer/log/DevLog.kt b/ui/developer/log/src/commonMain/kotlin/app/tivi/developer/log/DevLog.kt index 017fdfd1fa..32e173d8be 100644 --- a/ui/developer/log/src/commonMain/kotlin/app/tivi/developer/log/DevLog.kt +++ b/ui/developer/log/src/commonMain/kotlin/app/tivi/developer/log/DevLog.kt @@ -25,8 +25,8 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import app.tivi.common.compose.HazeScaffold import app.tivi.common.compose.LocalStrings -import app.tivi.common.compose.TiviScaffold import app.tivi.screens.DevLogScreen import app.tivi.util.Severity import com.slack.circuit.runtime.CircuitContext @@ -56,7 +56,7 @@ internal fun DevLog( ) { val eventSink = state.eventSink - TiviScaffold( + HazeScaffold( topBar = { TopAppBar( title = { Text("Log") }, diff --git a/ui/developer/settings/src/commonMain/kotlin/app/tivi/settings/developer/DevSettings.kt b/ui/developer/settings/src/commonMain/kotlin/app/tivi/settings/developer/DevSettings.kt index 4adaf6bc10..983396c6ed 100644 --- a/ui/developer/settings/src/commonMain/kotlin/app/tivi/settings/developer/DevSettings.kt +++ b/ui/developer/settings/src/commonMain/kotlin/app/tivi/settings/developer/DevSettings.kt @@ -15,8 +15,8 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import app.tivi.common.compose.HazeScaffold import app.tivi.common.compose.LocalStrings -import app.tivi.common.compose.TiviScaffold import app.tivi.common.compose.ui.CheckboxPreference import app.tivi.common.compose.ui.Preference import app.tivi.screens.DevSettingsScreen @@ -47,7 +47,7 @@ internal fun DevSettings( ) { val eventSink = state.eventSink - TiviScaffold( + HazeScaffold( topBar = { TopAppBar( title = { Text(LocalStrings.current.developerSettingsTitle) }, diff --git a/ui/discover/src/commonMain/kotlin/app/tivi/home/discover/Discover.kt b/ui/discover/src/commonMain/kotlin/app/tivi/home/discover/Discover.kt index d3c00a2e26..299725a225 100644 --- a/ui/discover/src/commonMain/kotlin/app/tivi/home/discover/Discover.kt +++ b/ui/discover/src/commonMain/kotlin/app/tivi/home/discover/Discover.kt @@ -59,12 +59,12 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.FirstBaseline import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp +import app.tivi.common.compose.HazeScaffold import app.tivi.common.compose.Layout import app.tivi.common.compose.LocalStrings import app.tivi.common.compose.LocalTiviTextCreator import app.tivi.common.compose.LocalWindowSizeClass import app.tivi.common.compose.ReportDrawnWhen -import app.tivi.common.compose.TiviScaffold import app.tivi.common.compose.bodyWidth import app.tivi.common.compose.rememberCoroutineScope import app.tivi.common.compose.theme.TiviTheme @@ -177,7 +177,7 @@ internal fun Discover( state.trendingItems.isNotEmpty() } - TiviScaffold( + HazeScaffold( topBar = { TiviRootScreenAppBar( title = LocalStrings.current.discoverTitle, diff --git a/ui/library/src/commonMain/kotlin/app/tivi/home/library/Library.kt b/ui/library/src/commonMain/kotlin/app/tivi/home/library/Library.kt index ad397e7584..f7ae89957d 100644 --- a/ui/library/src/commonMain/kotlin/app/tivi/home/library/Library.kt +++ b/ui/library/src/commonMain/kotlin/app/tivi/home/library/Library.kt @@ -61,11 +61,11 @@ import androidx.compose.ui.unit.dp import app.cash.paging.LoadStateLoading import app.cash.paging.compose.LazyPagingItems import app.cash.paging.compose.itemKey +import app.tivi.common.compose.HazeScaffold import app.tivi.common.compose.Layout import app.tivi.common.compose.LocalStrings import app.tivi.common.compose.LocalTiviDateFormatter import app.tivi.common.compose.LocalTiviTextCreator -import app.tivi.common.compose.TiviScaffold import app.tivi.common.compose.bodyWidth import app.tivi.common.compose.fullSpanItem import app.tivi.common.compose.rememberCoroutineScope @@ -169,7 +169,7 @@ internal fun Library( val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() - TiviScaffold( + HazeScaffold( topBar = { TiviRootScreenAppBar( title = LocalStrings.current.libraryTitle, diff --git a/ui/licenses/src/commonMain/kotlin/app/tivi/settings/licenses/Licenses.kt b/ui/licenses/src/commonMain/kotlin/app/tivi/settings/licenses/Licenses.kt index 84d12f9208..4cf57fff2c 100644 --- a/ui/licenses/src/commonMain/kotlin/app/tivi/settings/licenses/Licenses.kt +++ b/ui/licenses/src/commonMain/kotlin/app/tivi/settings/licenses/Licenses.kt @@ -21,8 +21,8 @@ import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import app.tivi.common.compose.HazeScaffold import app.tivi.common.compose.LocalStrings -import app.tivi.common.compose.TiviScaffold import app.tivi.common.compose.ui.Preference import app.tivi.common.compose.ui.PreferenceHeader import app.tivi.screens.LicensesScreen @@ -53,7 +53,7 @@ internal fun Licenses( ) { val eventSink = state.eventSink - TiviScaffold( + HazeScaffold( topBar = { TopAppBar( title = { Text(LocalStrings.current.settingsOpenSource) }, diff --git a/ui/root/src/commonMain/kotlin/app/tivi/home/Home.kt b/ui/root/src/commonMain/kotlin/app/tivi/home/Home.kt index 722dead5f7..2e38c0b22f 100644 --- a/ui/root/src/commonMain/kotlin/app/tivi/home/Home.kt +++ b/ui/root/src/commonMain/kotlin/app/tivi/home/Home.kt @@ -43,9 +43,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp +import app.tivi.common.compose.HazeScaffold import app.tivi.common.compose.LocalStrings import app.tivi.common.compose.LocalWindowSizeClass -import app.tivi.common.compose.TiviScaffold import app.tivi.common.ui.resources.TiviStrings import app.tivi.screens.DiscoverScreen import app.tivi.screens.LibraryScreen @@ -77,7 +77,7 @@ internal fun Home( val strings = LocalStrings.current val navigationItems = remember(strings) { buildNavigationItems(strings) } - TiviScaffold( + HazeScaffold( bottomBar = { if (navigationType == NavigationType.BOTTOM_NAVIGATION) { HomeNavigationBar( diff --git a/ui/search/src/commonMain/kotlin/app/tivi/home/search/Search.kt b/ui/search/src/commonMain/kotlin/app/tivi/home/search/Search.kt index 4e7ffd13b6..a22a732fdd 100644 --- a/ui/search/src/commonMain/kotlin/app/tivi/home/search/Search.kt +++ b/ui/search/src/commonMain/kotlin/app/tivi/home/search/Search.kt @@ -42,9 +42,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import app.tivi.common.compose.HazeScaffold import app.tivi.common.compose.Layout import app.tivi.common.compose.LocalStrings -import app.tivi.common.compose.TiviScaffold import app.tivi.common.compose.bodyWidth import app.tivi.common.compose.ui.EmptyContent import app.tivi.common.compose.ui.PosterCard @@ -116,7 +116,7 @@ internal fun Search( } } - TiviScaffold( + HazeScaffold( topBar = { Box( Modifier diff --git a/ui/settings/src/commonMain/kotlin/app/tivi/settings/Settings.kt b/ui/settings/src/commonMain/kotlin/app/tivi/settings/Settings.kt index 076cf14a63..dc66fe8c0d 100644 --- a/ui/settings/src/commonMain/kotlin/app/tivi/settings/Settings.kt +++ b/ui/settings/src/commonMain/kotlin/app/tivi/settings/Settings.kt @@ -24,8 +24,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp +import app.tivi.common.compose.HazeScaffold import app.tivi.common.compose.LocalStrings -import app.tivi.common.compose.TiviScaffold import app.tivi.common.compose.itemSpacer import app.tivi.common.compose.ui.CheckboxPreference import app.tivi.common.compose.ui.Preference @@ -63,7 +63,7 @@ internal fun Settings( val strings = LocalStrings.current - TiviScaffold( + HazeScaffold( topBar = { TopAppBar( title = { Text(strings.settingsTitle) }, diff --git a/ui/show/details/src/commonMain/kotlin/app/tivi/showdetails/details/ShowDetails.kt b/ui/show/details/src/commonMain/kotlin/app/tivi/showdetails/details/ShowDetails.kt index cb373b9b1b..f215e939f9 100644 --- a/ui/show/details/src/commonMain/kotlin/app/tivi/showdetails/details/ShowDetails.kt +++ b/ui/show/details/src/commonMain/kotlin/app/tivi/showdetails/details/ShowDetails.kt @@ -90,10 +90,10 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.max import androidx.compose.ui.unit.sp +import app.tivi.common.compose.HazeScaffold import app.tivi.common.compose.Layout import app.tivi.common.compose.LocalStrings import app.tivi.common.compose.LocalTiviTextCreator -import app.tivi.common.compose.TiviScaffold import app.tivi.common.compose.bodyWidth import app.tivi.common.compose.gutterSpacer import app.tivi.common.compose.itemSpacer @@ -203,7 +203,7 @@ internal fun ShowDetails( val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() - TiviScaffold( + HazeScaffold( topBar = { ShowDetailsAppBar( title = null, diff --git a/ui/show/seasons/src/commonMain/kotlin/app/tivi/showdetails/seasons/ShowSeasons.kt b/ui/show/seasons/src/commonMain/kotlin/app/tivi/showdetails/seasons/ShowSeasons.kt index d81f3ed159..a31c462078 100644 --- a/ui/show/seasons/src/commonMain/kotlin/app/tivi/showdetails/seasons/ShowSeasons.kt +++ b/ui/show/seasons/src/commonMain/kotlin/app/tivi/showdetails/seasons/ShowSeasons.kt @@ -67,10 +67,10 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.lerp +import app.tivi.common.compose.HazeScaffold import app.tivi.common.compose.Layout import app.tivi.common.compose.LocalStrings import app.tivi.common.compose.LocalTiviTextCreator -import app.tivi.common.compose.TiviScaffold import app.tivi.common.compose.bodyWidth import app.tivi.common.compose.rememberCoroutineScope import app.tivi.common.compose.ui.AsyncImage @@ -167,7 +167,7 @@ internal fun ShowSeasons( } } - TiviScaffold( + HazeScaffold( topBar = { Column { TopAppBar( diff --git a/ui/upnext/src/commonMain/kotlin/app/tivi/home/upnext/UpNext.kt b/ui/upnext/src/commonMain/kotlin/app/tivi/home/upnext/UpNext.kt index 46ae7b5222..9f3710489f 100644 --- a/ui/upnext/src/commonMain/kotlin/app/tivi/home/upnext/UpNext.kt +++ b/ui/upnext/src/commonMain/kotlin/app/tivi/home/upnext/UpNext.kt @@ -57,10 +57,10 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp import app.cash.paging.LoadStateLoading import app.cash.paging.compose.itemKey +import app.tivi.common.compose.HazeScaffold import app.tivi.common.compose.Layout import app.tivi.common.compose.LocalStrings import app.tivi.common.compose.LocalTiviTextCreator -import app.tivi.common.compose.TiviScaffold import app.tivi.common.compose.bodyWidth import app.tivi.common.compose.fullSpanItem import app.tivi.common.compose.rememberCoroutineScope @@ -175,7 +175,7 @@ internal fun UpNext( val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() - TiviScaffold( + HazeScaffold( topBar = { TiviRootScreenAppBar( title = LocalStrings.current.upnextTitle,