Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Magnifier for iOS 17+ #1000

Merged
merged 20 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.PointerInputScope
import androidx.compose.ui.util.fastAny
import kotlin.coroutines.cancellation.CancellationException
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -84,6 +85,12 @@ internal suspend fun PointerInputScope.detectDownAndDragGesturesWithObserver(
}
launch(start = CoroutineStart.UNDISPATCHED) {
detectDragGesturesWithObserver(observer)
}.invokeOnCompletion {
// Otherwise observer won't be notified if
// composable was disposed before the drag cancellation
if (it is CancellationException){
observer.onCancel()
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,13 @@ internal class TextFieldSelectionManager(
* when the dragging is stopped.
*/
var draggingHandle: Handle? by mutableStateOf(null)
private set
internal set

/**
* The current position of a drag, in decoration box coordinates.
*/
var currentDragPosition: Offset? by mutableStateOf(null)
private set
internal set

/**
* The previous offset of a drag, before selection adjustments.
Expand Down Expand Up @@ -451,7 +451,10 @@ internal class TextFieldSelectionManager(
updateFloatingToolbar(show = true)
}

override fun onCancel() {}
override fun onCancel() {
draggingHandle = null
currentDragPosition = null
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 The Android Open Source Project
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ internal fun Modifier.cupertinoTextFieldPointer(
): Modifier = if (enabled) {
// TODO switch to ".updateSelectionTouchMode { state.isInTouchMode = it }" as in defaultTextFieldPointer
if (isInTouchMode) {
val longPressHandlerModifier = getLongPressHandlerModifier(state, offsetMapping)
val longPressHandlerModifier = getLongPressHandlerModifier(state, offsetMapping, manager)
val tapHandlerModifier = getTapHandlerModifier(
interactionSource,
state,
Expand Down Expand Up @@ -110,7 +110,7 @@ private fun getTapHandlerModifier(
if (currentState.handleState != HandleState.Selection) {
currentState.layoutResult?.let { layoutResult ->
// TODO: Research native behavior with any text transformations (which adds symbols like with using NSNumberFormatter)
if (manager.visualTransformation != VisualTransformation.None) {
if (currentManager.visualTransformation != VisualTransformation.None) {
TextFieldDelegate.setCursorOffset(
touchPointOffset,
layoutResult,
Expand All @@ -126,7 +126,7 @@ private fun getTapHandlerModifier(
offsetMapping = currentOffsetMapping,
showContextMenu = {
// it shouldn't be selection, but this is a way to call context menu in BasicTextField
manager.enterSelectionMode(true)
currentManager.enterSelectionMode(true)
},
onValueChange = currentState.onValueChange
)
Expand Down Expand Up @@ -175,10 +175,12 @@ private fun getTapHandlerModifier(
@Composable
private fun getLongPressHandlerModifier(
state: TextFieldState,
offsetMapping: OffsetMapping
offsetMapping: OffsetMapping,
manager: TextFieldSelectionManager,
ASalavei marked this conversation as resolved.
Show resolved Hide resolved
): Modifier {
val currentState by rememberUpdatedState(state)
val currentOffsetMapping by rememberUpdatedState(offsetMapping)
val currentManager by rememberUpdatedState(manager)

return Modifier.pointerInput(Unit) {
val longTapActionsObserver =
Expand All @@ -187,6 +189,9 @@ private fun getLongPressHandlerModifier(
var dragBeginOffset = Offset.Zero

override fun onStart(startPoint: Offset) {
currentManager.draggingHandle = Handle.SelectionEnd // required for magnifier
alexzhirkevich marked this conversation as resolved.
Show resolved Hide resolved
currentManager.currentDragPosition = startPoint // required for magnifier

currentState.layoutResult?.let { layoutResult ->
TextFieldDelegate.setCursorOffset(
startPoint,
Expand All @@ -204,6 +209,7 @@ private fun getLongPressHandlerModifier(
dragTotalDistance += delta
currentState.layoutResult?.let { layoutResult ->
val currentDragPosition = dragBeginOffset + dragTotalDistance
currentManager.currentDragPosition = currentDragPosition // required for magnifier
TextFieldDelegate.setCursorOffset(
currentDragPosition,
layoutResult,
Expand All @@ -219,9 +225,15 @@ private fun getLongPressHandlerModifier(

override fun onUp() {}

override fun onStop() {}
override fun onStop() {
currentManager.draggingHandle = null // required for magnifier
currentManager.currentDragPosition = null // required for magnifier
}

override fun onCancel() {}
override fun onCancel() {
currentManager.draggingHandle = null // required for magnifier
currentManager.currentDragPosition = null // required for magnifier
}
}

detectDragGesturesAfterLongPress(
Expand Down
Loading