mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 15:29:15 +01:00
ui: show three lines of the collapsed artist bio
This commit is contained in:
@@ -584,7 +584,7 @@ private fun ArtistBio(
|
|||||||
remember(text, fullWidth, reserve, reserveLines, collapsed, expanded, moreText, lessText) {
|
remember(text, fullWidth, reserve, reserveLines, collapsed, expanded, moreText, lessText) {
|
||||||
when {
|
when {
|
||||||
collapsed -> {
|
collapsed -> {
|
||||||
splitBioCollapsed(measurer, text, style, fullWidth, reserve, moreText)
|
splitBioCollapsed(measurer, text, style, fullWidth, reserve, moreText, maxLines = 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
expanded -> {
|
expanded -> {
|
||||||
@@ -772,7 +772,7 @@ internal data class BioLines(
|
|||||||
val less: Boolean = false,
|
val less: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
/** Collapses text to two lines, appending a bold More only when it does not fit. */
|
/** Collapses text to [maxLines] lines, appending a bold More only when it does not fit. */
|
||||||
internal fun splitBioCollapsed(
|
internal fun splitBioCollapsed(
|
||||||
measurer: TextMeasurer,
|
measurer: TextMeasurer,
|
||||||
text: String,
|
text: String,
|
||||||
@@ -780,6 +780,7 @@ internal fun splitBioCollapsed(
|
|||||||
fullWidth: Int,
|
fullWidth: Int,
|
||||||
reserve: Int,
|
reserve: Int,
|
||||||
moreText: String,
|
moreText: String,
|
||||||
|
maxLines: Int = 2,
|
||||||
): BioLines {
|
): BioLines {
|
||||||
val words =
|
val words =
|
||||||
text
|
text
|
||||||
@@ -789,26 +790,33 @@ internal fun splitBioCollapsed(
|
|||||||
.filter { it.isNotEmpty() }
|
.filter { it.isNotEmpty() }
|
||||||
if (words.isEmpty()) return BioLines(listOf(AnnotatedString("")), more = false)
|
if (words.isEmpty()) return BioLines(listOf(AnnotatedString("")), more = false)
|
||||||
val available = (fullWidth - reserve).coerceAtLeast(1)
|
val available = (fullWidth - reserve).coerceAtLeast(1)
|
||||||
var first = ""
|
val lines = mutableListOf<AnnotatedString>()
|
||||||
var index = 0
|
var index = 0
|
||||||
|
while (index < words.size && lines.size < maxLines - 1) {
|
||||||
|
var current = ""
|
||||||
while (index < words.size) {
|
while (index < words.size) {
|
||||||
val candidate = if (first.isEmpty()) words[index] else "$first ${words[index]}"
|
val candidate = if (current.isEmpty()) words[index] else "$current ${words[index]}"
|
||||||
if (measurer.measure(AnnotatedString(candidate), style, maxLines = 1).size.width <= available) {
|
if (measurer.measure(AnnotatedString(candidate), style, maxLines = 1).size.width <= available) {
|
||||||
first = candidate
|
current = candidate
|
||||||
index++
|
index++
|
||||||
} else if (first.isEmpty()) {
|
} else if (current.isEmpty()) {
|
||||||
first = words[index]
|
current = words[index]
|
||||||
index++
|
index++
|
||||||
|
break
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (index >= words.size) return BioLines(listOf(AnnotatedString(first)), more = false)
|
lines += AnnotatedString(current)
|
||||||
|
}
|
||||||
|
if (index >= words.size) {
|
||||||
|
return BioLines(lines, more = false)
|
||||||
|
}
|
||||||
|
|
||||||
val secondStart = index
|
val remaining = words.drop(index).joinToString(" ")
|
||||||
val remaining = words.drop(secondStart).joinToString(" ")
|
|
||||||
if (measurer.measure(AnnotatedString(remaining), style, maxLines = 1).size.width <= available) {
|
if (measurer.measure(AnnotatedString(remaining), style, maxLines = 1).size.width <= available) {
|
||||||
return BioLines(listOf(AnnotatedString(first), AnnotatedString(remaining)), more = false)
|
lines += AnnotatedString(remaining)
|
||||||
|
return BioLines(lines, more = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val more =
|
val more =
|
||||||
@@ -819,25 +827,25 @@ internal fun splitBioCollapsed(
|
|||||||
val moreWidth =
|
val moreWidth =
|
||||||
measurer.measure(more, style, maxLines = 1).size.width +
|
measurer.measure(more, style, maxLines = 1).size.width +
|
||||||
measurer.measure(AnnotatedString(" "), style, maxLines = 1).size.width
|
measurer.measure(AnnotatedString(" "), style, maxLines = 1).size.width
|
||||||
val secondAvailable = (available - moreWidth).coerceAtLeast(1)
|
val lastAvailable = (available - moreWidth).coerceAtLeast(1)
|
||||||
var truncated = ""
|
var truncated = ""
|
||||||
var truncatedIndex = secondStart
|
while (index < words.size) {
|
||||||
while (truncatedIndex < words.size) {
|
val candidate = if (truncated.isEmpty()) words[index] else "$truncated ${words[index]}"
|
||||||
val candidate = if (truncated.isEmpty()) words[truncatedIndex] else "$truncated ${words[truncatedIndex]}"
|
if (measurer.measure(AnnotatedString(candidate), style, maxLines = 1).size.width <= lastAvailable) {
|
||||||
if (measurer.measure(AnnotatedString(candidate), style, maxLines = 1).size.width <= secondAvailable) {
|
|
||||||
truncated = candidate
|
truncated = candidate
|
||||||
truncatedIndex++
|
index++
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val secondLine =
|
val lastLine =
|
||||||
buildAnnotatedString {
|
buildAnnotatedString {
|
||||||
if (truncated.isNotEmpty()) append("$truncated ")
|
if (truncated.isNotEmpty()) append("$truncated ")
|
||||||
append("… ")
|
append("… ")
|
||||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { append(moreText) }
|
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { append(moreText) }
|
||||||
}
|
}
|
||||||
return BioLines(listOf(AnnotatedString(first), secondLine), more = true)
|
lines += lastLine
|
||||||
|
return BioLines(lines, more = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -257,6 +257,36 @@ class ArtistDetailScreenTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun theCollapsedPreviewCanShowThreeLines() {
|
||||||
|
val bio = List(6) { "This is a sufficiently long biography sentence." }.joinToString(" ")
|
||||||
|
var result: BioLines? = null
|
||||||
|
composeRule.setContent {
|
||||||
|
val measurer = rememberTextMeasurer()
|
||||||
|
result =
|
||||||
|
splitBioCollapsed(
|
||||||
|
measurer,
|
||||||
|
bio,
|
||||||
|
TextStyle(fontSize = 14.sp),
|
||||||
|
400,
|
||||||
|
0,
|
||||||
|
"More",
|
||||||
|
maxLines = 3,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composeRule.waitForIdle()
|
||||||
|
|
||||||
|
val lines = requireNotNull(result)
|
||||||
|
assertEquals(3, lines.lines.size)
|
||||||
|
assertTrue(lines.more)
|
||||||
|
assertTrue(
|
||||||
|
lines.lines
|
||||||
|
.last()
|
||||||
|
.text
|
||||||
|
.endsWith("More"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun anOversizedFirstWordIsStillShown() {
|
fun anOversizedFirstWordIsStillShown() {
|
||||||
var result: BioLines? = null
|
var result: BioLines? = null
|
||||||
|
|||||||
Reference in New Issue
Block a user