/*
* Copyright 2021 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.ui.input.rotary
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.focus.FocusDirectedInputEvent
import androidx.compose.ui.input.focus.FocusAwareInputModifier
import androidx.compose.ui.modifier.modifierLocalOf
import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.platform.inspectable
/**
* Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will
* allow it to intercept [RotaryScrollEvent]s if it (or one of its children) is focused.
*
* @param onRotaryScrollEvent This callback is invoked when the user interacts with the
* rotary side button or the bezel on a wear device. While implementing this callback, return true
* to stop propagation of this event. If you return false, the event will be sent to this
* [onRotaryScrollEvent]'s parent.
*
* @return true if the event is consumed, false otherwise.
*
* Here is an example of a scrollable container that scrolls in response to
* [RotaryScrollEvent]s.
* @sample androidx.compose.ui.samples.RotaryEventSample
*
* This sample demonstrates how a parent can add an [onRotaryScrollEvent] modifier to gain
* access to a [RotaryScrollEvent] when a child does not consume it:
* @sample androidx.compose.ui.samples.PreRotaryEventSample
*/
@ExperimentalComposeUiApi
fun Modifier.onRotaryScrollEvent(
onRotaryScrollEvent: (RotaryScrollEvent) -> Boolean
): Modifier = inspectable(
inspectorInfo = debugInspectorInfo {
name = "onRotaryScrollEvent"
properties["onRotaryScrollEvent"] = onRotaryScrollEvent
}
) {
FocusAwareInputModifier(
onEvent = onRotaryScrollEvent.focusAwareCallback(),
onPreEvent = null,
key = ModifierLocalRotaryScrollParent
)
}
/**
* Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will
* allow it to intercept [RotaryScrollEvent]s if it (or one of its children) is focused.
*
* @param onPreRotaryScrollEvent This callback is invoked when the user interacts with the
* rotary button on a wear device. It gives ancestors of a focused component the chance to
* intercept a [RotaryScrollEvent].
*
* When the user rotates the side button on a wear device, a [RotaryScrollEvent] is sent to
* the focused item. Before reaching the focused item, this event starts at the root composable,
* and propagates down the hierarchy towards the focused item. It invokes any
* [onPreRotaryScrollEvent]s it encounters on ancestors of the focused item. After reaching
* the focused item, the event propagates up the hierarchy back towards the parent. It invokes any
* [onRotaryScrollEvent]s it encounters on its way back.
*
* Return true to indicate that you consumed the event and want to stop propagation of this event.
*
* @return true if the event is consumed, false otherwise.
*
* @sample androidx.compose.ui.samples.PreRotaryEventSample
*/
@ExperimentalComposeUiApi
fun Modifier.onPreRotaryScrollEvent(
onPreRotaryScrollEvent: (RotaryScrollEvent) -> Boolean
): Modifier = inspectable(
inspectorInfo = debugInspectorInfo {
name = "onPreRotaryScrollEvent"
properties["onPreRotaryScrollEvent"] = onPreRotaryScrollEvent
}
) {
FocusAwareInputModifier(
onEvent = null,
onPreEvent = onPreRotaryScrollEvent.focusAwareCallback(),
key = ModifierLocalRotaryScrollParent
)
}
@ExperimentalComposeUiApi
internal val ModifierLocalRotaryScrollParent =
modifierLocalOf<FocusAwareInputModifier<RotaryScrollEvent>?> { null }
@ExperimentalComposeUiApi
private fun ((RotaryScrollEvent) -> Boolean).focusAwareCallback() = { e: FocusDirectedInputEvent ->
check(e is RotaryScrollEvent) {
"FocusAwareEvent is dispatched to the wrong FocusAwareParent."
}
invoke(e)
}