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 487ec527c4..f36c824b99 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 @@ -148,6 +148,7 @@ fun ChatView( val showCommandsMenu = rememberSaveable { mutableStateOf(false) } val contentFilter = rememberSaveable { mutableStateOf(null) } val availableContent = remember { mutableStateOf>(ContentFilter.initialList) } + val selectionManager = if (appPlatform.isDesktop) remember { SelectionManager() } else null if (appPlatform.isAndroid) { DisposableEffect(Unit) { @@ -178,6 +179,7 @@ fun ChatView( contentFilter.value = null availableContent.value = ContentFilter.initialList selectedChatItems.value = null + selectionManager?.clearSelection() val cInfo = activeChat.value?.chatInfo if (chatsCtx.secondaryContextFilter == null && (cInfo is ChatInfo.Direct || cInfo is ChatInfo.Group || cInfo is ChatInfo.Local)) { updateAvailableContent(chatRh, activeChat, availableContent) @@ -227,6 +229,7 @@ fun ChatView( val clipboard = LocalClipboardManager.current CompositionLocalProvider( LocalAppBarHandler provides rememberAppBarHandler(chatInfo.id, keyboardCoversBar = false), + LocalSelectionManager provides selectionManager, ) { when (chatInfo) { is ChatInfo.Direct, is ChatInfo.Group, is ChatInfo.Local -> { @@ -962,7 +965,7 @@ fun ChatLayout( val composeViewFocusRequester = remember { if (appPlatform.isDesktop) FocusRequester() else null } AdaptingBottomPaddingLayout(Modifier, CHAT_COMPOSE_LAYOUT_ID, composeViewHeight) { if (chat != null) { - val selectionManager = if (appPlatform.isDesktop) remember { SelectionManager() } else null + val selectionManager = LocalSelectionManager.current if (selectionManager != null) { LaunchedEffect(selectionManager) { snapshotFlow { selectionManager.selectionState != SelectionState.Idle } @@ -972,7 +975,6 @@ fun ChatLayout( Box(Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) { // disables scrolling to top of chat item on click inside the bubble CompositionLocalProvider( - LocalSelectionManager provides selectionManager, LocalBringIntoViewSpec provides object : BringIntoViewSpec { override fun calculateScrollDistance(offset: Float, size: Float, containerSize: Float): Float = 0f } @@ -1132,6 +1134,16 @@ fun ChatLayout( } } } + // Desktop selection copy button — last child of outer Box, on top of everything + if (appPlatform.isDesktop) { + val manager = LocalSelectionManager.current + if (manager != null && manager.selectionState == SelectionState.Selected && manager.onCopySelection != null) { + SelectionCopyButton( + modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = composeViewHeight.value + 8.dp), + onCopy = { manager.onCopySelection?.invoke() } + ) + } + } } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/TextSelection.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/TextSelection.kt index d7be9258a0..4d35386bd2 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/TextSelection.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/TextSelection.kt @@ -64,6 +64,7 @@ class SelectionManager { private set var focusWindowY by mutableStateOf(0f) var focusWindowX by mutableStateOf(0f) + var onCopySelection: (() -> Unit)? = null fun startSelection(startIndex: Int, anchorY: Float, anchorX: Float) { range = SelectionRange(startIndex, -1, startIndex, -1) @@ -168,13 +169,8 @@ fun BoxScope.SelectionHandler( } } - // Copy button - if (manager.selectionState == SelectionState.Selected) { - SelectionCopyButton( - onCopy = { - clipboard.setText(AnnotatedString(manager.getSelectedText(mergedItems.value.items, linkMode))) - } - ) + manager.onCopySelection = { + clipboard.setText(AnnotatedString(manager.getSelectedText(mergedItems.value.items, linkMode))) } return Modifier @@ -186,7 +182,7 @@ fun BoxScope.SelectionHandler( && event.key == Key.C && event.type == KeyEventType.KeyDown ) { - clipboard.setText(AnnotatedString(manager.getSelectedText(mergedItems.value.items, linkMode))) + manager.onCopySelection?.invoke() true } else false } @@ -298,11 +294,9 @@ private fun resolveIndexAtY(listState: LazyListState, localY: Float): Int? { } @Composable -private fun BoxScope.SelectionCopyButton(onCopy: () -> Unit) { +fun SelectionCopyButton(modifier: Modifier = Modifier, onCopy: () -> Unit) { Row( - Modifier - .align(Alignment.BottomCenter) - .padding(bottom = 8.dp) + modifier .background(MaterialTheme.colors.surface, RoundedCornerShape(20.dp)) .border(1.dp, MaterialTheme.colors.onSurface.copy(alpha = 0.12f), RoundedCornerShape(20.dp)) .clickable { onCopy() }