-
-
Notifications
You must be signed in to change notification settings - Fork 984
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
Add react-native-svg
interface
#3242
base: main
Are you sure you want to change the base?
Conversation
react-native-svg
for improved hit detection of svg elementsreact-native-svg
to improve hit-boxes of svg elements
react-native-svg
to improve hit-boxes of svg elementsreact-native-svg
interface
Integration works as of bd51ac7, but I have to test it a bit more before it's ready. |
…interface (#2555) <!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please follow the template so that the reviewers can easily understand what the code changes affect --> # Summary Adding an `RNGH <-> RNSVG` interface requires usage of `RenderableView.hitTest` to work. ([link](software-mansion/react-native-gesture-handler#3242)) Currently, `RenderableView.hitTest` is `package-private`, meaning it cannot be accessed by other packages. This change does not change any functionality of the library, it only exposes existing functions to other libraries. I only made public those `hitTest` implementation which are strictly necessary for this interface, this is why multiple, if not most `hitTest` implementations remain `package-private` despite the changes made in the PR. ## Test Plan - open the example app, see how the app builds successfully ### What's required for testing (prerequisites)? - `RNSVG`'s `paper-example` app ### What are the steps to reproduce (after prerequisites)? ## Compatibility | OS | Implemented | | ------- | :---------: | | iOS | ❌ | | MacOS | ❌ | | Android | ✅ | | Web | ❌ | ## Checklist <!-- Check completed item, when applicable, via: [X] --> - [X] I have tested this on a device and a simulator - [ ] I added documentation in `README.md` - [ ] ~~I updated the typed files (typescript)~~ - [ ] I added a test for the API in the `__tests__` folder
…tware-mansion/react-native-gesture-handler into @latekvo/add-svg-integration
…rfering with the rest of the system
Will remove this comment, and replace the minimal version check with the latest version, as soon as this PR is released. |
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.
Overall looks ok, left some comments
android/nosvg/src/main/java/com/swmansion/gesturehandler/RNSVGHitTester.kt
Outdated
Show resolved
Hide resolved
android/svg/src/main/java/com/swmansion/gesturehandler/RNSVGHitTester.kt
Outdated
Show resolved
Hide resolved
android/svg/src/main/java/com/swmansion/gesturehandler/RNSVGHitTester.kt
Outdated
Show resolved
Hide resolved
if (view is SvgView) { | ||
val childrenIds = view.children.map { it.id } | ||
|
||
val hasBeenPressed = view.id == pressedId | ||
val hasChildBeenPressed = pressedId in childrenIds | ||
|
||
val pressIsInBounds = 0 < posX && posX < view.width && 0 < posY && posY < view.height | ||
|
||
return (hasBeenPressed || hasChildBeenPressed) && pressIsInBounds | ||
} | ||
|
||
if (view is VirtualView) { | ||
return view.id == highestOrderSvgView.reactTagForTouch(rootX, rootY) | ||
} | ||
return false |
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.
As far as I can see this is function is called iff view is SvgElement
. In that case, can't we simplify it?
if (view is SvgView) { | |
val childrenIds = view.children.map { it.id } | |
val hasBeenPressed = view.id == pressedId | |
val hasChildBeenPressed = pressedId in childrenIds | |
val pressIsInBounds = 0 < posX && posX < view.width && 0 < posY && posY < view.height | |
return (hasBeenPressed || hasChildBeenPressed) && pressIsInBounds | |
} | |
if (view is VirtualView) { | |
return view.id == highestOrderSvgView.reactTagForTouch(rootX, rootY) | |
} | |
return false | |
if (view is VirtualView) { | |
return view.id == highestOrderSvgView.reactTagForTouch(rootX, rootY) | |
} | |
val childrenIds = view.children.map { it.id } | |
val pressIsInBounds = 0 < posX && posX < view.width && 0 < posY && posY < view.height | |
return (view.id == pressedId || pressedId in childrenIds) && pressIsInBounds |
Also it would be nice to use ..
instead of <=
in bounds checks, but 0 might be a bit problematic.
Speaking of which, is it possible to detect click out of bounds, so that posX
will be for example -1
?
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.
Are you sure we want to do that?
To me it seems the simplified version, especially the return statement, is much less readable.
The if
statement also has a double purpose of triggering the smart-cast of view
from View
to SvgView
.
Without the if statement, I'll have to explicitly cast the view
to SvgView
, which will also contribute to making the code less readable.
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.
Also it would be nice to use .. instead of <= in bounds checks, but 0 might be a bit problematic.
Thankfully the ranges in Kotlin are inclusive, so 0
won't cause any issues.
Done in 543a8fe
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.
Speaking of which, is it possible to detect click out of bounds, so that posX will be for example -1?
I think that should be currently possible, the isWithinBounds
call is executed for every gesture handler no matter it's position on the screen.
Are you asking about this in context of hitSlop
?
Most SVG shapes aren't rectangular, so to implement hitSlop
for them it'll likely require a lot more work, as the hitSlop
s will also have to be non-rectangular.
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.
To me it seems the simplified version, especially the return statement, is much less readable.
For me it doesn't make much of a difference if we use pressedId == view.id
instead of hasBeenPressed
, it is simple enough to understand and I wouldn't call it much less readable.
The if statement also has a double purpose of triggering the smart-cast of view from View to SvgView.
Okay, that complicates things a bit. Adding 4 more casts may not be the cleanest solution. I also don't like stuff like x = x as ...
(at least not in TypeScript
). As far as I can see Kotlin
doesn't support union types, so I'm not sure if there's much that we can do (cc kotlin master @j-piasecki).
Are you asking about this in context of hitSlop?
No, I was just wondering whether we have to check if posX >= 0
, since coordinates are view-relative.
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.
Adding 4 more casts may not be the cleanest solution
Only 1 cast will be necessary, but it's still not ideal.
No, I was just wondering whether we have to check if posX >= 0, since coordinates are view-relative.
I see. The received coordinates are often negative, so the posX >= 0
is indeed necessary.
For me it doesn't make much of a difference if we use pressedId == view.id instead of hasBeenPressed, it is simple enough to understand and I wouldn't call it much less readable.
It reduces the self-descriptiveness of both pressedId == view.id
and pressedId in childrenIds
, making it a bit harder to read, but i agree it's not that much of a difference.
If you prefer the simpler notation a lot, i'll change it right away.
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.
Only 1 cast will be necessary, but it's still not ideal.
Do you mean only in first occurrence of view
? If it's only 1 cast than it sounds ok.
If you prefer the simpler notation a lot, i'll change it right away.
It is not a strong opinion, it is just a fact that I don't think it is necessary to add new variables for obvious stuff. If for you it becomes less readable than you can leave it.
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.
Do you mean only in first occurrence of view? If it's only 1 cast than it sounds ok.
Yes, only the first occurrence.
But as I said, just looking at hitTest()
function, it's not in any way clear that view
is limited to being VirtualView
and SvgView
without looking at both the usages of hitTest()
, and the rest of RNSVGHitTester
.
Having if (view is SvgView)
and if (view is VirtualView)
is the only clear indication within hitTest()
that view
can be only either one of those.
Removing that indication in favour of an early return for VirtualView
, and then casting the view
to SvgView
in the rest of the code may seem arbitrary to someone unfamiliar with the code, so I would prefer to keep it the way it is now.
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.
(...) it's not in any way clear that view is limited to being
VirtualView
andSvgView
(...)
I wouldn't say so since we are in the RNSVGHitTester
file. Also, one line above hitTest
there's isSvgElement
method that also indicates which ones are from SVG
.
The thing is, I don't like the idea of using if
as cast if we can use cast directly (as you said, there's only one that is needed)
The if statement also has a double purpose of triggering the smart-cast of
view
fromView
toSvgView
.
Now I'd say that it is its only purpose, am I right?
Removing that indication in favour of an early return for
VirtualView
, and then casting the view toSvgView
in the rest of the code may seem arbitrary to someone unfamiliar with the code, so I would prefer to keep it the way it is now.
It is not really about early return, it is because this if
is unnecessary. Also, even if it would be arbitrary, we can leave a comment explaining that these are the only two possibilities.
What's your stance on this, @j-piasecki?
Description
This PR adds integration with the
react-native-svg
(RNSVG
) to improve hitbox detection of the SVG elements they provide.blocked by software-mansion/react-native-svg#2583
blocked by
nested SvgViews with viewBox prop have invalid hit detection
- No issue opened yetTest plan