mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-11-10 09:48:14 +00:00
desktop: fix dark layering and reply line in flat layout
This commit is contained in:
1 parent
8aa342061c
commit
b3146d1e40
4 files changed
+76
-37
No files matched your search
@@ -454,7 +454,7 @@ private fun DesktopScreenLayout(userPickerState: MutableStateFlow<AnimatedViewSt
|
||||
UserPicker(chatModel, userPickerState, setPerformLA = AppLock::setPerformLA)
|
||||
}
|
||||
}
|
||||
if (LocalFlatLayout.current) FlatListSurface { StartPart() } else StartPart()
|
||||
if (LocalFlatLayout.current) FlatSurface(flatListColor()) { StartPart() } else StartPart()
|
||||
}
|
||||
Box(Modifier.padding(start = railWidth).widthIn(max = DEFAULT_START_MODAL_WIDTH * fontSizeSqrtMultiplier)) {
|
||||
ModalManager.start.showInView()
|
||||
@@ -462,7 +462,7 @@ private fun DesktopScreenLayout(userPickerState: MutableStateFlow<AnimatedViewSt
|
||||
}
|
||||
Row(Modifier.padding(start = railWidth + DEFAULT_START_MODAL_WIDTH * fontSizeSqrtMultiplier).clipToBounds()) {
|
||||
Box(Modifier.widthIn(min = DEFAULT_MIN_CENTER_MODAL_WIDTH).weight(1f)) {
|
||||
CenterPartOfScreen()
|
||||
if (LocalFlatLayout.current) FlatSurface(flatChatColor()) { CenterPartOfScreen() } else CenterPartOfScreen()
|
||||
}
|
||||
if (ModalManager.end.hasModalsOpen()) {
|
||||
VerticalDivider()
|
||||
|
||||
+46
-22
@@ -2,6 +2,8 @@ package chat.simplex.common.views.chat.item
|
||||
|
||||
import chat.simplex.common.views.chat.group.MemberProfileImage
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.interaction.collectIsHoveredAsState
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
@@ -221,23 +223,52 @@ fun FramedItemView(
|
||||
}
|
||||
}
|
||||
|
||||
fun openQuotedItem(qi: CIQuote) {
|
||||
val itemId = qi.itemId
|
||||
if (itemId != null) {
|
||||
if (ci.isReport && chatsCtx.secondaryContextFilter != null) {
|
||||
scrollToItemId.value = itemId
|
||||
} else {
|
||||
scrollToItem(itemId)
|
||||
}
|
||||
} else {
|
||||
scrollToQuotedItemFromItem(ci.id)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ciReplyAvatar(qi: CIQuote) {
|
||||
when (val dir = qi.chatDir) {
|
||||
is CIDirection.GroupRcv -> MemberProfileImage(FLAT_REPLY_AVATAR_SIZE, dir.groupMember)
|
||||
is CIDirection.GroupSnd -> membership()?.let { MemberProfileImage(FLAT_REPLY_AVATAR_SIZE, it) }
|
||||
is CIDirection.ChannelRcv,
|
||||
is CIDirection.DirectRcv -> ChatInfoImage(chatInfo, FLAT_REPLY_AVATAR_SIZE)
|
||||
is CIDirection.DirectSnd,
|
||||
is CIDirection.LocalSnd,
|
||||
is CIDirection.LocalRcv -> ProfileImage(FLAT_REPLY_AVATAR_SIZE, chatModel.currentUser.value?.image)
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ciReplyLine(qi: CIQuote) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val color = if (interactionSource.collectIsHoveredAsState().value) MaterialTheme.colors.onBackground else MaterialTheme.colors.secondary
|
||||
Row(
|
||||
Modifier.padding(start = FLAT_TEXT_START_PADDING, top = FLAT_TEXT_VERTICAL_PADDING, end = 12.dp),
|
||||
Modifier
|
||||
.padding(start = FLAT_TEXT_START_PADDING, top = FLAT_TEXT_VERTICAL_PADDING, end = 12.dp)
|
||||
.combinedClickable(interactionSource = interactionSource, indication = null, onLongClick = { showMenu.value = true }, onClick = { openQuotedItem(qi) })
|
||||
.onRightClick { showMenu.value = true },
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(painterResource(MR.images.ic_reply), null, Modifier.size(FLAT_REPLY_ICON_SIZE), tint = MaterialTheme.colors.secondary)
|
||||
val quotedDir = qi.chatDir
|
||||
if (quotedDir is CIDirection.GroupRcv) {
|
||||
MemberProfileImage(FLAT_REPLY_AVATAR_SIZE, quotedDir.groupMember)
|
||||
}
|
||||
Icon(painterResource(MR.images.ic_reply), null, Modifier.size(FLAT_REPLY_ICON_SIZE), tint = color)
|
||||
ciReplyAvatar(qi)
|
||||
val sender = qi.sender(membership())
|
||||
if (sender != null) {
|
||||
Text(sender, fontSize = FLAT_REPLY_FONT_SIZE, fontWeight = FontWeight.SemiBold, color = MaterialTheme.colors.secondary, maxLines = 1)
|
||||
Text(sender, fontSize = FLAT_REPLY_FONT_SIZE, fontWeight = FontWeight.SemiBold, color = color, maxLines = 1)
|
||||
}
|
||||
val replyStyle = TextStyle(fontSize = FLAT_REPLY_FONT_SIZE, color = MaterialTheme.colors.secondary)
|
||||
val replyStyle = TextStyle(fontSize = FLAT_REPLY_FONT_SIZE, color = color)
|
||||
Box(Modifier.weight(1f, fill = false)) {
|
||||
when {
|
||||
qi.text.isNotEmpty() -> ciQuotedMsgTextView(qi, lines = 1, showTimestamp = false, style = replyStyle)
|
||||
@@ -263,7 +294,7 @@ fun FramedItemView(
|
||||
|
||||
val sentColor = MaterialTheme.appColors.sentMessage
|
||||
val receivedColor = MaterialTheme.appColors.receivedMessage
|
||||
Box(Modifier
|
||||
Column(Modifier
|
||||
.clipChatItem(ci, tailVisible, revealed = true)
|
||||
.background(
|
||||
when {
|
||||
@@ -273,6 +304,9 @@ fun FramedItemView(
|
||||
}
|
||||
)) {
|
||||
var metaColor = MaterialTheme.colors.secondary
|
||||
if (flat && ci.quotedItem != null) {
|
||||
ciReplyLine(ci.quotedItem)
|
||||
}
|
||||
Box(contentAlignment = Alignment.BottomEnd) {
|
||||
val style = shapeStyle(ci, chatItemTailEnabled(), tailVisible, true)
|
||||
val tailRendered = style is ShapeStyle.Bubble && style.tailVisible
|
||||
@@ -319,27 +353,17 @@ fun FramedItemView(
|
||||
FramedItemHeader(stringResource(MR.strings.live), false)
|
||||
}
|
||||
}
|
||||
if (ci.quotedItem != null) {
|
||||
if (ci.quotedItem != null && !flat) {
|
||||
Column(
|
||||
Modifier
|
||||
.combinedClickable(
|
||||
onLongClick = { showMenu.value = true },
|
||||
onClick = {
|
||||
if (ci.quotedItem.itemId != null) {
|
||||
if (ci.isReport && chatsCtx.secondaryContextFilter != null) {
|
||||
scrollToItemId.value = ci.quotedItem.itemId
|
||||
} else {
|
||||
scrollToItem(ci.quotedItem.itemId)
|
||||
}
|
||||
} else {
|
||||
scrollToQuotedItemFromItem(ci.id)
|
||||
}
|
||||
}
|
||||
onClick = { openQuotedItem(ci.quotedItem) }
|
||||
)
|
||||
.onRightClick { showMenu.value = true }
|
||||
) {
|
||||
Header()
|
||||
if (flat) ciReplyLine(ci.quotedItem) else ciQuoteView(ci.quotedItem)
|
||||
ciQuoteView(ci.quotedItem)
|
||||
}
|
||||
} else {
|
||||
Header()
|
||||
|
||||
+25
-12
@@ -40,31 +40,43 @@ private val RAIL_ACTIVE_MARK_WIDTH = 4.dp
|
||||
private val RAIL_ACTIVE_BACKGROUND_WIDTH = 40.dp
|
||||
private val RAIL_ACTIVE_BACKGROUND_HEIGHT = 32.dp
|
||||
private val RAIL_UNREAD_DOT_SIZE = 6.dp
|
||||
private const val RAIL_ACTIVE_BACKGROUND_ALPHA = 0.12f
|
||||
private const val RAIL_ACTIVE_BACKGROUND_ALPHA = 0.1f
|
||||
private val RAIL_ITEM_INSET = 6.dp
|
||||
private const val RAIL_TILE_CORNER_PERCENT = 30
|
||||
private const val RAIL_TILE_CIRCLE_PERCENT = 50
|
||||
private const val RAIL_TILE_ALPHA = 0.15f
|
||||
private const val RAIL_MARK_ACTIVE_FRACTION = 0.8f
|
||||
private const val RAIL_MARK_HOVER_FRACTION = 0.4f
|
||||
// background darkening of the rail and the chat list; dark themes need more for a visible step
|
||||
// surfaces step from the darkest rail to the lightest chat: light themes darken the rail and the list,
|
||||
// dark themes darken the rail and lift the chat, since their background is already near black
|
||||
private const val FLAT_RAIL_SHADE_LIGHT = 0.07f
|
||||
private const val FLAT_RAIL_SHADE_DARK = 0.4f
|
||||
private const val FLAT_LIST_SHADE_LIGHT = 0.035f
|
||||
private const val FLAT_LIST_SHADE_DARK = 0.2f
|
||||
private const val FLAT_RAIL_SHADE_DARK = 0.5f
|
||||
private const val FLAT_CHAT_LIFT_DARK = 0.045f
|
||||
// raises placeholder avatar contrast on the shaded surfaces while keeping the theme color
|
||||
private const val FLAT_PLACEHOLDER_CONTRAST = 0.15f
|
||||
|
||||
@Composable
|
||||
fun flatRailColor(): Color = flatShade(if (MaterialTheme.colors.isLight) FLAT_RAIL_SHADE_LIGHT else FLAT_RAIL_SHADE_DARK)
|
||||
fun flatRailColor(): Color =
|
||||
MaterialTheme.colors.background.mixWith(Color.Black, 1 - if (MaterialTheme.colors.isLight) FLAT_RAIL_SHADE_LIGHT else FLAT_RAIL_SHADE_DARK)
|
||||
|
||||
@Composable
|
||||
fun flatListColor(): Color = flatShade(if (MaterialTheme.colors.isLight) FLAT_LIST_SHADE_LIGHT else FLAT_LIST_SHADE_DARK)
|
||||
fun flatListColor(): Color =
|
||||
if (MaterialTheme.colors.isLight) MaterialTheme.colors.background.mixWith(Color.Black, 1 - FLAT_LIST_SHADE_LIGHT) else MaterialTheme.colors.background
|
||||
|
||||
@Composable
|
||||
private fun flatShade(amount: Float): Color = MaterialTheme.colors.background.mixWith(Color.Black, 1 - amount)
|
||||
fun flatChatColor(): Color =
|
||||
if (MaterialTheme.colors.isLight) MaterialTheme.colors.background else MaterialTheme.colors.background.mixWith(Color.White, 1 - FLAT_CHAT_LIFT_DARK)
|
||||
|
||||
@Composable
|
||||
fun FlatListSurface(content: @Composable () -> Unit) {
|
||||
MaterialTheme(colors = MaterialTheme.colors.copy(background = flatListColor()), typography = MaterialTheme.typography, shapes = MaterialTheme.shapes, content = content)
|
||||
fun FlatSurface(background: Color, content: @Composable () -> Unit) {
|
||||
val colors = MaterialTheme.colors
|
||||
MaterialTheme(
|
||||
colors = colors.copy(background = background, secondaryVariant = colors.secondaryVariant.mixWith(colors.onBackground, 1 - FLAT_PLACEHOLDER_CONTRAST)),
|
||||
typography = MaterialTheme.typography,
|
||||
shapes = MaterialTheme.shapes,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
// Spec: spec/client/chat-list.md#DesktopRail
|
||||
@@ -130,7 +142,8 @@ private fun RailUsers(userPickerState: MutableStateFlow<AnimatedViewState>) {
|
||||
else -> 0.dp
|
||||
}
|
||||
)
|
||||
val corner by animateIntAsState(if (user.activeUser || hovered) RAIL_TILE_CORNER_PERCENT else RAIL_TILE_CIRCLE_PERCENT)
|
||||
val baseCorner = remember { appPreferences.profileImageCornerRadius.state }.value.toInt().coerceIn(0, RAIL_TILE_CIRCLE_PERCENT)
|
||||
val corner by animateIntAsState(if (user.activeUser || hovered) minOf(baseCorner, RAIL_TILE_CORNER_PERCENT) else baseCorner)
|
||||
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
|
||||
Box(
|
||||
Modifier
|
||||
@@ -238,7 +251,7 @@ private fun RailItem(
|
||||
enabled: Boolean = true,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
val color = if (active) MaterialTheme.colors.primary else MaterialTheme.colors.secondary
|
||||
val color = if (active) MaterialTheme.colors.onBackground else MaterialTheme.colors.secondary
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -252,7 +265,7 @@ private fun RailItem(
|
||||
Box(
|
||||
Modifier
|
||||
.size(RAIL_ACTIVE_BACKGROUND_WIDTH * fontSizeSqrtMultiplier, RAIL_ACTIVE_BACKGROUND_HEIGHT * fontSizeSqrtMultiplier)
|
||||
.background(if (active) MaterialTheme.colors.primary.copy(alpha = RAIL_ACTIVE_BACKGROUND_ALPHA) else Color.Transparent, RoundedCornerShape(DEFAULT_PADDING_HALF)),
|
||||
.background(if (active) MaterialTheme.colors.onBackground.copy(alpha = RAIL_ACTIVE_BACKGROUND_ALPHA) else Color.Transparent, RoundedCornerShape(DEFAULT_PADDING_HALF)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (emoji != null) {
|
||||
|
||||
+3
-1
@@ -30,6 +30,8 @@ object NoIndication : IndicationNodeFactory {
|
||||
private val FLAT_ROW_INSET = 6.dp
|
||||
private val FLAT_ROW_GAP = 1.dp
|
||||
private val FLAT_ROW_CORNER = 6.dp
|
||||
// twice the hover tint, so the selected chat stands out from the hovered one
|
||||
private const val FLAT_SELECTED_ALPHA = 0.1f
|
||||
|
||||
@Composable
|
||||
actual fun ChatListNavLinkLayout(
|
||||
@@ -59,7 +61,7 @@ actual fun ChatListNavLinkLayout(
|
||||
chatLinkPreview()
|
||||
}
|
||||
if (selectedChat.value) {
|
||||
Box(Modifier.matchParentSize().background(MaterialTheme.colors.onBackground.copy(0.05f)))
|
||||
Box(Modifier.matchParentSize().background(MaterialTheme.colors.onBackground.copy(if (flat) FLAT_SELECTED_ALPHA else 0.05f)))
|
||||
}
|
||||
if (dropdownMenuItems != null) {
|
||||
DefaultDropdownMenu(showMenu, dropdownMenuItems = dropdownMenuItems)
|
||||
|
||||
Reference in new issue
Block a user