Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
5b3d71c
[fabric] Support selection of Paragraph component text content
Sep 20, 2023
529930e
[fabric] Fix onPress not working for Text
Oct 9, 2023
5f134a9
[fabric] Add native focus props to ViewComponentView
shwanton Oct 6, 2023
f5278be
[fabric] Don't flatten View if focusable or enableFocusRing
shwanton Oct 9, 2023
9addf74
[fabric] Add iterator to SurfaceTelemetry.cpp
8Keep Nov 9, 2023
c55fb33
[fabric] Selection range should use the correct backed input range
shwanton Nov 2, 2023
1ff3069
[fabric][a11y] Make text render with static text role
Dec 1, 2023
9bd1ab4
[fabric] Add wrapper class for TextView with scroll callback support
Dec 7, 2023
653c34e
[fabric] Add responder property to backing text input view protocol
Dec 7, 2023
2650c61
[fabric] Update backed text input copy method to support view instances
Dec 7, 2023
689b3cb
[fabric] Use wrapped text view for multiline TextInput
Dec 7, 2023
3f9c1d5
[fabric] Support showing/hiding the focus ring for TextInput
Dec 9, 2023
a40aecd
[fabric] Implement escape/cancel key press callback for TextInput
Dec 9, 2023
607c660
[fabric] Submit scroll view metrics when scrolling multiline TextInput
Dec 9, 2023
92ad521
[fabric] Fix random TextInput cursor position changes while typing
Jan 15, 2024
66c6596
[fabric] Add submitKeyEvents property to TextInput
Jan 18, 2024
ca210b3
[fabric] Implement TextInput key down event checking for submit
Jan 18, 2024
1652d20
[fabric] Add support for clearing the TextInput on submit
Jan 18, 2024
f48f566
[fabric] Add support for the secure text entry to TextInput
Jan 19, 2024
f8c2f57
[fabric] Copy accessibility attributes when switching TextInput backi…
Jan 19, 2024
f0277f5
[fabric] Add HostPlatformViewProps to ViewProps
Jan 29, 2024
4585d7b
[fabric] Add HostPlatformViewEventEmitter to ViewEventEmitter
Jan 29, 2024
6fe9604
[fabric] Move focus props to HostPlatformViewProps
Jan 29, 2024
3eb2766
[fabric] Add valid key down/up props to View
Jan 30, 2024
b7c1dc8
[fabric] Add key down/up event emitters to View
Jan 30, 2024
9340c6f
[fabric] Add key down/up handling to View component
Jan 30, 2024
6fae948
[fabric] Add keyboard event handling to TextInput component
Jan 30, 2024
8bd960c
[fabric] Fix missing includes for windows build
Jan 30, 2024
4e68979
[fabric] Add mouse event props to View
Feb 7, 2024
c29a062
[fabric] Add mouse enter/leave tracking to the View component
Feb 7, 2024
121921c
[fabric] Disable view flattening for views using mouse events
Feb 7, 2024
f3287c2
[fabric] Add mouse enter/leave tracking on scroll
Feb 7, 2024
da355fa
[fabric] Check for assigned event emitter before sending key/mouse ev…
Feb 8, 2024
b682b46
[fabric] Add draggedTypes prop to View
Feb 12, 2024
995a6b4
[fabric] Add drag and drop event emitters to View
Feb 12, 2024
6b39bc4
[fabric] Add drag and drop support to View component
Feb 12, 2024
242ce21
[fabric] Add drag and drop support to TextInput component
Feb 12, 2024
1f40bde
[fabric] Limit draggedTypes prop values to supported options
Feb 13, 2024
38c9690
[fabric] Add file paste event emitter to TextInput component
Feb 13, 2024
06f5346
[fabric] Add file paste support to TextInput component
Feb 13, 2024
a863682
[fabric] Fix missing method declaration of View component
Feb 13, 2024
06904fa
[fabric] Fix namespace in View component header
Feb 13, 2024
4e74505
[fabric] Fix crash on key up in non focusable view
Feb 20, 2024
391c4ca
[fabric] Add missing pragma marks in macOS view event emitter
Feb 20, 2024
56f4dce
[fabric] Add focus/blur event emitters to View
Feb 20, 2024
e49e103
[fabric] Add View focus/blur event emitting on first responder change
Feb 20, 2024
90f30a1
[fabric] Add double click event prop to View component
Feb 21, 2024
b8bacdc
[fabric] Add double click support to View component
Feb 21, 2024
a9a354e
[fabric] Make RCTViewComponentView to be layer-backed by enabling wan…
shwanton Apr 27, 2024
a862170
[fabric] Clean up explicit namespace references in View component
Feb 22, 2024
8f90aa3
[fabric] Add support for setting a tool tip on View component
Feb 22, 2024
121165d
[fabric] Add cursor prop to View component
Feb 23, 2024
66dcfc7
[fabric] Add support for updating cursor to View component
Feb 23, 2024
5a8d176
[fabric] TextInput should get focus with `autoFocus` prop
shwanton Mar 13, 2024
1ee22c8
[fabric] Fix random text display for undefined text content
Mar 26, 2024
8918fda
[fabric] TextInput should handle pasted types prop
shwanton Apr 25, 2024
1483eae
[upstream][paper] Fix crash when statically enabling wantsUpdateLayer…
Feb 22, 2024
387db94
[fabric][touch-handler] Remove `cancelTouchWithEvent` till surface to…
shwanton May 1, 2024
6b80a5f
[fabric] Refactor MacOS view props & emitters
shwanton Apr 30, 2024
5375f8a
[fabric] Revert to Core Cursor implementation
shwanton May 1, 2024
855aed1
[fabric] Fix warning & formatting
shwanton May 1, 2024
2ee90c1
Merge branch 'shwanton/fabric-core-fixes' into fabric/more
Saadnajmi Aug 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
[fabric] Add wrapper class for TextView with scroll callback support
Summary:
The multiline text input view on macOS needs its own view hierarchy, wrapping the RCTUITextView in a scroll view to support all the features offered by the React TextInput component.

