[go: nahoru, domu]

blob: fc9313024571797db7c74427a1d705573195f0ee [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.ui.core.gesture
import androidx.compose.Composable
import androidx.compose.remember
import androidx.ui.core.PointerEventPass
import androidx.ui.core.PointerInputChange
import androidx.ui.core.PointerInput
import androidx.ui.core.ambientDensity
import androidx.ui.core.changedToUpIgnoreConsumed
import androidx.ui.unit.IntPxSize
import androidx.ui.unit.Px
import androidx.ui.unit.withDensity
import kotlin.math.absoluteValue
// TODO(b/143877464): Implement a "can scale in / can scale out" check so that scale slop is only
// surpassed in the appropriate direction?
/**
* This gesture detector detects when a user's pointer input is intended to include scaling.
*
* This gesture detector is very similar to [RawScaleGestureDetector] except that instead of
* providing callbacks for scaling, it instead provides one callback for when a user is intending
* to scale. It does so using the same semantics as [RawScaleGestureDetector], and simply waits
* until the user has scaled just enough to suggest the user is truly intended to scale.
*
* The gesture is considered to include scaling when the absolute cumulative average change in
* distance of all pointers from the average pointer over time surpasses a particular value
* (currently [ScaleSlop]).
*
* For example, if the [ScaleSlop] is 5 pixels and 2 pointers were 1 pixel away from each
* other and now are 11.00001 pixels away from each other, the slop will have been surpassed and
* [onScaleSlopExceeded] will be called (both pointers are slightly more than 5 pixels away from
* the average of the pointers than they were).
*/
@Composable
fun ScaleSlopExceededGestureDetector(
onScaleSlopExceeded: () -> Unit,
children: @Composable() () -> Unit
) {
val scaleSlop = withDensity(ambientDensity()) { ScaleSlop.toPx() }
val recognizer = remember { ScaleSlopExceededGestureRecognizer(scaleSlop) }
// TODO(b/129784010): Consider also allowing onStart, onScale, and onEnd to be set individually.
recognizer.onScaleSlopExceeded = onScaleSlopExceeded
PointerInput(
pointerInputHandler = recognizer.pointerInputHandler,
cancelHandler = recognizer.cancelHandler,
children = children
)
}
/**
* @param scaleSlop The absolute cumulative average change in distance of all pointers from the
* average pointer over time that must be surpassed to indicate the user is trying to scale.
*
* @see ScaleSlopExceededGestureDetector
*/
internal class ScaleSlopExceededGestureRecognizer(private val scaleSlop: Px) {
lateinit var onScaleSlopExceeded: () -> Unit
var passedSlop = false
var scaleDiffTotal = 0f
val pointerInputHandler =
{ changes: List<PointerInputChange>, pass: PointerEventPass, _: IntPxSize ->
if (pass == PointerEventPass.PostUp) {
if (!passedSlop) {
val currentlyDownChanges =
changes.filter { it.current.down && it.previous.down }
if (currentlyDownChanges.isNotEmpty()) {
val dimensionInformation =
currentlyDownChanges.calculateAllDimensionInformation()
val scaleDifference = dimensionInformation.calculateScaleDifference()
scaleDiffTotal += scaleDifference
if (scaleDiffTotal.absoluteValue > scaleSlop.value) {
passedSlop = true
onScaleSlopExceeded.invoke()
}
}
}
}
if (passedSlop &&
pass == PointerEventPass.PostDown &&
changes.all { it.changedToUpIgnoreConsumed() }
) {
passedSlop = false
scaleDiffTotal = 0f
}
changes
}
val cancelHandler = {
passedSlop = false
scaleDiffTotal = 0f
}
}