Skip to content

Commit ba9cca9

Browse files
Fix Desktop PlotPanelRaw not showing plot after an error message
1 parent 97032c4 commit ba9cca9

File tree

2 files changed

+32
-11
lines changed
  • demo/plot/compose-desktop/src/main/kotlin/demo/plot/median
  • lets-plot-compose/src/desktopMain/kotlin/org/jetbrains/letsPlot/compose

2 files changed

+32
-11
lines changed

demo/plot/compose-desktop/src/main/kotlin/demo/plot/median/AppMain.kt

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,20 @@ import androidx.compose.ui.unit.dp
1919
import androidx.compose.ui.window.Window
2020
import androidx.compose.ui.window.application
2121
import demo.plot.median.ui.DemoList
22-
import org.jetbrains.letsPlot.compose.PlotPanel
23-
import plotSpec.DensitySpec
24-
import plotSpec.PerfSpec
25-
import plotSpec.PlotGridSpec
22+
import org.jetbrains.letsPlot.Figure
23+
import org.jetbrains.letsPlot.compose.PlotPanelRaw
24+
import org.jetbrains.letsPlot.intern.toSpec
25+
import plotSpec.*
2626

2727
fun main() = application {
2828
Window(onCloseRequest = ::exitApplication, title = "Demo (Compose Desktop, median)") {
2929

3030
val figures = listOf(
3131
"Density Plot" to DensitySpec().createFigure(),
3232
"Plot Grid" to PlotGridSpec().createFigure(),
33-
"25k Points" to PerfSpec().createFigure()
33+
"25k Points" to PerfSpec().createFigure(),
34+
"BackendError" to IllegalArgumentSpec().createFigure(),
35+
"FrontendError" to FrontendExceptionSpec().createRawSpec(),
3436
)
3537

3638
val preserveAspectRatio = remember { mutableStateOf(false) }
@@ -65,8 +67,18 @@ fun main() = application {
6567
modifier = Modifier.fillMaxSize()
6668
.padding(start = 10.dp, top = 10.dp, end = 10.dp, bottom = 10.dp),
6769
) {
68-
PlotPanel(
69-
figure = figures[figureIndex.value].second,
70+
// Cast to rawSpec to use only the PlotPanelRaw
71+
// Switch between PlotPanel and PlotPanelRaw causes re-creation of the whole panel
72+
// and hides bugs related to the panel re-use, like OK -> ERROR -> OK state transition.
73+
val rawSpec: Map<*, *> = when (val fig = figures[figureIndex.value].second) {
74+
is Figure -> fig.toSpec()
75+
is Map<*, *> -> fig
76+
else -> throw IllegalStateException("Unexpected figure type: ${fig.let { it::class }}")
77+
}
78+
79+
@Suppress("UNCHECKED_CAST")
80+
PlotPanelRaw(
81+
rawSpec = rawSpec as MutableMap<String, Any>,
7082
preserveAspectRatio = preserveAspectRatio.value,
7183
modifier = Modifier.fillMaxSize()
7284
) { computationMessages ->

lets-plot-compose/src/desktopMain/kotlin/org/jetbrains/letsPlot/compose/PlotPanelRaw.kt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import androidx.compose.ui.platform.LocalDensity
1919
import androidx.compose.ui.text.TextStyle
2020
import org.jetbrains.letsPlot.commons.geometry.DoubleVector
2121
import org.jetbrains.letsPlot.commons.logging.PortableLogging
22+
import org.jetbrains.letsPlot.compose.desktop.PlotContainer
23+
import org.jetbrains.letsPlot.compose.desktop.SvgViewPanel
2224
import org.jetbrains.letsPlot.core.plot.builder.interact.tools.FigureModelHelper
2325
import org.jetbrains.letsPlot.core.spec.Option.Meta.Kind.GG_TOOLBAR
2426
import org.jetbrains.letsPlot.core.spec.config.PlotConfig
@@ -27,14 +29,14 @@ import org.jetbrains.letsPlot.core.util.MonolithicCommon.processRawSpecs
2729
import org.jetbrains.letsPlot.core.util.PlotThemeHelper
2830
import org.jetbrains.letsPlot.core.util.sizing.SizingPolicy
2931
import org.jetbrains.letsPlot.skia.builder.MonolithicSkia
30-
import org.jetbrains.letsPlot.compose.desktop.PlotContainer
31-
import org.jetbrains.letsPlot.compose.desktop.SvgViewPanel
3232

3333
//import org.jetbrains.letsPlot.compose.util.NaiveLogger
3434

3535
//private val LOG = NaiveLogger("PlotPanel")
3636
private val LOG = PortableLogging.logger(name = "[PlotPanelRaw]")
3737

38+
private const val logRecompositions = true
39+
3840
@Suppress("FunctionName")
3941
@Composable
4042
actual fun PlotPanelRaw(
@@ -45,6 +47,9 @@ actual fun PlotPanelRaw(
4547
errorModifier: Modifier,
4648
computationMessagesHandler: (List<String>) -> Unit
4749
) {
50+
if (logRecompositions) {
51+
println("PlotPanelRaw: recomposition")
52+
}
4853

4954
// Update density on each recomposition to handle monitor DPI changes (e.g., drag between HIDPI/regular monitor)
5055
val density = LocalDensity.current.density.toDouble()
@@ -60,11 +65,14 @@ actual fun PlotPanelRaw(
6065
var panelSize by remember { mutableStateOf(DoubleVector.ZERO) }
6166
var dispatchComputationMessages by remember { mutableStateOf(true) }
6267
var specOverrideList by remember { mutableStateOf(emptyList<Map<String, Any>>()) }
63-
val plotContainer = remember { PlotContainer() }
6468
var plotFigureModel by remember { mutableStateOf<PlotFigureModel?>(null) }
6569

6670

67-
var errorMessage: String? by remember { mutableStateOf(null) }
71+
var errorMessage: String? by remember(processedPlotSpec) { mutableStateOf(null) }
72+
73+
// Reset the old plot on error to prevent blinking
74+
// We can't reset PlotContainer using updateViewmodel(), so we create a new one.
75+
val plotContainer = remember(errorMessage) { PlotContainer() }
6876

6977
// Background
7078
val finalModifier = if (errorMessage != null) {
@@ -123,6 +131,7 @@ actual fun PlotPanelRaw(
123131
LaunchedEffect(panelSize, processedPlotSpec, specOverrideList, preserveAspectRatio) {
124132

125133
if (PlotConfig.isFailure(processedPlotSpec)) {
134+
plotFigureModel = null
126135
errorMessage = PlotConfig.getErrorMessage(processedPlotSpec)
127136
return@LaunchedEffect
128137
}

0 commit comments

Comments
 (0)