Skip to content

Commit

Permalink
AndroidTextInput: support using commands instead of setNativeProps (n…
Browse files Browse the repository at this point in the history
…ative change)

Summary:
In AndroidTextInput, support codegen'd ViewCommands in native and add three commands that will eventually replace usage of setNativeProps on Android.

TextInput will use these commands in a future diff.

Changelog: [Internal]

Reviewed By: TheSavior

Differential Revision: D18612150

fbshipit-source-id: 5d427040686e8c5ab504dd845bc8ef863f558c35
  • Loading branch information
JoshuaGross authored and facebook-github-bot committed Nov 27, 2019
1 parent 98b8a17 commit 7ab5eb4
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ import type {
Float,
Int32,
WithDefault,
} from 'react-native/Libraries/Types/CodegenTypes';
} from '../../Types/CodegenTypes';
import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes';
import type {TextStyleProp, ViewStyleProp} from '../../StyleSheet/StyleSheet';
import type {ColorValue} from '../../StyleSheet/StyleSheetTypes';
import {requireNativeComponent} from 'react-native';
import codegenNativeCommands from '../../Utilities/codegenNativeCommands';
import * as React from 'react';

export type KeyboardType =
// Cross Platform
Expand Down Expand Up @@ -534,6 +536,33 @@ export type NativeProps = $ReadOnly<{|
text?: ?string,
|}>;

type NativeType = HostComponent<NativeProps>;

interface NativeCommands {
+focus: (viewRef: React.ElementRef<NativeType>) => void;
+blur: (viewRef: React.ElementRef<NativeType>) => void;
+setMostRecentEventCount: (
viewRef: React.ElementRef<NativeType>,
eventCount: Int32,
) => void;
+setTextAndSelection: (
viewRef: React.ElementRef<NativeType>,
mostRecentEventCount: Int32,
value: ?string, // in theory this is nullable
start: Int32,
end: Int32,
) => void;
}

export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
supportedCommands: [
'focus',
'blur',
'setMostRecentEventCount',
'setTextAndSelection',
],
});

