From d799558db5cf6a06ac9b49e771f8d6f44b60c3da Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Wed, 4 Nov 2015 04:04:37 -0800 Subject: [PATCH] Fix Groups text input Reviewed By: javache Differential Revision: D2590712 fb-gh-sync-id: 453e20970460c703230547d8fd649383ba7d4c4a --- Libraries/Components/TextInput/TextInput.js | 17 ++--- Libraries/Text/RCTShadowRawText.m | 2 +- Libraries/Text/RCTTextManager.m | 70 ++++++++++++++++++++- Libraries/Text/RCTTextView.h | 2 + Libraries/Text/RCTTextView.m | 5 ++ 5 files changed, 87 insertions(+), 9 deletions(-) diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index 0b01f0ad8..fe1f6f206 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -529,13 +529,16 @@ var TextInput = React.createClass({ this.props.onChange && this.props.onChange(event); this.props.onChangeText && this.props.onChangeText(text); this.setState({mostRecentEventCount: eventCount}, () => { - // This is a controlled component, so make sure to force the native value - // to match. Most usage shouldn't need this, but if it does this will be - // more correct but might flicker a bit and/or cause the cursor to jump. - if (text !== this.props.value && typeof this.props.value === 'string') { - this.refs.input.setNativeProps({ - text: this.props.value, - }); + // NOTE: this doesn't seem to be needed on iOS - keeping for now in case it's required on Android + if (Platform.OS === 'android') { + // This is a controlled component, so make sure to force the native value + // to match. Most usage shouldn't need this, but if it does this will be + // more correct but might flicker a bit and/or cause the cursor to jump. + if (text !== this.props.value && typeof this.props.value === 'string') { + this.refs.input.setNativeProps({ + text: this.props.value, + }); + } } }); }, diff --git a/Libraries/Text/RCTShadowRawText.m b/Libraries/Text/RCTShadowRawText.m index a76cc288b..6d1dd6d25 100644 --- a/Libraries/Text/RCTShadowRawText.m +++ b/Libraries/Text/RCTShadowRawText.m @@ -37,7 +37,7 @@ - (void)setText:(NSString *)text { - if (_text != text) { + if (_text != text && ![_text isEqualToString:text]) { _text = [text copy]; [self dirtyLayout]; [self dirtyText]; diff --git a/Libraries/Text/RCTTextManager.m b/Libraries/Text/RCTTextManager.m index a67cb6cd2..4b536cb29 100644 --- a/Libraries/Text/RCTTextManager.m +++ b/Libraries/Text/RCTTextManager.m @@ -17,8 +17,16 @@ #import "RCTShadowText.h" #import "RCTSparseArray.h" #import "RCTText.h" +#import "RCTTextView.h" #import "UIView+React.h" +@interface RCTShadowText (Private) + +- (NSTextStorage *)buildTextStorageForWidth:(CGFloat)width; + +@end + + @implementation RCTTextManager RCT_EXPORT_MODULE() @@ -54,6 +62,7 @@ RCT_EXPORT_SHADOW_PROPERTY(allowFontScaling, BOOL) - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry { + NSMutableSet *textViewTagsToUpdate = [NSMutableSet new]; for (RCTShadowView *rootView in shadowViewRegistry.allObjects) { if (![rootView isReactRootView]) { // This isn't a root view @@ -77,6 +86,19 @@ RCT_EXPORT_SHADOW_PROPERTY(allowFontScaling, BOOL) RCTLogError(@"Raw text cannot be used outside of a tag. Not rendering string: '%@'", [(RCTShadowRawText *)shadowView text]); } else { + NSNumber *reactTag = shadowView.reactTag; + // This isn't pretty, but hopefully it's temporary + // the problem is, there's no easy way (besides the viewName) + // to tell from the shadowView if the view is an RKTextView + if ([shadowView.viewName hasSuffix:@"TextView"]) { + // Add to textViewTagsToUpdate only if has a RCTShadowText subview + for (RCTShadowView *subview in shadowView.reactSubviews) { + if ([subview isKindOfClass:[RCTShadowText class]]) { + [textViewTagsToUpdate addObject:reactTag]; + break; + } + } + } for (RCTShadowView *child in [shadowView reactSubviews]) { if ([child isTextDirty]) { [queue addObject:child]; @@ -88,7 +110,53 @@ RCT_EXPORT_SHADOW_PROPERTY(allowFontScaling, BOOL) } } - return nil; + /** + * NOTE: this logic is included to support rich text editing inside multiline + * `` controls, a feature which is not yet supported in open source. + * It is required in order to ensure that the textStorage (aka attributed + * string) is copied over from the RCTShadowText to the RCTText view in time + * to be used to update the editable text content. + */ + if (textViewTagsToUpdate.count) { + + NSMutableArray *uiBlocks = [NSMutableArray new]; + for (NSNumber *reactTag in textViewTagsToUpdate) { + RCTShadowView *shadowTextView = shadowViewRegistry[reactTag]; + RCTShadowText *shadowText; + for (RCTShadowText *subview in shadowTextView.reactSubviews) { + if ([subview isKindOfClass:[RCTShadowText class]]) { + shadowText = subview; + break; + } + } + + UIEdgeInsets padding = shadowText.paddingAsInsets; + CGFloat width = shadowText.frame.size.width - (padding.left + padding.right); + NSTextStorage *textStorage = [shadowText buildTextStorageForWidth:width]; + + [uiBlocks addObject:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { + RCTTextView *textView = viewRegistry[reactTag]; + RCTText *text; + for (RCTText *subview in textView.reactSubviews) { + if ([subview isKindOfClass:[RCTText class]]) { + text = subview; + break; + } + } + + text.textStorage = textStorage; + [textView performTextUpdate]; + }]; + } + + return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { + for (RCTViewManagerUIBlock uiBlock in uiBlocks) { + uiBlock(uiManager, viewRegistry); + } + }; + } else { + return nil; + } } - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowText *)shadowView diff --git a/Libraries/Text/RCTTextView.h b/Libraries/Text/RCTTextView.h index c5012ec09..8fc6d4c28 100644 --- a/Libraries/Text/RCTTextView.h +++ b/Libraries/Text/RCTTextView.h @@ -30,4 +30,6 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; +- (void)performTextUpdate; + @end diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m index 93962916c..1080b4d92 100644 --- a/Libraries/Text/RCTTextView.m +++ b/Libraries/Text/RCTTextView.m @@ -309,4 +309,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) return [UIColor colorWithRed:0.0/255.0 green:0.0/255.0 blue:0.098/255.0 alpha:0.22]; } +- (void)performTextUpdate +{ + // Not used (yet) +} + @end