feat(ui): elevated style reader toolbars (#894)
* feat(ui): toolbar animation * feat(ui): elevated variant for reader toolbars
This commit is contained in:
parent
629f489ee3
commit
6e55c12ca8
@ -15,42 +15,30 @@ val LocalReadingPageTonalElevation =
|
||||
compositionLocalOf<ReadingPageTonalElevationPreference> { ReadingPageTonalElevationPreference.default }
|
||||
|
||||
sealed class ReadingPageTonalElevationPreference(val value: Int) : Preference() {
|
||||
object Level0 : ReadingPageTonalElevationPreference(ElevationTokens.Level0)
|
||||
object Level1 : ReadingPageTonalElevationPreference(ElevationTokens.Level1)
|
||||
object Level2 : ReadingPageTonalElevationPreference(ElevationTokens.Level2)
|
||||
object Level3 : ReadingPageTonalElevationPreference(ElevationTokens.Level3)
|
||||
object Level4 : ReadingPageTonalElevationPreference(ElevationTokens.Level4)
|
||||
object Level5 : ReadingPageTonalElevationPreference(ElevationTokens.Level5)
|
||||
data object Outlined : ReadingPageTonalElevationPreference(ElevationTokens.Level0)
|
||||
data object Elevated : ReadingPageTonalElevationPreference(ElevationTokens.Level2)
|
||||
|
||||
override fun put(context: Context, scope: CoroutineScope) {
|
||||
scope.launch {
|
||||
context.dataStore.put(DataStoreKey.readingPageTonalElevation, value)
|
||||
context.dataStore.put(readingPageTonalElevation, value)
|
||||
}
|
||||
}
|
||||
|
||||
fun toDesc(context: Context): String =
|
||||
when (this) {
|
||||
Level0 -> "Level 0 (${ElevationTokens.Level0}dp)"
|
||||
Level1 -> "Level 1 (${ElevationTokens.Level1}dp)"
|
||||
Level2 -> "Level 2 (${ElevationTokens.Level2}dp)"
|
||||
Level3 -> "Level 3 (${ElevationTokens.Level3}dp)"
|
||||
Level4 -> "Level 4 (${ElevationTokens.Level4}dp)"
|
||||
Level5 -> "Level 5 (${ElevationTokens.Level5}dp)"
|
||||
Outlined -> "${ElevationTokens.Level0}dp"
|
||||
Elevated -> "${ElevationTokens.Level2}dp"
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val default = Level0
|
||||
val values = listOf(Level0, Level1, Level2, Level3, Level4, Level5)
|
||||
val default = Outlined
|
||||
val values = listOf(Outlined, Elevated)
|
||||
|
||||
fun fromPreferences(preferences: Preferences) =
|
||||
when (preferences[DataStoreKey.keys[readingPageTonalElevation]?.key as Preferences.Key<Int>]) {
|
||||
ElevationTokens.Level0 -> Level0
|
||||
ElevationTokens.Level1 -> Level1
|
||||
ElevationTokens.Level2 -> Level2
|
||||
ElevationTokens.Level3 -> Level3
|
||||
ElevationTokens.Level4 -> Level4
|
||||
ElevationTokens.Level5 -> Level5
|
||||
ElevationTokens.Level0 -> Outlined
|
||||
ElevationTokens.Level2 -> Elevated
|
||||
else -> default
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,7 @@ package me.ash.reader.ui.page.home.reading
|
||||
|
||||
import android.view.HapticFeedbackConstants
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.VisibilityThreshold
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@ -33,15 +30,14 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.infrastructure.preference.LocalReadingPageTonalElevation
|
||||
import me.ash.reader.infrastructure.preference.LocalReadingRenderer
|
||||
import me.ash.reader.infrastructure.preference.ReadingPageTonalElevationPreference
|
||||
import me.ash.reader.infrastructure.preference.ReadingRendererPreference
|
||||
import me.ash.reader.ui.component.base.CanBeDisabledIconButton
|
||||
import me.ash.reader.ui.component.base.RYExtensibleVisibility
|
||||
import me.ash.reader.ui.component.webview.BionicReadingIcon
|
||||
|
||||
@Composable
|
||||
@ -60,6 +56,7 @@ fun BottomBar(
|
||||
onReadAloud: () -> Unit = {},
|
||||
) {
|
||||
val tonalElevation = LocalReadingPageTonalElevation.current
|
||||
val isOutlined = tonalElevation == ReadingPageTonalElevationPreference.Outlined
|
||||
val renderer = LocalReadingRenderer.current
|
||||
|
||||
Box(
|
||||
@ -70,16 +67,20 @@ fun BottomBar(
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = isShow,
|
||||
enter = expandVertically(),
|
||||
exit = shrinkVertically()
|
||||
enter = expandVertically(expandFrom = Alignment.Top),
|
||||
exit = shrinkVertically(shrinkTowards = Alignment.Top)
|
||||
) {
|
||||
val view = LocalView.current
|
||||
Column {
|
||||
HorizontalDivider(
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||
thickness = 0.5f.dp
|
||||
)
|
||||
Surface() {
|
||||
if (isOutlined) {
|
||||
HorizontalDivider(
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||
thickness = 0.5f.dp
|
||||
)
|
||||
}
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.run { if (isOutlined) surface else surfaceContainer }
|
||||
) {
|
||||
// TODO: Component styles await refactoring
|
||||
Row(
|
||||
modifier = Modifier
|
||||
|
@ -5,9 +5,7 @@ import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.LocalOverscrollConfiguration
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
@ -16,9 +14,7 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@ -28,11 +24,9 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.isSpecified
|
||||
import androidx.compose.ui.unit.sp
|
||||
@ -40,7 +34,6 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.infrastructure.preference.LocalPullToSwitchArticle
|
||||
@ -123,7 +116,7 @@ fun ReadingPage(
|
||||
TopBar(
|
||||
navController = navController,
|
||||
isShow = isShowToolBar,
|
||||
showDivider = showTopDivider,
|
||||
isScrolled = showTopDivider,
|
||||
title = readerState.title,
|
||||
link = readerState.link,
|
||||
onClick = { bringToTop = true },
|
||||
@ -194,7 +187,7 @@ fun ReadingPage(
|
||||
|
||||
|
||||
showTopDivider = snapshotFlow {
|
||||
scrollState.value != 0 || listState.firstVisibleItemIndex != 0
|
||||
scrollState.value >= 120 || listState.firstVisibleItemIndex != 0
|
||||
}.collectAsStateValue(initial = false)
|
||||
|
||||
CompositionLocalProvider(
|
||||
|
@ -1,6 +1,9 @@
|
||||
package me.ash.reader.ui.page.home.reading
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.clickable
|
||||
@ -15,7 +18,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Palette
|
||||
import androidx.compose.material.icons.outlined.Share
|
||||
@ -27,20 +29,22 @@ import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.navigation.NavHostController
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.infrastructure.preference.LocalReadingPageTonalElevation
|
||||
import me.ash.reader.infrastructure.preference.LocalSharedContent
|
||||
import me.ash.reader.infrastructure.preference.ReadingPageTonalElevationPreference
|
||||
import me.ash.reader.ui.component.base.FeedbackIconButton
|
||||
import me.ash.reader.ui.ext.surfaceColorAtElevation
|
||||
import me.ash.reader.ui.page.common.RouteName
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@ -48,7 +52,7 @@ import me.ash.reader.ui.page.common.RouteName
|
||||
fun TopBar(
|
||||
navController: NavHostController,
|
||||
isShow: Boolean,
|
||||
showDivider: Boolean = false,
|
||||
isScrolled: Boolean = false,
|
||||
title: String? = "",
|
||||
link: String? = "",
|
||||
onClick: (() -> Unit)? = null,
|
||||
@ -56,6 +60,13 @@ fun TopBar(
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val sharedContent = LocalSharedContent.current
|
||||
val isOutlined =
|
||||
LocalReadingPageTonalElevation.current == ReadingPageTonalElevationPreference.Outlined
|
||||
|
||||
val containerColor by animateColorAsState(with(MaterialTheme.colorScheme) {
|
||||
if (isOutlined || !isScrolled) surface else surfaceContainer
|
||||
}, label = "", animationSpec = spring(stiffness = Spring.StiffnessMediumLow))
|
||||
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@ -63,29 +74,30 @@ fun TopBar(
|
||||
.zIndex(1f),
|
||||
contentAlignment = Alignment.TopCenter
|
||||
) {
|
||||
Column(modifier = if (onClick == null) Modifier else Modifier.clickable(
|
||||
onClick = onClick,
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.drawBehind { drawRect(containerColor) }
|
||||
) {
|
||||
Surface(
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(
|
||||
WindowInsets.statusBars
|
||||
.asPaddingValues()
|
||||
.calculateTopPadding()
|
||||
)
|
||||
) {}
|
||||
),
|
||||
)
|
||||
AnimatedVisibility(
|
||||
visible = isShow,
|
||||
enter = expandVertically(expandFrom = Alignment.Top),
|
||||
exit = shrinkVertically(shrinkTowards = Alignment.Top)
|
||||
enter = expandVertically(expandFrom = Alignment.Bottom),
|
||||
exit = shrinkVertically(shrinkTowards = Alignment.Bottom)
|
||||
) {
|
||||
TopAppBar(
|
||||
title = {},
|
||||
modifier = Modifier,
|
||||
modifier = if (onClick == null) Modifier else Modifier.clickable(
|
||||
onClick = onClick,
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
),
|
||||
windowInsets = WindowInsets(0.dp),
|
||||
navigationIcon = {
|
||||
FeedbackIconButton(
|
||||
@ -95,7 +107,8 @@ fun TopBar(
|
||||
) {
|
||||
onClose()
|
||||
}
|
||||
}, actions = {
|
||||
},
|
||||
actions = {
|
||||
FeedbackIconButton(
|
||||
modifier = Modifier.size(22.dp),
|
||||
imageVector = Icons.Outlined.Palette,
|
||||
@ -114,10 +127,11 @@ fun TopBar(
|
||||
) {
|
||||
sharedContent.share(context, title, link)
|
||||
}
|
||||
}, colors = TopAppBarDefaults.topAppBarColors()
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent)
|
||||
)
|
||||
}
|
||||
if (showDivider) {
|
||||
if (isOutlined && isScrolled) {
|
||||
HorizontalDivider(
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||
thickness = 0.5f.dp
|
||||
|
@ -204,14 +204,19 @@ fun ReadingStylePage(
|
||||
onClick = { pullToSwitchArticle.toggle(context, scope) }) {
|
||||
RYSwitch(activated = pullToSwitchArticle.value)
|
||||
}
|
||||
/* SettingItem(
|
||||
Subtitle(
|
||||
modifier = Modifier.padding(horizontal = 24.dp),
|
||||
text = stringResource(R.string.toolbars)
|
||||
)
|
||||
SettingItem(
|
||||
title = stringResource(R.string.tonal_elevation),
|
||||
desc = "${tonalElevation.value}dp",
|
||||
onClick = {
|
||||
tonalElevationDialogVisible = true
|
||||
},
|
||||
) {}
|
||||
Spacer(modifier = Modifier.height(24.dp))*/
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
|
||||
// Advanced
|
||||
@ -271,7 +276,7 @@ fun ReadingStylePage(
|
||||
}
|
||||
)
|
||||
|
||||
/* RadioDialog(
|
||||
RadioDialog(
|
||||
visible = tonalElevationDialogVisible,
|
||||
title = stringResource(R.string.tonal_elevation),
|
||||
options = ReadingPageTonalElevationPreference.values.map {
|
||||
@ -284,7 +289,7 @@ fun ReadingStylePage(
|
||||
}
|
||||
) {
|
||||
tonalElevationDialogVisible = false
|
||||
}*/
|
||||
}
|
||||
|
||||
RadioDialog(
|
||||
visible = rendererDialogVisible,
|
||||
|
@ -460,4 +460,5 @@
|
||||
<string name="mark_as_read_on_scroll">Mark as read on scroll</string>
|
||||
<string name="become_a_sponsor">Become a sponsor</string>
|
||||
<string name="sponsor_desc">We build and upkeep this free, open-source app in our off-hours. If you enjoy it, please consider supporting us with a small donation! ☕️</string>
|
||||
<string name="toolbars">Toolbars</string>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user