Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
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,208 @@
/*
* 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.
#if !TARGET_OS_OSX // [macOS]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is there an iOS diff in a macOS only file?

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(boundsDidChange:)
name:NSViewBoundsDidChangeNotification
object:_scrollView.contentView];
#else // [macOS
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(scrollViewDidScroll:)
name:NSViewBoundsDidChangeNotification
object:_scrollView.contentView];
#endif // macOS]
}

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

#if TARGET_OS_OSX // [macOS
- (void)scrollViewDidScroll:(NSNotification *)notification
{
[self.textInputDelegate scrollViewDidScroll:_scrollView];
}
#endif // macOS]

- (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]
Original file line number Diff line number Diff line change
Expand Up @@ -398,17 +398,17 @@ - (void)textViewDidChangeSelection:(__unused UITextView *)textView
[self textViewProbablyDidChangeSelection];
}

#endif // [macOS]

#pragma mark - UIScrollViewDelegate

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
- (void)scrollViewDidScroll:(RCTUIScrollView *)scrollView // [macOS]
{
if ([_backedTextInputView.textInputDelegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
[_backedTextInputView.textInputDelegate scrollViewDidScroll:scrollView];
}
}

#endif // [macOS]

#if TARGET_OS_OSX // [macOS

#pragma mark - NSTextViewDelegate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, assign, readonly) BOOL textWasPasted;
#else // [macOS
@property (nonatomic, assign) BOOL textWasPasted;
@property (nonatomic, readonly) NSResponder *responder;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

macOS tag?

#endif // macOS]
@property (nonatomic, assign, readonly) BOOL dictationRecognizing;
@property (nonatomic, assign) UIEdgeInsets textContainerInset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,11 @@ - (void)setTextContainerInset:(UIEdgeInsets)textContainerInset

#if TARGET_OS_OSX // [macOS

- (NSResponder *)responder
{
return self;
}

+ (Class)cellClass
{
return RCTUITextFieldCell.class;
Expand Down
Loading
Loading