mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-08-05 19:59:51 +00:00
feat: add Toggle component and update UI to use it in place of Switch
This commit is contained in:
parent
6ac934ad19
commit
1ef1eff3cb
@ -299,6 +299,26 @@ fun IconButton(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun GhostButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
theme: BaseUITheme.ButtonStyle? = null,
|
||||||
|
content: @Composable RowScope.() -> Unit,
|
||||||
|
) {
|
||||||
|
val baseTheme = LocalBaseUITheme.current
|
||||||
|
val style = theme ?: baseTheme.buttons.ghost
|
||||||
|
OutlineButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled,
|
||||||
|
hoverOnly = true,
|
||||||
|
theme = style,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GhostIconButton(
|
fun GhostIconButton(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
@ -308,43 +328,13 @@ fun GhostIconButton(
|
|||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
val baseTheme = LocalBaseUITheme.current
|
val baseTheme = LocalBaseUITheme.current
|
||||||
val outlineStyle = baseTheme.iconButtons.outline
|
val style = theme ?: baseTheme.iconButtons.ghost
|
||||||
val ghostStyle = theme ?: BaseUITheme.ButtonStyle(
|
|
||||||
colors = BaseUITheme.InteractionState(
|
|
||||||
normal = BaseUITheme.ButtonColors(
|
|
||||||
background = Brush.verticalGradient(listOf(Color.Transparent, Color.Transparent)),
|
|
||||||
foreground = outlineStyle.colors.normal.foreground,
|
|
||||||
highlight = outlineStyle.colors.normal.highlight,
|
|
||||||
),
|
|
||||||
hovered = outlineStyle.colors.hovered,
|
|
||||||
pressed = outlineStyle.colors.pressed,
|
|
||||||
focused = BaseUITheme.ButtonColors(
|
|
||||||
background = Brush.verticalGradient(listOf(Color.Transparent, Color.Transparent)),
|
|
||||||
foreground = outlineStyle.colors.normal.foreground,
|
|
||||||
highlight = outlineStyle.colors.normal.highlight,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
shape = outlineStyle.shape,
|
|
||||||
shadow = BaseUITheme.InteractionState(
|
|
||||||
normal = BaseUITheme.Shadow(0.dp, false, Color.Transparent, Color.Transparent),
|
|
||||||
hovered = outlineStyle.shadow.hovered,
|
|
||||||
pressed = outlineStyle.shadow.pressed,
|
|
||||||
focused = BaseUITheme.Shadow(0.dp, false, Color.Transparent, Color.Transparent),
|
|
||||||
),
|
|
||||||
border = BaseUITheme.InteractionState(
|
|
||||||
normal = BaseUITheme.Border(Color.Transparent, 0.dp),
|
|
||||||
hovered = outlineStyle.border.hovered,
|
|
||||||
pressed = outlineStyle.border.pressed,
|
|
||||||
focused = BaseUITheme.Border(Color.Transparent, 0.dp),
|
|
||||||
),
|
|
||||||
padding = outlineStyle.padding,
|
|
||||||
)
|
|
||||||
OutlineButton(
|
OutlineButton(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
hoverOnly = true,
|
hoverOnly = true,
|
||||||
theme = ghostStyle,
|
theme = style,
|
||||||
) {
|
) {
|
||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,7 @@ data class BaseUITheme(
|
|||||||
val slider: SliderTheme,
|
val slider: SliderTheme,
|
||||||
val checkBox: CheckBoxTheme,
|
val checkBox: CheckBoxTheme,
|
||||||
val chipTab: ChipTabTheme,
|
val chipTab: ChipTabTheme,
|
||||||
|
val toggle: ToggleTheme,
|
||||||
val card: CardTheme,
|
val card: CardTheme,
|
||||||
val listRowTile: ListRowTheme,
|
val listRowTile: ListRowTheme,
|
||||||
) {
|
) {
|
||||||
@ -177,6 +178,13 @@ data class BaseUITheme(
|
|||||||
val selected: ButtonStyle,
|
val selected: ButtonStyle,
|
||||||
val unselected: ButtonStyle,
|
val unselected: ButtonStyle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class ToggleTheme(
|
||||||
|
val checked: ButtonStyle,
|
||||||
|
val unchecked: ButtonStyle,
|
||||||
|
val thumb: InteractionState<Color>,
|
||||||
|
val thumbShadow: InteractionState<Shadow>,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val LocalBaseUITheme = staticCompositionLocalOf<BaseUITheme> {
|
val LocalBaseUITheme = staticCompositionLocalOf<BaseUITheme> {
|
||||||
@ -519,6 +527,16 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
PaddingValues(horizontal = 14.dp, vertical = 4.dp),
|
PaddingValues(horizontal = 14.dp, vertical = 4.dp),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val toggleShape = RoundedCornerShape(50)
|
||||||
|
val toggleShapeState =
|
||||||
|
BaseUITheme.InteractionState<Shape>(toggleShape, toggleShape, toggleShape, toggleShape)
|
||||||
|
val togglePadding = BaseUITheme.InteractionState(
|
||||||
|
PaddingValues(2.dp),
|
||||||
|
PaddingValues(2.dp),
|
||||||
|
PaddingValues(2.dp),
|
||||||
|
PaddingValues(2.dp),
|
||||||
|
)
|
||||||
|
|
||||||
return BaseUITheme(
|
return BaseUITheme(
|
||||||
buttons = BaseUITheme.ButtonVariants(
|
buttons = BaseUITheme.ButtonVariants(
|
||||||
primary = primaryStyle,
|
primary = primaryStyle,
|
||||||
@ -731,6 +749,161 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
padding = chipTabPadding,
|
padding = chipTabPadding,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
toggle = BaseUITheme.ToggleTheme(
|
||||||
|
checked = BaseUITheme.ButtonStyle(
|
||||||
|
colors = BaseUITheme.InteractionState(
|
||||||
|
normal = BaseUITheme.ButtonColors(
|
||||||
|
background = Brush.verticalGradient(listOf(scheme.primary, scheme.primary)),
|
||||||
|
foreground = scheme.onPrimary,
|
||||||
|
highlight = Color.White.copy(alpha = 0.25f),
|
||||||
|
),
|
||||||
|
hovered = BaseUITheme.ButtonColors(
|
||||||
|
background = Brush.verticalGradient(listOf(scheme.primary, scheme.primary)),
|
||||||
|
foreground = scheme.onPrimary,
|
||||||
|
highlight = Color.White.copy(alpha = 0.25f),
|
||||||
|
),
|
||||||
|
pressed = BaseUITheme.ButtonColors(
|
||||||
|
background = Brush.verticalGradient(
|
||||||
|
listOf(
|
||||||
|
scheme.primary.copy(alpha = 0.85f),
|
||||||
|
scheme.primary
|
||||||
|
)
|
||||||
|
),
|
||||||
|
foreground = scheme.onPrimary,
|
||||||
|
highlight = Color.White.copy(alpha = 0.25f),
|
||||||
|
),
|
||||||
|
focused = BaseUITheme.ButtonColors(
|
||||||
|
background = Brush.verticalGradient(listOf(scheme.primary, scheme.primary)),
|
||||||
|
foreground = scheme.onPrimary,
|
||||||
|
highlight = Color.White.copy(alpha = 0.25f),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
shape = toggleShapeState,
|
||||||
|
shadow = BaseUITheme.InteractionState(
|
||||||
|
normal = BaseUITheme.Shadow(
|
||||||
|
4.dp,
|
||||||
|
true,
|
||||||
|
scheme.primary.copy(alpha = 0.3f),
|
||||||
|
scheme.primary.copy(alpha = 0.35f)
|
||||||
|
),
|
||||||
|
hovered = BaseUITheme.Shadow(
|
||||||
|
6.dp,
|
||||||
|
true,
|
||||||
|
scheme.primary.copy(alpha = 0.4f),
|
||||||
|
scheme.primary.copy(alpha = 0.45f)
|
||||||
|
),
|
||||||
|
pressed = BaseUITheme.Shadow(
|
||||||
|
1.dp,
|
||||||
|
true,
|
||||||
|
scheme.primary.copy(alpha = 0.2f),
|
||||||
|
scheme.primary.copy(alpha = 0.25f)
|
||||||
|
),
|
||||||
|
focused = BaseUITheme.Shadow(
|
||||||
|
4.dp,
|
||||||
|
true,
|
||||||
|
scheme.primary.copy(alpha = 0.3f),
|
||||||
|
scheme.primary.copy(alpha = 0.35f)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
border = BaseUITheme.InteractionState(
|
||||||
|
normal = BaseUITheme.Border(scheme.primary, 0.5.dp),
|
||||||
|
hovered = BaseUITheme.Border(scheme.primary, 0.5.dp),
|
||||||
|
pressed = BaseUITheme.Border(scheme.primary, 0.5.dp),
|
||||||
|
focused = BaseUITheme.Border(scheme.primary, 0.5.dp),
|
||||||
|
),
|
||||||
|
padding = togglePadding,
|
||||||
|
),
|
||||||
|
unchecked = BaseUITheme.ButtonStyle(
|
||||||
|
colors = BaseUITheme.InteractionState(
|
||||||
|
normal = BaseUITheme.ButtonColors(
|
||||||
|
background = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
||||||
|
foreground = scheme.onSurface,
|
||||||
|
highlight = highlight,
|
||||||
|
),
|
||||||
|
hovered = BaseUITheme.ButtonColors(
|
||||||
|
background = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
||||||
|
foreground = scheme.onSurface,
|
||||||
|
highlight = highlight,
|
||||||
|
),
|
||||||
|
pressed = BaseUITheme.ButtonColors(
|
||||||
|
background = Brush.verticalGradient(listOf(containerPressed, containerPressed)),
|
||||||
|
foreground = scheme.onSurface,
|
||||||
|
highlight = highlight,
|
||||||
|
),
|
||||||
|
focused = BaseUITheme.ButtonColors(
|
||||||
|
background = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
||||||
|
foreground = scheme.onSurface,
|
||||||
|
highlight = highlight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
shape = toggleShapeState,
|
||||||
|
shadow = BaseUITheme.InteractionState(
|
||||||
|
normal = BaseUITheme.Shadow(
|
||||||
|
4.dp,
|
||||||
|
true,
|
||||||
|
shadowColor.copy(alpha = 0.15f),
|
||||||
|
shadowColor.copy(alpha = 0.18f)
|
||||||
|
),
|
||||||
|
hovered = BaseUITheme.Shadow(
|
||||||
|
6.dp,
|
||||||
|
true,
|
||||||
|
shadowColor.copy(alpha = 0.22f),
|
||||||
|
shadowColor.copy(alpha = 0.26f)
|
||||||
|
),
|
||||||
|
pressed = BaseUITheme.Shadow(
|
||||||
|
1.dp,
|
||||||
|
true,
|
||||||
|
shadowColor.copy(alpha = 0.08f),
|
||||||
|
shadowColor.copy(alpha = 0.1f)
|
||||||
|
),
|
||||||
|
focused = BaseUITheme.Shadow(
|
||||||
|
4.dp,
|
||||||
|
true,
|
||||||
|
shadowColor.copy(alpha = 0.15f),
|
||||||
|
shadowColor.copy(alpha = 0.18f)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
border = BaseUITheme.InteractionState(
|
||||||
|
normal = BaseUITheme.Border(border, 0.5.dp),
|
||||||
|
hovered = BaseUITheme.Border(border, 0.5.dp),
|
||||||
|
pressed = BaseUITheme.Border(border.copy(alpha = 0.7f), 0.5.dp),
|
||||||
|
focused = BaseUITheme.Border(border, 0.5.dp),
|
||||||
|
),
|
||||||
|
padding = togglePadding,
|
||||||
|
),
|
||||||
|
thumb = BaseUITheme.InteractionState(
|
||||||
|
normal = Color.White,
|
||||||
|
hovered = Color.White,
|
||||||
|
pressed = Color.White,
|
||||||
|
focused = Color.White,
|
||||||
|
),
|
||||||
|
thumbShadow = BaseUITheme.InteractionState(
|
||||||
|
normal = BaseUITheme.Shadow(
|
||||||
|
3.dp,
|
||||||
|
true,
|
||||||
|
shadowColor.copy(alpha = 0.2f),
|
||||||
|
shadowColor.copy(alpha = 0.25f)
|
||||||
|
),
|
||||||
|
hovered = BaseUITheme.Shadow(
|
||||||
|
4.dp,
|
||||||
|
true,
|
||||||
|
shadowColor.copy(alpha = 0.28f),
|
||||||
|
shadowColor.copy(alpha = 0.32f)
|
||||||
|
),
|
||||||
|
pressed = BaseUITheme.Shadow(
|
||||||
|
1.dp,
|
||||||
|
true,
|
||||||
|
shadowColor.copy(alpha = 0.12f),
|
||||||
|
shadowColor.copy(alpha = 0.15f)
|
||||||
|
),
|
||||||
|
focused = BaseUITheme.Shadow(
|
||||||
|
3.dp,
|
||||||
|
true,
|
||||||
|
shadowColor.copy(alpha = 0.2f),
|
||||||
|
shadowColor.copy(alpha = 0.25f)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
card = BaseUITheme.CardTheme(
|
card = BaseUITheme.CardTheme(
|
||||||
background = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
background = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = RoundedCornerShape(16.dp),
|
||||||
|
|||||||
@ -0,0 +1,264 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.krtirtho.spotube.core.ui.base
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.hoverable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.interaction.collectIsHoveredAsState
|
||||||
|
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
|
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.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
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.shadow
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.IntOffset
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.max
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
private val ToggleWidth = 44.dp
|
||||||
|
private val ToggleHeight = 24.dp
|
||||||
|
private val ThumbSize = 20.dp
|
||||||
|
|
||||||
|
private data class ResolvedToggleState(
|
||||||
|
val colors: BaseUITheme.ButtonColors,
|
||||||
|
val shape: Shape,
|
||||||
|
val shadow: BaseUITheme.Shadow,
|
||||||
|
val border: BaseUITheme.Border,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun resolveToggleState(
|
||||||
|
style: BaseUITheme.ButtonStyle,
|
||||||
|
isPressed: Boolean,
|
||||||
|
isHovered: Boolean,
|
||||||
|
): ResolvedToggleState {
|
||||||
|
return when {
|
||||||
|
isPressed -> ResolvedToggleState(
|
||||||
|
style.colors.pressed,
|
||||||
|
style.shape.pressed,
|
||||||
|
style.shadow.pressed,
|
||||||
|
style.border.pressed,
|
||||||
|
)
|
||||||
|
|
||||||
|
isHovered -> ResolvedToggleState(
|
||||||
|
style.colors.hovered,
|
||||||
|
style.shape.hovered,
|
||||||
|
style.shadow.hovered,
|
||||||
|
style.border.hovered,
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> ResolvedToggleState(
|
||||||
|
style.colors.normal,
|
||||||
|
style.shape.normal,
|
||||||
|
style.shadow.normal,
|
||||||
|
style.border.normal,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Toggle(
|
||||||
|
checked: Boolean,
|
||||||
|
onCheckedChange: ((Boolean) -> Unit)?,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
interactionSource: MutableInteractionSource? = null,
|
||||||
|
theme: BaseUITheme.ToggleTheme? = null,
|
||||||
|
) {
|
||||||
|
val baseTheme = LocalBaseUITheme.current.toggle
|
||||||
|
val toggleTheme = theme ?: baseTheme
|
||||||
|
val source = interactionSource ?: remember { MutableInteractionSource() }
|
||||||
|
val isPressed by source.collectIsPressedAsState()
|
||||||
|
val isHovered by source.collectIsHoveredAsState()
|
||||||
|
|
||||||
|
val style = if (checked) toggleTheme.checked else toggleTheme.unchecked
|
||||||
|
val resolved = resolveToggleState(style, isPressed, isHovered)
|
||||||
|
|
||||||
|
val thumbColor = when {
|
||||||
|
isPressed -> toggleTheme.thumb.pressed
|
||||||
|
isHovered -> toggleTheme.thumb.hovered
|
||||||
|
else -> toggleTheme.thumb.normal
|
||||||
|
}
|
||||||
|
|
||||||
|
val thumbShadowState = when {
|
||||||
|
isPressed -> toggleTheme.thumbShadow.pressed
|
||||||
|
isHovered -> toggleTheme.thumbShadow.hovered
|
||||||
|
else -> toggleTheme.thumbShadow.normal
|
||||||
|
}
|
||||||
|
|
||||||
|
val borderColor = when {
|
||||||
|
!enabled -> resolved.colors.foreground.copy(alpha = 0.38f)
|
||||||
|
else -> resolved.border.color
|
||||||
|
}
|
||||||
|
|
||||||
|
val thumbProgress by animateFloatAsState(
|
||||||
|
targetValue = if (checked) 1f else 0f,
|
||||||
|
animationSpec = tween(durationMillis = 200),
|
||||||
|
label = "thumbProgress",
|
||||||
|
)
|
||||||
|
|
||||||
|
val thumbScale by animateFloatAsState(
|
||||||
|
targetValue = when {
|
||||||
|
isPressed -> 0.92f
|
||||||
|
isHovered -> 1.05f
|
||||||
|
else -> 1f
|
||||||
|
},
|
||||||
|
animationSpec = tween(durationMillis = 150),
|
||||||
|
label = "thumbScale",
|
||||||
|
)
|
||||||
|
|
||||||
|
val lift = if (isHovered && !isPressed && onCheckedChange != null) (-1).dp else 0.dp
|
||||||
|
val density = LocalDensity.current
|
||||||
|
val thumbOffsetXPx by animateDpAsState(
|
||||||
|
targetValue = with(density) {
|
||||||
|
val trackWidth = ToggleWidth - ThumbSize
|
||||||
|
max((thumbProgress * trackWidth.toPx()).toDp() - 4.dp, 2.dp)
|
||||||
|
},
|
||||||
|
animationSpec = tween(durationMillis = 200),
|
||||||
|
label = "thumbOffsetX",
|
||||||
|
)
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.width(ToggleWidth)
|
||||||
|
.height(ToggleHeight)
|
||||||
|
.graphicsLayer {
|
||||||
|
translationY = lift.toPx()
|
||||||
|
}
|
||||||
|
.then(
|
||||||
|
if (resolved.shadow.elevation > 0.dp) {
|
||||||
|
Modifier.shadow(
|
||||||
|
elevation = resolved.shadow.elevation,
|
||||||
|
shape = resolved.shape,
|
||||||
|
ambientColor = resolved.shadow.ambientColor,
|
||||||
|
spotColor = resolved.shadow.spotColor,
|
||||||
|
)
|
||||||
|
} else Modifier
|
||||||
|
)
|
||||||
|
.clip(resolved.shape)
|
||||||
|
.background(resolved.colors.background, resolved.shape)
|
||||||
|
.border(BorderStroke(resolved.border.width, borderColor), resolved.shape)
|
||||||
|
.highlight(resolved.colors.highlight)
|
||||||
|
.then(
|
||||||
|
if (onCheckedChange != null) {
|
||||||
|
Modifier
|
||||||
|
.hoverable(interactionSource = source, enabled = enabled)
|
||||||
|
.clickable(
|
||||||
|
enabled = enabled,
|
||||||
|
interactionSource = source,
|
||||||
|
indication = null,
|
||||||
|
onClick = { onCheckedChange(!checked) },
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
}
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.CenterStart,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.offset(x = thumbOffsetXPx)
|
||||||
|
.size(ThumbSize)
|
||||||
|
.graphicsLayer {
|
||||||
|
scaleX = thumbScale
|
||||||
|
scaleY = thumbScale
|
||||||
|
}
|
||||||
|
.shadow(
|
||||||
|
elevation = thumbShadowState.elevation,
|
||||||
|
shape = CircleShape,
|
||||||
|
ambientColor = thumbShadowState.ambientColor,
|
||||||
|
spotColor = thumbShadowState.spotColor,
|
||||||
|
)
|
||||||
|
.background(
|
||||||
|
brush = Brush.radialGradient(
|
||||||
|
colors = listOf(
|
||||||
|
Color.White.copy(alpha = 0.6f),
|
||||||
|
thumbColor.copy(alpha = 0.95f),
|
||||||
|
thumbColor,
|
||||||
|
),
|
||||||
|
center = Offset(0f, -0.55f),
|
||||||
|
radius = 1.1f,
|
||||||
|
),
|
||||||
|
shape = CircleShape,
|
||||||
|
)
|
||||||
|
.border(
|
||||||
|
width = 0.8.dp,
|
||||||
|
brush = Brush.verticalGradient(
|
||||||
|
colors = listOf(
|
||||||
|
Color.White.copy(alpha = 0.55f),
|
||||||
|
Color.White.copy(alpha = 0.08f),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
shape = CircleShape,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Composable
|
||||||
|
private fun TogglePreview() {
|
||||||
|
MaterialTheme {
|
||||||
|
val theme = rememberBaseUITheme()
|
||||||
|
CompositionLocalProvider(LocalBaseUITheme provides theme) {
|
||||||
|
Surface(
|
||||||
|
color = MaterialTheme.colorScheme.background,
|
||||||
|
modifier = Modifier.padding(24.dp),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
Toggle(checked = true, onCheckedChange = {})
|
||||||
|
Toggle(checked = false, onCheckedChange = {})
|
||||||
|
Toggle(checked = true, onCheckedChange = null)
|
||||||
|
Toggle(checked = false, onCheckedChange = null, enabled = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -34,7 +34,6 @@ import androidx.compose.material3.AlertDialog
|
|||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Switch
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@ -52,6 +51,7 @@ import coil3.compose.AsyncImage
|
|||||||
import dev.krtirtho.spotube.core.ui.base.OutlineButton
|
import dev.krtirtho.spotube.core.ui.base.OutlineButton
|
||||||
import dev.krtirtho.spotube.core.ui.base.PrimaryButton
|
import dev.krtirtho.spotube.core.ui.base.PrimaryButton
|
||||||
import dev.krtirtho.spotube.core.ui.base.TextField
|
import dev.krtirtho.spotube.core.ui.base.TextField
|
||||||
|
import dev.krtirtho.spotube.core.ui.base.Toggle
|
||||||
import io.github.vinceglb.filekit.dialogs.FileKitType
|
import io.github.vinceglb.filekit.dialogs.FileKitType
|
||||||
import io.github.vinceglb.filekit.dialogs.compose.rememberFilePickerLauncher
|
import io.github.vinceglb.filekit.dialogs.compose.rememberFilePickerLauncher
|
||||||
import io.github.vinceglb.filekit.readBytes
|
import io.github.vinceglb.filekit.readBytes
|
||||||
@ -265,7 +265,7 @@ private fun PlaylistFormFields(
|
|||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Switch(checked = isPublic, onCheckedChange = onIsPublicChange)
|
Toggle(checked = isPublic, onCheckedChange = onIsPublicChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
@ -285,7 +285,7 @@ private fun PlaylistFormFields(
|
|||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Switch(checked = isCollaborating, onCheckedChange = onIsCollaboratingChange)
|
Toggle(checked = isCollaborating, onCheckedChange = onIsCollaboratingChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
|
|||||||
@ -32,7 +32,7 @@ import androidx.compose.material3.AlertDialog
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.RadioButton
|
import androidx.compose.material3.RadioButton
|
||||||
import androidx.compose.material3.Switch
|
import dev.krtirtho.spotube.core.ui.base.Toggle
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TextField
|
import androidx.compose.material3.TextField
|
||||||
@ -64,7 +64,7 @@ internal fun SwitchSettingCard(
|
|||||||
subtitle = subtitle,
|
subtitle = subtitle,
|
||||||
icon = icon,
|
icon = icon,
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
Switch(
|
Toggle(
|
||||||
checked = checked,
|
checked = checked,
|
||||||
onCheckedChange = onCheckedChange,
|
onCheckedChange = onCheckedChange,
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user