JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
15.95k stars 1.16k forks source link

Crash when we use SelectionContainer in SelectionContainer #2326

Open igordmn opened 2 years ago

igordmn commented 2 years ago

1.2.0-alpha01-dev774

import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Text
import androidx.compose.ui.window.singleWindowApplication

fun main() = singleWindowApplication {
    SelectionContainer {
        SelectionContainer {
            Text("Text")
        }
    }
}

Crash when we click on Copy:

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:995)
    at androidx.compose.ui.node.LayoutNodeWrapper.localPositionOf-R5De75A(LayoutNodeWrapper.kt:694)
    at androidx.compose.foundation.text.selection.SelectionManager.convertToContainerCoordinates-Q7Q5hAU(SelectionManager.kt:639)
    at androidx.compose.foundation.text.selection.SelectionManager.access$convertToContainerCoordinates-Q7Q5hAU(SelectionManager.kt:62)
    at androidx.compose.foundation.text.selection.SelectionManager$2.invoke-d-4ec7I(SelectionManager.kt:219)
    at androidx.compose.foundation.text.selection.SelectionManager$2.invoke(SelectionManager.kt:217)
    at androidx.compose.foundation.text.selection.SelectionRegistrarImpl.notifySelectionUpdateStart-d-4ec7I(SelectionRegistrarImpl.kt:175)
    at androidx.compose.foundation.text.TextController$update$mouseSelectionObserver$1.onStart-3MmeM6k(CoreText.kt:248)
    at androidx.compose.foundation.text.selection.TextSelectionMouseDetectorKt$mouseSelectionDetector$2$1.invokeSuspend(TextSelectionMouseDetector.kt:110)
    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:569)
    at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter.dispatchPointerEvent(SuspendingPointerInputFilter.kt:459)
    at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter.onPointerEvent-H0pRuoY(SuspendingPointerInputFilter.kt:472)
    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.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:97)
    at androidx.compose.ui.platform.SkiaBasedOwner.processPointerInput-gBdvCQM$ui(SkiaBasedOwner.skiko.kt:357)
    at androidx.compose.ui.platform.SkiaBasedOwner.processPointerInput-gBdvCQM$ui$default(SkiaBasedOwner.skiko.kt:350)
    at androidx.compose.ui.ComposeScene.processPress(ComposeScene.skiko.kt:515)
    at androidx.compose.ui.ComposeScene.processPointerInput(ComposeScene.skiko.kt:497)
    at androidx.compose.ui.ComposeScene.sendPointerEvent-BGSDPeU(ComposeScene.skiko.kt:482)
    at androidx.compose.ui.ComposeScene.sendPointerEvent-BGSDPeU$default(ComposeScene.skiko.kt:451)
    at androidx.compose.ui.awt.ComposeLayer_desktopKt.onMouseEvent(ComposeLayer.desktop.kt:454)
    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:382)
    at androidx.compose.ui.awt.ComposeLayer$onMouseEvent$1.invoke(ComposeLayer.desktop.kt:375)
    at androidx.compose.ui.awt.ComposeLayer.catchExceptions(ComposeLayer.desktop.kt:106)
    at androidx.compose.ui.awt.ComposeLayer.onMouseEvent(ComposeLayer.desktop.kt:375)
    at androidx.compose.ui.awt.ComposeLayer.access$onMouseEvent(ComposeLayer.desktop.kt:84)
    at androidx.compose.ui.awt.ComposeLayer$4.mousePressed(ComposeLayer.desktop.kt:355)
    at java.desktop/java.awt.Component.processMouseEvent(Component.java:6623)
    at java.desktop/java.awt.Component.processEvent(Component.java:6391)
    at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5001)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:722)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:716)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:97)
    at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:746)
    at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:744)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:743)
    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)
    Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.ui.awt.ComposeLayer$coroutineExceptionHandler$1@55b308f9, androidx.compose.runtime.BroadcastFrameClock@8681747, StandaloneCoroutine{Cancelling}@4c82f1f7, FlushCoroutineDispatcher@2b71afda]

See also https://github.com/JetBrains/compose-jb/issues/979

hakanai commented 1 year ago

I saw the same thing when I tried to put ContextMenuArea inside SelectionContainer. But putting SelectionContainer inside ContextMenuArea seems to work fine.

JetpackDuba commented 1 year ago

@igordmn as this issue persisted, I've been investigating more about it. The problem seems to be because the context menu contains text "outside" the scope of the selected text. If you click on the part of the context menu that is on top of the selected text, it won't crash.

I have my own implementation of the context menu due to other issues with the default one, but I had the exact same issue. I've solved it by adding a DisableSelection around the popup that contains the context menu, perhaps something similar could be done for the Compose Desktop implementation.

Example: https://github.com/JetpackDuba/Gitnuro/blob/07bb331daf05da4769c7bfe5af4312dfa91c2b37/src/main/kotlin/com/jetpackduba/gitnuro/ui/context_menu/ContextMenu.kt#L68-L108

okushnikov commented 3 weeks ago

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.