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 381ab42ed8..89d0c1e957 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 @@ -6,13 +6,9 @@ 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 @@ -31,12 +27,6 @@ import kotlin.math.sin 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 { null } class ChatViewportInfo( @@ -74,23 +64,6 @@ private fun axisEndpoints(winW: Float, winH: Float): Pair { return start to end } -private fun sampleStops(stops: List>, 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. */ @@ -169,37 +142,9 @@ fun Modifier.chatBubbleBackground( ) } -// 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). +// Static tinted-transparency for small text and default avatars on the SIMPLEX gradient. A +// semi-transparent warm colour blends with the gradient at every height, so it stays uniformly +// legible without sampling the axis by screen position (only bubbles 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 @@ -213,28 +158,3 @@ 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 = simplexAxisSampledColor(LINK_STOPS, MaterialTheme.colors.primary) - -@Composable -private fun simplexAxisSampledColor(stops: List>?, fallback: Color): Pair { - 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 -} diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index 13543be1f0..bbc4b5ae60 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -2064,10 +2064,8 @@ fun BoxScope.ChatItemsList( } Row(Modifier.graphicsLayer { translationX = selectionOffset.toPx() }) { val member = cItem.chatDir.groupMember - 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.clickable { showMemberInfo(chatInfo.groupInfo, member) }) { + MemberImage(member, color = if (CurrentColors.value.base == DefaultTheme.SIMPLEX && member.image == null) simplexSecondaryTint() else MaterialTheme.colors.secondaryVariant) } Box(modifier = Modifier.padding(top = 2.dp, start = 4.dp).chatItemOffset(cItem, itemSeparation.largeGap, revealed = revealed.value)) { ChatItemViewShortHand(cItem, itemSeparation, range, false, dismissState.offset.value) @@ -2299,9 +2297,7 @@ fun BoxScope.ChatItemsList( .padding(top = DEFAULT_PADDING_HALF) .let { if (CurrentColors.value.base == DefaultTheme.SIMPLEX) it else it.background(MaterialTheme.appColors.receivedMessage) } ) { - 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)) - } + ChatInfoImage(chatInfo, size = alertProfileImageSize, iconColor = if (CurrentColors.value.base == DefaultTheme.SIMPLEX && chatInfo.image == null) simplexSecondaryTint() else MaterialTheme.colors.secondaryVariant.mixWith(MaterialTheme.colors.onBackground, 0.97f)) val bannerBadge = chatInfo.nameBadge val uriHandler = LocalUriHandler.current Text( @@ -2820,8 +2816,8 @@ private suspend fun preloadItemsAfter( val MEMBER_IMAGE_SIZE: Dp = 37.dp @Composable -fun MemberImage(member: GroupMember) { - MemberProfileImage(MEMBER_IMAGE_SIZE * fontSizeSqrtMultiplier, member, backgroundColor = MaterialTheme.colors.background) +fun MemberImage(member: GroupMember, color: Color = MaterialTheme.colors.secondaryVariant) { + MemberProfileImage(MEMBER_IMAGE_SIZE * fontSizeSqrtMultiplier, member, color = color, backgroundColor = MaterialTheme.colors.background) } @Composable diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt index 737a5158cf..b6d70c8337 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt @@ -55,16 +55,12 @@ fun CIFileView( ) { 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 - ) - } + Icon( + painterResource(MR.images.ic_draft_filled), + stringResource(MR.strings.icon_descr_file), + Modifier.fillMaxSize(), + tint = if (isSimplex && isDefaultTint) simplexSecondaryTint() else color + ) if (innerIcon != null) { Icon( innerIcon, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIGroupInvitationView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIGroupInvitationView.kt index 6f1f569717..b92e68d233 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIGroupInvitationView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIGroupInvitationView.kt @@ -53,15 +53,11 @@ fun CIGroupInvitationView( 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) ) { - Box(avatarOverlayMod) { - ProfileImage(size = 54.dp, image = groupInvitation.groupProfile.image, icon = MR.images.ic_supervised_user_circle_filled, color = iconColor) - } + ProfileImage(size = 54.dp, image = groupInvitation.groupProfile.image, icon = MR.images.ic_supervised_user_circle_filled, color = if (isSimplex && groupInvitation.groupProfile.image == null) simplexSecondaryTint() else iconColor) Spacer(Modifier.width(8.dp)) Column( Modifier.defaultMinSize(minHeight = 54.dp), diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt index 4751ef27ec..8c2aa7d330 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt @@ -207,13 +207,11 @@ 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 (linkColor, linkModifier) = simplexLinkColor() Box(Modifier .clipChatItem(ci, tailVisible, revealed = true) - .chatBubbleBackground(sent = sent, isQuote = false, transparent = transparentBackground) - .then(linkModifier)) { + .chatBubbleBackground(sent = sent, isQuote = false, transparent = transparentBackground)) { var metaColor: Color? = null - CompositionLocalProvider(LocalSimplexLinkColor provides linkColor) { + CompositionLocalProvider(LocalSimplexLinkColor provides MaterialTheme.colors.primary) { Box(contentAlignment = Alignment.BottomEnd) { val chatItemTail = remember { appPreferences.chatItemTail.state } val style = shapeStyle(ci, chatItemTail.value, tailVisible, true)