-
Notifications
You must be signed in to change notification settings - Fork 138
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
NSTabView doesn't send mouseUp event because it doesn't inherit from NSControl #2111
Comments
For the extra case for NSTabView, I think that's a fine PR to make. For the clicks being in the wrong spot, I actually ran into this and meant to write some docs about it: #1904 I think the hit testing can be fixed React Native macOS side so that all custom native components don't have to override hit testing.. but it's been a while. |
@Saadnajmi Thank you, your hitTest plus isFlipped is working better than what I had. I will make a PR for the NSTabView mouseUp soon. I wonder if there are any other elements inheriting from NSView that need to handle mouseDown/Up, but for now all I see is NSTabView. Everything else seems to inherit from NSControl. |
If you are open to it, you can also make one targeting the branch |
Related, I'm curious how well your NSTabView native module works. Any chance you can send a screenshot of it in an app? :) |
I'm having another issue with it, if I add any margin or padding styles on the tabview the click positions will be off by that many pixels. Even when the style is placed on a NSTabView is very tricky to get working because it wants to manage its own subviews based on which tab is active. But React Native also manages the subviews, so you end up with too many subviews. What I did is give each tab the exact same React Native view and let React conditionally render a single view based on the item returned from the I'm actually making a set of AppKit components for React Native Macos. I was forced to make a TabView because the preferences window requires an NSTabViewController in order to render the toolbar buttons in the preferences style. So I went ahead and made a standalone TabView as well. |
Thanks for the info. Yes, I think something like |
Thanks, I'm not ready to make the repo public yet. I'm not sure yet how I will release it. I'm thinking to make a basic version of it free, and maybe a paid version for advanced components. |
<!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please provide enough information so that others can review your pull request. The four fields below are mandatory. --> <!-- This fork of react-native provides React Native for macOS for the community. It also contains some changes that are required for usage internal to Microsoft. We are working on reducing the diff between Facebook's public version of react-native and our microsoft/react-native-macos fork. Long term, we want this fork to only contain macOS concerns and have the other iOS and Android concerns contributed upstream. If you are making a new change then one of the following should be done: - Consider if it is possible to achieve the desired behavior without making a change to microsoft/react-native-macos. Often a change can be made in a layer above in facebook/react-native instead. - Create a corresponding PR against [facebook/react-native](https://github.com/facebook/react-native) **Note:** Ideally you would wait for Facebook feedback before submitting to Microsoft, since we want to ensure that this fork doesn't deviate from upstream. --> ## Summary: <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? --> Fix for issue #2111 (NSTabView doesn't send mouseUp event because it doesn't inherit from NSControl). NSTabView inherits from NSView, not NSControl, so its click events aren't recorded in the _nativeTouches cache leading to mouseUp and mouseDown not firing consistently when clicking on the tab buttons. ## Test Plan: <!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface. --> Code for custom TabView component provided in the linked issue.
@Saadnajmi Not sure where to post this, but I figured out how to improve the hitTest function so that it accounts for margins and padding from CSS. This is for NSBox which puts its subviews in the contentView property, and places a title above the box which adds extra Y space. - (NSView *)hitTest:(NSPoint)point {
if (!self.isHidden && [self mouse:point inRect:self.bounds]) {
for (NSView *subview in self.contentView.subviews) {
NSPoint pointForHitTest = point;
// Compensate for title above NSBox
pointForHitTest.y -= self.titleRect.size.height;
// Compensate for CSS margins
pointForHitTest.y -= self.frame.origin.y;
pointForHitTest.x -= self.frame.origin.x;
// Compensate for CSS padding
pointForHitTest.y -= subview.frame.origin.y;
pointForHitTest.x -= subview.frame.origin.x;
NSView *result = [subview hitTest:pointForHitTest];
if (result != nil) {
return result;
}
}
return self;
}
return nil;
} It seems RN applies margins by moving the view frame's X and Y coordinates, and applies padding by moving the subview frame's X and Y. Changing borderWidth doesn't seem to cause any misalignment, so there is no need to compensate for it in the hitTest. Also to get this to work I created a flipped NSView and set it as the contentView of the NSBox during view initialization. @interface FlippedView : NSView
@end
@implementation FlippedView
- (BOOL)isFlipped {
return YES; // Make the coordinate system top-left origin
}
@end And this in the view method: - (NSBox *)view {
NSBox *box = [[NSBox alloc] init];
FlippedView *flippedView = [[FlippedView alloc] init];
box.contentView = flippedView;
box.contentViewMargins = NSMakeSize(0, 0);
return box;
} |
Better yet, instead of making a flipped view, you can just directly use RCTView because its Y axis is already flipped. Then there is no need for the - (NSBox *)view {
NSBox *box = [NSBox new];
box.contentView = [RCTView new];
box.contentViewMargins = NSMakeSize(0, 0);
return box;
} |
Nevermind what I wrote above about the hitTest. I figured out a simpler version, and it turns out, that's what you already have in the documentation. oops. For anyone looking for the objective-c version - (NSView *)hitTest:(NSPoint)point {
if (!self.isHidden && [self mouse:point inRect:self.frame]) {
for (NSView *subview in self.subviews) {
NSPoint pointForHitTest = [self.superview convertPoint:point toView:subview];
NSView *result = [subview hitTest:pointForHitTest];
if (result != nil) {
return result;
}
}
return self;
}
return nil;
} The part that I was missing is that the point needs to be converted from the superview point. And I needed to check if the point was in |
Environment
Steps to reproduce the bug
Expected Behavior
The tab buttons should activate on click.
Actual Behavior
The first click activates the button but the second click logs the error "Touch is already recorded. This is a critical bug." On the first click, the mouseDown event fires but mouseUp does not. On the second click the mouseDown does not fire but mouseUp does.
Reproducible Demo
No response
Additional context
The bug is located in RCTTouchHandler
NSTabView does not inherit from NSControl. According to the view hierarchy inspector this is the class inheritance hierarchy:
This code change seems to work
However this by itself seems to still have click locations in the wrong place. If I combine this with a hitTest on the custom NSTabView then it works:
The text was updated successfully, but these errors were encountered: