mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-15 07:28:30 +00:00
Calculate drag handle bounds using the container width instead of the screen width.
This commit is contained in:
@@ -31,8 +31,6 @@ import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
@@ -60,7 +58,6 @@ import org.signal.core.util.toInt
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.compose.ComposeFragment
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.ViewUtil
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
||||
/**
|
||||
@@ -127,8 +124,6 @@ fun FoldersScreen(
|
||||
onDeleteDismissed: () -> Unit = {},
|
||||
onDragAndDropEvent: (DragAndDropEvent) -> Unit = {}
|
||||
) {
|
||||
val screenWidth = LocalConfiguration.current.screenWidthDp.dp
|
||||
val isRtl = ViewUtil.isRtl(LocalContext.current)
|
||||
val listState = rememberLazyListState()
|
||||
val dragDropState = rememberDragDropState(listState, includeHeader = true, includeFooter = true, onEvent = onDragAndDropEvent)
|
||||
|
||||
@@ -153,8 +148,7 @@ fun FoldersScreen(
|
||||
LazyColumn(
|
||||
modifier = Modifier.dragContainer(
|
||||
dragDropState = dragDropState,
|
||||
leftDpOffset = if (isRtl) 0.dp else screenWidth - 48.dp,
|
||||
rightDpOffset = if (isRtl) 48.dp else screenWidth
|
||||
dragHandleWidth = 56.dp
|
||||
),
|
||||
state = listState
|
||||
) {
|
||||
@@ -220,6 +214,7 @@ fun FoldersScreen(
|
||||
onAdd = { onAdd(chatFolder) }
|
||||
)
|
||||
}
|
||||
|
||||
ChatFolderRecord.FolderType.INDIVIDUAL -> {
|
||||
val title: String = stringResource(R.string.ChatFoldersFragment__one_on_one_chats)
|
||||
FolderRow(
|
||||
@@ -229,6 +224,7 @@ fun FoldersScreen(
|
||||
onAdd = { onAdd(chatFolder) }
|
||||
)
|
||||
}
|
||||
|
||||
ChatFolderRecord.FolderType.GROUP -> {
|
||||
val title: String = stringResource(R.string.ChatFoldersFragment__groups)
|
||||
FolderRow(
|
||||
@@ -238,9 +234,11 @@ fun FoldersScreen(
|
||||
onAdd = { onAdd(chatFolder) }
|
||||
)
|
||||
}
|
||||
|
||||
ChatFolderRecord.FolderType.ALL -> {
|
||||
error("All chats should not be suggested")
|
||||
}
|
||||
|
||||
ChatFolderRecord.FolderType.CUSTOM -> {
|
||||
error("Custom folders should not be suggested")
|
||||
}
|
||||
|
||||
@@ -40,8 +40,6 @@ import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
@@ -154,8 +152,6 @@ private fun CreatePollScreen(
|
||||
var focusedOption by remember { mutableStateOf(-1) }
|
||||
|
||||
// Drag and drop
|
||||
val screenWidth = LocalConfiguration.current.screenWidthDp.dp
|
||||
val isRtl = ViewUtil.isRtl(LocalContext.current)
|
||||
val listState = rememberLazyListState()
|
||||
val dragDropState = rememberDragDropState(listState, includeHeader = true, includeFooter = true, onEvent = { event ->
|
||||
when (event) {
|
||||
@@ -164,6 +160,7 @@ private fun CreatePollScreen(
|
||||
options[event.fromIndex] = options[event.toIndex]
|
||||
options[event.toIndex] = oldIndex
|
||||
}
|
||||
|
||||
is DragAndDropEvent.OnItemDrop, is DragAndDropEvent.OnDragCancel -> Unit
|
||||
}
|
||||
})
|
||||
@@ -208,8 +205,7 @@ private fun CreatePollScreen(
|
||||
.imePadding()
|
||||
.dragContainer(
|
||||
dragDropState = dragDropState,
|
||||
leftDpOffset = if (isRtl) 0.dp else screenWidth - 56.dp,
|
||||
rightDpOffset = if (isRtl) 56.dp else screenWidth
|
||||
dragHandleWidth = 56.dp
|
||||
),
|
||||
state = listState
|
||||
) {
|
||||
|
||||
@@ -49,17 +49,14 @@ import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
@@ -484,8 +481,6 @@ private fun InstalledStickersContent(
|
||||
val listState = rememberLazyListState()
|
||||
val dragDropState = rememberDragDropState(lazyListState = listState, includeHeader = true, includeFooter = false, onEvent = callbacks::onDragAndDropEvent)
|
||||
|
||||
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
val screenWidth = LocalConfiguration.current.screenWidthDp.dp
|
||||
val density = LocalDensity.current
|
||||
val haptics = LocalHapticFeedback.current
|
||||
|
||||
@@ -503,8 +498,7 @@ private fun InstalledStickersContent(
|
||||
.fillMaxHeight()
|
||||
.dragContainer(
|
||||
dragDropState = dragDropState,
|
||||
leftDpOffset = if (isRtl) 0.dp else screenWidth - 56.dp,
|
||||
rightDpOffset = if (isRtl) 56.dp else screenWidth
|
||||
dragHandleWidth = 56.dp
|
||||
)
|
||||
) {
|
||||
item(key = "installed_section_header") {
|
||||
|
||||
@@ -23,7 +23,9 @@ import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.pointer.PointerInputChange
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.zIndex
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
@@ -161,14 +163,11 @@ internal constructor(
|
||||
}
|
||||
draggingItemIndex = targetItem.index
|
||||
} else {
|
||||
val overscroll =
|
||||
when {
|
||||
draggingItemDraggedDelta > 0 ->
|
||||
(endOffset - state.layoutInfo.viewportEndOffset).coerceAtLeast(0f)
|
||||
draggingItemDraggedDelta < 0 ->
|
||||
(startOffset - state.layoutInfo.viewportStartOffset).coerceAtMost(0f)
|
||||
else -> 0f
|
||||
}
|
||||
val overscroll = when {
|
||||
draggingItemDraggedDelta > 0 -> (endOffset - state.layoutInfo.viewportEndOffset).coerceAtLeast(0f)
|
||||
draggingItemDraggedDelta < 0 -> (startOffset - state.layoutInfo.viewportStartOffset).coerceAtMost(0f)
|
||||
else -> 0f
|
||||
}
|
||||
if (overscroll != 0f) {
|
||||
scrollChannel.trySend(overscroll)
|
||||
}
|
||||
@@ -198,21 +197,34 @@ sealed interface DragAndDropEvent {
|
||||
data object OnDragCancel : DragAndDropEvent
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables drag-to-reorder functionality within a container.
|
||||
*
|
||||
* @param dragDropState The state managing the drag operation.
|
||||
* @param dragHandleWidth Width of the draggable area (positioned at the end of the container).
|
||||
*/
|
||||
@Composable
|
||||
fun Modifier.dragContainer(
|
||||
dragDropState: DragDropState,
|
||||
leftDpOffset: Dp,
|
||||
rightDpOffset: Dp
|
||||
dragHandleWidth: Dp
|
||||
): Modifier {
|
||||
return pointerInput(dragDropState) {
|
||||
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
return pointerInput(dragDropState, dragHandleWidth, isRtl) {
|
||||
val containerWidthPx = size.width.toFloat()
|
||||
val handleWidthPx = dragHandleWidth.toPx()
|
||||
|
||||
val dragHandleXRange = if (isRtl) {
|
||||
0f..handleWidthPx
|
||||
} else {
|
||||
(containerWidthPx - handleWidthPx)..containerWidthPx
|
||||
}
|
||||
|
||||
detectDragGestures(
|
||||
onDrag = { change, offset ->
|
||||
dragDropState.onDrag(offset = offset, change = change)
|
||||
},
|
||||
dragHandleXRange = dragHandleXRange,
|
||||
onDrag = { change, offset -> dragDropState.onDrag(offset = offset, change = change) },
|
||||
onDragStart = { offset -> dragDropState.onDragStart(offset) },
|
||||
onDragEnd = { dragDropState.onDragEnd() },
|
||||
onDragCancel = { dragDropState.onDragCancel() },
|
||||
leftDpOffset = leftDpOffset,
|
||||
rightDpOffset = rightDpOffset
|
||||
onDragCancel = { dragDropState.onDragCancel() }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -227,11 +239,13 @@ fun LazyItemScope.DraggableItem(
|
||||
val dragging = index == dragDropState.draggingItemIndex
|
||||
val draggingModifier =
|
||||
if (dragging) {
|
||||
Modifier.zIndex(1f).graphicsLayer { translationY = dragDropState.draggingItemOffset }
|
||||
Modifier
|
||||
.zIndex(1f)
|
||||
.graphicsLayer { translationY = dragDropState.draggingItemOffset }
|
||||
} else if (index == dragDropState.previousIndexOfDraggedItem) {
|
||||
Modifier.zIndex(1f).graphicsLayer {
|
||||
translationY = dragDropState.previousItemOffset.value
|
||||
}
|
||||
Modifier
|
||||
.zIndex(1f)
|
||||
.graphicsLayer { translationY = dragDropState.previousItemOffset.value }
|
||||
} else {
|
||||
Modifier.animateItem(fadeInSpec = null, fadeOutSpec = null)
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@ import androidx.compose.ui.input.pointer.changedToUp
|
||||
import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
|
||||
import androidx.compose.ui.input.pointer.isOutOfBounds
|
||||
import androidx.compose.ui.input.pointer.positionChange
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastAll
|
||||
import androidx.compose.ui.util.fastAny
|
||||
import androidx.compose.ui.util.fastFirstOrNull
|
||||
@@ -25,21 +23,22 @@ import kotlinx.coroutines.CancellationException
|
||||
|
||||
/**
|
||||
* Modified version of detectDragGesturesAfterLongPress from [androidx.compose.foundation.gestures.DragGestureDetector]
|
||||
* that allows you to optionally offset the starting and ending position of the draggable area
|
||||
* that initiates drags when the touch starts within a specified x coordinate range.
|
||||
*
|
||||
* @param dragHandleXRange The x coordinate range (in pixels) where drags can be initiated.
|
||||
*/
|
||||
suspend fun PointerInputScope.detectDragGestures(
|
||||
dragHandleXRange: ClosedFloatingPointRange<Float>,
|
||||
onDragStart: (Offset) -> Unit = { },
|
||||
onDragEnd: () -> Unit = { },
|
||||
onDragCancel: () -> Unit = { },
|
||||
onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit,
|
||||
leftDpOffset: Dp = 0.dp,
|
||||
rightDpOffset: Dp
|
||||
onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit
|
||||
) {
|
||||
awaitEachGesture {
|
||||
try {
|
||||
val down = awaitFirstDown(requireUnconsumed = false)
|
||||
val drag = awaitLongPressOrCancellation(down.id)
|
||||
if (drag != null && (drag.position.x > leftDpOffset.toPx()) && (drag.position.x < rightDpOffset.toPx())) {
|
||||
if (drag != null && down.position.x in dragHandleXRange) {
|
||||
onDragStart.invoke(drag.position)
|
||||
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user