[go: nahoru, domu]

blob: de590abc3e7b162117cc20a46f23b9e7d3486b5b [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.remember
import androidx.ui.core.DensityAmbient
import androidx.ui.core.Modifier
import androidx.ui.core.PointerEventPass
import androidx.ui.core.PointerInputChange
import androidx.ui.core.changedToUpIgnoreConsumed
import androidx.ui.core.composed
import androidx.ui.core.pointerinput.PointerInputFilter
import androidx.ui.unit.IntSize
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 [rawScaleGestureFilter] 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 [rawScaleGestureFilter], 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).
*/
fun Modifier.scaleSlopExceededGestureFilter(
onScaleSlopExceeded: () -> Unit
): Modifier = composed {
val scaleSlop = with(DensityAmbient.current) { ScaleSlop.toPx() }
val filter = remember { ScaleSlopExceededGestureFilter(scaleSlop) }
// TODO(b/129784010): Consider also allowing onStart, onScale, and onEnd to be set individually.
filter.onScaleSlopExceeded = onScaleSlopExceeded
PointerInputModifierImpl(filter)
}
/**
* @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 scaleSlopExceededGestureFilter
*/
internal class ScaleSlopExceededGestureFilter(private val scaleSlop: Float) : PointerInputFilter
() {
lateinit var onScaleSlopExceeded: () -> Unit
var passedSlop = false
var scaleDiffTotal = 0f
override fun onPointerInput(
changes: List<PointerInputChange>,
pass: PointerEventPass,
bounds: IntSize
): List<PointerInputChange> {
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) {
passedSlop = true
onScaleSlopExceeded.invoke()
}
}
}
}
if (passedSlop &&
pass == PointerEventPass.PostDown &&
changes.all { it.changedToUpIgnoreConsumed() }
) {
passedSlop = false
scaleDiffTotal = 0f
}
return changes
}
override fun onCancel() {
passedSlop = false
scaleDiffTotal = 0f
}
}