-
Notifications
You must be signed in to change notification settings - Fork 24.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(Android) drawing additional empty line when 'textAlign' is set to 'justify' #47122
Changes from all commits
7a2726c
9b87f7c
4937a7f
9feb447
2288f1c
68cf371
ff5fa9a
232ed3f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -146,17 +146,48 @@ public static boolean isRTL(MapBuffer attributedString) { | |
== LayoutDirection.RTL; | ||
} | ||
|
||
private static Layout.Alignment getTextAlignment(MapBuffer attributedString, Spannable spanned) { | ||
@Nullable | ||
private static String getTextAlignmentAttr(MapBuffer attributedString) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is splitting the alignment methods related to core change, or just for consistency? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's just for consistency and to not retrieve alignment twice from |
||
// TODO: Don't read AS_KEY_FRAGMENTS, which may be expensive, and is not present when using | ||
// cached Spannable | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: looks like we chopped off this part of the comment |
||
if (!attributedString.contains(AS_KEY_FRAGMENTS)) { | ||
return Layout.Alignment.ALIGN_NORMAL; | ||
} | ||
|
||
// Android will align text based on the script, so normal and opposite alignment needs to be | ||
// swapped when the directions of paragraph and script don't match. | ||
// I.e. paragraph is LTR but script is RTL, text needs to be aligned to the left, which means | ||
// ALIGN_OPPOSITE needs to be used to align RTL script to the left | ||
if (!attributedString.contains(AS_KEY_FRAGMENTS)) { | ||
return null; | ||
} | ||
|
||
MapBuffer fragments = attributedString.getMapBuffer(AS_KEY_FRAGMENTS); | ||
if (fragments.getCount() != 0) { | ||
MapBuffer fragment = fragments.getMapBuffer(0); | ||
MapBuffer textAttributes = fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES); | ||
|
||
if (textAttributes.contains(TextAttributeProps.TA_KEY_ALIGNMENT)) { | ||
return textAttributes.getString(TextAttributeProps.TA_KEY_ALIGNMENT); | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
|
||
private static int getTextJustificationMode(@Nullable String alignmentAttr) { | ||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { | ||
return -1; | ||
} | ||
|
||
if (alignmentAttr != null && alignmentAttr.equals("justified")) { | ||
return Layout.JUSTIFICATION_MODE_INTER_WORD; | ||
} | ||
|
||
return Layout.JUSTIFICATION_MODE_NONE; | ||
} | ||
|
||
private static Layout.Alignment getTextAlignment( | ||
MapBuffer attributedString, | ||
Spannable spanned, | ||
@Nullable String alignmentAttr) { | ||
boolean isParagraphRTL = isRTL(attributedString); | ||
boolean isScriptRTL = | ||
TextDirectionHeuristics.FIRSTSTRONG_LTR.isRtl(spanned, 0, spanned.length()); | ||
|
@@ -165,23 +196,17 @@ private static Layout.Alignment getTextAlignment(MapBuffer attributedString, Spa | |
Layout.Alignment alignment = | ||
swapNormalAndOpposite ? Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_NORMAL; | ||
|
||
MapBuffer fragments = attributedString.getMapBuffer(AS_KEY_FRAGMENTS); | ||
if (fragments.getCount() != 0) { | ||
MapBuffer fragment = fragments.getMapBuffer(0); | ||
MapBuffer textAttributes = fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES); | ||
if (alignmentAttr == null) { | ||
return alignment; | ||
} | ||
|
||
if (textAttributes.contains(TextAttributeProps.TA_KEY_ALIGNMENT)) { | ||
String alignmentAttr = textAttributes.getString(TextAttributeProps.TA_KEY_ALIGNMENT); | ||
|
||
if (alignmentAttr.equals("center")) { | ||
alignment = Layout.Alignment.ALIGN_CENTER; | ||
} else if (alignmentAttr.equals("right")) { | ||
alignment = | ||
swapNormalAndOpposite | ||
? Layout.Alignment.ALIGN_NORMAL | ||
: Layout.Alignment.ALIGN_OPPOSITE; | ||
} | ||
} | ||
if (alignmentAttr.equals("center")) { | ||
alignment = Layout.Alignment.ALIGN_CENTER; | ||
} else if (alignmentAttr.equals("right")) { | ||
alignment = | ||
swapNormalAndOpposite | ||
? Layout.Alignment.ALIGN_NORMAL | ||
: Layout.Alignment.ALIGN_OPPOSITE; | ||
} | ||
|
||
return alignment; | ||
|
@@ -190,7 +215,8 @@ private static Layout.Alignment getTextAlignment(MapBuffer attributedString, Spa | |
public static int getTextGravity( | ||
MapBuffer attributedString, Spannable spanned, int defaultValue) { | ||
int gravity = defaultValue; | ||
Layout.Alignment alignment = getTextAlignment(attributedString, spanned); | ||
@Nullable String alignmentAttr = getTextAlignmentAttr(attributedString); | ||
Layout.Alignment alignment = getTextAlignment(attributedString, spanned, alignmentAttr); | ||
|
||
// depending on whether the script is LTR or RTL, ALIGN_NORMAL and ALIGN_OPPOSITE may mean | ||
// different things | ||
|
@@ -363,6 +389,7 @@ private static Layout createLayout( | |
int textBreakStrategy, | ||
int hyphenationFrequency, | ||
Layout.Alignment alignment, | ||
int justificationMode, | ||
TextPaint paint) { | ||
Layout layout; | ||
|
||
|
@@ -420,6 +447,10 @@ private static Layout createLayout( | |
.setTextDirection( | ||
isScriptRTL ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR); | ||
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||
builder.setJustificationMode(justificationMode); | ||
} | ||
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { | ||
builder.setUseLineSpacingFromFallbacks(true); | ||
} | ||
|
@@ -503,7 +534,9 @@ private static Layout createLayout( | |
? paragraphAttributes.getInt(PA_KEY_MAX_NUMBER_OF_LINES) | ||
: ReactConstants.UNSET; | ||
|
||
Layout.Alignment alignment = getTextAlignment(attributedString, text); | ||
@Nullable String alignmentAttr = getTextAlignmentAttr(attributedString); | ||
Layout.Alignment alignment = getTextAlignment(attributedString, text, alignmentAttr); | ||
int justificationMode = getTextJustificationMode(alignmentAttr); | ||
|
||
if (adjustFontSizeToFit) { | ||
double minimumFontSize = | ||
|
@@ -523,6 +556,7 @@ private static Layout createLayout( | |
textBreakStrategy, | ||
hyphenationFrequency, | ||
alignment, | ||
justificationMode, | ||
paint); | ||
} | ||
|
||
|
@@ -535,6 +569,7 @@ private static Layout createLayout( | |
textBreakStrategy, | ||
hyphenationFrequency, | ||
alignment, | ||
justificationMode, | ||
paint); | ||
} | ||
|
||
|
@@ -550,6 +585,7 @@ private static Layout createLayout( | |
int textBreakStrategy, | ||
int hyphenationFrequency, | ||
Layout.Alignment alignment, | ||
int justificationMode, | ||
TextPaint paint) { | ||
BoringLayout.Metrics boring = BoringLayout.isBoring(text, paint); | ||
Layout layout = | ||
|
@@ -562,6 +598,7 @@ private static Layout createLayout( | |
textBreakStrategy, | ||
hyphenationFrequency, | ||
alignment, | ||
justificationMode, | ||
paint); | ||
|
||
// Minimum font size is 4pts to match the iOS implementation. | ||
|
@@ -610,6 +647,7 @@ private static Layout createLayout( | |
textBreakStrategy, | ||
hyphenationFrequency, | ||
alignment, | ||
justificationMode, | ||
paint); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is correct, since justification can change line width.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah we could get current justification mode or pass -1 as well I think.