[go: nahoru, domu]

blob: fcd95d99c4a756b7f79bcf10d0f078babff374cb [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.foundation.gestures
import androidx.compose.onDispose
import androidx.ui.core.DensityAmbient
import androidx.ui.core.Direction
import androidx.ui.core.Modifier
import androidx.ui.core.composed
import androidx.ui.core.gesture.ScrollCallback
import androidx.ui.core.gesture.dragGestureFilter
import androidx.ui.core.gesture.scrollGestureFilter
import androidx.ui.core.gesture.scrollorientationlocking.Orientation
import androidx.compose.foundation.Interaction
import androidx.compose.foundation.InteractionState
import androidx.ui.geometry.Offset
import androidx.ui.unit.Density
/**
* Configure touch dragging for the UI element in a single [Orientation]. The drag distance is
* reported to [onDrag] as a single [Float] value in pixels.
*
* The common usecase for this component is when you need to be able to drag something
* inside the component on the screen and represent this state via one float value
*
* If you need to control the whole dragging flow, consider using [dragGestureFilter] instead.
*
* If you are implementing scroll/fling behavior, consider using [scrollable].
*
* @sample androidx.compose.foundation.samples.DraggableSample
*
* @param orientation orientation of the drag
* @param enabled whether or not drag is enabled
* @param reverseDirection reverse the direction of the scroll, so top to bottom scroll will
* behave like bottom to top and left to right will behave like right to left.
* @param interactionState [InteractionState] that will be updated when this draggable is
* being dragged, using [Interaction.Dragged].
* @param startDragImmediately when set to true, draggable will start dragging immediately and
* prevent other gesture detectors from reacting to "down" events (in order to block composed
* press-based gestures). This is intended to allow end users to "catch" an animating widget by
* pressing on it. It's useful to set it when value you're dragging is settling / animating.
* @param canDrag callback to indicate whether or not dragging is allowed for given [Direction]
* @param onDragStarted callback that will be invoked when drag has been started after touch slop
* has been passed, with starting position provided
* @param onDragStopped callback that will be invoked when drag stops, with velocity provided
* @param onDrag callback to be invoked when the drag occurs with the delta dragged from the
* previous event. [Density] provided in the scope for the convenient conversion between px and dp
*/
fun Modifier.draggable(
orientation: Orientation,
enabled: Boolean = true,
reverseDirection: Boolean = false,
interactionState: InteractionState? = null,
startDragImmediately: Boolean = false,
canDrag: (Direction) -> Boolean = { enabled },
onDragStarted: (startedPosition: Offset) -> Unit = {},
onDragStopped: (velocity: Float) -> Unit = {},
onDrag: Density.(Float) -> Unit
): Modifier = composed {
val density = DensityAmbient.current
onDispose {
interactionState?.removeInteraction(Interaction.Dragged)
}
scrollGestureFilter(
scrollCallback = object : ScrollCallback {
override fun onStart(downPosition: Offset) {
if (enabled) {
interactionState?.addInteraction(Interaction.Dragged)
onDragStarted(downPosition)
}
}
override fun onScroll(scrollDistance: Float): Float {
if (!enabled) return scrollDistance
val toConsume = if (reverseDirection) scrollDistance * -1 else scrollDistance
with(density) { onDrag(toConsume) }
// we explicitly disallow nested scrolling in draggable, as it should be
// accessible via Modifier.scrollable. For drags, usually nested dragging is not
// required
return scrollDistance
}
override fun onCancel() {
if (enabled) {
interactionState?.removeInteraction(Interaction.Dragged)
onDragStopped(0f)
}
}
override fun onStop(velocity: Float) {
if (enabled) {
interactionState?.removeInteraction(Interaction.Dragged)
onDragStopped(if (reverseDirection) velocity * -1 else velocity)
}
}
},
orientation = orientation,
canDrag = canDrag,
startDragImmediately = startDragImmediately
)
}