Files
react-native/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextLayoutManagerExtended.h
T
Nick Gerleman f85b30b24b Underpinnings for Caching Text Layouts (#51065)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51065

This adds infrastructure to let us start storing cached Android text layouts as part of a `ParagraphShadowNode`. After this, we will clear them out, and propagate them to state. Right now, the flag doesn't do much, apart from extra work.

This is done by adding `TextLayoutManagerExtended::supportsPreparedLayout()`, and `TextLayoutManager::PreparedLayout` types, to shim between platforms, then on Android, we add a `PreparedLayout`, which is for now just an Android layout, with extra field (`maxNumberOfLines`, for some reason not exposed on recent versions).

Android `TextLayoutManager` java side is split a little bit, so that we reuse all the existing logic for prepared layouts. I tried to set up the boundary, so that we don't reserialize a MapBuffer after preparation, and for simplicity, this means source of truth for attachment count, and attachment sizes, now lives on the layout. This means we need to change boundary a bit, where we are no longer able to pass in a buffer to fill from C++ side of attachment positions.

Changelog: [Internal]

Reviewed By: mdvacca

Differential Revision: D73970149

fbshipit-source-id: ff71c227e062c16fe52a4eb3ba2acbebf3d96e56
2025-05-05 16:48:16 -07:00

125 lines
4.0 KiB
C++

/*
* Copyright (c) Meta Platforms, Inc. and 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 <glog/logging.h>
#include <cstddef>
#include <react/renderer/attributedstring/AttributedStringBox.h>
#include <react/renderer/attributedstring/ParagraphAttributes.h>
#include <react/renderer/graphics/Size.h>
#include <react/renderer/textlayoutmanager/TextLayoutManager.h>
#include <react/renderer/textlayoutmanager/TextMeasureCache.h>
namespace facebook::react {
template <typename TextLayoutManagerT>
concept TextLayoutManagerWithPreparedLayout = requires(
TextLayoutManagerT textLayoutManager,
AttributedString attributedString,
ParagraphAttributes paragraphAttributes,
TextLayoutContext layoutContext,
LayoutConstraints layoutConstraints,
typename TextLayoutManagerT::PreparedLayout preparedLayout) {
sizeof(typename TextLayoutManagerT::PreparedLayout);
{
textLayoutManager.prepareLayout(
attributedString, paragraphAttributes, layoutContext, layoutConstraints)
} -> std::same_as<typename TextLayoutManagerT::PreparedLayout>;
{
textLayoutManager.measurePreparedLayout(
preparedLayout, layoutContext, layoutConstraints)
} -> std::same_as<TextMeasurement>;
};
namespace detail {
template <typename T>
struct PreparedLayoutT {
using type = std::nullptr_t;
};
template <TextLayoutManagerWithPreparedLayout T>
struct PreparedLayoutT<T> {
using type = typename T::PreparedLayout;
};
/**
* TextLayoutManagerExtended acts as an adapter for TextLayoutManager methods
* which may not exist for a specific platform. Callers can check at
* compile-time whether a method is supported, and calling if it is not will
* terminate.
*/
template <typename TextLayoutManagerT>
class TextLayoutManagerExtended {
public:
static constexpr bool supportsLineMeasurement() {
return requires(TextLayoutManagerT textLayoutManager) {
{
textLayoutManager.measureLines(
AttributedStringBox{}, ParagraphAttributes{}, Size{})
} -> std::same_as<LinesMeasurements>;
};
}
static constexpr bool supportsPreparedLayout() {
return TextLayoutManagerWithPreparedLayout<TextLayoutManagerT>;
}
using PreparedLayout = typename PreparedLayoutT<TextLayoutManagerT>::type;
TextLayoutManagerExtended(const TextLayoutManagerT& textLayoutManager)
: textLayoutManager_(textLayoutManager) {}
LinesMeasurements measureLines(
const AttributedStringBox& attributedStringBox,
const ParagraphAttributes& paragraphAttributes,
const Size& size) {
if constexpr (supportsLineMeasurement()) {
return textLayoutManager_.measureLines(
attributedStringBox, paragraphAttributes, size);
}
LOG(FATAL) << "Platform TextLayoutManager does not support measureLines";
}
PreparedLayout prepareLayout(
const AttributedString& attributedString,
const ParagraphAttributes& paragraphAttributes,
const TextLayoutContext& layoutContext,
const LayoutConstraints& layoutConstraints) const {
if constexpr (supportsPreparedLayout()) {
return textLayoutManager_.prepareLayout(
attributedString,
paragraphAttributes,
layoutContext,
layoutConstraints);
}
LOG(FATAL) << "Platform TextLayoutManager does not support prepareLayout";
}
TextMeasurement measurePreparedLayout(
const PreparedLayout& layout,
const TextLayoutContext& layoutContext,
const LayoutConstraints& layoutConstraints) const {
if constexpr (supportsPreparedLayout()) {
return textLayoutManager_.measurePreparedLayout(
layout, layoutContext, layoutConstraints);
}
LOG(FATAL)
<< "Platform TextLayoutManager does not support measurePreparedLayout";
}
private:
const TextLayoutManagerT& textLayoutManager_;
};
} // namespace detail
using TextLayoutManagerExtended =
detail::TextLayoutManagerExtended<TextLayoutManager>;
} // namespace facebook::react