const AndroidTextInputNativeComponent: HostComponent<NativeProps> = requireNativeComponent<NativeProps>(
'AndroidTextInput',
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public Object updateState(

return new ReactTextUpdate(
spanned,
-1, // TODO add this into local Data?
state.hasKey("mostRecentEventCount") ? state.getInt("mostRecentEventCount") : -1,
false, // TODO add this into local Data
textViewProps.getTextAlign(),
textBreakStrategy,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import android.text.InputType;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextWatcher;
import android.view.Gravity;
import android.view.KeyEvent;
Expand Down Expand Up @@ -55,6 +56,7 @@
import com.facebook.react.views.text.TextAttributeProps;
import com.facebook.react.views.text.TextInlineImageSpan;
import com.facebook.react.views.text.TextLayoutManager;
import com.facebook.react.views.text.TextTransform;
import com.facebook.yoga.YogaConstants;
import java.lang.reflect.Field;
import java.util.LinkedList;
Expand All @@ -72,6 +74,8 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout

private static final int FOCUS_TEXT_INPUT = 1;
private static final int BLUR_TEXT_INPUT = 2;
private static final int SET_MOST_RECENT_EVENT_COUNT = 3;
private static final int SET_TEXT_AND_SELECTION = 4;

private static final int INPUT_TYPE_KEYBOARD_NUMBER_PAD = InputType.TYPE_CLASS_NUMBER;
private static final int INPUT_TYPE_KEYBOARD_DECIMAL_PAD =
Expand Down Expand Up @@ -177,10 +181,16 @@ public void receiveCommand(
ReactEditText reactEditText, int commandId, @Nullable ReadableArray args) {
switch (commandId) {
case FOCUS_TEXT_INPUT:
reactEditText.requestFocusFromJS();
this.receiveCommand(reactEditText, "focus", args);
break;
case BLUR_TEXT_INPUT:
reactEditText.clearFocusFromJS();
this.receiveCommand(reactEditText, "blur", args);
break;
case SET_MOST_RECENT_EVENT_COUNT:
this.receiveCommand(reactEditText, "setMostRecentEventCount", args);
break;
case SET_TEXT_AND_SELECTION:
this.receiveCommand(reactEditText, "setTextAndSelection", args);
break;
}
}
Expand All @@ -197,9 +207,40 @@ public void receiveCommand(
case "blurTextInput":
reactEditText.clearFocusFromJS();
break;
case "setMostRecentEventCount":
reactEditText.setMostRecentEventCount(args.getInt(0));
break;
case "setTextAndSelection":
int mostRecentEventCount = args.getInt(0);

if (mostRecentEventCount != UNSET) {
String text = args.getString(1);

int start = args.getInt(2);
int end = args.getInt(3);
if (end == UNSET) {
end = start;
}

// TODO: construct a ReactTextUpdate and use that with maybeSetText
// instead of calling setText, etc directly - doing that will definitely cause bugs.
reactEditText.maybeSetText(getReactTextUpdate(text, mostRecentEventCount, start, end));
}
break;
}
}

// TODO: if we're able to fill in all these values and call maybeSetText when appropriate
// I think this is all that's needed to fully support TextInput in Fabric
private ReactTextUpdate getReactTextUpdate(
String text, int mostRecentEventCount, int start, int end) {
SpannableStringBuilder sb = new SpannableStringBuilder();
sb.append(TextTransform.apply(text, TextTransform.UNSET));

return new ReactTextUpdate(
sb, mostRecentEventCount, false, 0, 0, 0, 0, Gravity.NO_GRAVITY, 0, 0, start, end);
}

@Override
public void updateExtraData(ReactEditText view, Object extraData) {
if (extraData instanceof ReactTextUpdate) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,29 +36,40 @@ AttributedString AndroidTextInputShadowNode::getAttributedString(
textAttributes.apply(getProps()->textAttributes);

// Use BaseTextShadowNode to get attributed string from children
{
auto const &attributedString =
BaseTextShadowNode::getAttributedString(textAttributes, *this);
if (!attributedString.isEmpty() || !usePlaceholders) {
return attributedString;
auto const &attributedString =
BaseTextShadowNode::getAttributedString(textAttributes, *this);
if (!attributedString.isEmpty()) {
return attributedString;
}
if (!getProps()->text.empty() || usePlaceholders) {
// If the BaseTextShadowNode didn't detect any child Text nodes, we
// may actually just have a `text` attribute.
// TODO: figure out why BaseTextShadowNode doesn't pick this up, this
// is a bug. A minimal Playground example that triggers this: P122991121
auto textAttributedString = AttributedString{};
auto fragment = AttributedString::Fragment{};
fragment.string = getProps()->text;

if (usePlaceholders) {
// Return placeholder text instead, if text was empty.
if (fragment.string.empty()) {
fragment.string = getProps()->placeholder;
}
// For measurement purposes, we want to make sure that there's at least a
// single character in the string so that the measured height is greater
// than zero. Otherwise, empty TextInputs with no placeholder don't
// display at all.
if (fragment.string.empty()) {
fragment.string = " ";
}
}
fragment.textAttributes = textAttributes;
fragment.parentShadowView = ShadowView(*this);
textAttributedString.appendFragment(fragment);
return textAttributedString;
}

// Return placeholder text instead, if text was empty.
auto placeholderAttributedString = AttributedString{};
auto fragment = AttributedString::Fragment{};
fragment.string = getProps()->placeholder;

// For measurement purposes, we want to make sure that there's at least a
// single character in the string so that the measured height is greater than
// zero. Otherwise, empty TextInputs with no placeholder don't display at all.
if (fragment.string == "") {
fragment.string = " ";
}
fragment.textAttributes = textAttributes;
fragment.parentShadowView = ShadowView(*this);
placeholderAttributedString.appendFragment(fragment);
return placeholderAttributedString;
return attributedString;
}

void AndroidTextInputShadowNode::setTextLayoutManager(
Expand Down

0 comments on commit 7ab5eb4

Please sign in to comment.