| /* |
| * 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.runtime.remember |
| import androidx.ui.core.Modifier |
| import androidx.ui.core.PointerEventPass |
| import androidx.ui.core.PointerInputChange |
| import androidx.ui.core.changedToDown |
| import androidx.ui.core.changedToUp |
| import androidx.ui.core.composed |
| import androidx.ui.core.consumeDownChange |
| import androidx.ui.core.pointerinput.PointerInputFilter |
| import androidx.compose.ui.geometry.Offset |
| import androidx.compose.ui.unit.IntSize |
| |
| /** |
| * Reacts if the first pointer input change it sees is an unconsumed down change, and if it reacts, |
| * consumes all further down changes. |
| * |
| * This GestureDetector is not generally intended to be used directly, but is instead intended to be |
| * used as a building block to create more complex GestureDetectors. |
| * |
| * This GestureDetector is a bit more experimental then the other GestureDetectors (the number and |
| * types of GestureDetectors is still very much a work in progress) and is intended to be a |
| * generically useful building block for more complicated GestureDetectors. |
| * |
| * The theory is that this GestureDetector can be reused in PressIndicatorGestureDetector, and there |
| * could be a corresponding RawPressReleasedGestureDetector. |
| * |
| * @param onPressStart Called when the first pointer "presses" on the GestureDetector. [Offset] |
| * is the position of that first pointer on press. |
| * @param enabled If false, this GestureDetector will effectively act as if it is not in the |
| * hierarchy. |
| * @param executionPass The [PointerEventPass] during which this GestureDetector will attempt to |
| * react to and consume down changes. Defaults to [PointerEventPass.PostUp]. |
| */ |
| fun Modifier.rawPressStartGestureFilter( |
| onPressStart: (Offset) -> Unit, |
| enabled: Boolean = false, |
| executionPass: PointerEventPass = PointerEventPass.PostUp |
| ): Modifier = composed { |
| val filter = remember { RawPressStartGestureFilter() } |
| filter.onPressStart = onPressStart |
| filter.setEnabled(enabled = enabled) |
| filter.setExecutionPass(executionPass) |
| PointerInputModifierImpl(filter) |
| } |
| |
| internal class RawPressStartGestureFilter : PointerInputFilter() { |
| |
| lateinit var onPressStart: (Offset) -> Unit |
| private var enabled: Boolean = true |
| private var executionPass = PointerEventPass.InitialDown |
| |
| private var active = false |
| |
| override fun onPointerInput( |
| changes: List<PointerInputChange>, |
| pass: PointerEventPass, |
| bounds: IntSize |
| ): List<PointerInputChange> { |
| |
| var internalChanges = changes |
| |
| if (pass == executionPass) { |
| if (enabled && internalChanges.all { it.changedToDown() }) { |
| // If we have not yet started and all of the changes changed to down, we are |
| // starting. |
| active = true |
| onPressStart(internalChanges.first().current.position!!) |
| } else if (internalChanges.all { it.changedToUp() }) { |
| // If we have started and all of the changes changed to up, we are stopping. |
| active = false |
| } |
| |
| if (active) { |
| // If we have started, we should consume the down change on all changes. |
| internalChanges = internalChanges.map { it.consumeDownChange() } |
| } |
| } |
| |
| return internalChanges |
| } |
| |
| override fun onCancel() { |
| active = false |
| } |
| |
| fun setEnabled(enabled: Boolean) { |
| this.enabled = enabled |
| // Whenever we are disabled, we can just go ahead and become inactive (which is the state we |
| // should be in if we are to pretend that we aren't in the hierarchy. |
| if (!enabled) { |
| onCancel() |
| } |
| } |
| |
| fun setExecutionPass(executionPass: PointerEventPass) { |
| this.executionPass = executionPass |
| } |
| } |