Skip to content

Commit

Permalink
Refactor TextFragmentList into AttributedString
Browse files Browse the repository at this point in the history
...preparing it to support another layer over fragments.
  • Loading branch information
cubuspl42 committed Jan 29, 2024
1 parent cfc0ba0 commit bb434e9
Show file tree
Hide file tree
Showing 12 changed files with 122 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
import com.facebook.react.uimanager.ReactAccessibilityDelegate.Role;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.ViewProps;
import com.facebook.react.views.text.fragments.BridgeTextFragmentList;
import com.facebook.react.views.text.attributedstring.BridgeAttributedString;
import com.facebook.react.views.text.internal.span.CustomLetterSpacingSpan;
import com.facebook.react.views.text.internal.span.CustomLineHeightSpan;
import com.facebook.react.views.text.internal.span.CustomStyleSpan;
Expand Down Expand Up @@ -113,18 +113,38 @@ public static void deleteCachedSpannableForTag(int reactTag) {
sTagToSpannableCache.remove(reactTag);
}

private static void buildSpannableFromFragments(
private static void buildSpannableFromAttributedString(
Context context,
ReadableArray fragments,
ReadableMap attributedString,
SpannableStringBuilder sb,
List<SetSpanOperation> ops) {
if (ReactFeatureFlags.enableSpannableBuildingUnification) {
buildSpannableFromFragmentsUnified(context, fragments, sb, ops);
buildSpannableFromAttributedStringUnified(context, attributedString, sb, ops);
} else {
buildSpannableFromFragmentsDuplicated(context, fragments, sb, ops);
buildSpannableFromAttributedStringDuplicated(context, attributedString, sb, ops);
}
}

private static void buildSpannableFromAttributedStringDuplicated(
Context context,
ReadableMap attributedString,
SpannableStringBuilder sb,
List<SetSpanOperation> ops) {
buildSpannableFromFragmentsDuplicated(
context, attributedString.getArray("fragments"), sb, ops);
}

private static void buildSpannableFromAttributedStringUnified(
Context context,
ReadableMap attributedString,
SpannableStringBuilder sb,
List<SetSpanOperation> ops) {

final BridgeAttributedString bridgeAttributedString = new BridgeAttributedString(attributedString);

TextLayoutUtils.buildSpannableFromAttributedString(context, bridgeAttributedString, sb, ops);
}

private static void buildSpannableFromFragmentsDuplicated(
Context context,
ReadableArray fragments,
Expand Down Expand Up @@ -223,17 +243,6 @@ private static void buildSpannableFromFragmentsDuplicated(
}
}

private static void buildSpannableFromFragmentsUnified(
Context context,
ReadableArray fragments,
SpannableStringBuilder sb,
List<SetSpanOperation> ops) {

final BridgeTextFragmentList textFragmentList = new BridgeTextFragmentList(fragments);

TextLayoutUtils.buildSpannableFromTextFragmentList(context, textFragmentList, sb, ops);
}

// public because both ReactTextViewManager and ReactTextInputManager need to use this
public static Spannable getOrCreateSpannableForText(
Context context,
Expand All @@ -256,7 +265,7 @@ private static Spannable createSpannableFromAttributedString(
// a new spannable will be wiped out
List<SetSpanOperation> ops = new ArrayList<>();

buildSpannableFromFragments(context, attributedString.getArray("fragments"), sb, ops);
buildSpannableFromAttributedString(context, attributedString, sb, ops);

// TODO T31905686: add support for inline Images
// While setting the Spans on the final text, we also check whether any of them are images.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@
import com.facebook.react.common.mapbuffer.ReadableMapBuffer;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.views.text.attributedstring.MapBufferAttributedString;
import com.facebook.react.uimanager.ReactAccessibilityDelegate.AccessibilityRole;
import com.facebook.react.uimanager.ReactAccessibilityDelegate.Role;
import com.facebook.react.views.text.fragments.MapBufferTextFragmentList;
import com.facebook.react.views.text.internal.span.CustomLetterSpacingSpan;
import com.facebook.react.views.text.internal.span.CustomLineHeightSpan;
import com.facebook.react.views.text.internal.span.CustomStyleSpan;
Expand Down Expand Up @@ -138,15 +138,28 @@ public static boolean isRTL(MapBuffer attributedString) {
== LayoutDirection.RTL;
}

private static void buildSpannableFromFragments(
Context context, MapBuffer fragments, SpannableStringBuilder sb, List<SetSpanOperation> ops) {
private static void buildSpannableFromAttributedString(
Context context, MapBuffer attributedString, SpannableStringBuilder sb, List<SetSpanOperation> ops) {
if (ReactFeatureFlags.enableSpannableBuildingUnification) {
buildSpannableFromFragmentsUnified(context, fragments, sb, ops);
buildSpannableFromAttributedStringDuplicated(context, attributedString, sb, ops);
} else {
buildSpannableFromFragmentsDuplicated(context, fragments, sb, ops);
buildSpannableFromAttributedStringUnified(context, attributedString, sb, ops);
}
}

private static void buildSpannableFromAttributedStringDuplicated(
Context context, MapBuffer attributedString, SpannableStringBuilder sb, List<SetSpanOperation> ops) {
buildSpannableFromFragmentsDuplicated(context, attributedString.getMapBuffer(AS_KEY_FRAGMENTS), sb, ops);
}

private static void buildSpannableFromAttributedStringUnified(
Context context, MapBuffer attributedString, SpannableStringBuilder sb, List<SetSpanOperation> ops) {

final MapBufferAttributedString mapBufferAttributedString = new MapBufferAttributedString(attributedString);

TextLayoutUtils.buildSpannableFromAttributedString(context, mapBufferAttributedString, sb, ops);
}

private static void buildSpannableFromFragmentsDuplicated(
Context context, MapBuffer fragments, SpannableStringBuilder sb, List<SetSpanOperation> ops) {

Expand Down Expand Up @@ -241,14 +254,6 @@ private static void buildSpannableFromFragmentsDuplicated(
}
}

private static void buildSpannableFromFragmentsUnified(
Context context, MapBuffer fragments, SpannableStringBuilder sb, List<SetSpanOperation> ops) {

final MapBufferTextFragmentList textFragmentList = new MapBufferTextFragmentList(fragments);

TextLayoutUtils.buildSpannableFromTextFragmentList(context, textFragmentList, sb, ops);
}

// public because both ReactTextViewManager and ReactTextInputManager need to use this
public static Spannable getOrCreateSpannableForText(
Context context,
Expand Down Expand Up @@ -292,7 +297,7 @@ private static Spannable createSpannableFromAttributedString(
// a new spannable will be wiped out
List<SetSpanOperation> ops = new ArrayList<>();

buildSpannableFromFragments(context, attributedString.getMapBuffer(AS_KEY_FRAGMENTS), sb, ops);
buildSpannableFromAttributedString(context, attributedString, sb, ops);

// TODO T31905686: add support for inline Images
// While setting the Spans on the final text, we also check whether any of them are images.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import android.view.View
import com.facebook.react.common.ReactConstants
import com.facebook.react.uimanager.PixelUtil
import com.facebook.react.uimanager.ReactAccessibilityDelegate
import com.facebook.react.views.text.fragments.TextFragment
import com.facebook.react.views.text.fragments.TextFragmentList
import com.facebook.react.views.text.attributedstring.AttributedString
import com.facebook.react.views.text.attributedstring.AttributedStringFragment
import com.facebook.react.views.text.internal.span.CustomLetterSpacingSpan
import com.facebook.react.views.text.internal.span.CustomLineHeightSpan
import com.facebook.react.views.text.internal.span.CustomStyleSpan
Expand All @@ -36,14 +36,14 @@ internal object TextLayoutUtils {
private const val INLINE_VIEW_PLACEHOLDER = "0"

@JvmStatic
fun buildSpannableFromTextFragmentList(
fun buildSpannableFromAttributedString(
context: Context,
textFragmentList: TextFragmentList,
attributedString: AttributedString,
sb: SpannableStringBuilder,
ops: MutableList<SetSpanOperation>,
) {
for (i in 0 until textFragmentList.count) {
val fragment = textFragmentList.getFragment(i)
for (i in 0 until attributedString.fragmentCount) {
val fragment = attributedString.getFragment(i)

addApplicableFragmentSpans(
context = context,
Expand All @@ -54,9 +54,10 @@ internal object TextLayoutUtils {
}
}

@JvmStatic
private fun addApplicableFragmentSpans(
context: Context,
fragment: TextFragment,
fragment: AttributedStringFragment,
sb: SpannableStringBuilder,
ops: MutableList<SetSpanOperation>,
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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.
*/

package com.facebook.react.views.text.attributedstring

/** Interface for an attributed string */
internal interface AttributedString {
fun getFragment(index: Int): AttributedStringFragment

val fragmentCount: Int
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.react.views.text.fragments
package com.facebook.react.views.text.attributedstring

import com.facebook.react.views.text.TextAttributeProps

/** Interface for a text fragment */
internal interface TextFragment {
/** Interface for an attributed string fragment */
internal interface AttributedStringFragment {
val textAttributeProps: TextAttributeProps

val string: String?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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.
*/

package com.facebook.react.views.text.attributedstring

import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap

/** An [AttributedString] backed by a [ReadableMap] */
internal class BridgeAttributedString(private val attributedString: ReadableMap) : AttributedString {
override fun getFragment(index: Int): AttributedStringFragment = BridgeAttributedStringFragment(fragments.getMap(index))

override val fragmentCount: Int
get() = fragments.size()

private val fragments: ReadableArray
get() = attributedString.getArray("fragments")!!
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.react.views.text.fragments
package com.facebook.react.views.text.attributedstring

import com.facebook.react.bridge.ReadableMap
import com.facebook.react.uimanager.ReactStylesDiffMap
import com.facebook.react.uimanager.ViewProps
import com.facebook.react.views.text.TextAttributeProps

/** A [TextFragment] implementation backed by a a [ReadableMap] */
internal class BridgeTextFragment(private val fragment: ReadableMap) : TextFragment {
/** Am [AttributedStringFragment] implementation backed by a a [ReadableMap] */
internal class BridgeAttributedStringFragment(private val fragment: ReadableMap) : AttributedStringFragment {
override val textAttributeProps: TextAttributeProps
get() =
TextAttributeProps.fromReadableMap(ReactStylesDiffMap(fragment.getMap("textAttributes")))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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.
*/

package com.facebook.react.views.text.attributedstring

import com.facebook.react.common.mapbuffer.MapBuffer
import com.facebook.react.views.text.TextLayoutManagerMapBuffer.AS_KEY_FRAGMENTS

/** An [AttributedString] backed by a [MapBuffer] */
internal class MapBufferAttributedString(private val attributedString: MapBuffer) : AttributedString {
override fun getFragment(index: Int): AttributedStringFragment =
MapBufferAttributedStringFragment(fragments.getMapBuffer(index))

override val fragmentCount: Int
get() = fragments.count

private val fragments: MapBuffer
get() = attributedString.getMapBuffer(AS_KEY_FRAGMENTS.toInt())
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.react.views.text.fragments
package com.facebook.react.views.text.attributedstring

import com.facebook.react.common.mapbuffer.MapBuffer
import com.facebook.react.views.text.TextAttributeProps
Expand All @@ -16,8 +16,8 @@ import com.facebook.react.views.text.TextLayoutManagerMapBuffer.FR_KEY_STRING
import com.facebook.react.views.text.TextLayoutManagerMapBuffer.FR_KEY_TEXT_ATTRIBUTES
import com.facebook.react.views.text.TextLayoutManagerMapBuffer.FR_KEY_WIDTH

/** A [TextFragment] implementation backed by a [MapBuffer] */
internal class MapBufferTextFragment(private val fragment: MapBuffer) : TextFragment {
/** An [AttributedStringFragment] implementation backed by a [MapBuffer] */
internal class MapBufferAttributedStringFragment(private val fragment: MapBuffer) : AttributedStringFragment {
override val textAttributeProps: TextAttributeProps
get() = TextAttributeProps.fromMapBuffer(fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES.toInt()))

Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

0 comments on commit bb434e9

Please sign in to comment.