Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix crash when we resize ComposePanel after re-adding it to the hierarchy #1195

Merged
merged 1 commit into from
Mar 14, 2024
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 @@ -155,6 +155,7 @@ internal class ComposeContainer(
}

fun dispose() {
_windowContainer?.removeComponentListener(this)
igordmn marked this conversation as resolved.
Show resolved Hide resolved
mediator.dispose()
layers.fastForEach(DesktopComposeSceneLayer::close)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,17 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.onPointerEvent
import androidx.compose.ui.layout.layout
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.sendMouseEvent
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize
import androidx.compose.ui.util.ThrowUncaughtExceptionRule
import androidx.compose.ui.window.density
import androidx.compose.ui.window.runApplicationTest
import com.google.common.truth.Truth.assertThat
Expand All @@ -54,9 +59,13 @@ import org.jetbrains.skiko.MainUIDispatcher
import org.jetbrains.skiko.OS
import org.jetbrains.skiko.SkiaLayerAnalytics
import org.junit.Assume.assumeFalse
import org.junit.Rule
import org.junit.Test

class ComposePanelTest {
@get:Rule
val throwUncaughtExceptionRule = ThrowUncaughtExceptionRule()

@Test
fun `don't override user preferred size`() {
assumeFalse(GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance)
Expand Down Expand Up @@ -270,6 +279,47 @@ class ComposePanelTest {
}
}

// https://github.com/JetBrains/compose-multiplatform/issues/4479
@Test
fun `add, removing, add, set size`() {
assumeFalse(GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance)

runBlocking(MainUIDispatcher) {
var size = Size.Zero
val composePanel = ComposePanel()
composePanel.setContent {
Box(Modifier.fillMaxSize().onGloballyPositioned {
size = it.size.toSize()
})
}

val frame = JFrame()
frame.isUndecorated = true
frame.size = Dimension(100, 100)
try {
val density = frame.contentPane.density.density
frame.contentPane.add(composePanel)
frame.isVisible = true
delay(1000)
assertEquals(Size(100f * density, 100f * density), size)

frame.contentPane.remove(composePanel)
delay(1000)
assertEquals(Size(100f * density, 100f * density), size)

frame.contentPane.add(composePanel)
delay(1000)
assertEquals(Size(100f * density, 100f * density), size)

frame.size = Dimension(200, 100)
delay(1000)
assertEquals(Size(200f * density, 100f * density), size)
} finally {
frame.dispose()
}
}
}

@Test
fun `initial panel size with border layout`() {
assumeFalse(GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package androidx.compose.ui.util

import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement

/**
* A rule that throw an exception in the end of test if there were any uncaught exceptions.
*
* It is needed in cases, where exceptions are thrown outside testing thread.
*
* For example, AWT Event Thread can fire event independently,
* and its handler can throw an exception without failing the test.
*
* Usage:
* @get:Rule
* val throwUncaughtExceptionRule = ThrowUncaughtExceptionRule()
*/
class ThrowUncaughtExceptionRule : TestRule {
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
@Throws(Throwable::class)
override fun evaluate() {
val oldHandler = Thread.getDefaultUncaughtExceptionHandler()
var exception: Throwable? = null

Thread.setDefaultUncaughtExceptionHandler { t, e ->
if (exception != null) {
exception!!.addSuppressed(e)
} else {
exception = e
}
}

try {
base.evaluate()
} finally {
Thread.setDefaultUncaughtExceptionHandler(oldHandler)
}

exception?.let { throw it }
}
}
}
}
Loading