Tweak code

This commit is contained in:
vfsfitvnm 2022-09-28 15:43:42 +02:00
parent 6a69eb57e9
commit 752b29c93a
11 changed files with 173 additions and 189 deletions

View file

@ -1,105 +0,0 @@
package it.vfsfitvnm.vimusic.ui.components
import androidx.compose.animation.animateColor
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ripple.rememberRipple
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.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.layout
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
@Composable
fun TabColumn(
tabIndex: Int,
onTabIndexChanged: (Int) -> Unit,
selectedTextColor: Color,
disabledTextColor: Color,
textStyle: TextStyle,
modifier: Modifier = Modifier,
content: @Composable (@Composable (Int, String, Int) -> Unit) -> Unit
) {
Column(
modifier = modifier
.verticalScroll(rememberScrollState())
) {
val transition = updateTransition(targetState = tabIndex, label = null)
content { index, text, icon ->
val dothAlpha by transition.animateFloat(label = "") {
if (it == index) 1f else 0f
}
val textColor by transition.animateColor(label = "") {
if (it == index) selectedTextColor else disabledTextColor
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.clickable(
indication = rememberRipple(bounded = true),
interactionSource = remember { MutableInteractionSource() },
onClick = { onTabIndexChanged(index) }
)
.padding(horizontal = 8.dp)
) {
Image(
painter = painterResource(icon),
contentDescription = null,
colorFilter = ColorFilter.tint(selectedTextColor),
modifier = Modifier
.vertical()
.graphicsLayer {
alpha = dothAlpha
translationX = (1f - dothAlpha) * -48.dp.toPx()
rotationZ = -90f
}
.size(12.dp)
)
BasicText(
text = text,
style = textStyle.copy(color = textColor),
modifier = Modifier
.vertical()
.rotate(-90f)
.padding(horizontal = 16.dp)
)
}
}
}
}
fun Modifier.vertical() =
layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
layout(placeable.height, placeable.width) {
placeable.place(
x = -(placeable.width / 2 - placeable.height / 2),
y = -(placeable.height / 2 - placeable.width / 2)
)
}
}

View file

@ -18,6 +18,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.shimmer
import it.vfsfitvnm.vimusic.utils.medium
@ -55,7 +56,7 @@ fun Header(
horizontalAlignment = Alignment.End,
modifier = modifier
.padding(horizontal = 16.dp)
.height(128.dp)
.height(Dimensions.headerHeight)
.fillMaxWidth()
) {
Spacer(

View file

@ -0,0 +1,134 @@
package it.vfsfitvnm.vimusic.ui.components.themed
import androidx.compose.animation.animateColor
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ripple.rememberRipple
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.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.layout
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.semiBold
@Composable
fun NavigationRail(
topIconButtonId: Int,
onTopIconButtonClick: () -> Unit,
tabIndex: Int,
onTabIndexChanged: (Int) -> Unit,
content: @Composable ColumnScope.(@Composable (Int, String, Int) -> Unit) -> Unit,
modifier: Modifier = Modifier
) {
val (colorPalette, typography) = LocalAppearance.current
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier
) {
Box(
contentAlignment = Alignment.TopCenter,
modifier = Modifier
.size(width = Dimensions.navigationRailWidth, height = Dimensions.headerHeight)
) {
Image(
painter = painterResource(topIconButtonId),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.textSecondary),
modifier = Modifier
.offset(x = Dimensions.navigationRailIconOffset, y = 48.dp)
.clip(CircleShape)
.clickable(onClick = onTopIconButtonClick)
.padding(all = 12.dp)
.size(22.dp)
)
}
Column(
modifier = modifier
.verticalScroll(rememberScrollState())
) {
val transition = updateTransition(targetState = tabIndex, label = null)
content { index, text, icon ->
val dothAlpha by transition.animateFloat(label = "") {
if (it == index) 1f else 0f
}
val textColor by transition.animateColor(label = "") {
if (it == index) colorPalette.text else colorPalette.textDisabled
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(24.dp))
.clickable(
indication = rememberRipple(bounded = true),
interactionSource = remember { MutableInteractionSource() },
onClick = { onTabIndexChanged(index) }
)
.padding(horizontal = 8.dp)
) {
Image(
painter = painterResource(icon),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.vertical()
.graphicsLayer {
alpha = dothAlpha
translationX = (1f - dothAlpha) * -48.dp.toPx()
rotationZ = -90f
}
.size(Dimensions.navigationRailIconOffset * 2)
)
BasicText(
text = text,
style = typography.xs.semiBold.copy(color = textColor),
modifier = Modifier
.vertical()
.rotate(-90f)
.padding(horizontal = 16.dp)
)
}
}
}
}
}
private fun Modifier.vertical() =
layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
layout(placeable.height, placeable.width) {
placeable.place(
x = -(placeable.width / 2 - placeable.height / 2),
y = -(placeable.height / 2 - placeable.width / 2)
)
}
}

View file

