Skip to content
Merged
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,30 @@
/*
* Copyright (c) Microsoft Corporation.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// [macOS]

#if TARGET_OS_OSX

#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 // TARGET_OS_OSX
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
* Copyright (c) Microsoft Corporation.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// [macOS]

#if TARGET_OS_OSX

#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;
}

- (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
{
_scrollView.scrollEnabled = scrollEnabled;
[_clipView setConstrainScrolling:!scrollEnabled];
}

- (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 the UIEdgeInsets to a valid NSSize struct
_forwardingTextView.textContainerInsets = textContainerInsets;
}

@end

#endif // TARGET_OS_OSX
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;
#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 @@ -217,6 +217,11 @@ - (void)setTextContainerInset:(UIEdgeInsets)textContainerInset

#if TARGET_OS_OSX // [macOS

- (NSResponder *)responder
{
return self;
}

+ (Class)cellClass
{
return RCTUITextFieldCell.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#import <React/RCTUITextField.h>
#import <React/RCTUITextView.h>
#import <React/RCTUtils.h>
#if TARGET_OS_OSX // [macOS
#import <React/RCTWrappedTextView.h>
#endif // macOS]

#import "RCTConversions.h"
#import "RCTTextInputNativeCommands.h"
Expand Down Expand Up @@ -86,7 +89,11 @@ - (instancetype)initWithFrame:(CGRect)frame
const auto &defaultProps = TextInputShadowNode::defaultSharedProps();
_props = defaultProps;

#if !TARGET_OS_OSX // [macOS]
_backedTextInputView = defaultProps->multiline ? [RCTUITextView new] : [RCTUITextField new];
#else // [macOS
_backedTextInputView = defaultProps->multiline ? [[RCTWrappedTextView alloc] initWithFrame:self.bounds] : [RCTUITextField new];
#endif // macOS]
_backedTextInputView.textInputDelegate = self;
_ignoreNextTextInputCall = NO;
_comingFromJS = NO;
Expand Down Expand Up @@ -672,7 +679,7 @@ - (void)blur
[_backedTextInputView resignFirstResponder];
#else // [macOS
NSWindow *window = [_backedTextInputView window];
if ([window firstResponder] == _backedTextInputView) {
if ([window firstResponder] == _backedTextInputView.responder) {
[window makeFirstResponder:nil];
}
#endif // macOS]
Expand Down Expand Up @@ -967,7 +974,7 @@ - (void)_setMultiline:(BOOL)multiline
#if !TARGET_OS_OSX // [macOS]
RCTUIView<RCTBackedTextInputViewProtocol> *backedTextInputView = multiline ? [RCTUITextView new] : [RCTUITextField new];
#else // [macOS
RCTUITextView<RCTBackedTextInputViewProtocol> *backedTextInputView = [RCTUITextView new];
RCTPlatformView<RCTBackedTextInputViewProtocol> *backedTextInputView = multiline ? [RCTWrappedTextView new] : [RCTUITextField new];
#endif // macOS]
backedTextInputView.frame = _backedTextInputView.frame;
RCTCopyBackedTextInput(_backedTextInputView, backedTextInputView);
Expand Down
Loading