fix: посимвольное выделение + magnifier на позиции handle + haptic на каждый символ

Было: word snap при drag handle → нельзя выделить часть слова
Стало: посимвольно при drag (word snap только при первом long press)

Magnifier: показывается на позиции handle (текущий символ),
а не на позиции пальца. По Y — центр строки текста.

Haptic: TEXT_HANDLE_MOVE на каждый символ (не на каждое слово).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-12 16:17:32 +05:00
parent 78925dd61d
commit 9fe5f35923

View File

@@ -124,8 +124,7 @@ class TextSelectionHelper {
fun updateSelectionStart(charOffset: Int) {
if (!isActive) return
val text = layoutInfo?.text ?: return
var newStart = charOffset.coerceIn(0, text.length)
while (newStart > 0 && Character.isLetterOrDigit(text[newStart - 1])) newStart--
val newStart = charOffset.coerceIn(0, text.length)
if (newStart >= selectionEnd) return
val changed = newStart != selectionStart
selectionStart = newStart
@@ -135,8 +134,7 @@ class TextSelectionHelper {
fun updateSelectionEnd(charOffset: Int) {
if (!isActive) return
val text = layoutInfo?.text ?: return
var newEnd = charOffset.coerceIn(0, text.length)
while (newEnd < text.length && Character.isLetterOrDigit(text[newEnd])) newEnd++
val newEnd = charOffset.coerceIn(0, text.length)
if (newEnd <= selectionStart) return
val changed = newEnd != selectionEnd
selectionEnd = newEnd
@@ -201,7 +199,7 @@ class TextSelectionHelper {
.setSize(240, 64)
.setCornerRadius(12f)
.setElevation(4f)
.setDefaultSourceToMagnifierOffset(0, -80)
.setDefaultSourceToMagnifierOffset(0, -96)
.build()
} else {
@Suppress("DEPRECATION")
@@ -209,11 +207,21 @@ class TextSelectionHelper {
}
}
val info = layoutInfo ?: return
// Convert overlay-local → view-local (magnifierView coords)
// Magnifier should show at the HANDLE position (current char), not finger
// Use handle X for horizontal, and line center for vertical
val handleX = if (movingHandleStart) startHandleX else endHandleX
val handleY = if (movingHandleStart) startHandleY else endHandleY
val activeOffset = if (movingHandleStart) selectionStart else selectionEnd
val layout = info.layout
val line = layout.getLineForOffset(activeOffset.coerceIn(0, info.text.length))
val lineCenter = (layout.getLineTop(line) + layout.getLineBottom(line)) / 2f
// Convert to view-local coordinates
val viewLoc = IntArray(2)
view.getLocationInWindow(viewLoc)
val sourceX = (overlayLocalX + overlayWindowX - viewLoc[0]).coerceIn(0f, view.width.toFloat())
val sourceY = (overlayLocalY + overlayWindowY - viewLoc[1]).coerceIn(0f, view.height.toFloat())
val sourceX = (handleX + overlayWindowX - viewLoc[0]).coerceIn(0f, view.width.toFloat())
val sourceY = (lineCenter + info.windowY - viewLoc[1]).coerceIn(0f, view.height.toFloat())
magnifier?.show(sourceX, sourceY)
}