in lets-plot-compose/src/androidMain/kotlin/org/jetbrains/letsPlot/compose/PlotPanelAndroidView.kt [33:118]
fun PlotPanelAndroidView(
rawSpec: MutableMap<String, Any>,
preserveAspectRatio: Boolean,
modifier: Modifier,
errorTextStyle: TextStyle,
errorModifier: Modifier,
computationMessagesHandler: (List<String>) -> Unit
) {
if (logRecompositions) {
println("PlotPanel: recomposition")
}
var plotCanvasFigure: PlotCanvasFigure2? by remember { mutableStateOf(null) }
val sizingPolicy = SizingPolicy.fitContainerSize(preserveAspectRatio)
// Cache processed plot spec to avoid reprocessing the same raw spec on every recomposition.
// Note: Use remember(rawSpec.hashCode()), to bypass the equality check and use the content hash directly.
// The issue was that remember(rawSpec) uses some kind of comparison (equals()?) which somehow not working for `MutableMap`.
val processedPlotSpec = remember(rawSpec.hashCode()) {
processRawSpecs(rawSpec, frontendOnly = false)
}
var errorMessage: String? by remember(processedPlotSpec) { mutableStateOf(null) } // Reset error on spec change
LaunchedEffect(processedPlotSpec, sizingPolicy, computationMessagesHandler) {
runCatching {
if (PlotConfig.isFailure(processedPlotSpec)) {
errorMessage = PlotConfig.getErrorMessage(processedPlotSpec)
} else {
plotCanvasFigure?.update(processedPlotSpec, sizingPolicy, computationMessagesHandler)
?: LOG.info { "Error updating plot figure - plotCanvasFigure is null" }
}
}.onFailure { e ->
errorMessage = e.message ?: "Unknown error: ${e::class.simpleName}"
LOG.error(e) { "Error updating plot figure" }
}
}
// Background
val finalModifier = if (errorMessage != null) {
modifier.background(Color.LightGray)
} else {
if (containsBackground(modifier)) {
// Do not change the user-defined background
modifier
} else {
// Use background color from the plot theme
val lpColor = PlotThemeHelper.plotBackground(processedPlotSpec)
val lpBackground = Color(lpColor.red, lpColor.green, lpColor.blue, lpColor.alpha)
modifier.background(lpBackground)
}
}
errorMessage?.let { errMsg ->
// Reset the figure to resolve the 'Registration already removed' error.
// On error, the CanvasView is removed and the plotCanvasFigure changes state to 'detached',
// meaning it cannot be reused.
plotCanvasFigure = null
// Show error message
BasicTextField(
value = errMsg,
onValueChange = { },
readOnly = true,
textStyle = errorTextStyle,
modifier = errorModifier
)
} ?: run {
@Suppress("COMPOSE_APPLIER_CALL_MISMATCH") // Gemini says that this is a false positive
AndroidView(
modifier = finalModifier,
factory = { ctx ->
plotCanvasFigure = plotCanvasFigure ?: PlotCanvasFigure2()
CanvasView2(ctx).apply {
figure = plotCanvasFigure
onError = { e ->
@Suppress("AssignedValueIsNeverRead")
errorMessage = e.message ?: "Unknown error: ${e::class.simpleName}"
LOG.error(e) { "Error in CanvasView" }
}
}
}
)
}
}