Tweak code
This commit is contained in:
parent
6a69eb57e9
commit
752b29c93a
11 changed files with 173 additions and 189 deletions
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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 ->
|
||||
|
|
|
@ -210,7 +210,7 @@ fun HomeSongList() {
|
|||
ScrollToTop(
|
||||
lazyListState = lazyListState,
|
||||
modifier = Modifier
|
||||
.offset(x = -Dimensions.verticalBarWidth)
|
||||
.offset(x = Dimensions.navigationRailIconOffset - Dimensions.navigationRailWidth)
|
||||
.align(Alignment.BottomStart)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,12 +143,17 @@ inline fun <T : YouTube.Item> SearchResult(
|
|||
}
|
||||
}
|
||||
} ?: item(key = "loading") {
|
||||
repeat(if (items.isEmpty()) 8 else 3) { index ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.alpha(1f - index * 0.125f),
|
||||
content = itemShimmer
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.shimmer()
|
||||
) {
|
||||
repeat(if (items.isEmpty()) 8 else 3) { index ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.alpha(1f - index * 0.125f),
|
||||
content = itemShimmer
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue