From 2650f57e4f0e9fed3b548828f584444a891bf517 Mon Sep 17 00:00:00 2001 From: another-simple-pixel Date: Tue, 7 Jul 2026 17:18:35 -0700 Subject: [PATCH] Show the wallpaper gradient on bubbles in the theme preview The wallpaper-picker preview already draws the SIMPLEX sunrise gradient as its background, but the sample message bubbles stayed flat: bubbles anchor their gradient to the chat viewport, which the preview never provided. Give the preview its own viewport (with the same expanded axis the preview background uses), so the sample bubbles show the same gradient they do in a real chat, aligned with the background. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../simplex/common/ui/theme/SimplexBrushes.kt | 12 +++++++++--- .../common/views/usersettings/Appearance.kt | 18 +++++++++++------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/SimplexBrushes.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/SimplexBrushes.kt index 17c6b5c21d..254875f263 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/SimplexBrushes.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/SimplexBrushes.kt @@ -29,15 +29,16 @@ private const val SIMPLEX_TILT_DEG = 20f class ChatViewportInfo( val sizePx: State, val originInWindow: State, + val previewMode: Boolean = false, ) val LocalChatViewportInfo = compositionLocalOf { null } @Composable -fun rememberChatViewportInfo(): Pair { +fun rememberChatViewportInfo(previewMode: Boolean = false): Pair { val sizePx = remember { mutableStateOf(IntSize.Zero) } val originInWindow = remember { mutableStateOf(Offset.Zero) } - val info = remember { ChatViewportInfo(sizePx, originInWindow) } + val info = remember { ChatViewportInfo(sizePx, originInWindow, previewMode) } val mod = Modifier .onSizeChanged { sizePx.value = it } .onGloballyPositioned { originInWindow.value = it.positionInWindow() } @@ -97,7 +98,12 @@ fun Modifier.simplexBubbleBackground(slot: SimplexBubbleSlot): Modifier = compos .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 (bl, tr) = axisEndpoints(sz.width.toFloat(), sz.height.toFloat()) + // The preview swatch stretches the axis (same as simplexGradient()'s previewMode in + // ChatWallpaper.kt) so bubbles show the same representative slice as the wallpaper background. + val span = tr - bl + val axisStartLocal = if (viewport.previewMode) bl - span else bl + val axisEndLocal = if (viewport.previewMode) tr + span * 2f else tr val viewportOrigin = viewport.originInWindow.value val offset = viewportOrigin - bubblePos.value val brush = Brush.linearGradient( diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt index 5ae5a64cec..2b61f735a0 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt @@ -279,6 +279,7 @@ object AppearanceScope { val themeBackgroundColor = MaterialTheme.colors.background val backgroundColor = backgroundColor ?: wallpaperType?.defaultBackgroundColor(theme, MaterialTheme.colors.background) val tintColor = tintColor ?: wallpaperType?.defaultTintColor(theme) + val (previewViewport, previewViewportModifier) = rememberChatViewportInfo(previewMode = true) Column(Modifier .drawWithCache { if (wallpaperImage != null && wallpaperType != null && backgroundColor != null && tintColor != null) { @@ -289,19 +290,22 @@ object AppearanceScope { } } } + .then(previewViewportModifier) .padding(DEFAULT_PADDING_HALF) ) { if (withMessages) { val chatItemTail = remember { appPreferences.chatItemTail.state } - Column(verticalArrangement = Arrangement.spacedBy(4.dp), modifier = if (chatItemTail.value) Modifier else Modifier.padding(horizontal = msgTailWidthDp)) { - val alice = remember { ChatItem.getSampleData(1, CIDirection.DirectRcv(), Clock.System.now(), generalGetString(MR.strings.wallpaper_preview_hello_bob)) } - PreviewChatItemView(alice) - PreviewChatItemView( - ChatItem.getSampleData(2, CIDirection.DirectSnd(), Clock.System.now(), stringResource(MR.strings.wallpaper_preview_hello_alice), - quotedItem = CIQuote(alice.chatDir, alice.id, sentAt = alice.meta.itemTs, formattedText = alice.formattedText, content = MsgContent.MCText(alice.content.text)) + CompositionLocalProvider(LocalChatViewportInfo provides previewViewport) { + Column(verticalArrangement = Arrangement.spacedBy(4.dp), modifier = if (chatItemTail.value) Modifier else Modifier.padding(horizontal = msgTailWidthDp)) { + val alice = remember { ChatItem.getSampleData(1, CIDirection.DirectRcv(), Clock.System.now(), generalGetString(MR.strings.wallpaper_preview_hello_bob)) } + PreviewChatItemView(alice) + PreviewChatItemView( + ChatItem.getSampleData(2, CIDirection.DirectSnd(), Clock.System.now(), stringResource(MR.strings.wallpaper_preview_hello_alice), + quotedItem = CIQuote(alice.chatDir, alice.id, sentAt = alice.meta.itemTs, formattedText = alice.formattedText, content = MsgContent.MCText(alice.content.text)) + ) ) - ) + } } } else { Box(Modifier.fillMaxSize())