From c1af56df71cfbecea224440ac6a2ee2915a4aa51 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Tue, 22 Sep 2020 01:50:39 -0700 Subject: [PATCH] Wire call from C++ to Java to get lines measurements Summary: Changelog: [Internal] Reviewed By: shergin Differential Revision: D23782998 fbshipit-source-id: fa9bda274c024d5bbd3ca24f394b5d76f8c07ad2 --- .../react/fabric/FabricUIManager.java | 9 ++++ .../components/text/ParagraphShadowNode.cpp | 2 - .../textlayoutmanager/TextMeasureCache.cpp | 34 ++++++++++++++ .../textlayoutmanager/TextMeasureCache.h | 10 +++++ .../textlayoutmanager/TextLayoutManager.cpp | 44 +++++++++++++++++++ .../textlayoutmanager/TextLayoutManager.h | 9 ++++ 6 files changed, 106 insertions(+), 2 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index bf95f6e7da3..6eb969d9c89 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -36,7 +36,9 @@ import com.facebook.debug.tags.ReactDebugOverlayTags; import com.facebook.infer.annotation.ThreadConfined; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.ReactRootView; +import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.LifecycleEventListener; +import com.facebook.react.bridge.NativeArray; import com.facebook.react.bridge.NativeMap; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; @@ -443,6 +445,13 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { return new BatchMountItem(rootTag, items, size, commitNumber); } + @DoNotStrip + @SuppressWarnings("unused") + private NativeArray measureLines( + ReadableMap attributedString, ReadableMap paragraphAttributes, float width, float height) { + return (NativeArray) Arguments.createArray(); + } + @DoNotStrip @SuppressWarnings("unused") private long measure( diff --git a/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp b/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp index 2a7f0421dea..87745126463 100644 --- a/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -169,7 +169,6 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { content.paragraphAttributes, layoutConstraints); -#ifndef ANDROID if (getConcreteProps().onTextLayout) { auto linesMeasurements = textLayoutManager_->measureLines( content.attributedString, @@ -177,7 +176,6 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { measurement.size); getConcreteEventEmitter().onTextLayout(linesMeasurements); } -#endif if (content.attachments.empty()) { // No attachments to layout. diff --git a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp index 26b50ffd184..d3d9845f93c 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp +++ b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp @@ -10,6 +10,40 @@ namespace facebook { namespace react { +static Rect rectFromDynamic(folly::dynamic const &data) { + Point origin; + origin.x = data.getDefault("x", 0).getDouble(); + origin.y = data.getDefault("y", 0).getDouble(); + Size size; + size.width = data.getDefault("width", 0).getDouble(); + size.height = data.getDefault("height", 0).getDouble(); + Rect frame; + frame.origin = origin; + frame.size = size; + return frame; +} + +LineMeasurement::LineMeasurement( + std::string text, + Rect frame, + Float descender, + Float capHeight, + Float ascender, + Float xHeight) + : text(text), + frame(frame), + descender(descender), + capHeight(capHeight), + ascender(ascender) {} + +LineMeasurement::LineMeasurement(folly::dynamic const &data) + : text(data.getDefault("text", "").getString()), + frame(rectFromDynamic(data)), + descender(data.getDefault("descender", 0).getDouble()), + capHeight(data.getDefault("capHeight", 0).getDouble()), + ascender(data.getDefault("ascender", 0).getDouble()), + xHeight(data.getDefault("xHeight", 0).getDouble()) {} + bool LineMeasurement::operator==(LineMeasurement const &rhs) const { return std::tie( this->text, diff --git a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h index 34a0a2d8705..d0e71872810 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +++ b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h @@ -24,6 +24,16 @@ struct LineMeasurement { Float ascender; Float xHeight; + LineMeasurement( + std::string text, + Rect frame, + Float descender, + Float capHeight, + Float ascender, + Float xHeight); + + LineMeasurement(folly::dynamic const &data); + bool operator==(LineMeasurement const &rhs) const; }; diff --git a/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp b/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp index 3d69b4a84ed..be0201dec74 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp +++ b/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp @@ -98,6 +98,50 @@ TextMeasurement TextLayoutManager::measureCachedSpannableById( return TextMeasurement{size, attachments}; } +LinesMeasurements TextLayoutManager::measureLines( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const { + const jni::global_ref &fabricUIManager = + contextContainer_->at>("FabricUIManager"); + static auto measureLines = + jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") + ->getMethod("measureLines"); + + auto serializedAttributedString = toDynamic(attributedString); + + local_ref attributedStringRNM = + ReadableNativeMap::newObjectCxxArgs(serializedAttributedString); + local_ref paragraphAttributesRNM = + ReadableNativeMap::newObjectCxxArgs(toDynamic(paragraphAttributes)); + + local_ref attributedStringRM = make_local( + reinterpret_cast(attributedStringRNM.get())); + local_ref paragraphAttributesRM = make_local( + reinterpret_cast(paragraphAttributesRNM.get())); + + auto array = measureLines( + fabricUIManager, + attributedStringRM.get(), + paragraphAttributesRM.get(), + size.width, + size.height); + + auto dynamicArray = cthis(array)->consume(); + LinesMeasurements lineMeasurements; + lineMeasurements.reserve(dynamicArray.size()); + + for (auto const &data : dynamicArray) { + lineMeasurements.push_back(LineMeasurement(data)); + } + + return lineMeasurements; +} + TextMeasurement TextLayoutManager::doMeasure( AttributedString attributedString, ParagraphAttributes paragraphAttributes, diff --git a/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h b/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h index e1ec4b122f9..681c54e22fb 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h +++ b/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h @@ -46,6 +46,15 @@ class TextLayoutManager { ParagraphAttributes paragraphAttributes, LayoutConstraints layoutConstraints) const; + /* + * Measures lines of `attributedString` using native text rendering + * infrastructure. + */ + LinesMeasurements measureLines( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const; + /* * Returns an opaque pointer to platform-specific TextLayoutManager. * Is used on a native views layer to delegate text rendering to the manager.