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
16.24k stars 1.18k forks source link

SwingPanel overdraws massively when placed in a scrollable container #1202

Closed felixdivo closed 5 months ago

felixdivo commented 3 years ago

General description

Having a SwingPanel in a (vertically) scrollable Box or LazyColumn overdraws beyond the boundaries of the container (and into the blue box). I attached two reproducing examples with a small animation showing the problem. I had to add a small dark-grey area in front of each row to be able to scroll at all (see #1062).

I'm using the recent org.jetbrains.compose @ 1.0.0-alpha4-build348.

Example with LazyColumn

Animation

import androidx.compose.desktop.DesktopMaterialTheme
import androidx.compose.foundation.VerticalScrollbar
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollbarAdapter
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.SwingPanel
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import com.arkivanov.decompose.ExperimentalDecomposeApi
import com.arkivanov.decompose.extensions.compose.jetbrains.lifecycle.LifecycleController
import com.arkivanov.essenty.lifecycle.LifecycleRegistry
import javax.swing.JLabel

@OptIn(ExperimentalComposeUiApi::class, ExperimentalDecomposeApi::class)
fun main() {
    val lifecycle = LifecycleRegistry()

    application {
        val windowState = rememberWindowState()

        LifecycleController(lifecycle, windowState)

        Window(
            onCloseRequest = ::exitApplication,
            state = windowState,
        ) {

            Surface(modifier = Modifier.fillMaxSize()) {
                DesktopMaterialTheme {
                    actualContent()
                }
            }
        }
    }
}

@Composable
private fun actualContent() {

    Column(modifier = Modifier.fillMaxSize()) {
        Box(Modifier.height(100.dp).fillMaxWidth().background(Color.Blue))

        Box(Modifier.fillMaxSize()) {
            val scrollState = rememberLazyListState()
            LazyColumn(Modifier.fillMaxHeight().background(Color.Gray), scrollState) {
                items(25) { scope ->

                    Row {
                        Spacer(Modifier.width(20.dp).background(Color.Yellow))
                        SwingPanel(
                            modifier = Modifier.height(32.dp).weight(1f),
                            factory = {
                                JLabel(scope.toString()).also {
                                    it.background = java.awt.Color.RED
                                }
                            },
                        )
                    }
                }
            }
            VerticalScrollbar(
                modifier = Modifier.fillMaxHeight().align(Alignment.CenterEnd),
                adapter = rememberScrollbarAdapter(scrollState)
            )
        }
    }
}

Example with Box

This is a bit different but the problem occurs too:

Animation

import androidx.compose.desktop.DesktopMaterialTheme
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.SwingPanel
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import com.arkivanov.decompose.ExperimentalDecomposeApi
import com.arkivanov.decompose.extensions.compose.jetbrains.lifecycle.LifecycleController
import com.arkivanov.essenty.lifecycle.LifecycleRegistry
import javax.swing.JLabel

@OptIn(ExperimentalComposeUiApi::class, ExperimentalDecomposeApi::class)
fun main() {
    val lifecycle = LifecycleRegistry()

    application {
        val windowState = rememberWindowState()

        LifecycleController(lifecycle, windowState)

        Window(
            onCloseRequest = ::exitApplication,
            state = windowState,
        ) {

            Surface(modifier = Modifier.fillMaxSize()) {
                DesktopMaterialTheme {
                    actualContent()
                }
            }
        }
    }
}

@Composable
private fun actualContent() {

    Column(modifier = Modifier.fillMaxSize()) {
        Box(Modifier.height(100.dp).fillMaxWidth().background(Color.Blue))

        Box(Modifier.fillMaxSize().background(Color.Gray)) {
            val scrollState = rememberScrollState(0)
            Box(Modifier.fillMaxSize().verticalScroll(scrollState)) {
                Column {
                    for (index in 1..25)
                        Row {
                            Spacer(Modifier.width(20.dp).background(Color.Yellow))
                            SwingPanel(
                                modifier = Modifier.height(32.dp).weight(1f),
                                factory = {
                                    JLabel(index.toString()).also {
                                        it.background = java.awt.Color.RED
                                    }
                                },
                            )
                        }
                }
            }
            VerticalScrollbar(
                modifier = Modifier.fillMaxHeight().align(Alignment.CenterEnd),
                adapter = rememberScrollbarAdapter(scrollState)
            )
        }
    }
}
MatkovIvan commented 5 months ago

Fixed in https://github.com/JetBrains/compose-multiplatform-core/pull/1147

okushnikov commented 4 months ago

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