Add C++ AndroidTextInput component for backwards-compatible Fabric support of TextInput on Android

Summary: Support existing, backwards-compatible AndroidTextInput component for minimal support of TextInput on Android.

Reviewed By: shergin, mdvacca

Differential Revision: D17086758

fbshipit-source-id: 25726f22229e0d5dfe96eb36b386a5317601283d
This commit is contained in:
Joshua Gross
2019-08-30 19:04:14 -07:00
committed by Facebook Github Bot
parent 898124541c
commit 5abe5843e2
13 changed files with 1200 additions and 5 deletions
@@ -6,6 +6,7 @@
*/
package com.facebook.react.views.textinput;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
@@ -20,6 +21,7 @@ import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
@@ -29,6 +31,7 @@ import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.module.annotations.ReactModule;
@@ -50,7 +53,9 @@ import com.facebook.react.views.text.DefaultStyleValuesUtil;
import com.facebook.react.views.text.ReactFontManager;
import com.facebook.react.views.text.ReactTextUpdate;
import com.facebook.react.views.text.TextInlineImageSpan;
import com.facebook.react.views.text.TextLayoutManager;
import com.facebook.yoga.YogaConstants;
import com.facebook.yoga.YogaMeasureMode;
import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.Map;
@@ -91,6 +96,8 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
private static final InputFilter[] EMPTY_FILTERS = new InputFilter[0];
private static final int UNSET = -1;
@Nullable private static EditText mDummyEditText = null;
@Override
public String getName() {
return REACT_CLASS;
@@ -1075,4 +1082,18 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
"sentences",
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES));
}
/** Measure function for Fabric. */
@Override
public long measure(
Context context,
ReadableMap localData,
ReadableMap props,
ReadableMap state,
float width,
YogaMeasureMode widthMode,
float height,
YogaMeasureMode heightMode) {
return TextLayoutManager.measureText(
context, localData, props, width, widthMode, height, heightMode);
}
}
@@ -6,9 +6,8 @@
*/
#include "SliderShadowNode.h"
#include "SliderLocalData.h"
#include <react/components/slider/SliderLocalData.h>
#include <react/components/slider/SliderShadowNode.h>
#include <react/core/LayoutContext.h>
namespace facebook {
@@ -19,7 +19,7 @@ namespace react {
AttributedString BaseTextShadowNode::getAttributedString(
const TextAttributes &textAttributes,
const SharedShadowNode &parentNode) const {
const SharedShadowNode &parentNode) {
auto attributedString = AttributedString{};
for (const auto &childNode : parentNode->getChildren()) {
@@ -21,10 +21,14 @@ class BaseTextShadowNode {
public:
/*
* Returns a `AttributedString` which represents text content of the node.
* This is static so that both Paragraph (which subclasses BaseText) and
* TextInput (which does not) can use this.
* TODO T53299884: decide if this should be moved out and made a static
* function, or if TextInput should inherit from BaseTextShadowNode.
*/
AttributedString getAttributedString(
static AttributedString getAttributedString(
const TextAttributes &baseTextAttributes,
const SharedShadowNode &parentNode) const;
const SharedShadowNode &parentNode);
};
} // namespace react
@@ -0,0 +1,98 @@
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_debug_preprocessor_flags")
load(
"//tools/build_defs/oss:rn_defs.bzl",
"ANDROID",
"APPLE",
"CXX",
"YOGA_CXX_TARGET",
"fb_xplat_cxx_test",
"get_apple_compiler_flags",
"get_apple_inspector_flags",
"react_native_xplat_target",
"rn_xplat_cxx_library",
"subdir_glob",
)
APPLE_COMPILER_FLAGS = get_apple_compiler_flags()
rn_xplat_cxx_library(
name = "androidtextinput",
srcs = glob(
["**/*.cpp"],
exclude = glob(["tests/**/*.cpp"]),
),
headers = glob(
["**/*.h"],
exclude = glob(["tests/**/*.h"]),
),
header_namespace = "",
exported_headers = subdir_glob(
[
("", "*.h"),
("androidtextinput", "*.h"),
],
prefix = "react/components/androidtextinput",
),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
cxx_tests = [":tests"],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_preprocessor_flags = get_debug_preprocessor_flags() + get_apple_inspector_flags(),
force_static = True,
platforms = (ANDROID, APPLE, CXX),
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
visibility = ["PUBLIC"],
deps = [
"fbsource//xplat/fbsystrace:fbsystrace",
"fbsource//xplat/folly:evicting_cache_map",
"fbsource//xplat/folly:headers_only",
"fbsource//xplat/folly:memory",
"fbsource//xplat/folly:molly",
"fbsource//xplat/third-party/glog:glog",
YOGA_CXX_TARGET,
react_native_xplat_target("utils:utils"),
react_native_xplat_target("fabric/attributedstring:attributedstring"),
react_native_xplat_target("fabric/core:core"),
react_native_xplat_target("fabric/debug:debug"),
react_native_xplat_target("fabric/graphics:graphics"),
react_native_xplat_target("fabric/textlayoutmanager:textlayoutmanager"),
react_native_xplat_target("fabric/components/text:text"),
react_native_xplat_target("fabric/components/view:view"),
react_native_xplat_target("fabric/components/image:image"),
react_native_xplat_target("fabric/uimanager:uimanager"),
react_native_xplat_target("fabric/imagemanager:imagemanager"),
],
)
fb_xplat_cxx_test(
name = "tests",
srcs = glob(["tests/**/*.cpp"]),
headers = glob(["tests/**/*.h"]),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
contacts = ["oncall+react_native@xmail.facebook.com"],
platforms = (
# `Apple` and `Android` flavors are disabled because the module depends on `textlayoutmanager` which requires real an Emulator/Simulator to run.
# At the same time, the code of tests does not rely on the simulator capabilities and it would be wasteful to add `fbandroid_use_instrumentation_test = True`.
# (Beware of this option though.)
# ANDROID,
# APPLE,
CXX
),
deps = [
"fbsource//xplat/folly:molly",
"fbsource//xplat/third-party/gmock:gtest",
":androidtextinput",
],
)
@@ -0,0 +1,46 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/core/ConcreteComponentDescriptor.h>
#include "AndroidTextInputShadowNode.h"
namespace facebook {
namespace react {
/*
* Descriptor for <AndroidTextInput> component.
*/
class AndroidTextInputComponentDescriptor final
: public ConcreteComponentDescriptor<AndroidTextInputShadowNode> {
public:
AndroidTextInputComponentDescriptor(
EventDispatcher::Shared eventDispatcher,
const ContextContainer::Shared &contextContainer)
: ConcreteComponentDescriptor<AndroidTextInputShadowNode>(
eventDispatcher,
contextContainer) {}
protected:
void adopt(UnsharedShadowNode shadowNode) const override {
assert(std::dynamic_pointer_cast<AndroidTextInputShadowNode>(shadowNode));
auto concreteShadowNode =
std::static_pointer_cast<AndroidTextInputShadowNode>(shadowNode);
concreteShadowNode->setContextContainer(
const_cast<ContextContainer *>(getContextContainer().get()));
concreteShadowNode->dirtyLayout();
concreteShadowNode->enableMeasurement();
ConcreteComponentDescriptor::adopt(shadowNode);
}
};
} // namespace react
} // namespace facebook
@@ -0,0 +1,179 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "AndroidTextInputEventEmitter.h"
namespace facebook {
namespace react {
void AndroidTextInputEventEmitter::onBlur(
AndroidTextInputOnBlurStruct event) const {
dispatchEvent("blur", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
return payload;
});
}
void AndroidTextInputEventEmitter::onFocus(
AndroidTextInputOnFocusStruct event) const {
dispatchEvent("focus", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
return payload;
});
}
void AndroidTextInputEventEmitter::onChange(
AndroidTextInputOnChangeStruct event) const {
dispatchEvent("change", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
payload.setProperty(runtime, "eventCount", event.eventCount);
payload.setProperty(runtime, "text", event.text);
return payload;
});
}
void AndroidTextInputEventEmitter::onChangeText(
AndroidTextInputOnChangeTextStruct event) const {
dispatchEvent(
"changeText", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
payload.setProperty(runtime, "eventCount", event.eventCount);
payload.setProperty(runtime, "text", event.text);
return payload;
});
}
void AndroidTextInputEventEmitter::onContentSizeChange(
AndroidTextInputOnContentSizeChangeStruct event) const {
dispatchEvent(
"contentSizeChange", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
{
auto contentSize = jsi::Object(runtime);
contentSize.setProperty(runtime, "width", event.contentSize.width);
contentSize.setProperty(runtime, "height", event.contentSize.height);
payload.setProperty(runtime, "contentSize", contentSize);
}
return payload;
});
}
void AndroidTextInputEventEmitter::onTextInput(
AndroidTextInputOnTextInputStruct event) const {
dispatchEvent("textInput", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
payload.setProperty(runtime, "text", event.text);
payload.setProperty(runtime, "previousText", event.previousText);
{
auto range = jsi::Object(runtime);
range.setProperty(runtime, "start", event.range.start);
range.setProperty(runtime, "end", event.range.end);
payload.setProperty(runtime, "range", range);
}
return payload;
});
}
void AndroidTextInputEventEmitter::onEndEditing(
AndroidTextInputOnEndEditingStruct event) const {
dispatchEvent(
"endEditing", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
payload.setProperty(runtime, "text", event.text);
return payload;
});
}
void AndroidTextInputEventEmitter::onSelectionChange(
AndroidTextInputOnSelectionChangeStruct event) const {
dispatchEvent(
"selectionChange", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
{
auto selection = jsi::Object(runtime);
selection.setProperty(runtime, "start", event.selection.start);
selection.setProperty(runtime, "end", event.selection.end);
payload.setProperty(runtime, "selection", selection);
}
return payload;
});
}
void AndroidTextInputEventEmitter::onSubmitEditing(
AndroidTextInputOnSubmitEditingStruct event) const {
dispatchEvent(
"submitEditing", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
payload.setProperty(runtime, "text", event.text);
return payload;
});
}
void AndroidTextInputEventEmitter::onKeyPress(
AndroidTextInputOnKeyPressStruct event) const {
dispatchEvent("keyPress", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
payload.setProperty(runtime, "key", event.key);
return payload;
});
}
void AndroidTextInputEventEmitter::onScroll(
AndroidTextInputOnScrollStruct event) const {
dispatchEvent("scroll", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
payload.setProperty(
runtime, "responderIgnoreScroll", event.responderIgnoreScroll);
{
auto contentInset = jsi::Object(runtime);
contentInset.setProperty(runtime, "top", event.contentInset.top);
contentInset.setProperty(runtime, "bottom", event.contentInset.bottom);
contentInset.setProperty(runtime, "left", event.contentInset.left);
contentInset.setProperty(runtime, "right", event.contentInset.right);
payload.setProperty(runtime, "contentInset", contentInset);
}
{
auto contentOffset = jsi::Object(runtime);
contentOffset.setProperty(runtime, "x", event.contentOffset.x);
contentOffset.setProperty(runtime, "y", event.contentOffset.y);
payload.setProperty(runtime, "contentOffset", contentOffset);
}
{
auto contentSize = jsi::Object(runtime);
contentSize.setProperty(runtime, "width", event.contentSize.width);
contentSize.setProperty(runtime, "height", event.contentSize.height);
payload.setProperty(runtime, "contentSize", contentSize);
}
{
auto layoutMeasurement = jsi::Object(runtime);
layoutMeasurement.setProperty(
runtime, "width", event.layoutMeasurement.width);
layoutMeasurement.setProperty(
runtime, "height", event.layoutMeasurement.height);
payload.setProperty(runtime, "layoutMeasurement", layoutMeasurement);
}
{
auto velocity = jsi::Object(runtime);
velocity.setProperty(runtime, "x", event.velocity.x);
velocity.setProperty(runtime, "y", event.velocity.y);
payload.setProperty(runtime, "velocity", velocity);
}
return payload;
});
}
} // namespace react
} // namespace facebook
@@ -0,0 +1,148 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/components/view/ViewEventEmitter.h>
namespace facebook {
namespace react {
struct AndroidTextInputOnBlurStruct {
int target;
};
struct AndroidTextInputOnFocusStruct {
int target;
};
struct AndroidTextInputOnChangeStruct {
int target;
int eventCount;
std::string text;
};
struct AndroidTextInputOnChangeTextStruct {
int target;
int eventCount;
std::string text;
};
struct AndroidTextInputOnContentSizeChangeContentSizeStruct {
double width;
double height;
};
struct AndroidTextInputOnContentSizeChangeStruct {
int target;
AndroidTextInputOnContentSizeChangeContentSizeStruct contentSize;
};
struct AndroidTextInputOnTextInputRangeStruct {
double start;
double end;
};
struct AndroidTextInputOnTextInputStruct {
int target;
std::string text;
std::string previousText;
AndroidTextInputOnTextInputRangeStruct range;
};
struct AndroidTextInputOnEndEditingStruct {
int target;
std::string text;
};
struct AndroidTextInputOnSelectionChangeSelectionStruct {
double start;
double end;
};
struct AndroidTextInputOnSelectionChangeStruct {
int target;
AndroidTextInputOnSelectionChangeSelectionStruct selection;
};
struct AndroidTextInputOnSubmitEditingStruct {
int target;
std::string text;
};
struct AndroidTextInputOnKeyPressStruct {
int target;
std::string key;
};
struct AndroidTextInputOnScrollContentInsetStruct {
double top;
double bottom;
double left;
double right;
};
struct AndroidTextInputOnScrollContentOffsetStruct {
double x;
double y;
};
struct AndroidTextInputOnScrollContentSizeStruct {
double width;
double height;
};
struct AndroidTextInputOnScrollLayoutMeasurementStruct {
double width;
double height;
};
struct AndroidTextInputOnScrollVelocityStruct {
double x;
double y;
};
struct AndroidTextInputOnScrollStruct {
int target;
bool responderIgnoreScroll;
AndroidTextInputOnScrollContentInsetStruct contentInset;
AndroidTextInputOnScrollContentOffsetStruct contentOffset;
AndroidTextInputOnScrollContentSizeStruct contentSize;
AndroidTextInputOnScrollLayoutMeasurementStruct layoutMeasurement;
AndroidTextInputOnScrollVelocityStruct velocity;
};
class AndroidTextInputEventEmitter : public ViewEventEmitter {
public:
using ViewEventEmitter::ViewEventEmitter;
void onBlur(AndroidTextInputOnBlurStruct value) const;
void onFocus(AndroidTextInputOnFocusStruct value) const;
void onChange(AndroidTextInputOnChangeStruct value) const;
void onChangeText(AndroidTextInputOnChangeTextStruct value) const;
void onContentSizeChange(
AndroidTextInputOnContentSizeChangeStruct value) const;
void onTextInput(AndroidTextInputOnTextInputStruct value) const;
void onEndEditing(AndroidTextInputOnEndEditingStruct value) const;
void onSelectionChange(AndroidTextInputOnSelectionChangeStruct value) const;
void onSubmitEditing(AndroidTextInputOnSubmitEditingStruct value) const;
void onKeyPress(AndroidTextInputOnKeyPressStruct value) const;
void onScroll(AndroidTextInputOnScrollStruct value) const;
};
} // namespace react
} // namespace facebook
@@ -0,0 +1,383 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "AndroidTextInputProps.h"
#include <react/components/image/conversions.h>
#include <react/core/propsConversions.h>
#include <react/graphics/conversions.h>
namespace facebook {
namespace react {
/**
* This was cribbed from BaseTextProps. Maybe we can unify someday.
* TODO: we should probably just move this to BaseTextProps / subclass it
*
* @param rawProps
* @param defaultTextAttributes
* @return
*/
static TextAttributes convertRawProp(
const RawProps &rawProps,
const TextAttributes defaultTextAttributes) {
auto textAttributes = TextAttributes{};
// Color
textAttributes.foregroundColor =
convertRawProp(rawProps, "color", defaultTextAttributes.foregroundColor);
// Todo T53300333: not found in AndroidTextInput (java) and/or TextInput
textAttributes.backgroundColor = convertRawProp(
rawProps, "backgroundColor", defaultTextAttributes.backgroundColor);
// Todo T53300333: not found in AndroidTextInput (java) and/or TextInput
textAttributes.opacity =
convertRawProp(rawProps, "opacity", defaultTextAttributes.opacity);
// Font
textAttributes.fontFamily =
convertRawProp(rawProps, "fontFamily", defaultTextAttributes.fontFamily);
textAttributes.fontSize =
convertRawProp(rawProps, "fontSize", defaultTextAttributes.fontSize);
// Todo T53300333: not found in AndroidTextInput (java) and/or TextInput
// is this maxFontSizeMultiplier?
textAttributes.fontSizeMultiplier = convertRawProp(
rawProps, "fontSizeMultiplier", defaultTextAttributes.fontSizeMultiplier);
textAttributes.fontWeight =
convertRawProp(rawProps, "fontWeight", defaultTextAttributes.fontWeight);
textAttributes.fontStyle =
convertRawProp(rawProps, "fontStyle", defaultTextAttributes.fontStyle);
// Todo T53300333: not found in AndroidTextInput (java) and/or TextInput
textAttributes.fontVariant = convertRawProp(
rawProps, "fontVariant", defaultTextAttributes.fontVariant);
textAttributes.allowFontScaling = convertRawProp(
rawProps, "allowFontScaling", defaultTextAttributes.allowFontScaling);
textAttributes.letterSpacing = convertRawProp(
rawProps, "letterSpacing", defaultTextAttributes.letterSpacing);
// Paragraph
textAttributes.lineHeight =
convertRawProp(rawProps, "lineHeight", defaultTextAttributes.lineHeight);
textAttributes.alignment =
convertRawProp(rawProps, "textAlign", defaultTextAttributes.alignment);
// Todo T53300333: not found in AndroidTextInput (java) and/or TextInput
textAttributes.baseWritingDirection = convertRawProp(
rawProps,
"baseWritingDirection",
defaultTextAttributes.baseWritingDirection);
// Decoration
// Todo T53300333: not found in AndroidTextInput (java) and/or TextInput
textAttributes.textDecorationColor = convertRawProp(
rawProps,
"textDecorationColor",
defaultTextAttributes.textDecorationColor);
textAttributes.textDecorationLineType = convertRawProp(
rawProps,
"textDecorationLine",
defaultTextAttributes.textDecorationLineType);
// Todo T53300333: not found in AndroidTextInput (java) and/or TextInput
textAttributes.textDecorationLineStyle = convertRawProp(
rawProps,
"textDecorationLineStyle",
defaultTextAttributes.textDecorationLineStyle);
// Todo T53300333: not found in AndroidTextInput (java) and/or TextInput
textAttributes.textDecorationLinePattern = convertRawProp(
rawProps,
"textDecorationLinePattern",
defaultTextAttributes.textDecorationLinePattern);
// Shadow
textAttributes.textShadowOffset = convertRawProp(
rawProps, "textShadowOffset", defaultTextAttributes.textShadowOffset);
textAttributes.textShadowRadius = convertRawProp(
rawProps, "textShadowRadius", defaultTextAttributes.textShadowRadius);
textAttributes.textShadowColor = convertRawProp(
rawProps, "textShadowColor", defaultTextAttributes.textShadowColor);
// Special
// Todo T53300333: not found in AndroidTextInput (java) and/or TextInput
textAttributes.isHighlighted = convertRawProp(
rawProps, "isHighlighted", defaultTextAttributes.isHighlighted);
return textAttributes;
}
AndroidTextInputProps::AndroidTextInputProps(
const AndroidTextInputProps &sourceProps,
const RawProps &rawProps)
: ViewProps(sourceProps, rawProps),
autoCompleteType(convertRawProp(
rawProps,
"autoCompleteType",
sourceProps.autoCompleteType,
{})),
returnKeyLabel(convertRawProp(
rawProps,
"returnKeyLabel",
sourceProps.returnKeyLabel,
{})),
numberOfLines(convertRawProp(
rawProps,
"numberOfLines",
sourceProps.numberOfLines,
{0})),
disableFullscreenUI(convertRawProp(
rawProps,
"disableFullscreenUI",
sourceProps.disableFullscreenUI,
{false})),
textBreakStrategy(convertRawProp(
rawProps,
"textBreakStrategy",
sourceProps.textBreakStrategy,
{})),
underlineColorAndroid(convertRawProp(
rawProps,
"underlineColorAndroid",
sourceProps.underlineColorAndroid,
{})),
inlineImageLeft(convertRawProp(
rawProps,
"inlineImageLeft",
sourceProps.inlineImageLeft,
{})),
inlineImagePadding(convertRawProp(
rawProps,
"inlineImagePadding",
sourceProps.inlineImagePadding,
{0})),
importantForAutofill(convertRawProp(
rawProps,
"importantForAutofill",
sourceProps.importantForAutofill,
{})),
showSoftInputOnFocus(convertRawProp(
rawProps,
"showSoftInputOnFocus",
sourceProps.showSoftInputOnFocus,
{false})),
autoCapitalize(convertRawProp(
rawProps,
"autoCapitalize",
sourceProps.autoCapitalize,
{})),
autoCorrect(convertRawProp(
rawProps,
"autoCorrect",
sourceProps.autoCorrect,
{false})),
autoFocus(convertRawProp(
rawProps,
"autoFocus",
sourceProps.autoFocus,
{false})),
allowFontScaling(convertRawProp(
rawProps,
"allowFontScaling",
sourceProps.allowFontScaling,
{false})),
maxFontSizeMultiplier(convertRawProp(
rawProps,
"maxFontSizeMultiplier",
sourceProps.maxFontSizeMultiplier,
{0.0})),
editable(
convertRawProp(rawProps, "editable", sourceProps.editable, {false})),
keyboardType(convertRawProp(
rawProps,
"keyboardType",
sourceProps.keyboardType,
{})),
returnKeyType(convertRawProp(
rawProps,
"returnKeyType",
sourceProps.returnKeyType,
{})),
maxLength(
convertRawProp(rawProps, "maxLength", sourceProps.maxLength, {0})),
multiline(convertRawProp(
rawProps,
"multiline",
sourceProps.multiline,
{false})),
placeholder(
convertRawProp(rawProps, "placeholder", sourceProps.placeholder, {})),
placeholderTextColor(convertRawProp(
rawProps,
"placeholderTextColor",
sourceProps.placeholderTextColor,
{})),
secureTextEntry(convertRawProp(
rawProps,
"secureTextEntry",
sourceProps.secureTextEntry,
{false})),
selectionColor(convertRawProp(
rawProps,
"selectionColor",
sourceProps.selectionColor,
{})),
selection(
convertRawProp(rawProps, "selection", sourceProps.selection, {})),
value(convertRawProp(rawProps, "value", sourceProps.value, {})),
defaultValue(convertRawProp(
rawProps,
"defaultValue",
sourceProps.defaultValue,
{})),
selectTextOnFocus(convertRawProp(
rawProps,
"selectTextOnFocus",
sourceProps.selectTextOnFocus,
{false})),
blurOnSubmit(convertRawProp(
rawProps,
"blurOnSubmit",
sourceProps.blurOnSubmit,
{false})),
caretHidden(convertRawProp(
rawProps,
"caretHidden",
sourceProps.caretHidden,
{false})),
contextMenuHidden(convertRawProp(
rawProps,
"contextMenuHidden",
sourceProps.contextMenuHidden,
{false})),
textShadowColor(convertRawProp(
rawProps,
"textShadowColor",
sourceProps.textShadowColor,
{})),
textShadowRadius(convertRawProp(
rawProps,
"textShadowRadius",
sourceProps.textShadowRadius,
{0.0})),
textDecorationLine(convertRawProp(
rawProps,
"textDecorationLine",
sourceProps.textDecorationLine,
{})),
fontStyle(
convertRawProp(rawProps, "fontStyle", sourceProps.fontStyle, {})),
textShadowOffset(convertRawProp(
rawProps,
"textShadowOffset",
sourceProps.textShadowOffset,
{})),
lineHeight(convertRawProp(
rawProps,
"lineHeight",
sourceProps.lineHeight,
{0.0})),
textTransform(convertRawProp(
rawProps,
"textTransform",
sourceProps.textTransform,
{})),
color(convertRawProp(rawProps, "color", sourceProps.color, {0})),
letterSpacing(convertRawProp(
rawProps,
"letterSpacing",
sourceProps.letterSpacing,
{0.0})),
fontSize(
convertRawProp(rawProps, "fontSize", sourceProps.fontSize, {0.0})),
textAlign(
convertRawProp(rawProps, "textAlign", sourceProps.textAlign, {})),
includeFontPadding(convertRawProp(
rawProps,
"includeFontPadding",
sourceProps.includeFontPadding,
{false})),
fontWeight(
convertRawProp(rawProps, "fontWeight", sourceProps.fontWeight, {})),
fontFamily(
convertRawProp(rawProps, "fontFamily", sourceProps.fontFamily, {})),
textAlignVertical(convertRawProp(
rawProps,
"textAlignVertical",
sourceProps.textAlignVertical,
{})),
cursorColor(
convertRawProp(rawProps, "cursorColor", sourceProps.cursorColor, {})),
mostRecentEventCount(convertRawProp(
rawProps,
"mostRecentEventCount",
sourceProps.mostRecentEventCount,
{0})),
text(convertRawProp(rawProps, "text", sourceProps.text, {})),
textAttributes(convertRawProp(rawProps, sourceProps.textAttributes)) {}
// TODO T53300085: support this in codegen; this was hand-written
folly::dynamic AndroidTextInputProps::getDynamic() const {
folly::dynamic props = folly::dynamic::object();
props["autoCompleteType"] = autoCompleteType;
props["returnKeyLabel"] = returnKeyLabel;
props["numberOfLines"] = numberOfLines;
props["disableFullscreenUI"] = disableFullscreenUI;
props["textBreakStrategy"] = textBreakStrategy;
props["underlineColorAndroid"] = toDynamic(underlineColorAndroid);
props["inlineImageLeft"] = inlineImageLeft;
props["inlineImagePadding"] = inlineImagePadding;
props["importantForAutofill"] = importantForAutofill;
props["showSoftInputOnFocus"] = showSoftInputOnFocus;
props["autoCapitalize"] = autoCapitalize;
props["autoCorrect"] = autoCorrect;
props["autoFocus"] = autoFocus;
props["allowFontScaling"] = allowFontScaling;
props["maxFontSizeMultiplier"] = maxFontSizeMultiplier;
props["editable"] = editable;
props["keyboardType"] = keyboardType;
props["returnKeyType"] = returnKeyType;
props["maxLength"] = maxLength;
props["multiline"] = multiline;
props["placeholder"] = placeholder;
props["placeholderTextColor"] = toDynamic(placeholderTextColor);
props["secureTextEntry"] = secureTextEntry;
props["selectionColor"] = toDynamic(selectionColor);
props["selection"] = toDynamic(selection);
props["value"] = value;
props["defaultValue"] = defaultValue;
props["selectTextOnFocus"] = selectTextOnFocus;
props["blurOnSubmit"] = blurOnSubmit;
props["caretHidden"] = caretHidden;
props["contextMenuHidden"] = contextMenuHidden;
props["textShadowColor"] = toDynamic(textShadowColor);
props["textShadowRadius"] = textShadowRadius;
props["textDecorationLine"] = textDecorationLine;
props["fontStyle"] = fontStyle;
props["textShadowOffset"] = toDynamic(textShadowOffset);
props["lineHeight"] = lineHeight;
props["textTransform"] = textTransform;
props["color"] = color;
props["letterSpacing"] = letterSpacing;
props["fontSize"] = fontSize;
props["textAlign"] = textAlign;
props["includeFontPadding"] = includeFontPadding;
props["fontWeight"] = fontWeight;
props["fontFamily"] = fontFamily;
props["textAlignVertical"] = textAlignVertical;
props["cursorColor"] = toDynamic(cursorColor);
props["mostRecentEventCount"] = mostRecentEventCount;
props["text"] = text;
return props;
}
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
// TODO: codegen these
SharedDebugStringConvertibleList TextProps::getDebugProps() const {
return {};
}
#endif
} // namespace react
} // namespace facebook
@@ -0,0 +1,163 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
// #include <react/components/text/BaseTextProps.h>
#include <react/core/Props.h>
#include <react/graphics/Color.h>
#include <react/graphics/Geometry.h>
#include <react/attributedstring/TextAttributes.h>
#include <react/attributedstring/conversions.h>
#include <react/components/view/ViewProps.h>
#include <react/core/propsConversions.h>
#include <react/graphics/Color.h>
#include <react/imagemanager/primitives.h>
#include <cinttypes>
#include <vector>
namespace facebook {
namespace react {
struct AndroidTextInputSelectionStruct {
int start;
int end;
};
static inline void fromRawValue(
const RawValue &value,
AndroidTextInputSelectionStruct &result) {
auto map = (better::map<std::string, RawValue>)value;
auto start = map.find("start");
if (start != map.end()) {
fromRawValue(start->second, result.start);
}
auto end = map.find("end");
if (end != map.end()) {
fromRawValue(end->second, result.end);
}
}
static inline std::string toString(
const AndroidTextInputSelectionStruct &value) {
return "[Object AndroidTextInputSelectionStruct]";
}
struct AndroidTextInputTextShadowOffsetStruct {
double width;
double height;
};
static inline void fromRawValue(
const RawValue &value,
AndroidTextInputTextShadowOffsetStruct &result) {
auto map = (better::map<std::string, RawValue>)value;
auto width = map.find("width");
if (width != map.end()) {
fromRawValue(width->second, result.width);
}
auto height = map.find("height");
if (height != map.end()) {
fromRawValue(height->second, result.height);
}
}
static inline std::string toString(
const AndroidTextInputTextShadowOffsetStruct &value) {
return "[Object AndroidTextInputTextShadowOffsetStruct]";
}
#ifdef ANDROID
inline folly::dynamic toDynamic(
const AndroidTextInputTextShadowOffsetStruct &value) {
folly::dynamic dynamicValue = folly::dynamic::object();
dynamicValue["width"] = value.width;
dynamicValue["height"] = value.height;
return dynamicValue;
}
inline folly::dynamic toDynamic(const AndroidTextInputSelectionStruct &value) {
folly::dynamic dynamicValue = folly::dynamic::object();
dynamicValue["start"] = value.start;
dynamicValue["end"] = value.end;
return dynamicValue;
}
#endif
class AndroidTextInputProps final : public ViewProps {
public:
AndroidTextInputProps() = default;
AndroidTextInputProps(
const AndroidTextInputProps &sourceProps,
const RawProps &rawProps);
folly::dynamic getDynamic() const;
#pragma mark - Props
const std::string autoCompleteType{};
const std::string returnKeyLabel{};
const int numberOfLines{0};
const bool disableFullscreenUI{false};
const std::string textBreakStrategy{};
const SharedColor underlineColorAndroid{};
const std::string inlineImageLeft{};
const int inlineImagePadding{0};
const std::string importantForAutofill{};
const bool showSoftInputOnFocus{false};
const std::string autoCapitalize{};
const bool autoCorrect{false};
const bool autoFocus{false};
const bool allowFontScaling{false};
const Float maxFontSizeMultiplier{0.0};
const bool editable{false};
const std::string keyboardType{};
const std::string returnKeyType{};
const int maxLength{0};
const bool multiline{false};
const std::string placeholder{};
const SharedColor placeholderTextColor{};
const bool secureTextEntry{false};
const SharedColor selectionColor{};
const AndroidTextInputSelectionStruct selection{};
const std::string value{};
const std::string defaultValue{};
const bool selectTextOnFocus{false};
const bool blurOnSubmit{false};
const bool caretHidden{false};
const bool contextMenuHidden{false};
const SharedColor textShadowColor{};
const Float textShadowRadius{0.0};
const std::string textDecorationLine{};
const std::string fontStyle{};
const AndroidTextInputTextShadowOffsetStruct textShadowOffset{};
const Float lineHeight{0.0};
const std::string textTransform{};
const int color{0};
const Float letterSpacing{0.0};
const Float fontSize{0.0};
const std::string textAlign{};
const bool includeFontPadding{false};
const std::string fontWeight{};
const std::string fontFamily{};
const std::string textAlignVertical{};
const SharedColor cursorColor{};
const int mostRecentEventCount{0};
const std::string text{};
/**
* TextAttributes: see all BaseText. These attributes are not set
* directly; see convertRawProps.
*/
const TextAttributes textAttributes{};
};
} // namespace react
} // namespace facebook
@@ -0,0 +1,102 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "AndroidTextInputShadowNode.h"
#include <fb/fbjni.h>
#include <react/attributedstring/TextAttributes.h>
#include <react/components/text/BaseTextShadowNode.h>
#include <react/core/LayoutConstraints.h>
#include <react/core/LayoutContext.h>
#include <react/core/conversions.h>
#include <react/jni/ReadableNativeMap.h>
#include <Glog/logging.h>
using namespace facebook::jni;
namespace facebook {
namespace react {
extern const char AndroidTextInputComponentName[] = "AndroidTextInput";
void AndroidTextInputShadowNode::setContextContainer(
ContextContainer *contextContainer) {
ensureUnsealed();
contextContainer_ = contextContainer;
}
AttributedString AndroidTextInputShadowNode::getAttributedString() const {
auto textAttributes = TextAttributes::defaultTextAttributes();
textAttributes.apply(getProps()->textAttributes);
// Use BaseTextShadowNode to get attributed string from children
return BaseTextShadowNode::getAttributedString(
textAttributes, shared_from_this());
}
#pragma mark - LayoutableShadowNode
Size AndroidTextInputShadowNode::measure(
LayoutConstraints layoutConstraints) const {
AttributedString attributedString = getAttributedString();
if (attributedString.isEmpty()) {
return {0, 0};
}
const jni::global_ref<jobject> &fabricUIManager =
contextContainer_->at<jni::global_ref<jobject>>("FabricUIManager");
static auto measure =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<jlong(
jstring,
ReadableMap::javaobject,
ReadableMap::javaobject,
ReadableMap::javaobject,
jfloat,
jfloat,
jfloat,
jfloat)>("measure");
auto minimumSize = layoutConstraints.minimumSize;
auto maximumSize = layoutConstraints.maximumSize;
local_ref<JString> componentName =
make_jstring(AndroidTextInputComponentName);
local_ref<ReadableNativeMap::javaobject> attributedStringRNM =
ReadableNativeMap::newObjectCxxArgs(toDynamic(attributedString));
local_ref<ReadableMap::javaobject> attributedStringRM = make_local(
reinterpret_cast<ReadableMap::javaobject>(attributedStringRNM.get()));
local_ref<ReadableNativeMap::javaobject> nativeLocalProps = make_local(
ReadableNativeMap::createWithContents(getProps()->getDynamic()));
local_ref<ReadableMap::javaobject> props = make_local(
reinterpret_cast<ReadableMap::javaobject>(nativeLocalProps.get()));
// For AndroidTextInput purposes:
// localData == textAttributes
return yogaMeassureToSize(measure(
fabricUIManager,
componentName.get(),
attributedStringRM.get(),
props.get(),
nullptr,
minimumSize.width,
maximumSize.width,
minimumSize.height,
maximumSize.height));
}
void AndroidTextInputShadowNode::layout(LayoutContext layoutContext) {
ConcreteViewShadowNode::layout(layoutContext);
}
} // namespace react
} // namespace facebook
@@ -0,0 +1,50 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include "AndroidTextInputEventEmitter.h"
#include "AndroidTextInputProps.h"
#include <react/components/view/ConcreteViewShadowNode.h>
#include <react/utils/ContextContainer.h>
#include <react/attributedstring/AttributedString.h>
namespace facebook {
namespace react {
extern const char AndroidTextInputComponentName[];
/*
* `ShadowNode` for <AndroidTextInput> component.
*/
class AndroidTextInputShadowNode : public ConcreteViewShadowNode<
AndroidTextInputComponentName,
AndroidTextInputProps,
AndroidTextInputEventEmitter> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
void setContextContainer(ContextContainer *contextContainer);
/*
* Returns a `AttributedString` which represents text content of the node.
*/
AttributedString getAttributedString() const;
#pragma mark - LayoutableShadowNode
Size measure(LayoutConstraints layoutConstraints) const override;
void layout(LayoutContext layoutContext) override;
private:
ContextContainer *contextContainer_{};
};
} // namespace react
} // namespace facebook
@@ -18,6 +18,8 @@
#include <react/debug/SystraceSection.h>
#include <yoga/Yoga.h>
#include <Glog/logging.h>
namespace facebook {
namespace react {