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

Compose 1.4, AGP 8.0, refactored forEachGesture with awaitEachGesture #257

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
12 changes: 6 additions & 6 deletions android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ plugins {

dependencies {
implementation(project(":reorderable"))
implementation("androidx.compose.runtime:runtime:1.3.3")
implementation("androidx.compose.material:material:1.3.1")
implementation("androidx.activity:activity-compose:1.6.1")
implementation("androidx.compose.runtime:runtime:1.4.2")
implementation("androidx.compose.material:material:1.4.2")
implementation("androidx.activity:activity-compose:1.7.1")
implementation("com.google.android.material:material:1.8.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
implementation("androidx.navigation:navigation-compose:2.5.3")
implementation("io.coil-kt:coil-compose:2.2.2")
}
Expand All @@ -38,7 +38,7 @@ android {
}

kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "11"
}
namespace = "org.burnoutcrew.android"
}
8 changes: 4 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
plugins {
`maven-publish`
id("com.android.library") version "7.4.0" apply false
id("org.jetbrains.kotlin.multiplatform") version "1.8.0" apply false
id("org.jetbrains.kotlin.android") version "1.8.0" apply false
id("org.jetbrains.compose") version "1.3.0" apply false
id("com.android.library") version "8.0.0" apply false
id("org.jetbrains.kotlin.multiplatform") version "1.8.20" apply false
id("org.jetbrains.kotlin.android") version "1.8.20" apply false
id("org.jetbrains.compose") version "1.4.0" apply false
}

