-
Notifications
You must be signed in to change notification settings - Fork 486
[Windows] Add onContextMenu callback to GestureView #599
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
[Windows] Add onContextMenu callback to GestureView #599
Conversation
src/common/Types.ts
Outdated
| clientY: number; | ||
| pageX: number; | ||
| pageY: number; | ||
| isRightButton?: boolean; // UWP only, for desktop context menu |
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.
For consistency with MouseEvent, this should be called "button", it should return the integer corresponding to the button, and it should be implemented for all mouse-based platforms (not just UWP).
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, since this is an addition to the public interface, it will need to be documented (in GestureView.md) and added to the ReactXP test app (GestureViewTest.tsx).
|
Changes:
|
src/common/Types.ts
Outdated
| export interface MouseEvent extends SyntheticEvent { | ||
| altKey: boolean; | ||
| button: number; | ||
| button: MouseButton; |
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.
This is a breaking interface change. While I like the use of an enumeration here, it's not worth the pain associated with breaking existing code. Please revert back to "number".
src/common/Types.ts
Outdated
| pageX?: number; | ||
| pageY?: number; | ||
| touches: TouchList; | ||
| button?: number; // Mac, web native events |
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.
We shouldn't reflect this differently on Mac/web/UWP. We should use "button" on all three platforms. Please remove isRightButton and isMiddleButton.
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.
That's not up to me - there are attributes coming directly from native code. While I agree it would be great to have them unified, it would require changes in react-native-windows.
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 the job of ReactXP to eliminate differences between the underlying RN implementations. Why can't you simply translate the isRightButton and isMiddleButton into common "button" values?
src/common/Types.ts
Outdated
| export type FocusEvent = SyntheticEvent; | ||
|
|
||
| export enum MouseButton { | ||
| Primary, |
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.
Please remove this enumeration since its use in MouseEvent will result in a breaking change.
src/native-common/GestureView.tsx
Outdated
| if (this.props.onTap) { | ||
| const button = EventHelpers.toMouseButton(e); | ||
| if (button === Types.MouseButton.Secondary) { | ||
| // always handle secondary button, even if context menu is not set - it shouldn't trigger onTap |
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.
Nit - for consistency, capitalize comment and end in period.
src/native-common/GestureView.tsx
Outdated
| } | ||
|
|
||
| private _sendDoubleTapEvent(e: Types.TouchEvent) { | ||
| // if user did a double click with different mouse buttons, eg. left (50ms) right |
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.
Nit - for consistency, capitalize comment and end in period.
| const clientRect = this._getGestureViewClientRect(); | ||
|
|
||
| if (clientRect) { | ||
| const tapEvent: Types.TapGestureState = { |
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.
The TapGestureState doesn't derive from SyntheticEvent, so it doesn't offer the standard event methods preventDefault and stopPropagation. I think that's a problem here. I think we have two options:
- Change the TapGestureState to inherit from SyntheticEvent and implement these two methods - and fill in the other fields that go along with SyntheticEvent
- Always call e.preventDefault() and e.stopPropagation() for onContextMenu in a gesture view and document it as such.
Option #1 is more general and arguably more consistent, but it's a big change. For that reason, I am leaning to option #2. Let's discuss.
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.
I think #2 is consistent with all the other events GestureView is handling - none of them bubble down. CMIIW, but you can't get onPress on a parent. So the only thing missing here is possibly e.stopPropagation() as e.preventDefault is a few lines below already.
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.
Yes, that's a good argument. Please add the call to e.stopPropagation.
5add235 to
b1ea334
Compare
src/common/Types.ts
Outdated
| onPanHorizontal?: (gestureState: PanGestureState) => void; | ||
| onTap?: (gestureState: TapGestureState) => void; | ||
| onDoubleTap?: (gestureState: TapGestureState) => void; | ||
| onContextMenuGesture?: (gestureState: TapGestureState) => void; |
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.
This should be "onContextMenu" for consistency. We don't add the word "Gesture" in any other cases.
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.
This can't be onContextMenu because of conflicting interface (ViewPropsShared). As discussed in the chat you created:
onContextMenuis defined as(e: MouseEvent)which 1) doesn't make sense for touch interface 2) would be hard to fake due to how GestureView works now and would require serious refactoring of the component.All the other callbacks for GestureView are
(gestureState: TapGestureState)so my proposal is to keep up with this and addonContextMenuGesture(gestureState: TapGestureState).Thoughts?
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.
Yeah, I realize I used the name "onContextMenuGesture" in the chat, but now that I'm reviewing the change, I think "onContextMenu" is better for consistency. There's no conflict with ViewPropsShared because GestureProps has no inheritance relationship with ViewPropsShared.
The GestureView documentation also needs to be updated to include the new prop.
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.
Yes, good point about documentation. I'll add it.
There's no conflict with ViewPropsShared because GestureProps has no inheritance relationship with ViewPropsShared.
src/native-common/GestureView.tsx(42,52): error TS2344: Type 'GestureViewProps' does not satisfy the constraint 'ViewProps'.
Types of property 'onContextMenu' are incompatible.
Type '((gestureState: TapGestureState) => void) | undefined' is not assignable to type '((e: MouseEvent) => void) | undefined'.
GestureView extends ViewBase<Types.GestureViewProps, {}> -> ViewBase<P extends Types.ViewProps, S> -> ViewProps extends ViewPropsShared. Therefore GestureViewProps need to be interface compatible with ViewPropsShared.
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.
Yikes, that's a bug. GestureView shouldn't be extending ViewBase like that. Neither should ScrollView. I'm surprised that the TypeScript compiler didn't complain about that because ViewBase requires that "P extends Types.ViewProps", and GestureViewProps doesn't properly extend this interface. I'm working on a fix, and I'll let you know when I'm done.
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.
OK, I just pushed a fix for the bug. Turns out it was a problem for some implementations of GestureView, ScrollView and WebView. I've fixed all of them.
| const nativeEvent = e as any; | ||
| if (nativeEvent.button !== undefined) { | ||
| return nativeEvent.button; | ||
| } else if (nativeEvent.isRightButton) { |
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 "isRightButton" shouldn't be capitalized? It was in the old code. Same with IsMiddleButton. Was the old code incorrect?
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.
I believe so (look right below at method isRightMouseButton, it suddenly checks camelCase isRightButton). Mac sends button property, RNUWP sends camelCase isRightButton. isMiddleButton is not implemented right now at all, I added it for future compatibility with RNUWP (unless they switch to button too).
| const clientRect = this._getGestureViewClientRect(); | ||
|
|
||
| if (clientRect) { | ||
| const tapEvent: Types.TapGestureState = { |
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.
Yes, that's a good argument. Please add the call to e.stopPropagation.
| const nativeEvent = e as any; | ||
| if (nativeEvent.button !== undefined) { | ||
| return nativeEvent.button; | ||
| } else if (nativeEvent.isRightButton || nativeEvent.IsRightButton) { |
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 mentioned in previous PR, I tested current implementations and it seems that UpperCase IsRightButton is not produced by any native code, but I'd rather keep backwards compatibility.
| private _sendContextMenuEvent = (e: React.MouseEvent<any>) => { | ||
| if (this.props.onContextMenu) { | ||
| e.preventDefault(); | ||
| e.stopPropagation(); |
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.
I moved these a bit earlier to keep this behavior on par with the rest on reactxp - events are being cancelled before their synthentic handlers. At the same time if onContextMenu is not defined, I don't think we should prevent the event from bubbling down.
* add isRightButton property to GestureView tap * generic handling of mouse tap on GestureView * add onContextMenu handling * add conversion for Mac * cleanup whitespace changes * PR fixes: change MouseEvent.button back to number, unify comment formatting * rename onContextMenuGesture to onContextMenu; add e.stopPropagation to web
When using RX.GestureView as an interaction handler, it's impossible to detect right mouse button clicks right now in UWP. This info is already propagated from native code in the event received by RX, but it's hidden by GestureView. This PR passes the information further so that user can detect right clicks.