[go: nahoru, domu]

blob: e1e3ec201194d8515ab9bcec7dbd6dbbd29c9de4 [file] [log] [blame]
/*
* Copyright 2018 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.rendering.proxybox
import androidx.ui.engine.geometry.Offset
import androidx.ui.foundation.diagnostics.DiagnosticPropertiesBuilder
import androidx.ui.foundation.diagnostics.IterableProperty
import androidx.ui.gestures.drag_details.DragUpdateDetails
import androidx.ui.gestures.drag_details.GestureDragUpdateCallback
import androidx.ui.gestures.long_press.GestureLongPressCallback
import androidx.ui.gestures.tap.GestureTapCallback
import androidx.ui.rendering.box.RenderBox
import androidx.ui.semantics.SemanticsAction
import androidx.ui.semantics.SemanticsConfiguration
// TODO(Migration/shepshapard): Porting of tests requires SemanticsConfiguration which is not yet
// ported.
/**
* Listens for the specified gestures from the semantics server (e.g.
* an accessibility tool).
*/
class RenderSemanticsGestureHandler(
child: RenderBox? = null,
onTap: GestureTapCallback? = null,
onLongPress: GestureLongPressCallback? = null,
onHorizontalDragUpdate: GestureDragUpdateCallback? = null,
onVerticalDragUpdate: GestureDragUpdateCallback? = null,
/**
* The fraction of the dimension of this render box to use when
* scrolling. For example, if this is 0.8 and the box is 200 pixels
* wide, then when a left-scroll action is received from the
* accessibility system, it will translate into a 160 pixel
* leftwards drag.
*/
val scrollFactor: Float = 0.8f
) : RenderProxyBox(
child
) {
/**
* If non-null, the set of actions to allow. Other actions will be omitted,
* even if their callback is provided.
*
* For example, if [onTap] is non-null but [validActions] does not contain
* [SemanticsAction.tap], then the semantic description of this node will
* not claim to support taps.
*
* This is normally used to filter the actions made available by
* [onHorizontalDragUpdate] and [onVerticalDragUpdate]. Normally, these make
* both the right and left, or up and down, actions available. For example,
* if [onHorizontalDragUpdate] is set but [validActions] only contains
* [SemanticsAction.scrollLeft], then the [SemanticsAction.scrollRight]
* action will be omitted.
*/
var validActions: Set<SemanticsAction>? = null
set(value) {
if (value == field) return
field = value
markNeedsSemanticsUpdate()
}
/** Called when the user taps on the render object. */
var onTap: GestureTapCallback? = onTap
set(value) {
if (value == field) return
val handlerExistenceChanged = (field != null) != (value != null)
field = value
if (handlerExistenceChanged) markNeedsSemanticsUpdate()
}
/** Called when the user presses on the render object for a long period of time. */
var onLongPress: GestureLongPressCallback? = onLongPress
set(value) {
if (value == field) return
val handlerExistenceChanged = (field != null) != (value != null)
field = value
if (handlerExistenceChanged) markNeedsSemanticsUpdate()
}
/** Called when the user scrolls to the left or to the right. */
var onHorizontalDragUpdate: GestureDragUpdateCallback? = onHorizontalDragUpdate
set(value) {
if (value == field) return
val handlerExistenceChanged = (field != null) != (value != null)
field = value
if (handlerExistenceChanged) markNeedsSemanticsUpdate()
}
/** Called when the user scrolls up or down. */
var onVerticalDragUpdate: GestureDragUpdateCallback? = onVerticalDragUpdate
set(value) {
if (value == field) return
val handlerExistenceChanged = (field != null) != (value != null)
field = value
if (handlerExistenceChanged) markNeedsSemanticsUpdate()
}
override fun describeSemanticsConfiguration(config: SemanticsConfiguration) {
super.describeSemanticsConfiguration(config)
if (onTap != null && this.isValidAction(SemanticsAction.tap)) {
config.onTap = onTap
}
if (onLongPress != null && this.isValidAction(SemanticsAction.longPress)) {
config.onLongPress = onLongPress
}
if (onHorizontalDragUpdate != null) {
if (this.isValidAction(SemanticsAction.scrollRight)) {
config.onScrollRight = ::performSemanticScrollRight
}
if (this.isValidAction(SemanticsAction.scrollLeft)) {
config.onScrollLeft = ::performSemanticScrollLeft
}
}
if (onVerticalDragUpdate != null) {
if (this.isValidAction(SemanticsAction.scrollUp)) {
config.onScrollUp = ::performSemanticScrollUp
}
if (this.isValidAction(SemanticsAction.scrollDown)) {
config.onScrollDown = ::performSemanticScrollDown
}
}
}
private fun isValidAction(action: SemanticsAction) = validActions?.contains(action) ?: true
private fun performSemanticScrollLeft() {
onHorizontalDragUpdate?.let {
var primaryDelta = size.width * -scrollFactor
it(
DragUpdateDetails(
delta = Offset(dx = primaryDelta, dy = 0.0f),
primaryDelta = primaryDelta,
globalPosition = this.localToGlobal(size.center(Offset.zero))
)
)
}
}
private fun performSemanticScrollRight() {
onHorizontalDragUpdate?.let {
var primaryDelta = size.width * scrollFactor
it(
DragUpdateDetails(
delta = Offset(dx = primaryDelta, dy = 0.0f),
primaryDelta = primaryDelta,
globalPosition = this.localToGlobal(size.center(Offset.zero))
)
)
}
}
private fun performSemanticScrollUp() {
onVerticalDragUpdate?.let {
var primaryDelta = size.height * -scrollFactor
it(
DragUpdateDetails(
delta = Offset(dx = 0.0f, dy = primaryDelta),
primaryDelta = primaryDelta,
globalPosition = this.localToGlobal(size.center(Offset.zero))
)
)
}
}
private fun performSemanticScrollDown() {
onVerticalDragUpdate?.let {
var primaryDelta = size.height * scrollFactor
it(
DragUpdateDetails(
delta = Offset(dx = 0.0f, dy = primaryDelta),
primaryDelta = primaryDelta,
globalPosition = this.localToGlobal(size.center(Offset.zero))
)
)
}
}
override fun debugFillProperties(properties: DiagnosticPropertiesBuilder) {
super.debugFillProperties(properties)
val gestures: MutableList<String> = mutableListOf()
if (onTap != null) gestures.add("tap")
if (onLongPress != null) gestures.add("long press")
if (onHorizontalDragUpdate != null) gestures.add("horizontal scroll")
if (onVerticalDragUpdate != null) gestures.add("vertical scroll")
if (gestures.isEmpty()) gestures.add("<none>")
properties.add(IterableProperty("gestures", gestures))
}
}