Remove the drag-right-to-go-back gesture
The screen no longer follows a horizontal drag. Back is the arrow at the top left and the platform's own edge gesture, both unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9fdab777b4
commit
8881a40919
3 files changed
+11
-85
No files matched your search
@@ -198,21 +198,17 @@ fun AppRoot(
|
||||
// row key. Only reachable since a notification can move straight from one session to
|
||||
// another; every other way here passes through [Screen.Main], which disposes it anyway.
|
||||
key(here.summary.id) {
|
||||
// The gesture goes on a box around the screen rather than inside it, so it is the
|
||||
// outermost thing in the tree and everything within has already had its chance at
|
||||
// the drag. See [swipeBack]. No imePadding here, for the reason above.
|
||||
Box(Modifier.swipeBack(goToMain)) {
|
||||
SessionScreen(
|
||||
settings = current,
|
||||
summary = here.summary,
|
||||
onBack = goToMain,
|
||||
share = share,
|
||||
onShareTaken = { share = null },
|
||||
)
|
||||
}
|
||||
// No imePadding here, for the reason above.
|
||||
SessionScreen(
|
||||
settings = current,
|
||||
summary = here.summary,
|
||||
onBack = goToMain,
|
||||
share = share,
|
||||
onShareTaken = { share = null },
|
||||
)
|
||||
}
|
||||
is Screen.Spawn ->
|
||||
Box(Modifier.imePadding().swipeBack(goToMain)) {
|
||||
Box(Modifier.imePadding()) {
|
||||
SpawnScreen(
|
||||
settings = current,
|
||||
onSpawned = { spawned ->
|
||||
@@ -223,7 +219,7 @@ fun AppRoot(
|
||||
)
|
||||
}
|
||||
is Screen.Settings ->
|
||||
Box(Modifier.imePadding().swipeBack(goToMain)) {
|
||||
Box(Modifier.imePadding()) {
|
||||
SettingsScreen(
|
||||
existing = current,
|
||||
onSaved = { saved ->
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
package com.example.aiapp
|
||||
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.draggable
|
||||
import androidx.compose.foundation.gestures.rememberDraggableState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Dragging the screen to the right to step back to the one behind it.
|
||||
*
|
||||
* The platform's own back gesture is a swipe from the very edge, and only from there; on a phone
|
||||
* held in one hand the way back from a session is either that narrow strip or the arrow at the top
|
||||
* left, which is the far corner from the thumb. This is the same movement from anywhere on the
|
||||
* screen.
|
||||
*
|
||||
* **It loses every argument.** The gesture is a plain horizontal [draggable] on the outside of the
|
||||
* screen, so anything inside that wants horizontal drags has already taken them by the time this
|
||||
* would see them: pointer events reach the innermost node first, and a drag a child has consumed
|
||||
* never crosses this modifier's touch slop. That is what keeps a wide code fence, a table scrolled
|
||||
* sideways or a text selection working -- they are the components the reader meant, and this is
|
||||
* only what is left over. Vertical drags are not its orientation, so the transcript scrolls
|
||||
* untouched.
|
||||
*
|
||||
* The screen follows the finger rather than jumping at the end, because a gesture with no feedback
|
||||
* cannot be aborted: the reader has to be able to see it starting and change their mind. Released
|
||||
* short of [SWIPE_BACK_TRAVEL] it slides back and nothing happens. Right rather than left, and only
|
||||
* right, since there is nothing forward of these screens to go to.
|
||||
*/
|
||||
@Composable
|
||||
fun Modifier.swipeBack(onBack: () -> Unit): Modifier {
|
||||
val offset = remember { Animatable(0f) }
|
||||
val scope = rememberCoroutineScope()
|
||||
val travel = with(LocalDensity.current) { SWIPE_BACK_TRAVEL.toPx() }
|
||||
return draggable(
|
||||
state =
|
||||
rememberDraggableState { delta ->
|
||||
// Rightward only: a leftward drag stays at zero rather than lifting the
|
||||
// screen off its left edge, which would look like a gesture that does
|
||||
// something and does not.
|
||||
scope.launch { offset.snapTo((offset.value + delta).coerceAtLeast(0f)) }
|
||||
},
|
||||
orientation = Orientation.Horizontal,
|
||||
onDragStopped = {
|
||||
if (offset.value >= travel) {
|
||||
onBack()
|
||||
// Straight back rather than animated: the screen this was moving is being
|
||||
// replaced, and animating it home first would show the old one sliding back
|
||||
// into place after the new one had arrived.
|
||||
offset.snapTo(0f)
|
||||
} else {
|
||||
offset.animateTo(0f)
|
||||
}
|
||||
},
|
||||
)
|
||||
// Read inside the block, so following the finger is a draw-phase change and costs no
|
||||
// recomposition of the screen being dragged.
|
||||
.graphicsLayer { translationX = offset.value }
|
||||
}
|
||||
|
||||
/** How far the screen has to be pulled for letting go to mean "back" rather than "never mind". */
|
||||
private val SWIPE_BACK_TRAVEL: Dp = 96.dp
|
||||
Reference in new issue
Block a user