@ -13,6 +13,7 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
@ -37,7 +38,7 @@ fun Scaffold(
onTopIconButtonClick: () -> Unit,
tabIndex: Int,
onTabChanged: (Int) -> Unit,
tabColumnContent: @Composable (@Composable (Int, String, Int) -> Unit) -> Unit,
tabColumnContent: @Composable ColumnScope.(@Composable (Int, String, Int) -> Unit) -> Unit,
primaryIconButtonId: Int? = null,
onPrimaryIconButtonClick: () -> Unit = {},
modifier: Modifier = Modifier,
@ -54,14 +55,14 @@ fun Scaffold(
modifier = modifier
.fillMaxSize()
) {
VerticalBar(
NavigationRail(
topIconButtonId = topIconButtonId,
onTopIconButtonClick = onTopIconButtonClick,
tabIndex = tabIndex,
onTabChanged = onTabChanged,
tabColumnContent = tabColumnContent,
onTabIndexChanged = onTabChanged,
modifier = Modifier
.padding(LocalPlayerAwarePaddingValues.current)
.padding(LocalPlayerAwarePaddingValues.current),
content = tabColumnContent
)
AnimatedContent(

View file

@ -1,66 +0,0 @@
package it.vfsfitvnm.vimusic.ui.components.themed
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.ui.components.TabColumn
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.semiBold
@Composable
fun VerticalBar(
topIconButtonId: Int,
onTopIconButtonClick: () -> Unit,
tabIndex: Int,
onTabChanged: (Int) -> Unit,
tabColumnContent: @Composable (@Composable (Int, String, Int) -> Unit) -> Unit,
modifier: Modifier = Modifier
) {
val (colorPalette, typography) = LocalAppearance.current
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier
.padding(vertical = 16.dp)
) {
Image(
painter = painterResource(topIconButtonId),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.textSecondary),
modifier = Modifier
.clip(CircleShape)
.clickable(onClick = onTopIconButtonClick)
.padding(all = 12.dp)
.size(22.dp)
)
Spacer(
modifier = Modifier
.width(Dimensions.verticalBarWidth)
.height(32.dp)
)
TabColumn(
tabIndex = tabIndex,
onTabIndexChanged = onTabChanged,
selectedTextColor = colorPalette.text,
disabledTextColor = colorPalette.textDisabled,
textStyle = typography.xs.semiBold,
content = tabColumnContent,
)
}
}

View file

@ -130,7 +130,7 @@ fun AlbumOverview(
}
BoxWithConstraints {
val thumbnailSizeDp = maxWidth - Dimensions.verticalBarWidth
val thumbnailSizeDp = maxWidth - Dimensions.navigationRailWidth
val thumbnailSizePx = (thumbnailSizeDp - 32.dp).px
albumResult?.getOrNull()?.let { album ->

View file

@ -210,7 +210,7 @@ fun HomeSongList() {
ScrollToTop(
lazyListState = lazyListState,
modifier = Modifier
.offset(x = -Dimensions.verticalBarWidth)
.offset(x = Dimensions.navigationRailIconOffset - Dimensions.navigationRailWidth)
.align(Alignment.BottomStart)
)
}

View file

@ -99,7 +99,7 @@ fun PlaylistSongList(
}
BoxWithConstraints {
val thumbnailSizeDp = maxWidth - Dimensions.verticalBarWidth
val thumbnailSizeDp = maxWidth - Dimensions.navigationRailWidth
val thumbnailSizePx = (thumbnailSizeDp - 32.dp).px
val songThumbnailSizeDp = Dimensions.thumbnails.song

View file

@ -49,9 +49,9 @@ import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.savers.SearchQueryListSaver
import it.vfsfitvnm.vimusic.savers.StringListResultSaver
import it.vfsfitvnm.vimusic.ui.components.themed.Header
import it.vfsfitvnm.vimusic.ui.components.themed.LoadingOrError
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.align
import it.vfsfitvnm.vimusic.utils.center
import it.vfsfitvnm.vimusic.utils.medium
import it.vfsfitvnm.vimusic.utils.produceSaveableOneShotState
import it.vfsfitvnm.vimusic.utils.produceSaveableState
@ -320,7 +320,17 @@ fun OnlineSearch(
}
} ?: suggestionsResult?.exceptionOrNull()?.let { throwable ->
item {
LoadingOrError(errorMessage = throwable.javaClass.canonicalName) {}
Box(
modifier = Modifier
.fillMaxSize()
) {
BasicText(
text = "An error has occurred.",
style = typography.s.medium.secondary.center,
modifier = Modifier
.align(Alignment.Center)
)
}
}
}
}

View file

@ -4,6 +4,7 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyItemScope
@ -19,6 +20,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.input.pointer.pointerInput
import com.valentinilk.shimmer.shimmer
import it.vfsfitvnm.vimusic.LocalPlayerAwarePaddingValues
import it.vfsfitvnm.vimusic.savers.ListSaver
import it.vfsfitvnm.vimusic.savers.StringResultSaver
@ -141,6 +143,10 @@ inline fun <T : YouTube.Item> SearchResult(
}
}
} ?: item(key = "loading") {
Column(
modifier = Modifier
.shimmer()
) {
repeat(if (items.isEmpty()) 8 else 3) { index ->
Box(
modifier = Modifier
@ -151,3 +157,4 @@ inline fun <T : YouTube.Item> SearchResult(
}
}
}
}

View file

@ -10,7 +10,9 @@ import androidx.compose.ui.unit.dp
object Dimensions {
val itemsVerticalPadding = 8.dp
val verticalBarWidth = 64.dp
val navigationRailWidth = 64.dp
val navigationRailIconOffset = 6.dp
val headerHeight = 128.dp
object thumbnails {
val album = 128.dp