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 @@ -30,6 +30,7 @@ - (RCTShadowView *)shadowView
RCT_REMAP_SHADOW_PROPERTY(backgroundColor, textAttributes.backgroundColor, UIColor)
RCT_REMAP_SHADOW_PROPERTY(gradientColors, textAttributes.gradientColors, NSArray)
RCT_REMAP_SHADOW_PROPERTY(gradientAngle, textAttributes.gradientAngle, CGFloat)
RCT_REMAP_SHADOW_PROPERTY(gradientWidth, textAttributes.gradientWidth, CGFloat)
RCT_REMAP_SHADOW_PROPERTY(opacity, textAttributes.opacity, CGFloat)
// Font
RCT_REMAP_SHADOW_PROPERTY(fontFamily, textAttributes.fontFamily, NSString)
Expand Down
1 change: 1 addition & 0 deletions packages/react-native/Libraries/Text/RCTTextAttributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ extern NSString *const RCTTextAttributesTagAttributeName;
@property (nonatomic, strong, nullable) UIColor *backgroundColor;
@property (nonatomic, copy, nullable) NSArray *gradientColors;
@property (nonatomic, assign) CGFloat gradientAngle;
@property (nonatomic, assign) CGFloat gradientWidth; // Width of gradient pattern in pixels; NAN = use default (100px)
@property (nonatomic, assign) CGFloat opacity;
// Font
@property (nonatomic, copy, nullable) NSString *fontFamily;
Expand Down
7 changes: 4 additions & 3 deletions packages/react-native/Libraries/Text/RCTTextAttributes.mm
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ - (void)applyTextAttributes:(RCTTextAttributes *)textAttributes
_backgroundColor = textAttributes->_backgroundColor ?: _backgroundColor;
_gradientColors = textAttributes->_gradientColors ?: _gradientColors;
_gradientAngle = !isnan(textAttributes->_gradientAngle) ? textAttributes->_gradientAngle : _gradientAngle;
_gradientWidth = !isnan(textAttributes->_gradientWidth) ? textAttributes->_gradientWidth : _gradientWidth;
_opacity =
!isnan(textAttributes->_opacity) ? (isnan(_opacity) ? 1.0 : _opacity) * textAttributes->_opacity : _opacity;