ext {
Expand Down
6 changes: 5 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ kotlin.code.style=official
org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"
android.useAndroidX=true

org.jetbrains.compose.experimental.jscanvas.enabled=true
org.jetbrains.compose.experimental.jscanvas.enabled=true

android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
Expand Up @@ -15,46 +15,44 @@
*/
package org.burnoutcrew.reorderable

import androidx.compose.foundation.gestures.awaitDragOrCancellation
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.gestures.awaitLongPressOrCancellation
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.AwaitPointerEventScope
import androidx.compose.ui.input.pointer.PointerId
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInWindow

fun Modifier.detectReorder(state: ReorderableState<*>) =
this.then(
Modifier.pointerInput(Unit) {
forEachGesture {
awaitPointerEventScope {
val down = awaitFirstDown(requireUnconsumed = false)
var drag: PointerInputChange?
var overSlop = Offset.Zero
do {
drag = awaitPointerSlopOrCancellation(down.id, down.type) { change, over ->
change.consume()
overSlop = over
}
} while (drag != null && !drag.isConsumed)
if (drag != null) {
state.interactions.trySend(StartDrag(down.id, overSlop))
}
}
}
}
)


fun Modifier.detectReorderAfterLongPress(state: ReorderableState<*>) =
this.then(
Modifier.pointerInput(Unit) {
forEachGesture {
val down = awaitPointerEventScope {
awaitFirstDown(requireUnconsumed = false)
}
awaitLongPressOrCancellation(down)?.also {
state.interactions.trySend(StartDrag(down.id))
}
fun Modifier.detectReorder(state: ReorderableState<*>) = detect(state){

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fun Modifier.detectReorder(state: ReorderableState<*>): Modifier = composed {
    var itemPosition by remember { mutableStateOf(Offset.Zero) }
    Modifier.onGloballyPositioned {
        itemPosition = it.positionInWindow()
    }.pointerInput(Unit) {
        detectDragGestures(
            onDragStart = { offset ->
                val relativePosition = itemPosition - state.layoutWindowPosition + offset
                state.onDragStart(relativePosition.x.toInt(), relativePosition.y.toInt())
            },
            onDrag = { change, dragAmount ->
                change.consume()
                state.onDrag(dragAmount.x.toInt(), dragAmount.y.toInt())
            },
            onDragCancel = state::onDragCanceled,
            onDragEnd = state::onDragCanceled
        )
    }
}

awaitDragOrCancellation(it)
}

fun Modifier.detectReorderAfterLongPress(state: ReorderableState<*>) = detect(state) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fun Modifier.detectReorderAfterLongPress(state: ReorderableState<*>): Modifier = composed {
    var itemPosition by remember { mutableStateOf(Offset.Zero) }
    Modifier.onGloballyPositioned {
        itemPosition = it.positionInWindow()
    }.pointerInput(Unit) {
        detectDragGesturesAfterLongPress(
            onDragStart = { offset ->
                val relativePosition = itemPosition - state.layoutWindowPosition + offset
                state.onDragStart(relativePosition.x.toInt(), relativePosition.y.toInt())
            },
            onDrag = { change, dragAmount ->
                change.consume()
                state.onDrag(dragAmount.x.toInt(), dragAmount.y.toInt())
            },
            onDragCancel = state::onDragCanceled,
            onDragEnd = state::onDragCanceled
        )
    }
}

awaitLongPressOrCancellation(it)
}


private fun Modifier.detect(state: ReorderableState<*>, detect: suspend AwaitPointerEventScope.(PointerId)->PointerInputChange?) = composed {

val itemPosition = remember { mutableStateOf(Offset.Zero) }

Modifier.onGloballyPositioned { itemPosition.value = it.positionInWindow() }.pointerInput(Unit) {
awaitEachGesture {
val down = awaitFirstDown(requireUnconsumed = false)
val start = detect(down.id)

if (start != null) {
val relativePosition = itemPosition.value - state.layoutWindowPosition.value + start.position
state.onDragStart(relativePosition.x.toInt(), relativePosition.y.toInt())
}
}
)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,69 +15,38 @@
*/
package org.burnoutcrew.reorderable

import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.drag
import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.PointerId
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.PointerInputScope
import androidx.compose.ui.input.pointer.changedToUp
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.positionChange
import androidx.compose.ui.util.fastFirstOrNull
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInWindow

fun Modifier.reorderable(
state: ReorderableState<*>
) = then(
Modifier.pointerInput(Unit) {
forEachGesture {
val dragStart = state.interactions.receive()
val down = awaitPointerEventScope {
currentEvent.changes.fastFirstOrNull { it.id == dragStart.id }
}
if (down != null && state.onDragStart(down.position.x.toInt(), down.position.y.toInt())) {
dragStart.offset?.apply {
state.onDrag(x.toInt(), y.toInt())
Modifier.onGloballyPositioned { state.layoutWindowPosition.value = it.positionInWindow()}.pointerInput(Unit) {
Copy link

@serbelga serbelga Nov 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fun Modifier.reorderable(
    state: ReorderableState<*>
): Modifier = this then Modifier.onGloballyPositioned {
    state.layoutWindowPosition = it.positionInWindow()
}

awaitEachGesture {
val down = awaitFirstDown(requireUnconsumed = false)

val dragResult = drag(down.id) {
if (state.draggingItemIndex != null){
state.onDrag(it.positionChange().x.toInt(), it.positionChange().y.toInt())
it.consume()
}
detectDrag(
down.id,
onDragEnd = {
state.onDragCanceled()
},
onDragCancel = {
state.onDragCanceled()
},
onDrag = { change, dragAmount ->
change.consume()
state.onDrag(dragAmount.x.toInt(), dragAmount.y.toInt())
})
}
}
})

internal suspend fun PointerInputScope.detectDrag(
down: PointerId,
onDragEnd: () -> Unit = { },
onDragCancel: () -> Unit = { },
onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit,
) {
awaitPointerEventScope {
if (
drag(down) {
onDrag(it, it.positionChange())
it.consume()
}
) {
// consume up if we quit drag gracefully with the up
currentEvent.changes.forEach {
if (it.changedToUp()) it.consume()
if (dragResult) {
// consume up if we quit drag gracefully with the up
currentEvent.changes.forEach {
if (it.changedToUp()) it.consume()
}
}
onDragEnd()
} else {
onDragCancel()

state.onDragCanceled()
}
}
}

internal data class StartDrag(val id: PointerId, val offset: Offset? = null)
)
Loading