SIMPLEX theme: sunrise gradient on chat bubbles, avatars and text

Paints the SIMPLEX chat over one screen-spanning "sunrise" gradient:
- bubbles, avatars and links sample the gradient by on-screen position (a shared
  20°-tilted axis anchored to the chat viewport);
- small text (dates, delivery checks, names, quoted authors, event notices,
  call/voice/deleted secondary text) uses a per-wallpaper semi-transparent warm
  tint, so it stays evenly legible over the gradient;
- decryption/integrity-error and deleted/marked-deleted bubbles use the received
  gradient instead of a flat grey;
- wallpaper stops and secondary colours resolve from the composition, so per-chat
  wallpaper overrides and the wallpaper-picker preview render correctly.

Side effects for all themes (details in the PR): event/feature text follows the
active theme instead of a start-up-frozen colour, and the via-proxy meta icon
matches the meta row on media bubbles.
This commit is contained in:
another-simple-pixel
2026-07-02 15:02:00 -07:00
parent c644c9ab7a
commit 871978bf31
19 changed files with 419 additions and 178 deletions
@@ -4850,7 +4850,7 @@ sealed class Format {
val isSimplexLink = this is SimplexLink
companion object {
val linkStyle @Composable get() = SpanStyle(color = MaterialTheme.colors.primary, textDecoration = TextDecoration.Underline)
val linkStyle @Composable get() = SpanStyle(color = LocalSimplexLinkColor.current ?: MaterialTheme.colors.primary, textDecoration = TextDecoration.Underline)
}
}
@@ -0,0 +1,240 @@
package chat.simplex.common.ui.theme
import androidx.compose.foundation.background
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.unit.IntSize
import chat.simplex.common.views.helpers.PresetWallpaper
import chat.simplex.common.views.helpers.SimplexStops
import chat.simplex.common.views.helpers.WallpaperType
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
// SIMPLEX 20°-tilted "sunrise" axis spans the whole chat surface (top app bar to compose bar).
// Bubble backgrounds and secondary text sample stops along this same axis so colour rises
// from bottom-left dark to top-right warm in one coordinated motion across the screen.
private const val SIMPLEX_TILT_DEG = 20f
// Per-wallpaper gradient stops live in PresetWallpaper._simplexStops (ChatWallpaper.kt).
// Only LINK_STOPS is global — aligned with primary accent, not wallpaper-dependent.
private val LINK_STOPS = listOf(
0.55f to oklch(0.7993f, 0.1442f, 220.36f), // hue aligned with primary (cyan) — sRGB #00D3F9
0.80f to oklch(0.8600f, 0.1000f, 220.36f), // lighter, same hue
)
val LocalSimplexLinkColor = compositionLocalOf<Color?> { null }
class ChatViewportInfo(
val sizePx: State<IntSize>,
val originInWindow: State<Offset>,
)
val LocalChatViewportInfo = compositionLocalOf<ChatViewportInfo?> { null }
@Composable
fun rememberChatViewportInfo(): Pair<ChatViewportInfo, Modifier> {
val sizePx = remember { mutableStateOf(IntSize.Zero) }
val originInWindow = remember { mutableStateOf(Offset.Zero) }
val info = remember { ChatViewportInfo(sizePx, originInWindow) }
val mod = Modifier
.onSizeChanged { sizePx.value = it }
.onGloballyPositioned { originInWindow.value = it.positionInWindow() }
return info to mod
}
private fun axisEndpoints(winW: Float, winH: Float): Pair<Offset, Offset> {
val theta = SIMPLEX_TILT_DEG * PI.toFloat() / 180f
val dx = sin(theta); val dy = -cos(theta)
val cx = winW / 2f; val cy = winH / 2f
val projs = floatArrayOf(
(-cx) * dx + (-cy) * dy,
(winW - cx) * dx + (-cy) * dy,
(-cx) * dx + (winH - cy) * dy,
(winW - cx) * dx + (winH - cy) * dy,
)
var minP = projs[0]; var maxP = projs[0]
for (p in projs) { if (p < minP) minP = p; if (p > maxP) maxP = p }
val start = Offset(cx + dx * minP, cy + dy * minP)
val end = Offset(cx + dx * maxP, cy + dy * maxP)
return start to end
}
private fun sampleStops(stops: List<Pair<Float, Color>>, t: Float): Color {
if (t <= stops.first().first) return stops.first().second
if (t >= stops.last().first) return stops.last().second
for (i in 0 until stops.size - 1) {
val a = stops[i]; val b = stops[i + 1]
if (t in a.first..b.first) {
val f = (t - a.first) / (b.first - a.first)
return Color(
red = a.second.red + f * (b.second.red - a.second.red),
green = a.second.green + f * (b.second.green - a.second.green),
blue = a.second.blue + f * (b.second.blue - a.second.blue),
)
}
}
return stops.last().second
}
enum class SimplexBubbleSlot { Sent, SentQuote, Received, ReceivedQuote }
/** SimplexStops for a wallpaper, or null when it has none. */
private fun stopsForWallpaper(wallpaper: AppWallpaper): SimplexStops? {
val type = wallpaper.type as? WallpaperType.Preset ?: return null
return PresetWallpaper.from(type.filename)?.simplexStops
}
// The active theme's base is always the global one — ThemeManager.currentColors never overrides
// the base per chat; only wallpaper and colours vary per chat/preview. So the SIMPLEX check stays
// global, while the stops/tints read wallpaper + colours from composition (per-chat/preview aware).
private fun isSimplexActive(): Boolean = CurrentColors.value.base == DefaultTheme.SIMPLEX
@Composable private fun activeSimplexStops(): SimplexStops? = stopsForWallpaper(MaterialTheme.wallpaper)
@Composable private fun stopsFor(slot: SimplexBubbleSlot): Array<Pair<Float, Color>>? {
val stops = activeSimplexStops() ?: return null
return when (slot) {
SimplexBubbleSlot.Sent -> stops.sent
SimplexBubbleSlot.SentQuote -> stops.sentQuote
SimplexBubbleSlot.Received -> stops.received
SimplexBubbleSlot.ReceivedQuote -> stops.receivedQuote
}
}
// Paint a bubble's background with the tilted gradient anchored to the chat viewport.
// Brush start/end live in viewport coordinates; we translate them into bubble-local
// coordinates so the gradient continues across bubbles as a single coherent axis.
fun Modifier.simplexBubbleBackground(slot: SimplexBubbleSlot): Modifier = composed {
val viewport = LocalChatViewportInfo.current ?: return@composed this
val bubblePos = remember { mutableStateOf(Offset.Zero) }
val stops = stopsFor(slot) ?: return@composed this
this
.onGloballyPositioned { bubblePos.value = it.positionInWindow() }
.drawBehind {
val sz = viewport.sizePx.value
if (sz.width == 0 || sz.height == 0) return@drawBehind
val (axisStartLocal, axisEndLocal) = axisEndpoints(sz.width.toFloat(), sz.height.toFloat())
val viewportOrigin = viewport.originInWindow.value
val offset = viewportOrigin - bubblePos.value
val brush = Brush.linearGradient(
colorStops = stops,
start = axisStartLocal + offset,
end = axisEndLocal + offset,
)
drawRect(brush)
}
}
// Single entry point for chat-bubble backgrounds: routes all four slots to SIMPLEX
// gradients when SIMPLEX is active and viewport info is available, else flat AppColors.
fun Modifier.chatBubbleBackground(
sent: Boolean,
isQuote: Boolean,
transparent: Boolean = false,
): Modifier = composed {
if (transparent) return@composed this.background(Color.Transparent)
val simplexActive = isSimplexActive()
val viewportAvailable = LocalChatViewportInfo.current != null
val stops = activeSimplexStops()
if (simplexActive && viewportAvailable && stops != null) {
val slot = when {
sent && isQuote -> SimplexBubbleSlot.SentQuote
sent -> SimplexBubbleSlot.Sent
isQuote -> SimplexBubbleSlot.ReceivedQuote
else -> SimplexBubbleSlot.Received
}
return@composed this.simplexBubbleBackground(slot)
}
this.background(
when {
sent && isQuote -> MaterialTheme.appColors.sentQuote
sent -> MaterialTheme.appColors.sentMessage
isQuote -> MaterialTheme.appColors.receivedQuote
else -> MaterialTheme.appColors.receivedMessage
}
)
}
// Tint a single-colour icon (e.g. default ProfileImage placeholder) with the
// SIMPLEX axis brush. Drawn as an overlay using BlendMode.SrcAtop so the brush
// is masked by the icon's existing alpha — the icon's shape is preserved and
// only its colour is replaced by the gradient.
fun Modifier.simplexAvatarBrushOverlay(slot: SimplexBubbleSlot): Modifier = composed {
if (!isSimplexActive()) return@composed this
val viewport = LocalChatViewportInfo.current ?: return@composed this
val avatarPos = remember { mutableStateOf(Offset.Zero) }
val stops = stopsFor(slot) ?: return@composed this
this
.onGloballyPositioned { avatarPos.value = it.positionInWindow() }
.graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
.drawWithContent {
drawContent()
val sz = viewport.sizePx.value
if (sz.width == 0 || sz.height == 0) return@drawWithContent
val (axisStartLocal, axisEndLocal) = axisEndpoints(sz.width.toFloat(), sz.height.toFloat())
val viewportOrigin = viewport.originInWindow.value
val offset = viewportOrigin - avatarPos.value
val brush = Brush.linearGradient(
colorStops = stops,
start = axisStartLocal + offset,
end = axisEndLocal + offset,
)
drawRect(brush, blendMode = BlendMode.SrcAtop)
}
}
// Static tinted-transparency for small text on the SIMPLEX gradient. A semi-transparent
// warm colour blends with the gradient at every height, so text stays uniformly legible
// without sampling the axis by screen position (bubbles and avatars still sample it).
// Composition-scoped: honours the per-chat override / wallpaper preview, and its fallback is
// the composition secondary, so non-SIMPLEX chats keep their own (override-aware) colour.
@Composable
fun simplexSecondaryTint(): Color {
if (!isSimplexActive()) return MaterialTheme.colors.secondary
return activeSimplexStops()?.secondaryTint ?: MaterialTheme.colors.secondary
}
@Composable
fun simplexAuthorTint(): Color {
if (!isSimplexActive()) return MaterialTheme.colors.onBackground
return activeSimplexStops()?.authorTint ?: MaterialTheme.colors.onBackground
}
// Link colour still samples the axis — accent cyan aligned across the whole screen.
@Composable
fun simplexLinkColor(): Pair<Color, Modifier> = simplexAxisSampledColor(LINK_STOPS, MaterialTheme.colors.primary)
@Composable
private fun simplexAxisSampledColor(stops: List<Pair<Float, Color>>?, fallback: Color): Pair<Color, Modifier> {
val isSimplex = isSimplexActive()
val viewport = LocalChatViewportInfo.current
if (!isSimplex || viewport == null || stops == null) return fallback to Modifier
val color = remember(stops) { mutableStateOf(stops.first().second) }
val mod = Modifier.onGloballyPositioned { coords ->
val sz = viewport.sizePx.value
if (sz.width == 0 || sz.height == 0) return@onGloballyPositioned
val pos = coords.positionInWindow() - viewport.originInWindow.value
val cx = pos.x + coords.size.width / 2f
val cy = pos.y + coords.size.height / 2f
val (axisStart, axisEnd) = axisEndpoints(sz.width.toFloat(), sz.height.toFloat())
val span = (axisEnd - axisStart)
val spanLen2 = span.x * span.x + span.y * span.y
val t = ((cx - axisStart.x) * span.x + (cy - axisStart.y) * span.y) / spanLen2
color.value = sampleStops(stops, t.coerceIn(0f, 1f))
}
return color.value to mod
}
@@ -979,7 +979,9 @@ fun ChatLayout(
sheetShape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp)
) {
val composeViewHeight = remember { mutableStateOf(0.dp) }
Box(Modifier.fillMaxSize().chatViewBackgroundModifier(MaterialTheme.colors, MaterialTheme.wallpaper, LocalAppBarHandler.current?.backgroundGraphicsLayerSize, LocalAppBarHandler.current?.backgroundGraphicsLayer, drawWallpaper = chatsCtx.secondaryContextFilter == null)) {
val (chatViewport, chatViewportModifier) = rememberChatViewportInfo()
Box(Modifier.fillMaxSize().then(chatViewportModifier).chatViewBackgroundModifier(MaterialTheme.colors, MaterialTheme.wallpaper, LocalAppBarHandler.current?.backgroundGraphicsLayerSize, LocalAppBarHandler.current?.backgroundGraphicsLayer, drawWallpaper = chatsCtx.secondaryContextFilter == null)) {
CompositionLocalProvider(LocalChatViewportInfo provides chatViewport) {
val remoteHostId = remember { remoteHostId }.value
val chat = remember { chat }.value
val chatInfo = chat?.chatInfo
@@ -1164,6 +1166,7 @@ fun ChatLayout(
}
}
}
}
}
}
}
@@ -2023,6 +2026,7 @@ fun BoxScope.ChatItemsList(
null to 1
}
// the name and the badge are one element, so SpaceBetween separates them from the role, not from each other
val memberNameColor = simplexSecondaryTint()
NameWithBadge(
memberNames(member, prevMember, memCount),
if (prevMember == null && memCount == 1) member.nameBadge else null,
@@ -2030,7 +2034,7 @@ fun BoxScope.ChatItemsList(
.padding(start = (MEMBER_IMAGE_SIZE * fontSizeSqrtMultiplier) + DEFAULT_PADDING_HALF)
.weight(1f, false),
fontSize = 13.5.sp,
color = MaterialTheme.colors.secondary,
color = memberNameColor,
overflow = TextOverflow.Ellipsis,
maxLines = 1
)
@@ -2038,13 +2042,14 @@ fun BoxScope.ChatItemsList(
val chatItemTail = remember { appPreferences.chatItemTail.state }
val style = shapeStyle(cItem, chatItemTail.value, itemSeparation.largeGap, true)
val tailRendered = style is ShapeStyle.Bubble && style.tailVisible
val roleColor = simplexSecondaryTint()
Text(
member.memberRole.text(isChannel = chatInfo.isChannel),
Modifier.padding(start = DEFAULT_PADDING_HALF * 1.5f, end = DEFAULT_PADDING_HALF + if (tailRendered) msgTailWidthDp else 0.dp),
fontSize = 13.5.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colors.secondary,
color = roleColor,
maxLines = 1
)
}
@@ -2059,7 +2064,9 @@ fun BoxScope.ChatItemsList(
}
Row(Modifier.graphicsLayer { translationX = selectionOffset.toPx() }) {
val member = cItem.chatDir.groupMember
Box(Modifier.clickable { showMemberInfo(chatInfo.groupInfo, member) }) {
val memberAvatarOverlay = if (CurrentColors.value.base == DefaultTheme.SIMPLEX && member.image == null)
Modifier.simplexAvatarBrushOverlay(SimplexBubbleSlot.ReceivedQuote) else Modifier
Box(Modifier.clickable { showMemberInfo(chatInfo.groupInfo, member) }.then(memberAvatarOverlay)) {
MemberImage(member)
}
Box(modifier = Modifier.padding(top = 2.dp, start = 4.dp).chatItemOffset(cItem, itemSeparation.largeGap, revealed = revealed.value)) {
@@ -2105,6 +2112,7 @@ fun BoxScope.ChatItemsList(
) {
@Composable
fun ChannelNameAndRole() {
val channelColor = simplexSecondaryTint()
Row(Modifier.padding(bottom = 2.dp).graphicsLayer { translationX = selectionOffset.toPx() }, horizontalArrangement = Arrangement.SpaceBetween) {
Text(
chatInfo.groupInfo.chatViewName,
@@ -2112,7 +2120,7 @@ fun BoxScope.ChatItemsList(
.padding(start = (MEMBER_IMAGE_SIZE * fontSizeSqrtMultiplier) + DEFAULT_PADDING_HALF)
.weight(1f, false),
fontSize = 13.5.sp,
color = MaterialTheme.colors.secondary,
color = channelColor,
overflow = TextOverflow.Ellipsis,
maxLines = 1
)
@@ -2124,7 +2132,7 @@ fun BoxScope.ChatItemsList(
Modifier.padding(start = DEFAULT_PADDING_HALF * 1.5f, end = DEFAULT_PADDING_HALF + if (tailRendered) msgTailWidthDp else 0.dp),
fontSize = 13.5.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colors.secondary,
color = channelColor,
maxLines = 1
)
}
@@ -2278,7 +2286,7 @@ fun BoxScope.ChatItemsList(
Box(
Modifier
.clipChatItem()
.background(MaterialTheme.appColors.receivedMessage)
.chatBubbleBackground(sent = false, isQuote = false)
) {
val bannerModifier = if (appPlatform.isDesktop) Modifier.width(400.dp) else Modifier.fillMaxWidth()
Column(
@@ -2289,9 +2297,11 @@ fun BoxScope.ChatItemsList(
// ChatInfoImage has its own padding somewhere,
// also not doing verticalArrangement = Arrangement.spacedBy(DEFAULT_PADDING_HALF) because of it
.padding(top = DEFAULT_PADDING_HALF)
.background(MaterialTheme.appColors.receivedMessage)
.let { if (CurrentColors.value.base == DefaultTheme.SIMPLEX) it else it.background(MaterialTheme.appColors.receivedMessage) }
) {
ChatInfoImage(chatInfo, size = alertProfileImageSize, iconColor = MaterialTheme.colors.secondaryVariant.mixWith(MaterialTheme.colors.onBackground, 0.97f))
Box(if (CurrentColors.value.base == DefaultTheme.SIMPLEX && chatInfo.image == null) Modifier.simplexAvatarBrushOverlay(SimplexBubbleSlot.ReceivedQuote) else Modifier) {
ChatInfoImage(chatInfo, size = alertProfileImageSize, iconColor = MaterialTheme.colors.secondaryVariant.mixWith(MaterialTheme.colors.onBackground, 0.97f))
}
val bannerBadge = chatInfo.nameBadge
val uriHandler = LocalUriHandler.current
Text(
@@ -2345,11 +2355,12 @@ fun BoxScope.ChatItemsList(
val contextStr = chatContext()
if (contextStr != null) {
val subtitleColor = simplexSecondaryTint()
Text(
contextStr,
style = MaterialTheme.typography.body2,
textAlign = TextAlign.Center,
color = MaterialTheme.colors.secondary,
color = subtitleColor,
modifier = Modifier.padding(top = DEFAULT_PADDING)
)
}
@@ -3036,13 +3047,14 @@ private fun ButtonRow(horizontalArrangement: Arrangement.Horizontal, content: @C
@Composable
private fun DateSeparator(date: Instant) {
val secColor = simplexSecondaryTint()
Text(
text = getTimestampDateText(date),
Modifier.padding(vertical = DEFAULT_PADDING_HALF + 4.dp, horizontal = DEFAULT_PADDING_HALF).fillMaxWidth(),
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
textAlign = TextAlign.Center,
color = MaterialTheme.colors.secondary
color = secColor
)
}
@@ -41,9 +41,12 @@ fun CICallItemView(
CICallStatus.Accepted -> ConnectingCallIcon()
CICallStatus.Negotiated -> ConnectingCallIcon()
CICallStatus.Progress -> Icon(painterResource(MR.images.ic_phone_in_talk_filled), stringResource(MR.strings.icon_descr_call_progress), tint = SimplexGreen)
CICallStatus.Ended -> Row {
Icon(painterResource(MR.images.ic_call_end), stringResource(MR.strings.icon_descr_call_ended), tint = MaterialTheme.colors.secondary, modifier = Modifier.padding(end = 4.dp))
Text(durationText(duration), color = MaterialTheme.colors.secondary)
CICallStatus.Ended -> {
val callColor = simplexSecondaryTint()
Row {
Icon(painterResource(MR.images.ic_call_end), stringResource(MR.strings.icon_descr_call_ended), tint = callColor, modifier = Modifier.padding(end = 4.dp))
Text(durationText(duration), color = callColor)
}
}
CICallStatus.Error -> {}
}
@@ -13,6 +13,7 @@ import androidx.compose.ui.unit.sp
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatModel.getChatItemIndexOrNull
import chat.simplex.common.platform.onRightClick
import chat.simplex.common.ui.theme.simplexSecondaryTint
@Composable
fun CIChatFeatureView(
@@ -106,7 +107,7 @@ private fun FeatureIconView(f: FeatureInfo) {
if (f.param != null) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp)) {
icon()
Text(chatEventText(f.param, ""), maxLines = 1)
Text(chatEventText(f.param, ""), color = simplexSecondaryTint(), maxLines = 1)
}
} else {
icon()
@@ -129,6 +130,7 @@ private fun FullFeatureView(
Text(
chatEventText(chatItem),
Modifier,
color = simplexSecondaryTint(),
// this is important. Otherwise, aligning will be bad because annotated string has a Span with size 12.sp
fontSize = 12.sp
)
@@ -12,7 +12,7 @@ import chat.simplex.common.ui.theme.*
@Composable
fun CIEventView(text: AnnotatedString) {
Text(text, Modifier.padding(horizontal = 6.dp, vertical = 6.dp), style = MaterialTheme.typography.body1.copy(lineHeight = 22.sp), maxLines = 4)
Text(text, Modifier.padding(horizontal = 6.dp, vertical = 6.dp), color = simplexSecondaryTint(), style = MaterialTheme.typography.body1.copy(lineHeight = 22.sp), maxLines = 4)
}
@Preview/*(
uiMode = Configuration.UI_MODE_NIGHT_YES,
@@ -11,6 +11,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.common.views.helpers.generalGetString
import chat.simplex.common.model.*
import chat.simplex.common.ui.theme.simplexSecondaryTint
import chat.simplex.res.MR
@Composable
@@ -42,12 +43,13 @@ fun CIFeaturePreferenceView(
fun accept(offset: Int): Boolean = annotatedText.getStringAnnotations(tag = "Accept", start = offset, end = offset).isNotEmpty()
ClickableText(
annotatedText,
style = TextStyle(color = simplexSecondaryTint()),
onClick = { if (accept(it)) { acceptFeature(contact, feature, param) } },
shouldConsumeEvent = ::accept
)
} else {
Text(chatItem.content.text + " " + chatItem.timestampText,
fontSize = 12.sp, fontWeight = FontWeight.Light, color = MaterialTheme.colors.secondary)
fontSize = 12.sp, fontWeight = FontWeight.Light, color = simplexSecondaryTint())
}
}
}
@@ -53,12 +53,18 @@ fun CIFileView(
Box(
contentAlignment = Alignment.Center
) {
Icon(
painterResource(MR.images.ic_draft_filled),
stringResource(MR.strings.icon_descr_file),
Modifier.fillMaxSize(),
tint = color
)
val isDefaultTint = color == (if (isInDarkTheme()) FileDark else FileLight)
val isSimplex = CurrentColors.value.base == DefaultTheme.SIMPLEX
val outerOverlay = if (isSimplex && isDefaultTint)
Modifier.simplexAvatarBrushOverlay(SimplexBubbleSlot.ReceivedQuote) else Modifier
Box(outerOverlay.fillMaxSize()) {
Icon(
painterResource(MR.images.ic_draft_filled),
stringResource(MR.strings.icon_descr_file),
Modifier.fillMaxSize(),
tint = color
)
}
if (innerIcon != null) {
Icon(
innerIcon,
@@ -220,12 +226,13 @@ fun CIFileView(
file.fileName,
maxLines = 1
)
val fileSizeColor = simplexSecondaryTint()
Text(
buildAnnotatedString {
append(formatBytes(file.fileSize))
append(metaReserve)
},
color = secondaryColor,
color = fileSizeColor,
fontSize = 14.sp,
maxLines = 1
)
@@ -4,6 +4,7 @@ import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.draw.clip
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
@@ -51,11 +52,16 @@ fun CIGroupInvitationView(
val iconColor =
if (action && !inProgress.value) if (chatIncognito) Indigo else MaterialTheme.colors.primary
else if (isInDarkTheme()) FileDark else FileLight
val isSimplex = CurrentColors.value.base == DefaultTheme.SIMPLEX
val avatarOverlayMod = if (isSimplex && groupInvitation.groupProfile.image == null)
Modifier.simplexAvatarBrushOverlay(SimplexBubbleSlot.ReceivedQuote) else Modifier
Row(
Modifier.defaultMinSize(minWidth = 220.dp)
) {
ProfileImage(size = 54.dp, image = groupInvitation.groupProfile.image, icon = MR.images.ic_supervised_user_circle_filled, color = iconColor)
Box(avatarOverlayMod) {
ProfileImage(size = 54.dp, image = groupInvitation.groupProfile.image, icon = MR.images.ic_supervised_user_circle_filled, color = iconColor)
}
Spacer(Modifier.width(8.dp))
Column(
Modifier.defaultMinSize(minHeight = 54.dp),
@@ -84,15 +90,12 @@ fun CIGroupInvitationView(
val sentColor = MaterialTheme.appColors.sentMessage
val receivedColor = MaterialTheme.appColors.receivedMessage
Surface(
modifier = if (action && !inProgress.value) Modifier.clickable(onClick = {
inProgress.value = true
joinGroup(groupInvitation.groupId) { inProgress.value = false }
}) else Modifier,
shape = RoundedCornerShape(18.dp),
color = if (sent) sentColor else receivedColor,
contentColor = LocalContentColor.current
) {
val isSimplex = CurrentColors.value.base == DefaultTheme.SIMPLEX
val containerClickable = if (action && !inProgress.value) Modifier.clickable(onClick = {
inProgress.value = true
joinGroup(groupInvitation.groupId) { inProgress.value = false }
}) else Modifier
@Composable fun BubbleContent() {
Box(
Modifier
.width(IntrinsicSize.Min)
@@ -148,6 +151,25 @@ fun CIGroupInvitationView(
CIMetaView(ci, timedMessagesTTL, showStatus = false, showEdited = false, showViaProxy = false, showTimestamp = showTimestamp)
}
}
if (isSimplex) {
Box(
containerClickable
.clip(RoundedCornerShape(18.dp))
.chatBubbleBackground(sent = sent, isQuote = false)
) {
BubbleContent()
}
} else {
Surface(
modifier = containerClickable,
shape = RoundedCornerShape(18.dp),
color = if (sent) sentColor else receivedColor,
contentColor = LocalContentColor.current
) {
BubbleContent()
}
}
}
@Preview/*(
@@ -10,6 +10,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.common.views.helpers.generalGetString
import chat.simplex.common.model.*
import chat.simplex.common.ui.theme.simplexSecondaryTint
import chat.simplex.res.MR
@Composable
@@ -51,6 +52,7 @@ fun CIMemberCreatedContactView(
fun open(offset: Int): Boolean = annotatedText.getStringAnnotations(tag = "Open", start = offset, end = offset).isNotEmpty()
ClickableText(
annotatedText,
style = TextStyle(color = simplexSecondaryTint()),
onClick = {
if (open(it)) {
openDirectChat(chatItem.chatDir.groupMember.memberContactId)
@@ -64,7 +66,7 @@ fun CIMemberCreatedContactView(
append(" ")
withStyle(chatEventStyle) { append(chatItem.timestampText) }
}
Text(annotatedText)
Text(annotatedText, color = simplexSecondaryTint())
}
}
}
@@ -13,36 +13,44 @@ import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.common.model.*
import chat.simplex.common.ui.theme.CurrentColors
import chat.simplex.common.ui.theme.DefaultTheme
import chat.simplex.common.ui.theme.isInDarkTheme
import chat.simplex.common.ui.theme.simplexSecondaryTint
import chat.simplex.res.MR
import kotlinx.datetime.Clock
private fun palette(metaColor: Color, dark: Boolean): Color =
if (dark) metaColor.copy(
red = metaColor.red * 0.67F,
green = metaColor.green * 0.67F,
blue = metaColor.red * 0.67F,
) else metaColor.copy(
red = minOf(metaColor.red * 1.33F, 1F),
green = minOf(metaColor.green * 1.33F, 1F),
blue = minOf(metaColor.red * 1.33F, 1F),
)
@Composable
fun CIMetaView(
chatItem: ChatItem,
timedMessagesTTL: Int?,
metaColor: Color = MaterialTheme.colors.secondary,
paleMetaColor: Color = if (isInDarkTheme()) {
metaColor.copy(
red = metaColor.red * 0.67F,
green = metaColor.green * 0.67F,
blue = metaColor.red * 0.67F)
} else {
metaColor.copy(
red = minOf(metaColor.red * 1.33F, 1F),
green = minOf(metaColor.green * 1.33F, 1F),
blue = minOf(metaColor.red * 1.33F, 1F))
},
metaColor: Color? = null,
paleMetaColor: Color? = null,
showStatus: Boolean = true,
showEdited: Boolean = true,
showTimestamp: Boolean,
showViaProxy: Boolean,
) {
val effectiveMeta = metaColor ?: simplexSecondaryTint()
// Partial delivery keeps the same warm tint but at lower alpha, so it reads a touch
// dimmer than full delivery while staying legible on the gradient.
val effectivePale = paleMetaColor ?: if (CurrentColors.value.base == DefaultTheme.SIMPLEX) effectiveMeta.copy(alpha = 0.4f) else palette(effectiveMeta, isInDarkTheme())
Row(Modifier.padding(start = 3.dp), verticalAlignment = Alignment.CenterVertically) {
if (chatItem.isDeletedContent) {
Text(
chatItem.timestampText,
color = metaColor,
color = effectiveMeta,
fontSize = 12.sp,
modifier = Modifier.padding(start = 3.dp)
)
@@ -51,8 +59,8 @@ fun CIMetaView(
chatItem.meta,
timedMessagesTTL,
encrypted = chatItem.encryptedFile,
metaColor,
paleMetaColor,
effectiveMeta,
effectivePale,
showStatus = showStatus,
showEdited = showEdited,
showViaProxy = showViaProxy,
@@ -87,7 +95,7 @@ private fun CIMetaText(
}
if (showViaProxy && meta.sentViaProxy == true) {
Spacer(Modifier.width(4.dp))
Icon(painterResource(MR.images.ic_arrow_forward), null, Modifier.height(17.dp), tint = MaterialTheme.colors.secondary)
Icon(painterResource(MR.images.ic_arrow_forward), null, Modifier.height(17.dp), tint = color)
}
if (showStatus) {
Spacer(Modifier.width(4.dp))
@@ -7,14 +7,14 @@ import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.common.model.*
import chat.simplex.common.ui.theme.CurrentColors
import chat.simplex.common.ui.theme.appColors
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.AlertManager
import chat.simplex.common.views.helpers.generalGetString
import chat.simplex.res.MR
@@ -138,12 +138,10 @@ fun DecryptionErrorItemFixButton(
onClick: () -> Unit,
syncSupported: Boolean
) {
val receivedColor = MaterialTheme.appColors.receivedMessage
Surface(
Modifier.clickable(onClick = onClick),
shape = RoundedCornerShape(18.dp),
color = receivedColor,
contentColor = LocalContentColor.current
Box(
Modifier.clickable(onClick = onClick)
.clip(RoundedCornerShape(18.dp))
.chatBubbleBackground(sent = false, isQuote = false)
) {
Box(
Modifier.padding(vertical = 6.dp, horizontal = 12.dp),
@@ -186,12 +184,10 @@ fun DecryptionErrorItem(
ci: ChatItem,
onClick: () -> Unit
) {
val receivedColor = MaterialTheme.appColors.receivedMessage
Surface(
Modifier.clickable(onClick = onClick),
shape = RoundedCornerShape(18.dp),
color = receivedColor,
contentColor = LocalContentColor.current
Box(
Modifier.clickable(onClick = onClick)
.clip(RoundedCornerShape(18.dp))
.chatBubbleBackground(sent = false, isQuote = false)
) {
Box(
Modifier.padding(vertical = 6.dp, horizontal = 12.dp),
@@ -227,12 +227,13 @@ private fun VoiceLayout(
@Composable
private fun DurationText(text: State<String>, padding: PaddingValues, smallView: Boolean = false) {
val minWidth = with(LocalDensity.current) { 45.sp.toDp() }
val durationColor = simplexSecondaryTint()
Text(
text.value,
Modifier
.padding(padding)
.widthIn(min = minWidth),
color = MaterialTheme.colors.secondary,
color = durationColor,
fontSize = if (smallView) 15.sp else 16.sp,
maxLines = 1
)
@@ -340,6 +341,7 @@ private fun FileStatusIcon(
) {
val sentColor = MaterialTheme.appColors.sentMessage
val receivedColor = MaterialTheme.appColors.receivedMessage
val statusIconColor = simplexSecondaryTint()
Surface(
color = if (sent) sentColor else receivedColor,
shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
@@ -359,7 +361,7 @@ private fun FileStatusIcon(
painterResource(icon),
contentDescription = null,
Modifier.size(36.sp.toDp() * sizeMultiplier),
tint = MaterialTheme.colors.secondary
tint = statusIconColor
)
}
}
@@ -48,7 +48,10 @@ val msgTailWidthDp = 9.dp
private val msgTailMinHeightDp = msgTailWidthDp * 1.254f // ~56deg
private val msgTailMaxHeightDp = msgTailWidthDp * 1.732f // 60deg
val chatEventStyle = SpanStyle(fontSize = 12.sp, fontWeight = FontWeight.Light, color = CurrentColors.value.colors.secondary)
// Event-text style; the colour is applied at the render site (event views use
// simplexSecondaryTint), so it follows the composition theme. Unspecified here defers the
// colour to the Text/ClickableText that draws the string.
val chatEventStyle = SpanStyle(fontSize = 12.sp, fontWeight = FontWeight.Light, color = Color.Unspecified)
fun chatEventText(ci: ChatItem, isChannel: Boolean = false): AnnotatedString =
chatEventText(ci.content.text(isChannel), ci.timestampText)
@@ -647,7 +650,8 @@ fun ChatItemView(
buildAnnotatedString {
withStyle(chatEventStyle.copy(fontWeight = FontWeight.Bold)) { append(cItem.content.text(cInfo.isChannel)) }
},
Modifier.padding(horizontal = 6.dp, vertical = 6.dp)
Modifier.padding(horizontal = 6.dp, vertical = 6.dp),
color = simplexSecondaryTint()
)
}
@@ -677,7 +681,8 @@ fun ChatItemView(
buildAnnotatedString {
withStyle(chatEventStyle) { append(annotatedStringResource(sId)) }
},
Modifier.padding(horizontal = 6.dp, vertical = 6.dp)
Modifier.padding(horizontal = 6.dp, vertical = 6.dp),
color = simplexSecondaryTint()
)
}
@@ -7,6 +7,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.desktop.ui.tooling.preview.Preview
@@ -18,20 +19,19 @@ import chat.simplex.common.ui.theme.*
@Composable
fun DeletedItemView(ci: ChatItem, timedMessagesTTL: Int?, showViaProxy: Boolean, showTimestamp: Boolean) {
val sent = ci.chatDir.sent
val sentColor = MaterialTheme.appColors.sentMessage
val receivedColor = MaterialTheme.appColors.receivedMessage
Surface(
shape = RoundedCornerShape(18.dp),
color = if (sent) sentColor else receivedColor,
contentColor = LocalContentColor.current
Box(
Modifier
.clip(RoundedCornerShape(18.dp))
.chatBubbleBackground(sent = sent, isQuote = false)
) {
Row(
Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
verticalAlignment = Alignment.Bottom
) {
val deletedColor = simplexSecondaryTint()
Text(
buildAnnotatedString {
withStyle(SpanStyle(fontStyle = FontStyle.Italic, color = MaterialTheme.colors.secondary)) { append(ci.content.text) }
withStyle(SpanStyle(fontStyle = FontStyle.Italic, color = deletedColor)) { append(ci.content.text) }
},
style = MaterialTheme.typography.body1.copy(lineHeight = 22.sp),
modifier = Modifier.padding(end = 8.dp)
@@ -84,12 +84,13 @@ fun FramedItemView(
) {
val sender = qi.sender(membership())
if (sender != null) {
val authorColor = simplexAuthorTint()
Column(
horizontalAlignment = Alignment.Start
) {
Text(
sender,
style = TextStyle(fontSize = 13.5.sp, color = if (qi.chatDir is CIDirection.GroupSnd) CurrentColors.value.colors.primary else CurrentColors.value.colors.secondary),
style = TextStyle(fontSize = 13.5.sp, color = if (CurrentColors.value.base == DefaultTheme.SIMPLEX) authorColor else if (qi.chatDir is CIDirection.GroupSnd) CurrentColors.value.colors.primary else CurrentColors.value.colors.secondary),
maxLines = 1
)
ciQuotedMsgTextView(qi, lines = 2, showTimestamp = showTimestamp, stripLink = stripLink, prefix = prefix)
@@ -102,11 +103,9 @@ fun FramedItemView(
@Composable
fun FramedItemHeader(caption: String, italic: Boolean, icon: Painter? = null, pad: Boolean = false, iconColor: Color? = null) {
val sentColor = MaterialTheme.appColors.sentQuote
val receivedColor = MaterialTheme.appColors.receivedQuote
Row(
Modifier
.background(if (sent) sentColor else receivedColor)
.chatBubbleBackground(sent = sent, isQuote = true)
.fillMaxWidth()
.padding(start = 8.dp, top = 6.dp, end = 12.dp, bottom = if (pad || (ci.quotedItem == null && ci.meta.itemForwarded == null)) 6.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
@@ -135,11 +134,9 @@ fun FramedItemView(
@Composable
fun ciQuoteView(qi: CIQuote) {
val sentColor = MaterialTheme.appColors.sentQuote
val receivedColor = MaterialTheme.appColors.receivedQuote
Row(
Modifier
.background(if (sent) sentColor else receivedColor)
.chatBubbleBackground(sent = sent, isQuote = true)
.fillMaxWidth()
) {
when (qi.content) {
@@ -210,18 +207,13 @@ fun FramedItemView(
val transparentBackground = (ci.content.msgContent is MsgContent.MCImage || ci.content.msgContent is MsgContent.MCVideo) &&
!ci.meta.isLive && ci.content.text.isEmpty() && ci.quotedItem == null && ci.meta.itemForwarded == null
val sentColor = MaterialTheme.appColors.sentMessage
val receivedColor = MaterialTheme.appColors.receivedMessage
val (linkColor, linkModifier) = simplexLinkColor()
Box(Modifier
.clipChatItem(ci, tailVisible, revealed = true)
.background(
when {
transparentBackground -> Color.Transparent
sent -> sentColor
else -> receivedColor
}
)) {
var metaColor = MaterialTheme.colors.secondary
.chatBubbleBackground(sent = sent, isQuote = false, transparent = transparentBackground)
.then(linkModifier)) {
var metaColor: Color? = null
CompositionLocalProvider(LocalSimplexLinkColor provides linkColor) {
Box(contentAlignment = Alignment.BottomEnd) {
val chatItemTail = remember { appPreferences.chatItemTail.state }
val style = shapeStyle(ci, chatItemTail.value, tailVisible, true)
@@ -385,6 +377,7 @@ fun FramedItemView(
CIMetaView(ci, chatTTL, metaColor, showViaProxy = showViaProxy, showTimestamp = showTimestamp)
}
}
}
}
}
@@ -1,6 +1,7 @@
package chat.simplex.common.views.chat.item
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -9,6 +10,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontStyle
@@ -51,12 +53,10 @@ fun IntegrityErrorItemView(msgError: MsgErrorType, ci: ChatItem, showTimestamp:
@Composable
fun CIMsgError(ci: ChatItem, showTimestamp: Boolean, timedMessagesTTL: Int?, onClick: () -> Unit) {
val receivedColor = MaterialTheme.appColors.receivedMessage
Surface(
Modifier.clickable(onClick = onClick),
shape = RoundedCornerShape(18.dp),
color = receivedColor,
contentColor = LocalContentColor.current
Box(
Modifier.clickable(onClick = onClick)
.clip(RoundedCornerShape(18.dp))
.chatBubbleBackground(sent = false, isQuote = false)
) {
Row(
Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
@@ -4,6 +4,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextOverflow
@@ -21,12 +22,10 @@ import kotlinx.datetime.Clock
@Composable
fun MarkedDeletedItemView(chatsCtx: ChatModel.ChatsContext, ci: ChatItem, chatInfo: ChatInfo, timedMessagesTTL: Int?, revealed: State<Boolean>, showViaProxy: Boolean, showTimestamp: Boolean) {
val sentColor = MaterialTheme.appColors.sentMessage
val receivedColor = MaterialTheme.appColors.receivedMessage
Surface(
shape = RoundedCornerShape(18.dp),
color = if (ci.chatDir.sent) sentColor else receivedColor,
contentColor = LocalContentColor.current
Box(
Modifier
.clip(RoundedCornerShape(18.dp))
.chatBubbleBackground(sent = ci.chatDir.sent, isQuote = false)
) {
Row(
Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
@@ -80,9 +79,10 @@ private fun MergedMarkedDeletedText(chatsCtx: ChatModel.ChatsContext, chatItem:
markedDeletedText(chatItem, chatInfo)
}
val deletedColor = simplexSecondaryTint()
Text(
buildAnnotatedString {
withStyle(SpanStyle(fontSize = 12.sp, fontStyle = FontStyle.Italic, color = MaterialTheme.colors.secondary)) { append(text) }
withStyle(SpanStyle(fontSize = 12.sp, fontStyle = FontStyle.Italic, color = deletedColor)) { append(text) }
},
style = MaterialTheme.typography.body1.copy(lineHeight = 22.sp),
modifier = Modifier.padding(end = 8.dp),
@@ -22,16 +22,17 @@ import kotlinx.serialization.Serializable
import java.io.File
import kotlin.math.*
// Per-wallpaper SIMPLEX gradient stops: chat background + the four bubble slots + secondary/author text.
// The background stops are drawn by simplexGradient(); bubble/text stops are consumed by SimplexBrushes.
// Per-wallpaper SIMPLEX gradient stops: chat background + the four bubble slots, plus the
// secondary/author text tints. Background stops are drawn by simplexGradient(); bubble stops
// and the semi-transparent text tints are consumed by SimplexBrushes.
data class SimplexStops(
val bg: Array<Pair<Float, Color>>,
val sent: Array<Pair<Float, Color>>,
val sentQuote: Array<Pair<Float, Color>>,
val received: Array<Pair<Float, Color>>,
val receivedQuote: Array<Pair<Float, Color>>,
val secondary: List<Pair<Float, Color>>,
val author: List<Pair<Float, Color>>,
val secondaryTint: Color,
val authorTint: Color,
)
enum class PresetWallpaper(
@@ -108,17 +109,8 @@ enum class PresetWallpaper(
0.84f to oklch(0.3700f, 0.0742f, 88f),
1.00f to oklch(0.4350f, 0.1049f, 105f),
),
secondary = listOf(
0.20f to oklch(0.5050f, 0.1072f, 88f),
0.55f to oklch(0.5350f, 0.1135f, 88f),
0.90f to oklch(0.7050f, 0.1200f, 88f),
),
author = listOf(
0.20f to oklch(0.6300f, 0.1367f, 88f),
0.55f to oklch(0.6450f, 0.1399f, 88f),
0.70f to oklch(0.7250f, 0.1100f, 88f),
0.90f to oklch(0.9923f, 0.0170f, 100f),
),
secondaryTint = oklch(0.8874f, 0.1270f, 86.2f, 0.6f),
authorTint = oklch(0.8518f, 0.0736f, 87.2f, 0.6f),
)
),
FLOWERS(MR.images.wallpaper_flowers, "flowers", 0.53f,
@@ -186,17 +178,8 @@ enum class PresetWallpaper(
0.84f to oklch(0.4130f, 0.1171f, 130f),
1.00f to oklch(0.5040f, 0.1217f, 113f),
),
secondary = listOf(
0.20f to oklch(0.5200f, 0.1320f, 130f),
0.55f to oklch(0.5500f, 0.1421f, 130f),
0.90f to oklch(0.7200f, 0.1218f, 130f),
),
author = listOf(
0.20f to oklch(0.6450f, 0.1523f, 130f),
0.55f to oklch(0.6600f, 0.1523f, 130f),
0.70f to oklch(0.7400f, 0.1117f, 130f),
0.90f to oklch(0.9923f, 0.0170f, 100f),
),
secondaryTint = oklch(0.8874f, 0.1270f, 130.0f, 0.6f),
authorTint = oklch(0.8518f, 0.0736f, 130.0f, 0.6f),
)
),
HEARTS(MR.images.wallpaper_hearts, "hearts", 0.59f,
@@ -264,17 +247,8 @@ enum class PresetWallpaper(
0.84f to oklch(0.4230f, 0.1319f, 5f),
1.00f to oklch(0.5140f, 0.1234f, 22f),
),
secondary = listOf(
0.20f to oklch(0.5300f, 0.1339f, 5f),
0.55f to oklch(0.5600f, 0.1442f, 5f),
0.90f to oklch(0.7300f, 0.1236f, 5f),
),
author = listOf(
0.20f to oklch(0.6550f, 0.1545f, 5f),
0.55f to oklch(0.6700f, 0.1545f, 5f),
0.70f to oklch(0.7500f, 0.1133f, 5f),
0.90f to oklch(0.9923f, 0.0170f, 100f),
),
secondaryTint = oklch(0.8874f, 0.1270f, 5.0f, 0.6f),
authorTint = oklch(0.8518f, 0.0736f, 5.0f, 0.6f),
)
),
KIDS(MR.images.wallpaper_kids, "kids", 0.53f,
@@ -342,17 +316,8 @@ enum class PresetWallpaper(
0.84f to oklch(0.4230f, 0.0819f, 200f),
1.00f to oklch(0.5140f, 0.1195f, 217f),
),
secondary = listOf(
0.20f to oklch(0.5300f, 0.1087f, 200f),
0.55f to oklch(0.5600f, 0.1149f, 200f),
0.90f to oklch(0.7300f, 0.1200f, 200f),
),
author = listOf(
0.20f to oklch(0.6550f, 0.1373f, 200f),
0.55f to oklch(0.6700f, 0.1405f, 200f),
0.70f to oklch(0.7500f, 0.1100f, 200f),
0.90f to oklch(0.9923f, 0.0170f, 100f),
),
secondaryTint = oklch(0.8874f, 0.1270f, 200.0f, 0.6f),
authorTint = oklch(0.8518f, 0.0736f, 200.0f, 0.6f),
)
),
SCHOOL(MR.images.wallpaper_school, "school", 0.53f,
@@ -420,17 +385,8 @@ enum class PresetWallpaper(
0.84f to oklch(0.4950f, 0.1346f, 271f),
1.00f to oklch(0.6250f, 0.1286f, 254f),
),
secondary = listOf(
0.20f to oklch(0.5600f, 0.1397f, 271f),
0.55f to oklch(0.5900f, 0.1505f, 271f),
0.90f to oklch(0.7600f, 0.1172f, 271f),
),
author = listOf(
0.20f to oklch(0.6850f, 0.1612f, 271f),
0.55f to oklch(0.7000f, 0.1562f, 271f),
0.70f to oklch(0.7800f, 0.1116f, 271f),
0.90f to oklch(0.9923f, 0.0170f, 100f),
),
secondaryTint = oklch(0.8874f, 0.1270f, 271.0f, 0.6f),
authorTint = oklch(0.8518f, 0.0736f, 271.0f, 0.6f),
)
),
TRAVEL(MR.images.wallpaper_travel, "travel", 0.68f,
@@ -498,17 +454,8 @@ enum class PresetWallpaper(
0.84f to oklch(0.4470f, 0.1328f, 315f),
1.00f to oklch(0.5510f, 0.1251f, 298f),
),
secondary = listOf(
0.20f to oklch(0.5400f, 0.1358f, 315f),
0.55f to oklch(0.5700f, 0.1463f, 315f),
0.90f to oklch(0.7400f, 0.1254f, 315f),
),
author = listOf(
0.20f to oklch(0.6650f, 0.1567f, 315f),
0.55f to oklch(0.6800f, 0.1567f, 315f),
0.70f to oklch(0.7600f, 0.1149f, 315f),
0.90f to oklch(0.9923f, 0.0170f, 100f),
),
secondaryTint = oklch(0.8874f, 0.1270f, 315.0f, 0.6f),
authorTint = oklch(0.8518f, 0.0736f, 315.0f, 0.6f),
)
);