@@ -20,7 +20,11 @@ package androidx.compose.foundation
2020
2121import androidx.compose.animation.animateColorAsState
2222import androidx.compose.animation.core.TweenSpec
23- import androidx.compose.foundation.gestures.*
23+ import androidx.compose.foundation.gestures.awaitEachGesture
24+ import androidx.compose.foundation.gestures.awaitFirstDown
25+ import androidx.compose.foundation.gestures.awaitHorizontalDragOrCancellation
26+ import androidx.compose.foundation.gestures.awaitVerticalDragOrCancellation
27+ import androidx.compose.foundation.gestures.drag
2428import androidx.compose.foundation.interaction.DragInteraction
2529import androidx.compose.foundation.interaction.MutableInteractionSource
2630import androidx.compose.foundation.interaction.collectIsHoveredAsState
@@ -48,13 +52,11 @@ import androidx.compose.runtime.rememberCoroutineScope
4852import androidx.compose.runtime.setValue
4953import androidx.compose.runtime.staticCompositionLocalOf
5054import androidx.compose.ui.Modifier
51- import androidx.compose.ui.composed
5255import androidx.compose.ui.geometry.Offset
5356import androidx.compose.ui.graphics.Color
5457import androidx.compose.ui.graphics.Shape
5558import androidx.compose.ui.input.pointer.PointerInputScope
5659import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
57- import androidx.compose.ui.input.pointer.pointerInput
5860import androidx.compose.ui.input.pointer.positionChange
5961import androidx.compose.ui.layout.Layout
6062import androidx.compose.ui.layout.MeasurePolicy
@@ -892,30 +894,32 @@ private class ScrollbarDragModifierNode(
892894 private var sliderAdapter : SliderAdapter ,
893895) : DelegatingNode() {
894896
895- private val pointerInputNode = delegate(
896- SuspendingPointerInputModifierNode (
897- pointerInputEventHandler = {
898- awaitEachGesture {
899- val down = awaitFirstDown(requireUnconsumed = false )
900- val interaction = DragInteraction .Start ()
901- interactionSource.tryEmit(interaction)
902- draggedInteraction.value = interaction
903- sliderAdapter.onDragStarted()
904- val isSuccess = drag(down.id) { change ->
905- sliderAdapter.onDragDelta(change.positionChange())
906- change.consume()
897+ init {
898+ delegate(
899+ SuspendingPointerInputModifierNode (
900+ pointerInputEventHandler = {
901+ awaitEachGesture {
902+ val down = awaitFirstDown(requireUnconsumed = false )
903+ val interaction = DragInteraction .Start ()
904+ interactionSource.tryEmit(interaction)
905+ draggedInteraction.value = interaction
906+ sliderAdapter.onDragStarted()
907+ val isSuccess = drag(down.id) { change ->
908+ sliderAdapter.onDragDelta(change.positionChange())
909+ change.consume()
910+ }
911+ val finishInteraction = if (isSuccess) {
912+ DragInteraction .Stop (interaction)
913+ } else {
914+ DragInteraction .Cancel (interaction)
915+ }
916+ interactionSource.tryEmit(finishInteraction)
917+ draggedInteraction.value = null
907918 }
908- val finishInteraction = if (isSuccess) {
909- DragInteraction .Stop (interaction)
910- } else {
911- DragInteraction .Cancel (interaction)
912- }
913- interactionSource.tryEmit(finishInteraction)
914- draggedInteraction.value = null
915919 }
916- }
920+ )
917921 )
918- )
922+ }
919923
920924 fun update (
921925 interactionSource : MutableInteractionSource ,
@@ -932,17 +936,99 @@ private fun Modifier.scrollOnPressTrack(
932936 isVertical : Boolean ,
933937 reverseLayout : Boolean ,
934938 sliderAdapter : SliderAdapter ,
935- ) = composed {
936- val coroutineScope = rememberCoroutineScope()
937- val scroller = remember(sliderAdapter, coroutineScope, reverseLayout) {
938- TrackPressScroller (coroutineScope, sliderAdapter, reverseLayout)
939+ ): Modifier = this .then(
940+ ScrollOnPressTrackElement (
941+ isVertical = isVertical,
942+ reverseLayout = reverseLayout,
943+ sliderAdapter = sliderAdapter,
944+ )
945+ )
946+
947+ private class ScrollOnPressTrackElement (
948+ private val isVertical : Boolean ,
949+ private val reverseLayout : Boolean ,
950+ private val sliderAdapter : SliderAdapter ,
951+ ): ModifierNodeElement<ScrollOnPressTrackNode>() {
952+ override fun create (): ScrollOnPressTrackNode = ScrollOnPressTrackNode (
953+ isVertical = isVertical,
954+ reverseLayout = reverseLayout,
955+ sliderAdapter = sliderAdapter,
956+ )
957+
958+ override fun update (node : ScrollOnPressTrackNode ) = node.update(
959+ isVertical = isVertical,
960+ reverseLayout = reverseLayout,
961+ sliderAdapter = sliderAdapter,
962+ )
963+
964+ override fun InspectorInfo.inspectableProperties () {
965+ name = " scrollOnPressTrack"
966+ properties[" isVertical" ] = isVertical
967+ properties[" reverseLayout" ] = reverseLayout
968+ properties[" sliderAdapter" ] = sliderAdapter
969+ }
970+
971+ override fun hashCode (): Int {
972+ var result = isVertical.hashCode()
973+ result = 31 * result + reverseLayout.hashCode()
974+ result = 31 * result + sliderAdapter.hashCode()
975+ return result
939976 }
940- Modifier .pointerInput(scroller) {
941- detectScrollViaTrackGestures(
942- isVertical = isVertical,
943- scroller = scroller
977+
978+ override fun equals (other : Any? ): Boolean {
979+ if (this == = other) return true
980+ if (other !is ScrollOnPressTrackElement ) return false
981+ return isVertical == other.isVertical &&
982+ reverseLayout == other.reverseLayout &&
983+ sliderAdapter == other.sliderAdapter
984+ }
985+ }
986+
987+ private class ScrollOnPressTrackNode (
988+ private var isVertical : Boolean ,
989+ private var reverseLayout : Boolean ,
990+ private var sliderAdapter : SliderAdapter ,
991+ ): DelegatingNode() {
992+ private lateinit var scroller: TrackPressScroller
993+ private lateinit var pointerInputNode: SuspendingPointerInputModifierNode
994+
995+ override fun onAttach () {
996+ super .onAttach()
997+
998+ recreateScroller()
999+
1000+ pointerInputNode = delegate(
1001+ SuspendingPointerInputModifierNode (
1002+ pointerInputEventHandler = {
1003+ detectScrollViaTrackGestures(
1004+ isVertical = isVertical,
1005+ scroller = scroller,
1006+ )
1007+ }
1008+ )
9441009 )
9451010 }
1011+
1012+ fun update (
1013+ isVertical : Boolean ,
1014+ reverseLayout : Boolean ,
1015+ sliderAdapter : SliderAdapter ,
1016+ ) {
1017+ val needsReset = isVertical != this .isVertical || reverseLayout != this .reverseLayout || sliderAdapter != this .sliderAdapter
1018+
1019+ this .isVertical = isVertical
1020+ this .reverseLayout = reverseLayout
1021+ this .sliderAdapter = sliderAdapter
1022+
1023+ if (needsReset) {
1024+ recreateScroller()
1025+ pointerInputNode.resetPointerInputHandler()
1026+ }
1027+ }
1028+
1029+ private fun recreateScroller () {
1030+ scroller = TrackPressScroller (coroutineScope, sliderAdapter, reverseLayout)
1031+ }
9461032}
9471033
9481034/* *
0 commit comments