This diff adds a wrapper class for RCTUITextView that provides the appropriate view hierarchy while still supporting the text input protocols required for text input.

The wrapper forwards all unimplemented methods to the RCTUITextView so that it can be used as a direct substitute for the RCTUITextView. This allows us to reduce the custom changes need for macOS in RCTTextInputComponentView while re-using all the logic in RCTUITextView.

Test Plan: Tested later in this stack.

Reviewers: shawndempsey, #rn-desktop

Reviewed By: shawndempsey

Differential Revision: https://phabricator.intern.facebook.com/D51962394

Tasks: T167538822, T157889591

Tags: uikit-diff
  • Loading branch information
Nick Lefever authored and Saadnajmi committed Sep 4, 2025
commit 9bd1ab4bcf5bc188d86c68414007e92e66d39db2
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#if TARGET_OS_OSX // [macOS

#import <React/RCTUIKit.h>

#import "RCTTextUIKit.h"

#import <React/RCTBackedTextInputDelegate.h>
#import <React/RCTBackedTextInputViewProtocol.h>

NS_ASSUME_NONNULL_BEGIN

@interface RCTWrappedTextView : RCTPlatformView <RCTBackedTextInputViewProtocol>

@property (nonatomic, weak) id<RCTBackedTextInputDelegate> textInputDelegate;
@property (assign) BOOL hideVerticalScrollIndicator;

@end

NS_ASSUME_NONNULL_END

#endif // macOS]
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#if TARGET_OS_OSX // [macOS

#import <React/RCTWrappedTextView.h>

#import <React/RCTUITextView.h>
#import <React/RCTTextAttributes.h>

@implementation RCTWrappedTextView {
RCTUITextView *_forwardingTextView;
RCTUIScrollView *_scrollView;
RCTClipView *_clipView;
}

- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

self.hideVerticalScrollIndicator = NO;

_scrollView = [[RCTUIScrollView alloc] initWithFrame:self.bounds];
_scrollView.backgroundColor = [RCTUIColor clearColor];
_scrollView.drawsBackground = NO;
_scrollView.borderType = NSNoBorder;
_scrollView.hasHorizontalRuler = NO;
_scrollView.hasVerticalRuler = NO;
_scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[_scrollView setHasVerticalScroller:YES];
[_scrollView setHasHorizontalScroller:NO];

