From 76d3157ad9677efe4ad55863e16f5f038547f7ba Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Mon, 2 Oct 2023 08:41:01 +0200 Subject: [PATCH] Fix `ChartValuesManager`-related concurrency issues (WIP) Co-authored-by: Patryk Goworowski --- .../vico/compose/chart/Charts.kt | 19 ++++-- .../chart/entry/ChartEntryModelExtensions.kt | 39 ++++++----- .../layout/MeasureContextExtensions.kt | 13 ++-- .../compose/state/ChartEntryModelState.kt | 36 ---------- .../compose/state/ChartEntryModelWrapper.kt | 65 +++++++++++++++++++ .../DefaultHorizontalAxisItemPlacer.kt | 6 +- .../core/axis/horizontal/HorizontalAxis.kt | 8 +-- .../vertical/DefaultVerticalAxisItemPlacer.kt | 4 +- .../vico/core/axis/vertical/VerticalAxis.kt | 10 +-- .../vico/core/chart/BaseChart.kt | 2 +- .../patrykandpatrick/vico/core/chart/Chart.kt | 3 +- .../vico/core/chart/column/ColumnChart.kt | 9 +-- .../vico/core/chart/composed/ComposedChart.kt | 5 +- .../core/chart/decoration/ThresholdLine.kt | 4 +- .../vico/core/chart/draw/ChartDrawContext.kt | 5 +- .../chart/draw/ChartDrawContextExtensions.kt | 3 +- .../vico/core/chart/line/LineChart.kt | 11 ++-- .../core/chart/values/ChartValuesManager.kt | 33 +++++----- .../core/chart/values/ChartValuesProvider.kt | 16 +++-- .../core/chart/values/MutableChartValues.kt | 13 ++++ .../vico/core/context/MeasureContext.kt | 9 ++- .../core/context/MutableMeasureContext.kt | 8 ++- .../vico/core/draw/DrawContextExtensions.kt | 4 +- .../vico/core/entry/ChartEntryModel.kt | 5 ++ .../core/entry/ChartEntryModelProducer.kt | 8 +-- .../vico/core/entry/ChartModelProducer.kt | 6 +- .../ComposedChartEntryModelProducer.kt | 8 +-- .../vico/core/util/ValueWrapper.kt | 6 ++ .../vico/views/chart/BaseChartView.kt | 9 ++- 29 files changed, 223 insertions(+), 144 deletions(-) delete mode 100644 vico/compose/src/main/java/com/patrykandpatrick/vico/compose/state/ChartEntryModelState.kt create mode 100644 vico/compose/src/main/java/com/patrykandpatrick/vico/compose/state/ChartEntryModelWrapper.kt diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/Charts.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/Charts.kt index a4a66ae90..eb26b2aa8 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/Charts.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/Charts.kt @@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableFloatState import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -49,6 +50,9 @@ import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollState import com.patrykandpatrick.vico.compose.extension.chartTouchEvent import com.patrykandpatrick.vico.compose.gesture.OnZoom import com.patrykandpatrick.vico.compose.layout.getMeasureContext +import com.patrykandpatrick.vico.compose.state.component1 +import com.patrykandpatrick.vico.compose.state.component2 +import com.patrykandpatrick.vico.compose.state.component3 import com.patrykandpatrick.vico.compose.style.currentChartStyle import com.patrykandpatrick.vico.core.DEF_MAX_ZOOM import com.patrykandpatrick.vico.core.DEF_MIN_ZOOM @@ -65,6 +69,8 @@ import com.patrykandpatrick.vico.core.chart.edges.FadingEdges import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout import com.patrykandpatrick.vico.core.chart.scale.AutoScaleUp import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider +import com.patrykandpatrick.vico.core.chart.values.toChartValuesProvider import com.patrykandpatrick.vico.core.entry.ChartEntryModel import com.patrykandpatrick.vico.core.entry.ChartModelProducer import com.patrykandpatrick.vico.core.extension.set @@ -130,12 +136,11 @@ public fun Chart( getXStep: ((Model) -> Float)? = null, ) { val chartValuesManager = remember(chart) { ChartValuesManager() } - val (model, oldModel) = chartModelProducer + val chartEntryModelWrapper by chartModelProducer .collectAsState(chart, chartModelProducer, diffAnimationSpec, runInitialAnimation, chartValuesManager, getXStep) - .value ChartBox(modifier = modifier) { - if (model != null) { + chartEntryModelWrapper?.also { (model, oldModel, chartValuesProvider) -> ChartImpl( chart = chart, model = model, @@ -153,7 +158,7 @@ public fun Chart( autoScaleUp = autoScaleUp, chartScrollState = chartScrollState, horizontalLayout = horizontalLayout, - chartValuesManager = chartValuesManager, + chartValuesProvider = chartValuesProvider, ) } } @@ -233,7 +238,7 @@ public fun Chart( autoScaleUp = autoScaleUp, chartScrollState = chartScrollState, horizontalLayout = horizontalLayout, - chartValuesManager = chartValuesManager, + chartValuesProvider = chartValuesManager.toChartValuesProvider(), ) } } @@ -257,7 +262,7 @@ internal fun ChartImpl( autoScaleUp: AutoScaleUp, chartScrollState: ChartScrollState = rememberChartScrollState(), horizontalLayout: HorizontalLayout, - chartValuesManager: ChartValuesManager, + chartValuesProvider: ChartValuesProvider, ) { val axisManager = remember { AxisManager() } val bounds = remember { RectF() } @@ -269,7 +274,7 @@ internal fun ChartImpl( bounds, horizontalLayout, with(LocalContext.current) { ::spToPx }, - chartValuesManager, + chartValuesProvider, ) val scrollListener = rememberScrollListener(markerTouchPoint) val lastMarkerEntryModels = remember { mutableStateOf(emptyList()) } diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/entry/ChartEntryModelExtensions.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/entry/ChartEntryModelExtensions.kt index d6b2dba8a..ccac4e85d 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/entry/ChartEntryModelExtensions.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/entry/ChartEntryModelExtensions.kt @@ -25,10 +25,13 @@ import androidx.compose.runtime.State import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalInspectionMode -import com.patrykandpatrick.vico.compose.state.ChartEntryModelState +import com.patrykandpatrick.vico.compose.state.ChartEntryModelWrapper +import com.patrykandpatrick.vico.compose.state.ChartEntryModelWrapperState import com.patrykandpatrick.vico.core.Animation import com.patrykandpatrick.vico.core.chart.Chart import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider +import com.patrykandpatrick.vico.core.chart.values.toChartValuesProvider import com.patrykandpatrick.vico.core.entry.ChartEntryModel import com.patrykandpatrick.vico.core.entry.ChartModelProducer import com.patrykandpatrick.vico.core.entry.diff.MutableDrawingModelStore @@ -60,23 +63,22 @@ public fun ChartModelProducer.collectAsState( chartValuesManager: ChartValuesManager, getXStep: ((Model) -> Float)?, dispatcher: CoroutineDispatcher = Dispatchers.Default, -): State> { - val chartEntryModelState = remember(chart, producerKey) { ChartEntryModelState() } - +): State?> { + val chartEntryModelWrapperState = remember(chart, producerKey) { ChartEntryModelWrapperState() } val modelTransformerProvider = remember(chart) { chart.modelTransformerProvider } val drawingModelStore = remember(chart) { MutableDrawingModelStore() } - val scope = rememberCoroutineScope() val isInPreview = LocalInspectionMode.current - var mainAnimationJob: Job? = null - var animationFrameJob: Job? = null - var finalAnimationFrameJob: Job? = null - var isAnimationRunning: Boolean - var isAnimationFrameGenerationRunning = false DisposableEffect(chart, producerKey, runInitialAnimation, isInPreview) { + var mainAnimationJob: Job? = null + var animationFrameJob: Job? = null + var finalAnimationFrameJob: Job? = null + var isAnimationRunning: Boolean + var isAnimationFrameGenerationRunning = false + var chartValuesProvider: ChartValuesProvider = ChartValuesProvider.Empty val afterUpdate: (progressModel: suspend (chartKey: Any, progress: Float) -> Unit) -> Unit = { progressModel -> if (animationSpec != null && !isInPreview && - (chartEntryModelState.value.first != null || runInitialAnimation) + (chartEntryModelWrapperState.value != null || runInitialAnimation) ) { isAnimationRunning = true mainAnimationJob = scope.launch(dispatcher) { @@ -120,18 +122,23 @@ public fun ChartModelProducer.collectAsState( isAnimationRunning = false }, startAnimation = afterUpdate, - getOldModel = { chartEntryModelState.value.first }, + getOldModel = { chartEntryModelWrapperState.value?.chartEntryModel }, modelTransformerProvider = modelTransformerProvider, drawingModelStore = drawingModelStore, updateChartValues = { model -> chartValuesManager.resetChartValues() chart.updateChartValues(chartValuesManager, model, getXStep?.invoke(model)) - chartValuesManager + chartValuesManager.toChartValuesProvider().also { provider -> chartValuesProvider = provider } }, - onModelCreated = chartEntryModelState::set, + onModelCreated = { chartEntryModelWrapperState.set(it, chartValuesProvider) }, ) } - onDispose { unregisterFromUpdates(chart) } + onDispose { + mainAnimationJob?.cancel() + animationFrameJob?.cancel() + finalAnimationFrameJob?.cancel() + unregisterFromUpdates(chart) + } } - return chartEntryModelState + return chartEntryModelWrapperState } diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/layout/MeasureContextExtensions.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/layout/MeasureContextExtensions.kt index 9b8d60140..b20687319 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/layout/MeasureContextExtensions.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/layout/MeasureContextExtensions.kt @@ -24,7 +24,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.LayoutDirection import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout import com.patrykandpatrick.vico.core.chart.values.ChartValues -import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider import com.patrykandpatrick.vico.core.context.MeasureContext import com.patrykandpatrick.vico.core.context.MutableMeasureContext @@ -35,7 +35,7 @@ import com.patrykandpatrick.vico.core.context.MutableMeasureContext * @param canvasBounds the bounds of the canvas that will be used to draw the chart and its components. * @param horizontalLayout defines how the chart’s content is positioned horizontally. * @param spToPx converts dimensions from sp to px. - * @param chartValuesManager manages the chart’s [ChartValues]. + * @param chartValuesProvider provides the chart’s [ChartValues] instances. */ @Composable public fun getMeasureContext( @@ -43,19 +43,20 @@ public fun getMeasureContext( canvasBounds: RectF, horizontalLayout: HorizontalLayout, spToPx: (Float) -> Float, - chartValuesManager: ChartValuesManager, + chartValuesProvider: ChartValuesProvider, ): MutableMeasureContext = remember { MutableMeasureContext( canvasBounds = canvasBounds, density = 0f, isLtr = true, - isHorizontalScrollEnabled = isHorizontalScrollEnabled, - horizontalLayout = horizontalLayout, spToPx = spToPx, - chartValuesManager = chartValuesManager, + chartValuesProvider = chartValuesProvider, ) }.apply { this.density = LocalDensity.current.density this.isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr this.isHorizontalScrollEnabled = isHorizontalScrollEnabled + this.horizontalLayout = horizontalLayout + this.spToPx = spToPx + this.chartValuesProvider = chartValuesProvider } diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/state/ChartEntryModelState.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/state/ChartEntryModelState.kt deleted file mode 100644 index d173624b1..000000000 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/state/ChartEntryModelState.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. - * - * 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 com.patrykandpatrick.vico.compose.state - -import androidx.compose.runtime.State -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import com.patrykandpatrick.vico.core.entry.ChartEntryModel - -internal class ChartEntryModelState : State> { - private var previousValue: T? = null - - override var value by mutableStateOf>(null to null) - private set - - fun set(value: T) { - val currentValue = this.value.first - if (value.id != currentValue?.id) previousValue = currentValue - this.value = value to previousValue - } -} diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/state/ChartEntryModelWrapper.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/state/ChartEntryModelWrapper.kt new file mode 100644 index 000000000..2597a2b5d --- /dev/null +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/state/ChartEntryModelWrapper.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * + * 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 com.patrykandpatrick.vico.compose.state + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.State +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider +import com.patrykandpatrick.vico.core.entry.ChartEntryModel + +/** + * Holds a chart’s current [ChartEntryModel] ([chartEntryModel]), previous [ChartEntryModel] + * ([previousChartEntryModel]), and [ChartValuesProvider] ([chartValuesProvider]). + */ +@Immutable +public class ChartEntryModelWrapper( + public val chartEntryModel: T, + public val previousChartEntryModel: T?, + public val chartValuesProvider: ChartValuesProvider, +) + +/** + * Returns [ChartEntryModelWrapper.chartEntryModel]. + */ +public operator fun ChartEntryModelWrapper.component1(): T = chartEntryModel + +/** + * Returns [ChartEntryModelWrapper.previousChartEntryModel]. + */ +public operator fun ChartEntryModelWrapper.component2(): T? = previousChartEntryModel + +/** + * Returns [ChartEntryModelWrapper.chartValuesProvider]. + */ +public operator fun ChartEntryModelWrapper.component3(): ChartValuesProvider = + chartValuesProvider + +internal class ChartEntryModelWrapperState : State?> { + private var previousChartEntryModel: T? = null + + override var value by mutableStateOf?>(null) + private set + + fun set(chartEntryModel: T, chartValuesProvider: ChartValuesProvider) { + val currentChartEntryModel = value?.chartEntryModel + if (chartEntryModel.id != currentChartEntryModel?.id) previousChartEntryModel = currentChartEntryModel + value = ChartEntryModelWrapper(chartEntryModel, previousChartEntryModel, chartValuesProvider) + } +} diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/horizontal/DefaultHorizontalAxisItemPlacer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/horizontal/DefaultHorizontalAxisItemPlacer.kt index a769ec736..8e59767ad 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/horizontal/DefaultHorizontalAxisItemPlacer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/horizontal/DefaultHorizontalAxisItemPlacer.kt @@ -38,7 +38,7 @@ internal class DefaultHorizontalAxisItemPlacer( visibleXRange: ClosedFloatingPointRange, fullXRange: ClosedFloatingPointRange, ): List { - val chartValues = context.chartValuesManager.getChartValues() + val chartValues = context.chartValuesProvider.getChartValues() val remainder = ((visibleXRange.start - chartValues.minX) / chartValues.xStep - offset) % spacing val firstValue = visibleXRange.start + (spacing - remainder) % spacing * chartValues.xStep val minXOffset = chartValues.minX % chartValues.xStep @@ -61,7 +61,7 @@ internal class DefaultHorizontalAxisItemPlacer( horizontalDimensions: HorizontalDimensions, fullXRange: ClosedFloatingPointRange, ): List { - val chartValues = context.chartValuesManager.getChartValues() + val chartValues = context.chartValuesProvider.getChartValues() return listOf(chartValues.minX, (chartValues.minX + chartValues.maxX).half, chartValues.maxX) } @@ -71,7 +71,7 @@ internal class DefaultHorizontalAxisItemPlacer( visibleXRange: ClosedFloatingPointRange, fullXRange: ClosedFloatingPointRange, ): List? { - val chartValues = context.chartValuesManager.getChartValues() + val chartValues = context.chartValuesProvider.getChartValues() return when (context.horizontalLayout) { is HorizontalLayout.Segmented -> { val remainder = (visibleXRange.start - fullXRange.start) % chartValues.xStep diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/horizontal/HorizontalAxis.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/horizontal/HorizontalAxis.kt index 321b863b4..bdec45786 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/horizontal/HorizontalAxis.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/horizontal/HorizontalAxis.kt @@ -89,7 +89,7 @@ public class HorizontalAxis( val clipRestoreCount = canvas.save() val tickMarkTop = if (position.isBottom) bounds.top else bounds.bottom - axisThickness - tickLength val tickMarkBottom = tickMarkTop + axisThickness + tickLength - val chartValues = chartValuesManager.getChartValues() + val chartValues = chartValuesProvider.getChartValues() canvas.clipRect( bounds.left - itemPlacer.getStartHorizontalAxisInset(this, horizontalDimensions, tickThickness), @@ -186,7 +186,7 @@ public class HorizontalAxis( val clipRestoreCount = canvas.save() canvas.clipRect(chartBounds) - val chartValues = chartValuesManager.getChartValues() + val chartValues = chartValuesProvider.getChartValues() if (lineValues == null) { labelValues.forEach { x -> @@ -238,7 +238,7 @@ public class HorizontalAxis( private fun MeasureContext.getFullXRange( horizontalDimensions: HorizontalDimensions, ): ClosedFloatingPointRange = with(horizontalDimensions) { - val chartValues = chartValuesManager.getChartValues() + val chartValues = chartValuesProvider.getChartValues() val start = chartValues.minX - startPadding / xSpacing * chartValues.xStep val end = chartValues.maxX + endPadding / xSpacing * chartValues.xStep start..end @@ -248,7 +248,7 @@ public class HorizontalAxis( context: MeasureContext, horizontalDimensions: HorizontalDimensions, ): Float = with(context) { - val chartValues = chartValuesManager.getChartValues() + val chartValues = chartValuesProvider.getChartValues() val fullXRange = getFullXRange(horizontalDimensions) when (val constraint = sizeConstraint) { diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/vertical/DefaultVerticalAxisItemPlacer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/vertical/DefaultVerticalAxisItemPlacer.kt index 76618f059..d03bfa746 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/vertical/DefaultVerticalAxisItemPlacer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/vertical/DefaultVerticalAxisItemPlacer.kt @@ -49,7 +49,7 @@ internal class DefaultVerticalAxisItemPlacer( position: AxisPosition.Vertical, ): List { if (maxItemCount == 0) return emptyList() - val chartValues = context.chartValuesManager.getChartValues(position) + val chartValues = context.chartValuesProvider.getChartValues(position) return if (chartValues.minY * chartValues.maxY >= 0) { getSimpleLabelValues(axisHeight, maxLabelHeight, chartValues) } else { @@ -61,7 +61,7 @@ internal class DefaultVerticalAxisItemPlacer( context: MeasureContext, position: AxisPosition.Vertical, ): List { - val chartValues = context.chartValuesManager.getChartValues(position) + val chartValues = context.chartValuesProvider.getChartValues(position) return listOf(chartValues.minY, (chartValues.minY + chartValues.maxY).half, chartValues.maxY) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/vertical/VerticalAxis.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/vertical/VerticalAxis.kt index 89d6826ca..82a3c1f8f 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/vertical/VerticalAxis.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/vertical/VerticalAxis.kt @@ -103,7 +103,7 @@ public class VerticalAxis( context: ChartDrawContext, ): Unit = with(context) { var centerY: Float - val chartValues = chartValuesManager.getChartValues(position) + val chartValues = chartValuesProvider.getChartValues(position) val maxLabelHeight = getMaxLabelHeight() val lineValues = itemPlacer.getLineValues(this, bounds.height(), maxLabelHeight, position) ?: itemPlacer.getLabelValues(this, bounds.height(), maxLabelHeight, position) @@ -146,7 +146,7 @@ public class VerticalAxis( val tickRightX = tickLeftX + axisThickness + tickLength val labelX = if (areLabelsOutsideAtStartOrInsideAtEnd == isLtr) tickLeftX else tickRightX var tickCenterY: Float - val chartValues = chartValuesManager.getChartValues(position) + val chartValues = chartValuesProvider.getChartValues(position) labelValues.forEach { labelValue -> tickCenterY = bounds.bottom - bounds.height() * (labelValue - chartValues.minY) / chartValues.lengthY + @@ -289,21 +289,21 @@ public class VerticalAxis( } private fun MeasureContext.getMaxLabelHeight() = label?.let { label -> - val chartValues = chartValuesManager.getChartValues(position) + val chartValues = chartValuesProvider.getChartValues(position) itemPlacer .getHeightMeasurementLabelValues(this, position) .maxOfOrNull { value -> label.getHeight(this, valueFormatter.formatValue(value, chartValues)) } }.orZero private fun MeasureContext.getMaxLabelWidth(axisHeight: Float) = label?.let { label -> - val chartValues = chartValuesManager.getChartValues(position) + val chartValues = chartValuesProvider.getChartValues(position) itemPlacer .getWidthMeasurementLabelValues(this, axisHeight, getMaxLabelHeight(), position) .maxOfOrNull { value -> label.getWidth(this, valueFormatter.formatValue(value, chartValues)) } }.orZero private fun ChartDrawContext.getLineCanvasYCorrection(thickness: Float, y: Float): Float { - val chartValues = chartValuesManager.getChartValues(position) + val chartValues = chartValuesProvider.getChartValues(position) return if (y == chartValues.maxY && itemPlacer.getShiftTopLines(this)) -thickness.half else thickness.half } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/BaseChart.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/BaseChart.kt index bc1e9e4bd..a115306c2 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/BaseChart.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/BaseChart.kt @@ -110,7 +110,7 @@ public abstract class BaseChart : Chart, Boun context = context, bounds = bounds, markedEntries = markerModel, - chartValuesProvider = chartValuesManager, + chartValuesProvider = chartValuesProvider, ) } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/Chart.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/Chart.kt index 18a01ee6b..73476f2f6 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/Chart.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/Chart.kt @@ -26,6 +26,7 @@ import com.patrykandpatrick.vico.core.chart.line.LineChart import com.patrykandpatrick.vico.core.chart.values.AxisValuesOverrider import com.patrykandpatrick.vico.core.chart.values.ChartValues import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider import com.patrykandpatrick.vico.core.context.MeasureContext import com.patrykandpatrick.vico.core.dimensions.BoundsAware import com.patrykandpatrick.vico.core.entry.ChartEntryModel @@ -229,7 +230,7 @@ public interface Chart : BoundsAware, ChartInsetter { oldModel: Model?, newModel: Model, drawingModelStore: MutableDrawingModelStore, - chartValuesManager: ChartValuesManager, + chartValuesProvider: ChartValuesProvider, ) /** diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/column/ColumnChart.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/column/ColumnChart.kt index c343535f4..dc5eda1d7 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/column/ColumnChart.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/column/ColumnChart.kt @@ -30,6 +30,7 @@ import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout import com.patrykandpatrick.vico.core.chart.put import com.patrykandpatrick.vico.core.chart.values.ChartValues import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider import com.patrykandpatrick.vico.core.component.shape.LineComponent import com.patrykandpatrick.vico.core.component.text.TextComponent import com.patrykandpatrick.vico.core.component.text.VerticalPosition @@ -120,7 +121,7 @@ public open class ColumnChart( ): Unit = with(context) { entryLocationMap.clear() drawChartInternal( - chartValues = chartValuesManager.getChartValues(axisPosition = targetVerticalAxisPosition), + chartValues = chartValuesProvider.getChartValues(axisPosition = targetVerticalAxisPosition), model = model, drawingModel = model.drawingModelStore.getOrNull(drawingModelKey), ) @@ -276,7 +277,7 @@ public open class ColumnChart( } val text = dataLabelValueFormatter.formatValue( value = dataLabelValue, - chartValues = chartValuesManager.getChartValues(axisPosition = targetVerticalAxisPosition), + chartValues = chartValuesProvider.getChartValues(axisPosition = targetVerticalAxisPosition), ) val dataLabelWidth = textComponent.getWidth( context = this, @@ -446,11 +447,11 @@ public open class ColumnChart( oldModel: ChartEntryModel?, newModel: ChartEntryModel, drawingModelStore: MutableDrawingModelStore, - chartValuesManager: ChartValuesManager, + chartValuesProvider: ChartValuesProvider, ) { drawingModelInterpolator.setModels( drawingModelStore.getOrNull(key), - newModel.toDrawingModel(chartValuesManager.getChartValues(getTargetVerticalAxisPosition())), + newModel.toDrawingModel(chartValuesProvider.getChartValues(getTargetVerticalAxisPosition())), ) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChart.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChart.kt index a1a4c4c33..151071b31 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChart.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChart.kt @@ -27,6 +27,7 @@ import com.patrykandpatrick.vico.core.chart.insets.ChartInsetter import com.patrykandpatrick.vico.core.chart.insets.HorizontalInsets import com.patrykandpatrick.vico.core.chart.insets.Insets import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider import com.patrykandpatrick.vico.core.context.MeasureContext import com.patrykandpatrick.vico.core.entry.ChartEntryModel import com.patrykandpatrick.vico.core.entry.diff.DrawingModelStore @@ -175,7 +176,7 @@ public class ComposedChart( oldModel: T?, newModel: T, drawingModelStore: MutableDrawingModelStore, - chartValuesManager: ChartValuesManager, + chartValuesProvider: ChartValuesProvider, ) { getModelTransformers().forEachIndexed { index, transformer -> @Suppress("UNCHECKED_CAST") @@ -183,7 +184,7 @@ public class ComposedChart( (oldModel as ComposedChartEntryModel<*>?)?.composedEntryCollections?.getOrNull(index) as T?, (newModel as ComposedChartEntryModel<*>).composedEntryCollections[index] as T, drawingModelStore, - chartValuesManager, + chartValuesProvider, ) } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/decoration/ThresholdLine.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/decoration/ThresholdLine.kt index 8a0375ab5..67cbd88ce 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/decoration/ThresholdLine.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/decoration/ThresholdLine.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 by Patryk Goworowski and Patrick Michalik. + * Copyright 2023 by Patryk Goworowski and Patrick Michalik. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -99,7 +99,7 @@ public data class ThresholdLine( context: ChartDrawContext, bounds: RectF, ): Unit = with(context) { - val chartValues = chartValuesManager.getChartValues() + val chartValues = chartValuesProvider.getChartValues() val valueRange = chartValues.maxY - chartValues.minY diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/draw/ChartDrawContext.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/draw/ChartDrawContext.kt index 9181c863a..0e169e7a7 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/draw/ChartDrawContext.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/draw/ChartDrawContext.kt @@ -66,7 +66,7 @@ public fun MeasureContext.getMaxScrollDistance( ): Float { val contentWidth = horizontalDimensions .run { if (zoom != null) scaled(zoom) else this } - .getContentWidth(chartValuesManager.getChartValues().getMaxMajorEntryCount()) + .getContentWidth(chartValuesProvider.getChartValues().getMaxMajorEntryCount()) return (layoutDirectionMultiplier * (contentWidth - chartWidth)).run { if (isLtr) coerceAtLeast(minimumValue = 0f) else coerceAtMost(maximumValue = 0f) @@ -90,7 +90,8 @@ public fun MeasureContext.getAutoZoom( chartBounds: RectF, autoScaleUp: AutoScaleUp, ): Float { - val contentWidth = horizontalDimensions.getContentWidth(chartValuesManager.getChartValues().getMaxMajorEntryCount()) + val contentWidth = + horizontalDimensions.getContentWidth(chartValuesProvider.getChartValues().getMaxMajorEntryCount()) return when { contentWidth < chartBounds.width() -> if (autoScaleUp == AutoScaleUp.Full) (chartBounds.width() / contentWidth).coerceAtMost(DEF_MAX_ZOOM) else 1f diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/draw/ChartDrawContextExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/draw/ChartDrawContextExtensions.kt index 175f72e73..8d2e9cd2a 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/draw/ChartDrawContextExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/draw/ChartDrawContextExtensions.kt @@ -93,12 +93,11 @@ public fun ChartDrawContext.drawMarker( markerTouchPoint ?.let(chart.entryLocationMap::getClosestMarkerEntryModel) ?.let { markerEntryModels -> - chartValuesManager.getChartValues() marker.draw( context = this, bounds = chart.bounds, markedEntries = markerEntryModels, - chartValuesProvider = chartValuesManager, + chartValuesProvider = chartValuesProvider, ) if (wasMarkerVisible.not()) { markerVisibilityChangeListener?.onMarkerShown( diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/line/LineChart.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/line/LineChart.kt index d96f0eefd..99265bd80 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/line/LineChart.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/line/LineChart.kt @@ -38,6 +38,7 @@ import com.patrykandpatrick.vico.core.chart.line.LineChart.LineSpec.PointConnect import com.patrykandpatrick.vico.core.chart.put import com.patrykandpatrick.vico.core.chart.values.ChartValues import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider import com.patrykandpatrick.vico.core.component.Component import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShader import com.patrykandpatrick.vico.core.component.text.TextComponent @@ -378,7 +379,7 @@ public open class LineChart( pointInfoMap: Map?, ) { if (lineSpec.point == null && lineSpec.dataLabel == null) return - val chartValues = chartValuesManager.getChartValues(targetVerticalAxisPosition) + val chartValues = chartValuesProvider.getChartValues(targetVerticalAxisPosition) forEachPointWithinBoundsIndexed( entries = entries, @@ -440,7 +441,7 @@ public open class LineChart( previousX: Float?, nextX: Float?, ): Int { - val chartValues = chartValuesManager.getChartValues(targetVerticalAxisPosition) + val chartValues = chartValuesProvider.getChartValues(targetVerticalAxisPosition) return when { previousX != null && nextX != null -> min(x - previousX, nextX - x) @@ -487,7 +488,7 @@ public open class LineChart( pointInfoMap: Map?, action: (index: Int, entry: ChartEntry, x: Float, y: Float, previousX: Float?, nextX: Float?) -> Unit, ) { - val chartValues = chartValuesManager.getChartValues(targetVerticalAxisPosition) + val chartValues = chartValuesProvider.getChartValues(targetVerticalAxisPosition) val minX = chartValues.minX val maxX = chartValues.maxX @@ -605,11 +606,11 @@ public open class LineChart( oldModel: ChartEntryModel?, newModel: ChartEntryModel, drawingModelStore: MutableDrawingModelStore, - chartValuesManager: ChartValuesManager, + chartValuesProvider: ChartValuesProvider, ) { drawingModelInterpolator.setModels( drawingModelStore.getOrNull(key), - newModel.toDrawingModel(chartValuesManager.getChartValues(getTargetVerticalAxisPosition())), + newModel.toDrawingModel(chartValuesProvider.getChartValues(getTargetVerticalAxisPosition())), ) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/ChartValuesManager.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/ChartValuesManager.kt index ec8659e4d..4e43f247e 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/ChartValuesManager.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/ChartValuesManager.kt @@ -38,27 +38,13 @@ import com.patrykandpatrick.vico.core.entry.ChartEntryModel */ public class ChartValuesManager : ChartValuesProvider { - private val chartValues: MutableMap = mutableMapOf() + internal val chartValues: MutableMap = mutableMapOf() - /** - * Returns the [ChartValues] associated with the given [axisPosition]. - * @param axisPosition if this is null, the main [ChartValues] instance is returned. Otherwise, the [ChartValues] - * instance associated with the given [AxisPosition.Vertical] is returned. - */ - public fun getChartValues(axisPosition: AxisPosition.Vertical? = null): MutableChartValues = + override fun getChartValues(axisPosition: AxisPosition.Vertical?): ChartValues = chartValues[axisPosition] ?.takeIf { it.hasValuesSet } ?: chartValues.getOrPut(null) { MutableChartValues() } - override fun getChartValues(): ChartValues = getChartValues(null) - - override fun getChartValuesForAxisPosition(axisPosition: AxisPosition.Vertical): ChartValues? = - if (chartValues.containsKey(axisPosition)) { - getChartValues(axisPosition).takeIf { it.hasValuesSet } - } else { - null - } - /** * Attempts to update the stored values to the provided values. * [MutableChartValues.minX] and [MutableChartValues.minY] can be updated to a lower value. @@ -105,3 +91,18 @@ public class ChartValuesManager : ChartValuesProvider { chartValues.values.forEach { it.reset() } } } + +/** + * Creates and returns a [ChartValuesProvider] implementation with this [ChartValuesManager]’s [ChartValues] + * instances. + */ +public fun ChartValuesManager.toChartValuesProvider(): ChartValuesProvider = + object : ChartValuesProvider { + val chartValues = this@toChartValuesProvider + .chartValues + .map { (axisPosition, chartValues) -> axisPosition to chartValues.toImmutable() } + .toMap() + + override fun getChartValues(axisPosition: AxisPosition.Vertical?): ChartValues = + chartValues[axisPosition] ?: chartValues.getValue(null) + } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/ChartValuesProvider.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/ChartValuesProvider.kt index 6cc40077a..9f5204e29 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/ChartValuesProvider.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/ChartValuesProvider.kt @@ -17,21 +17,23 @@ package com.patrykandpatrick.vico.core.chart.values import com.patrykandpatrick.vico.core.axis.AxisPosition -import com.patrykandpatrick.vico.core.chart.Chart /** - * Provides a [Chart]’s [ChartValues]. + * Provides a chart’s [ChartValues] instances. */ public interface ChartValuesProvider { /** - * Returns the [Chart]’s main [ChartValues]. + * Returns the [ChartValues] instance associated with the specified [AxisPosition.Vertical] subclass. If + * [axisPosition] is `null`, the chart’s main [ChartValues] instance is returned. */ - public fun getChartValues(): ChartValues + public fun getChartValues(axisPosition: AxisPosition.Vertical? = null): ChartValues /** - * Returns the [ChartValues] associated with the specified [AxisPosition.Vertical] subclass, or `null` if there is - * no such association. + * An empty [ChartValuesProvider] implementation. [getChartValues] throws an exception when called. */ - public fun getChartValuesForAxisPosition(axisPosition: AxisPosition.Vertical): ChartValues? + public companion object Empty : ChartValuesProvider { + override fun getChartValues(axisPosition: AxisPosition.Vertical?): ChartValues = + error("`ChartValuesProvider.Empty#getChartValues` shouldn’t be used.") + } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/MutableChartValues.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/MutableChartValues.kt index c85b85841..071de4109 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/MutableChartValues.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/MutableChartValues.kt @@ -106,3 +106,16 @@ public class MutableChartValues : ChartValues { } } } + +/** + * Creates and returns an immutable copy of this [MutableChartValues] instance. + */ +public fun MutableChartValues.toImmutable(): ChartValues = + object : ChartValues { + override val minX: Float = this@toImmutable.minX + override val maxX: Float = this@toImmutable.maxX + override val xStep: Float = this@toImmutable.xStep + override val minY: Float = this@toImmutable.minY + override val maxY: Float = this@toImmutable.maxY + override val chartEntryModel: ChartEntryModel = this@toImmutable.chartEntryModel.toImmutable() + } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/MeasureContext.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/MeasureContext.kt index 095215493..e25d9438a 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/MeasureContext.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/MeasureContext.kt @@ -17,10 +17,9 @@ package com.patrykandpatrick.vico.core.context import android.graphics.RectF -import com.patrykandpatrick.vico.core.chart.Chart import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout import com.patrykandpatrick.vico.core.chart.values.ChartValues -import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider /** * [MeasureContext] holds data used by various chart components during the measuring and drawing phases. @@ -33,11 +32,11 @@ public interface MeasureContext : Extras { public val canvasBounds: RectF /** - * Manages the associated [Chart]’s [ChartValues]. + * Provides the chart’s [ChartValues] instances. * - * @see [ChartValuesManager] + * @see [ChartValuesProvider] */ - public val chartValuesManager: ChartValuesManager + public val chartValuesProvider: ChartValuesProvider /** * The pixel density. diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/MutableMeasureContext.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/MutableMeasureContext.kt index efbb81a39..6922d90fa 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/MutableMeasureContext.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/MutableMeasureContext.kt @@ -17,8 +17,9 @@ package com.patrykandpatrick.vico.core.context import android.graphics.RectF +import androidx.annotation.RestrictTo import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout -import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider /** * A [MeasureContext] implementation that facilitates the mutation of some of its properties. @@ -29,8 +30,9 @@ public data class MutableMeasureContext( override var isLtr: Boolean, override var isHorizontalScrollEnabled: Boolean = false, override var horizontalLayout: HorizontalLayout = HorizontalLayout.Segmented, - private var spToPx: (Float) -> Float, - override val chartValuesManager: ChartValuesManager, + @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + public var spToPx: (Float) -> Float, + override var chartValuesProvider: ChartValuesProvider, ) : MeasureContext, Extras by DefaultExtras() { override fun reset() { diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/draw/DrawContextExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/draw/DrawContextExtensions.kt index e816d37f8..29f24a168 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/draw/DrawContextExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/draw/DrawContextExtensions.kt @@ -20,7 +20,7 @@ import android.graphics.Canvas import android.graphics.RectF import com.patrykandpatrick.vico.core.DefaultColors import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout -import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider import com.patrykandpatrick.vico.core.context.DefaultExtras import com.patrykandpatrick.vico.core.context.DrawContext import com.patrykandpatrick.vico.core.context.Extras @@ -54,7 +54,7 @@ public fun drawContext( override val density: Float = density override val isLtr: Boolean = isLtr override val isHorizontalScrollEnabled: Boolean = false - override val chartValuesManager: ChartValuesManager = ChartValuesManager() + override val chartValuesProvider: ChartValuesProvider = ChartValuesProvider.Empty override val horizontalLayout: HorizontalLayout = HorizontalLayout.Segmented override fun withOtherCanvas(canvas: Canvas, block: (DrawContext) -> Unit) { diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModel.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModel.kt index 7049e7ad8..07636c0ff 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModel.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModel.kt @@ -96,4 +96,9 @@ public interface ChartEntryModel { */ public val drawingModelStore: DrawingModelStore get() = DrawingModelStore.empty + + /** + * Returns an immutable copy of this [ChartEntryModel]. + */ + public fun toImmutable(): ChartEntryModel = this } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModelProducer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModelProducer.kt index 18fd18e74..8be82f2ab 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModelProducer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModelProducer.kt @@ -18,7 +18,7 @@ package com.patrykandpatrick.vico.core.entry import androidx.annotation.WorkerThread import com.patrykandpatrick.vico.core.chart.Chart -import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider import com.patrykandpatrick.vico.core.entry.diff.DrawingModelStore import com.patrykandpatrick.vico.core.entry.diff.MutableDrawingModelStore import com.patrykandpatrick.vico.core.extension.copy @@ -169,7 +169,7 @@ public class ChartEntryModelProducer( getOldModel: () -> ChartEntryModel?, modelTransformerProvider: Chart.ModelTransformerProvider?, drawingModelStore: MutableDrawingModelStore, - updateChartValues: (ChartEntryModel) -> ChartValuesManager, + updateChartValues: (ChartEntryModel) -> ChartValuesProvider, onModelCreated: (ChartEntryModel) -> Unit, ) { UpdateReceiver( @@ -199,7 +199,7 @@ public class ChartEntryModelProducer( val drawingModelStore: MutableDrawingModelStore, val modelTransformer: Chart.ModelTransformer?, val getOldModel: () -> ChartEntryModel?, - val updateChartValues: (ChartEntryModel) -> ChartValuesManager, + val updateChartValues: (ChartEntryModel) -> ChartValuesProvider, ) { fun handleUpdate() { cancelAnimation() @@ -207,7 +207,7 @@ public class ChartEntryModelProducer( oldModel = getOldModel(), newModel = getModel(), drawingModelStore = drawingModelStore, - chartValuesManager = updateChartValues(getModel()), + chartValuesProvider = updateChartValues(getModel()), ) startAnimation(::progressModel) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartModelProducer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartModelProducer.kt index 72c601125..8718104da 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartModelProducer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartModelProducer.kt @@ -18,7 +18,7 @@ package com.patrykandpatrick.vico.core.entry import com.patrykandpatrick.vico.core.chart.Chart import com.patrykandpatrick.vico.core.chart.values.ChartValues -import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider import com.patrykandpatrick.vico.core.entry.diff.MutableDrawingModelStore /** @@ -44,7 +44,7 @@ public interface ChartModelProducer { * called after a data update is requested, with [cancelAnimation] being called before the update starts * being processed (at which point [progressModel] should stop being used), and [startAnimation] being * called once the update has been processed (at which point it’s safe to use [progressModel]). [updateChartValues] - * updates the chart’s [ChartValues] and returns its [ChartValuesManager]. [onModelCreated] is called when a new + * updates the chart’s [ChartValues] and returns its [ChartValuesProvider]. [onModelCreated] is called when a new * [Model] has been generated. */ public fun registerForUpdates( @@ -54,7 +54,7 @@ public interface ChartModelProducer { getOldModel: () -> Model?, modelTransformerProvider: Chart.ModelTransformerProvider?, drawingModelStore: MutableDrawingModelStore, - updateChartValues: (Model) -> ChartValuesManager, + updateChartValues: (Model) -> ChartValuesProvider, onModelCreated: (Model) -> Unit, ) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/composed/ComposedChartEntryModelProducer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/composed/ComposedChartEntryModelProducer.kt index 3b6a83e96..9480aef6f 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/composed/ComposedChartEntryModelProducer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/composed/ComposedChartEntryModelProducer.kt @@ -19,7 +19,7 @@ package com.patrykandpatrick.vico.core.entry.composed import androidx.annotation.WorkerThread import com.patrykandpatrick.vico.core.chart.Chart import com.patrykandpatrick.vico.core.chart.composed.ComposedChartEntryModel -import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider import com.patrykandpatrick.vico.core.entry.ChartEntry import com.patrykandpatrick.vico.core.entry.ChartEntryModel import com.patrykandpatrick.vico.core.entry.ChartModelProducer @@ -164,7 +164,7 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor getOldModel: () -> ComposedChartEntryModel?, modelTransformerProvider: Chart.ModelTransformerProvider?, drawingModelStore: MutableDrawingModelStore, - updateChartValues: (ComposedChartEntryModel) -> ChartValuesManager, + updateChartValues: (ComposedChartEntryModel) -> ChartValuesProvider, onModelCreated: (ComposedChartEntryModel) -> Unit, ) { UpdateReceiver( @@ -289,7 +289,7 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor val drawingModelStore: MutableDrawingModelStore, val modelTransformer: Chart.ModelTransformer>?, val getOldModel: () -> ComposedChartEntryModel?, - val updateChartValues: (ComposedChartEntryModel) -> ChartValuesManager, + val updateChartValues: (ComposedChartEntryModel) -> ChartValuesProvider, ) { fun handleUpdate() { cancelAnimation() @@ -297,7 +297,7 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor oldModel = getOldModel(), newModel = getModel(), drawingModelStore = drawingModelStore, - chartValuesManager = updateChartValues(getModel()), + chartValuesProvider = updateChartValues(getModel()), ) startAnimation(::progressModel) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/ValueWrapper.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/ValueWrapper.kt index 8d4122e5a..776ea0483 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/ValueWrapper.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/ValueWrapper.kt @@ -29,3 +29,9 @@ public operator fun ValueWrapper.getValue(thisObj: Any?, property: KPrope public operator fun ValueWrapper.setValue(thisObj: Any?, property: KProperty<*>, value: T) { this.value = value } + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +public operator fun ValueWrapper.component1(): T = value + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +public operator fun ValueWrapper.component2(): (T) -> Unit = { value = it } diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/BaseChartView.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/BaseChartView.kt index 8d7053bd9..655bd2c93 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/BaseChartView.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/BaseChartView.kt @@ -44,6 +44,8 @@ import com.patrykandpatrick.vico.core.chart.edges.FadingEdges import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout import com.patrykandpatrick.vico.core.chart.scale.AutoScaleUp import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager +import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider +import com.patrykandpatrick.vico.core.chart.values.toChartValuesProvider import com.patrykandpatrick.vico.core.component.shape.ShapeComponent import com.patrykandpatrick.vico.core.context.MeasureContext import com.patrykandpatrick.vico.core.context.MutableMeasureContext @@ -121,7 +123,7 @@ public abstract class BaseChartView internal constructo isLtr = context.isLtr, isHorizontalScrollEnabled = false, spToPx = context::spToPx, - chartValuesManager = chartValuesManager, + chartValuesProvider = ChartValuesProvider.Empty, ) private val scaleGestureListener: ScaleGestureDetector.OnScaleGestureListener = @@ -167,6 +169,8 @@ public abstract class BaseChartView internal constructo private var wasZoomOverridden = false + private var chartValuesProvider: ChartValuesProvider = ChartValuesProvider.Empty + internal val themeHandler: ThemeHandler = ThemeHandler(context, attrs, chartType) /** @@ -284,11 +288,12 @@ public abstract class BaseChartView internal constructo updateChartValues = { model -> chartValuesManager.resetChartValues() chart?.updateChartValues(chartValuesManager, model, getXStep?.invoke(model)) - chartValuesManager + chartValuesManager.toChartValuesProvider().also { provider -> chartValuesProvider = provider } }, ) { model -> post { setModel(model = model, updateChartValues = false) + measureContext.chartValuesProvider = chartValuesProvider postInvalidateOnAnimation() } }