desaturate the playing row's art behind the primary indicator

Back to the primary border and glyph, and drop the playing cover's
saturation so the accent reads against its own artwork.
This commit is contained in:
austinried
2026-10-04 00:30:02 +00:00
parent 3eef4e79b6
commit aa954ffc3e
2 changed files with 14 additions and 2 deletions
@@ -32,6 +32,7 @@ import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.FilterQuality import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
@@ -70,6 +71,7 @@ fun CoverArt(
showPlaceholder: Boolean = true, showPlaceholder: Boolean = true,
square: Boolean = true, square: Boolean = true,
elevation: Dp = 0.dp, elevation: Dp = 0.dp,
colorFilter: ColorFilter? = null,
) { ) {
val context = LocalPlatformContext.current val context = LocalPlatformContext.current
var failed by remember(ref, thumbnailRef) { mutableStateOf(false) } var failed by remember(ref, thumbnailRef) { mutableStateOf(false) }
@@ -93,6 +95,7 @@ fun CoverArt(
onThumbnailLoaded = { thumbnailLoaded = true }, onThumbnailLoaded = { thumbnailLoaded = true },
onThumbnailRatio = { thumbnailRatio = it }, onThumbnailRatio = { thumbnailRatio = it },
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
colorFilter = colorFilter,
) { ) {
if (ref != null) { if (ref != null) {
AsyncImage( AsyncImage(
@@ -100,6 +103,7 @@ fun CoverArt(
contentDescription = null, contentDescription = null,
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
filterQuality = FilterQuality.High, filterQuality = FilterQuality.High,
colorFilter = colorFilter,
onSuccess = { success -> onSuccess = { success ->
success.painter.intrinsicSize success.painter.intrinsicSize
.ratioOrNull() .ratioOrNull()
@@ -154,12 +158,14 @@ fun CoverArt(
onThumbnailLoaded = { thumbnailLoaded = true }, onThumbnailLoaded = { thumbnailLoaded = true },
onThumbnailRatio = { thumbnailRatio = it }, onThumbnailRatio = { thumbnailRatio = it },
contentScale = ContentScale.Fit, contentScale = ContentScale.Fit,
colorFilter = colorFilter,
) { ) {
if (ref != null) { if (ref != null) {
Image( Image(
painter = painter, painter = painter,
contentDescription = null, contentDescription = null,
contentScale = ContentScale.Fit, contentScale = ContentScale.Fit,
colorFilter = colorFilter,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
) )
} }
@@ -180,6 +186,7 @@ private fun BoxScope.CoverArtContent(
onThumbnailLoaded: () -> Unit, onThumbnailLoaded: () -> Unit,
onThumbnailRatio: (Float) -> Unit, onThumbnailRatio: (Float) -> Unit,
contentScale: ContentScale, contentScale: ContentScale,
colorFilter: ColorFilter?,
main: @Composable () -> Unit, main: @Composable () -> Unit,
) { ) {
val context = LocalPlatformContext.current val context = LocalPlatformContext.current
@@ -196,6 +203,7 @@ private fun BoxScope.CoverArtContent(
model = remember(thumbnailRef) { imageRequest(context, thumbnailRef, crossfade = false) }, model = remember(thumbnailRef) { imageRequest(context, thumbnailRef, crossfade = false) },
contentDescription = null, contentDescription = null,
contentScale = contentScale, contentScale = contentScale,
colorFilter = colorFilter,
onSuccess = { success -> onSuccess = { success ->
onThumbnailLoaded() onThumbnailLoaded()
success.painter.intrinsicSize success.painter.intrinsicSize
@@ -30,6 +30,8 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.RoundRect import androidx.compose.ui.geometry.RoundRect
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.graphics.Path import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.PathOperation import androidx.compose.ui.graphics.PathOperation
import androidx.compose.ui.graphics.asAndroidPath import androidx.compose.ui.graphics.asAndroidPath
@@ -66,6 +68,7 @@ private val PLAY_SHADOW_RADIUS = 3.dp
private val PLAY_SHADOW = Color.Black.copy(alpha = 0.6f) private val PLAY_SHADOW = Color.Black.copy(alpha = 0.6f)
private val INNER_SHADOW_RADIUS = 8.dp private val INNER_SHADOW_RADIUS = 8.dp
private val INNER_SHADOW = Color.Black.copy(alpha = 0.85f) private val INNER_SHADOW = Color.Black.copy(alpha = 0.85f)
private val DESATURATE = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0.45f) })
private val PLAY_ARROW = Icons.Rounded.PlayArrow private val PLAY_ARROW = Icons.Rounded.PlayArrow
private val PLAY_ARROW_PATH: Path by lazy { buildVectorPath(PLAY_ARROW) } private val PLAY_ARROW_PATH: Path by lazy { buildVectorPath(PLAY_ARROW) }
@@ -168,13 +171,14 @@ private fun CoverArtCell(
ref = ref, ref = ref,
name = name, name = name,
modifier = Modifier.size(48.dp).clip(shape), modifier = Modifier.size(48.dp).clip(shape),
colorFilter = if (isPlaying) DESATURATE else null,
) )
if (isPlaying) { if (isPlaying) {
Box(Modifier.matchParentSize().drawBehind { drawInnerShadow() }) Box(Modifier.matchParentSize().drawBehind { drawInnerShadow() })
Box( Box(
Modifier Modifier
.matchParentSize() .matchParentSize()
.border(1.5.dp, Color.White, shape), .border(1.5.dp, MaterialTheme.colorScheme.primary, shape),
) )
PlayIndicator( PlayIndicator(
shadow = true, shadow = true,
@@ -216,7 +220,7 @@ private fun PlayIndicator(
shadow: Boolean, shadow: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val tint = Color.White val tint = MaterialTheme.colorScheme.primary
if (shadow) { if (shadow) {
val radius = with(LocalDensity.current) { PLAY_SHADOW_RADIUS.toPx() } val radius = with(LocalDensity.current) { PLAY_SHADOW_RADIUS.toPx() }
Canvas(modifier.semantics { contentDescription = "Playing" }) { Canvas(modifier.semantics { contentDescription = "Playing" }) {