Skip to content
Open
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
Expand Up @@ -20,7 +20,7 @@ namespace facebook::react {

#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList PasteTextInputProps::getDebugProps() const {
return PasteTextInputProps::getDebugProps();
return {};
}
#endif

Expand Down Expand Up @@ -361,15 +361,6 @@ folly::dynamic PasteTextInputProps::getDynamic() const {
return props;
}

static folly::dynamic toDynamic(
const std::vector<std::string>& acceptDragAndDropTypes) {
folly::dynamic acceptDragAndDropTypesArray = folly::dynamic::array();
for (const auto& acceptDragAndDropType : acceptDragAndDropTypes) {
acceptDragAndDropTypesArray.push_back(acceptDragAndDropType);
}
return acceptDragAndDropTypesArray;
}

ComponentName PasteTextInputProps::getDiffPropsImplementationTarget() const {
return "TextInput";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@

#include "ShadowNodes.h"

#include <react/renderer/core/LayoutConstraints.h>
#include <react/renderer/core/LayoutContext.h>
#include <react/renderer/core/conversions.h>

#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/attributedstring/AttributedStringBox.h>
#include <react/renderer/attributedstring/TextAttributes.h>
Expand All @@ -22,7 +18,6 @@
#include <react/renderer/core/LayoutContext.h>
#include <react/renderer/core/conversions.h>
#include <react/renderer/textlayoutmanager/TextLayoutContext.h>
#include <android/log.h>

namespace facebook::react {

Expand All @@ -34,61 +29,66 @@ void PasteTextInputShadowNode::setTextLayoutManager(
textLayoutManager_ = std::move(textLayoutManager);
}

Size PasteTextInputShadowNode::measureContent(
const LayoutContext& layoutContext,
const LayoutConstraints& layoutConstraints) const {
auto textConstraints = getTextConstraints(layoutConstraints);
Size PasteTextInputShadowNode::measureContent(
const LayoutContext& layoutContext,
const LayoutConstraints& layoutConstraints) const {
auto textConstraints = getTextConstraints(layoutConstraints);

TextLayoutContext textLayoutContext{
.pointScaleFactor = layoutContext.pointScaleFactor,
};

if (getStateData().cachedAttributedStringId != 0) {
auto textSize = textLayoutManager_
->measureCachedSpannableById(
getStateData().cachedAttributedStringId,
getConcreteProps().paragraphAttributes,
textLayoutContext,
textConstraints)
.size;
return layoutConstraints.clamp(textSize);
}

// Layout is called right after measure.
// Measure is marked as `const`, and `layout` is not; so State can be
// updated during layout, but not during `measure`. If State is out-of-date
// in layout, it's too late: measure will have already operated on old
// State. Thus, we use the same value here that we *will* use in layout to
// update the state.
AttributedString attributedString =
getMostRecentAttributedString(layoutContext);

if (attributedString.isEmpty()) {
attributedString = getPlaceholderAttributedString(layoutContext);
}

if (attributedString.isEmpty() && getStateData().mostRecentEventCount != 0) {
return {.width = 0, .height = 0};
}

if (getStateData().cachedAttributedStringId != 0) {
auto textSize = textLayoutManager_
->measureCachedSpannableById(
getStateData().cachedAttributedStringId,
->measure(
AttributedStringBox{attributedString},
getConcreteProps().paragraphAttributes,
textLayoutContext,
textConstraints)
.size;
return layoutConstraints.clamp(textSize);
}

// Layout is called right after measure.
// Measure is marked as `const`, and `layout` is not; so State can be
// updated during layout, but not during `measure`. If State is out-of-date
// in layout, it's too late: measure will have already operated on old
// State. Thus, we use the same value here that we *will* use in layout to
// update the state.
AttributedString attributedString = getMostRecentAttributedString();

if (attributedString.isEmpty()) {
attributedString = getPlaceholderAttributedString();
}

if (attributedString.isEmpty() && getStateData().mostRecentEventCount != 0) {
return {.width = 0, .height = 0};
}

TextLayoutContext textLayoutContext;
textLayoutContext.pointScaleFactor = layoutContext.pointScaleFactor;
auto textSize = textLayoutManager_
->measure(
AttributedStringBox{attributedString},
getConcreteProps().paragraphAttributes,
textLayoutContext,
textConstraints)
.size;
return layoutConstraints.clamp(textSize);
}

void PasteTextInputShadowNode::layout(LayoutContext layoutContext) {
updateStateIfNeeded();
updateStateIfNeeded(layoutContext);
ConcreteViewShadowNode::layout(layoutContext);
}

Float PasteTextInputShadowNode::baseline(
const LayoutContext& /*layoutContext*/,
const LayoutContext& layoutContext,
Size size) const {
AttributedString attributedString = getMostRecentAttributedString();
AttributedString attributedString =
getMostRecentAttributedString(layoutContext);

if (attributedString.isEmpty()) {
attributedString = getPlaceholderAttributedString();
attributedString = getPlaceholderAttributedString(layoutContext);
}

// Yoga expects a baseline relative to the Node's border-box edge instead of
Expand All @@ -98,10 +98,10 @@ Float PasteTextInputShadowNode::baseline(
YGNodeLayoutGetPadding(&yogaNode_, YGEdgeTop);

AttributedStringBox attributedStringBox{attributedString};
return textLayoutManager_->baseline(
return LineMeasurement::baseline(textLayoutManager_->measureLines(
attributedStringBox,
getConcreteProps().paragraphAttributes,
size) +
size)) +
top;
}

Expand All @@ -126,10 +126,12 @@ LayoutConstraints PasteTextInputShadowNode::getTextConstraints(
}
}

void PasteTextInputShadowNode::updateStateIfNeeded() {

void PasteTextInputShadowNode::updateStateIfNeeded(
const LayoutContext& layoutContext) {
ensureUnsealed();
const auto& stateData = getStateData();
auto reactTreeAttributedString = getAttributedString();
auto reactTreeAttributedString = getAttributedString(layoutContext);

// Tree is often out of sync with the value of the TextInput.
// This is by design - don't change the value of the TextInput in the State,
Expand All @@ -153,7 +155,7 @@ void PasteTextInputShadowNode::updateStateIfNeeded() {
reactTreeAttributedString)
? 0
: props.mostRecentEventCount;
auto newAttributedString = getMostRecentAttributedString();
auto newAttributedString = getMostRecentAttributedString(layoutContext);

setStateData(TextInputState{
AttributedStringBox(newAttributedString),
Expand All @@ -162,9 +164,10 @@ void PasteTextInputShadowNode::updateStateIfNeeded() {
newEventCount});
}

AttributedString PasteTextInputShadowNode::getAttributedString() const {
AttributedString PasteTextInputShadowNode::getAttributedString(const LayoutContext& layoutContext) const {
// Use BaseTextShadowNode to get attributed string from children
auto childTextAttributes = TextAttributes::defaultTextAttributes();
childTextAttributes.fontSizeMultiplier = layoutContext.fontSizeMultiplier;
childTextAttributes.apply(getConcreteProps().textAttributes);
// Don't propagate the background color of the TextInput onto the attributed
// string. Android tries to render shadow of the background alongside the
Expand All @@ -173,19 +176,16 @@ AttributedString PasteTextInputShadowNode::getAttributedString() const {

auto attributedString = AttributedString{};
auto attachments = BaseTextShadowNode::Attachments{};

BaseTextShadowNode::buildAttributedString(
childTextAttributes, *this, attributedString, attachments);
attributedString.setBaseTextAttributes(childTextAttributes);

auto txt = attributedString.getString();


// BaseTextShadowNode only gets children. We must detect and prepend text
// value attributes manually.
if (!getConcreteProps().text.empty()) {
auto textAttributes = TextAttributes::defaultTextAttributes();
textAttributes.apply(getConcreteProps().textAttributes);
textAttributes.fontSizeMultiplier = layoutContext.fontSizeMultiplier;
auto fragment = AttributedString::Fragment{};
fragment.string = getConcreteProps().text;
fragment.textAttributes = textAttributes;
Expand All @@ -200,11 +200,11 @@ AttributedString PasteTextInputShadowNode::getAttributedString() const {
return attributedString;
}

AttributedString PasteTextInputShadowNode::getMostRecentAttributedString()
const {
AttributedString PasteTextInputShadowNode::getMostRecentAttributedString(
const LayoutContext& layoutContext) const {
const auto& state = getStateData();

auto reactTreeAttributedString = getAttributedString();
auto reactTreeAttributedString = getAttributedString(layoutContext);

// Sometimes the treeAttributedString will only differ from the state
// not by inherent properties (string or prop attributes), but by the frame of
Expand All @@ -225,22 +225,23 @@ const {
// display at all.
// TODO T67606511: We will redefine the measurement of empty strings as part
// of T67606511
AttributedString PasteTextInputShadowNode::getPlaceholderAttributedString()
const {
const auto& props = BaseShadowNode::getConcreteProps();

AttributedString attributedString;
auto placeholderString = !props.placeholder.empty()
? props.placeholder
: BaseTextShadowNode::getEmptyPlaceholder();
auto textAttributes = TextAttributes::defaultTextAttributes();
textAttributes.apply(props.textAttributes);
attributedString.appendFragment(
{.string = std::move(placeholderString),
.textAttributes = textAttributes,
.parentShadowView = ShadowView(*this)});
return attributedString;
}
AttributedString PasteTextInputShadowNode::getPlaceholderAttributedString(
const LayoutContext& layoutContext) const {
const auto& props = BaseShadowNode::getConcreteProps();

AttributedString attributedString;
auto placeholderString = !props.placeholder.empty()
? props.placeholder
: BaseTextShadowNode::getEmptyPlaceholder();
auto textAttributes = TextAttributes::defaultTextAttributes();
textAttributes.fontSizeMultiplier = layoutContext.fontSizeMultiplier;
textAttributes.apply(props.textAttributes);
attributedString.appendFragment(
{.string = std::move(placeholderString),
.textAttributes = textAttributes,
.parentShadowView = ShadowView(*this)});
return attributedString;
}


} // namespace facebook::react
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
#include "Props.h"
#include "States.h"
#include <jsi/jsi.h>

#include <react/renderer/attributedstring/AttributedString.h>
#include <react/renderer/textlayoutmanager/TextLayoutManager.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
#include <react/renderer/components/textinput/TextInputState.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
#include <react/utils/ContextContainer.h>


namespace facebook::react {
Expand All @@ -31,8 +32,7 @@ class PasteTextInputShadowNode final : public ConcreteViewShadowNode<
PasteTextInputComponentName,
PasteTextInputProps,
PasteTextInputEventEmitter,
TextInputState,
true> {
TextInputState> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;

Expand Down Expand Up @@ -74,19 +74,22 @@ class PasteTextInputShadowNode final : public ConcreteViewShadowNode<
* Creates a `State` object (with `AttributedText` and
* `TextLayoutManager`) if needed.
*/
void updateStateIfNeeded();
void updateStateIfNeeded(const LayoutContext& layoutContext);

/*
* Returns a `AttributedString` which represents text content of the node.
*/
AttributedString getAttributedString() const;
AttributedString getAttributedString(
const LayoutContext& layoutContext) const;

/**
* Get the most up-to-date attributed string for measurement and State.
*/
AttributedString getMostRecentAttributedString() const;
AttributedString getMostRecentAttributedString(
const LayoutContext& layoutContext) const;

AttributedString getPlaceholderAttributedString() const;
AttributedString getPlaceholderAttributedString(
const LayoutContext& layoutContext) const;
};

} // namespace facebook::react
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ class PasteTextInputManager(context: ReactApplicationContext) : ReactTextInputMa
}

override fun getExportedCustomBubblingEventTypeConstants(): MutableMap<String, Any> {
val map = super.getExportedCustomBubblingEventTypeConstants()!!
val map = super.getExportedCustomBubblingEventTypeConstants()?.toMutableMap()
?: return mutableMapOf()

map["onPaste"] = MapBuilder.of(
"phasedRegistrationNames",
MapBuilder.of("bubbled", "onPaste")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
import com.facebook.soloader.SoLoader

class MainApplication : Application(), ReactApplication {

Expand All @@ -35,10 +33,6 @@ class MainApplication : Application(), ReactApplication {

override fun onCreate() {
super.onCreate()
SoLoader.init(this, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
load()
}
loadReactNative(this)
}
}
}
2 changes: 1 addition & 1 deletion example/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ buildscript {
compileSdkVersion = 35
targetSdkVersion = 35
ndkVersion = "27.1.12297006"
kotlinVersion = "2.0.21"
kotlinVersion = "2.1.20"
}
repositories {
google()
Expand Down
Binary file modified example/android/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion example/android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
4 changes: 2 additions & 2 deletions example/android/gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading