Skip to content

Commit

Permalink
Merge pull request #13 from Mr-Pine/development
Browse files Browse the repository at this point in the history
Version 1.2.1
  • Loading branch information
Mr-Pine authored Mar 31, 2023
2 parents 34075c3 + ad5a746 commit b068511
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 71 deletions.
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ plugins {
alias(libs.plugins.android.library) apply false
alias(libs.plugins.dokka) apply false
kotlin("android") version libs.versions.kotlin.get() apply false
id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
alias(libs.plugins.nexus.publish)
}

tasks.create<Delete>("clean") {
delete(rootProject.buildDir)
}

nexusPublishing {
repositoryDescription.set("zoomables:$version")
this.repositories {
sonatype {
stagingProfileId.set(publishData.sonatypeStagingProfileId)
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/artifact.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
val artifact = Artifact(
group = "de.mr-pine.utils",
id = "zoomables",
version = "1.2.0"
version = "1.2.1"
)
4 changes: 3 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ ktx = "1.9.0"
library = "7.4.2"
application = "7.4.2"
kotlin = "1.8.10"
nexusPublish = "1.3.0"

[libraries]
androidx-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "ktx" }
Expand All @@ -15,4 +16,5 @@ compose-material = { group = "androidx.compose.material", name = "material", ver
[plugins]
android-library = { id = "com.android.library", version.ref = "library" }
android-application = { id = "com.android.application", version.ref = "application" }
dokka = { id = "org.jetbrains.dokka", version.ref = "kotlin" }
dokka = { id = "org.jetbrains.dokka", version.ref = "kotlin" }
nexus-publish = {id = "io.github.gradle-nexus.publish-plugin", version.ref = "nexusPublish"}
176 changes: 108 additions & 68 deletions zoomables/src/main/kotlin/de/mr_pine/zoomables/Zoomables.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package de.mr_pine.zoomables

import android.util.Log
import androidx.compose.foundation.gestures.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
Expand All @@ -12,10 +13,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.positionChange
import androidx.compose.ui.input.pointer.positionChanged
import androidx.compose.ui.input.pointer.*
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.util.fastAny
Expand All @@ -24,6 +22,8 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlin.math.*

private const val TAG = "Zoomables"

/**
* Creates a composable that wraps a given [Composable] and adds zoom, pan, rotation, double tap and swipe functionality
*
Expand Down Expand Up @@ -131,82 +131,101 @@ public fun Zoomable(
var pan = Offset.Zero
var pastTouchSlop = false
val touchSlop = viewConfiguration.touchSlop
var lockedToPanZoom = false
var drag: PointerInputChange?
var dragChange: PointerInputChange?
var overSlop = Offset.Zero
var lockedToPanZoom = false

var transformEventCounter = 0
do {
val event = awaitPointerEvent()
val canceled = event.changes.fastAny { it.isConsumed }
var event = awaitPointerEvent()
var canceled = false
var relevant = true
if (event.changes.size > 1) {
if (!canceled) {
val zoomChange = event.calculateZoom()
val rotationChange = event.calculateRotation()
val panChange = event.calculatePan()

if (!pastTouchSlop) {
zoom *= zoomChange
transformRotation += rotationChange
pan += panChange
var transformEventCounter = 0
while (!canceled && event.changes.fastAny { it.pressed } && relevant) {
canceled = event.changes.fastAny { it.isConsumed }
relevant = true
if (event.changes.size > 1) {
if (!canceled) {
val zoomChange = event.calculateZoom()
val rotationChange = event.calculateRotation()
val panChange = event.calculatePan()

val centroidSize =
event.calculateCentroidSize(useCurrent = false)
val zoomMotion = abs(1 - zoom) * centroidSize
val rotationMotion =
abs(transformRotation * PI.toFloat() * centroidSize / 180f)
val panMotion = pan.getDistance()
if (!pastTouchSlop) {
zoom *= zoomChange
transformRotation += rotationChange
pan += panChange

if (zoomMotion > touchSlop || rotationMotion > touchSlop || panMotion > touchSlop) {
pastTouchSlop = true
lockedToPanZoom =
zoomableState.rotationBehavior == ZoomableState.Rotation.LOCK_ROTATION_ON_ZOOM_PAN && rotationMotion < touchSlop
}
}
val centroidSize =
event.calculateCentroidSize(useCurrent = false)
val zoomMotion = abs(1 - zoom) * centroidSize
val rotationMotion =
abs(transformRotation * PI.toFloat() * centroidSize / 180f)
val panMotion = pan.getDistance()

if (pastTouchSlop) {
val eventCentroid = event.calculateCentroid(useCurrent = false)
val effectiveRotation =
if (lockedToPanZoom) 0f else rotationChange
if (effectiveRotation != 0f || zoomChange != 1f || panChange != Offset.Zero) {
onTransformGesture(
eventCentroid, panChange, zoomChange, effectiveRotation
)
if (zoomMotion > touchSlop || rotationMotion > touchSlop || panMotion > touchSlop) {
pastTouchSlop = true
lockedToPanZoom =
zoomableState.rotationBehavior == ZoomableState.Rotation.LOCK_ROTATION_ON_ZOOM_PAN && rotationMotion < touchSlop
}
}
event.changes.fastForEach {
if (it.positionChanged()) {
it.consume()

if (pastTouchSlop) {
val eventCentroid =
event.calculateCentroid(useCurrent = false)
val effectiveRotation =
if (lockedToPanZoom) 0f else rotationChange
if (effectiveRotation != 0f || zoomChange != 1f || panChange != Offset.Zero) {
onTransformGesture(
eventCentroid,
panChange,
zoomChange,
effectiveRotation
)
}
event.changes.fastForEach {
if (it.positionChanged()) {
it.consume()
}
}
}
}
}
} else if (transformEventCounter > 3) relevant = false
transformEventCounter++
} while (!canceled && event.changes.fastAny { it.pressed } && relevant)
} else if (transformEventCounter > 3) relevant = false

if (zoomableState.dragGestureMode() != DragGestureMode.DISABLED) {
do {
val event = awaitPointerEvent()
drag = event.changes.firstOrNull()?.id?.let { pointerId ->
transformEventCounter++
event = awaitPointerEvent()
}

if (zoomableState.dragGestureMode() != DragGestureMode.DISABLED) {
dragChange = event.changes.firstOrNull()?.id?.let { pointerId ->
awaitTouchSlopOrCancellation(pointerId) { change, over ->
if (change.positionChange() != Offset.Zero) change.consume()
overSlop = over
}
}
} while (drag != null && !drag.isConsumed)
if (drag != null) {
dragOffset = Offset.Zero
when (zoomableState.dragGestureMode()) {
DragGestureMode.PAN -> coroutineScope.launch {
zoomableState.transform {
transformBy(1f, overSlop, 0f)

while (dragChange != null && !dragChange.isConsumed) {
dragChange = event.changes.firstOrNull()?.id?.let { pointerId ->
awaitTouchSlopOrCancellation(pointerId) { change, over ->
if (change.positionChange() != Offset.Zero) change.consume()
overSlop = over
}
}
DragGestureMode.SWIPE_GESTURES -> dragOffset += overSlop
else -> {}
event = awaitPointerEvent()
}
if (drag(drag.id) {
if (dragChange != null) {
dragOffset = Offset.Zero
when (zoomableState.dragGestureMode()) {
DragGestureMode.PAN -> coroutineScope.launch {
zoomableState.transform {
transformBy(1f, overSlop, 0f)
}
}
DragGestureMode.SWIPE_GESTURES -> dragOffset += overSlop
else -> {}
}
val dragSuccessful = conditionalDrag(
dragChange.id,
condition = { currentEvent.changes.size < 2 }) {
when (zoomableState.dragGestureMode()) {
DragGestureMode.PAN -> {
zoomableState.offset.value += it.positionChange()
Expand All @@ -215,19 +234,21 @@ public fun Zoomable(
else -> {}
}
if (it.positionChange() != Offset.Zero) it.consume()
}) {
if (zoomableState.dragGestureMode() == DragGestureMode.SWIPE_GESTURES) {
val offsetX = dragOffset.x
if (offsetX > minimumSwipeDistance) {
onSwipeRight()

} else if (offsetX < -minimumSwipeDistance) {
onSwipeLeft()
}
if (dragSuccessful) {
if (zoomableState.dragGestureMode() == DragGestureMode.SWIPE_GESTURES) {
val offsetX = dragOffset.x
if (offsetX > minimumSwipeDistance) {
onSwipeRight()
} else if (offsetX < -minimumSwipeDistance) {
onSwipeLeft()
}
}
}
}
}
}
Log.d(TAG, "Zoomable: ${currentEvent.changes.map(PointerInputChange::isConsumed)}")
} while (currentEvent.changes.any { !it.isConsumed && !it.changedToUp()})
}
}) {
Box(
Expand Down Expand Up @@ -257,4 +278,23 @@ public fun Zoomable(
Content()
}
}
}

private suspend fun AwaitPointerEventScope.conditionalDrag(
pointerId: PointerId,
condition: AwaitPointerEventScope.() -> Boolean,
onDrag: (PointerInputChange) -> Unit
): Boolean {
var pointer = pointerId
while (condition()) {
val change = awaitDragOrCancellation(pointer) ?: return false

if (change.changedToUpIgnoreConsumed()) {
return true
}

onDrag(change)
pointer = change.id
}
return true
}

0 comments on commit b068511

Please sign in to comment.