Skip to content

Commit d25b5c7

Browse files
committed
Merge remote-tracking branch 'google-androidx/androidx-compose-release' into integration-release/compose-ui/1.9
2 parents 3328a6e + b7d3ef4 commit d25b5c7

6 files changed

Lines changed: 220 additions & 4 deletions

File tree

compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import android.text.style.LeadingMarginSpan
2828
import android.text.style.LocaleSpan
2929
import android.text.style.RelativeSizeSpan
3030
import android.text.style.ScaleXSpan
31+
import androidx.compose.foundation.text.appendInlineContent
3132
import androidx.compose.ui.geometry.Offset
3233
import androidx.compose.ui.geometry.Size
3334
import androidx.compose.ui.graphics.BlendMode
@@ -2078,6 +2079,148 @@ class AndroidParagraphTest {
20782079
assertThat(paragraph.textPaint.hinting).isEqualTo(TextPaint.HINTING_OFF)
20792080
}
20802081

2082+
@Test
2083+
fun placeholder_onEllipsizedLastLine_isNotNull() {
2084+
with(defaultDensity) {
2085+
val fontSize = 10.sp
2086+
val text = "Hello World Hello World Hello World"
2087+
val inlineContentId = "inline"
2088+
val annotatedString = buildAnnotatedString {
2089+
append(text)
2090+
appendInlineContent(inlineContentId)
2091+
}
2092+
val placeholder =
2093+
Placeholder(fontSize, fontSize, PlaceholderVerticalAlign.AboveBaseline)
2094+
2095+
val placeholderRange =
2096+
AnnotatedString.Range(placeholder, start = text.length, end = text.length + 1)
2097+
2098+
val paragraph =
2099+
AndroidParagraph(
2100+
text = annotatedString.text,
2101+
style = TextStyle(fontSize = fontSize, fontFamily = basicFontFamily),
2102+
annotations = annotatedString.spanStyles,
2103+
placeholders = listOf(placeholderRange),
2104+
maxLines = 2,
2105+
overflow = TextOverflow.Ellipsis,
2106+
constraints = Constraints(maxWidth = (20 * fontSize.toPx()).roundToInt()),
2107+
fontFamilyResolver = UncachedFontFamilyResolver(context),
2108+
density = defaultDensity,
2109+
)
2110+
2111+
assertThat(paragraph.placeholderRects).hasSize(1)
2112+
assertThat(paragraph.placeholderRects[0]).isNotNull()
2113+
}
2114+
}
2115+
2116+
@Test
2117+
fun placeholder_fullyEllipsizedAway_isNull() {
2118+
with(defaultDensity) {
2119+
val fontSize = 10.sp
2120+
val text = "Hello World Hello World Hello World"
2121+
val inlineContentId = "inline"
2122+
val annotatedString = buildAnnotatedString {
2123+
append(text)
2124+
appendInlineContent(inlineContentId)
2125+
}
2126+
val placeholder =
2127+
Placeholder(fontSize, fontSize, PlaceholderVerticalAlign.AboveBaseline)
2128+
2129+
val placeholderRange =
2130+
AnnotatedString.Range(placeholder, start = text.length, end = text.length + 1)
2131+
2132+
val paragraph =
2133+
AndroidParagraph(
2134+
text = annotatedString.text,
2135+
style = TextStyle(fontSize = fontSize, fontFamily = basicFontFamily),
2136+
annotations = annotatedString.spanStyles,
2137+
placeholders = listOf(placeholderRange),
2138+
maxLines = 2,
2139+
overflow = TextOverflow.Ellipsis,
2140+
constraints = Constraints(maxWidth = (15 * fontSize.toPx()).roundToInt()),
2141+
fontFamilyResolver = UncachedFontFamilyResolver(context),
2142+
density = defaultDensity,
2143+
)
2144+
2145+
assertThat(paragraph.placeholderRects).hasSize(1)
2146+
assertThat(paragraph.placeholderRects[0]).isNull()
2147+
}
2148+
}
2149+
2150+
@Test
2151+
fun placeholder_onLastLine_withOverflowClip_isNotNull() {
2152+
with(defaultDensity) {
2153+
val fontSize = 10.sp
2154+
val text = "Hello World Hello World Hello World"
2155+
val inlineContentId = "inline"
2156+
val annotatedString = buildAnnotatedString {
2157+
append(text)
2158+
appendInlineContent(inlineContentId)
2159+
}
2160+
val placeholder =
2161+
Placeholder(fontSize, fontSize, PlaceholderVerticalAlign.AboveBaseline)
2162+
2163+
val placeholderRange =
2164+
AnnotatedString.Range(placeholder, start = text.length, end = text.length + 1)
2165+
2166+
val paragraph =
2167+
AndroidParagraph(
2168+
text = annotatedString.text,
2169+
style = TextStyle(fontSize = fontSize, fontFamily = basicFontFamily),
2170+
annotations = annotatedString.spanStyles,
2171+
placeholders = listOf(placeholderRange),
2172+
maxLines = 2,
2173+
overflow = TextOverflow.Clip,
2174+
constraints = Constraints(maxWidth = (20 * fontSize.toPx()).roundToInt()),
2175+
fontFamilyResolver = UncachedFontFamilyResolver(context),
2176+
density = defaultDensity,
2177+
)
2178+
2179+
assertThat(paragraph.placeholderRects).hasSize(1)
2180+
assertThat(paragraph.placeholderRects[0]).isNotNull()
2181+
}
2182+
}
2183+
2184+
@Test
2185+
fun placeholder_onNonEllipsizedLine_isNotNull() {
2186+
with(defaultDensity) {
2187+
val fontSize = 10.sp
2188+
val textBefore = "Hello "
2189+
val textAfter = " World Hello World Hello World"
2190+
val inlineContentId = "inline"
2191+
val annotatedString = buildAnnotatedString {
2192+
append(textBefore)
2193+
appendInlineContent(inlineContentId)
2194+
append(textAfter)
2195+
}
2196+
val placeholder =
2197+
Placeholder(fontSize, fontSize, PlaceholderVerticalAlign.AboveBaseline)
2198+
2199+
val placeholderRange =
2200+
AnnotatedString.Range(
2201+
placeholder,
2202+
start = textBefore.length,
2203+
end = textBefore.length + 1,
2204+
)
2205+
2206+
val paragraph =
2207+
AndroidParagraph(
2208+
text = annotatedString.text,
2209+
style = TextStyle(fontSize = fontSize, fontFamily = basicFontFamily),
2210+
annotations = annotatedString.spanStyles,
2211+
placeholders = listOf(placeholderRange),
2212+
maxLines = 2,
2213+
overflow = TextOverflow.Ellipsis,
2214+
constraints = Constraints(maxWidth = (15 * fontSize.toPx()).roundToInt()),
2215+
fontFamilyResolver = UncachedFontFamilyResolver(context),
2216+
density = defaultDensity,
2217+
)
2218+
2219+
assertThat(paragraph.placeholderRects).hasSize(1)
2220+
assertThat(paragraph.placeholderRects[0]).isNotNull()
2221+
}
2222+
}
2223+
20812224
private fun simpleParagraph(
20822225
text: String = "",
20832226
spanStyles: List<AnnotatedString.Range<SpanStyle>> = listOf(),

compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ internal class AndroidParagraph(
305305
val exceedsMaxLines = line >= maxLines
306306
val isPlaceholderSpanEllipsized =
307307
layout.getLineEllipsisCount(line) > 0 &&
308-
end > layout.getLineEllipsisOffset(line)
308+
end > (layout.getLineStart(line) + layout.getLineEllipsisOffset(line))
309309
val isPlaceholderSpanTruncated = end > layout.getLineEnd(line)
310310
// This Placeholder is ellipsized or truncated, return null instead.
311311
if (isPlaceholderSpanEllipsized || isPlaceholderSpanTruncated || exceedsMaxLines) {

compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/OnGlobalRectChangedTest.kt

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,76 @@ class OnGlobalRectChangedTest {
389389
assertEquals(IntOffset(10, 0), childGlobalPosition)
390390
}
391391

392+
@Test
393+
fun callbackCalledForChildWhenParentMoved_defaultDebounce() {
394+
var position by mutableStateOf(0)
395+
var childGlobalPosition = IntOffset(0, 0)
396+
var latch = CountDownLatch(1)
397+
rule.setContent {
398+
Layout(
399+
measurePolicy = { measurables, constraints ->
400+
layout(10, 10) { measurables[0].measure(constraints).place(position, 0) }
401+
},
402+
content = {
403+
Wrap(minWidth = 10, minHeight = 10) {
404+
Wrap(
405+
minWidth = 10,
406+
minHeight = 10,
407+
modifier =
408+
Modifier.onLayoutRectChanged { rect ->
409+
childGlobalPosition = rect.boundsInRoot.offset()
410+
latch.countDown()
411+
},
412+
)
413+
}
414+
},
415+
)
416+
}
417+
418+
assertTrue(latch.await(1, TimeUnit.SECONDS))
419+
420+
latch = CountDownLatch(1)
421+
rule.runOnUiThread { position = 10 }
422+
423+
assertTrue(latch.await(1, TimeUnit.SECONDS))
424+
assertEquals(IntOffset(10, 0), childGlobalPosition)
425+
}
426+
427+
@Test
428+
fun callbackCalledForChildWhenParentMoved_largerDebounce() {
429+
var position by mutableStateOf(0)
430+
var childGlobalPosition = IntOffset(0, 0)
431+
var latch = CountDownLatch(1)
432+
rule.setContent {
433+
Layout(
434+
measurePolicy = { measurables, constraints ->
435+
layout(10, 10) { measurables[0].measure(constraints).place(position, 0) }
436+
},
437+
content = {
438+
Wrap(minWidth = 10, minHeight = 10) {
439+
Wrap(
440+
minWidth = 10,
441+
minHeight = 10,
442+
modifier =
443+
Modifier.onLayoutRectChanged(debounceMillis = 150) { rect ->
444+
childGlobalPosition = rect.boundsInRoot.offset()
445+
latch.countDown()
446+
},
447+
)
448+
}
449+
},
450+
)
451+
}
452+
453+
assertTrue(latch.await(1, TimeUnit.SECONDS))
454+
455+
latch = CountDownLatch(1)
456+
rule.runOnUiThread { position = 10 }
457+
458+
assertTrue(latch.await(1, TimeUnit.SECONDS))
459+
assertEquals(IntOffset(10, 0), childGlobalPosition)
460+
}
461+
392462
@Test
393463
fun callbackCalledForChildWhenParentMoved_1000children() {
394464
var position by mutableStateOf(0)

compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/spatial/RectManager.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ internal class RectManager(
124124
// this gets called frequently, but we might need to schedule it more often to ensure that
125125
// debounced callbacks get fired
126126
throttledCallbacks.triggerDebounced(currentTime)
127+
if (throttledCallbacks.minDebounceDeadline > 0) {
128+
scheduleDebounceCallback(ensureSomethingScheduled = true)
129+
}
127130
}
128131

129132
fun scheduleDebounceCallback(ensureSomethingScheduled: Boolean) {

compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/spatial/ThrottledCallbacks.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ internal class ThrottledCallbacks {
367367
): Long {
368368
var newMinDeadline = minDeadline
369369
if (entry.debounceMillis > 0 && entry.lastUninvokedFireMillis > 0) {
370-
if (currentMillis - entry.lastUninvokedFireMillis > entry.debounceMillis) {
370+
if (currentMillis - entry.lastUninvokedFireMillis >= entry.debounceMillis) {
371371
entry.lastInvokeMillis = currentMillis
372372
entry.lastUninvokedFireMillis = -1
373373
val topLeft = entry.topLeft

libraryversions.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ CAMERA_TESTING = "1.0.0-alpha01"
2323
CARDVIEW = "1.1.0-alpha01"
2424
CAR_APP = "1.8.0-alpha02"
2525
COLLECTION = "1.5.0"
26-
COMPOSE = "1.9.3"
26+
COMPOSE = "1.9.4"
2727
COMPOSE_MATERIAL3 = "1.4.0-alpha17"
2828
COMPOSE_MATERIAL3_ADAPTIVE = "1.2.0-alpha08"
2929
COMPOSE_MATERIAL3_ADAPTIVE_NAV3 = "1.0.0-alpha01"
3030
COMPOSE_MATERIAL3_COMMON = "1.0.0-alpha01"
3131
COMPOSE_MATERIAL3_XR = "1.0.0-alpha09"
32-
COMPOSE_RUNTIME = "1.9.3"
32+
COMPOSE_RUNTIME = "1.9.4"
3333
CONSTRAINTLAYOUT = "2.3.0-alpha01"
3434
CONSTRAINTLAYOUT_COMPOSE = "1.2.0-alpha01"
3535
CONSTRAINTLAYOUT_CORE = "1.2.0-alpha01"

0 commit comments

Comments
 (0)