Skip to content

Commit 00c4d95

Browse files
sammy-SCfacebook-github-bot
authored andcommittedApr 9, 2020
Implement event count for TextInput
Summary: Changelog: [Internal] Implementation of event count for Fabric's Text input. Reviewed By: JoshuaGross Differential Revision: D20800185 fbshipit-source-id: 988692cb2fc786649821cccb06e629b40b9b0479
1 parent 0ef63d0 commit 00c4d95

File tree

10 files changed

+79
-23
lines changed

10 files changed

+79
-23
lines changed
 

‎Libraries/Components/TextInput/TextInput.js

+1
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,7 @@ function InternalTextInput(props: Props): React.Node {
10731073
ref={_setNativeRef}
10741074
{...props}
10751075
dataDetectorTypes={props.dataDetectorTypes}
1076+
mostRecentEventCount={mostRecentEventCount}
10761077
onBlur={_onBlur}
10771078
onChange={_onChange}
10781079
onContentSizeChange={props.onContentSizeChange}

‎Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ exports[`TextInput tests should render as expected: should deep render when mock
66
allowFontScaling={true}
77
focusable={true}
88
forwardedRef={null}
9+
mostRecentEventCount={0}
910
onBlur={[Function]}
1011
onChange={[Function]}
1112
onClick={[Function]}
@@ -32,6 +33,7 @@ exports[`TextInput tests should render as expected: should deep render when not
3233
allowFontScaling={true}
3334
focusable={true}
3435
forwardedRef={null}
36+
mostRecentEventCount={0}
3537
onBlur={[Function]}
3638
onChange={[Function]}
3739
onClick={[Function]}

‎React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm

+12-14
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ @interface RCTTextInputComponentView () <RCTBackedTextInputDelegate, RCTTextInpu
2828
@implementation RCTTextInputComponentView {
2929
TextInputShadowNode::ConcreteState::Shared _state;
3030
UIView<RCTBackedTextInputViewProtocol> *_backedTextInputView;
31-
size_t _stateRevision;
31+
NSUInteger _mostRecentEventCount;
3232
NSAttributedString *_lastStringStateWasUpdatedWith;
3333

3434
/*
@@ -55,7 +55,6 @@ - (instancetype)initWithFrame:(CGRect)frame
5555
_backedTextInputView.frame = self.bounds;
5656
_backedTextInputView.textInputDelegate = self;
5757
_ignoreNextTextInputCall = NO;
58-
_stateRevision = State::initialRevisionValue;
5958
[self addSubview:_backedTextInputView];
6059
}
6160

@@ -180,9 +179,8 @@ - (void)updateState:(State::Shared const &)state oldState:(State::Shared const &
180179
return;
181180
}
182181

183-
if (_state->getRevision() != _stateRevision) {
182+
if (_mostRecentEventCount == _state->getData().mostRecentEventCount) {
184183
auto data = _state->getData();
185-
_stateRevision = _state->getRevision();
186184
[self _setAttributedString:RCTNSAttributedStringFromAttributedStringBox(data.attributedStringBox)];
187185
}
188186
}
@@ -221,8 +219,8 @@ - (void)prepareForRecycle
221219
{
222220
[super prepareForRecycle];
223221
_backedTextInputView.attributedText = [[NSAttributedString alloc] init];
222+
_mostRecentEventCount = 0;
224223
_state.reset();
225-
_stateRevision = State::initialRevisionValue;
226224
_lastStringStateWasUpdatedWith = nil;
227225
_ignoreNextTextInputCall = NO;
228226
}
@@ -360,6 +358,7 @@ - (TextInputMetrics)_textInputMetrics
360358
TextInputMetrics metrics;
361359
metrics.text = RCTStringFromNSString(_backedTextInputView.attributedText.string);
362360
metrics.selectionRange = [self _selectionRange];
361+
metrics.eventCount = _mostRecentEventCount;
363362
return metrics;
364363
}
365364

@@ -370,12 +369,12 @@ - (void)_updateState
370369
if (!_state) {
371370
return;
372371
}
373-
374372
auto data = _state->getData();
375373
_lastStringStateWasUpdatedWith = attributedString;
376374
data.attributedStringBox = RCTAttributedStringBoxFromNSAttributedString(attributedString);
377-
_state->updateState(std::move(data), EventPriority::SynchronousUnbatched);
378-
_stateRevision = _state->getRevision() + 1;
375+
_mostRecentEventCount += 1;
376+
data.mostRecentEventCount = _mostRecentEventCount;
377+
_state->updateState(std::move(data));
379378
}
380379

381380
- (AttributedString::Range)_selectionRange
@@ -407,19 +406,18 @@ - (void)blur
407406

408407
- (void)setMostRecentEventCount:(NSInteger)eventCount
409408
{
410-
// no-op. `eventCount` isn't used in Fabric's TextInput.
411-
// We are keeping it so commands are backwards
412-
// compatible with Paper's TextInput.
409+
_mostRecentEventCount = eventCount;
413410
}
414411

415412
- (void)setTextAndSelection:(NSInteger)eventCount
416413
value:(NSString *__nullable)value
417414
start:(NSInteger)start
418415
end:(NSInteger)end
419416
{
420-
// `eventCount` is ignored, isn't used in Fabric's TextInput.
421-
// We are keeping it so commands are
422-
// backwards compatible with Paper's TextInput.
417+
if (_mostRecentEventCount != eventCount) {
418+
return;
419+
}
420+
423421
if (value) {
424422
NSMutableAttributedString *mutableString =
425423
[[NSMutableAttributedString alloc] initWithAttributedString:_backedTextInputView.attributedText];

‎React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputNativeCommands.h

+24-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,23 @@ RCTTextInputHandleCommand(id<RCTTextInputViewProtocol> componentView, NSString c
5151
}
5252

5353
if ([commandName isEqualToString:@"setMostRecentEventCount"]) {
54-
[componentView setMostRecentEventCount:0];
54+
#if RCT_DEBUG
55+
if ([args count] != 1) {
56+
RCTLogError(
57+
@"%@ command %@ received %d arguments, expected %d.", @"TextInput", commandName, (int)[args count], 1);
58+
return;
59+
}
60+
#endif
61+
62+
NSObject *arg0 = args[0];
63+
#if RCT_DEBUG
64+
if (!RCTValidateTypeOfViewCommandArgument(arg0, [NSNumber class], @"number", @"TextInput", commandName, @"1st")) {
65+
return;
66+
}
67+
#endif
68+
NSInteger eventCount = [(NSNumber *)arg0 intValue];
69+
70+
[componentView setMostRecentEventCount:eventCount];
5571
return;
5672
}
5773

@@ -64,7 +80,13 @@ RCTTextInputHandleCommand(id<RCTTextInputViewProtocol> componentView, NSString c
6480
}
6581
#endif
6682

67-
NSInteger eventCount = 0;
83+
NSObject *arg0 = args[0];
84+
#if RCT_DEBUG
85+
if (!RCTValidateTypeOfViewCommandArgument(arg0, [NSNumber class], @"number", @"TextInput", commandName, @"1st")) {
86+
return;
87+
}
88+
#endif
89+
NSInteger eventCount = [(NSNumber *)arg0 intValue];
6890

6991
NSObject *arg1 = args[1];
7092
#if RCT_DEBUG

‎ReactCommon/fabric/components/textinput/iostextinput/TextInputEventEmitter.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ static jsi::Value textInputMetricsPayload(
2020
"text",
2121
jsi::String::createFromUtf8(runtime, textInputMetrics.text));
2222

23+
payload.setProperty(runtime, "eventCount", textInputMetrics.eventCount);
24+
2325
{
2426
auto selection = jsi::Object(runtime);
2527
selection.setProperty(

‎ReactCommon/fabric/components/textinput/iostextinput/TextInputEventEmitter.h

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class TextInputMetrics {
2222
Point contentOffset;
2323
EdgeInsets contentInset;
2424
Size containerSize;
25+
int eventCount;
2526
};
2627

2728
class TextInputEventEmitter : public ViewEventEmitter {

‎ReactCommon/fabric/components/textinput/iostextinput/TextInputProps.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ TextInputProps::TextInputProps(
4949
"underlineColorAndroid",
5050
sourceProps.underlineColorAndroid,
5151
{})),
52-
text(convertRawProp(rawProps, "text", sourceProps.text, {})){};
52+
text(convertRawProp(rawProps, "text", sourceProps.text, {})),
53+
mostRecentEventCount(convertRawProp(
54+
rawProps,
55+
"mostRecentEventCount",
56+
sourceProps.mostRecentEventCount,
57+
{})){};
5358

5459
TextAttributes TextInputProps::getEffectiveTextAttributes() const {
5560
auto result = TextAttributes::defaultTextAttributes();

‎ReactCommon/fabric/components/textinput/iostextinput/TextInputProps.h

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class TextInputProps final : public ViewProps, public BaseTextProps {
5151
* "Private" (only used by TextInput.js) props
5252
*/
5353
std::string const text{};
54+
int const mostRecentEventCount{0};
5455

5556
/*
5657
* Accessors

‎ReactCommon/fabric/components/textinput/iostextinput/TextInputShadowNode.cpp

+19-6
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,26 @@ void TextInputShadowNode::setTextLayoutManager(
6969
void TextInputShadowNode::updateStateIfNeeded() {
7070
ensureUnsealed();
7171

72-
if (!getState() || getState()->getRevision() == State::initialRevisionValue) {
73-
auto state = TextInputState{};
74-
state.attributedStringBox = AttributedStringBox{getAttributedString()};
75-
state.paragraphAttributes = getConcreteProps().paragraphAttributes;
76-
state.layoutManager = textLayoutManager_;
77-
setStateData(std::move(state));
72+
auto reactTreeAttributedString = getAttributedString();
73+
auto const &state = getStateData();
74+
75+
assert(textLayoutManager_);
76+
assert(
77+
(!state.layoutManager || state.layoutManager == textLayoutManager_) &&
78+
"`StateData` refers to a different `TextLayoutManager`");
79+
80+
if (state.reactTreeAttributedString == reactTreeAttributedString &&
81+
state.layoutManager == textLayoutManager_) {
82+
return;
7883
}
84+
85+
auto newState = TextInputState{};
86+
newState.attributedStringBox = AttributedStringBox{reactTreeAttributedString};
87+
newState.paragraphAttributes = getConcreteProps().paragraphAttributes;
88+
newState.reactTreeAttributedString = reactTreeAttributedString;
89+
newState.layoutManager = textLayoutManager_;
90+
newState.mostRecentEventCount = getConcreteProps().mostRecentEventCount;
91+
setStateData(std::move(newState));
7992
}
8093

8194
#pragma mark - LayoutableShadowNode

‎ReactCommon/fabric/components/textinput/iostextinput/TextInputState.h

+11
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ class TextInputState final {
2828
*/
2929
AttributedStringBox attributedStringBox;
3030

31+
/*
32+
* All content of <TextInput> component represented as an `AttributedString`.
33+
* This stores the previous computed *from the React tree*. This usually
34+
* doesn't change as the TextInput contents are being updated. If it does
35+
* change, we need to wipe out current contents of the TextInput and replace
36+
* with the new value from the tree.
37+
*/
38+
AttributedString reactTreeAttributedString{};
39+
3140
/*
3241
* Represents all visual attributes of a paragraph of text represented as
3342
* a ParagraphAttributes.
@@ -40,6 +49,8 @@ class TextInputState final {
4049
* `AttributedString`.
4150
*/
4251
SharedTextLayoutManager layoutManager;
52+
53+
size_t mostRecentEventCount{0};
4354
};
4455

4556
} // namespace react

0 commit comments

Comments
 (0)
Please sign in to comment.