Skip to content

Commit 5bada15

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
Platform specific TextLayoutManager Headers (#50889)
Summary: Pull Request resolved: #50889 This effectively reverts D67064488 We are trying to smash together functions, variables, virtual or non-virtual, all required by different platforms, into a single header, often using #ifdefs that are not what we want (apart from a bad editor experience, `#ifdef ANDROID` may or may not be compiled into the react-native-cxx platform, and we cannot use it for the Android platform reliably). For Facsimile, we are going to be introducing more potential divergence, with the idea of `PreparedText`, where we can generate intermediate products as part of the layout process to be reused later. I'm planning to design that in a way which can be eventually reused across platforms, but not everywhere. I think the best path for this is going to be to allow each platform to have their own headers, instead of the current messiness, then allow shared code (e.g. in `ParagraphShadowNode`) to pick how to interact at compile time. I added an example of this as part of `TextLayoutManagerExtended`, to customize how we act if a `TextLayoutManager` chooses not to implement `measureLines`. Changelog: [Internal] Differential Revision: D73557126
1 parent 319ba0a commit 5bada15

File tree

7 files changed

+229
-43
lines changed

7 files changed

+229
-43
lines changed

packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp

+21-5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <react/renderer/graphics/rounding.h>
1818
#include <react/renderer/telemetry/TransactionTelemetry.h>
1919
#include <react/renderer/textlayoutmanager/TextLayoutContext.h>
20+
#include <react/renderer/textlayoutmanager/TextLayoutManagerExtended.h>
2021

2122
#include "ParagraphState.h"
2223

@@ -215,8 +216,17 @@ Float ParagraphShadowNode::baseline(
215216

216217
AttributedStringBox attributedStringBox{attributedString};
217218

218-
return LineMeasurement::baseline(textLayoutManager_->measureLines(
219-
attributedStringBox, getConcreteProps().paragraphAttributes, size));
219+
if constexpr (TextLayoutManagerExtended::supportsLineMeasurement()) {
220+
auto lines =
221+
TextLayoutManagerExtended(*textLayoutManager_)
222+
.measureLines(
223+
attributedStringBox, content.paragraphAttributes, size);
224+
return LineMeasurement::baseline(lines);
225+
} else {
226+
LOG(WARNING)
227+
<< "Baseline alignment is not supported by the current platform";
228+
return 0;
229+
}
220230
}
221231

222232
void ParagraphShadowNode::layout(LayoutContext layoutContext) {
@@ -239,9 +249,15 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) {
239249
AttributedStringBox attributedStringBox{content.attributedString};
240250

241251
if (getConcreteProps().onTextLayout) {
242-
auto linesMeasurements = textLayoutManager_->measureLines(
243-
attributedStringBox, content.paragraphAttributes, size);
244-
getConcreteEventEmitter().onTextLayout(linesMeasurements);
252+
if constexpr (TextLayoutManagerExtended::supportsLineMeasurement()) {
253+
auto linesMeasurements =
254+
TextLayoutManagerExtended(*textLayoutManager_)
255+
.measureLines(
256+
attributedStringBox, content.paragraphAttributes, size);
257+
getConcreteEventEmitter().onTextLayout(linesMeasurements);
258+
} else {
259+
LOG(WARNING) << "onTextLayout is not supported by the current platform";
260+
}
245261
}
246262

247263
if (content.attachments.empty()) {

packages/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputShadowNode.h

+15-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#pragma once
99

10+
#include <glog/logging.h>
11+
1012
#include <react/renderer/attributedstring/AttributedString.h>
1113
#include <react/renderer/attributedstring/AttributedStringBox.h>
1214
#include <react/renderer/components/text/BaseTextShadowNode.h>
@@ -18,6 +20,7 @@
1820
#include <react/renderer/core/LayoutContext.h>
1921
#include <react/renderer/textlayoutmanager/TextLayoutContext.h>
2022
#include <react/renderer/textlayoutmanager/TextLayoutManager.h>
23+
#include <react/renderer/textlayoutmanager/TextLayoutManagerExtended.h>
2124
#include <react/utils/ContextContainer.h>
2225

2326
namespace facebook::react {
@@ -106,9 +109,18 @@ class BaseTextInputShadowNode : public ConcreteViewShadowNode<
106109
&(YogaLayoutableShadowNode::yogaNode_), YGEdgeTop);
107110

108111
AttributedStringBox attributedStringBox{attributedString};
109-
return LineMeasurement::baseline(textLayoutManager_->measureLines(
110-
attributedStringBox, props.paragraphAttributes, size)) +
111-
top;
112+
113+
if constexpr (TextLayoutManagerExtended::supportsLineMeasurement()) {
114+
auto lines =
115+
TextLayoutManagerExtended(*textLayoutManager_)
116+
.measureLines(
117+
attributedStringBox, props.paragraphAttributes, size);
118+
return LineMeasurement::baseline(lines) + top;
119+
} else {
120+
LOG(WARNING)
121+
<< "Baseline alignment is not supported by the current platform";
122+
return top;
123+
}
112124
}
113125

114126
/*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <glog/logging.h>
11+
12+
#include <react/renderer/attributedstring/AttributedStringBox.h>
13+
#include <react/renderer/attributedstring/ParagraphAttributes.h>
14+
#include <react/renderer/graphics/Size.h>
15+
#include <react/renderer/textlayoutmanager/TextLayoutManager.h>
16+
#include <react/renderer/textlayoutmanager/TextMeasureCache.h>
17+
18+
namespace facebook::react {
19+
20+
namespace detail {
21+
/**
22+
* TextLayoutManagerExtended acts as an adapter for TextLayoutManager methods
23+
* which may not exist for a specific platform. Callers can check at
24+
* compile-time whether a method is supported, and calling if it is not will
25+
* terminate.
26+
*/
27+
template <typename TextLayoutManagerT>
28+
class TextLayoutManagerExtended {
29+
public:
30+
static constexpr bool supportsLineMeasurement() {
31+
return requires(TextLayoutManagerT textLayoutManager) {
32+
{
33+
textLayoutManager.measureLines(
34+
AttributedStringBox{}, ParagraphAttributes{}, Size{})
35+
} -> std::same_as<LinesMeasurements>;
36+
};
37+
}
38+
39+
TextLayoutManagerExtended(const TextLayoutManagerT& textLayoutManager)
40+
: textLayoutManager_(textLayoutManager) {}
41+
42+
LinesMeasurements measureLines(
43+
const AttributedStringBox& attributedStringBox,
44+
const ParagraphAttributes& paragraphAttributes,
45+
const Size& size) {
46+
if constexpr (supportsLineMeasurement()) {
47+
return textLayoutManager_.measureLines(
48+
attributedStringBox, paragraphAttributes, size);
49+
}
50+
LOG(FATAL) << "Platform TextLayoutManager does not support measureLines";
51+
}
52+
53+
private:
54+
const TextLayoutManagerT& textLayoutManager_;
55+
};
56+
} // namespace detail
57+
58+
using TextLayoutManagerExtended =
59+
detail::TextLayoutManagerExtended<TextLayoutManager>;
60+
61+
} // namespace facebook::react

packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextLayoutManager.h renamed to packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h

+3-17
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ class TextLayoutManager;
2626
class TextLayoutManager {
2727
public:
2828
TextLayoutManager(const ContextContainer::Shared& contextContainer);
29-
virtual ~TextLayoutManager() = default;
3029

3130
/*
3231
* Not copyable.
@@ -43,13 +42,12 @@ class TextLayoutManager {
4342
/*
4443
* Measures `attributedString` using native text rendering infrastructure.
4544
*/
46-
virtual TextMeasurement measure(
45+
TextMeasurement measure(
4746
const AttributedStringBox& attributedStringBox,
4847
const ParagraphAttributes& paragraphAttributes,
4948
const TextLayoutContext& layoutContext,
5049
const LayoutConstraints& layoutConstraints) const;
5150

52-
#ifdef ANDROID
5351
/**
5452
* Measures an AttributedString on the platform, as identified by some
5553
* opaque cache ID.
@@ -58,30 +56,18 @@ class TextLayoutManager {
5856
int64_t cacheId,
5957
const ParagraphAttributes& paragraphAttributes,
6058
const LayoutConstraints& layoutConstraints) const;
61-
#endif
6259

6360
/*
6461
* Measures lines of `attributedString` using native text rendering
6562
* infrastructure.
6663
*/
67-
virtual LinesMeasurements measureLines(
64+
LinesMeasurements measureLines(
6865
const AttributedStringBox& attributedStringBox,
6966
const ParagraphAttributes& paragraphAttributes,
7067
const Size& size) const;
7168

72-
#ifdef __APPLE__
73-
/*
74-
* Returns an opaque pointer to platform-specific TextLayoutManager.
75-
* Is used on a native views layer to delegate text rendering to the manager.
76-
*/
77-
std::shared_ptr<void> getNativeTextLayoutManager() const;
78-
#endif
79-
80-
protected:
69+
private:
8170
std::shared_ptr<const ContextContainer> contextContainer_;
82-
#ifdef __APPLE__
83-
std::shared_ptr<void> nativeTextLayoutManager_;
84-
#endif
8571
TextMeasureCache textMeasureCache_;
8672
LineMeasureCache lineMeasureCache_;
8773
};

packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.cpp renamed to packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/react/renderer/textlayoutmanager/TextLayoutManager.cpp

+1-18
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ namespace facebook::react {
1111

1212
TextLayoutManager::TextLayoutManager(
1313
const ContextContainer::Shared& /*contextContainer*/)
14-
: textMeasureCache_(kSimpleThreadSafeCacheSizeCap),
15-
lineMeasureCache_(kSimpleThreadSafeCacheSizeCap) {}
14+
: textMeasureCache_(kSimpleThreadSafeCacheSizeCap) {}
1615

1716
TextMeasurement TextLayoutManager::measure(
1817
const AttributedStringBox& attributedStringBox,
@@ -29,20 +28,4 @@ TextMeasurement TextLayoutManager::measure(
2928
return TextMeasurement{{0, 0}, attachments};
3029
}
3130

32-
#ifdef ANDROID
33-
TextMeasurement TextLayoutManager::measureCachedSpannableById(
34-
int64_t /*cacheId*/,
35-
const ParagraphAttributes& /*paragraphAttributes*/,
36-
const LayoutConstraints& /*layoutConstraints*/) const {
37-
return {};
38-
}
39-
#endif
40-
41-
LinesMeasurements TextLayoutManager::measureLines(
42-
const AttributedStringBox& /*attributedStringBox*/,
43-
const ParagraphAttributes& /*paragraphAttributes*/,
44-
const Size& /*size*/) const {
45-
return {};
46-
};
47-
4831
} // namespace facebook::react
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <react/renderer/attributedstring/AttributedStringBox.h>
11+
#include <react/renderer/attributedstring/ParagraphAttributes.h>
12+
#include <react/renderer/core/LayoutConstraints.h>
13+
#include <react/renderer/textlayoutmanager/TextLayoutContext.h>
14+
#include <react/renderer/textlayoutmanager/TextMeasureCache.h>
15+
#include <react/utils/ContextContainer.h>
16+
#include <memory>
17+
18+
namespace facebook::react {
19+
20+
class TextLayoutManager;
21+
22+
/*
23+
* Cross platform facade for text measurement (e.g. Android-specific
24+
* TextLayoutManager)
25+
*/
26+
class TextLayoutManager {
27+
public:
28+
TextLayoutManager(const ContextContainer::Shared& contextContainer);
29+
virtual ~TextLayoutManager() = default;
30+
31+
/*
32+
* Not copyable.
33+
*/
34+
TextLayoutManager(const TextLayoutManager&) = delete;
35+
TextLayoutManager& operator=(const TextLayoutManager&) = delete;
36+
37+
/*
38+
* Not movable.
39+
*/
40+
TextLayoutManager(TextLayoutManager&&) = delete;
41+
TextLayoutManager& operator=(TextLayoutManager&&) = delete;
42+
43+
/*
44+
* Measures `attributedString` using native text rendering infrastructure.
45+
*/
46+
virtual TextMeasurement measure(
47+
const AttributedStringBox& attributedStringBox,
48+
const ParagraphAttributes& paragraphAttributes,
49+
const TextLayoutContext& layoutContext,
50+
const LayoutConstraints& layoutConstraints) const;
51+
52+
protected:
53+
std::shared_ptr<const ContextContainer> contextContainer_;
54+
TextMeasureCache textMeasureCache_;
55+
};
56+
57+
} // namespace facebook::react
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <react/renderer/attributedstring/AttributedStringBox.h>
11+
#include <react/renderer/attributedstring/ParagraphAttributes.h>
12+
#include <react/renderer/core/LayoutConstraints.h>
13+
#include <react/renderer/textlayoutmanager/TextLayoutContext.h>
14+
#include <react/renderer/textlayoutmanager/TextMeasureCache.h>
15+
#include <react/utils/ContextContainer.h>
16+
#include <memory>
17+
18+
namespace facebook::react {
19+
20+
/*
21+
* Cross platform facade for text measurement (e.g. Android-specific
22+
* TextLayoutManager)
23+
*/
24+
class TextLayoutManager {
25+
public:
26+
TextLayoutManager(const ContextContainer::Shared& contextContainer);
27+
28+
/*
29+
* Not copyable.
30+
*/
31+
TextLayoutManager(const TextLayoutManager&) = delete;
32+
TextLayoutManager& operator=(const TextLayoutManager&) = delete;
33+
34+
/*
35+
* Not movable.
36+
*/
37+
TextLayoutManager(TextLayoutManager&&) = delete;
38+
TextLayoutManager& operator=(TextLayoutManager&&) = delete;
39+
40+
/*
41+
* Measures `attributedString` using native text rendering infrastructure.
42+
*/
43+
TextMeasurement measure(
44+
const AttributedStringBox& attributedStringBox,
45+
const ParagraphAttributes& paragraphAttributes,
46+
const TextLayoutContext& layoutContext,
47+
const LayoutConstraints& layoutConstraints) const;
48+
49+
/*
50+
* Measures lines of `attributedString` using native text rendering
51+
* infrastructure.
52+
*/
53+
LinesMeasurements measureLines(
54+
const AttributedStringBox& attributedStringBox,
55+
const ParagraphAttributes& paragraphAttributes,
56+
const Size& size) const;
57+
58+
/*
59+
* Returns an opaque pointer to platform-specific TextLayoutManager.
60+
* Is used on a native views layer to delegate text rendering to the manager.
61+
*/
62+
std::shared_ptr<void> getNativeTextLayoutManager() const;
63+
64+
protected:
65+
std::shared_ptr<const ContextContainer> contextContainer_;
66+
std::shared_ptr<void> nativeTextLayoutManager_;
67+
TextMeasureCache textMeasureCache_;
68+
LineMeasureCache lineMeasureCache_;
69+
};
70+
71+
} // namespace facebook::react

0 commit comments

Comments
 (0)