Expand Down Expand Up @@ -323,8 +324,8 @@ - (UIColor *)effectiveForegroundColor
if([cgColors count] > 0) {
[cgColors addObject:cgColors[0]];
CAGradientLayer *gradient = [CAGradientLayer layer];
// this pattern width corresponds roughly to desktop's pattern width
int patternWidth = 100;
// Use gradientWidth if specified, otherwise default to 100
CGFloat patternWidth = (!isnan(_gradientWidth) && _gradientWidth > 0) ? _gradientWidth : 100;
CGFloat height = _lineHeight * self.effectiveFontSizeMultiplier;
gradient.frame = CGRectMake(0, 0, patternWidth, height);
gradient.colors = cgColors;
Expand Down Expand Up @@ -423,7 +424,7 @@ - (BOOL)isEqual:(RCTTextAttributes *)textAttributes

return RCTTextAttributesCompareObjects(_foregroundColor) && RCTTextAttributesCompareObjects(_backgroundColor) &&
RCTTextAttributesCompareObjects(_gradientColors) && RCTTextAttributesCompareFloats(_gradientAngle) &&
RCTTextAttributesCompareFloats(_opacity) &&
RCTTextAttributesCompareFloats(_gradientWidth) && RCTTextAttributesCompareFloats(_opacity) &&
// Font
RCTTextAttributesCompareObjects(_fontFamily) && RCTTextAttributesCompareFloats(_fontSize) &&
RCTTextAttributesCompareFloats(_fontSizeMultiplier) && RCTTextAttributesCompareFloats(_maxFontSizeMultiplier) &&
Expand Down
5 changes: 5 additions & 0 deletions packages/react-native/Libraries/Text/Text.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,11 @@ export interface TextProps
*/
gradientAngle?: number | undefined;

/**
* Width of the gradient pattern in pixels. Default is 100.
*/
gradientWidth?: number | undefined;

/**
* Width of the text stroke (outline). Creates an outer stroke effect.
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/react-native/Libraries/Text/TextNativeComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const textViewConfig = {
lineBreakStrategyIOS: true,
gradientColors: true,
gradientAngle: true,
gradientWidth: true,
textStrokeWidth: true,
textStrokeColor: true,
},
Expand All @@ -67,6 +68,7 @@ const virtualTextViewConfig = {
maxFontSizeMultiplier: true,
gradientColors: true,
gradientAngle: true,
gradientWidth: true,
textStrokeWidth: true,
textStrokeColor: true,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ private static void buildSpannedFromShadowNode(
if (textShadowNode.mGradientColors != null && textShadowNode.mGradientColors.length >= 2) {
int effectiveFontSize = textAttributes.getEffectiveFontSize();
float gradientAngle = Float.isNaN(textShadowNode.mGradientAngle) ? 0f : textShadowNode.mGradientAngle;
ops.add(new SetSpanOperation(start, end, new LinearGradientSpan(start * effectiveFontSize, textShadowNode.mGradientColors, gradientAngle)));
float gradientWidth = textShadowNode.mGradientWidth;
ops.add(new SetSpanOperation(start, end, new LinearGradientSpan(start * effectiveFontSize, textShadowNode.mGradientColors, gradientAngle, gradientWidth)));
}
if (textShadowNode.mIsBackgroundColorSet) {
ops.add(
Expand Down Expand Up @@ -351,6 +352,7 @@ protected Spannable spannedFromShadowNode(

protected @Nullable int[] mGradientColors = null;
protected float mGradientAngle = Float.NaN;
protected float mGradientWidth = Float.NaN;

protected @Nullable AccessibilityRole mAccessibilityRole = null;
protected @Nullable Role mRole = null;
Expand Down Expand Up @@ -547,6 +549,12 @@ public void setGradientAngle(float gradientAngle) {
markUpdated();
}

@ReactProp(name = "gradientWidth", defaultFloat = Float.NaN)
public void setGradientWidth(float gradientWidth) {
mGradientWidth = gradientWidth;
markUpdated();
}

@ReactProp(name = ViewProps.BACKGROUND_COLOR, customType = "Color")
public void setBackgroundColor(@Nullable Integer color) {
// Background color needs to be handled here for virtual nodes so it can be incorporated into
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public class TextAttributeProps {
public static final short TA_KEY_TEXT_STROKE_WIDTH = 31;
public static final short TA_KEY_TEXT_STROKE_COLOR = 32;
public static final short TA_KEY_GRADIENT_ANGLE = 33;
public static final short TA_KEY_GRADIENT_WIDTH = 34;

public static final int UNSET = -1;

Expand Down Expand Up @@ -159,6 +160,7 @@ public class TextAttributeProps {

protected @Nullable int[] mGradientColors = null;
protected float mGradientAngle = Float.NaN;
protected float mGradientWidth = Float.NaN;

private TextAttributeProps() {}

Expand Down Expand Up @@ -255,6 +257,9 @@ public static TextAttributeProps fromMapBuffer(MapBuffer props) {
case TA_KEY_GRADIENT_ANGLE:
result.setGradientAngle((float) entry.getDoubleValue());
break;
case TA_KEY_GRADIENT_WIDTH:
result.setGradientWidth((float) entry.getDoubleValue());
break;
case TA_KEY_MAX_FONT_SIZE_MULTIPLIER:
result.setMaxFontSizeMultiplier((float) entry.getDoubleValue());
break;
Expand Down Expand Up @@ -303,6 +308,7 @@ public static TextAttributeProps fromReadableMap(ReactStylesDiffMap props) {
result.setRole(getStringProp(props, ViewProps.ROLE));
result.setGradientColors(getArrayProp(props, "gradientColors"));
result.setGradientAngle(getFloatProp(props, "gradientAngle", Float.NaN));
result.setGradientWidth(getFloatProp(props, "gradientWidth", Float.NaN));
result.setTextStrokeWidth(getFloatProp(props, "textStrokeWidth", Float.NaN));
if (props.hasKey("textStrokeColor")) {
result.setTextStrokeColor(props.getInt("textStrokeColor", 0));
Expand Down Expand Up @@ -824,6 +830,14 @@ private void setGradientAngle(float gradientAngle) {
mGradientAngle = gradientAngle;
}

public float getGradientWidth() {
return mGradientWidth;
}

private void setGradientWidth(float gradientWidth) {
mGradientWidth = gradientWidth;
}

public static int getTextBreakStrategy(@Nullable String textBreakStrategy) {
int androidTextBreakStrategy = DEFAULT_BREAK_STRATEGY;
if (textBreakStrategy != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,11 +259,12 @@ internal object TextLayoutManager {
if (textAttributes.gradientColors != null && textAttributes.gradientColors!!.size >= 2) {
val effectiveFontSize = textAttributes.effectiveFontSize
val gradientAngle = if (textAttributes.gradientAngle.isNaN()) 0f else textAttributes.gradientAngle
val gradientWidth = textAttributes.gradientWidth
ops.add(
SetSpanOperation(
start,
end,
LinearGradientSpan(start * effectiveFontSize.toFloat(), textAttributes.gradientColors!!, gradientAngle)))
LinearGradientSpan(start * effectiveFontSize.toFloat(), textAttributes.gradientColors!!, gradientAngle, gradientWidth)))
}
if (textAttributes.mIsBackgroundColorSet) {
ops.add(
Expand Down Expand Up @@ -418,8 +419,9 @@ internal object TextLayoutManager {
if (fragment.props.gradientColors != null && fragment.props.gradientColors!!.size >= 2) {
val effectiveFontSize = fragment.props.effectiveFontSize
val gradientAngle = if (fragment.props.gradientAngle.isNaN()) 0f else fragment.props.gradientAngle
val gradientWidth = fragment.props.gradientWidth
spannable.setSpan(
LinearGradientSpan(start * effectiveFontSize.toFloat(), fragment.props.gradientColors!!, gradientAngle),
LinearGradientSpan(start * effectiveFontSize.toFloat(), fragment.props.gradientColors!!, gradientAngle, gradientWidth),
start,
end,
spanFlags)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@ import android.text.TextPaint
import android.text.style.CharacterStyle
import android.text.style.UpdateAppearance

/**
* Span that applies a linear gradient to text.
*
* @param start The x-offset for the gradient start position
* @param colors Array of gradient colors
* @param angle Gradient angle in degrees (0 = horizontal)
* @param gradientWidth Width of the gradient pattern in pixels. Default is 100.
*/
public class LinearGradientSpan(
private val start: Float,
private val colors: IntArray,
private val angle: Float = 0f,
private val gradientWidth: Float = Float.NaN,
) : CharacterStyle(), ReactSpan,
UpdateAppearance {
public override fun updateDrawState(tp: TextPaint) {
Expand All @@ -18,7 +27,7 @@ public class LinearGradientSpan(
tp.setColor(colors[0])

val radians = Math.toRadians(angle.toDouble())
val width = 100.0f
val width = if (gradientWidth.isNaN()) 100f else gradientWidth
val height = tp.textSize

val centerX = start + width / 2
Expand Down
Loading