refactor 2

This commit is contained in:
Evgeny @ SimpleX Chat
2026-04-02 06:42:33 +00:00
parent f75e4fc266
commit 9d289c8c96
3 changed files with 41 additions and 43 deletions

View File

@@ -14,7 +14,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.*
import androidx.compose.ui.draw.*
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.layer.GraphicsLayer
import androidx.compose.ui.layout.layoutId
@@ -1137,19 +1136,7 @@ fun ChatLayout(
}
// Desktop selection copy button — last child of outer Box, on top of everything
if (appPlatform.isDesktop) {
val manager = LocalSelectionManager.current
val range = manager?.range
if (manager != null && manager.selectionState == SelectionState.Selected && range != null && manager.focusCharRect != Rect.Zero) {
val draggingDown = range.startIndex > range.endIndex || (range.startIndex == range.endIndex && range.startOffset < range.endOffset)
val gap = with(LocalDensity.current) { 4.dp.toPx() }
var buttonSize by remember { mutableStateOf(IntSize.Zero) }
SelectionCopyButton(
modifier = Modifier
.offset { manager.copyButtonOffset(draggingDown, gap, buttonSize) }
.onSizeChanged { buttonSize = it },
onCopy = { manager.onCopySelection?.invoke() }
)
}
SelectionCopyButton()
}
}
}

View File

@@ -23,8 +23,10 @@ import androidx.compose.ui.input.key.*
import androidx.compose.ui.input.pointer.*
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextLayoutResult
@@ -404,14 +406,48 @@ fun setupItemSelection(selectionManager: SelectionManager?, selectionIndex: Int,
return ItemSelection(highlightRange, positionModifier, onTextLayoutResult)
}
// Sets up full-item selection for emoji items (no character-level tracking).
@Composable
fun SelectionCopyButton(modifier: Modifier = Modifier, onCopy: () -> Unit) {
fun setupEmojiSelection(selectionManager: SelectionManager?, selectionIndex: Int, textLength: Int): Boolean {
if (selectionManager == null || selectionIndex < 0) return false
val isAnchor = remember(selectionIndex) {
derivedStateOf { selectionManager.range?.startIndex == selectionIndex && selectionManager.selectionState == SelectionState.Selecting }
}
LaunchedEffect(isAnchor.value) {
if (!isAnchor.value) return@LaunchedEffect
selectionManager.setAnchorOffset(0)
}
val isFocus = remember(selectionIndex) {
derivedStateOf { selectionManager.range?.endIndex == selectionIndex && selectionManager.selectionState == SelectionState.Selecting }
}
if (isFocus.value) {
LaunchedEffect(Unit) {
snapshotFlow { selectionManager.focusWindowY }
.collect { selectionManager.updateFocusOffset(textLength) }
}
}
return remember(selectionIndex) { derivedStateOf { selectedRange(selectionManager.range, selectionIndex) != null } }.value
}
@Composable
fun SelectionCopyButton() {
val manager = LocalSelectionManager.current ?: return
val range = manager.range ?: return
if (manager.selectionState != SelectionState.Selected || manager.focusCharRect == Rect.Zero) return
val draggingDown = range.startIndex > range.endIndex || (range.startIndex == range.endIndex && range.startOffset < range.endOffset)
val gap = with(LocalDensity.current) { 4.dp.toPx() }
var buttonSize by remember { mutableStateOf(IntSize.Zero) }
Row(
modifier
Modifier
.offset { manager.copyButtonOffset(draggingDown, gap, buttonSize) }
.onSizeChanged { buttonSize = it }
.background(MaterialTheme.colors.surface, RoundedCornerShape(20.dp))
.border(1.dp, MaterialTheme.colors.onSurface.copy(alpha = 0.12f), RoundedCornerShape(20.dp))
.clip(RoundedCornerShape(20.dp))
.clickable { onCopy() }
.clickable { manager.onCopySelection?.invoke() }
.padding(horizontal = 16.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {

View File

@@ -22,33 +22,8 @@ val mediumEmojiFont: TextStyle = TextStyle(fontSize = 36.sp, fontFamily = EmojiF
@Composable
fun EmojiItemView(chatItem: ChatItem, timedMessagesTTL: Int?, showViaProxy: Boolean, showTimestamp: Boolean) {
val selectionManager = LocalSelectionManager.current
val selectionIndex = LocalItemContext.current.selectionIndex
val emojiText = chatItem.content.text.trim()
if (selectionManager != null && selectionIndex >= 0) {
val isAnchor = remember(selectionIndex) {
derivedStateOf { selectionManager.range?.startIndex == selectionIndex && selectionManager.selectionState == SelectionState.Selecting }
}
LaunchedEffect(isAnchor.value) {
if (!isAnchor.value) return@LaunchedEffect
selectionManager.setAnchorOffset(0)
}
val isFocus = remember(selectionIndex) {
derivedStateOf { selectionManager.range?.endIndex == selectionIndex && selectionManager.selectionState == SelectionState.Selecting }
}
if (isFocus.value) {
LaunchedEffect(Unit) {
snapshotFlow { selectionManager.focusWindowY }
.collect { selectionManager.updateFocusOffset(emojiText.length) }
}
}
}
val isSelected = if (selectionManager != null && selectionIndex >= 0) {
remember(selectionIndex) { derivedStateOf { selectedRange(selectionManager.range, selectionIndex) != null } }.value
} else false
val isSelected = setupEmojiSelection(LocalSelectionManager.current, LocalItemContext.current.selectionIndex, emojiText.length)
Column(
Modifier.padding(vertical = 8.dp, horizontal = 12.dp),