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 @@ -27,8 +27,8 @@ import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.uimanager.BackgroundStyleApplicator
import com.facebook.react.uimanager.ReactCompoundView
import com.facebook.react.uimanager.style.Overflow
import com.facebook.react.views.text.internal.span.DiscordShadowStyleSpan
import com.facebook.react.views.text.internal.span.ReactTagSpan
import com.facebook.react.views.text.internal.span.StrokeStyleSpan
import kotlin.collections.ArrayList
import kotlin.math.roundToInt

Expand Down Expand Up @@ -102,18 +102,14 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
override fun onDraw(canvas: Canvas) {
val layout = preparedLayout?.layout

// Get shadow adjustment from custom span if configured
val spanned = layout?.text as? Spanned
val shadowAdj = DiscordShadowStyleSpan.getShadowAdjustment(spanned)

if (overflow != Overflow.VISIBLE && !shadowAdj.hasShadow) {
if (overflow != Overflow.VISIBLE) {
BackgroundStyleApplicator.clipToPaddingBox(this, canvas)
}

super.onDraw(canvas)

canvas.translate(
paddingLeft.toFloat() + shadowAdj.leftOffset,
paddingLeft.toFloat(),
paddingTop.toFloat() + (preparedLayout?.verticalOffset ?: 0f))

if (layout != null) {
Expand All @@ -122,10 +118,17 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
selectionColor ?: DefaultStyleValuesUtil.getDefaultTextColorHighlight(context))
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
Api34Utils.draw(layout, canvas, selection?.path, selectionPaint)
} else {
layout.draw(canvas, selection?.path, selectionPaint, 0)
val drawLayout = Runnable {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
Api34Utils.draw(layout, canvas, selection?.path, selectionPaint)
} else {
layout.draw(canvas, selection?.path, selectionPaint, 0)
}
}

val strokeSpan = StrokeStyleSpan.getStrokeSpan(layout.text as? Spanned)
if (strokeSpan == null || !strokeSpan.draw(layout.paint, drawLayout)) {
drawLayout.run()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.LeadingMarginSpan;
import android.view.Gravity;
import androidx.annotation.Nullable;
import com.facebook.common.logging.FLog;
Expand Down Expand Up @@ -48,7 +50,7 @@
import com.facebook.react.views.text.internal.span.ReactTagSpan;
import com.facebook.react.views.text.internal.span.ReactUnderlineSpan;
import com.facebook.react.views.text.internal.span.SetSpanOperation;
import com.facebook.react.views.text.internal.span.DiscordShadowStyleSpan;
import com.facebook.react.views.text.internal.span.ShadowStyleSpan;
import com.facebook.react.views.text.internal.span.StrokeStyleSpan;
import com.facebook.react.views.text.internal.span.TextInlineImageSpan;
import com.facebook.react.views.text.internal.span.TextInlineViewPlaceholderSpan;
Expand Down Expand Up @@ -226,31 +228,29 @@ private static void buildSpannedFromShadowNode(
if (textShadowNode.mIsLineThroughTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new ReactStrikethroughSpan()));
}
ShadowStyleSpan shadowSpan = null;
if ((textShadowNode.mTextShadowOffsetDx != 0
|| textShadowNode.mTextShadowOffsetDy != 0
|| textShadowNode.mTextShadowRadius != 0)
&& Color.alpha(textShadowNode.mTextShadowColor) != 0) {
ops.add(
new SetSpanOperation(
start,
end,
new DiscordShadowStyleSpan(
textShadowNode.mTextShadowOffsetDx,
textShadowNode.mTextShadowOffsetDy,
textShadowNode.mTextShadowRadius,
textShadowNode.mTextShadowColor)));
shadowSpan = new ShadowStyleSpan(
textShadowNode.mTextShadowOffsetDx,
textShadowNode.mTextShadowOffsetDy,
textShadowNode.mTextShadowRadius,
textShadowNode.mTextShadowColor);
ops.add(new SetSpanOperation(start, end, shadowSpan));
}

StrokeStyleSpan strokeSpan = null;
if (!Float.isNaN(textShadowNode.mTextStrokeWidth)
&& textShadowNode.mTextStrokeWidth > 0
&& textShadowNode.mIsTextStrokeColorSet) {
ops.add(
new SetSpanOperation(
start,
end,
new StrokeStyleSpan(
textShadowNode.mTextStrokeWidth,
textShadowNode.mTextStrokeColor)));
strokeSpan = new StrokeStyleSpan(
textShadowNode.mTextStrokeWidth,
textShadowNode.mTextStrokeColor);
ops.add(new SetSpanOperation(start, end, strokeSpan));
}

float effectiveLineHeight = textAttributes.getEffectiveLineHeight();
if (!Float.isNaN(effectiveLineHeight)
&& (parentTextAttributes == null
Expand Down Expand Up @@ -335,6 +335,20 @@ protected Spannable spannedFromShadowNode(
textShadowNode.mTextAttributes.setHeightOfTallestInlineViewOrImage(
heightOfTallestInlineViewOrImage);

// Add leading margin for stroke/shadow that extends beyond text bounds
StrokeStyleSpan strokeSpan = StrokeStyleSpan.getStrokeSpan(sb);
ShadowStyleSpan shadowSpan = ShadowStyleSpan.getShadowSpan(sb);
float strokeOffset = strokeSpan != null ? strokeSpan.getLeftOffset() : 0f;
float shadowOffset = shadowSpan != null ? shadowSpan.getLeftOffset() : 0f;
int leadingMargin = (int) Math.max(strokeOffset, shadowOffset);
if (leadingMargin > 0) {
sb.setSpan(
new LeadingMarginSpan.Standard(leadingMargin),
0,
sb.length(),
Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}

if (mReactTextViewManagerCallback != null) {
mReactTextViewManagerCallback.onPostProcessSpannable(sb);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.Layout;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
Expand Down Expand Up @@ -52,8 +54,8 @@
import com.facebook.react.uimanager.style.BorderStyle;
import com.facebook.react.uimanager.style.LogicalEdge;
import com.facebook.react.uimanager.style.Overflow;
import com.facebook.react.views.text.internal.span.DiscordShadowStyleSpan;
import com.facebook.react.views.text.internal.span.ReactTagSpan;
import com.facebook.react.views.text.internal.span.StrokeStyleSpan;
import com.facebook.react.views.text.internal.span.TextInlineImageSpan;
import com.facebook.react.views.text.internal.span.TextInlineViewPlaceholderSpan;
import com.facebook.yoga.YogaMeasureMode;
Expand Down Expand Up @@ -361,19 +363,19 @@ protected void onDraw(Canvas canvas) {
setText(spanned);
}

// Get shadow adjustment from custom span if configured
DiscordShadowStyleSpan.ShadowAdjustment shadowAdj =
DiscordShadowStyleSpan.getShadowAdjustment(spanned);

canvas.save();
canvas.translate(shadowAdj.getLeftOffset(), 0);

if (mOverflow != Overflow.VISIBLE && !shadowAdj.getHasShadow()) {
if (mOverflow != Overflow.VISIBLE) {
canvas.save();
BackgroundStyleApplicator.clipToPaddingBox(this, canvas);
}

super.onDraw(canvas);
canvas.restore();
StrokeStyleSpan strokeSpan = StrokeStyleSpan.getStrokeSpan((Spanned) getText());
if (strokeSpan == null || !strokeSpan.draw(getPaint(), () -> super.onDraw(canvas))) {
super.onDraw(canvas);
}

if (mOverflow != Overflow.VISIBLE) {
canvas.restore();
}
}
}

Expand Down Expand Up @@ -800,4 +802,5 @@ public void setOverflow(@Nullable String overflow) {

invalidate();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import android.text.StaticLayout
import android.text.TextDirectionHeuristics
import android.text.TextPaint
import android.text.TextUtils
import android.text.style.LeadingMarginSpan
import android.util.LayoutDirection
import android.view.Gravity
import android.view.View
Expand All @@ -47,10 +48,10 @@ import com.facebook.react.views.text.internal.span.ReactOpacitySpan
import com.facebook.react.views.text.internal.span.ReactStrikethroughSpan
import com.facebook.react.views.text.internal.span.ReactTagSpan
import com.facebook.react.views.text.internal.span.ReactTextPaintHolderSpan
import com.facebook.react.views.text.internal.span.DiscordShadowStyleSpan
import com.facebook.react.views.text.internal.span.ShadowStyleSpan
import com.facebook.react.views.text.internal.span.StrokeStyleSpan
import com.facebook.react.views.text.internal.span.ReactUnderlineSpan
import com.facebook.react.views.text.internal.span.SetSpanOperation
import com.facebook.react.views.text.internal.span.StrokeStyleSpan
import com.facebook.react.views.text.internal.span.TextInlineViewPlaceholderSpan
import com.facebook.yoga.YogaMeasureMode
import com.facebook.yoga.YogaMeasureOutput
Expand Down Expand Up @@ -306,7 +307,7 @@ internal object TextLayoutManager {
SetSpanOperation(
start,
end,
DiscordShadowStyleSpan(
ShadowStyleSpan(
textAttributes.mTextShadowOffsetDx,
textAttributes.mTextShadowOffsetDy,
textAttributes.mTextShadowRadius,
Expand All @@ -315,14 +316,15 @@ internal object TextLayoutManager {
if (!textAttributes.textStrokeWidth.isNaN() &&
textAttributes.textStrokeWidth > 0 &&
textAttributes.isTextStrokeColorSet) {
val strokeWidth = PixelUtil.toPixelFromDIP(textAttributes.textStrokeWidth.toDouble()).toFloat()
val strokeColor = textAttributes.textStrokeColor
ops.add(
SetSpanOperation(
start,
end,
StrokeStyleSpan(strokeWidth, strokeColor)))
StrokeStyleSpan(
textAttributes.textStrokeWidth,
textAttributes.textStrokeColor)))
}

if (!textAttributes.effectiveLineHeight.isNaN()) {
ops.add(
SetSpanOperation(
Expand Down Expand Up @@ -470,7 +472,7 @@ internal object TextLayoutManager {
fragment.props.textShadowRadius != 0f) &&
Color.alpha(fragment.props.textShadowColor) != 0) {
spannable.setSpan(
DiscordShadowStyleSpan(
ShadowStyleSpan(
fragment.props.textShadowOffsetDx,
fragment.props.textShadowOffsetDy,
fragment.props.textShadowRadius,
Expand All @@ -483,9 +485,10 @@ internal object TextLayoutManager {
if (!fragment.props.textStrokeWidth.isNaN() &&
fragment.props.textStrokeWidth > 0 &&
fragment.props.isTextStrokeColorSet) {
val strokeWidth = PixelUtil.toPixelFromDIP(fragment.props.textStrokeWidth.toDouble()).toFloat()
spannable.setSpan(
StrokeStyleSpan(strokeWidth, fragment.props.textStrokeColor),
StrokeStyleSpan(
fragment.props.textStrokeWidth,
fragment.props.textStrokeColor),
start,
end,
spanFlags)
Expand All @@ -502,9 +505,25 @@ internal object TextLayoutManager {
start = end
}

addLeadingMarginForTextEffects(spannable)
return spannable
}

private fun addLeadingMarginForTextEffects(spannable: Spannable) {
val strokeSpan = StrokeStyleSpan.getStrokeSpan(spannable)
val shadowSpan = ShadowStyleSpan.getShadowSpan(spannable)
val strokeOffset = strokeSpan?.getLeftOffset() ?: 0f
val shadowOffset = shadowSpan?.getLeftOffset() ?: 0f
val leadingMargin = max(strokeOffset, shadowOffset).toInt()
if (leadingMargin > 0) {
spannable.setSpan(
LeadingMarginSpan.Standard(leadingMargin),
0,
spannable.length,
Spanned.SPAN_INCLUSIVE_INCLUSIVE)
}
}

fun getOrCreateSpannableForText(
context: Context,
attributedString: MapBuffer,
Expand Down Expand Up @@ -555,6 +574,7 @@ internal object TextLayoutManager {
op.execute(sb, priorityIndex)
}

addLeadingMarginForTextEffects(sb)
reactTextViewManagerCallback?.onPostProcessSpannable(sb)
return sb
}
Expand Down
Loading
Loading