-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Gesture relations #3693
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Gesture relations #3693
Changes from all commits
daa017c
1e17cf8
08d1e2b
256f707
19fd985
133319b
e50f6cc
b3ef022
952fbe1
ec9eaa7
60a56e6
fda23cc
e46554b
13f3af4
d7a2b1a
1b04d3c
b8df015
93343da
d1d3efb
03a1e64
cc1313a
dac39cf
51b5a86
92b4a3a
e73ee57
432aee0
b09d746
db5d261
43212af
4ec26c0
fa599af
42fc8c2
f62a6df
504feae
122613f
246f096
e229d84
fac31e2
53ce245
fb88c8c
8e5f07e
feaa167
3f2ea8c
4022b96
8d9fc49
2dc44b8
fab60e5
31c0e8b
832ffb0
bea99ab
fb8c8e3
192556e
7c7876b
3e2b661
8cd19ff
619aed9
b12824b
4ad7f5b
a6f1ecb
761e22a
648f862
ce2de67
ec85bb5
2e99481
adfada7
7ab9b3b
c2c88cc
4baf2cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import RNGestureHandlerDetectorNativeComponent from '../../specs/RNGestureHandlerDetectorNativeComponent'; | ||
const HostGestureDetector = RNGestureHandlerDetectorNativeComponent; | ||
export default HostGestureDetector; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
// This piece of magic traverses the gesture tree and populates `waitFor` and `simultaneousHandlers` | ||
// arrays for each gesture. It traverses the tree recursively using DFS. | ||
// `waitFor` and `simultaneousHandlers` are global data structures that will be populated into each gesture. | ||
// For `waitFor` we need array as order of the gestures matters. | ||
// For `simultaneousHandlers` we use Set as the order doesn't matter. | ||
|
||
import RNGestureHandlerModule from '../../RNGestureHandlerModule'; | ||
import { | ||
isComposedGesture, | ||
prepareRelations, | ||
} from '../hooks/utils/relationUtils'; | ||
import { | ||
ComposedGesture, | ||
ComposedGestureType, | ||
Gesture, | ||
NativeGesture, | ||
} from '../types'; | ||
|
||
// The tree consists of ComposedGestures and NativeGestures. NativeGestures are always leaf nodes. | ||
export const traverseGestureRelations = ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uhm, can we have tests for that 🥺 (follow-up is fine, this is quite large already)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think so 😅 Do you mean jest tests that will check result arrays after DFS? |
||
node: NativeGesture | ComposedGesture, | ||
simultaneousHandlers: Set<number>, | ||
waitFor: number[] = [] | ||
) => { | ||
// If we are in the leaf node, we want to fill gesture relations arrays with current | ||
// waitFor and simultaneousHandlers. We also want to configure relations on the native side. | ||
if (!isComposedGesture(node)) { | ||
node.gestureRelations = prepareRelations(node.config, node.tag); | ||
|
||
node.gestureRelations.simultaneousHandlers.push(...simultaneousHandlers); | ||
akwasniewski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
node.gestureRelations.waitFor.push(...waitFor); | ||
Comment on lines
+30
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't this cause the arrays to grow with duplicates on every render? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, you're right 🙈 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've changed it a bit in ce2de67, let me know what you think |
||
|
||
RNGestureHandlerModule.configureRelations(node.tag, { | ||
waitFor: node.gestureRelations.waitFor, | ||
simultaneousHandlers: node.gestureRelations.simultaneousHandlers, | ||
blocksHandlers: node.gestureRelations.blocksHandlers, | ||
}); | ||
Comment on lines
+33
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this be done outside of this function? If not, can this be renamed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it will be better to rename it, though I'm open for a discussion (cc @akwasniewski). If we stick to rename, what about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is most logical to leave it here. Maybe we could simply adhere to the name of |
||
|
||
return; | ||
} | ||
|
||
// If we are in the composed gesture, we want to traverse its children. | ||
node.gestures.forEach((child) => { | ||
// If child is composed gesture, we have to correctly fill `waitFor` and `simultaneousHandlers`. | ||
if (isComposedGesture(child)) { | ||
// We have to update `simultaneousHandlers` before traversing the child (going top-down). | ||
// Simultaneous is an all-to-all relation - it needs to be prepared when entering the node. | ||
// Exclusive is a one-to-many relation - gesture depends on the preceding ones and not on itself - it should be prepared when leaving the node (bottom-up). | ||
|
||
// If we go from a non-simultaneous gesture to a simultaneous gesture, | ||
// we add the tags of the simultaneous gesture to the `simultaneousHandlers`. | ||
// This way when we traverse the child, we already have the tags of the simultaneous gestures | ||
if ( | ||
node.type !== ComposedGestureType.Simultaneous && | ||
child.type === ComposedGestureType.Simultaneous | ||
) { | ||
child.tags.forEach((tag) => simultaneousHandlers.add(tag)); | ||
} | ||
|
||
// If we go from a simultaneous gesture to a non-simultaneous gesture, | ||
// we remove the tags of the child gestures from the `simultaneousHandlers`, | ||
// as those are not simultaneous with each other. | ||
if ( | ||
node.type === ComposedGestureType.Simultaneous && | ||
child.type !== ComposedGestureType.Simultaneous | ||
) { | ||
child.tags.forEach((tag) => simultaneousHandlers.delete(tag)); | ||
} | ||
|
||
// We will keep the current length of `waitFor` to reset it to previous state | ||
// after traversing the child. | ||
const length = waitFor.length; | ||
|
||
// We traverse the child, passing the current `waitFor` and `simultaneousHandlers`. | ||
traverseGestureRelations(child, simultaneousHandlers, waitFor); | ||
|
||
// After traversing the child, we need to update `waitFor` and `simultaneousHandlers` | ||
|
||
// If we go back from a simultaneous gesture to a non-simultaneous gesture, | ||
// we want to delete the tags of the simultaneous gesture from the `simultaneousHandlers` - | ||
// those gestures are not simultaneous with each other anymore. | ||
if ( | ||
child.type === ComposedGestureType.Simultaneous && | ||
node.type !== ComposedGestureType.Simultaneous | ||
) { | ||
node.tags.forEach((tag) => simultaneousHandlers.delete(tag)); | ||
} | ||
|
||
// If we go back from a non-simultaneous gesture to a simultaneous gesture, | ||
// we want to add the tags of the simultaneous gesture to the `simultaneousHandlers`, | ||
// as those gestures are simultaneous with other children of the current node. | ||
if ( | ||
child.type !== ComposedGestureType.Simultaneous && | ||
node.type === ComposedGestureType.Simultaneous | ||
) { | ||
node.tags.forEach((tag) => simultaneousHandlers.add(tag)); | ||
} | ||
|
||
// If we go back to an exclusive gesture, we want to add the tags of the child gesture to the `waitFor` array. | ||
// This will allow us to pass exclusive gesture tags to the right subtree of the current node. | ||
if (node.type === ComposedGestureType.Exclusive) { | ||
child.tags.forEach((tag) => waitFor.push(tag)); | ||
} | ||
|
||
// If we go back from an exclusive gesture to a non-exclusive gesture, we want to reset the `waitFor` array | ||
// to the previous state, siblings of the exclusive gesture are not exclusive with it. Since we use `push` method to | ||
// add tags to the `waitFor` array, we can override `length` property to reset it to the previous state. | ||
if ( | ||
child.type === ComposedGestureType.Exclusive && | ||
node.type !== ComposedGestureType.Exclusive | ||
) { | ||
waitFor.length = length; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this sorcery? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It resets |
||
} | ||
} | ||
// This means that child is a leaf node. | ||
else { | ||
// We don't want to mark gesture as simultaneous with itself, so we remove its tag from the set. | ||
const hasRemovedTag = simultaneousHandlers.delete(child.tag); | ||
|
||
traverseGestureRelations(child, simultaneousHandlers, waitFor); | ||
|
||
if (hasRemovedTag) { | ||
simultaneousHandlers.add(child.tag); | ||
} | ||
|
||
// In the leaf node, we only care about filling `waitFor` array. | ||
if (node.type === ComposedGestureType.Exclusive) { | ||
waitFor.push(child.tag); | ||
} | ||
} | ||
}); | ||
}; | ||
|
||
export function configureRelations(gesture: Gesture) { | ||
if (isComposedGesture(gesture)) { | ||
traverseGestureRelations( | ||
gesture, | ||
new Set( | ||
// If root is simultaneous, we want to add its tags to the set | ||
gesture.type === ComposedGestureType.Simultaneous ? gesture.tags : [] | ||
) | ||
); | ||
} else { | ||
const relations = prepareRelations(gesture.config, gesture.tag); | ||
|
||
gesture.gestureRelations = relations; | ||
|
||
RNGestureHandlerModule.configureRelations(gesture.tag, relations); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doing so in
resetConfig
resulted in relations not working oniOS
since it was reset after setting those relations. Also now they are not part of the config so it is not the best place to do it. Given that callingupdateRelations
sets, not updates this array:I'm not sure if reset is necessary in that case. If so, let me know.
On android it is handled in
InteractionsManager
, so it is a bit different flow and everything worked out of the box.