-
-
Notifications
You must be signed in to change notification settings - Fork 980
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## Description Introduce a new gesture: `Hover`. It works exactly how you would expect based on the name 😄. It supports hovering with a mouse & stylus on all platforms and makes it easy to add [Pointer interactions](https://developer.apple.com/documentation/uikit/pointer_interactions) to a view on iOS (via `withFeedback` method). The API is identical to all other gestures, you can simply create the configuration object and set the callbacks: ```jsx const gesture = Gesture.Hover() .onBegin(() => { console.log('hover begin'); }) .onFinalize(() => { console.log('hover finalize'); }) ``` ## Test plan This PR adds two examples: `Hover` and `HoverableIcons`, which can be used to verify that the gesture works correctly quickly.
- Loading branch information
1 parent
04ed17e
commit 16a266e
Showing
35 changed files
with
885 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
120 changes: 120 additions & 0 deletions
120
android/src/main/java/com/swmansion/gesturehandler/core/HoverGestureHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package com.swmansion.gesturehandler.core | ||
|
||
import android.os.Handler | ||
import android.os.Looper | ||
import android.view.MotionEvent | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import com.swmansion.gesturehandler.react.RNViewConfigurationHelper | ||
|
||
class HoverGestureHandler : GestureHandler<HoverGestureHandler>() { | ||
private var handler: Handler? = null | ||
private var finishRunnable = Runnable { finish() } | ||
|
||
private infix fun isAncestorOf(other: GestureHandler<*>): Boolean { | ||
var current: View? = other.view | ||
|
||
while (current != null) { | ||
if (current == this.view) { | ||
return true | ||
} | ||
|
||
current = current.parent as? View | ||
} | ||
|
||
return false | ||
} | ||
|
||
private fun isViewDisplayedOverAnother(view: View, other: View, rootView: View = view.rootView): Boolean? { | ||
// traverse the tree starting on the root view, to see which view will be drawn first | ||
if (rootView == other) { | ||
return true | ||
} | ||
|
||
if (rootView == view) { | ||
return false | ||
} | ||
|
||
if (rootView is ViewGroup) { | ||
for (i in 0 until rootView.childCount) { | ||
val child = viewConfigHelper.getChildInDrawingOrderAtIndex(rootView, i) | ||
return isViewDisplayedOverAnother(view, other, child) ?: continue | ||
} | ||
} | ||
|
||
return null | ||
} | ||
|
||
override fun shouldBeCancelledBy(handler: GestureHandler<*>): Boolean { | ||
if (handler is HoverGestureHandler && !(handler isAncestorOf this)) { | ||
return isViewDisplayedOverAnother(handler.view!!, this.view!!)!! | ||
} | ||
|
||
return super.shouldBeCancelledBy(handler) | ||
} | ||
|
||
override fun shouldRequireToWaitForFailure(handler: GestureHandler<*>): Boolean { | ||
if (handler is HoverGestureHandler) { | ||
if (!(this isAncestorOf handler) && !(handler isAncestorOf this)) { | ||
isViewDisplayedOverAnother(this.view!!, handler.view!!)?.let { | ||
return it | ||
} | ||
} | ||
} | ||
|
||
return super.shouldRequireToWaitForFailure(handler) | ||
} | ||
|
||
override fun shouldRecognizeSimultaneously(handler: GestureHandler<*>): Boolean { | ||
if (handler is HoverGestureHandler && (this isAncestorOf handler || handler isAncestorOf this)) { | ||
return true | ||
} | ||
|
||
return super.shouldRecognizeSimultaneously(handler) | ||
} | ||
|
||
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) { | ||
if (event.action == MotionEvent.ACTION_DOWN) { | ||
handler?.removeCallbacksAndMessages(null) | ||
handler = null | ||
} else if (event.action == MotionEvent.ACTION_UP) { | ||
if (!isWithinBounds) { | ||
finish() | ||
} | ||
} | ||
} | ||
|
||
override fun onHandleHover(event: MotionEvent, sourceEvent: MotionEvent) { | ||
when { | ||
event.action == MotionEvent.ACTION_HOVER_EXIT -> { | ||
if (handler == null) { | ||
handler = Handler(Looper.getMainLooper()) | ||
} | ||
|
||
handler!!.postDelayed(finishRunnable, 4) | ||
} | ||
|
||
!isWithinBounds -> { | ||
finish() | ||
} | ||
|
||
this.state == STATE_UNDETERMINED && | ||
(event.action == MotionEvent.ACTION_HOVER_MOVE || event.action == MotionEvent.ACTION_HOVER_ENTER) -> { | ||
begin() | ||
activate() | ||
} | ||
} | ||
} | ||
|
||
private fun finish() { | ||
when (this.state) { | ||
STATE_UNDETERMINED -> cancel() | ||
STATE_BEGAN -> fail() | ||
STATE_ACTIVE -> end() | ||
} | ||
} | ||
|
||
companion object { | ||
private val viewConfigHelper = RNViewConfigurationHelper() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
--- | ||
id: hover-gesture | ||
title: Hover gesture | ||
sidebar_label: Hover gesture | ||
--- | ||
|
||
import BaseEventData from './base-gesture-event-data.md'; | ||
import BaseEventConfig from './base-gesture-config.md'; | ||
import BaseEventCallbacks from './base-gesture-callbacks.md'; | ||
import BaseContinousEventCallbacks from './base-continous-gesture-callbacks.md'; | ||
|
||
A continuous gesture that can recognize hovering above the view it's attached to. The hover effect may be activated by moving a mouse or a stylus over the view. | ||
|
||
On iOS additional visual effects may be configured. | ||
|
||
:::info | ||
Don't rely on `Hover` gesture to continue after the mouse button is clicked or the stylus touches the screen. If you want to handle both cases, [compose](../../gesture-composition.md) it with [`Pan` gesture](./pan-gesture.md). | ||
::: | ||
|
||
## Config | ||
|
||
### Properties specific to `HoverGesture`: | ||
|
||
### `effect(effect: HoverEffect)` (iOS only) | ||
|
||
Visual effect applied to the view while the view is hovered. The possible values are: | ||
|
||
- `HoverEffect.None` | ||
- `HoverEffect.Lift` | ||
- `HoverEffect.Highlight` | ||
|
||
Defaults to `HoverEffect.None` | ||
|
||
<BaseEventConfig /> | ||
|
||
## Callbacks | ||
|
||
<BaseEventCallbacks /> | ||
<BaseContinousEventCallbacks /> | ||
|
||
## Event data | ||
|
||
### Event attributes specific to `HoverGesture`: | ||
|
||
### `x` | ||
|
||
X coordinate of the current position of the pointer relative to the view attached to the [`GestureDetector`](./gesture-detector.md). Expressed in point units. | ||
|
||
### `y` | ||
|
||
Y coordinate of the current position of the pointer relative to the view attached to the [`GestureDetector`](./gesture-detector.md). Expressed in point units. | ||
|
||
### `absoluteX` | ||
|
||
X coordinate of the current position of the pointer relative to the window. The value is expressed in point units. It is recommended to use it instead of [`x`](#x) in cases when the original view can be transformed as an effect of the gesture. | ||
|
||
### `absoluteY` | ||
|
||
Y coordinate of the current position of the pointer relative to the window. The value is expressed in point units. It is recommended to use it instead of [`y`](#y) in cases when the original view can be transformed as an effect of the gesture. | ||
|
||
<BaseEventData /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.