mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-11-04 10:13:59 +00:00
feat(surface): implement Frutiger-Aero themed surfaces with glass and acrylic styles
This commit is contained in:
parent
fa9ad9981c
commit
4da5b25a5c
@ -0,0 +1,465 @@
|
|||||||
|
/*
|
||||||
|
* 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.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.LocalContentColor
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.draw.drawWithCache
|
||||||
|
import androidx.compose.ui.draw.shadow
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Path
|
||||||
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
|
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||||
|
import androidx.compose.ui.graphics.luminance
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Frutiger-Aero material families available to [BaseSurface].
|
||||||
|
*
|
||||||
|
* - [Watery]: deep aqua water with caustic light and bubbles.
|
||||||
|
* - [Icy]: pale frosted ice with crystalline facets and frost.
|
||||||
|
* - [Chrome]: brushed metal with a hard specular reflection.
|
||||||
|
* - [Acrylic]: translucent tinted panel with fine grain.
|
||||||
|
* - [Glass]: clear, glossy glass with a strong sheen.
|
||||||
|
*/
|
||||||
|
enum class SurfaceStyle {
|
||||||
|
Watery,
|
||||||
|
Icy,
|
||||||
|
Chrome,
|
||||||
|
Acrylic,
|
||||||
|
Glass,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A themed surface panel rendered in one of the Frutiger-Aero [SurfaceStyle] materials.
|
||||||
|
*
|
||||||
|
* Distinct from [androidx.compose.material3.Surface] — this draws decorative material
|
||||||
|
* backgrounds (gradients, gloss, grain, bubbles, facets) and provides a matching
|
||||||
|
* [LocalContentColor] so text/content inside stays legible.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun BaseSurface(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
style: SurfaceStyle = SurfaceStyle.Glass,
|
||||||
|
shape: Shape = RectangleShape,
|
||||||
|
elevation: Dp? = null,
|
||||||
|
contentColor: Color? = null,
|
||||||
|
contentPadding: PaddingValues = PaddingValues.Zero,
|
||||||
|
contentAlignment: Alignment = Alignment.TopStart,
|
||||||
|
content: @Composable BoxScope.() -> Unit,
|
||||||
|
) {
|
||||||
|
val isLight = MaterialTheme.colorScheme.surface.luminance() > 0.5f
|
||||||
|
val onSurface = MaterialTheme.colorScheme.onSurface
|
||||||
|
|
||||||
|
val resolvedElevation = elevation ?: when (style) {
|
||||||
|
SurfaceStyle.Watery -> 14.dp
|
||||||
|
SurfaceStyle.Icy -> 12.dp
|
||||||
|
SurfaceStyle.Chrome -> 10.dp
|
||||||
|
SurfaceStyle.Acrylic -> 16.dp
|
||||||
|
SurfaceStyle.Glass -> 12.dp
|
||||||
|
}
|
||||||
|
val resolvedContentColor = contentColor ?: when (style) {
|
||||||
|
SurfaceStyle.Watery -> Color.White
|
||||||
|
SurfaceStyle.Icy -> if (isLight) Color(0xFF0B3A55) else Color(0xFFDCEBF7)
|
||||||
|
SurfaceStyle.Chrome -> Color(0xFF1B2733)
|
||||||
|
SurfaceStyle.Acrylic, SurfaceStyle.Glass -> onSurface
|
||||||
|
}
|
||||||
|
val ambient = when (style) {
|
||||||
|
SurfaceStyle.Watery -> Color(0xFF0E6FB0).copy(alpha = 0.38f)
|
||||||
|
SurfaceStyle.Icy -> Color(0xFF3FA9E0).copy(alpha = 0.3f)
|
||||||
|
SurfaceStyle.Chrome -> Color.Black.copy(alpha = 0.3f)
|
||||||
|
SurfaceStyle.Acrylic -> onSurface.copy(alpha = 0.22f)
|
||||||
|
SurfaceStyle.Glass -> onSurface.copy(alpha = 0.18f)
|
||||||
|
}
|
||||||
|
val spot = ambient.copy(alpha = (ambient.alpha + 0.08f).coerceAtMost(1f))
|
||||||
|
|
||||||
|
val borderBrush = when (style) {
|
||||||
|
SurfaceStyle.Watery -> Brush.verticalGradient(
|
||||||
|
listOf(Color.White.copy(alpha = 0.7f), Color.White.copy(alpha = 0.12f)),
|
||||||
|
)
|
||||||
|
|
||||||
|
SurfaceStyle.Icy -> Brush.verticalGradient(
|
||||||
|
listOf(Color.White.copy(alpha = 0.95f), Color.White.copy(alpha = 0.35f)),
|
||||||
|
)
|
||||||
|
|
||||||
|
SurfaceStyle.Chrome -> Brush.verticalGradient(
|
||||||
|
listOf(Color.White.copy(alpha = 0.95f), Color.Black.copy(alpha = 0.28f)),
|
||||||
|
)
|
||||||
|
|
||||||
|
SurfaceStyle.Acrylic -> Brush.verticalGradient(
|
||||||
|
listOf(Color.White.copy(alpha = 0.5f), Color.White.copy(alpha = 0.12f)),
|
||||||
|
)
|
||||||
|
|
||||||
|
SurfaceStyle.Glass -> Brush.verticalGradient(
|
||||||
|
listOf(Color.White.copy(alpha = 0.9f), Color.White.copy(alpha = 0.3f)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translucent materials (Glass, Acrylic) must not use a drop shadow: it is drawn
|
||||||
|
// behind the node and, because the panel is see-through, shows up as a dark inset
|
||||||
|
// shape. Their depth comes from the edge, sheen and bevel instead.
|
||||||
|
val castsShadow = style != SurfaceStyle.Glass && style != SurfaceStyle.Acrylic
|
||||||
|
|
||||||
|
CompositionLocalProvider(LocalContentColor provides resolvedContentColor) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.then(
|
||||||
|
if (castsShadow) {
|
||||||
|
Modifier.shadow(
|
||||||
|
elevation = resolvedElevation,
|
||||||
|
shape = shape,
|
||||||
|
ambientColor = ambient,
|
||||||
|
spotColor = spot,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.clip(shape)
|
||||||
|
.drawWithCache {
|
||||||
|
onDrawBehind { drawSurfaceMaterial(style, isLight) }
|
||||||
|
}
|
||||||
|
.border(BorderStroke(1.dp, borderBrush), shape)
|
||||||
|
.padding(contentPadding),
|
||||||
|
contentAlignment = contentAlignment,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DrawScope.drawSurfaceMaterial(style: SurfaceStyle, isLight: Boolean) {
|
||||||
|
when (style) {
|
||||||
|
SurfaceStyle.Watery -> drawWatery(isLight)
|
||||||
|
SurfaceStyle.Icy -> drawIcy(isLight)
|
||||||
|
SurfaceStyle.Chrome -> drawChrome()
|
||||||
|
SurfaceStyle.Acrylic -> drawAcrylic(isLight)
|
||||||
|
SurfaceStyle.Glass -> drawGlass(isLight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DrawScope.drawGlass(isLight: Boolean) {
|
||||||
|
// See-through glass: a faint blue-tinted fill so whatever is behind shows through,
|
||||||
|
// defined by its bright edge, sheen and shadow rather than an opaque body.
|
||||||
|
val tint = if (isLight) Color(0xFFCFE0F2) else Color(0xFF9FB8D6)
|
||||||
|
drawRect(
|
||||||
|
brush = Brush.verticalGradient(
|
||||||
|
listOf(
|
||||||
|
tint.copy(alpha = if (isLight) 0.4f else 0.2f),
|
||||||
|
tint.copy(alpha = if (isLight) 0.24f else 0.1f),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
drawGloss(if (isLight) 0.55f else 0.16f)
|
||||||
|
drawBottomReflection(if (isLight) 0.4f else 0.1f)
|
||||||
|
drawInnerBevel(isLight)
|
||||||
|
drawBubbles(seed = 11, count = 3, alpha = if (isLight) 0.4f else 0.14f)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DrawScope.drawAcrylic(isLight: Boolean) {
|
||||||
|
// See-through acrylic: a translucent tinted sheet with a fine grain.
|
||||||
|
val tint = if (isLight) Color(0xFFCFD9E8) else Color(0xFF8FA2B8)
|
||||||
|
drawRect(
|
||||||
|
brush = Brush.verticalGradient(
|
||||||
|
listOf(
|
||||||
|
tint.copy(alpha = if (isLight) 0.45f else 0.22f),
|
||||||
|
tint.copy(alpha = if (isLight) 0.28f else 0.12f),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fine grain so the panel reads as a translucent acrylic sheet.
|
||||||
|
val random = Random(23)
|
||||||
|
repeat(180) {
|
||||||
|
val light = random.nextBoolean()
|
||||||
|
drawCircle(
|
||||||
|
color = if (light) {
|
||||||
|
Color.White.copy(alpha = if (isLight) 0.16f else 0.06f)
|
||||||
|
} else {
|
||||||
|
Color.Black.copy(alpha = if (isLight) 0.06f else 0.08f)
|
||||||
|
},
|
||||||
|
radius = 0.7f,
|
||||||
|
center = Offset(random.nextFloat() * size.width, random.nextFloat() * size.height),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
drawGloss(if (isLight) 0.4f else 0.12f, heightFraction = 0.45f)
|
||||||
|
drawInnerBevel(isLight)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DrawScope.drawChrome() {
|
||||||
|
drawRect(
|
||||||
|
brush = Brush.verticalGradient(
|
||||||
|
0f to Color(0xFFF8FBFF),
|
||||||
|
0.16f to Color(0xFFD6DEE7),
|
||||||
|
0.4f to Color(0xFF8E99A8),
|
||||||
|
0.5f to Color(0xFFEDF2F7),
|
||||||
|
0.62f to Color(0xFF9AA5B4),
|
||||||
|
1f to Color(0xFF6C7787),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
// Hard specular reflection near the top.
|
||||||
|
drawRect(
|
||||||
|
brush = Brush.verticalGradient(
|
||||||
|
colors = listOf(Color.White.copy(alpha = 0.85f), Color.Transparent),
|
||||||
|
startY = size.height * 0.05f,
|
||||||
|
endY = size.height * 0.22f,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
// Darkened lower edge for the metal bevel.
|
||||||
|
drawRect(
|
||||||
|
brush = Brush.verticalGradient(
|
||||||
|
colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.25f)),
|
||||||
|
startY = size.height * 0.78f,
|
||||||
|
endY = size.height,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DrawScope.drawIcy(isLight: Boolean) {
|
||||||
|
val colors = if (isLight) {
|
||||||
|
listOf(Color(0xFFF4FCFF), Color(0xFFCBE9FB), Color(0xFF9BD3F0))
|
||||||
|
} else {
|
||||||
|
// Snowy-night ice: dim slate-blue with faint frost.
|
||||||
|
listOf(Color(0xFF2A3B4D), Color(0xFF1B2A39), Color(0xFF0F1A26))
|
||||||
|
}
|
||||||
|
drawRect(brush = Brush.verticalGradient(colors))
|
||||||
|
|
||||||
|
val frost = if (isLight) Color.White else Color(0xFFB8CCE0)
|
||||||
|
|
||||||
|
// Crystalline facets (kept inset so they are never clipped by the shape).
|
||||||
|
val random = Random(37)
|
||||||
|
repeat(4) {
|
||||||
|
val s = size.minDimension * (0.2f + random.nextFloat() * 0.3f)
|
||||||
|
val x = random.nextFloat() * (size.width - s).coerceAtLeast(0f)
|
||||||
|
val y = random.nextFloat() * (size.height - s).coerceAtLeast(0f)
|
||||||
|
val path = Path().apply {
|
||||||
|
moveTo(x, y)
|
||||||
|
lineTo(x + s, y + s * 0.4f)
|
||||||
|
lineTo(x + s * 0.4f, y + s)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
drawPath(path, frost.copy(alpha = if (isLight) 0.25f else 0.12f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frost speckles.
|
||||||
|
repeat(90) {
|
||||||
|
drawCircle(
|
||||||
|
color = frost.copy(alpha = if (isLight) 0.5f else 0.28f),
|
||||||
|
radius = 0.8f,
|
||||||
|
center = Offset(random.nextFloat() * size.width, random.nextFloat() * size.height),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
drawGloss(if (isLight) 0.6f else 0.12f, heightFraction = 0.5f)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DrawScope.drawWatery(isLight: Boolean) {
|
||||||
|
val colors = if (isLight) {
|
||||||
|
listOf(Color(0xFFA6E3FF), Color(0xFF3FB6EA), Color(0xFF0E6FB0))
|
||||||
|
} else {
|
||||||
|
// Moonlit night water: deep blue with a pale silver sheen.
|
||||||
|
listOf(Color(0xFF1C3A5E), Color(0xFF102942), Color(0xFF071827))
|
||||||
|
}
|
||||||
|
drawRect(brush = Brush.verticalGradient(colors))
|
||||||
|
|
||||||
|
val sheen = if (isLight) Color.White else Color(0xFFB9D2F0)
|
||||||
|
|
||||||
|
// Caustic / moonlight pooling from the surface.
|
||||||
|
drawRect(
|
||||||
|
brush = Brush.verticalGradient(
|
||||||
|
colors = listOf(sheen.copy(alpha = if (isLight) 0.35f else 0.22f), Color.Transparent),
|
||||||
|
startY = 0f,
|
||||||
|
endY = size.height * 0.5f,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val random = Random(53)
|
||||||
|
repeat(5) {
|
||||||
|
val rx = size.width * (0.12f + random.nextFloat() * 0.2f)
|
||||||
|
val cx = rx + random.nextFloat() * (size.width - 2 * rx).coerceAtLeast(0f)
|
||||||
|
val cy = rx * 0.5f + random.nextFloat() * (size.height * 0.6f - rx).coerceAtLeast(0f)
|
||||||
|
drawOval(
|
||||||
|
brush = Brush.radialGradient(
|
||||||
|
colors = listOf(sheen.copy(alpha = if (isLight) 0.3f else 0.18f), Color.Transparent),
|
||||||
|
center = Offset(cx, cy),
|
||||||
|
radius = rx,
|
||||||
|
),
|
||||||
|
topLeft = Offset(cx - rx, cy - rx * 0.5f),
|
||||||
|
size = Size(rx * 2f, rx),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
drawBubbles(seed = 71, count = 6, alpha = if (isLight) 0.4f else 0.22f)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DrawScope.drawGloss(alpha: Float, heightFraction: Float = 0.5f) {
|
||||||
|
drawRect(
|
||||||
|
brush = Brush.verticalGradient(
|
||||||
|
colors = listOf(Color.White.copy(alpha = alpha), Color.Transparent),
|
||||||
|
startY = 0f,
|
||||||
|
endY = size.height * heightFraction,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DrawScope.drawBottomReflection(alpha: Float) {
|
||||||
|
drawRect(
|
||||||
|
brush = Brush.verticalGradient(
|
||||||
|
colors = listOf(Color.Transparent, Color.White.copy(alpha = alpha)),
|
||||||
|
startY = size.height * 0.7f,
|
||||||
|
endY = size.height,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A bright inner top edge plus a shaded inner bottom edge, giving the pane a glass bevel. */
|
||||||
|
private fun DrawScope.drawInnerBevel(isLight: Boolean) {
|
||||||
|
drawRect(
|
||||||
|
brush = Brush.verticalGradient(
|
||||||
|
colors = listOf(Color.White.copy(alpha = if (isLight) 0.85f else 0.22f), Color.Transparent),
|
||||||
|
startY = 0f,
|
||||||
|
endY = size.height * 0.1f,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
drawRect(
|
||||||
|
brush = Brush.verticalGradient(
|
||||||
|
colors = listOf(Color.Transparent, Color.Black.copy(alpha = if (isLight) 0.08f else 0.28f)),
|
||||||
|
startY = size.height * 0.9f,
|
||||||
|
endY = size.height,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DrawScope.drawBubbles(seed: Int, count: Int, alpha: Float) {
|
||||||
|
val random = Random(seed)
|
||||||
|
repeat(count) {
|
||||||
|
val radius = size.minDimension * (0.05f + random.nextFloat() * 0.14f)
|
||||||
|
// Keep bubbles inset so they are never clipped into hard arcs at the edges.
|
||||||
|
val cx = radius + random.nextFloat() * (size.width - 2 * radius).coerceAtLeast(0f)
|
||||||
|
val cy = radius + random.nextFloat() * (size.height - 2 * radius).coerceAtLeast(0f)
|
||||||
|
val center = Offset(cx, cy)
|
||||||
|
drawCircle(
|
||||||
|
brush = Brush.radialGradient(
|
||||||
|
colors = listOf(Color.White.copy(alpha = alpha), Color.White.copy(alpha = 0f)),
|
||||||
|
center = center,
|
||||||
|
radius = radius,
|
||||||
|
),
|
||||||
|
radius = radius,
|
||||||
|
center = center,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@BaseUIPhonePreview
|
||||||
|
@BaseUIDesktopPreview
|
||||||
|
@Composable
|
||||||
|
private fun BaseSurfacePreview() {
|
||||||
|
BaseUIPreview {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(20.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(14.dp),
|
||||||
|
) {
|
||||||
|
SurfaceStyle.entries.forEach { style ->
|
||||||
|
BaseSurface(
|
||||||
|
style = style,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(84.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = style.name,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@BaseUIPhonePreview
|
||||||
|
@Composable
|
||||||
|
private fun BaseSurfaceGridPreview() {
|
||||||
|
BaseUIPreview {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(20.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(14.dp),
|
||||||
|
) {
|
||||||
|
SurfaceStyle.entries.chunked(2).forEach { rowStyles ->
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||||
|
) {
|
||||||
|
rowStyles.forEach { style ->
|
||||||
|
BaseSurface(
|
||||||
|
style = style,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.height(140.dp),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
verticalArrangement = Arrangement.SpaceBetween,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = style.name,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Frutiger Aero",
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repeat(2 - rowStyles.size) {
|
||||||
|
androidx.compose.foundation.layout.Spacer(modifier = Modifier.weight(1f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,6 +30,7 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.graphics.Shape
|
import androidx.compose.ui.graphics.Shape
|
||||||
import androidx.compose.ui.graphics.SolidColor
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
import androidx.compose.ui.graphics.luminance
|
import androidx.compose.ui.graphics.luminance
|
||||||
|
import androidx.compose.ui.graphics.lerp
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@ -219,19 +220,51 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
val scheme = MaterialTheme.colorScheme
|
val scheme = MaterialTheme.colorScheme
|
||||||
val isLight = scheme.surface.luminance() > 0.5f
|
val isLight = scheme.surface.luminance() > 0.5f
|
||||||
|
|
||||||
val containerLighter = if (isLight) Color.White else scheme.surfaceContainerHigh
|
// ------------------------------------------------------------------
|
||||||
val containerDarker = if (isLight) Color(0xFFF2F2F4) else scheme.surfaceContainer
|
// Frutiger-Aero glass palette.
|
||||||
val containerPressed = if (isLight) Color(0xFFE0E0E3) else scheme.surfaceContainerHighest
|
//
|
||||||
val highlight = if (isLight) Color.White.copy(alpha = 0.9f) else Color.White.copy(alpha = 0.06f)
|
// Surfaces are glossy, top-lit gradients with a strong white specular
|
||||||
|
// highlight and translucent "glass" edges, lifted by soft, coloured
|
||||||
|
// shadows — the aqua/glass language of Frutiger-Aero.
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
val glassTop = if (isLight) {
|
||||||
|
Color.White
|
||||||
|
} else {
|
||||||
|
lerp(scheme.surfaceContainerHigh, Color.White, 0.10f)
|
||||||
|
}
|
||||||
|
val glassBottom = if (isLight) {
|
||||||
|
lerp(scheme.surface, Color.White, 0.04f)
|
||||||
|
} else {
|
||||||
|
scheme.surfaceContainer
|
||||||
|
}
|
||||||
|
val glassPressed = if (isLight) {
|
||||||
|
lerp(scheme.surfaceContainerHighest, Color.White, 0.06f)
|
||||||
|
} else {
|
||||||
|
lerp(scheme.surfaceContainerHighest, Color.Black, 0.08f)
|
||||||
|
}
|
||||||
|
|
||||||
|
val gloss = if (isLight) Color.White.copy(alpha = 0.75f) else Color.White.copy(alpha = 0.18f)
|
||||||
|
val glossSoft = if (isLight) Color.White.copy(alpha = 0.5f) else Color.White.copy(alpha = 0.12f)
|
||||||
|
val glassEdge = if (isLight) Color.White.copy(alpha = 0.95f) else Color.White.copy(alpha = 0.22f)
|
||||||
|
val glassBorder = if (isLight) {
|
||||||
|
lerp(scheme.outlineVariant, Color.White, 0.55f)
|
||||||
|
} else {
|
||||||
|
scheme.outlineVariant.copy(alpha = 0.5f)
|
||||||
|
}
|
||||||
val shadowColor = scheme.onSurface
|
val shadowColor = scheme.onSurface
|
||||||
val border = scheme.outlineVariant
|
|
||||||
|
|
||||||
val secondaryContainer = scheme.secondaryContainer
|
fun glassBrush(top: Color, bottom: Color): Brush =
|
||||||
val secondaryContainerPressed = secondaryContainer.copy(alpha = if (isLight) 0.85f else 0.92f)
|
Brush.verticalGradient(listOf(top, bottom))
|
||||||
val secondaryHighlight =
|
|
||||||
if (isLight) Color.White.copy(alpha = 0.5f) else Color.White.copy(alpha = 0.08f)
|
|
||||||
|
|
||||||
val defaultShape = RoundedCornerShape(14.dp)
|
fun aqua(accent: Color): Brush = Brush.verticalGradient(
|
||||||
|
listOf(
|
||||||
|
lerp(accent, Color.White, 0.45f),
|
||||||
|
accent,
|
||||||
|
lerp(accent, Color.Black, 0.14f),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val defaultShape = RoundedCornerShape(16.dp)
|
||||||
val defaultShapeState =
|
val defaultShapeState =
|
||||||
BaseUITheme.InteractionState<Shape>(defaultShape, defaultShape, defaultShape, defaultShape)
|
BaseUITheme.InteractionState<Shape>(defaultShape, defaultShape, defaultShape, defaultShape)
|
||||||
val noBorder = BaseUITheme.Border(Color.Transparent, 0.dp)
|
val noBorder = BaseUITheme.Border(Color.Transparent, 0.dp)
|
||||||
@ -239,231 +272,208 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
val noShadow = BaseUITheme.Shadow(0.dp, false, Color.Transparent, Color.Transparent)
|
val noShadow = BaseUITheme.Shadow(0.dp, false, Color.Transparent, Color.Transparent)
|
||||||
val noShadowState = BaseUITheme.InteractionState(noShadow, noShadow, noShadow, noShadow)
|
val noShadowState = BaseUITheme.InteractionState(noShadow, noShadow, noShadow, noShadow)
|
||||||
val defaultButtonPadding = BaseUITheme.InteractionState(
|
val defaultButtonPadding = BaseUITheme.InteractionState(
|
||||||
PaddingValues(horizontal = 20.dp, vertical = 10.dp),
|
PaddingValues(horizontal = 22.dp, vertical = 10.dp),
|
||||||
PaddingValues(horizontal = 20.dp, vertical = 10.dp),
|
PaddingValues(horizontal = 22.dp, vertical = 10.dp),
|
||||||
PaddingValues(horizontal = 20.dp, vertical = 10.dp),
|
PaddingValues(horizontal = 22.dp, vertical = 10.dp),
|
||||||
PaddingValues(horizontal = 20.dp, vertical = 10.dp),
|
PaddingValues(horizontal = 22.dp, vertical = 10.dp),
|
||||||
)
|
)
|
||||||
val iconButtonPadding = BaseUITheme.InteractionState(
|
val iconButtonPadding = BaseUITheme.InteractionState(
|
||||||
PaddingValues(8.dp),
|
PaddingValues(9.dp),
|
||||||
PaddingValues(8.dp),
|
PaddingValues(9.dp),
|
||||||
PaddingValues(8.dp),
|
PaddingValues(9.dp),
|
||||||
PaddingValues(8.dp),
|
PaddingValues(9.dp),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// --- Glass (outline) -------------------------------------------------
|
||||||
val outlineColors = BaseUITheme.InteractionState(
|
val outlineColors = BaseUITheme.InteractionState(
|
||||||
normal = BaseUITheme.ButtonColors(
|
normal = BaseUITheme.ButtonColors(
|
||||||
background = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
background = glassBrush(glassTop, glassBottom),
|
||||||
foreground = scheme.onSurface,
|
foreground = scheme.onSurface,
|
||||||
highlight = highlight,
|
highlight = gloss,
|
||||||
),
|
),
|
||||||
hovered = BaseUITheme.ButtonColors(
|
hovered = BaseUITheme.ButtonColors(
|
||||||
background = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
background = glassBrush(glassTop, glassBottom),
|
||||||
foreground = scheme.onSurface,
|
foreground = scheme.onSurface,
|
||||||
highlight = highlight,
|
highlight = Color.White.copy(alpha = if (isLight) 0.9f else 0.26f),
|
||||||
),
|
),
|
||||||
pressed = BaseUITheme.ButtonColors(
|
pressed = BaseUITheme.ButtonColors(
|
||||||
background = Brush.verticalGradient(listOf(containerPressed, containerPressed)),
|
background = glassBrush(glassPressed, glassPressed),
|
||||||
foreground = scheme.onSurface,
|
foreground = scheme.onSurface,
|
||||||
highlight = highlight,
|
highlight = glossSoft,
|
||||||
),
|
),
|
||||||
focused = BaseUITheme.ButtonColors(
|
focused = BaseUITheme.ButtonColors(
|
||||||
background = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
background = glassBrush(glassTop, glassBottom),
|
||||||
foreground = scheme.onSurface,
|
foreground = scheme.onSurface,
|
||||||
highlight = highlight,
|
highlight = gloss,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val outlineShadow = BaseUITheme.InteractionState(
|
val glassShadow = BaseUITheme.InteractionState(
|
||||||
normal = BaseUITheme.Shadow(
|
normal = BaseUITheme.Shadow(
|
||||||
6.dp,
|
8.dp,
|
||||||
true,
|
true,
|
||||||
shadowColor.copy(alpha = 0.15f),
|
shadowColor.copy(alpha = 0.16f),
|
||||||
shadowColor.copy(alpha = 0.18f)
|
shadowColor.copy(alpha = 0.2f),
|
||||||
),
|
),
|
||||||
hovered = BaseUITheme.Shadow(
|
hovered = BaseUITheme.Shadow(
|
||||||
9.dp,
|
14.dp,
|
||||||
true,
|
true,
|
||||||
shadowColor.copy(alpha = 0.22f),
|
shadowColor.copy(alpha = 0.22f),
|
||||||
shadowColor.copy(alpha = 0.26f)
|
shadowColor.copy(alpha = 0.26f),
|
||||||
),
|
),
|
||||||
pressed = BaseUITheme.Shadow(
|
pressed = BaseUITheme.Shadow(
|
||||||
1.dp,
|
2.dp,
|
||||||
true,
|
true,
|
||||||
shadowColor.copy(alpha = 0.08f),
|
shadowColor.copy(alpha = 0.1f),
|
||||||
shadowColor.copy(alpha = 0.1f)
|
shadowColor.copy(alpha = 0.12f),
|
||||||
),
|
),
|
||||||
focused = BaseUITheme.Shadow(
|
focused = BaseUITheme.Shadow(
|
||||||
6.dp,
|
8.dp,
|
||||||
true,
|
true,
|
||||||
shadowColor.copy(alpha = 0.15f),
|
shadowColor.copy(alpha = 0.16f),
|
||||||
shadowColor.copy(alpha = 0.18f)
|
shadowColor.copy(alpha = 0.2f),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val outlineBorder = BaseUITheme.InteractionState(
|
val glassBorderState = BaseUITheme.InteractionState(
|
||||||
normal = BaseUITheme.Border(border, 0.5.dp),
|
normal = BaseUITheme.Border(glassBorder, 1.dp),
|
||||||
hovered = BaseUITheme.Border(border, 0.5.dp),
|
hovered = BaseUITheme.Border(glassEdge, 1.dp),
|
||||||
pressed = BaseUITheme.Border(border.copy(alpha = 0.7f), 0.5.dp),
|
pressed = BaseUITheme.Border(glassBorder.copy(alpha = 0.7f), 1.dp),
|
||||||
focused = BaseUITheme.Border(border, 0.5.dp),
|
focused = BaseUITheme.Border(glassEdge, 1.dp),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// --- Aqua accent (primary) -------------------------------------------
|
||||||
|
val primaryEdge = lerp(scheme.primary, Color.White, 0.5f)
|
||||||
val primaryColors = BaseUITheme.InteractionState(
|
val primaryColors = BaseUITheme.InteractionState(
|
||||||
normal = BaseUITheme.ButtonColors(
|
normal = BaseUITheme.ButtonColors(aqua(scheme.primary), scheme.onPrimary, gloss),
|
||||||
background = Brush.verticalGradient(listOf(scheme.primary, scheme.primary)),
|
|
||||||
foreground = scheme.onPrimary,
|
|
||||||
highlight = Color.White.copy(alpha = 0.25f),
|
|
||||||
),
|
|
||||||
hovered = BaseUITheme.ButtonColors(
|
hovered = BaseUITheme.ButtonColors(
|
||||||
background = Brush.verticalGradient(listOf(scheme.primary, scheme.primary)),
|
aqua(scheme.primary),
|
||||||
foreground = scheme.onPrimary,
|
scheme.onPrimary,
|
||||||
highlight = Color.White.copy(alpha = 0.25f),
|
Color.White.copy(alpha = if (isLight) 0.9f else 0.3f),
|
||||||
),
|
),
|
||||||
pressed = BaseUITheme.ButtonColors(
|
pressed = BaseUITheme.ButtonColors(
|
||||||
background = Brush.verticalGradient(
|
glassBrush(
|
||||||
listOf(
|
lerp(scheme.primary, Color.Black, 0.16f),
|
||||||
scheme.primary.copy(alpha = 0.85f),
|
lerp(scheme.primary, Color.Black, 0.24f),
|
||||||
scheme.primary
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
foreground = scheme.onPrimary,
|
scheme.onPrimary,
|
||||||
highlight = Color.White.copy(alpha = 0.25f),
|
glossSoft,
|
||||||
),
|
|
||||||
focused = BaseUITheme.ButtonColors(
|
|
||||||
background = Brush.verticalGradient(listOf(scheme.primary, scheme.primary)),
|
|
||||||
foreground = scheme.onPrimary,
|
|
||||||
highlight = Color.White.copy(alpha = 0.25f),
|
|
||||||
),
|
),
|
||||||
|
focused = BaseUITheme.ButtonColors(aqua(scheme.primary), scheme.onPrimary, gloss),
|
||||||
)
|
)
|
||||||
val primaryShadow = BaseUITheme.InteractionState(
|
val primaryShadow = BaseUITheme.InteractionState(
|
||||||
normal = BaseUITheme.Shadow(
|
normal = BaseUITheme.Shadow(
|
||||||
6.dp,
|
10.dp,
|
||||||
true,
|
true,
|
||||||
scheme.primary.copy(alpha = 0.35f),
|
scheme.primary.copy(alpha = 0.35f),
|
||||||
scheme.primary.copy(alpha = 0.4f)
|
scheme.primary.copy(alpha = 0.42f),
|
||||||
),
|
),
|
||||||
hovered = BaseUITheme.Shadow(
|
hovered = BaseUITheme.Shadow(
|
||||||
9.dp,
|
16.dp,
|
||||||
true,
|
true,
|
||||||
scheme.primary.copy(alpha = 0.45f),
|
scheme.primary.copy(alpha = 0.45f),
|
||||||
scheme.primary.copy(alpha = 0.5f)
|
scheme.primary.copy(alpha = 0.5f),
|
||||||
),
|
),
|
||||||
pressed = BaseUITheme.Shadow(
|
pressed = BaseUITheme.Shadow(
|
||||||
1.dp,
|
3.dp,
|
||||||
true,
|
true,
|
||||||
scheme.primary.copy(alpha = 0.2f),
|
scheme.primary.copy(alpha = 0.22f),
|
||||||
scheme.primary.copy(alpha = 0.25f)
|
scheme.primary.copy(alpha = 0.28f),
|
||||||
),
|
),
|
||||||
focused = BaseUITheme.Shadow(
|
focused = BaseUITheme.Shadow(
|
||||||
6.dp,
|
10.dp,
|
||||||
true,
|
true,
|
||||||
scheme.primary.copy(alpha = 0.35f),
|
scheme.primary.copy(alpha = 0.35f),
|
||||||
scheme.primary.copy(alpha = 0.4f)
|
scheme.primary.copy(alpha = 0.42f),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val primaryBorder = BaseUITheme.InteractionState(
|
val primaryBorder = BaseUITheme.InteractionState(
|
||||||
normal = BaseUITheme.Border(scheme.primary, 0.5.dp),
|
normal = BaseUITheme.Border(primaryEdge, 1.dp),
|
||||||
hovered = BaseUITheme.Border(scheme.primary, 0.5.dp),
|
hovered = BaseUITheme.Border(primaryEdge, 1.dp),
|
||||||
pressed = BaseUITheme.Border(scheme.primary, 0.5.dp),
|
pressed = BaseUITheme.Border(primaryEdge.copy(alpha = 0.8f), 1.dp),
|
||||||
focused = BaseUITheme.Border(scheme.primary, 0.5.dp),
|
focused = BaseUITheme.Border(primaryEdge, 1.dp),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// --- Secondary (soft aqua glass) -------------------------------------
|
||||||
|
val secondaryBase = scheme.secondaryContainer
|
||||||
val secondaryColors = BaseUITheme.InteractionState(
|
val secondaryColors = BaseUITheme.InteractionState(
|
||||||
normal = BaseUITheme.ButtonColors(
|
normal = BaseUITheme.ButtonColors(
|
||||||
background = Brush.verticalGradient(
|
glassBrush(lerp(secondaryBase, Color.White, 0.5f), secondaryBase),
|
||||||
listOf(
|
scheme.onSecondaryContainer,
|
||||||
secondaryContainer,
|
gloss,
|
||||||
secondaryContainer.copy(alpha = if (secondaryContainer.luminance() > 0.5f) 0.92f else 1f)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
foreground = scheme.onSecondaryContainer,
|
|
||||||
highlight = secondaryHighlight,
|
|
||||||
),
|
),
|
||||||
hovered = BaseUITheme.ButtonColors(
|
hovered = BaseUITheme.ButtonColors(
|
||||||
background = Brush.verticalGradient(
|
glassBrush(lerp(secondaryBase, Color.White, 0.55f), secondaryBase),
|
||||||
listOf(
|
scheme.onSecondaryContainer,
|
||||||
secondaryContainer,
|
Color.White.copy(alpha = if (isLight) 0.85f else 0.24f),
|
||||||
secondaryContainer.copy(alpha = if (secondaryContainer.luminance() > 0.5f) 0.92f else 1f)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
foreground = scheme.onSecondaryContainer,
|
|
||||||
highlight = secondaryHighlight,
|
|
||||||
),
|
),
|
||||||
pressed = BaseUITheme.ButtonColors(
|
pressed = BaseUITheme.ButtonColors(
|
||||||
background = Brush.verticalGradient(
|
glassBrush(
|
||||||
listOf(
|
lerp(secondaryBase, Color.Black, 0.08f),
|
||||||
secondaryContainerPressed,
|
lerp(secondaryBase, Color.Black, 0.14f),
|
||||||
secondaryContainerPressed
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
foreground = scheme.onSecondaryContainer,
|
scheme.onSecondaryContainer,
|
||||||
highlight = secondaryHighlight,
|
glossSoft,
|
||||||
),
|
),
|
||||||
focused = BaseUITheme.ButtonColors(
|
focused = BaseUITheme.ButtonColors(
|
||||||
background = Brush.verticalGradient(
|
glassBrush(lerp(secondaryBase, Color.White, 0.5f), secondaryBase),
|
||||||
listOf(
|
scheme.onSecondaryContainer,
|
||||||
secondaryContainer,
|
gloss,
|
||||||
secondaryContainer.copy(alpha = if (secondaryContainer.luminance() > 0.5f) 0.92f else 1f)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
foreground = scheme.onSecondaryContainer,
|
|
||||||
highlight = secondaryHighlight,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val secondaryShadow = BaseUITheme.InteractionState(
|
val secondaryShadow = BaseUITheme.InteractionState(
|
||||||
normal = BaseUITheme.Shadow(
|
normal = BaseUITheme.Shadow(
|
||||||
6.dp,
|
8.dp,
|
||||||
true,
|
true,
|
||||||
shadowColor.copy(alpha = 0.15f),
|
secondaryBase.copy(alpha = 0.4f),
|
||||||
shadowColor.copy(alpha = 0.18f)
|
secondaryBase.copy(alpha = 0.5f),
|
||||||
),
|
),
|
||||||
hovered = BaseUITheme.Shadow(
|
hovered = BaseUITheme.Shadow(
|
||||||
9.dp,
|
14.dp,
|
||||||
true,
|
true,
|
||||||
shadowColor.copy(alpha = 0.22f),
|
secondaryBase.copy(alpha = 0.5f),
|
||||||
shadowColor.copy(alpha = 0.26f)
|
secondaryBase.copy(alpha = 0.6f),
|
||||||
),
|
),
|
||||||
pressed = BaseUITheme.Shadow(
|
pressed = BaseUITheme.Shadow(
|
||||||
1.dp,
|
2.dp,
|
||||||
true,
|
true,
|
||||||
shadowColor.copy(alpha = 0.08f),
|
secondaryBase.copy(alpha = 0.3f),
|
||||||
shadowColor.copy(alpha = 0.1f)
|
secondaryBase.copy(alpha = 0.35f),
|
||||||
),
|
),
|
||||||
focused = BaseUITheme.Shadow(
|
focused = BaseUITheme.Shadow(
|
||||||
6.dp,
|
8.dp,
|
||||||
true,
|
true,
|
||||||
shadowColor.copy(alpha = 0.15f),
|
secondaryBase.copy(alpha = 0.4f),
|
||||||
shadowColor.copy(alpha = 0.18f)
|
secondaryBase.copy(alpha = 0.5f),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val secondaryBorder = BaseUITheme.InteractionState(
|
val secondaryBorder = BaseUITheme.InteractionState(
|
||||||
normal = BaseUITheme.Border(secondaryContainer.copy(alpha = 0.5f), 0.5.dp),
|
normal = BaseUITheme.Border(lerp(secondaryBase, Color.White, 0.5f), 1.dp),
|
||||||
hovered = BaseUITheme.Border(secondaryContainer.copy(alpha = 0.5f), 0.5.dp),
|
hovered = BaseUITheme.Border(lerp(secondaryBase, Color.White, 0.6f), 1.dp),
|
||||||
pressed = BaseUITheme.Border(secondaryContainer.copy(alpha = 0.5f), 0.5.dp),
|
pressed = BaseUITheme.Border(lerp(secondaryBase, Color.White, 0.4f), 1.dp),
|
||||||
focused = BaseUITheme.Border(secondaryContainer.copy(alpha = 0.5f), 0.5.dp),
|
focused = BaseUITheme.Border(lerp(secondaryBase, Color.White, 0.5f), 1.dp),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// --- Ghost -----------------------------------------------------------
|
||||||
val ghostColors = BaseUITheme.InteractionState(
|
val ghostColors = BaseUITheme.InteractionState(
|
||||||
normal = BaseUITheme.ButtonColors(
|
normal = BaseUITheme.ButtonColors(
|
||||||
background = Brush.verticalGradient(listOf(Color.Transparent, Color.Transparent)),
|
background = glassBrush(Color.Transparent, Color.Transparent),
|
||||||
foreground = scheme.onSurface,
|
foreground = scheme.onSurface,
|
||||||
highlight = Color.Transparent,
|
highlight = Color.Transparent,
|
||||||
),
|
),
|
||||||
hovered = BaseUITheme.ButtonColors(
|
hovered = BaseUITheme.ButtonColors(
|
||||||
background = Brush.verticalGradient(
|
background = glassBrush(
|
||||||
listOf(
|
lerp(scheme.surfaceContainerHigh, Color.White, 0.1f),
|
||||||
containerPressed.copy(alpha = 0.5f),
|
scheme.surfaceContainer,
|
||||||
containerPressed.copy(alpha = 0.5f)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
foreground = scheme.onSurface,
|
foreground = scheme.onSurface,
|
||||||
highlight = Color.Transparent,
|
highlight = glossSoft,
|
||||||
),
|
),
|
||||||
pressed = BaseUITheme.ButtonColors(
|
pressed = BaseUITheme.ButtonColors(
|
||||||
background = Brush.verticalGradient(listOf(containerPressed, containerPressed)),
|
background = glassBrush(glassPressed, glassPressed),
|
||||||
foreground = scheme.onSurface,
|
foreground = scheme.onSurface,
|
||||||
highlight = Color.Transparent,
|
highlight = Color.Transparent,
|
||||||
),
|
),
|
||||||
focused = BaseUITheme.ButtonColors(
|
focused = BaseUITheme.ButtonColors(
|
||||||
background = Brush.verticalGradient(listOf(Color.Transparent, Color.Transparent)),
|
background = glassBrush(Color.Transparent, Color.Transparent),
|
||||||
foreground = scheme.onSurface,
|
foreground = scheme.onSurface,
|
||||||
highlight = Color.Transparent,
|
highlight = Color.Transparent,
|
||||||
),
|
),
|
||||||
@ -472,8 +482,8 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
val outlineStyle = BaseUITheme.ButtonStyle(
|
val outlineStyle = BaseUITheme.ButtonStyle(
|
||||||
outlineColors,
|
outlineColors,
|
||||||
defaultShapeState,
|
defaultShapeState,
|
||||||
outlineShadow,
|
glassShadow,
|
||||||
outlineBorder,
|
glassBorderState,
|
||||||
defaultButtonPadding
|
defaultButtonPadding
|
||||||
)
|
)
|
||||||
val primaryStyle = BaseUITheme.ButtonStyle(
|
val primaryStyle = BaseUITheme.ButtonStyle(
|
||||||
@ -501,8 +511,8 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
val outlineIconStyle = BaseUITheme.ButtonStyle(
|
val outlineIconStyle = BaseUITheme.ButtonStyle(
|
||||||
outlineColors,
|
outlineColors,
|
||||||
defaultShapeState,
|
defaultShapeState,
|
||||||
outlineShadow,
|
glassShadow,
|
||||||
outlineBorder,
|
glassBorderState,
|
||||||
iconButtonPadding
|
iconButtonPadding
|
||||||
)
|
)
|
||||||
val primaryIconStyle = BaseUITheme.ButtonStyle(
|
val primaryIconStyle = BaseUITheme.ButtonStyle(
|
||||||
@ -534,20 +544,20 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
focused = scheme.onSurface,
|
focused = scheme.onSurface,
|
||||||
)
|
)
|
||||||
|
|
||||||
val checkBoxShape = RoundedCornerShape(6.dp)
|
val checkBoxShape = RoundedCornerShape(7.dp)
|
||||||
val checkBoxShapeState =
|
val checkBoxShapeState =
|
||||||
BaseUITheme.InteractionState<Shape>(checkBoxShape, checkBoxShape, checkBoxShape, checkBoxShape)
|
BaseUITheme.InteractionState<Shape>(checkBoxShape, checkBoxShape, checkBoxShape, checkBoxShape)
|
||||||
val noPadding =
|
val noPadding =
|
||||||
BaseUITheme.InteractionState(PaddingValues(0.dp), PaddingValues(0.dp), PaddingValues(0.dp), PaddingValues(0.dp))
|
BaseUITheme.InteractionState(PaddingValues(0.dp), PaddingValues(0.dp), PaddingValues(0.dp), PaddingValues(0.dp))
|
||||||
|
|
||||||
val chipTabShape = RoundedCornerShape(10.dp)
|
val chipTabShape = RoundedCornerShape(999.dp)
|
||||||
val chipTabShapeState =
|
val chipTabShapeState =
|
||||||
BaseUITheme.InteractionState<Shape>(chipTabShape, chipTabShape, chipTabShape, chipTabShape)
|
BaseUITheme.InteractionState<Shape>(chipTabShape, chipTabShape, chipTabShape, chipTabShape)
|
||||||
val chipTabPadding = BaseUITheme.InteractionState(
|
val chipTabPadding = BaseUITheme.InteractionState(
|
||||||
PaddingValues(horizontal = 14.dp, vertical = 4.dp),
|
PaddingValues(horizontal = 16.dp, vertical = 6.dp),
|
||||||
PaddingValues(horizontal = 14.dp, vertical = 4.dp),
|
PaddingValues(horizontal = 16.dp, vertical = 6.dp),
|
||||||
PaddingValues(horizontal = 14.dp, vertical = 4.dp),
|
PaddingValues(horizontal = 16.dp, vertical = 6.dp),
|
||||||
PaddingValues(horizontal = 14.dp, vertical = 4.dp),
|
PaddingValues(horizontal = 16.dp, vertical = 6.dp),
|
||||||
)
|
)
|
||||||
|
|
||||||
val toggleShape = RoundedCornerShape(50)
|
val toggleShape = RoundedCornerShape(50)
|
||||||
@ -575,16 +585,16 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
),
|
),
|
||||||
textField = TextFieldTheme(
|
textField = TextFieldTheme(
|
||||||
background = BaseUITheme.InteractionState(
|
background = BaseUITheme.InteractionState(
|
||||||
normal = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
normal = glassBrush(glassTop, glassBottom),
|
||||||
hovered = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
hovered = glassBrush(glassTop, glassBottom),
|
||||||
pressed = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
pressed = glassBrush(glassPressed, glassPressed),
|
||||||
focused = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
focused = glassBrush(glassTop, glassBottom),
|
||||||
),
|
),
|
||||||
highlight = BaseUITheme.InteractionState(
|
highlight = BaseUITheme.InteractionState(
|
||||||
normal = highlight,
|
normal = gloss,
|
||||||
hovered = highlight,
|
hovered = gloss,
|
||||||
pressed = highlight,
|
pressed = glossSoft,
|
||||||
focused = highlight,
|
focused = Color.White.copy(alpha = if (isLight) 0.85f else 0.22f),
|
||||||
),
|
),
|
||||||
shape = BaseUITheme.InteractionState(
|
shape = BaseUITheme.InteractionState(
|
||||||
normal = defaultShape,
|
normal = defaultShape,
|
||||||
@ -593,53 +603,48 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
focused = defaultShape,
|
focused = defaultShape,
|
||||||
),
|
),
|
||||||
border = BaseUITheme.InteractionState(
|
border = BaseUITheme.InteractionState(
|
||||||
normal = BaseUITheme.Border(border.copy(alpha = 0.85f), 0.5.dp),
|
normal = BaseUITheme.Border(glassBorder, 1.dp),
|
||||||
hovered = BaseUITheme.Border(border.copy(alpha = 0.85f), 0.5.dp),
|
hovered = BaseUITheme.Border(glassEdge, 1.dp),
|
||||||
pressed = BaseUITheme.Border(border, 0.5.dp),
|
pressed = BaseUITheme.Border(glassBorder.copy(alpha = 0.7f), 1.dp),
|
||||||
focused = BaseUITheme.Border(scheme.primary, 0.5.dp),
|
focused = BaseUITheme.Border(primaryEdge, 1.5.dp),
|
||||||
),
|
),
|
||||||
shadow = BaseUITheme.InteractionState(
|
shadow = BaseUITheme.InteractionState(
|
||||||
normal = BaseUITheme.Shadow(
|
normal = BaseUITheme.Shadow(
|
||||||
9.dp,
|
8.dp,
|
||||||
true,
|
true,
|
||||||
shadowColor.copy(alpha = 0.22f),
|
shadowColor.copy(alpha = 0.14f),
|
||||||
shadowColor.copy(alpha = 0.26f)
|
|
||||||
),
|
|
||||||
hovered = BaseUITheme.Shadow(
|
|
||||||
9.dp,
|
|
||||||
true,
|
|
||||||
shadowColor.copy(alpha = 0.22f),
|
|
||||||
shadowColor.copy(alpha = 0.26f)
|
|
||||||
),
|
|
||||||
pressed = BaseUITheme.Shadow(
|
|
||||||
6.dp,
|
|
||||||
true,
|
|
||||||
shadowColor.copy(alpha = 0.15f),
|
|
||||||
shadowColor.copy(alpha = 0.18f)
|
shadowColor.copy(alpha = 0.18f)
|
||||||
),
|
),
|
||||||
focused = BaseUITheme.Shadow(
|
hovered = BaseUITheme.Shadow(
|
||||||
9.dp,
|
10.dp,
|
||||||
true,
|
true,
|
||||||
scheme.primary.copy(alpha = 0.2f),
|
shadowColor.copy(alpha = 0.18f),
|
||||||
scheme.primary.copy(alpha = 0.25f)
|
shadowColor.copy(alpha = 0.22f)
|
||||||
|
),
|
||||||
|
pressed = BaseUITheme.Shadow(
|
||||||
|
4.dp,
|
||||||
|
true,
|
||||||
|
shadowColor.copy(alpha = 0.1f),
|
||||||
|
shadowColor.copy(alpha = 0.12f)
|
||||||
|
),
|
||||||
|
focused = BaseUITheme.Shadow(
|
||||||
|
10.dp,
|
||||||
|
true,
|
||||||
|
scheme.primary.copy(alpha = 0.22f),
|
||||||
|
scheme.primary.copy(alpha = 0.28f)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
padding = PaddingValues(horizontal = 14.dp, vertical = 10.dp),
|
padding = PaddingValues(horizontal = 16.dp, vertical = 12.dp),
|
||||||
textStyle = TextStyle.Default,
|
textStyle = TextStyle.Default,
|
||||||
cursor = SolidColor(scheme.primary),
|
cursor = SolidColor(scheme.primary),
|
||||||
foreground = textFieldForeground,
|
foreground = textFieldForeground,
|
||||||
),
|
),
|
||||||
autoCompleteMenuTheme = AutoCompleteMenuTheme(
|
autoCompleteMenuTheme = AutoCompleteMenuTheme(
|
||||||
background = Brush.verticalGradient(
|
background = glassBrush(glassTop, glassBottom),
|
||||||
listOf(
|
shape = RoundedCornerShape(16.dp),
|
||||||
scheme.surfaceContainerHigh,
|
border = BaseUITheme.Border(glassBorder, 1.dp),
|
||||||
scheme.surfaceContainer
|
|
||||||
)
|
|
||||||
),
|
|
||||||
shape = RoundedCornerShape(12.dp),
|
|
||||||
border = BaseUITheme.Border(border, 0.5.dp),
|
|
||||||
padding = PaddingValues(0.dp),
|
padding = PaddingValues(0.dp),
|
||||||
shadowElevation = 8.dp,
|
shadowElevation = 16.dp,
|
||||||
),
|
),
|
||||||
slider = SliderTheme(
|
slider = SliderTheme(
|
||||||
trackActiveColor = BaseUITheme.InteractionState(
|
trackActiveColor = BaseUITheme.InteractionState(
|
||||||
@ -649,10 +654,10 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
focused = scheme.primary,
|
focused = scheme.primary,
|
||||||
),
|
),
|
||||||
trackInactiveColor = BaseUITheme.InteractionState(
|
trackInactiveColor = BaseUITheme.InteractionState(
|
||||||
normal = border.copy(alpha = 0.6f),
|
normal = glassBorder,
|
||||||
hovered = border.copy(alpha = 0.6f),
|
hovered = glassBorder,
|
||||||
pressed = border.copy(alpha = 0.6f),
|
pressed = glassBorder,
|
||||||
focused = border.copy(alpha = 0.6f),
|
focused = glassBorder,
|
||||||
),
|
),
|
||||||
thumbColor = BaseUITheme.InteractionState(
|
thumbColor = BaseUITheme.InteractionState(
|
||||||
normal = scheme.primary,
|
normal = scheme.primary,
|
||||||
@ -664,94 +669,44 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
normal = BaseUITheme.Shadow(
|
normal = BaseUITheme.Shadow(
|
||||||
5.dp,
|
5.dp,
|
||||||
true,
|
true,
|
||||||
scheme.primary.copy(alpha = 0.35f),
|
scheme.primary.copy(alpha = 0.4f),
|
||||||
scheme.primary.copy(alpha = 0.4f)
|
scheme.primary.copy(alpha = 0.45f)
|
||||||
),
|
),
|
||||||
hovered = BaseUITheme.Shadow(
|
hovered = BaseUITheme.Shadow(
|
||||||
8.dp,
|
8.dp,
|
||||||
true,
|
true,
|
||||||
scheme.primary.copy(alpha = 0.4f),
|
scheme.primary.copy(alpha = 0.45f),
|
||||||
scheme.primary.copy(alpha = 0.5f)
|
scheme.primary.copy(alpha = 0.5f)
|
||||||
),
|
),
|
||||||
pressed = BaseUITheme.Shadow(
|
pressed = BaseUITheme.Shadow(
|
||||||
2.dp,
|
2.dp,
|
||||||
true,
|
true,
|
||||||
scheme.primary.copy(alpha = 0.2f),
|
scheme.primary.copy(alpha = 0.25f),
|
||||||
scheme.primary.copy(alpha = 0.25f)
|
scheme.primary.copy(alpha = 0.3f)
|
||||||
),
|
),
|
||||||
focused = BaseUITheme.Shadow(
|
focused = BaseUITheme.Shadow(
|
||||||
5.dp,
|
5.dp,
|
||||||
true,
|
true,
|
||||||
scheme.primary.copy(alpha = 0.35f),
|
scheme.primary.copy(alpha = 0.4f),
|
||||||
scheme.primary.copy(alpha = 0.4f)
|
scheme.primary.copy(alpha = 0.45f)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
stepActiveColor = scheme.onPrimary.copy(alpha = 0.5f),
|
stepActiveColor = Color.White.copy(alpha = 0.7f),
|
||||||
stepInactiveColor = scheme.onSurface.copy(alpha = 0.25f),
|
stepInactiveColor = scheme.onSurface.copy(alpha = 0.25f),
|
||||||
),
|
),
|
||||||
checkBox = CheckBoxTheme(
|
checkBox = CheckBoxTheme(
|
||||||
selected = BaseUITheme.ButtonStyle(
|
selected = BaseUITheme.ButtonStyle(
|
||||||
colors = primaryColors,
|
colors = primaryColors,
|
||||||
shape = checkBoxShapeState,
|
shape = checkBoxShapeState,
|
||||||
shadow = BaseUITheme.InteractionState(
|
shadow = primaryShadow,
|
||||||
normal = BaseUITheme.Shadow(
|
|
||||||
6.dp,
|
|
||||||
true,
|
|
||||||
scheme.primary.copy(alpha = 0.3f),
|
|
||||||
scheme.primary.copy(alpha = 0.35f)
|
|
||||||
),
|
|
||||||
hovered = BaseUITheme.Shadow(
|
|
||||||
8.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(
|
|
||||||
6.dp,
|
|
||||||
true,
|
|
||||||
scheme.primary.copy(alpha = 0.3f),
|
|
||||||
scheme.primary.copy(alpha = 0.35f)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
border = primaryBorder,
|
border = primaryBorder,
|
||||||
padding = noPadding,
|
padding = noPadding,
|
||||||
),
|
),
|
||||||
unselected = BaseUITheme.ButtonStyle(
|
unselected = BaseUITheme.ButtonStyle(
|
||||||
colors = outlineColors,
|
colors = outlineColors,
|
||||||
shape = checkBoxShapeState,
|
shape = checkBoxShapeState,
|
||||||
shadow = BaseUITheme.InteractionState(
|
shadow = glassShadow,
|
||||||
normal = BaseUITheme.Shadow(
|
border = glassBorderState,
|
||||||
3.dp,
|
|
||||||
true,
|
|
||||||
shadowColor.copy(alpha = 0.15f),
|
|
||||||
shadowColor.copy(alpha = 0.18f)
|
|
||||||
),
|
|
||||||
hovered = BaseUITheme.Shadow(
|
|
||||||
5.dp,
|
|
||||||
true,
|
|
||||||
shadowColor.copy(alpha = 0.2f),
|
|
||||||
shadowColor.copy(alpha = 0.24f)
|
|
||||||
),
|
|
||||||
pressed = BaseUITheme.Shadow(
|
|
||||||
1.dp,
|
|
||||||
true,
|
|
||||||
shadowColor.copy(alpha = 0.08f),
|
|
||||||
shadowColor.copy(alpha = 0.1f)
|
|
||||||
),
|
|
||||||
focused = BaseUITheme.Shadow(
|
|
||||||
3.dp,
|
|
||||||
true,
|
|
||||||
shadowColor.copy(alpha = 0.15f),
|
|
||||||
shadowColor.copy(alpha = 0.18f)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
border = outlineBorder,
|
|
||||||
padding = noPadding,
|
padding = noPadding,
|
||||||
),
|
),
|
||||||
checkmarkColor = scheme.onPrimary,
|
checkmarkColor = scheme.onPrimary,
|
||||||
@ -767,131 +722,24 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
unselected = BaseUITheme.ButtonStyle(
|
unselected = BaseUITheme.ButtonStyle(
|
||||||
colors = outlineColors,
|
colors = outlineColors,
|
||||||
shape = chipTabShapeState,
|
shape = chipTabShapeState,
|
||||||
shadow = outlineShadow,
|
shadow = glassShadow,
|
||||||
border = outlineBorder,
|
border = glassBorderState,
|
||||||
padding = chipTabPadding,
|
padding = chipTabPadding,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
toggle = BaseUITheme.ToggleTheme(
|
toggle = BaseUITheme.ToggleTheme(
|
||||||
checked = BaseUITheme.ButtonStyle(
|
checked = BaseUITheme.ButtonStyle(
|
||||||
colors = BaseUITheme.InteractionState(
|
colors = primaryColors,
|
||||||
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,
|
shape = toggleShapeState,
|
||||||
shadow = BaseUITheme.InteractionState(
|
shadow = primaryShadow,
|
||||||
normal = BaseUITheme.Shadow(
|
border = primaryBorder,
|
||||||
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,
|
padding = togglePadding,
|
||||||
),
|
),
|
||||||
unchecked = BaseUITheme.ButtonStyle(
|
unchecked = BaseUITheme.ButtonStyle(
|
||||||
colors = BaseUITheme.InteractionState(
|
colors = outlineColors,
|
||||||
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,
|
shape = toggleShapeState,
|
||||||
shadow = BaseUITheme.InteractionState(
|
shadow = glassShadow,
|
||||||
normal = BaseUITheme.Shadow(
|
border = glassBorderState,
|
||||||
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,
|
padding = togglePadding,
|
||||||
),
|
),
|
||||||
thumb = BaseUITheme.InteractionState(
|
thumb = BaseUITheme.InteractionState(
|
||||||
@ -928,61 +776,62 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
card = BaseUITheme.CardTheme(
|
card = BaseUITheme.CardTheme(
|
||||||
background = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
background = glassBrush(glassTop, glassBottom),
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = RoundedCornerShape(20.dp),
|
||||||
border = BaseUITheme.Border(border, 0.5.dp),
|
border = BaseUITheme.Border(glassBorder, 1.dp),
|
||||||
padding = PaddingValues(0.dp),
|
padding = PaddingValues(0.dp),
|
||||||
shadow = BaseUITheme.Shadow(
|
shadow = BaseUITheme.Shadow(
|
||||||
6.dp,
|
12.dp,
|
||||||
true,
|
true,
|
||||||
shadowColor.copy(alpha = 0.15f),
|
shadowColor.copy(alpha = 0.16f),
|
||||||
shadowColor.copy(alpha = 0.18f)
|
shadowColor.copy(alpha = 0.2f)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
listRowTile = BaseUITheme.ListRowTheme(
|
listRowTile = BaseUITheme.ListRowTheme(
|
||||||
background = BaseUITheme.AdvancedInteractionState(
|
background = BaseUITheme.AdvancedInteractionState(
|
||||||
normal = Brush.verticalGradient(listOf(Color.Transparent, Color.Transparent)),
|
normal = glassBrush(Color.Transparent, Color.Transparent),
|
||||||
hovered = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
hovered = glassBrush(glassTop, glassBottom),
|
||||||
pressed = Brush.verticalGradient(listOf(containerPressed, containerPressed)),
|
pressed = glassBrush(glassPressed, glassPressed),
|
||||||
focused = Brush.verticalGradient(listOf(containerLighter, containerDarker)),
|
focused = glassBrush(glassTop, glassBottom),
|
||||||
selected = Brush.verticalGradient(
|
selected = glassBrush(
|
||||||
listOf(
|
lerp(scheme.primaryContainer, Color.White, 0.4f),
|
||||||
scheme.primaryContainer.copy(alpha = 0.3f),
|
scheme.primaryContainer,
|
||||||
scheme.primaryContainer.copy(alpha = 0.3f)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
disabled = Brush.verticalGradient(
|
disabled = glassBrush(
|
||||||
listOf(
|
glassTop.copy(alpha = 0.5f),
|
||||||
containerLighter.copy(alpha = 0.5f),
|
glassBottom.copy(alpha = 0.5f),
|
||||||
containerDarker.copy(alpha = 0.5f)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
shape = BaseUITheme.AdvancedInteractionState(
|
shape = BaseUITheme.AdvancedInteractionState(
|
||||||
normal = RoundedCornerShape(8.dp),
|
normal = RoundedCornerShape(10.dp),
|
||||||
hovered = RoundedCornerShape(8.dp),
|
hovered = RoundedCornerShape(10.dp),
|
||||||
pressed = RoundedCornerShape(8.dp),
|
pressed = RoundedCornerShape(10.dp),
|
||||||
focused = RoundedCornerShape(8.dp),
|
focused = RoundedCornerShape(10.dp),
|
||||||
selected = RoundedCornerShape(8.dp),
|
selected = RoundedCornerShape(10.dp),
|
||||||
disabled = RoundedCornerShape(8.dp),
|
disabled = RoundedCornerShape(10.dp),
|
||||||
),
|
),
|
||||||
border = BaseUITheme.AdvancedInteractionState(
|
border = BaseUITheme.AdvancedInteractionState(
|
||||||
normal = BaseUITheme.Border(Color.Transparent, 0.dp),
|
normal = BaseUITheme.Border(Color.Transparent, 0.dp),
|
||||||
hovered = BaseUITheme.Border(Color.Transparent, 0.dp),
|
hovered = BaseUITheme.Border(glassBorder, 1.dp),
|
||||||
pressed = BaseUITheme.Border(Color.Transparent, 0.dp),
|
pressed = BaseUITheme.Border(Color.Transparent, 0.dp),
|
||||||
focused = BaseUITheme.Border(Color.Transparent, 0.dp),
|
focused = BaseUITheme.Border(glassBorder, 1.dp),
|
||||||
selected = BaseUITheme.Border(Color.Transparent, 0.dp),
|
selected = BaseUITheme.Border(Color.Transparent, 0.dp),
|
||||||
disabled = BaseUITheme.Border(Color.Transparent, 0.dp),
|
disabled = BaseUITheme.Border(Color.Transparent, 0.dp),
|
||||||
),
|
),
|
||||||
shadow = BaseUITheme.AdvancedInteractionState(
|
shadow = BaseUITheme.AdvancedInteractionState(
|
||||||
normal = BaseUITheme.Shadow(0.dp, false, Color.Transparent, Color.Transparent),
|
normal = noShadow,
|
||||||
hovered = BaseUITheme.Shadow(0.dp, false, Color.Transparent, Color.Transparent),
|
hovered = BaseUITheme.Shadow(
|
||||||
pressed = BaseUITheme.Shadow(0.dp, false, Color.Transparent, Color.Transparent),
|
6.dp,
|
||||||
focused = BaseUITheme.Shadow(0.dp, false, Color.Transparent, Color.Transparent),
|
true,
|
||||||
selected = BaseUITheme.Shadow(0.dp, false, Color.Transparent, Color.Transparent),
|
shadowColor.copy(alpha = 0.12f),
|
||||||
disabled = BaseUITheme.Shadow(0.dp, false, Color.Transparent, Color.Transparent),
|
shadowColor.copy(alpha = 0.16f)
|
||||||
|
),
|
||||||
|
pressed = noShadow,
|
||||||
|
focused = noShadow,
|
||||||
|
selected = noShadow,
|
||||||
|
disabled = noShadow,
|
||||||
),
|
),
|
||||||
padding = PaddingValues(horizontal = 8.dp, vertical = 6.dp),
|
padding = PaddingValues(horizontal = 10.dp, vertical = 8.dp),
|
||||||
textStyle = BaseUITheme.AdvancedInteractionState(
|
textStyle = BaseUITheme.AdvancedInteractionState(
|
||||||
normal = TextStyle.Default,
|
normal = TextStyle.Default,
|
||||||
hovered = TextStyle.Default,
|
hovered = TextStyle.Default,
|
||||||
@ -1001,17 +850,12 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
dialog = BaseUITheme.DialogTheme(
|
dialog = BaseUITheme.DialogTheme(
|
||||||
background = Brush.verticalGradient(
|
background = glassBrush(glassTop, glassBottom),
|
||||||
listOf(
|
|
||||||
scheme.surface,
|
|
||||||
scheme.surfaceContainerHigh,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
scrim = Color.Black.copy(alpha = 0.42f),
|
scrim = Color.Black.copy(alpha = 0.42f),
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = RoundedCornerShape(24.dp),
|
||||||
border = BaseUITheme.Border(border, 0.5.dp),
|
border = BaseUITheme.Border(glassEdge, 1.dp),
|
||||||
shadow = BaseUITheme.Shadow(
|
shadow = BaseUITheme.Shadow(
|
||||||
24.dp,
|
28.dp,
|
||||||
true,
|
true,
|
||||||
shadowColor.copy(alpha = 0.3f),
|
shadowColor.copy(alpha = 0.3f),
|
||||||
shadowColor.copy(alpha = 0.4f)
|
shadowColor.copy(alpha = 0.4f)
|
||||||
@ -1019,27 +863,20 @@ fun rememberBaseUITheme(): BaseUITheme {
|
|||||||
padding = PaddingValues(24.dp),
|
padding = PaddingValues(24.dp),
|
||||||
),
|
),
|
||||||
dropdownMenu = BaseUITheme.DropdownMenuTheme(
|
dropdownMenu = BaseUITheme.DropdownMenuTheme(
|
||||||
background = Brush.verticalGradient(
|
background = glassBrush(glassTop, glassBottom),
|
||||||
listOf(
|
shape = RoundedCornerShape(16.dp),
|
||||||
scheme.surfaceContainerHigh,
|
border = BaseUITheme.Border(glassBorder, 1.dp),
|
||||||
scheme.surfaceContainer,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
shape = RoundedCornerShape(12.dp),
|
|
||||||
border = BaseUITheme.Border(border, 0.5.dp),
|
|
||||||
shadow = BaseUITheme.Shadow(
|
shadow = BaseUITheme.Shadow(
|
||||||
12.dp,
|
16.dp,
|
||||||
true,
|
true,
|
||||||
shadowColor.copy(alpha = 0.2f),
|
shadowColor.copy(alpha = 0.22f),
|
||||||
shadowColor.copy(alpha = 0.25f)
|
shadowColor.copy(alpha = 0.28f)
|
||||||
),
|
|
||||||
itemHoverBackground = Brush.verticalGradient(
|
|
||||||
listOf(containerLighter, containerDarker)
|
|
||||||
),
|
),
|
||||||
|
itemHoverBackground = glassBrush(glassTop, glassBottom),
|
||||||
itemForeground = scheme.onSurface,
|
itemForeground = scheme.onSurface,
|
||||||
itemHighlight = highlight,
|
itemHighlight = gloss,
|
||||||
padding = PaddingValues(4.dp),
|
padding = PaddingValues(6.dp),
|
||||||
itemPadding = PaddingValues(horizontal = 14.dp, vertical = 10.dp),
|
itemPadding = PaddingValues(horizontal = 14.dp, vertical = 11.dp),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,15 +24,27 @@ import androidx.compose.ui.unit.dp
|
|||||||
|
|
||||||
fun Modifier.highlight(color: Color): Modifier =
|
fun Modifier.highlight(color: Color): Modifier =
|
||||||
drawWithCache {
|
drawWithCache {
|
||||||
val highlightBrush = Brush.verticalGradient(
|
// Frutiger-Aero glass: a bright specular sheen across the top half plus a
|
||||||
colors = listOf(color, Color.Transparent),
|
// faint reflection rising from the bottom edge.
|
||||||
|
val topGloss = Brush.verticalGradient(
|
||||||
|
colors = listOf(color, color.copy(alpha = color.alpha * 0.25f), Color.Transparent),
|
||||||
startY = 0f,
|
startY = 0f,
|
||||||
endY = size.height * 0.5f,
|
endY = size.height * 0.55f,
|
||||||
|
)
|
||||||
|
val bottomReflection = Brush.verticalGradient(
|
||||||
|
colors = listOf(Color.Transparent, color.copy(alpha = color.alpha * 0.3f)),
|
||||||
|
startY = size.height * 0.72f,
|
||||||
|
endY = size.height,
|
||||||
)
|
)
|
||||||
onDrawWithContent {
|
onDrawWithContent {
|
||||||
drawContent()
|
drawContent()
|
||||||
drawRect(
|
drawRect(
|
||||||
brush = highlightBrush,
|
brush = topGloss,
|
||||||
|
topLeft = androidx.compose.ui.geometry.Offset.Zero,
|
||||||
|
size = size,
|
||||||
|
)
|
||||||
|
drawRect(
|
||||||
|
brush = bottomReflection,
|
||||||
topLeft = androidx.compose.ui.geometry.Offset.Zero,
|
topLeft = androidx.compose.ui.geometry.Offset.Zero,
|
||||||
size = size,
|
size = size,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -50,9 +50,6 @@ import androidx.compose.ui.text.font.FontFamily
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import dev.chrisbanes.haze.HazeState
|
import dev.chrisbanes.haze.HazeState
|
||||||
import dev.chrisbanes.haze.blur.HazeColorEffect
|
|
||||||
import dev.chrisbanes.haze.blur.blurEffect
|
|
||||||
import dev.chrisbanes.haze.hazeEffect
|
|
||||||
import dev.krtirtho.spotube.NavigationItem
|
import dev.krtirtho.spotube.NavigationItem
|
||||||
import dev.krtirtho.spotube.core.navigation.NavigationState
|
import dev.krtirtho.spotube.core.navigation.NavigationState
|
||||||
import dev.krtirtho.spotube.core.navigation.Navigator
|
import dev.krtirtho.spotube.core.navigation.Navigator
|
||||||
@ -85,21 +82,13 @@ fun AppSidebar(
|
|||||||
var expanded by rememberSaveable { mutableStateOf(true) }
|
var expanded by rememberSaveable { mutableStateOf(true) }
|
||||||
val width by animateDpAsState(targetValue = if (expanded) 236.dp else 86.dp)
|
val width by animateDpAsState(targetValue = if (expanded) 236.dp else 86.dp)
|
||||||
val currentLibraryTab by libraryState.currentTab.collectAsState()
|
val currentLibraryTab by libraryState.currentTab.collectAsState()
|
||||||
val surfaceTint = MaterialTheme.colorScheme.surface.copy(alpha = 0.85f)
|
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
.width(width)
|
.width(width),
|
||||||
.hazeEffect(hazeState) {
|
|
||||||
blurEffect {
|
|
||||||
blurRadius = 20.dp
|
|
||||||
colorEffects = listOf(
|
|
||||||
HazeColorEffect.tint(surfaceTint)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user