From 12ef478b0edfe4997e3f8bdce5cb561d0b34f3a1 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sun, 5 Jul 2026 22:23:16 +0600 Subject: [PATCH] feat: simplify highlight implementation in UI components for improved readability and maintainability --- .../core/ui/base/AutocompleteTextField.kt | 16 +--- .../krtirtho/spotube/core/ui/base/Buttons.kt | 84 ++++++++----------- .../krtirtho/spotube/core/ui/base/CheckBox.kt | 16 +--- .../krtirtho/spotube/core/ui/base/ChipTabs.kt | 16 +--- .../spotube/core/ui/base/TextField.kt | 16 +--- .../krtirtho/spotube/core/ui/base/Theme.kt | 30 ++++++- .../krtirtho/spotube/core/ui/base/Utils.kt | 23 +++++ .../spotube/core/ui/component/TrackList.kt | 23 ++--- 8 files changed, 94 insertions(+), 130 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Utils.kt diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/AutocompleteTextField.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/AutocompleteTextField.kt index d3dece46..5744a69e 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/AutocompleteTextField.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/AutocompleteTextField.kt @@ -269,21 +269,7 @@ fun AutocompleteTextField( BorderStroke(resolvedBorder.width, resolvedBorder.color), fieldState.shape ) - .drawWithCache { - val highlightBrush = Brush.verticalGradient( - colors = listOf(fieldState.highlight, Color.Transparent), - startY = 0f, - endY = size.height * 0.5f, - ) - onDrawWithContent { - drawContent() - drawRect( - brush = highlightBrush, - topLeft = androidx.compose.ui.geometry.Offset.Zero, - size = size, - ) - } - } + .highlight(fieldState.highlight) .padding(resolvedTextFieldTheme.padding), ) { Row( diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Buttons.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Buttons.kt index 3677ebbf..53b87ee8 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Buttons.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Buttons.kt @@ -89,9 +89,29 @@ private fun resolveButtonState( isHovered: Boolean, ): ResolvedButtonState { return when { - isPressed -> ResolvedButtonState(style.colors.pressed, style.shape.pressed, style.shadow.pressed, style.border.pressed, style.padding.pressed) - isHovered -> ResolvedButtonState(style.colors.hovered, style.shape.hovered, style.shadow.hovered, style.border.hovered, style.padding.hovered) - else -> ResolvedButtonState(style.colors.focused, style.shape.focused, style.shadow.focused, style.border.focused, style.padding.focused) + isPressed -> ResolvedButtonState( + style.colors.pressed, + style.shape.pressed, + style.shadow.pressed, + style.border.pressed, + style.padding.pressed + ) + + isHovered -> ResolvedButtonState( + style.colors.hovered, + style.shape.hovered, + style.shadow.hovered, + style.border.hovered, + style.padding.hovered + ) + + else -> ResolvedButtonState( + style.colors.focused, + style.shape.focused, + style.shadow.focused, + style.border.focused, + style.padding.focused + ) } } @@ -134,11 +154,17 @@ fun OutlineButton( .hoverable(interactionSource = interactionSource, enabled = enabled) .graphicsLayer { translationY = lift.toPx() } .then( - if (hoverOnly && !isHovered) Modifier else Modifier.applyShadow(state.shadow, state.shape) + if (hoverOnly && !isHovered) Modifier else Modifier.applyShadow( + state.shadow, + state.shape + ) ) .clip(state.shape) .then( - if (hoverOnly && !isHovered) Modifier else Modifier.background(state.colors.background, state.shape) + if (hoverOnly && !isHovered) Modifier else Modifier.background( + state.colors.background, + state.shape + ) .border(BorderStroke(state.border.width, state.border.color), state.shape) ) .clickable( @@ -147,21 +173,7 @@ fun OutlineButton( indication = ripple(), onClick = onClick, ) - .then(if (hoverOnly && !isHovered) Modifier else Modifier.drawWithCache { - val highlightBrush = Brush.verticalGradient( - colors = listOf(state.colors.highlight, Color.Transparent), - startY = 0f, - endY = size.height * 0.5f, - ) - onDrawWithContent { - drawContent() - drawRect( - brush = highlightBrush, - topLeft = androidx.compose.ui.geometry.Offset.Zero, - size = size, - ) - } - }) + .then(if (hoverOnly && !isHovered) Modifier else Modifier.highlight(state.colors.highlight)) .padding(state.padding), contentAlignment = Alignment.Center, ) { @@ -208,21 +220,7 @@ fun PrimaryButton( indication = ripple(), onClick = onClick, ) - .drawWithCache { - val highlightBrush = Brush.verticalGradient( - colors = listOf(state.colors.highlight, Color.Transparent), - startY = 0f, - endY = size.height * 0.5f, - ) - onDrawWithContent { - drawContent() - drawRect( - brush = highlightBrush, - topLeft = androidx.compose.ui.geometry.Offset.Zero, - size = size, - ) - } - } + .highlight(state.colors.highlight) .padding(state.padding), contentAlignment = Alignment.Center, ) { @@ -267,21 +265,7 @@ fun SecondaryButton( indication = ripple(), onClick = onClick, ) - .drawWithCache { - val highlightBrush = Brush.verticalGradient( - colors = listOf(state.colors.highlight, Color.Transparent), - startY = 0f, - endY = size.height * 0.5f, - ) - onDrawWithContent { - drawContent() - drawRect( - brush = highlightBrush, - topLeft = androidx.compose.ui.geometry.Offset.Zero, - size = size, - ) - } - } + .highlight(state.colors.highlight) .padding(state.padding), contentAlignment = Alignment.Center, ) { diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/CheckBox.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/CheckBox.kt index f9c47abb..7fda42c7 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/CheckBox.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/CheckBox.kt @@ -145,21 +145,7 @@ fun CheckBox( .clip(resolved.shape) .background(resolved.colors.background, resolved.shape) .border(BorderStroke(0.5.dp, borderColor), resolved.shape) - .drawWithCache { - val highlightBrush = Brush.verticalGradient( - colors = listOf(resolved.colors.highlight, Color.Transparent), - startY = 0f, - endY = size.height * 0.5f, - ) - onDrawWithContent { - drawContent() - drawRect( - brush = highlightBrush, - topLeft = Offset.Zero, - size = size, - ) - } - } + .highlight(resolved.colors.highlight) .then( if (onClick != null) { Modifier diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/ChipTabs.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/ChipTabs.kt index 2b988826..6afa99d9 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/ChipTabs.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/ChipTabs.kt @@ -121,21 +121,7 @@ fun ChipTab( indication = ripple(), onClick = onClick, ) - .drawWithCache { - val highlightBrush = Brush.verticalGradient( - colors = listOf(state.colors.highlight, Color.Transparent), - startY = 0f, - endY = size.height * 0.5f, - ) - onDrawWithContent { - drawContent() - drawRect( - brush = highlightBrush, - topLeft = androidx.compose.ui.geometry.Offset.Zero, - size = size, - ) - } - } + .highlight(state.colors.highlight) .padding(state.padding), contentAlignment = Alignment.Center, ) { diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/TextField.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/TextField.kt index 964e7330..b75cc7ed 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/TextField.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/TextField.kt @@ -152,21 +152,7 @@ fun TextField( .clip(state.shape) .background(state.background, state.shape) .border(BorderStroke(resolvedBorder.width, resolvedBorder.color), state.shape) - .drawWithCache { - val highlightBrush = Brush.verticalGradient( - colors = listOf(state.highlight, Color.Transparent), - startY = 0f, - endY = size.height * 0.5f, - ) - onDrawWithContent { - drawContent() - drawRect( - brush = highlightBrush, - topLeft = androidx.compose.ui.geometry.Offset.Zero, - size = size, - ) - } - } + .highlight(state.highlight) .padding(textFieldTheme.padding), ) { Row( diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Theme.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Theme.kt index 25a0f673..7039e867 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Theme.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Theme.kt @@ -23,6 +23,8 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithCache import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape @@ -651,15 +653,30 @@ fun rememberBaseUITheme(): BaseUITheme { shape = RoundedCornerShape(16.dp), border = BaseUITheme.Border(border, 0.5.dp), padding = PaddingValues(0.dp), - shadow = BaseUITheme.Shadow(6.dp, true, shadowColor.copy(alpha = 0.15f), shadowColor.copy(alpha = 0.18f)), + shadow = BaseUITheme.Shadow( + 6.dp, + true, + shadowColor.copy(alpha = 0.15f), + shadowColor.copy(alpha = 0.18f) + ), ), listRowTile = BaseUITheme.ListRowTheme( background = BaseUITheme.AdvancedInteractionState( hovered = Brush.verticalGradient(listOf(containerLighter, containerDarker)), pressed = Brush.verticalGradient(listOf(containerPressed, containerPressed)), focused = Brush.verticalGradient(listOf(containerLighter, containerDarker)), - selected = Brush.verticalGradient(listOf(scheme.primaryContainer.copy(alpha = 0.3f), scheme.primaryContainer.copy(alpha = 0.3f))), - disabled = Brush.verticalGradient(listOf(containerLighter.copy(alpha = 0.5f), containerDarker.copy(alpha = 0.5f))), + selected = Brush.verticalGradient( + listOf( + scheme.primaryContainer.copy(alpha = 0.3f), + scheme.primaryContainer.copy(alpha = 0.3f) + ) + ), + disabled = Brush.verticalGradient( + listOf( + containerLighter.copy(alpha = 0.5f), + containerDarker.copy(alpha = 0.5f) + ) + ), ), shape = BaseUITheme.AdvancedInteractionState( hovered = RoundedCornerShape(8.dp), @@ -730,7 +747,12 @@ fun invertedButtonStyle(): BaseUITheme.ButtonStyle { highlight = if (isLight) Color.White.copy(alpha = 0.15f) else Color.White.copy(alpha = 0.08f), ) val pressedColors = BaseUITheme.ButtonColors( - background = Brush.verticalGradient(listOf(scheme.onSurfaceVariant, scheme.onSurfaceVariant)), + background = Brush.verticalGradient( + listOf( + scheme.onSurfaceVariant, + scheme.onSurfaceVariant + ) + ), foreground = scheme.surface, highlight = if (isLight) Color.White.copy(alpha = 0.1f) else Color.White.copy(alpha = 0.05f), ) diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Utils.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Utils.kt new file mode 100644 index 00000000..fc8ce272 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Utils.kt @@ -0,0 +1,23 @@ +package dev.krtirtho.spotube.core.ui.base + +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color + +fun Modifier.highlight(color: Color): Modifier = + drawWithCache { + val highlightBrush = Brush.verticalGradient( + colors = listOf(color, Color.Transparent), + startY = 0f, + endY = size.height * 0.5f, + ) + onDrawWithContent { + drawContent() + drawRect( + brush = highlightBrush, + topLeft = androidx.compose.ui.geometry.Offset.Zero, + size = size, + ) + } + } diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/component/TrackList.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/component/TrackList.kt index 506e2a3a..44cb0044 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/component/TrackList.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/component/TrackList.kt @@ -91,6 +91,7 @@ import dev.krtirtho.spotube.core.ui.base.GhostIconButton import dev.krtirtho.spotube.core.ui.base.GroupIconButton import dev.krtirtho.spotube.core.ui.base.LocalBaseUITheme import dev.krtirtho.spotube.core.ui.base.TextField +import dev.krtirtho.spotube.core.ui.base.highlight import dev.krtirtho.spotube.core.ui.misc.SkeletonTree import dev.krtirtho.spotube.core.ui.misc.TextWithShimmer import dev.krtirtho.spotube.core.ui.misc.shimmerApply @@ -539,23 +540,13 @@ private fun TrackListRow( onLongClick = onLongClick, ) .background(rowBackgroundColor) - .drawWithCache { - val highlightBrush = Brush.verticalGradient( - colors = listOf(highlightColor, Color.Transparent), - startY = 0f, - endY = size.height * 0.5f, - ) - onDrawWithContent { - drawContent() - if (isRowHovered || isSelected || (isCurrentTrack && isCurrentTrackPlaying)) { - drawRect( - brush = highlightBrush, - topLeft = Offset.Zero, - size = size, - ) - } + .then( + if (isRowHovered || isSelected || (isCurrentTrack && isCurrentTrackPlaying)) { + Modifier.highlight(highlightColor) + } else { + Modifier } - } + ) .clip(MaterialTheme.shapes.small) .padding(horizontal = 8.dp, vertical = 6.dp) .padding(end = 6.dp),