-
Notifications
You must be signed in to change notification settings - Fork 527
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 #5039 : Align policy text and symbols to the left, partial fix for list items #5573
base: develop
Are you sure you want to change the base?
Changes from 10 commits
f8143d2
fdb0929
b5148d4
220580f
64fca2b
8c521f3
eebbadf
492284e
f54c59c
28c8ea5
957b27a
061e0a7
a6624fa
73e09dc
642f106
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 |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package org.oppia.android.util.locale | ||
|
||
import android.graphics.Canvas | ||
import android.graphics.Paint | ||
import android.text.style.ReplacementSpan | ||
|
||
// Custom span to force LTR alignment for symbols as well | ||
// Custom span to force LTR alignment for all text including symbols | ||
class LeftAlignedSymbolsSpan : ReplacementSpan() { | ||
override fun getSize( | ||
paint: Paint, | ||
text: CharSequence?, | ||
start: Int, | ||
end: Int, | ||
fm: Paint.FontMetricsInt? | ||
): Int { | ||
return paint.measureText(text, start, end).toInt() | ||
} | ||
|
||
override fun draw( | ||
canvas: Canvas, | ||
text: CharSequence, | ||
start: Int, | ||
end: Int, | ||
x: Float, | ||
top: Int, | ||
y: Int, | ||
bottom: Int, | ||
paint: Paint | ||
) { | ||
val originalAlignment = paint.textAlign | ||
paint.textAlign = Paint.Align.LEFT | ||
|
||
// Draw the bullet point at the exact x position | ||
canvas.drawText(text, start, end, x, y.toFloat(), paint) | ||
|
||
// Restore original alignment | ||
paint.textAlign = originalAlignment | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,6 @@ import android.text.style.LeadingMarginSpan | |
import androidx.core.view.ViewCompat | ||
import org.oppia.android.util.R | ||
import org.oppia.android.util.locale.OppiaLocale | ||
import kotlin.math.max | ||
|
||
// TODO(#562): Add screenshot tests to check whether the drawing logic works correctly on all devices. | ||
|
||
|
@@ -39,14 +38,13 @@ sealed class ListItemLeadingMarginSpan : LeadingMarginSpan { | |
) : ListItemLeadingMarginSpan() { | ||
private val resources = context.resources | ||
private val bulletRadius = resources.getDimensionPixelSize(R.dimen.bullet_radius) | ||
private val spacingBeforeText = resources.getDimensionPixelSize(R.dimen.spacing_before_text) | ||
private val spacingBeforeBullet = resources.getDimensionPixelSize(R.dimen.spacing_before_bullet) | ||
|
||
private val bulletDiameter by lazy { bulletRadius * 2 } | ||
private val baseMargin = (16f * context.resources.displayMetrics.density).toInt() | ||
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. Here and below, why are we using a hardcoded value of 16f here? 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. Apologies for the oversight. I’ve replaced the hardcoded value to improve maintainability. For Unordered List (UL):
For Ordered List (OL):
|
||
|
||
private val isRtl by lazy { | ||
displayLocale.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL | ||
} | ||
private val clipBounds by lazy { Rect() } | ||
|
||
override fun drawLeadingMargin( | ||
canvas: Canvas, | ||
|
@@ -69,16 +67,14 @@ sealed class ListItemLeadingMarginSpan : LeadingMarginSpan { | |
val previousStyle = paint.style | ||
val bulletDrawRadius = bulletRadius.toFloat() | ||
|
||
val indentedX = parentAbsoluteLeadingMargin + spacingBeforeBullet | ||
val bulletCenterLtrX = indentedX + bulletDrawRadius | ||
val bulletCenterX = if (isRtl) { | ||
// See https://stackoverflow.com/a/21845993/3689782 for 'right' property exclusivity. | ||
val maxDrawX = if (canvas.getClipBounds(clipBounds)) { | ||
clipBounds.right - 1 | ||
} else canvas.width - 1 | ||
maxDrawX - bulletCenterLtrX | ||
} else bulletCenterLtrX | ||
// Force left alignment | ||
paint.textAlign = Paint.Align.LEFT | ||
|
||
// Positioning calculation | ||
val bulletCenterLtrX = x.toFloat() + baseMargin * (indentationLevel + 1) | ||
val bulletCenterX = bulletCenterLtrX | ||
val bulletCenterY = (top + bottom) / 2f | ||
|
||
when (indentationLevel) { | ||
0 -> { | ||
// A solid circle is used for the outermost bullet. | ||
|
@@ -111,7 +107,7 @@ sealed class ListItemLeadingMarginSpan : LeadingMarginSpan { | |
} | ||
|
||
override fun getLeadingMargin(first: Boolean) = | ||
bulletDiameter + spacingBeforeBullet + spacingBeforeText | ||
baseMargin * (indentationLevel + 2) | ||
} | ||
|
||
/** A subclass of [LeadingMarginSpan] that shows nested list span for <ol> tags. */ | ||
|
@@ -123,17 +119,11 @@ sealed class ListItemLeadingMarginSpan : LeadingMarginSpan { | |
private val displayLocale: OppiaLocale.DisplayLocale | ||
) : ListItemLeadingMarginSpan() { | ||
private val resources = context.resources | ||
private val spacingBeforeText = resources.getDimensionPixelSize(R.dimen.spacing_before_text) | ||
private val spacingBeforeNumberPrefix = | ||
resources.getDimensionPixelSize(R.dimen.spacing_before_number_prefix) | ||
private val baseMargin = (16f * context.resources.displayMetrics.density).toInt() | ||
|
||
// Try to use a computed margin, but otherwise guess if there's no guaranteed spacing. | ||
private var computedLeadingMargin = | ||
2 * longestNumberedItemPrefix.length + spacingBeforeText | ||
|
||
private val isRtl by lazy { | ||
displayLocale.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL | ||
} | ||
2 * longestNumberedItemPrefix.length + baseMargin | ||
|
||
override fun drawLeadingMargin( | ||
canvas: Canvas, | ||
|
@@ -153,30 +143,24 @@ sealed class ListItemLeadingMarginSpan : LeadingMarginSpan { | |
val isFirstCharacter = startCharOfSpan == start | ||
|
||
if (isFirstCharacter) { | ||
// Force left alignment | ||
paint.textAlign = Paint.Align.LEFT | ||
|
||
val textWidth = Rect().also { | ||
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. Looks like this variable is nolonger used in the new code. 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. Thanks @adhiamboperes , I’ve removed this unused variable. |
||
paint.getTextBounds( | ||
numberedItemPrefix, /* start= */ 0, /* end= */ numberedItemPrefix.length, it | ||
) | ||
}.width() | ||
val longestTextWidth = Rect().also { | ||
paint.getTextBounds( | ||
longestNumberedItemPrefix, | ||
/* start= */ 0, | ||
/* end= */ longestNumberedItemPrefix.length, | ||
it | ||
) | ||
}.width() | ||
computedLeadingMargin = longestTextWidth + spacingBeforeNumberPrefix + spacingBeforeText | ||
|
||
// Compute the prefix's start x value such that it is right-aligned with other numbers in | ||
// the list. | ||
val indentedX = parentAbsoluteLeadingMargin + spacingBeforeNumberPrefix | ||
val endAlignedX = (max(textWidth, longestTextWidth) - textWidth) + indentedX | ||
val prefixStartX = if (isRtl) canvas.width - endAlignedX - 1 else endAlignedX | ||
canvas.drawText(numberedItemPrefix, prefixStartX.toFloat(), baseline.toFloat(), paint) | ||
|
||
// Positioning calculation | ||
val prefixStartX = x.toFloat() + baseMargin | ||
|
||
// Draw the numbered prefix | ||
canvas.drawText(numberedItemPrefix, prefixStartX, baseline.toFloat(), paint) | ||
} | ||
} | ||
|
||
override fun getLeadingMargin(first: Boolean) = computedLeadingMargin | ||
override fun getLeadingMargin(first: Boolean) = | ||
baseMargin * 2 | ||
} | ||
} |
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.
This should be a KDOC rather than a comment.
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.
Done.