From d7ed8c4d2c4be5cdedc63d171393b3930680ed31 Mon Sep 17 00:00:00 2001 From: vladcipariu Date: Fri, 15 Sep 2023 17:16:17 +0100 Subject: [PATCH 1/4] adds isContinuous flag to Gesture to control whether extra drag is treated as a new operation or simply discarded --- .../puzzle15/ui/Puzzle15MotionController.kt | 14 ++++--- .../ui/parallax/BackStackParallax.kt | 1 + .../backstack/ui/stack3d/BackStack3D.kt | 3 +- .../model/progress/DragProgressController.kt | 39 +++++++++++++------ .../interactions/core/ui/gesture/Gesture.kt | 9 +++++ 5 files changed, 48 insertions(+), 18 deletions(-) diff --git a/appyx-components/experimental/puzzle15/common/src/commonMain/kotlin/com/bumble/appyx/components/experimental/puzzle15/ui/Puzzle15MotionController.kt b/appyx-components/experimental/puzzle15/common/src/commonMain/kotlin/com/bumble/appyx/components/experimental/puzzle15/ui/Puzzle15MotionController.kt index e51aa973f..70fa56788 100644 --- a/appyx-components/experimental/puzzle15/common/src/commonMain/kotlin/com/bumble/appyx/components/experimental/puzzle15/ui/Puzzle15MotionController.kt +++ b/appyx-components/experimental/puzzle15/common/src/commonMain/kotlin/com/bumble/appyx/components/experimental/puzzle15/ui/Puzzle15MotionController.kt @@ -20,7 +20,7 @@ import com.bumble.appyx.interactions.core.ui.state.MatchedTargetUiState import com.bumble.appyx.transitionmodel.BaseMotionController class Puzzle15MotionController( - private val uiContext: UiContext, + uiContext: UiContext, defaultAnimationSpec: SpringSpec = DefaultAnimationSpec ) : BaseMotionController( uiContext = uiContext, @@ -66,22 +66,26 @@ class Puzzle15MotionController( return when (dragDirection4(delta)) { Drag.Direction4.UP -> Gesture( operation = Swap(Swap.Direction.DOWN), - completeAt = Offset(0f, -cellSize) + completeAt = Offset(0f, -cellSize), + isContinuous = false ) Drag.Direction4.LEFT -> Gesture( operation = Swap(Swap.Direction.RIGHT), - completeAt = Offset(-cellSize, 0f) + completeAt = Offset(-cellSize, 0f), + isContinuous = false ) Drag.Direction4.RIGHT -> Gesture( operation = Swap(Swap.Direction.LEFT), - completeAt = Offset(cellSize, 0f) + completeAt = Offset(cellSize, 0f), + isContinuous = false ) Drag.Direction4.DOWN -> Gesture( operation = Swap(Swap.Direction.UP), - completeAt = Offset(0f, cellSize) + completeAt = Offset(0f, cellSize), + isContinuous = false ) } diff --git a/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/parallax/BackStackParallax.kt b/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/parallax/BackStackParallax.kt index 9b6bb61b3..f03e72f0d 100644 --- a/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/parallax/BackStackParallax.kt +++ b/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/parallax/BackStackParallax.kt @@ -89,6 +89,7 @@ class BackStackParallax( Gesture( operation = Pop(), completeAt = Offset(x = transitionBounds.widthPx.toFloat(), y = 0f), + isContinuous = false ) } else { Gesture.Noop() diff --git a/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/stack3d/BackStack3D.kt b/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/stack3d/BackStack3D.kt index 394d2ea0f..9a34dc478 100644 --- a/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/stack3d/BackStack3D.kt +++ b/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/stack3d/BackStack3D.kt @@ -99,7 +99,8 @@ class BackStack3D( return if (dragVerticalDirection(delta) == Drag.VerticalDirection.DOWN) { Gesture( operation = Pop(), - completeAt = Offset(x = 0f, y = heightInPx) + completeAt = Offset(x = 0f, y = heightInPx), + isContinuous = false ) } else { Gesture.Noop() diff --git a/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/model/progress/DragProgressController.kt b/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/model/progress/DragProgressController.kt index 5966e741a..c644b29da 100644 --- a/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/model/progress/DragProgressController.kt +++ b/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/model/progress/DragProgressController.kt @@ -97,19 +97,25 @@ internal class DragProgressController( // Case: standard forward progress if (totalTarget < startProgress + 1) { model.setProgress(totalTarget) - val currentProgress = if (currentState is Keyframes<*>) currentState.progress else 0f - AppyxLogger.d( - TAG, - "delta applied forward, new progress: $currentProgress" - ) + val currentProgress = + if (currentState is Keyframes<*>) currentState.progress else 0f + AppyxLogger.d(TAG, "delta applied forward, new progress: $currentProgress") // Case: target is beyond the current segment, we'll need a new operation } else { // TODO without recursion - val remainder = - consumePartial(COMPLETE, dragAmount, totalTarget, deltaProgress, startProgress + 1) - if (remainder.getDistanceSquared() > 0) { - consumeDrag(remainder) + if (gesture!!.isContinuous) { + val remainder = + consumePartial( + direction = COMPLETE, + dragAmount = dragAmount, + totalTarget = totalTarget, + deltaProgress = deltaProgress, + boundary = startProgress + 1 + ) + if (remainder.getDistanceSquared() > 0) { + consumeDrag(remainder) + } } } @@ -117,9 +123,18 @@ internal class DragProgressController( // now we need to re-evaluate for a new operation } else { // TODO without recursion - val remainder = consumePartial(REVERT, dragAmount, totalTarget, deltaProgress, startProgress) - if (dragAmount != remainder) { - consumeDrag(remainder) + if (gesture!!.isContinuous) { + val remainder = + consumePartial( + direction = REVERT, + dragAmount = dragAmount, + totalTarget = totalTarget, + deltaProgress = deltaProgress, + boundary = startProgress + ) + if (dragAmount != remainder) { + consumeDrag(remainder) + } } } } diff --git a/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/ui/gesture/Gesture.kt b/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/ui/gesture/Gesture.kt index 60c9864d5..bd08cb031 100644 --- a/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/ui/gesture/Gesture.kt +++ b/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/ui/gesture/Gesture.kt @@ -6,16 +6,24 @@ import com.bumble.appyx.interactions.core.ui.math.proportionOf open class Gesture internal constructor( val operation: Operation, + val isContinuous: Boolean, val dragToProgress: (Offset) -> Float, val partial: (Offset, Float) -> Offset ) { var startProgress: Float? = null + /** + * isContinuous: Boolean - indicates that if during a drag gesture this operation completes + * but there's still offset to process a new gesture will be created that handles the remaining + * amount. + */ constructor( operation: Operation, completeAt: Offset, + isContinuous: Boolean = true, ) : this( operation = operation, + isContinuous = isContinuous, dragToProgress = { offset -> proportionOf(offset, completeAt) }, partial = { offset, remainder -> val totalProgress = proportionOf(offset, completeAt) @@ -25,6 +33,7 @@ open class Gesture internal constructor( class Noop : Gesture( operation = Operation.Noop(), + isContinuous = false, dragToProgress = { 0f }, partial = { _, _ -> Offset(0f, 0f) } ) From 6b7218af16b2ed0f205ffa664c8c3a72eee8cda8 Mon Sep 17 00:00:00 2001 From: vladcipariu Date: Mon, 18 Sep 2023 14:34:57 +0100 Subject: [PATCH 2/4] moves the isContinuous flag to the GestureFactory instead of Gesture. --- .../experimental/puzzle15/ui/Puzzle15MotionController.kt | 6 ++---- .../backstack/ui/parallax/BackStackParallax.kt | 3 ++- .../appyx/components/backstack/ui/stack3d/BackStack3D.kt | 5 ++++- .../core/model/progress/DragProgressController.kt | 5 +++-- .../bumble/appyx/interactions/core/ui/gesture/Gesture.kt | 9 --------- .../appyx/interactions/core/ui/gesture/GestureFactory.kt | 8 ++++++++ 6 files changed, 19 insertions(+), 17 deletions(-) diff --git a/appyx-components/experimental/puzzle15/common/src/commonMain/kotlin/com/bumble/appyx/components/experimental/puzzle15/ui/Puzzle15MotionController.kt b/appyx-components/experimental/puzzle15/common/src/commonMain/kotlin/com/bumble/appyx/components/experimental/puzzle15/ui/Puzzle15MotionController.kt index 70fa56788..abcec4d68 100644 --- a/appyx-components/experimental/puzzle15/common/src/commonMain/kotlin/com/bumble/appyx/components/experimental/puzzle15/ui/Puzzle15MotionController.kt +++ b/appyx-components/experimental/puzzle15/common/src/commonMain/kotlin/com/bumble/appyx/components/experimental/puzzle15/ui/Puzzle15MotionController.kt @@ -56,6 +56,8 @@ class Puzzle15MotionController( bounds: TransitionBounds, ) : GestureFactory { + override val isContinuous: Boolean = false + private val cellSize: Float = bounds.widthPx / 4f override fun createGesture( @@ -67,25 +69,21 @@ class Puzzle15MotionController( Drag.Direction4.UP -> Gesture( operation = Swap(Swap.Direction.DOWN), completeAt = Offset(0f, -cellSize), - isContinuous = false ) Drag.Direction4.LEFT -> Gesture( operation = Swap(Swap.Direction.RIGHT), completeAt = Offset(-cellSize, 0f), - isContinuous = false ) Drag.Direction4.RIGHT -> Gesture( operation = Swap(Swap.Direction.LEFT), completeAt = Offset(cellSize, 0f), - isContinuous = false ) Drag.Direction4.DOWN -> Gesture( operation = Swap(Swap.Direction.UP), completeAt = Offset(0f, cellSize), - isContinuous = false ) } diff --git a/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/parallax/BackStackParallax.kt b/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/parallax/BackStackParallax.kt index f03e72f0d..dec3e1cfd 100644 --- a/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/parallax/BackStackParallax.kt +++ b/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/parallax/BackStackParallax.kt @@ -79,6 +79,8 @@ class BackStackParallax( private val transitionBounds: TransitionBounds, ) : GestureFactory> { + override val isContinuous: Boolean = false + override fun createGesture( state: State, delta: Offset, @@ -89,7 +91,6 @@ class BackStackParallax( Gesture( operation = Pop(), completeAt = Offset(x = transitionBounds.widthPx.toFloat(), y = 0f), - isContinuous = false ) } else { Gesture.Noop() diff --git a/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/stack3d/BackStack3D.kt b/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/stack3d/BackStack3D.kt index 9a34dc478..d5563e274 100644 --- a/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/stack3d/BackStack3D.kt +++ b/appyx-components/stable/backstack/common/src/commonMain/kotlin/com/bumble/appyx/components/backstack/ui/stack3d/BackStack3D.kt @@ -88,7 +88,11 @@ class BackStack3D( class Gestures( transitionBounds: TransitionBounds, ) : GestureFactory> { + + override val isContinuous: Boolean = false + private val height = transitionBounds.screenHeightDp + override fun createGesture( state: State, delta: Offset, @@ -100,7 +104,6 @@ class BackStack3D( Gesture( operation = Pop(), completeAt = Offset(x = 0f, y = heightInPx), - isContinuous = false ) } else { Gesture.Noop() diff --git a/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/model/progress/DragProgressController.kt b/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/model/progress/DragProgressController.kt index c644b29da..5f49c54a3 100644 --- a/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/model/progress/DragProgressController.kt +++ b/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/model/progress/DragProgressController.kt @@ -90,6 +90,7 @@ internal class DragProgressController( } val startProgress = gesture!!.startProgress!! + val isGestureContinuous = gestureFactory().isContinuous // Case: we go forward, it's cool if (totalTarget > startProgress) { @@ -104,7 +105,7 @@ internal class DragProgressController( // Case: target is beyond the current segment, we'll need a new operation } else { // TODO without recursion - if (gesture!!.isContinuous) { + if (isGestureContinuous) { val remainder = consumePartial( direction = COMPLETE, @@ -123,7 +124,7 @@ internal class DragProgressController( // now we need to re-evaluate for a new operation } else { // TODO without recursion - if (gesture!!.isContinuous) { + if (isGestureContinuous) { val remainder = consumePartial( direction = REVERT, diff --git a/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/ui/gesture/Gesture.kt b/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/ui/gesture/Gesture.kt index bd08cb031..60c9864d5 100644 --- a/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/ui/gesture/Gesture.kt +++ b/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/ui/gesture/Gesture.kt @@ -6,24 +6,16 @@ import com.bumble.appyx.interactions.core.ui.math.proportionOf open class Gesture internal constructor( val operation: Operation, - val isContinuous: Boolean, val dragToProgress: (Offset) -> Float, val partial: (Offset, Float) -> Offset ) { var startProgress: Float? = null - /** - * isContinuous: Boolean - indicates that if during a drag gesture this operation completes - * but there's still offset to process a new gesture will be created that handles the remaining - * amount. - */ constructor( operation: Operation, completeAt: Offset, - isContinuous: Boolean = true, ) : this( operation = operation, - isContinuous = isContinuous, dragToProgress = { offset -> proportionOf(offset, completeAt) }, partial = { offset, remainder -> val totalProgress = proportionOf(offset, completeAt) @@ -33,7 +25,6 @@ open class Gesture internal constructor( class Noop : Gesture( operation = Operation.Noop(), - isContinuous = false, dragToProgress = { 0f }, partial = { _, _ -> Offset(0f, 0f) } ) diff --git a/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/ui/gesture/GestureFactory.kt b/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/ui/gesture/GestureFactory.kt index f6ea8bf88..b9b5419f5 100644 --- a/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/ui/gesture/GestureFactory.kt +++ b/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/ui/gesture/GestureFactory.kt @@ -5,6 +5,14 @@ import androidx.compose.ui.unit.Density interface GestureFactory { + /** + * isContinuous: Boolean - indicates that if during a drag gesture this operation completes + * but there's still offset to process a new gesture will be created that handles the remaining + * amount. + */ + val isContinuous: Boolean + get() = true + fun onStartDrag(position: Offset) {} fun createGesture( From 5ae6637a84cbe7b531792bb330521c2c163da2c5 Mon Sep 17 00:00:00 2001 From: vladcipariu Date: Mon, 18 Sep 2023 16:15:31 +0100 Subject: [PATCH 3/4] fix detekt issues --- .../model/progress/DragProgressController.kt | 37 ++++++------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/model/progress/DragProgressController.kt b/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/model/progress/DragProgressController.kt index 5f49c54a3..cff026566 100644 --- a/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/model/progress/DragProgressController.kt +++ b/appyx-interactions/common/src/commonMain/kotlin/com/bumble/appyx/interactions/core/model/progress/DragProgressController.kt @@ -103,39 +103,24 @@ internal class DragProgressController( AppyxLogger.d(TAG, "delta applied forward, new progress: $currentProgress") // Case: target is beyond the current segment, we'll need a new operation - } else { + } else if (isGestureContinuous) { // TODO without recursion - if (isGestureContinuous) { - val remainder = - consumePartial( - direction = COMPLETE, - dragAmount = dragAmount, - totalTarget = totalTarget, - deltaProgress = deltaProgress, - boundary = startProgress + 1 - ) - if (remainder.getDistanceSquared() > 0) { - consumeDrag(remainder) - } + + val remainder = + consumePartial(COMPLETE, dragAmount, totalTarget, deltaProgress, startProgress + 1) + if (remainder.getDistanceSquared() > 0) { + consumeDrag(remainder) } } // Case: we went back to or beyond the start, // now we need to re-evaluate for a new operation - } else { + } else if (isGestureContinuous) { // TODO without recursion - if (isGestureContinuous) { - val remainder = - consumePartial( - direction = REVERT, - dragAmount = dragAmount, - totalTarget = totalTarget, - deltaProgress = deltaProgress, - boundary = startProgress - ) - if (dragAmount != remainder) { - consumeDrag(remainder) - } + val remainder = + consumePartial(REVERT, dragAmount, totalTarget, deltaProgress, startProgress) + if (dragAmount != remainder) { + consumeDrag(remainder) } } } From 85512568fb4dfa7a9ca95c18e1cd1bed04ad5aa6 Mon Sep 17 00:00:00 2001 From: vladcipariu Date: Tue, 19 Sep 2023 13:51:56 +0100 Subject: [PATCH 4/4] update documentation and change log --- CHANGELOG.md | 3 +-- documentation/interactions/gestures.md | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e8dc7362..712c0ada1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Pending changes -- +- [#494](https://github.com/bumble-tech/appyx/pull/599) Added isContinuous flag to GestureFactory ## 2.0.0-alpha06 @@ -10,7 +10,6 @@ - [#594](https://github.com/bumble-tech/appyx/pull/594) Reverted JVM bytecode target to JDK11 instead of 17 -
15 Sep 2023
--- diff --git a/documentation/interactions/gestures.md b/documentation/interactions/gestures.md index 373b78444..02da9ff94 100644 --- a/documentation/interactions/gestures.md +++ b/documentation/interactions/gestures.md @@ -127,8 +127,10 @@ class Gestures : GestureFactory