compose-multiplatform
compose-multiplatform copied to clipboard
TooltipArea in SelectionContainer crash
On a Desktop application on MacOS using
plugins {
kotlin("jvm") version "1.6.10"
id("org.jetbrains.compose") version "1.1.0"
}
Steps to reproduce
- Run code
- Hover over text
- Wait for tooltip
- Click in the tooltip bounds
- App crashes because it tries to select the text in the tooltip
Code to reproduce
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.TooltipArea
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Info
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
@Composable
@Preview
fun App() {
MaterialTheme {
Column(
modifier = Modifier
.padding(32.dp)
) {
SelectionContainer {
@OptIn(ExperimentalFoundationApi::class)
TooltipArea(
tooltip = {
Card(
// Flip colors for high contrast on other elements
backgroundColor = MaterialTheme.colors.onSurface,
contentColor = MaterialTheme.colors.surface,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(8.dp)
) {
Text("Click on ")
Icon(Icons.Default.Info, contentDescription = "Info Icon")
Text(" to inspect")
}
}
},
content = {
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
},
)
}
}
}
}
fun main() = application {
Window(onCloseRequest = ::exitApplication) {
App()
}
}
Stacktrace
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: layouts are not part of the same hierarchy
at androidx.compose.ui.node.LayoutNodeWrapper.findCommonAncestor$ui(LayoutNodeWrapper.kt:879)
at androidx.compose.ui.node.LayoutNodeWrapper.localPositionOf-R5De75A(LayoutNodeWrapper.kt:437)
at androidx.compose.foundation.text.selection.SelectionManager.convertToContainerCoordinates-Q7Q5hAU(SelectionManager.kt:592)
at androidx.compose.foundation.text.selection.SelectionManager.access$convertToContainerCoordinates-Q7Q5hAU(SelectionManager.kt:58)
at androidx.compose.foundation.text.selection.SelectionManager$2.invoke-d-4ec7I(SelectionManager.kt:196)
at androidx.compose.foundation.text.selection.SelectionManager$2.invoke(SelectionManager.kt:194)
at androidx.compose.foundation.text.selection.SelectionRegistrarImpl.notifySelectionUpdateStart-d-4ec7I(SelectionRegistrarImpl.kt:177)
at androidx.compose.foundation.text.TextController$update$mouseSelectionObserver$1.onStart-3MmeM6k(CoreText.kt:239)
at androidx.compose.foundation.text.selection.TextSelectionMouseDetectorKt$mouseSelectionDetector$2$1.invokeSuspend(TextSelectionMouseDetector.kt:112)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:178)
at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:328)
at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter$PointerEventHandlerCoroutine.offerPointerEvent(SuspendingPointerInputFilter.kt:529)
at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter.dispatchPointerEvent(SuspendingPointerInputFilter.kt:422)
at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter.onPointerEvent-H0pRuoY(SuspendingPointerInputFilter.kt:435)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:285)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:272)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:272)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:272)
at androidx.compose.ui.input.pointer.NodeParent.dispatchMainEventPass(HitPathTracker.kt:152)
at androidx.compose.ui.input.pointer.HitPathTracker.dispatchChanges(HitPathTracker.kt:89)
at androidx.compose.ui.input.pointer.PointerInputEventProcessor.process-BIzXfog(PointerInputEventProcessor.kt:80)
at androidx.compose.ui.platform.SkiaBasedOwner.processPointerInput-gBdvCQM$ui(SkiaBasedOwner.skiko.kt:321)
at androidx.compose.ui.platform.SkiaBasedOwner.processPointerInput-gBdvCQM$ui$default(SkiaBasedOwner.skiko.kt:320)
at androidx.compose.ui.ComposeScene.onMousePressed(ComposeScene.skiko.kt:481)
at androidx.compose.ui.ComposeScene.processPointerInput(ComposeScene.skiko.kt:463)
at androidx.compose.ui.ComposeScene.sendPointerEvent-Kr8mkKM(ComposeScene.skiko.kt:451)
at androidx.compose.ui.ComposeScene.sendPointerEvent-Kr8mkKM$default(ComposeScene.skiko.kt:421)
at androidx.compose.ui.awt.ComposeLayer_desktopKt.onMouseEvent(ComposeLayer.desktop.kt:357)
at androidx.compose.ui.awt.ComposeLayer_desktopKt.access$onMouseEvent(ComposeLayer.desktop.kt:1)
at androidx.compose.ui.awt.ComposeLayer$onMouseEvent$1.invoke(ComposeLayer.desktop.kt:285)
at androidx.compose.ui.awt.ComposeLayer$onMouseEvent$1.invoke(ComposeLayer.desktop.kt:282)
at androidx.compose.ui.awt.ComposeLayer.catchExceptions(ComposeLayer.desktop.kt:89)
at androidx.compose.ui.awt.ComposeLayer.onMouseEvent(ComposeLayer.desktop.kt:282)
at androidx.compose.ui.awt.ComposeLayer.access$onMouseEvent(ComposeLayer.desktop.kt:70)
at androidx.compose.ui.awt.ComposeLayer$3.mousePressed(ComposeLayer.desktop.kt:262)
at java.desktop/java.awt.Component.processMouseEvent(Component.java:6632)
at java.desktop/java.awt.Component.processEvent(Component.java:6400)
at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5011)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4843)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:772)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:743)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
For those looking for a workaround, you'll have to wrap your tooltip content or offending content with DisableSelection.
Ok ran into this issue again, this time with context menus.
If you have a context menu inside a SelectionContainer then you can't click on the menu items without it crashing…
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.ContextMenuArea
import androidx.compose.foundation.ContextMenuItem
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
@OptIn(ExperimentalFoundationApi::class)
@Composable
@Preview
fun App() {
MaterialTheme {
Column(
modifier = Modifier
.padding(32.dp)
) {
SelectionContainer {
ContextMenuArea(
items = {
listOf(
ContextMenuItem(
"Click me",
onClick = {
println("Hello World")
},
)
)
},
) {
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
}
}
}
}
}
fun main() = application {
Window(onCloseRequest = ::exitApplication) {
App()
}
}
Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.