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 @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<a89ede36c4631b7a7709899858a7f9c9>>
* @generated SignedSource<<8224c3abed0fa461f272b4bfaa9ec698>>
*/

/**
Expand Down Expand Up @@ -396,6 +396,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = accessor.fixMappingOfEventPrioritiesBetweenFabricAndReact()

/**
* Grows lines of text with `lineHeight` to fit taller inline views instead of letting them overflow, keeps lines after an inline view at `lineHeight` on Android, and aligns inline views with the text baseline on iOS.
*/
@JvmStatic
public fun fixTextInlineViewLineHeight(): Boolean = accessor.fixTextInlineViewLineHeight()

/**
* Re-measures text and re-sends padding and overflow insets to mounted views when the pixel density changes, so text and padded views do not keep sizes from the old density.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<cd3b4f6a7fb4e616d3f56ab8c9eb9658>>
* @generated SignedSource<<1b9a676a7359de07c9660be087c262dc>>
*/

/**
Expand Down Expand Up @@ -81,6 +81,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var enableVirtualViewContainerStateExperimentalCache: Boolean? = null
private var fixBorderlessRippleAndroidCache: Boolean? = null
private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
private var fixTextInlineViewLineHeightCache: Boolean? = null
private var fixTextLayoutOnPixelDensityChangeCache: Boolean? = null
private var fixYogaFlexBasisFitContentInMainAxisCache: Boolean? = null
private var fuseboxAssertSingleHostStateCache: Boolean? = null
Expand Down Expand Up @@ -667,6 +668,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}

override fun fixTextInlineViewLineHeight(): Boolean {
var cached = fixTextInlineViewLineHeightCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.fixTextInlineViewLineHeight()
fixTextInlineViewLineHeightCache = cached
}
return cached
}

override fun fixTextLayoutOnPixelDensityChange(): Boolean {
var cached = fixTextLayoutOnPixelDensityChangeCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<6d86d4adbc261c9271b5bed72bd12dcf>>
* @generated SignedSource<<c3cf91414b1206d03ff6c40f843b74a9>>
*/

/**
Expand Down Expand Up @@ -150,6 +150,8 @@ public object ReactNativeFeatureFlagsCxxInterop {

@DoNotStrip @JvmStatic public external fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean

@DoNotStrip @JvmStatic public external fun fixTextInlineViewLineHeight(): Boolean

@DoNotStrip @JvmStatic public external fun fixTextLayoutOnPixelDensityChange(): Boolean

@DoNotStrip @JvmStatic public external fun fixYogaFlexBasisFitContentInMainAxis(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<a1368426dd823245c54b4312ae7b14e6>>
* @generated SignedSource<<63ab6b4c29d6a2cf2d4bab0f5677d901>>
*/

/**
Expand Down Expand Up @@ -145,6 +145,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi

override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = false

override fun fixTextInlineViewLineHeight(): Boolean = false

override fun fixTextLayoutOnPixelDensityChange(): Boolean = false

override fun fixYogaFlexBasisFitContentInMainAxis(): Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<c305274be2fc6720b6c0eb75e78bd6b0>>
* @generated SignedSource<<974bc6b82dc2de7c83c381ad960cbdfc>>
*/

/**
Expand Down Expand Up @@ -85,6 +85,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var enableVirtualViewContainerStateExperimentalCache: Boolean? = null
private var fixBorderlessRippleAndroidCache: Boolean? = null
private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
private var fixTextInlineViewLineHeightCache: Boolean? = null
private var fixTextLayoutOnPixelDensityChangeCache: Boolean? = null
private var fixYogaFlexBasisFitContentInMainAxisCache: Boolean? = null
private var fuseboxAssertSingleHostStateCache: Boolean? = null
Expand Down Expand Up @@ -732,6 +733,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}

override fun fixTextInlineViewLineHeight(): Boolean {
var cached = fixTextInlineViewLineHeightCache
if (cached == null) {
cached = currentProvider.fixTextInlineViewLineHeight()
accessedFeatureFlags.add("fixTextInlineViewLineHeight")
fixTextInlineViewLineHeightCache = cached
}
return cached
}

override fun fixTextLayoutOnPixelDensityChange(): Boolean {
var cached = fixTextLayoutOnPixelDensityChangeCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<c82895ef7e5cee6ba387620a43aa88b8>>
* @generated SignedSource<<4f36eb12d5d8cd8a955b46fbb164bc25>>
*/

