Skip to content

Commit

Permalink
Add support for macOS legacy scroller system setting. (#262)
Browse files Browse the repository at this point in the history
* Add support for macOS legacy scroller system setting.

* Add missing comment.  Whitespace cleanup

* Add locals

* Fixed ] in comment
  • Loading branch information
tom-un authored Mar 23, 2020
1 parent 08a90a0 commit d604a2d
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 17 deletions.
9 changes: 9 additions & 0 deletions Libraries/Components/ScrollView/ScrollView.js
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@ export type Props = $ReadOnly<{|
|}>;

type State = {|
contentKey: number, // TODO(macOS ISS#2323203)
layoutHeight: ?number,
...ScrollResponderState,
|};
Expand Down Expand Up @@ -683,6 +684,7 @@ class ScrollView extends React.Component<Props, State> {
_headerLayoutYs: Map<string, number> = new Map();

state = {
contentKey: 1, // TODO(macOS ISS#2323203)
layoutHeight: null,
...ScrollResponder.Mixin.scrollResponderMixinGetInitialState(),
};
Expand Down Expand Up @@ -956,6 +958,10 @@ class ScrollView extends React.Component<Props, State> {
x: Math.max(0, Math.min(maxX, newOffset.x)),
y: Math.max(0, Math.min(maxY, newOffset.y)),
});
};

_handlePreferredScrollerStyleDidChange = (event: ScrollEvent) => {
this.setState({contentKey: this.state.contentKey + 1});
}; // ]TODO(macOS ISS#2323203)