_clipView = [[RCTClipView alloc] initWithFrame:_scrollView.bounds];
[_scrollView setContentView:_clipView];

_forwardingTextView = [[RCTUITextView alloc] initWithFrame:_scrollView.bounds];
_forwardingTextView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_forwardingTextView.delegate = self;

_forwardingTextView.verticallyResizable = YES;
_forwardingTextView.horizontallyResizable = YES;
_forwardingTextView.textContainer.containerSize = NSMakeSize(FLT_MAX, FLT_MAX);
_forwardingTextView.textContainer.widthTracksTextView = YES;
_forwardingTextView.textInputDelegate = self;

_scrollView.documentView = _forwardingTextView;
_scrollView.contentView.postsBoundsChangedNotifications = YES;

// Enable the focus ring by default
_scrollView.enableFocusRing = YES;
[self addSubview:_scrollView];

// a register for those notifications on the content view.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(boundsDidChange:)
name:NSViewBoundsDidChangeNotification
object:_scrollView.contentView];
}

return self;
}

- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (BOOL)isFlipped
{
return YES;
}

#pragma mark -
#pragma mark Method forwarding to text view

- (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation invokeWithTarget:_forwardingTextView];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
if ([_forwardingTextView respondsToSelector:selector]) {
return [_forwardingTextView methodSignatureForSelector:selector];
}

return [super methodSignatureForSelector:selector];
}

- (void)boundsDidChange:(NSNotification *)notification
{
}

#pragma mark -
#pragma mark First Responder forwarding

- (NSResponder *)responder
{
return _forwardingTextView;
}

- (BOOL)acceptsFirstResponder
{
return _forwardingTextView.acceptsFirstResponder;
}

- (BOOL)becomeFirstResponder
{
return [_forwardingTextView becomeFirstResponder];
}

- (BOOL)resignFirstResponder
{
return [_forwardingTextView resignFirstResponder];
}

#pragma mark -
#pragma mark Text Input delegate forwarding

- (id<RCTBackedTextInputDelegate>)textInputDelegate
{
return _forwardingTextView.textInputDelegate;
}

- (void)setTextInputDelegate:(id<RCTBackedTextInputDelegate>)textInputDelegate
{
_forwardingTextView.textInputDelegate = textInputDelegate;
}

#pragma mark -
#pragma mark Scrolling control

- (BOOL)scrollEnabled
{
return _scrollView.isScrollEnabled;
}

- (void)setScrollEnabled:(BOOL)scrollEnabled
{
if (scrollEnabled) {
_scrollView.scrollEnabled = YES;
[_clipView setConstrainScrolling:NO];
} else {
_scrollView.scrollEnabled = NO;
[_clipView setConstrainScrolling:YES];
}
}

- (BOOL)shouldShowVerticalScrollbar
{
// Hide vertical scrollbar if explicity set to NO
if (self.hideVerticalScrollIndicator) {
return NO;
}

// Hide vertical scrollbar if attributed text overflows view
CGSize textViewSize = [_forwardingTextView intrinsicContentSize];
NSClipView *clipView = (NSClipView *)_scrollView.contentView;
if (textViewSize.height > clipView.bounds.size.height) {
return YES;
};

return NO;
}

- (void)textInputDidChange
{
[_scrollView setHasVerticalScroller:[self shouldShowVerticalScrollbar]];
}

- (void)setAttributedText:(NSAttributedString *)attributedText
{
[_forwardingTextView setAttributedText:attributedText];
[_scrollView setHasVerticalScroller:[self shouldShowVerticalScrollbar]];
}

#pragma mark -
#pragma mark Text Container Inset override for NSTextView

// This method is there to match the textContainerInset property on RCTUITextField
- (void)setTextContainerInset:(UIEdgeInsets)textContainerInsets
{
// RCTUITextView has logic in setTextContainerInset[s] to convert th UIEdgeInsets to a valid NSSize struct
_forwardingTextView.textContainerInsets = textContainerInsets;
}

@end

#endif // macOS]