Skip to content
Merged
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 @@ -33,6 +33,11 @@ import androidx.compose.foundation.text.Handle
import androidx.compose.foundation.text.test.assertThatIntRect
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.movableContentOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
Expand Down Expand Up @@ -573,6 +578,47 @@ internal class SelectionContainerTest : AbstractSelectionContainerTest() {
rule.runOnIdle { assertThat(clickCounter).isEqualTo(1) }
}

@Test
fun selectionContainer_insideDisappearingMovableContent_withActiveSelection() =
with(rule.density) {
var toggle by mutableStateOf(true)
rule.setContent {
val content = remember {
movableContentOf {
SelectionContainer(
modifier = Modifier.testTag("selectionContainer"),
selection = selection.value,
onSelectionChange = { selection.value = it },
) {
TestText(textContent)
}
}
}
if (toggle) {
content()
} else {
TestText("Hello")
}
}
val characterSize = fontSize.toPx()
// Act. Long Press "m" in "Demo", and "Demo" should be selected.
rule.onSelectionContainer().performTouchInput {
longClick(Offset(textContent.indexOf('m') * characterSize, 0.5f * characterSize))
}

// Assert.
rule.mainClock.advanceTimeByFrame()
assertAnchorInfo(selection.value?.start, offset = 5, selectableId = 1)
assertAnchorInfo(selection.value?.end, offset = 9, selectableId = 1)

// Act 2. Remove movableContentOf from composition
toggle = false
rule.mainClock.advanceTimeByFrame()

// Assert. No crash is enough
assertThat(selection.value).isNull()
}

private fun startSelection(tag: String, offset: Int = 0) {
val textLayoutResult = rule.onNodeWithTag(tag).fetchTextLayoutResult()
val boundingBox = textLayoutResult.getBoundingBox(offset)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,13 @@ internal class AndroidTextContextMenuToolbarProvider(
observeReadsAndGet("dataBuilder", onDataChange) { dataProvider.data() }

private fun observeAndGetBounds(dataProvider: TextContextMenuDataProvider): Rect =
observeReadsAndGet("positioner", onPositionChange) { calculateBoundsInRoot(dataProvider) }

private fun calculateBoundsInRoot(dataProvider: TextContextMenuDataProvider): Rect {
val destinationCoordinates = coordinatesProvider()
val localBoundingBox = dataProvider.contentBounds(destinationCoordinates)
return localBoundingBox.translate(destinationCoordinates.positionInRoot())
}
observeReadsAndGet("positioner", onPositionChange) {
val destinationCoordinates =
coordinatesProvider().takeIf { it.isAttached }
?: return@observeReadsAndGet Rect.Zero
val localBoundingBox = dataProvider.contentBounds(destinationCoordinates)
localBoundingBox.translate(destinationCoordinates.positionInRoot())
}

/**
* Same functionality as [SnapshotStateObserver.observeReads] except this function returns the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,13 +219,16 @@ internal class TextContextMenuToolbarHandlerNode(
override fun position(destinationCoordinates: LayoutCoordinates): Offset =
contentBounds(destinationCoordinates).topLeft

// This can update as the modifier is getting disposed,
// so return zero if we aren't attached to avoid crashing.
/**
* This can update as the modifier is getting disposed, so return zero if we aren't attached to
* avoid crashing. However the caller is responsible for providing a valid and attached
* [destinationCoordinates].
*/
override fun contentBounds(destinationCoordinates: LayoutCoordinates): Rect {
if (!isAttached) return previousContentBounds

val computedContentBounds = computeContentBounds(destinationCoordinates)
if (computedContentBounds == null) return previousContentBounds
val computedContentBounds =
computeContentBounds(destinationCoordinates) ?: return previousContentBounds

previousContentBounds = computedContentBounds
return computedContentBounds
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ jetbrains.compose.compiler.version=1.5.14.1
# artifactRedirection.version.<redirectionGroup>.<redirectionModule>
# covers all sub-module projects
# (artifactRedirection.version.androidx.compose covers androidx.compose.*:*)
artifactRedirection.version.androidx.compose=1.10.1
artifactRedirection.version.androidx.compose=1.10.2
artifactRedirection.version.androidx.compose.material3=1.5.0-alpha08
artifactRedirection.version.androidx.compose.material3.adaptive=1.3.0-alpha03
artifactRedirection.version.androidx.compose.material3.common=1.0.0-alpha01
Expand Down
2 changes: 1 addition & 1 deletion libraryversions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ CAMERA_VIEWFINDER = "1.4.0-alpha08"
CARDVIEW = "1.1.0-alpha01"
CAR_APP = "1.7.0-beta02"
COLLECTION = "1.5.0-alpha03"
COMPOSE = "1.10.1"
COMPOSE = "1.10.2"
COMPOSE_MATERIAL3 = "1.5.0-alpha08"
COMPOSE_MATERIAL3_ADAPTIVE = "1.3.0-alpha03"
COMPOSE_RUNTIME_TRACING = "1.0.0-beta01"
Expand Down