_handleScroll = (e: ScrollEvent) => {
Expand Down Expand Up @@ -1107,6 +1113,7 @@ class ScrollView extends React.Component<Props, State> {
? false
: this.props.removeClippedSubviews
}
key={this.state.contentKey} // TODO(macOS ISS#2323203)
collapsable={false}>
{children}
</ScrollContentContainerViewClass>
Expand Down Expand Up @@ -1138,6 +1145,8 @@ class ScrollView extends React.Component<Props, State> {
// bubble up from TextInputs
onContentSizeChange: null,
onScrollKeyDown: this._handleKeyDown, // TODO(macOS ISS#2323203)
onPreferredScrollerStyleDidChange: this
._handlePreferredScrollerStyleDidChange, // TODO(macOS ISS#2323203)
onLayout: this._handleLayout,
onMomentumScrollBegin: this._scrollResponder
.scrollResponderHandleMomentumScrollBegin,
Expand Down
8 changes: 7 additions & 1 deletion Libraries/Components/View/ViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,19 @@ type DirectEventProps = $ReadOnly<{|
*/
onDoubleClick?: ?(event: SyntheticEvent<{}>) => mixed, // TODO(macOS ISS#2323203)

/**
* This event is fired when the system's preferred scroller style changes.
* The `preferredScrollerStyle` key will be `legacy` or `overlay`.
*/
onPreferredScrollerStyleDidChange?: ?(event: ScrollEvent) => mixed, // TODO(macOS ISS#2323203)

/**
* When `acceptsKeyboardFocus` is true, the system will try to invoke this function
* when the user performs accessibility key down gesture.
*/
onScrollKeyDown?: ?(event: ScrollEvent) => mixed, // TODO(macOS ISS#2323203)

onMouseEnter?: (event: SyntheticEvent<{}>) => mixed, // [TODO(macOS ISS#2323203)
onMouseEnter?: (event: SyntheticEvent<{}>) => mixed, // TODO(macOS ISS#2323203)

/**
* Invoked on mount and layout changes with:
Expand Down
20 changes: 16 additions & 4 deletions Libraries/Lists/VirtualizedList.js
Original file line number Diff line number Diff line change
Expand Up @@ -1104,7 +1104,9 @@ class VirtualizedList extends React.PureComponent<Props, State> {
keyEventHandler = this.props.enableSelectionOnKeyPress
? this._handleKeyDown
: null;
} // ]TODO(macOS ISS#2323203)
}
const preferredScrollerStyleDidChangeHandler = this.props
.onPreferredScrollerStyleDidChange; // ]TODO(macOS ISS#2323203)
const onRefresh = props.onRefresh;
if (this._isNestedWithSameOrientation()) {
// $FlowFixMe - Typing ReactNativeComponent revealed errors
Expand All @@ -1121,6 +1123,9 @@ class VirtualizedList extends React.PureComponent<Props, State> {
<ScrollView
{...props}
onScrollKeyDown={keyEventHandler} // TODO(macOS ISS#2323203)
onPreferredScrollerStyleDidChange={
preferredScrollerStyleDidChangeHandler
} // TODO(macOS ISS#2323203)
refreshControl={
props.refreshControl == null ? (
<RefreshControl
Expand All @@ -1135,11 +1140,18 @@ class VirtualizedList extends React.PureComponent<Props, State> {
/>
);
} else {
// $FlowFixMe Invalid prop usage
return <ScrollView {...props} onScrollKeyDown={keyEventHandler} />; // TODO(macOS ISS#2323203)
return (
// $FlowFixMe Invalid prop usage
<ScrollView
{...props}
onScrollKeyDown={keyEventHandler}
onPreferredScrollerStyleDidChange={
preferredScrollerStyleDidChangeHandler
}
/>
); // TODO(macOS ISS#2323203)
}
};

_onCellLayout(e, cellKey, index) {
const layout = e.nativeEvent.layout;
const next = {
Expand Down
7 changes: 6 additions & 1 deletion Libraries/Lists/VirtualizedSectionList.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,12 +345,17 @@ class VirtualizedSectionList<
keyEventHandler = this.props.enableSelectionOnKeyPress
? this._handleKeyDown
: null;
} // ]TODO(macOS ISS#2323203)
}
const preferredScrollerStyleDidChangeHandler = this.props
.onPreferredScrollerStyleDidChange; // ]TODO(macOS ISS#2323203)
return (
<VirtualizedList
{...this.state.childProps}
ref={this._captureRef}
onScrollKeyDown={keyEventHandler}
onPreferredScrollerStyleDidChange={
preferredScrollerStyleDidChangeHandler
}
{...this.state.selectedRowIndexPath}
/> // TODO(macOS ISS#2323203)
);
Expand Down
1 change: 1 addition & 0 deletions Libraries/Types/CoreEventTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export type ScrollEvent = SyntheticEvent<
zoomScale?: number,
responderIgnoreScroll?: boolean,
key?: string, // TODO(macOS)
preferredScrollerStyle?: string, // TODO(macOS)
|}>,
>;

Expand Down
23 changes: 21 additions & 2 deletions React/Views/ScrollView/RCTScrollContentView.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,34 @@ @implementation RCTScrollContentView

- (void)reactSetFrame:(CGRect)frame
{
[super reactSetFrame:frame];

#if !TARGET_OS_OSX // TODO(macOS ISS#2323203)
RCTScrollView *scrollView = (RCTScrollView *)self.superview.superview;
#else // [TODO(macOS ISS#2323203)
// macOS also has a NSClipView in its hierarchy
RCTScrollView *scrollView = (RCTScrollView *)self.superview.superview.superview;

if (scrollView != nil) {
// On macOS scroll indicators may float over the content view like they do in iOS
// or depending on system preferences they may be outside of the content view
// which means the clip view will be smaller than the scroll view itself.
// In such cases the content view layout must shrink accordingly otherwise
// the contents will overflow causing the scroll indicators to appear unnecessarily.
NSScrollView *platformScrollView = scrollView.scrollView;
if (platformScrollView.scrollerStyle == NSScrollerStyleLegacy) {
NSScroller *verticalScroller = platformScrollView.verticalScroller;
if (!verticalScroller.isHidden) {
frame.size.width -= verticalScroller.frame.size.width;
}
NSScroller *horizontalScroller = platformScrollView.horizontalScroller;
if (!horizontalScroller.isHidden) {
frame.size.height -= horizontalScroller.frame.size.height;
}
}
}
#endif // ]TODO(macOS ISS#2323203)

[super reactSetFrame:frame];

if (!scrollView) {
return;
}
Expand Down
1 change: 1 addition & 0 deletions React/Views/ScrollView/RCTScrollView.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
@property (nonatomic, copy) RCTDirectEventBlock onMomentumScrollBegin;
@property (nonatomic, copy) RCTDirectEventBlock onMomentumScrollEnd;
@property (nonatomic, copy) RCTDirectEventBlock onScrollKeyDown; // TODO(macOS ISS#2323203)
@property (nonatomic, copy) RCTDirectEventBlock onPreferredScrollerStyleDidChange; // TODO(macOS ISS#2323203)

- (void)flashScrollIndicators; // TODO(macOS ISS#2323203)

Expand Down
41 changes: 32 additions & 9 deletions React/Views/ScrollView/RCTScrollView.m
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ - (instancetype)initWithFrame:(CGRect)frame
self.scrollEnabled = YES;
self.hasHorizontalScroller = YES;
self.hasVerticalScroller = YES;
self.autohidesScrollers = YES;
self.panGestureRecognizer = [[NSPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleCustomPan:)];
#else // ]TODO(macOS ISS#2323203)
[self.panGestureRecognizer addTarget:self action:@selector(handleCustomPan:)];
Expand Down Expand Up @@ -756,19 +757,28 @@ - (void)updateClippedSubviews
- (void)viewDidMoveToWindow
{
[super viewDidMoveToWindow];


NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
if ([self window] == nil) {
// Unregister for bounds change notifications
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSViewBoundsDidChangeNotification object:_scrollView.contentView];
}
else {
[defaultCenter removeObserver:self
name:NSViewBoundsDidChangeNotification
object:_scrollView.contentView];
[defaultCenter removeObserver:self
name:NSPreferredScrollerStyleDidChangeNotification
object:nil];
} else {
// Register for bounds change notifications so we can track scrolling
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(scrollViewDocumentViewBoundsDidChange:)
name:NSViewBoundsDidChangeNotification
object:_scrollView.contentView]; // NSClipView
[defaultCenter addObserver:self
selector:@selector(scrollViewDocumentViewBoundsDidChange:)
name:NSViewBoundsDidChangeNotification
object:_scrollView.contentView]; // NSClipView
[defaultCenter addObserver:self
selector:@selector(preferredScrollerStyleDidChange:)
name:NSPreferredScrollerStyleDidChangeNotification
object:nil];
}

_notifyDidScroll = ([self window] != nil);
}
#endif // ]TODO(macOS ISS#2323203)
Expand Down Expand Up @@ -1426,6 +1436,19 @@ - (void)keyDown:(UIEvent*)theEvent
}
}
}

static NSString *RCTStringForScrollerStyle(NSScrollerStyle scrollerStyle) {
switch (scrollerStyle) {
case NSScrollerStyleLegacy:
return @"legacy";
case NSScrollerStyleOverlay:
return @"overlay";
}
}

- (void)preferredScrollerStyleDidChange:(__unused NSNotification *)notification {
RCT_SEND_SCROLL_EVENT(onPreferredScrollerStyleDidChange, (@{ @"preferredScrollerStyle": RCTStringForScrollerStyle([NSScroller preferredScrollerStyle])}));
}
#endif // ]TODO(macOS ISS#2323203)

// Note: setting several properties of UIScrollView has the effect of
Expand Down
1 change: 1 addition & 0 deletions React/Views/ScrollView/RCTScrollViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ - (RCTPlatformView *)view
RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollEnd, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(DEPRECATED_sendUpdatedChildFrames, BOOL)
RCT_EXPORT_OSX_VIEW_PROPERTY(onScrollKeyDown, RCTDirectEventBlock) // TODO(macOS ISS#2323203)
RCT_EXPORT_OSX_VIEW_PROPERTY(onPreferredScrollerStyleDidChange, RCTDirectEventBlock) // TODO(macOS ISS#2323203)
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
RCT_EXPORT_VIEW_PROPERTY(contentInsetAdjustmentBehavior, UIScrollViewContentInsetAdjustmentBehavior)
#endif
Expand Down

0 comments on commit d604a2d

Please sign in to comment.