From 1ac3d93f74ce4f8ecdf27fd6c30da3e79a40b880 Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Sun, 12 Apr 2026 16:08:12 +0500 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D1=8B=D0=B5=20=D0=BA=D0=BE=D0=BE=D1=80=D0=B4=D0=B8?= =?UTF-8?q?=D0=BD=D0=B0=D1=82=D1=8B=20text=20selection=20=E2=80=94=20windo?= =?UTF-8?q?w=E2=86=92overlay-local=20=D0=BA=D0=BE=D0=BD=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: overlay Canvas рисует в локальных координатах, но LayoutInfo возвращает позицию в window coordinates. Разница = position status bar, toolbar, и parent padding → highlight смещался вниз. Фикс: - onGloballyPositioned на overlay Box → знаем overlayWindowX/Y - Canvas: offsetX/Y = info.windowX - overlayWindowX (window→local) - getCharOffsetFromCoords: overlay-local → text-local через ту же delta - Handle positions теперь в overlay-local координатах → drag работает Co-Authored-By: Claude Opus 4.6 (1M context) --- .../chats/components/TextSelectionHelper.kt | 52 +++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/components/TextSelectionHelper.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/components/TextSelectionHelper.kt index 0c88ce0..5ab7aa9 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/components/TextSelectionHelper.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/components/TextSelectionHelper.kt @@ -19,6 +19,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf @@ -34,6 +35,8 @@ import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.layout.positionInWindow import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.font.FontWeight @@ -77,6 +80,10 @@ class TextSelectionHelper { var endHandleX by mutableFloatStateOf(0f) var endHandleY by mutableFloatStateOf(0f) + // Overlay position in window — set by TextSelectionOverlay + var overlayWindowX = 0f + var overlayWindowY = 0f + val isInSelectionMode: Boolean get() = isActive && selectionStart >= 0 && selectionEnd > selectionStart fun startSelection( @@ -199,13 +206,14 @@ class TextSelectionHelper { magnifier = null } - fun getCharOffsetFromCoords(x: Int, y: Int): Int { + fun getCharOffsetFromCoords(overlayLocalX: Int, overlayLocalY: Int): Int { val info = layoutInfo ?: return -1 - val localX = x - info.windowX - val localY = y - info.windowY + // overlay-local → text-local: subtract text position relative to overlay + val textLocalX = overlayLocalX - (info.windowX - overlayWindowX) + val textLocalY = overlayLocalY - (info.windowY - overlayWindowY) val layout = info.layout - val line = layout.getLineForVertical(localY.coerceIn(0, layout.height)) - val hx = localX.toFloat().coerceIn(layout.getLineLeft(line), layout.getLineRight(line)) + val line = layout.getLineForVertical(textLocalY.toInt().coerceIn(0, layout.height)) + val hx = textLocalX.toFloat().coerceIn(layout.getLineLeft(line), layout.getLineRight(line)) return layout.getOffsetForHorizontal(line, hx) } @@ -324,7 +332,15 @@ fun TextSelectionOverlay( val handleInsetPx = with(density) { HandleInset.toPx() } val highlightCornerPx = with(density) { HighlightCorner.toPx() } - Box(modifier = modifier.fillMaxSize()) { + Box( + modifier = modifier + .fillMaxSize() + .onGloballyPositioned { coords -> + val pos = coords.positionInWindow() + helper.overlayWindowX = pos.x + helper.overlayWindowY = pos.y + } + ) { FloatingToolbarPopup(helper = helper) Canvas( modifier = Modifier @@ -387,6 +403,10 @@ fun TextSelectionOverlay( val layout = info.layout val text = info.text + // Convert window coords to overlay-local coords + val offsetX = info.windowX - helper.overlayWindowX + val offsetY = info.windowY - helper.overlayWindowY + val startOffset = helper.selectionStart.coerceIn(0, text.length) val endOffset = helper.selectionEnd.coerceIn(0, text.length) if (startOffset >= endOffset) return@Canvas @@ -395,17 +415,17 @@ fun TextSelectionOverlay( val endLine = layout.getLineForOffset(endOffset) for (line in startLine..endLine) { - val lineTop = layout.getLineTop(line).toFloat() + info.windowY - val lineBottom = layout.getLineBottom(line).toFloat() + info.windowY + val lineTop = layout.getLineTop(line).toFloat() + offsetY + val lineBottom = layout.getLineBottom(line).toFloat() + offsetY val left = if (line == startLine) { - layout.getPrimaryHorizontal(startOffset) + info.windowX + layout.getPrimaryHorizontal(startOffset) + offsetX } else { - layout.getLineLeft(line) + info.windowX + layout.getLineLeft(line) + offsetX } val right = if (line == endLine) { - layout.getPrimaryHorizontal(endOffset) + info.windowX + layout.getPrimaryHorizontal(endOffset) + offsetX } else { - layout.getLineRight(line) + info.windowX + layout.getLineRight(line) + offsetX } drawRoundRect( color = HighlightColor, @@ -415,10 +435,10 @@ fun TextSelectionOverlay( ) } - val startHx = layout.getPrimaryHorizontal(startOffset) + info.windowX - val startHy = layout.getLineBottom(startLine).toFloat() + info.windowY - val endHx = layout.getPrimaryHorizontal(endOffset) + info.windowX - val endHy = layout.getLineBottom(endLine).toFloat() + info.windowY + val startHx = layout.getPrimaryHorizontal(startOffset) + offsetX + val startHy = layout.getLineBottom(startLine).toFloat() + offsetY + val endHx = layout.getPrimaryHorizontal(endOffset) + offsetX + val endHy = layout.getLineBottom(endLine).toFloat() + offsetY helper.startHandleX = startHx helper.startHandleY = startHy