/**
Expand Down Expand Up @@ -35,6 +35,8 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Canary_Android : ReactN

override fun fixBorderlessRippleAndroid(): Boolean = true

override fun fixTextInlineViewLineHeight(): Boolean = true

override fun fuseboxFrameRecordingEnabled(): Boolean = true

override fun fuseboxWebSocketEventsEnabled(): Boolean = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<da45e41e9a58711ee3b68a38965938fb>>
* @generated SignedSource<<65c68d67b088b44cde90cebd7bda9e63>>
*/

/**
Expand Down Expand Up @@ -145,6 +145,8 @@ public interface ReactNativeFeatureFlagsProvider {

@DoNotStrip public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean

@DoNotStrip public fun fixTextInlineViewLineHeight(): Boolean

@DoNotStrip public fun fixTextLayoutOnPixelDensityChange(): Boolean

@DoNotStrip public fun fixYogaFlexBasisFitContentInMainAxis(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
package com.facebook.react.views.text.internal.span

import android.graphics.Paint.FontMetricsInt
import android.text.Spanned
import android.text.TextPaint
import android.text.style.LineHeightSpan
import android.text.style.MetricAffectingSpan
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
import kotlin.math.ceil
import kotlin.math.floor

Expand All @@ -17,7 +21,7 @@ import kotlin.math.floor
* LineHeightSpan.Standard which only effects space between the baselines of adjacent line boxes
* (does not impact space before the first line or after the last).
*/
internal class CustomLineHeightSpan(height: Float) : LineHeightSpan, ReactSpan {
internal class CustomLineHeightSpan(height: Float) : LineHeightSpan.WithDensity, ReactSpan {
val lineHeight: Int = ceil(height.toDouble()).toInt()

override fun chooseHeight(
Expand Down Expand Up @@ -53,4 +57,95 @@ internal class CustomLineHeightSpan(height: Float) : LineHeightSpan, ReactSpan {
fm.bottom = fm.descent
}
}

override fun chooseHeight(
text: CharSequence,
start: Int,
end: Int,
spanstartv: Int,
v: Int,
fm: FontMetricsInt,
paint: TextPaint,
) {
val spanned = text as? Spanned
if (
!ReactNativeFeatureFlags.fixTextInlineViewLineHeight() ||
spanned == null ||
!containsInlineView(spanned)
) {
chooseHeight(text, start, end, spanstartv, v, fm)
return
}

// If text contains an inline view anywhere, remeasure fm before calling chooseHeight() to avoid
// clipping the view. StaticLayout seeds the next line's fm with this line's if a text run
// wraps, so remeasurement is needed even if an inline view doesn't exist on the current line.
val inlineViewHeight = maxInlineViewHeight(spanned, start, end)
measureTextRuns(spanned, start, end, paint, fm)
chooseHeight(text, start, end, spanstartv, v, fm)
if (-fm.ascent < inlineViewHeight) {
fm.ascent = -inlineViewHeight
fm.top = minOf(fm.top, fm.ascent)
}
}

/**
* Sets [fm] to the combined font metrics of the text in [start, end), excluding inline views.
*
* The line is split into runs at each [MetricAffectingSpan] transition. Each run that is not an
* inline view placeholder is measured with [basePaint] after applying that run's spans (font
* size, typeface, etc.), and [fm] encloses the tallest ascent and deepest descent across runs. If
* the line has no text runs (only inline views), [fm] falls back to the metrics of [basePaint],
* matching the strut of a CSS line box.
*
* Only the primary font of each run is measured, so unlike StaticLayout, glyphs drawn with a
* fallback font (such as emoji) do not contribute to the metrics.
*/
private fun measureTextRuns(
text: Spanned,
start: Int,
end: Int,
basePaint: TextPaint,
fm: FontMetricsInt,
) {
val runPaint = TextPaint()
val runFm = FontMetricsInt()
fm.top = 0
fm.ascent = 0
fm.descent = 0
fm.bottom = 0
var hasTextRun = false
var runStart = start
while (runStart < end) {
val runEnd = text.nextSpanTransition(runStart, end, MetricAffectingSpan::class.java)
if (text.getSpans(runStart, runEnd, TextInlineViewPlaceholderSpan::class.java).isEmpty()) {
hasTextRun = true
runPaint.set(basePaint)
text.getSpans(runStart, runEnd, MetricAffectingSpan::class.java).forEach {
it.updateMeasureState(runPaint)
}
runPaint.getFontMetricsInt(runFm)
fm.top = minOf(fm.top, runFm.top)
fm.ascent = minOf(fm.ascent, runFm.ascent)
fm.descent = maxOf(fm.descent, runFm.descent)
fm.bottom = maxOf(fm.bottom, runFm.bottom)
}
runStart = runEnd
}
if (!hasTextRun) {
basePaint.getFontMetricsInt(fm)
}
}

/** Returns whether [text] contains an inline view anywhere, not just on the current line. */
private fun containsInlineView(text: Spanned): Boolean {
val firstTransition =
text.nextSpanTransition(0, text.length, TextInlineViewPlaceholderSpan::class.java)
return firstTransition < text.length
}

/** Returns the height of the tallest inline view in [start, end), or 0 if there are none. */
private fun maxInlineViewHeight(text: Spanned, start: Int, end: Int): Int =
text.getSpans(start, end, TextInlineViewPlaceholderSpan::class.java).maxOfOrNull { it.height }
?: 0
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<944781e7a547e601f0637869a3a3ec5e>>
* @generated SignedSource<<c33618010c146191e9b11cfdf0bf94d3>>
*/

/**
Expand Down Expand Up @@ -405,6 +405,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}

bool fixTextInlineViewLineHeight() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fixTextInlineViewLineHeight");
return method(javaProvider_);
}

bool fixTextLayoutOnPixelDensityChange() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fixTextLayoutOnPixelDensityChange");
Expand Down Expand Up @@ -930,6 +936,11 @@ bool JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabri
return ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact();
}

bool JReactNativeFeatureFlagsCxxInterop::fixTextInlineViewLineHeight(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::fixTextInlineViewLineHeight();
}

bool JReactNativeFeatureFlagsCxxInterop::fixTextLayoutOnPixelDensityChange(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::fixTextLayoutOnPixelDensityChange();
Expand Down Expand Up @@ -1324,6 +1335,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"fixMappingOfEventPrioritiesBetweenFabricAndReact",
JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabricAndReact),
makeNativeMethod(
"fixTextInlineViewLineHeight",
JReactNativeFeatureFlagsCxxInterop::fixTextInlineViewLineHeight),
makeNativeMethod(
"fixTextLayoutOnPixelDensityChange",
JReactNativeFeatureFlagsCxxInterop::fixTextLayoutOnPixelDensityChange),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<ad9275a6afb74520bec7d446faafd1bc>>
* @generated SignedSource<<7c754a14e96a400f346076811c13cecf>>
*/

/**
Expand Down Expand Up @@ -213,6 +213,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool fixMappingOfEventPrioritiesBetweenFabricAndReact(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

static bool fixTextInlineViewLineHeight(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

static bool fixTextLayoutOnPixelDensityChange(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<e54d2f7c5761d2bdb0edff1065896eb2>>
* @generated SignedSource<<a48eac32b58c7c0f90dd5bf090f8c25f>>
*/

/**
Expand Down Expand Up @@ -270,6 +270,10 @@ bool ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact()
return getAccessor().fixMappingOfEventPrioritiesBetweenFabricAndReact();
}

bool ReactNativeFeatureFlags::fixTextInlineViewLineHeight() {
return getAccessor().fixTextInlineViewLineHeight();
}

bool ReactNativeFeatureFlags::fixTextLayoutOnPixelDensityChange() {
return getAccessor().fixTextLayoutOnPixelDensityChange();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<6887b671526bc8a3b0678e3e45b4e3b4>>
* @generated SignedSource<<242f143d34bd97101cb0d71075cfcd9e>>
*/

/**
Expand Down Expand Up @@ -346,6 +346,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool fixMappingOfEventPrioritiesBetweenFabricAndReact();

/**
* Grows lines of text with `lineHeight` to fit taller inline views instead of letting them overflow, keeps lines after an inline view at `lineHeight` on Android, and aligns inline views with the text baseline on iOS.
*/
RN_EXPORT static bool fixTextInlineViewLineHeight();

/**
* Re-measures text and re-sends padding and overflow insets to mounted views when the pixel density changes, so text and padded views do not keep sizes from the old density.
*/
Expand Down
Loading
Loading