From aafc7a462952a28bf2339a85e0ff960b8c9817e8 Mon Sep 17 00:00:00 2001 From: Patryk Goworowski Date: Fri, 6 Oct 2023 19:33:39 +0200 Subject: [PATCH 01/39] Add View attributes to control preview models --- sample/src/main/res/layout/chart_1.xml | 1 + .../shape/shader/ComposeDynamicShader.kt | 33 ++++++++-------- .../component/shape/shader/DynamicShaders.kt | 38 ++++++++++++++++++- .../vico/core/util/RandomEntriesGenerator.kt | 20 ++++++---- .../vico/views/chart/ChartView.kt | 25 +++++++++++- .../vico/views/chart/ComposedChartView.kt | 28 +++++++++++++- vico/views/src/main/res/values/attrs.xml | 16 ++++++++ 7 files changed, 134 insertions(+), 27 deletions(-) diff --git a/sample/src/main/res/layout/chart_1.xml b/sample/src/main/res/layout/chart_1.xml index 64d4174ad..d725c7366 100644 --- a/sample/src/main/res/layout/chart_1.xml +++ b/sample/src/main/res/layout/chart_1.xml @@ -25,6 +25,7 @@ android:layout_height="wrap_content" app:chart="line" app:lineChartStyle="@style/Chart1LineChartStyle" + app:previewSeriesCount="1" app:showBottomAxis="true" app:showStartAxis="true" /> diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/ComposeDynamicShader.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/ComposeDynamicShader.kt index 82d85f306..83d2fa93b 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/ComposeDynamicShader.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/ComposeDynamicShader.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. @@ -22,34 +22,37 @@ import android.graphics.PorterDuff import android.os.Build import androidx.annotation.RequiresApi import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShader +import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders /** * Creates a [ComposeShader] out of two [DynamicShader]s by using a [BlendMode]. */ +@Deprecated( + message = "This function has been moved to the `core` module.", + replaceWith = ReplaceWith( + expression = "DynamicShaders.composeShader(first, second, mode)", + imports = ["com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders"], + ), +) @RequiresApi(Build.VERSION_CODES.Q) public fun composeShader( first: DynamicShader, second: DynamicShader, mode: BlendMode, -): DynamicShader = DynamicShader { context, left, top, right, bottom -> - ComposeShader( - first.provideShader(context, left, top, right, bottom), - second.provideShader(context, left, top, right, bottom), - mode, - ) -} +): DynamicShader = DynamicShaders.composeShader(first, second, mode) /** * Creates a [ComposeShader] out of two [DynamicShader]s by using a [PorterDuff.Mode]. */ +@Deprecated( + message = "This function has been moved to the `core` module.", + replaceWith = ReplaceWith( + expression = "DynamicShaders.composeShader(first, second, mode)", + imports = ["com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders"], + ), +) public fun composeShader( first: DynamicShader, second: DynamicShader, mode: PorterDuff.Mode, -): DynamicShader = DynamicShader { context, left, top, right, bottom -> - ComposeShader( - first.provideShader(context, left, top, right, bottom), - second.provideShader(context, left, top, right, bottom), - mode, - ) -} +): DynamicShader = DynamicShaders.composeShader(first, second, mode) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/DynamicShaders.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/DynamicShaders.kt index 879b353f3..02ca3a066 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/DynamicShaders.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/DynamicShaders.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. @@ -18,7 +18,12 @@ package com.patrykandpatrick.vico.core.component.shape.shader import android.graphics.Bitmap import android.graphics.BitmapShader +import android.graphics.BlendMode +import android.graphics.ComposeShader +import android.graphics.PorterDuff import android.graphics.Shader +import android.os.Build +import androidx.annotation.RequiresApi import com.patrykandpatrick.vico.core.context.DrawContext /** @@ -42,4 +47,35 @@ public object DynamicShaders { bottom: Float, ): Shader = BitmapShader(bitmap, tileXMode, tileYMode) } + + /** + * Creates a [ComposeShader] out of two [DynamicShader]s by using a [BlendMode]. + */ + @RequiresApi(Build.VERSION_CODES.Q) + public fun composeShader( + first: DynamicShader, + second: DynamicShader, + mode: BlendMode, + ): DynamicShader = DynamicShader { context, left, top, right, bottom -> + ComposeShader( + first.provideShader(context, left, top, right, bottom), + second.provideShader(context, left, top, right, bottom), + mode, + ) + } + + /** + * Creates a [ComposeShader] out of two [DynamicShader]s by using a [PorterDuff.Mode]. + */ + public fun composeShader( + first: DynamicShader, + second: DynamicShader, + mode: PorterDuff.Mode, + ): DynamicShader = DynamicShader { context, left, top, right, bottom -> + ComposeShader( + first.provideShader(context, left, top, right, bottom), + second.provideShader(context, left, top, right, bottom), + mode, + ) + } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/RandomEntriesGenerator.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/RandomEntriesGenerator.kt index 120c3a094..6d3dadf41 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/RandomEntriesGenerator.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/RandomEntriesGenerator.kt @@ -16,6 +16,7 @@ package com.patrykandpatrick.vico.core.util +import androidx.annotation.RestrictTo import com.patrykandpatrick.vico.core.chart.composed.ComposedChartEntryModel import com.patrykandpatrick.vico.core.entry.ChartEntryModel import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer @@ -29,10 +30,13 @@ import kotlin.random.Random * Generates randomized chart entries. * @param xRange the range of _x_ values. * @param yRange the range from which _y_ values are randomly selected. + * @param seriesCount the number of data series to generate. */ public class RandomEntriesGenerator( private val xRange: IntProgression = 0..X_RANGE_TOP, private val yRange: IntProgression = 0..Y_RANGE_TOP, + private val seriesCount: Int = SERIES_COUNT, + private val modelCount: Int = MODEL_COUNT, ) { /** * Generates a [List] of [FloatEntry] instances with randomized _y_ values. @@ -60,15 +64,17 @@ public class RandomEntriesGenerator( * [xRange]. */ public fun randomComposedEntryModel(): ComposedChartEntryModel = ComposedChartEntryModelProducer - .build { repeat(MODEL_SIZE) { add(List(MODEL_SIZE) { generateRandomEntries() }) } } + .build { repeat(modelCount) { add(List(seriesCount) { generateRandomEntries() }) } } .requireModel() - private companion object { - const val X_RANGE_TOP = 10 - const val Y_RANGE_TOP = 20 - const val MODEL_SIZE = 3 + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + public companion object { + public const val X_RANGE_TOP: Int = 10 + public const val Y_RANGE_TOP: Int = 20 + public const val SERIES_COUNT: Int = 3 + public const val MODEL_COUNT: Int = 3 - fun RandomEntriesGenerator.getChartEntryModelProducer(): ChartModelProducer = - ChartEntryModelProducer(List(MODEL_SIZE) { generateRandomEntries() }) + private fun RandomEntriesGenerator.getChartEntryModelProducer(): ChartModelProducer = + ChartEntryModelProducer(List(seriesCount) { generateRandomEntries() }) } } diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ChartView.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ChartView.kt index ddb2c559c..9119caf65 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ChartView.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ChartView.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. @@ -20,6 +20,10 @@ import android.content.Context import android.util.AttributeSet import com.patrykandpatrick.vico.core.entry.ChartEntryModel import com.patrykandpatrick.vico.core.util.RandomEntriesGenerator +import com.patrykandpatrick.vico.core.util.RandomEntriesGenerator.Companion.SERIES_COUNT +import com.patrykandpatrick.vico.core.util.RandomEntriesGenerator.Companion.X_RANGE_TOP +import com.patrykandpatrick.vico.core.util.RandomEntriesGenerator.Companion.Y_RANGE_TOP +import com.patrykandpatrick.vico.views.R import com.patrykandpatrick.vico.views.theme.ThemeHandler /** @@ -37,6 +41,23 @@ public class ChartView @JvmOverloads constructor( ) { init { chart = themeHandler.chart - if (isInEditMode) setModel(model = RandomEntriesGenerator().randomEntryModel()) + if (isInEditMode && attrs != null) setPreviewModel(attrs, defStyleAttr) + } + + private fun setPreviewModel(attrs: AttributeSet, defStyleAttr: Int) { + context.obtainStyledAttributes(attrs, R.styleable.ChartView, defStyleAttr, 0).use { typedArray -> + val seriesCount = typedArray.getInt(R.styleable.ChartView_previewSeriesCount, SERIES_COUNT) + val minX = typedArray.getInt(R.styleable.ChartView_previewMinX, 0) + val maxX = typedArray.getInt(R.styleable.ChartView_previewMaxX, X_RANGE_TOP) + val minY = typedArray.getInt(R.styleable.ChartView_previewMinY, 0) + val maxY = typedArray.getInt(R.styleable.ChartView_previewMaxY, Y_RANGE_TOP) + setModel( + model = RandomEntriesGenerator( + xRange = minX..maxX, + yRange = minY..maxY, + seriesCount = seriesCount, + ).randomEntryModel(), + ) + } } } diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ComposedChartView.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ComposedChartView.kt index 535e7b0c7..efe479189 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ComposedChartView.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ComposedChartView.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. @@ -21,6 +21,11 @@ import android.util.AttributeSet import com.patrykandpatrick.vico.core.chart.composed.ComposedChartEntryModel import com.patrykandpatrick.vico.core.entry.ChartEntryModel import com.patrykandpatrick.vico.core.util.RandomEntriesGenerator +import com.patrykandpatrick.vico.core.util.RandomEntriesGenerator.Companion.MODEL_COUNT +import com.patrykandpatrick.vico.core.util.RandomEntriesGenerator.Companion.SERIES_COUNT +import com.patrykandpatrick.vico.core.util.RandomEntriesGenerator.Companion.X_RANGE_TOP +import com.patrykandpatrick.vico.core.util.RandomEntriesGenerator.Companion.Y_RANGE_TOP +import com.patrykandpatrick.vico.views.R import com.patrykandpatrick.vico.views.theme.ThemeHandler /** @@ -39,6 +44,25 @@ public class ComposedChartView @JvmOverloads constructor( init { chart = themeHandler.composedChart - if (isInEditMode) setModel(model = RandomEntriesGenerator().randomComposedEntryModel()) + if (isInEditMode && attrs != null) setPreviewModel(attrs, defStyleAttr) + } + + private fun setPreviewModel(attrs: AttributeSet, defStyleAttr: Int) { + context.obtainStyledAttributes(attrs, R.styleable.ComposedChartView, defStyleAttr, 0).use { typedArray -> + val modelCount = typedArray.getInt(R.styleable.ComposedChartView_previewModelCount, MODEL_COUNT) + val seriesCount = typedArray.getInt(R.styleable.ComposedChartView_previewSeriesCount, SERIES_COUNT) + val minX = typedArray.getInt(R.styleable.ComposedChartView_previewMinX, 0) + val maxX = typedArray.getInt(R.styleable.ComposedChartView_previewMaxX, X_RANGE_TOP) + val minY = typedArray.getInt(R.styleable.ComposedChartView_previewMinY, 0) + val maxY = typedArray.getInt(R.styleable.ComposedChartView_previewMaxY, Y_RANGE_TOP) + setModel( + model = RandomEntriesGenerator( + xRange = minX..maxX, + yRange = minY..maxY, + seriesCount = seriesCount, + modelCount = modelCount, + ).randomComposedEntryModel(), + ) + } } } diff --git a/vico/views/src/main/res/values/attrs.xml b/vico/views/src/main/res/values/attrs.xml index ce777169a..a4805609a 100644 --- a/vico/views/src/main/res/values/attrs.xml +++ b/vico/views/src/main/res/values/attrs.xml @@ -22,6 +22,11 @@ + + + + + @@ -35,6 +40,11 @@ + + + + + @@ -43,6 +53,12 @@ + + + + + + From 2be6a7884cab61b12287980c4de71492bd86585f Mon Sep 17 00:00:00 2001 From: Patryk Goworowski Date: Sun, 15 Oct 2023 19:04:17 +0200 Subject: [PATCH 02/39] Add and use `FillStyle` for the line and its background appearance in `LineChart` --- .../LineChartsWithNegativeValuesPreviews.kt | 131 +++++++++++ .../vico/sample/showcase/ShowcaseScreen.kt | 2 + .../vico/sample/showcase/ShowcaseViewModel.kt | 9 + .../vico/sample/showcase/charts/Chart9.kt | 168 ++++++++++++++ sample/src/main/res/layout/chart_9.xml | 39 ++++ .../main/res/values-night/chart_9_styles.xml | 20 ++ sample/src/main/res/values-night/colors.xml | 19 ++ sample/src/main/res/values/chart_9_styles.xml | 77 +++++++ sample/src/main/res/values/colors.xml | 19 ++ sample/src/main/res/values/themes.xml | 6 +- versions.gradle | 2 +- .../vico/compose/chart/fill/FillStyles.kt | 58 +++++ .../vico/core/chart/LineSpecExtensions.kt | 76 ++++++- .../vico/core/chart/fill/FillStyle.kt | 211 ++++++++++++++++++ .../vico/core/chart/line/LineChart.kt | 161 ++++++++----- .../shape/shader/HorizontalSplitShader.kt | 207 +++++++++++++++++ .../views/theme/ComponentStyleExtensions.kt | 58 +++-- vico/views/src/main/res/values/attrs.xml | 2 + .../src/main/res/values/line_chart_attrs.xml | 6 + 19 files changed, 1202 insertions(+), 69 deletions(-) create mode 100644 sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartsWithNegativeValuesPreviews.kt create mode 100644 sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart9.kt create mode 100644 sample/src/main/res/layout/chart_9.xml create mode 100644 sample/src/main/res/values-night/chart_9_styles.xml create mode 100644 sample/src/main/res/values-night/colors.xml create mode 100644 sample/src/main/res/values/chart_9_styles.xml create mode 100644 sample/src/main/res/values/colors.xml create mode 100644 vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/fill/FillStyles.kt create mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/fill/FillStyle.kt create mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/HorizontalSplitShader.kt diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartsWithNegativeValuesPreviews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartsWithNegativeValuesPreviews.kt new file mode 100644 index 000000000..aaff9e7c2 --- /dev/null +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartsWithNegativeValuesPreviews.kt @@ -0,0 +1,131 @@ +/* + * 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.sample.previews + +import androidx.compose.foundation.layout.height +import androidx.compose.material.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.patrykandpatrick.vico.compose.axis.axisLineComponent +import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis +import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis +import com.patrykandpatrick.vico.compose.chart.Chart +import com.patrykandpatrick.vico.compose.chart.fill.split +import com.patrykandpatrick.vico.compose.chart.layout.fullWidth +import com.patrykandpatrick.vico.compose.chart.line.lineChart +import com.patrykandpatrick.vico.compose.chart.line.lineSpec +import com.patrykandpatrick.vico.compose.component.textComponent +import com.patrykandpatrick.vico.core.axis.AxisItemPlacer +import com.patrykandpatrick.vico.core.chart.fill.FillStyle +import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout +import com.patrykandpatrick.vico.core.chart.values.AxisValuesOverrider +import com.patrykandpatrick.vico.core.entry.entryModelOf +import com.patrykandpatrick.vico.sample.showcase.rememberMarker + +private val model = entryModelOf(-2f, -1f, 4f, -2f, 1f, 5f, -3f) + +@Preview +@Composable +public fun SingleLineChartWithNegativeValues() { + val marker = rememberMarker() + Surface { + Chart( + modifier = Modifier.height(250.dp), + chart = lineChart( + lines = listOf( + lineSpec( + lineFill = FillStyle.split( + positiveColor = Color(0xFF25BE53), + negativeColor = Color(0xFFE73B3B), + ), + ), + ), + persistentMarkers = mapOf( + 2f to marker, + 3f to marker, + ), + ), + model = model, + startAxis = rememberStartAxis( + itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 4) }, + guideline = axisLineComponent(), + ), + bottomAxis = rememberBottomAxis( + guideline = axisLineComponent(), + itemPlacer = AxisItemPlacer.Horizontal.default( + spacing = 2, + ), + ), + horizontalLayout = HorizontalLayout.fullWidth(), + ) + } +} + +@Preview +@Composable +public fun SingleLineChartWithNegativeValuesAndDataLabels() { + Surface { + Chart( + chart = lineChart( + lines = listOf(lineSpec(lineColor = Color.DarkGray, dataLabel = textComponent())), + ), + model = model, + startAxis = rememberStartAxis(), + bottomAxis = rememberBottomAxis(), + ) + } +} + +@Preview +@Composable +public fun SingleLineChartWithNegativeValuesAndAxisValuesOverridden() { + Surface { + Chart( + chart = lineChart( + axisValuesOverrider = AxisValuesOverrider.fixed( + minY = 1f, + maxY = 4f, + ), + ), + model = model, + startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 4) }), + bottomAxis = rememberBottomAxis(), + ) + } +} + +@Preview +@Composable +public fun SingleLineChartWithNegativeValuesAndAxisValuesOverridden2() { + Surface { + Chart( + chart = lineChart( + axisValuesOverrider = AxisValuesOverrider.fixed( + minY = -2f, + maxY = 0f, + ), + ), + model = model, + startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 3) }), + bottomAxis = rememberBottomAxis(), + ) + } +} diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseScreen.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseScreen.kt index 69658eec8..db24809f5 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseScreen.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseScreen.kt @@ -47,6 +47,7 @@ import com.patrykandpatrick.vico.sample.showcase.charts.Chart5 import com.patrykandpatrick.vico.sample.showcase.charts.Chart6 import com.patrykandpatrick.vico.sample.showcase.charts.Chart7 import com.patrykandpatrick.vico.sample.showcase.charts.Chart8 +import com.patrykandpatrick.vico.sample.showcase.charts.Chart9 import com.patrykandpatrick.vico.sample.utils.plus @Composable @@ -89,6 +90,7 @@ internal fun ShowcaseScreen(viewModel: ShowcaseViewModel = viewModel()) { } private fun LazyListScope.chartItems(uiSystem: UISystem, viewModel: ShowcaseViewModel) { + cardItem { Chart9(uiSystem, viewModel.positiveAndNegativeChartEntryModelProducer) } cardItem { Chart1(uiSystem, viewModel.customStepChartEntryModelProducer) } cardItem { Chart2(uiSystem, viewModel.chartEntryModelProducer) } cardItem { Chart3(uiSystem, viewModel.chartEntryModelProducer) } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseViewModel.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseViewModel.kt index 48844068b..21eae0e0f 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseViewModel.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseViewModel.kt @@ -41,8 +41,15 @@ internal class ShowcaseViewModel : ViewModel() { yRange = GENERATOR_Y_RANGE_BOTTOM..GENERATOR_Y_RANGE_TOP, ) + private val positiveAndNegativeValuesGenerator = RandomEntriesGenerator( + xRange = 0..GENERATOR_X_RANGE_TOP, + yRange = -10..GENERATOR_Y_RANGE_TOP, + ) + internal val chartEntryModelProducer: ChartEntryModelProducer = ChartEntryModelProducer() + internal val positiveAndNegativeChartEntryModelProducer: ChartEntryModelProducer = ChartEntryModelProducer() + internal val customStepChartEntryModelProducer: ChartEntryModelProducer = ChartEntryModelProducer() internal val multiDataSetChartEntryModelProducer: ChartEntryModelProducer = ChartEntryModelProducer() @@ -58,6 +65,8 @@ internal class ShowcaseViewModel : ViewModel() { val randomSeries = generator.generateRandomEntries() val randomDataSet = List(MULTI_ENTRIES_COMBINED) { generator.generateRandomEntries() } chartEntryModelProducer.setEntries(randomSeries) + positiveAndNegativeChartEntryModelProducer + .setEntries(positiveAndNegativeValuesGenerator.generateRandomEntries()) multiDataSetChartEntryModelProducer.setEntries(randomDataSet) customStepChartEntryModelProducer.setEntries(customStepGenerator.generateRandomEntries()) composedChartEntryModelProducer.runTransaction { diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart9.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart9.kt new file mode 100644 index 000000000..ce58fe842 --- /dev/null +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart9.kt @@ -0,0 +1,168 @@ +/* + * 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.sample.showcase.charts + +import android.graphics.PorterDuff +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidViewBinding +import com.patrykandpatrick.vico.R +import com.patrykandpatrick.vico.compose.axis.axisLabelComponent +import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis +import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis +import com.patrykandpatrick.vico.compose.chart.Chart +import com.patrykandpatrick.vico.compose.chart.fill.split +import com.patrykandpatrick.vico.compose.chart.fill.splitShader +import com.patrykandpatrick.vico.compose.chart.layout.fullWidth +import com.patrykandpatrick.vico.compose.chart.line.lineChart +import com.patrykandpatrick.vico.compose.chart.line.lineSpec +import com.patrykandpatrick.vico.compose.component.lineComponent +import com.patrykandpatrick.vico.compose.component.shape.dashedShape +import com.patrykandpatrick.vico.compose.component.shape.shader.fromComponent +import com.patrykandpatrick.vico.compose.component.shape.shader.verticalGradient +import com.patrykandpatrick.vico.compose.component.shapeComponent +import com.patrykandpatrick.vico.compose.dimensions.dimensionsOf +import com.patrykandpatrick.vico.compose.style.ProvideChartStyle +import com.patrykandpatrick.vico.core.axis.Axis +import com.patrykandpatrick.vico.core.axis.AxisItemPlacer +import com.patrykandpatrick.vico.core.chart.fill.FillStyle +import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout +import com.patrykandpatrick.vico.core.component.shape.Shapes +import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders +import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer +import com.patrykandpatrick.vico.databinding.Chart9Binding +import com.patrykandpatrick.vico.sample.showcase.UISystem +import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle +import com.patrykandpatrick.vico.sample.showcase.rememberMarker + +@Composable +internal fun Chart9(uiSystem: UISystem, chartEntryModelProducer: ChartEntryModelProducer) { + when (uiSystem) { + UISystem.Compose -> ComposeChart9(chartEntryModelProducer) + UISystem.Views -> ViewChart9(chartEntryModelProducer) + } +} + +@Composable +private fun ComposeChart9(chartEntryModelProducer: ChartEntryModelProducer) { + val marker = rememberMarker() + ProvideChartStyle(rememberChartStyle(chartColors)) { + Chart( + chart = lineChart( + lines = listOf( + lineSpec( + lineFill = FillStyle.split(chartColors[0], chartColors[1]), +// lineBackgroundFill = FillStyle.brush( +// Brush.horizontalGradient(listOf(chartColors[0], chartColors[1])) +// ), +// lineBackgroundFill = FillStyle.split(chartColors[0], chartColors[1]), + lineBackgroundFill = FillStyle.splitShader( + DynamicShaders.composeShader( + DynamicShaders.fromComponent( + componentSize = 6.dp, + component = shapeComponent( + shape = Shapes.pillShape, + color = chartColors[0], + margins = remember { dimensionsOf(1.dp) }, + ), + ), + verticalGradient(arrayOf(Color.Black, Color.Transparent)), + PorterDuff.Mode.DST_IN, + ), + DynamicShaders.composeShader( + DynamicShaders.fromComponent( + componentSize = 5.dp, + component = shapeComponent( + shape = Shapes.rectShape, + color = chartColors[1], + margins = remember { dimensionsOf(horizontal = 2.dp) }, + ), + checkeredArrangement = false, + ), + verticalGradient(arrayOf(Color.Transparent, Color.Black)), + PorterDuff.Mode.DST_IN, + ), + ), + ), + ), + ), + chartModelProducer = chartEntryModelProducer, + startAxis = rememberStartAxis( + label = axisLabelComponent( + color = MaterialTheme.colorScheme.onBackground, + background = shapeComponent( + shape = Shapes.pillShape, + color = MaterialTheme.colorScheme.background, + strokeColor = MaterialTheme.colorScheme.outlineVariant, + strokeWidth = 1.dp, + ), + padding = remember { dimensionsOf(horizontal = 6.dp, vertical = 2.dp) }, + margins = remember { dimensionsOf(end = 8.dp) }, + ), + axis = null, + tick = null, + guideline = lineComponent( + thickness = 1.dp, + color = MaterialTheme.colorScheme.outlineVariant, + shape = remember { + Shapes.dashedShape( + shape = Shapes.pillShape, + dashLength = 4.dp, + gapLength = 8.dp, + ) + }, + ), + itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 4) }, + ), + bottomAxis = rememberBottomAxis( + guideline = null, + itemPlacer = AxisItemPlacer.Horizontal.default( + spacing = 3, + ), + ), + marker = marker, + runInitialAnimation = false, + horizontalLayout = HorizontalLayout.fullWidth(), + ) + } +} + +@Composable +private fun ViewChart9(chartEntryModelProducer: ChartEntryModelProducer) { + val marker = rememberMarker() + AndroidViewBinding(Chart9Binding::inflate) { + with(chartView) { + runInitialAnimation = false + entryProducer = chartEntryModelProducer + (bottomAxis as Axis).guideline = null + this.marker = marker + } + } +} + +private val chartColors + @ReadOnlyComposable + @Composable + get() = listOf( + colorResource(id = R.color.chart_9_color_positive), + colorResource(id = R.color.chart_9_color_negative), + ) diff --git a/sample/src/main/res/layout/chart_9.xml b/sample/src/main/res/layout/chart_9.xml new file mode 100644 index 000000000..81a618baa --- /dev/null +++ b/sample/src/main/res/layout/chart_9.xml @@ -0,0 +1,39 @@ + + + + + + diff --git a/sample/src/main/res/values-night/chart_9_styles.xml b/sample/src/main/res/values-night/chart_9_styles.xml new file mode 100644 index 000000000..5097869de --- /dev/null +++ b/sample/src/main/res/values-night/chart_9_styles.xml @@ -0,0 +1,20 @@ + + + + #C7FF6C + #A46CFF + diff --git a/sample/src/main/res/values-night/colors.xml b/sample/src/main/res/values-night/colors.xml new file mode 100644 index 000000000..e0ae485e3 --- /dev/null +++ b/sample/src/main/res/values-night/colors.xml @@ -0,0 +1,19 @@ + + + + #000 + diff --git a/sample/src/main/res/values/chart_9_styles.xml b/sample/src/main/res/values/chart_9_styles.xml new file mode 100644 index 000000000..378a4ae60 --- /dev/null +++ b/sample/src/main/res/values/chart_9_styles.xml @@ -0,0 +1,77 @@ + + + + #FF3716FF + #FFFF166A + + + + + + + + + + + + + + + + + + + + + diff --git a/sample/src/main/res/values/colors.xml b/sample/src/main/res/values/colors.xml new file mode 100644 index 000000000..daa68970b --- /dev/null +++ b/sample/src/main/res/values/colors.xml @@ -0,0 +1,19 @@ + + + + #FFF5F5F7 + diff --git a/sample/src/main/res/values/themes.xml b/sample/src/main/res/values/themes.xml index 3059aed03..95eb13609 100644 --- a/sample/src/main/res/values/themes.xml +++ b/sample/src/main/res/values/themes.xml @@ -1,7 +1,7 @@ - - - @@ -167,12 +163,6 @@ - - - - - - From 6de3863a9bdd6ae3aac18d7a165e2151e92d583e Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Fri, 1 Dec 2023 18:22:47 +0100 Subject: [PATCH 14/39] Remove Detekt in favor of Ktlint --- .editorconfig | 2 + .../{run-detekt.yml => run-ktlint.yml} | 13 +- .idea/detekt-config.yml | 71 --- .idea/detekt.xml | 10 - .idea/jsonSchemas.xml | 25 - build.gradle.kts | 21 +- gradle/libs.versions.toml | 3 - .../vico/sample/MainActivity.kt | 1 - .../patrykandpatrick/vico/sample/VicoApp.kt | 7 +- .../vico/sample/previews/AxisLinePreviews.kt | 170 +++--- .../ColumnChartsWithNegativeValuesPreviews.kt | 43 +- .../sample/previews/ContainedChartsPreview.kt | 72 +-- .../vico/sample/previews/LineChartPreviews.kt | 111 ++-- .../LineChartsWithNegativeValuesPreviews.kt | 83 +-- .../vico/sample/previews/Previews.kt | 203 +++---- .../vico/sample/previews/ShapePreviews.kt | 69 +-- ...dColumnChartsWithNegativeValuesPreviews.kt | 81 +-- .../sample/previews/ThresholdLinePreviews.kt | 269 +++++---- .../composables/column/ColumnCharts.kt | 11 +- .../vico/sample/showcase/ChartStyle.kt | 18 +- .../vico/sample/showcase/Marker.kt | 57 +- .../vico/sample/showcase/ShowcaseScreen.kt | 14 +- .../vico/sample/showcase/ShowcaseViewModel.kt | 28 +- .../vico/sample/showcase/UISystem.kt | 5 +- .../vico/sample/showcase/charts/Chart1.kt | 5 +- .../vico/sample/showcase/charts/Chart2.kt | 47 +- .../vico/sample/showcase/charts/Chart3.kt | 49 +- .../vico/sample/showcase/charts/Chart4.kt | 39 +- .../vico/sample/showcase/charts/Chart5.kt | 57 +- .../vico/sample/showcase/charts/Chart6.kt | 48 +- .../vico/sample/showcase/charts/Chart7.kt | 79 +-- .../vico/sample/showcase/charts/Chart8.kt | 14 +- .../vico/sample/showcase/charts/Chart9.kt | 208 +++---- .../vico/sample/utils/PaddingValues.kt | 17 +- .../vico/sample/utils/VicoTheme.kt | 18 +- .../vico/sample/PaparazziTest.kt | 22 +- .../vico/compose/m2/style/M2ChartStyle.kt | 42 +- .../vico/compose/m3/style/M3ChartStyle.kt | 44 +- .../vico/compose/axis/AxisComponents.kt | 155 +++--- .../compose/axis/vertical/VerticalAxis.kt | 62 ++- .../vico/compose/chart/Charts.kt | 134 ++--- .../vico/compose/chart/column/ColumnChart.kt | 31 +- .../chart/edges/FadingEdgesExtensions.kt | 42 +- .../chart/entry/ChartEntryModelExtensions.kt | 50 +- .../compose/chart/layout/HorizontalLayout.kt | 13 +- .../vico/compose/chart/line/LineChart.kt | 176 +++--- .../compose/chart/scroll/ChartScrollSpec.kt | 39 +- .../compose/chart/scroll/ChartScrollState.kt | 34 +- .../vico/compose/component/Components.kt | 179 +++--- .../compose/component/dimension/Padding.kt | 55 +- .../component/marker/MarkerComponent.kt | 13 +- .../vico/compose/component/shape/Shapes.kt | 247 +++++---- .../component/shape/shader/BrushShader.kt | 12 +- .../component/shape/shader/DynamicShaders.kt | 21 +- .../shape/shader/LinearGradientShader.kt | 78 +-- .../compose/dimensions/DimensionExtensions.kt | 41 +- .../compose/extension/ComponentExtensions.kt | 21 +- .../vico/compose/gesture/Zoomable.kt | 33 +- .../layout/MeasureContextExtensions.kt | 33 +- .../vico/compose/legend/Legends.kt | 55 +- .../compose/state/ChartEntryModelWrapper.kt | 5 +- .../vico/compose/style/ChartStyle.kt | 96 ++-- .../patrykandpatrick/vico/core/Defaults.kt | 4 - .../patrykandpatrick/vico/core/axis/Axis.kt | 9 +- .../vico/core/axis/AxisItemPlacer.kt | 11 +- .../vico/core/axis/AxisManager.kt | 3 +- .../vico/core/axis/AxisPosition.kt | 11 +- .../vico/core/axis/AxisRenderer.kt | 6 +- .../DecimalFormatAxisValueFormatter.kt | 3 +- .../PercentageFormatAxisValueFormatter.kt | 3 +- .../DefaultHorizontalAxisItemPlacer.kt | 3 - .../core/axis/horizontal/HorizontalAxis.kt | 327 +++++------ .../vertical/DefaultVerticalAxisItemPlacer.kt | 28 +- .../vico/core/axis/vertical/VerticalAxis.kt | 307 ++++++----- .../vico/core/chart/BaseChart.kt | 77 +-- .../patrykandpatrick/vico/core/chart/Chart.kt | 20 +- .../vico/core/chart/DefaultPointConnector.kt | 7 +- .../vico/core/chart/EntryModelExtensions.kt | 5 +- .../vico/core/chart/LineSpecExtensions.kt | 27 +- .../vico/core/chart/column/ColumnChart.kt | 172 +++--- .../chart/column/ColumnChartDrawingModel.kt | 6 +- .../vico/core/chart/composed/ComposedChart.kt | 36 +- .../chart/composed/ComposedChartEntryModel.kt | 3 +- .../chart/composed/ComposedChartExtensions.kt | 13 +- .../vico/core/chart/decoration/Decoration.kt | 13 +- .../core/chart/decoration/ThresholdLine.kt | 107 ++-- .../chart/dimensions/HorizontalDimensions.kt | 30 +- .../dimensions/MutableHorizontalDimensions.kt | 30 +- .../vico/core/chart/draw/ChartDrawContext.kt | 8 +- .../chart/draw/ChartDrawContextExtensions.kt | 37 +- .../vico/core/chart/edges/FadingEdges.kt | 79 +-- .../vico/core/chart/insets/ChartInsetter.kt | 1 - .../core/chart/insets/HorizontalInsets.kt | 13 +- .../vico/core/chart/insets/Insets.kt | 46 +- .../vico/core/chart/line/LineChart.kt | 299 +++++----- .../core/chart/line/LineChartDrawingModel.kt | 6 +- .../vico/core/chart/scale/AutoScaleUp.kt | 1 - .../core/chart/values/AxisValuesOverrider.kt | 42 +- .../vico/core/chart/values/ChartValues.kt | 4 +- .../core/chart/values/ChartValuesManager.kt | 19 +- .../core/chart/values/ChartValuesProvider.kt | 1 - .../core/chart/values/MutableChartValues.kt | 57 +- .../vico/core/collections/ArrayDelegates.kt | 13 +- .../vico/core/component/Component.kt | 1 - .../core/component/OverlayingComponent.kt | 32 +- .../vico/core/component/dimension/Margins.kt | 7 +- .../vico/core/component/dimension/Padding.kt | 7 +- .../core/component/marker/MarkerComponent.kt | 88 +-- .../vico/core/component/shape/DashedShape.kt | 96 ++-- .../core/component/shape/LineComponent.kt | 94 ++-- .../vico/core/component/shape/Shape.kt | 1 - .../core/component/shape/ShapeComponent.kt | 96 ++-- .../vico/core/component/shape/Shapes.kt | 188 +++---- .../core/component/shape/cornered/Corner.kt | 22 +- .../shape/cornered/CornerTreatment.kt | 1 - .../shape/cornered/CornerTreatments.kt | 32 +- .../component/shape/cornered/CorneredShape.kt | 7 +- .../shape/cornered/MarkerCorneredShape.kt | 64 +-- .../shape/shader/CacheableDynamicShader.kt | 3 +- .../component/shape/shader/ComponentShader.kt | 38 +- .../component/shape/shader/DynamicShader.kt | 46 +- .../component/shape/shader/DynamicShaders.kt | 50 +- .../shape/shader/HorizontalSplitShader.kt | 45 +- .../component/shape/shader/SolidShader.kt | 26 +- .../component/shape/shader/StaticShader.kt | 3 +- .../component/shape/shadow/ComponentShadow.kt | 23 +- .../vico/core/component/text/TextComponent.kt | 297 +++++----- .../core/component/text/VerticalPosition.kt | 13 +- .../text/VerticalPositionExtensions.kt | 13 +- .../vico/core/context/DefaultExtras.kt | 15 +- .../vico/core/context/DrawContext.kt | 15 +- .../vico/core/context/Extras.kt | 13 +- .../vico/core/context/MeasureContext.kt | 1 - .../core/context/MutableMeasureContext.kt | 1 - .../vico/core/debug/DebugHelper.kt | 22 +- .../vico/core/dimensions/BoundsAware.kt | 3 +- .../vico/core/dimensions/Dimensions.kt | 3 +- .../vico/core/dimensions/MutableDimensions.kt | 38 +- .../vico/core/draw/DrawContextExtensions.kt | 44 +- .../vico/core/entry/ChartEntry.kt | 3 +- .../vico/core/entry/ChartEntryExtensions.kt | 29 +- .../vico/core/entry/ChartEntryModel.kt | 1 - .../core/entry/ChartEntryModelProducer.kt | 31 +- .../vico/core/entry/ChartModelProducer.kt | 6 +- .../vico/core/entry/FloatEntry.kt | 12 +- .../ComposedChartEntryModelProducer.kt | 511 +++++++++--------- .../diff/DefaultDrawingModelInterpolator.kt | 57 +- .../vico/core/entry/diff/DrawingModel.kt | 6 +- .../entry/diff/DrawingModelInterpolator.kt | 5 +- .../vico/core/entry/diff/ExtraStore.kt | 18 +- .../core/extension/CollectionExtensions.kt | 14 +- .../vico/core/extension/ColorExtensions.kt | 27 +- .../core/extension/ColorOverlayExtensions.kt | 33 +- .../vico/core/extension/ColorUtils.kt | 18 +- .../vico/core/extension/CommonExtensions.kt | 13 +- .../vico/core/extension/DrawableExtensions.kt | 9 +- .../vico/core/extension/MapExtensions.kt | 13 +- .../vico/core/extension/NumberExtensions.kt | 30 +- .../vico/core/extension/PaintExtensions.kt | 8 +- .../vico/core/extension/RectExtensions.kt | 18 +- .../core/extension/ReflectionExtensions.kt | 7 +- .../formatter/DecimalFormatValueFormatter.kt | 11 +- .../PercentageFormatValueFormatter.kt | 4 +- .../vico/core/formatter/ValueFormatter.kt | 3 +- .../vico/core/layout/VirtualLayout.kt | 99 ++-- .../vico/core/legend/HorizontalLegend.kt | 164 +++--- .../vico/core/legend/Legend.kt | 8 +- .../vico/core/legend/LegendItem.kt | 47 +- .../vico/core/legend/VerticalLegend.kt | 97 ++-- .../marker/DefaultMarkerLabelFormatter.kt | 30 +- .../vico/core/marker/Marker.kt | 1 - .../vico/core/marker/MarkerLabelFormatter.kt | 1 - .../marker/MarkerVisibilityChangeListener.kt | 1 - .../patrykandpatrick/vico/core/model/Point.kt | 11 +- .../vico/core/scroll/AutoScrollCondition.kt | 34 +- .../vico/core/scroll/ScrollHandler.kt | 10 +- .../vico/core/scroll/ScrollListener.kt | 11 +- .../vico/core/scroll/ScrollListenerHost.kt | 3 +- .../vico/core/text/StaticLayoutExtensions.kt | 9 +- .../vico/core/util/InlineClassHelper.kt | 15 +- .../vico/core/util/RandomEntriesGenerator.kt | 10 +- .../vico/core/util/ValueWrapper.kt | 11 +- .../vico/core/ChartEntryModelProducerTest.kt | 1 - .../vico/core/CollectionExtensionsTest.kt | 57 +- .../core/extension/RectFExtensionsTest.kt | 3 +- .../vico/views/chart/BaseChartView.kt | 148 +++-- .../vico/views/chart/ChartView.kt | 61 ++- .../vico/views/chart/ComposedChartView.kt | 66 +-- .../chart/column/ColumnChartExtensions.kt | 41 +- .../views/chart/line/LineChartExtensions.kt | 24 +- .../views/component/shape/ShapeDrawable.kt | 19 +- .../vico/views/component/shape/Shapes.kt | 34 +- .../component/shape/shader/DynamicShaders.kt | 127 +++-- .../views/dimensions/DimensionsExtensions.kt | 41 +- .../vico/views/extension/ContextExtensions.kt | 11 +- .../vico/views/extension/ViewExtensions.kt | 5 +- .../gestures/ChartScaleGestureListener.kt | 3 +- .../vico/views/gestures/MotionEventHandler.kt | 5 +- .../vico/views/scroll/ChartScrollSpec.kt | 45 +- .../vico/views/theme/ChartStyleExtensions.kt | 210 +++---- .../views/theme/ComponentStyleExtensions.kt | 347 ++++++------ .../vico/views/theme/ShapeStyleExtensions.kt | 140 ++--- .../theme/TextComponentStyleExtensions.kt | 121 +++-- .../vico/views/theme/ThemeHandler.kt | 258 +++++---- .../vico/views/theme/TypedArrayExtensions.kt | 42 +- 205 files changed, 5497 insertions(+), 4797 deletions(-) rename .github/workflows/{run-detekt.yml => run-ktlint.yml} (52%) delete mode 100644 .idea/detekt-config.yml delete mode 100644 .idea/detekt.xml delete mode 100644 .idea/jsonSchemas.xml diff --git a/.editorconfig b/.editorconfig index 0f2a4eb72..13aee403c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,3 +11,5 @@ indent_size = 2 [*.{kts,kt}] ij_kotlin_allow_trailing_comma = true ij_kotlin_allow_trailing_comma_on_call_site = true +ktlint_function_naming_ignore_when_annotated_with = Composable +max_line_length = 120 diff --git a/.github/workflows/run-detekt.yml b/.github/workflows/run-ktlint.yml similarity index 52% rename from .github/workflows/run-detekt.yml rename to .github/workflows/run-ktlint.yml index cecfbcdaa..bd04d3fea 100644 --- a/.github/workflows/run-detekt.yml +++ b/.github/workflows/run-ktlint.yml @@ -1,16 +1,19 @@ -name: Run Detekt +name: Run Ktlint on: push: pull_request: jobs: - run-detekt: + run-ktlint: runs-on: ubuntu-latest continue-on-error: true steps: - - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: java-version: 17 distribution: zulu - - uses: gradle/gradle-build-action@v2 - - run: ./gradlew detekt + - run: | + curl -sSLO https://github.com/pinterest/ktlint/releases/download/1.0.1/ktlint + chmod +x ktlint + sudo mv ktlint /usr/local/bin + - uses: actions/checkout@v3 + - run: ktlint diff --git a/.idea/detekt-config.yml b/.idea/detekt-config.yml deleted file mode 100644 index 25b3e15ba..000000000 --- a/.idea/detekt-config.yml +++ /dev/null @@ -1,71 +0,0 @@ -comments: - active: true - UndocumentedPublicClass: - active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/commonTest/**" - - "**/jvmTest/**" - - "**/jsTest/**" - - "**/iosTest/**" - - "**/com/patrykandpatrick/vico/sample/**" - ignoreAnnotated: - - "RestrictTo" - UndocumentedPublicFunction: - active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/commonTest/**" - - "**/jvmTest/**" - - "**/jsTest/**" - - "**/iosTest/**" - - "**/com/patrykandpatrick/vico/sample/**" - ignoreAnnotated: - - "RestrictTo" - UndocumentedPublicProperty: - active: true - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/commonTest/**" - - "**/jvmTest/**" - - "**/jsTest/**" - - "**/iosTest/**" - - "**/com/patrykandpatrick/vico/sample/**" - ignoreAnnotated: - - "RestrictTo" -complexity: - active: false -style: - MagicNumber: - excludes: - - "**/test/**" - - "**/androidTest/**" - - "**/commonTest/**" - - "**/jvmTest/**" - - "**/jsTest/**" - - "**/iosTest/**" - - "**/previews/**" - - "**/Preview.kt" - - "**/Previews.kt" - ignoreNumbers: - - "-1" - - "0" - - "1" - - "2" - - "100" - UnnecessaryParentheses: - active: true - allowForUnclearPrecedence: true -naming: - FunctionNaming: - ignoreAnnotated: - - "Composable" -formatting: - active: true - TrailingCommaOnCallSite: - active: true - TrailingCommaOnDeclarationSite: - active: true diff --git a/.idea/detekt.xml b/.idea/detekt.xml deleted file mode 100644 index e23219b8e..000000000 --- a/.idea/detekt.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - true - true - true - true - $PROJECT_DIR$/.idea/detekt-config.yml - - \ No newline at end of file diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml deleted file mode 100644 index 1a3417a83..000000000 --- a/.idea/jsonSchemas.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/build.gradle.kts b/build.gradle.kts index eeb60f332..4711f5504 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,8 +14,6 @@ * limitations under the License. */ -import io.gitlab.arturbosch.detekt.Detekt - buildscript { dependencies { classpath(libs.buildTools) @@ -24,25 +22,8 @@ buildscript { } } -plugins { - alias(libs.plugins.detekt) - alias(libs.plugins.dokka) apply false -} +plugins { alias(libs.plugins.dokka) apply false } apply("versions.gradle") -dependencies { detektPlugins(libs.detektFormatting) } - tasks.register("clean") { delete(rootProject.layout.buildDirectory) } - -tasks.register("detektFix") { autoCorrect = true } - -tasks.withType().configureEach { - source = fileTree(projectDir) - config = files(".idea/detekt-config.yml") - buildUponDefaultConfig = true - reports.html { - required = true - outputLocation = file("build/reports/detekt/detekt.html") - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 94bf0bfc5..204ecdb72 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,6 @@ appcompat = "1.6.1" composeBom = "2023.10.01" composeCompiler = "1.5.4" coroutines = "1.7.3" -detekt = "1.23.3" dokka = "1.9.10" jUnit = "4.13.2" jUnitExt = "1.1.5" @@ -33,7 +32,6 @@ composeUI = { group = "androidx.compose.ui", name = "ui" } composeUITooling = { group = "androidx.compose.ui", name = "ui-tooling" } composeViewBinding = { group = "androidx.compose.ui", name = "ui-viewbinding" } coroutinesCore = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" } -detektFormatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" } jUnit = { group = "junit", name = "junit", version.ref = "jUnit" } jUnitExt = { group = "androidx.test.ext", name = "junit", version.ref = "jUnitExt" } kotlinGradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } @@ -48,5 +46,4 @@ testCore = { group = "androidx.test", name = "core-ktx", version.ref = "testCore viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" } [plugins] -detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/MainActivity.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/MainActivity.kt index 072635080..e8e0ca4ed 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/MainActivity.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/MainActivity.kt @@ -22,7 +22,6 @@ import androidx.activity.compose.setContent import androidx.core.view.WindowCompat internal class MainActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/VicoApp.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/VicoApp.kt index a0b1c1071..bf030c524 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/VicoApp.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/VicoApp.kt @@ -42,9 +42,10 @@ internal fun VicoApp() { } VicoTheme { Box( - modifier = Modifier - .fillMaxSize() - .background(color = MaterialTheme.colorScheme.background), + modifier = + Modifier + .fillMaxSize() + .background(color = MaterialTheme.colorScheme.background), ) { ShowcaseScreen() } } } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/AxisLinePreviews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/AxisLinePreviews.kt index 2751f9203..e3b3c2568 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/AxisLinePreviews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/AxisLinePreviews.kt @@ -42,24 +42,28 @@ private val model = entryModelOf(1, 2, 3, 4) @Composable private fun ProvidePreviewChartStyle(content: @Composable () -> Unit) { - val chartStyle = LocalChartStyle.current.copy( - axis = LocalChartStyle.current.axis.copy( - axisLabelColor = Color.Black, - axisLineColor = Color.Black.copy(alpha = 0.5f), - axisGuidelineColor = Color.Black.copy(alpha = 0.2f), - ), - columnChart = LocalChartStyle.current.columnChart.copy( - columns = LocalChartStyle.current.columnChart.columns.map { - lineComponent( - color = Color.Gray, - thickness = it.thicknessDp.dp, - shape = it.shape, - dynamicShader = it.dynamicShader, - margins = it.margins, - ) - }, - ), - ) + val chartStyle = + LocalChartStyle.current.copy( + axis = + LocalChartStyle.current.axis.copy( + axisLabelColor = Color.Black, + axisLineColor = Color.Black.copy(alpha = 0.5f), + axisGuidelineColor = Color.Black.copy(alpha = 0.2f), + ), + columnChart = + LocalChartStyle.current.columnChart.copy( + columns = + LocalChartStyle.current.columnChart.columns.map { + lineComponent( + color = Color.Gray, + thickness = it.thicknessDp.dp, + shape = it.shape, + dynamicShader = it.dynamicShader, + margins = it.margins, + ) + }, + ), + ) CompositionLocalProvider(LocalChartStyle provides chartStyle, content = content) } @@ -67,39 +71,46 @@ private fun ProvidePreviewChartStyle(content: @Composable () -> Unit) { @Preview(showBackground = true, widthDp = 250) public fun HorizontalAxisTextInside() { ProvidePreviewChartStyle { - val label = axisLabelComponent( - background = shapeComponent( - shape = CorneredShape( - topLeft = Corner.Relative( - percentage = 50, - cornerTreatment = CutCornerTreatment, - ), - bottomRight = Corner.Relative( - percentage = 50, - cornerTreatment = RoundedCornerTreatment, + val label = + axisLabelComponent( + background = + shapeComponent( + shape = + CorneredShape( + topLeft = + Corner.Relative( + percentage = 50, + cornerTreatment = CutCornerTreatment, + ), + bottomRight = + Corner.Relative( + percentage = 50, + cornerTreatment = RoundedCornerTreatment, + ), + ), + color = Color.LightGray, + strokeColor = Color.Gray, + strokeWidth = 1.dp, ), - ), - color = Color.LightGray, - strokeColor = Color.Gray, - strokeWidth = 1.dp, - ), - verticalPadding = 2.dp, - horizontalPadding = 8.dp, - verticalMargin = 4.dp, - horizontalMargin = 4.dp, - ) + verticalPadding = 2.dp, + horizontalPadding = 8.dp, + verticalMargin = 4.dp, + horizontalMargin = 4.dp, + ) Chart( chart = columnChart(), model = model, - startAxis = rememberStartAxis( - horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside, - label = label, - ), - endAxis = rememberEndAxis( - horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside, - guideline = null, - label = label, - ), + startAxis = + rememberStartAxis( + horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside, + label = label, + ), + endAxis = + rememberEndAxis( + horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside, + guideline = null, + label = label, + ), ) } } @@ -108,28 +119,32 @@ public fun HorizontalAxisTextInside() { @Preview(showBackground = true, widthDp = 250) public fun HorizontalAxisTextInsideAndBottomAxis() { ProvidePreviewChartStyle { - val label = axisLabelComponent( - background = shapeComponent( - shape = Shapes.pillShape, - color = Color.LightGray, - ), - verticalPadding = 2.dp, - horizontalPadding = 8.dp, - verticalMargin = 4.dp, - horizontalMargin = 4.dp, - ) + val label = + axisLabelComponent( + background = + shapeComponent( + shape = Shapes.pillShape, + color = Color.LightGray, + ), + verticalPadding = 2.dp, + horizontalPadding = 8.dp, + verticalMargin = 4.dp, + horizontalMargin = 4.dp, + ) Chart( chart = columnChart(), model = model, - startAxis = rememberStartAxis( - horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside, - label = label, - ), - endAxis = rememberEndAxis( - horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside, - guideline = null, - label = label, - ), + startAxis = + rememberStartAxis( + horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside, + label = label, + ), + endAxis = + rememberEndAxis( + horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside, + guideline = null, + label = label, + ), bottomAxis = rememberBottomAxis(), ) } @@ -142,13 +157,15 @@ public fun HorizontalAxisTextOutside() { Chart( chart = columnChart(), model = model, - startAxis = rememberStartAxis( - horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Outside, - ), - endAxis = rememberEndAxis( - horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Outside, - guideline = null, - ), + startAxis = + rememberStartAxis( + horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Outside, + ), + endAxis = + rememberEndAxis( + horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Outside, + guideline = null, + ), ) } } @@ -160,9 +177,10 @@ public fun HorizontalAxisGuidelineDoesNotOverlayBottomAxisLine() { Chart( chart = columnChart(), model = model, - startAxis = rememberStartAxis( - horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Outside, - ), + startAxis = + rememberStartAxis( + horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Outside, + ), bottomAxis = rememberBottomAxis(), ) } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ColumnChartsWithNegativeValuesPreviews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ColumnChartsWithNegativeValuesPreviews.kt index 6c92f521d..fb60f2a0a 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ColumnChartsWithNegativeValuesPreviews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ColumnChartsWithNegativeValuesPreviews.kt @@ -42,12 +42,14 @@ public fun SingleColumnChartWithNegativeValues() { Surface { Chart( modifier = Modifier.height(250.dp), - chart = columnChart( - persistentMarkers = mapOf( - 2f to marker, - 3f to marker, + chart = + columnChart( + persistentMarkers = + mapOf( + 2f to marker, + 3f to marker, + ), ), - ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 9) }), bottomAxis = rememberBottomAxis(), @@ -60,9 +62,10 @@ public fun SingleColumnChartWithNegativeValues() { public fun SingleColumnChartWithNegativeValuesAndDataLabels() { Surface { Chart( - chart = columnChart( - dataLabel = textComponent(), - ), + chart = + columnChart( + dataLabel = textComponent(), + ), model = model, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), @@ -75,12 +78,14 @@ public fun SingleColumnChartWithNegativeValuesAndDataLabels() { public fun SingleColumnChartWithNegativeValuesAndAxisValuesOverridden() { Surface { Chart( - chart = columnChart( - axisValuesOverrider = AxisValuesOverrider.fixed( - minY = 1f, - maxY = 4f, + chart = + columnChart( + axisValuesOverrider = + AxisValuesOverrider.fixed( + minY = 1f, + maxY = 4f, + ), ), - ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 4) }), bottomAxis = rememberBottomAxis(), @@ -93,12 +98,14 @@ public fun SingleColumnChartWithNegativeValuesAndAxisValuesOverridden() { public fun SingleColumnChartWithNegativeValuesAndAxisValuesOverridden2() { Surface { Chart( - chart = columnChart( - axisValuesOverrider = AxisValuesOverrider.fixed( - minY = -2f, - maxY = 0f, + chart = + columnChart( + axisValuesOverrider = + AxisValuesOverrider.fixed( + minY = -2f, + maxY = 0f, + ), ), - ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 3) }), bottomAxis = rememberBottomAxis(), diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ContainedChartsPreview.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ContainedChartsPreview.kt index 7f603b641..986550528 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ContainedChartsPreview.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ContainedChartsPreview.kt @@ -56,52 +56,60 @@ private val markerMap: Map private fun getColumnChart( markerMap: Map = emptyMap(), targetVerticalAxisPosition: Vertical? = null, -): ColumnChart = columnChart( - columns = listOf( - lineComponent( - color = Color.Black, - thickness = 8.dp, - shape = Shapes.pillShape, - ), - ), - persistentMarkers = markerMap, - targetVerticalAxisPosition = targetVerticalAxisPosition, -) +): ColumnChart = + columnChart( + columns = + listOf( + lineComponent( + color = Color.Black, + thickness = 8.dp, + shape = Shapes.pillShape, + ), + ), + persistentMarkers = markerMap, + targetVerticalAxisPosition = targetVerticalAxisPosition, + ) @Composable private fun getLineChart( markerMap: Map = emptyMap(), targetVerticalAxisPosition: Vertical? = null, -): LineChart = lineChart( - lines = listOf( - lineSpec( - lineColor = Color.DarkGray, - lineBackgroundShader = verticalGradient( - arrayOf(Color.DarkGray, Color.DarkGray.copy(alpha = 0f)), +): LineChart = + lineChart( + lines = + listOf( + lineSpec( + lineColor = Color.DarkGray, + lineBackgroundShader = + verticalGradient( + arrayOf(Color.DarkGray, Color.DarkGray.copy(alpha = 0f)), + ), + ), ), - ), - ), - persistentMarkers = markerMap, - targetVerticalAxisPosition = targetVerticalAxisPosition, -) + persistentMarkers = markerMap, + targetVerticalAxisPosition = targetVerticalAxisPosition, + ) private val startAxis: Axis - @Composable get() = rememberStartAxis( - label = textComponent(color = Color.Black), - itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 5) }, - ) + @Composable get() = + rememberStartAxis( + label = textComponent(color = Color.Black), + itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 5) }, + ) private val endAxis: Axis - @Composable get() = rememberEndAxis( - label = textComponent(color = Color.DarkGray), - itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 7) }, - ) + @Composable get() = + rememberEndAxis( + label = textComponent(color = Color.DarkGray), + itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 7) }, + ) @Composable @Preview("Chart with independent axes", widthDp = 350) public fun ChartWithIndependentAxes(modifier: Modifier = Modifier) { - val composedChart = getColumnChart(targetVerticalAxisPosition = Start) + - getLineChart(targetVerticalAxisPosition = End) + val composedChart = + getColumnChart(targetVerticalAxisPosition = Start) + + getLineChart(targetVerticalAxisPosition = End) composedChart.setPersistentMarkers(markerMap) diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartPreviews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartPreviews.kt index 4dc2d74b7..f98dce63d 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartPreviews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartPreviews.kt @@ -51,23 +51,27 @@ public fun LineChartDark() { Chart( modifier = Modifier.padding(8.dp), - chart = lineChart( - lines = listOf( - lineSpec( - lineColor = yellow, - lineBackgroundShader = verticalGradient( - arrayOf(yellow.copy(0.5f), yellow.copy(alpha = 0f)), + chart = + lineChart( + lines = + listOf( + lineSpec( + lineColor = yellow, + lineBackgroundShader = + verticalGradient( + arrayOf(yellow.copy(0.5f), yellow.copy(alpha = 0f)), + ), + ), + lineSpec( + lineColor = pink, + lineBackgroundShader = + verticalGradient( + arrayOf(pink.copy(0.5f), pink.copy(alpha = 0f)), + ), + ), ), - ), - lineSpec( - lineColor = pink, - lineBackgroundShader = verticalGradient( - arrayOf(pink.copy(0.5f), pink.copy(alpha = 0f)), - ), - ), + axisValuesOverrider = AxisValuesOverrider.fixed(maxY = 4f), ), - axisValuesOverrider = AxisValuesOverrider.fixed(maxY = 4f), - ), model = model3, ) } @@ -87,12 +91,14 @@ public fun RegularLineChart() { @Composable public fun RegularLineChartExpanded() { Chart( - chart = lineChart( - axisValuesOverrider = AxisValuesOverrider.fixed( - minY = -1f, - maxY = 5f, + chart = + lineChart( + axisValuesOverrider = + AxisValuesOverrider.fixed( + minY = -1f, + maxY = 5f, + ), ), - ), model = model1, startAxis = rememberStartAxis(), ) @@ -102,12 +108,14 @@ public fun RegularLineChartExpanded() { @Composable public fun RegularLineChartCollapsed() { Chart( - chart = lineChart( - axisValuesOverrider = AxisValuesOverrider.fixed( - minY = 1f, - maxY = 3f, + chart = + lineChart( + axisValuesOverrider = + AxisValuesOverrider.fixed( + minY = 1f, + maxY = 3f, + ), ), - ), model = model1, startAxis = rememberStartAxis(), ) @@ -117,19 +125,24 @@ public fun RegularLineChartCollapsed() { @Composable public fun ComposedLineChart() { Chart( - chart = lineChart() + lineChart( - lines = listOf( - lineSpec( - lineColor = Color.Blue, - lineBackgroundShader = verticalGradient( - colors = arrayOf( - Color.Blue.copy(alpha = 0.4f), - Color.Blue.copy(alpha = 0f), + chart = + lineChart() + + lineChart( + lines = + listOf( + lineSpec( + lineColor = Color.Blue, + lineBackgroundShader = + verticalGradient( + colors = + arrayOf( + Color.Blue.copy(alpha = 0.4f), + Color.Blue.copy(alpha = 0f), + ), + ), + ), ), - ), ), - ), - ), model = model1 + model2, startAxis = rememberStartAxis(), ) @@ -139,17 +152,21 @@ public fun ComposedLineChart() { @Composable public fun ComposedLineChartCollapsed() { Chart( - chart = lineChart( - axisValuesOverrider = AxisValuesOverrider.fixed( - minY = 1f, - maxY = 3f, - ), - ) + lineChart( - axisValuesOverrider = AxisValuesOverrider.fixed( - minY = 1f, - maxY = 3f, - ), - ), + chart = + lineChart( + axisValuesOverrider = + AxisValuesOverrider.fixed( + minY = 1f, + maxY = 3f, + ), + ) + + lineChart( + axisValuesOverrider = + AxisValuesOverrider.fixed( + minY = 1f, + maxY = 3f, + ), + ), model = model1 + model2, startAxis = rememberStartAxis(), ) diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartsWithNegativeValuesPreviews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartsWithNegativeValuesPreviews.kt index 96e7e17ef..94460afcd 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartsWithNegativeValuesPreviews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartsWithNegativeValuesPreviews.kt @@ -50,31 +50,38 @@ public fun SingleLineChartWithNegativeValues() { Surface { Chart( modifier = Modifier.height(250.dp), - chart = lineChart( - lines = listOf( - lineSpec( - lineShader = DynamicShaders.split( - positiveColor = Color(0xFF25BE53), - negativeColor = Color(0xFFE73B3B), + chart = + lineChart( + lines = + listOf( + lineSpec( + lineShader = + DynamicShaders.split( + positiveColor = Color(0xFF25BE53), + negativeColor = Color(0xFFE73B3B), + ), + ), + ), + persistentMarkers = + mapOf( + 2f to marker, + 3f to marker, ), - ), - ), - persistentMarkers = mapOf( - 2f to marker, - 3f to marker, ), - ), model = model, - startAxis = rememberStartAxis( - itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 4) }, - guideline = axisLineComponent(), - ), - bottomAxis = rememberBottomAxis( - guideline = axisLineComponent(), - itemPlacer = AxisItemPlacer.Horizontal.default( - spacing = 2, + startAxis = + rememberStartAxis( + itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 4) }, + guideline = axisLineComponent(), + ), + bottomAxis = + rememberBottomAxis( + guideline = axisLineComponent(), + itemPlacer = + AxisItemPlacer.Horizontal.default( + spacing = 2, + ), ), - ), horizontalLayout = HorizontalLayout.fullWidth(), ) } @@ -85,11 +92,13 @@ public fun SingleLineChartWithNegativeValues() { public fun SingleLineChartWithNegativeValuesAndDataLabels() { Surface { Chart( - chart = lineChart( - lines = listOf( - lineSpec(lineShader = DynamicShaders.solid(Color.DarkGray), dataLabel = textComponent()), + chart = + lineChart( + lines = + listOf( + lineSpec(lineShader = DynamicShaders.solid(Color.DarkGray), dataLabel = textComponent()), + ), ), - ), model = model, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), @@ -102,12 +111,14 @@ public fun SingleLineChartWithNegativeValuesAndDataLabels() { public fun SingleLineChartWithNegativeValuesAndAxisValuesOverridden() { Surface { Chart( - chart = lineChart( - axisValuesOverrider = AxisValuesOverrider.fixed( - minY = 1f, - maxY = 4f, + chart = + lineChart( + axisValuesOverrider = + AxisValuesOverrider.fixed( + minY = 1f, + maxY = 4f, + ), ), - ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 4) }), bottomAxis = rememberBottomAxis(), @@ -120,12 +131,14 @@ public fun SingleLineChartWithNegativeValuesAndAxisValuesOverridden() { public fun SingleLineChartWithNegativeValuesAndAxisValuesOverridden2() { Surface { Chart( - chart = lineChart( - axisValuesOverrider = AxisValuesOverrider.fixed( - minY = -2f, - maxY = 0f, + chart = + lineChart( + axisValuesOverrider = + AxisValuesOverrider.fixed( + minY = -2f, + maxY = 0f, + ), ), - ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 3) }), bottomAxis = rememberBottomAxis(), diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/Previews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/Previews.kt index 68ee890e0..fb920ab43 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/Previews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/Previews.kt @@ -60,110 +60,127 @@ private val chartModifier = Modifier.height(100.dp) @Preview("Sample Card With Column Chart", widthDp = 200) @Composable -public fun ColumnChartCard(): Unit = VicoTheme { - val colors = MaterialTheme.colors +public fun ColumnChartCard(): Unit = + VicoTheme { + val colors = MaterialTheme.colors - SampleCard { - Chart( - modifier = chartModifier, - chart = columnChart( - columns = listOf( - lineComponent( - colors.primary, - thickness = 8.dp, - shape = RoundedCornerShape(4.dp), - dynamicShader = verticalGradient(arrayOf(colors.primary, colors.secondary)), + SampleCard { + Chart( + modifier = chartModifier, + chart = + columnChart( + columns = + listOf( + lineComponent( + colors.primary, + thickness = 8.dp, + shape = RoundedCornerShape(4.dp), + dynamicShader = verticalGradient(arrayOf(colors.primary, colors.secondary)), + ), + ), ), - ), - ), - startAxis = createVerticalAxis { - label = textComponent( - color = colors.primary, - textSize = 10.sp, - background = shapeComponent( - shape = CutCornerShape( - CornerSize(percent = 25), - CornerSize(percent = 50), - CornerSize(percent = 50), - CornerSize(percent = 25), - ), - color = colors.primary.copy(alpha = 0.1f), - ), - padding = dimensionsOf(end = 8.dp, start = 4.dp), - ) - axis = null - tick = null - guideline = LineComponent( - colors.primary.copy(alpha = 0.1f).toArgb(), - 1.dp.value, - ) - }, - model = @Suppress("MagicNumber") (entryModelOf(1, 2, 3, 2)), - ) + startAxis = + createVerticalAxis { + label = + textComponent( + color = colors.primary, + textSize = 10.sp, + background = + shapeComponent( + shape = + CutCornerShape( + CornerSize(percent = 25), + CornerSize(percent = 50), + CornerSize(percent = 50), + CornerSize(percent = 25), + ), + color = colors.primary.copy(alpha = 0.1f), + ), + padding = dimensionsOf(end = 8.dp, start = 4.dp), + ) + axis = null + tick = null + guideline = + LineComponent( + colors.primary.copy(alpha = 0.1f).toArgb(), + 1.dp.value, + ) + }, + model = entryModelOf(1, 2, 3, 2), + ) + } } -} @Preview("Sample Card With Line Chart", widthDp = 200) @Composable -public fun LineChartCard(): Unit = VicoTheme { - val colors = MaterialTheme.colors +public fun LineChartCard(): Unit = + VicoTheme { + val colors = MaterialTheme.colors - SampleCard { - Chart( - modifier = Modifier.height(100.dp), - chart = lineChart( - lines = listOf( - lineSpec( - point = null, - lineColor = colors.primary, - lineBackgroundShader = DynamicShaders.fromComponent( - componentSize = 4.dp, - component = shapeComponent(shape = pillShape, color = colors.primary).apply { - setMargins(0.5.dp.value) - }, - ), + SampleCard { + Chart( + modifier = Modifier.height(100.dp), + chart = + lineChart( + lines = + listOf( + lineSpec( + point = null, + lineColor = colors.primary, + lineBackgroundShader = + DynamicShaders.fromComponent( + componentSize = 4.dp, + component = + shapeComponent(shape = pillShape, color = colors.primary).apply { + setMargins(0.5.dp.value) + }, + ), + ), + ), + axisValuesOverrider = + AxisValuesOverrider.fixed( + minX = 0f, + maxY = 3f, + ), ), - ), - axisValuesOverrider = AxisValuesOverrider.fixed( - minX = 0f, - maxY = 3f, - ), - ), - model = entryModelOf(-1 to 0, 0 to 0, 1 to 1, 2 to 2, 3 to 0, 4 to 2, 5 to 1), - startAxis = createVerticalAxis { - label = textComponent( - color = colors.onSurface, - textSize = 10.sp, - background = shapeComponent(shape = rectShape, color = Color.LightGray), - padding = dimensionsOf(horizontal = 4.dp, vertical = 2.dp), - ) - axis = null - tick = null - guideline = LineComponent( - color = Color.LightGray.toArgb(), - thicknessDp = 1.dp.value, - shape = DashedShape( - shape = pillShape, - dashLengthDp = 2.dp.value, - gapLengthDp = 4.dp.value, - ), - ) - horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside - }, - bottomAxis = createHorizontalAxis { - label = null - tick = null - guideline = null - axis = lineComponent(color = Color.LightGray, thickness = 1.dp) - }, - ) + model = entryModelOf(-1 to 0, 0 to 0, 1 to 1, 2 to 2, 3 to 0, 4 to 2, 5 to 1), + startAxis = + createVerticalAxis { + label = + textComponent( + color = colors.onSurface, + textSize = 10.sp, + background = shapeComponent(shape = rectShape, color = Color.LightGray), + padding = dimensionsOf(horizontal = 4.dp, vertical = 2.dp), + ) + axis = null + tick = null + guideline = + LineComponent( + color = Color.LightGray.toArgb(), + thicknessDp = 1.dp.value, + shape = + DashedShape( + shape = pillShape, + dashLengthDp = 2.dp.value, + gapLengthDp = 4.dp.value, + ), + ) + horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside + }, + bottomAxis = + createHorizontalAxis { + label = null + tick = null + guideline = null + axis = lineComponent(color = Color.LightGray, thickness = 1.dp) + }, + ) + } } -} @Composable -private fun SampleCard( - chart: @Composable ColumnScope.() -> Unit, -) { +private fun SampleCard(chart: @Composable ColumnScope.() -> Unit) { Card( modifier = Modifier.padding(8.dp), shape = RoundedCornerShape(8.dp), diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ShapePreviews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ShapePreviews.kt index e9aa97a97..be7f8ddcc 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ShapePreviews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ShapePreviews.kt @@ -56,18 +56,20 @@ private fun PreviewShape(shape: Shape) { val path = remember { Path() } Column( - modifier = Modifier - .width(100.dp) - .background(color = Color.White, shape = RoundedCornerShape(size = 4.dp)) - .padding(8.dp), + modifier = + Modifier + .width(100.dp) + .background(color = Color.White, shape = RoundedCornerShape(size = 4.dp)) + .padding(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), ) { Text(text = "Canvas") Canvas( - modifier = Modifier - .fillMaxWidth() - .height(50.dp), + modifier = + Modifier + .fillMaxWidth() + .height(50.dp), ) { shape.drawShape( context = drawContext(drawContext.canvas.nativeCanvas), @@ -86,13 +88,14 @@ private fun PreviewShape(shape: Shape) { Text(text = "Compose Shape") Box( - modifier = Modifier - .height(50.dp) - .fillMaxWidth() - .background( - color = Color(black), - shape = shape.composeShape(), - ), + modifier = + Modifier + .height(50.dp) + .fillMaxWidth() + .background( + color = Color(black), + shape = shape.composeShape(), + ), ) } } @@ -138,11 +141,12 @@ public fun CutCornerCustom1Shape() { @Preview public fun DrawableShape() { PreviewShape( - shape = Shapes.drawableShape( - drawable = getDrawable(id = R.drawable.ic_baseline_android_24), - keepAspectRatio = true, - otherShape = Shapes.pillShape, - ), + shape = + Shapes.drawableShape( + drawable = getDrawable(id = R.drawable.ic_baseline_android_24), + keepAspectRatio = true, + otherShape = Shapes.pillShape, + ), ) } @@ -150,11 +154,12 @@ public fun DrawableShape() { @Preview public fun DrawableShape2() { PreviewShape( - shape = Shapes.drawableShape( - drawable = getDrawable(id = R.drawable.ic_baseline_android_24), - keepAspectRatio = true, - otherShape = Shapes.pillShape, - ), + shape = + Shapes.drawableShape( + drawable = getDrawable(id = R.drawable.ic_baseline_android_24), + keepAspectRatio = true, + otherShape = Shapes.pillShape, + ), ) } @@ -168,14 +173,16 @@ public fun DrawableShapeStretched() { @Preview public fun DashedCutCornerCustomShape() { PreviewShape( - shape = Shapes.dashedShape( - shape = Shapes.cutCornerShape(topRightPercent = 50, bottomLeftPercent = 50), - dashLength = 24.dp, - gapLength = 8.dp, - ), + shape = + Shapes.dashedShape( + shape = Shapes.cutCornerShape(topRightPercent = 50, bottomLeftPercent = 50), + dashLength = 24.dp, + gapLength = 8.dp, + ), ) } @Composable -private fun getDrawable(@DrawableRes id: Int): Drawable = - ResourcesCompat.getDrawable(LocalContext.current.resources, id, null)!! +private fun getDrawable( + @DrawableRes id: Int, +): Drawable = ResourcesCompat.getDrawable(LocalContext.current.resources, id, null)!! diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/StackedColumnChartsWithNegativeValuesPreviews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/StackedColumnChartsWithNegativeValuesPreviews.kt index e893f7acd..a2fddb4d6 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/StackedColumnChartsWithNegativeValuesPreviews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/StackedColumnChartsWithNegativeValuesPreviews.kt @@ -38,19 +38,21 @@ import com.patrykandpatrick.vico.core.entry.entriesOf import com.patrykandpatrick.vico.core.entry.entryModelOf import com.patrykandpatrick.vico.sample.showcase.rememberMarker -private val model = entryModelOf( - entriesOf(2f, -1f, -4f, 2f, 1f, -5f, -2f, -3f), - entriesOf(3f, -2f, 2f, -1f, 2f, -3f, -4f, -1f), - entriesOf(1f, -2f, 2f, 1f, -1f, 4f, 4f, -2f), -) +private val model = + entryModelOf( + entriesOf(2f, -1f, -4f, 2f, 1f, -5f, -2f, -3f), + entriesOf(3f, -2f, 2f, -1f, 2f, -3f, -4f, -1f), + entriesOf(1f, -2f, 2f, 1f, -1f, 4f, 4f, -2f), + ) private val columns: List @Composable - get() = listOf( - lineComponent(color = Color(0xFF494949), thickness = 8.dp), - lineComponent(color = Color(0xFF7C7A7A), thickness = 8.dp), - lineComponent(color = Color(0xFFFF5D73), thickness = 8.dp), - ) + get() = + listOf( + lineComponent(color = Color(0xFF494949), thickness = 8.dp), + lineComponent(color = Color(0xFF7C7A7A), thickness = 8.dp), + lineComponent(color = Color(0xFFFF5D73), thickness = 8.dp), + ) @Preview @Composable @@ -59,14 +61,16 @@ public fun StackedColumnChartWithNegativeValues() { Surface { Chart( modifier = Modifier.height(250.dp), - chart = columnChart( - columns = columns, - persistentMarkers = mapOf( - 2f to marker, - 3f to marker, + chart = + columnChart( + columns = columns, + persistentMarkers = + mapOf( + 2f to marker, + 3f to marker, + ), + mergeMode = Stack, ), - mergeMode = Stack, - ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 8) }), bottomAxis = rememberBottomAxis(), @@ -79,11 +83,12 @@ public fun StackedColumnChartWithNegativeValues() { public fun StackedColumnChartWithNegativeValuesAndDataLabels() { Surface { Chart( - chart = columnChart( - columns = columns, - dataLabel = textComponent(), - mergeMode = Stack, - ), + chart = + columnChart( + columns = columns, + dataLabel = textComponent(), + mergeMode = Stack, + ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 8) }), bottomAxis = rememberBottomAxis(), @@ -96,14 +101,16 @@ public fun StackedColumnChartWithNegativeValuesAndDataLabels() { public fun StackedColumnChartWithNegativeValuesAndAxisValuesOverridden() { Surface { Chart( - chart = columnChart( - columns = columns, - axisValuesOverrider = AxisValuesOverrider.fixed( - minY = 1f, - maxY = 4f, + chart = + columnChart( + columns = columns, + axisValuesOverrider = + AxisValuesOverrider.fixed( + minY = 1f, + maxY = 4f, + ), + mergeMode = Stack, ), - mergeMode = Stack, - ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 4) }), bottomAxis = rememberBottomAxis(), @@ -116,14 +123,16 @@ public fun StackedColumnChartWithNegativeValuesAndAxisValuesOverridden() { public fun StackedColumnChartWithNegativeValuesAndAxisValuesOverridden2() { Surface { Chart( - chart = columnChart( - columns = columns, - axisValuesOverrider = AxisValuesOverrider.fixed( - minY = -2f, - maxY = 0f, + chart = + columnChart( + columns = columns, + axisValuesOverrider = + AxisValuesOverrider.fixed( + minY = -2f, + maxY = 0f, + ), + mergeMode = Stack, ), - mergeMode = Stack, - ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 3) }), bottomAxis = rememberBottomAxis(), diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ThresholdLinePreviews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ThresholdLinePreviews.kt index f4c25ac82..a1c6f3940 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ThresholdLinePreviews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ThresholdLinePreviews.kt @@ -50,30 +50,35 @@ public val Color.Companion.DimmedGray: Color @Composable private fun ProvidePreviewChartStyle(content: @Composable () -> Unit) { - val chartStyle = LocalChartStyle.current.copy( - axis = LocalChartStyle.current.axis.copy( - axisLabelColor = Color.DimmedGray, - axisLineColor = Color.DimmedGray, - axisTickColor = Color.DimmedGray, - axisGuidelineColor = Color.DimmedGray, - ), - columnChart = LocalChartStyle.current.columnChart.copy( - columns = LocalChartStyle.current.columnChart.columns.map { - lineComponent( - color = Color.DimmedGray, - thickness = it.thicknessDp.dp, - shape = it.shape, - dynamicShader = it.dynamicShader, - margins = it.margins, - ) - }, - ), - ) + val chartStyle = + LocalChartStyle.current.copy( + axis = + LocalChartStyle.current.axis.copy( + axisLabelColor = Color.DimmedGray, + axisLineColor = Color.DimmedGray, + axisTickColor = Color.DimmedGray, + axisGuidelineColor = Color.DimmedGray, + ), + columnChart = + LocalChartStyle.current.columnChart.copy( + columns = + LocalChartStyle.current.columnChart.columns.map { + lineComponent( + color = Color.DimmedGray, + thickness = it.thicknessDp.dp, + shape = it.shape, + dynamicShader = it.dynamicShader, + margins = it.margins, + ) + }, + ), + ) Surface( color = Color.Transparent, - modifier = Modifier - .background(color = Color.LightGray, shape = RoundedCornerShape(size = 4.dp)) - .padding(8.dp), + modifier = + Modifier + .background(color = Color.LightGray, shape = RoundedCornerShape(size = 4.dp)) + .padding(8.dp), ) { CompositionLocalProvider(LocalChartStyle provides chartStyle, content = content) } @@ -85,15 +90,16 @@ public fun ThresholdLine() { ProvidePreviewChartStyle { Chart( modifier = Modifier, - chart = columnChart().apply { - addDecoration( - ThresholdLine( - thresholdValue = 2f, - lineComponent = shapeComponent(color = Color.Black), - labelComponent = textComponent(Color.Black, padding = dimensionsOf(horizontal = 8.dp)), - ), - ) - }, + chart = + columnChart().apply { + addDecoration( + ThresholdLine( + thresholdValue = 2f, + lineComponent = shapeComponent(color = Color.Black), + labelComponent = textComponent(Color.Black, padding = dimensionsOf(horizontal = 8.dp)), + ), + ) + }, model = model, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), @@ -108,53 +114,64 @@ public fun ThresholdLineWithCustomText() { ProvidePreviewChartStyle { Chart( modifier = Modifier, - chart = columnChart().apply { - addDecoration( - ThresholdLine( - thresholdValue = 2f, - thresholdLabel = "Threshold line 1 📐", - lineComponent = shapeComponent(color = Color.Black), - labelComponent = textComponent( - color = Color.White, - lineCount = 3, - background = shapeComponent( - shape = Shapes.roundedCornerShape(bottomLeftPercent = 25, bottomRightPercent = 25), - color = Color.Black, - ), - padding = dimensionsOf( - start = 8.dp, - top = 2.dp, - end = 8.dp, - bottom = 4.dp, - ), - margins = dimensionsOf(horizontal = 4.dp), + chart = + columnChart().apply { + addDecoration( + ThresholdLine( + thresholdValue = 2f, + thresholdLabel = "Threshold line 1 📐", + lineComponent = shapeComponent(color = Color.Black), + labelComponent = + textComponent( + color = Color.White, + lineCount = 3, + background = + shapeComponent( + shape = + Shapes.roundedCornerShape( + bottomLeftPercent = 25, + bottomRightPercent = 25, + ), + color = Color.Black, + ), + padding = + dimensionsOf( + start = 8.dp, + top = 2.dp, + end = 8.dp, + bottom = 4.dp, + ), + margins = dimensionsOf(horizontal = 4.dp), + ), + labelVerticalPosition = ThresholdLine.LabelVerticalPosition.Bottom, ), - labelVerticalPosition = ThresholdLine.LabelVerticalPosition.Bottom, - ), - ) - addDecoration( - ThresholdLine( - thresholdValue = 3f, - thresholdLabel = "Threshold line 2 📐", - lineComponent = shapeComponent(color = Color.DarkGray), - labelComponent = textComponent( - color = Color.White, - lineCount = 3, - background = shapeComponent( - shape = Shapes.cutCornerShape(topLeftPercent = 25, topRightPercent = 25), - color = Color.DarkGray, - ), - padding = dimensionsOf( - start = 8.dp, - top = 4.dp, - end = 8.dp, - bottom = 2.dp, - ), - margins = dimensionsOf(horizontal = 4.dp), + ) + addDecoration( + ThresholdLine( + thresholdValue = 3f, + thresholdLabel = "Threshold line 2 📐", + lineComponent = shapeComponent(color = Color.DarkGray), + labelComponent = + textComponent( + color = Color.White, + lineCount = 3, + background = + shapeComponent( + shape = Shapes.cutCornerShape(topLeftPercent = 25, topRightPercent = 25), + color = Color.DarkGray, + ), + padding = + dimensionsOf( + start = 8.dp, + top = 4.dp, + end = 8.dp, + bottom = 2.dp, + ), + margins = dimensionsOf(horizontal = 4.dp), + ), ), - ), - ) - }, + ) + }, model = model, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), @@ -169,15 +186,20 @@ public fun RangedThresholdLine() { ProvidePreviewChartStyle { Chart( modifier = Modifier, - chart = columnChart().apply { - addDecoration( - ThresholdLine( - thresholdRange = 2f..3f, - lineComponent = shapeComponent(color = Color.Black.copy(alpha = 0.5f)), - labelComponent = textComponent(color = Color.Black, padding = dimensionsOf(horizontal = 8.dp)), - ), - ) - }, + chart = + columnChart().apply { + addDecoration( + ThresholdLine( + thresholdRange = 2f..3f, + lineComponent = shapeComponent(color = Color.Black.copy(alpha = 0.5f)), + labelComponent = + textComponent( + color = Color.Black, + padding = dimensionsOf(horizontal = 8.dp), + ), + ), + ) + }, model = model, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), @@ -192,23 +214,31 @@ public fun RangedThresholdLineWithBrushShader() { ProvidePreviewChartStyle { Chart( modifier = Modifier, - chart = columnChart().apply { - addDecoration( - ThresholdLine( - thresholdRange = 2f..3f, - lineComponent = shapeComponent( - color = Color.Black, - dynamicShader = Brush.verticalGradient( - colors = listOf( - Color.Black.copy(0.75f), - Color.Black.copy(0.25f), + chart = + columnChart().apply { + addDecoration( + ThresholdLine( + thresholdRange = 2f..3f, + lineComponent = + shapeComponent( + color = Color.Black, + dynamicShader = + Brush.verticalGradient( + colors = + listOf( + Color.Black.copy(0.75f), + Color.Black.copy(0.25f), + ), + ).toDynamicShader(), + ), + labelComponent = + textComponent( + color = Color.Black, + padding = dimensionsOf(horizontal = 8.dp), ), - ).toDynamicShader(), ), - labelComponent = textComponent(color = Color.Black, padding = dimensionsOf(horizontal = 8.dp)), - ), - ) - }, + ) + }, model = model, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), @@ -223,23 +253,30 @@ public fun RangedThresholdLineWithComponentShader() { ProvidePreviewChartStyle { Chart( modifier = Modifier, - chart = columnChart().apply { - addDecoration( - ThresholdLine( - thresholdRange = 2f..3f, - lineComponent = shapeComponent( - color = Color.Black, - dynamicShader = ComponentShader( - shapeComponent(shape = Shapes.pillShape, color = Color.Black), - componentSizeDp = 4f, - ), - strokeWidth = 2.dp, - strokeColor = Color.Black, + chart = + columnChart().apply { + addDecoration( + ThresholdLine( + thresholdRange = 2f..3f, + lineComponent = + shapeComponent( + color = Color.Black, + dynamicShader = + ComponentShader( + shapeComponent(shape = Shapes.pillShape, color = Color.Black), + componentSizeDp = 4f, + ), + strokeWidth = 2.dp, + strokeColor = Color.Black, + ), + labelComponent = + textComponent( + color = Color.Black, + padding = dimensionsOf(horizontal = 8.dp), + ), ), - labelComponent = textComponent(color = Color.Black, padding = dimensionsOf(horizontal = 8.dp)), - ), - ) - }, + ) + }, model = model, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/composables/column/ColumnCharts.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/composables/column/ColumnCharts.kt index 6e3015af3..c2f22f1b7 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/composables/column/ColumnCharts.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/composables/column/ColumnCharts.kt @@ -46,11 +46,12 @@ public fun DefaultColumnChart( oldModel = oldModel, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), - chartScrollSpec = rememberChartScrollSpec( - isScrollEnabled = scrollable, - initialScroll = initialScroll, - autoScrollCondition = autoScrollCondition, - ), + chartScrollSpec = + rememberChartScrollSpec( + isScrollEnabled = scrollable, + initialScroll = initialScroll, + autoScrollCondition = autoScrollCondition, + ), ) } } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ChartStyle.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ChartStyle.kt index ef22255b3..6f84e962b 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ChartStyle.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ChartStyle.kt @@ -33,7 +33,10 @@ import com.patrykandpatrick.vico.core.component.shape.Shapes import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders @Composable -internal fun rememberChartStyle(columnChartColors: List, lineChartColors: List): ChartStyle { +internal fun rememberChartStyle( + columnChartColors: List, + lineChartColors: List, +): ChartStyle { val isSystemInDarkTheme = isSystemInDarkTheme() return remember(columnChartColors, lineChartColors, isSystemInDarkTheme) { val defaultColors = if (isSystemInDarkTheme) DefaultColors.Dark else DefaultColors.Light @@ -56,14 +59,15 @@ internal fun rememberChartStyle(columnChartColors: List, lineChartColors: lineChartColors.map { lineChartColor -> LineChart.LineSpec( lineColor = lineChartColor.toArgb(), - lineBackgroundShader = DynamicShaders.fromBrush( - Brush.verticalGradient( - listOf( - lineChartColor.copy(DefaultAlpha.LINE_BACKGROUND_SHADER_START), - lineChartColor.copy(DefaultAlpha.LINE_BACKGROUND_SHADER_END), + lineBackgroundShader = + DynamicShaders.fromBrush( + Brush.verticalGradient( + listOf( + lineChartColor.copy(DefaultAlpha.LINE_BACKGROUND_SHADER_START), + lineChartColor.copy(DefaultAlpha.LINE_BACKGROUND_SHADER_END), + ), ), ), - ), ) }, ), diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/Marker.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/Marker.kt index 508261a5d..bec0ac776 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/Marker.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/Marker.kt @@ -43,36 +43,41 @@ import com.patrykandpatrick.vico.core.marker.Marker @Composable internal fun rememberMarker(): Marker { val labelBackgroundColor = MaterialTheme.colorScheme.surface - val labelBackground = remember(labelBackgroundColor) { - ShapeComponent(labelBackgroundShape, labelBackgroundColor.toArgb()).setShadow( - radius = LABEL_BACKGROUND_SHADOW_RADIUS, - dy = LABEL_BACKGROUND_SHADOW_DY, - applyElevationOverlay = true, + val labelBackground = + remember(labelBackgroundColor) { + ShapeComponent(labelBackgroundShape, labelBackgroundColor.toArgb()).setShadow( + radius = LABEL_BACKGROUND_SHADOW_RADIUS, + dy = LABEL_BACKGROUND_SHADOW_DY, + applyElevationOverlay = true, + ) + } + val label = + textComponent( + background = labelBackground, + lineCount = LABEL_LINE_COUNT, + padding = labelPadding, + typeface = Typeface.MONOSPACE, ) - } - val label = textComponent( - background = labelBackground, - lineCount = LABEL_LINE_COUNT, - padding = labelPadding, - typeface = Typeface.MONOSPACE, - ) val indicatorInnerComponent = shapeComponent(Shapes.pillShape, MaterialTheme.colorScheme.surface) val indicatorCenterComponent = shapeComponent(Shapes.pillShape, Color.White) val indicatorOuterComponent = shapeComponent(Shapes.pillShape, Color.White) - val indicator = overlayingComponent( - outer = indicatorOuterComponent, - inner = overlayingComponent( - outer = indicatorCenterComponent, - inner = indicatorInnerComponent, - innerPaddingAll = indicatorInnerAndCenterComponentPaddingValue, - ), - innerPaddingAll = indicatorCenterAndOuterComponentPaddingValue, - ) - val guideline = lineComponent( - MaterialTheme.colorScheme.onSurface.copy(GUIDELINE_ALPHA), - guidelineThickness, - guidelineShape, - ) + val indicator = + overlayingComponent( + outer = indicatorOuterComponent, + inner = + overlayingComponent( + outer = indicatorCenterComponent, + inner = indicatorInnerComponent, + innerPaddingAll = indicatorInnerAndCenterComponentPaddingValue, + ), + innerPaddingAll = indicatorCenterAndOuterComponentPaddingValue, + ) + val guideline = + lineComponent( + MaterialTheme.colorScheme.onSurface.copy(GUIDELINE_ALPHA), + guidelineThickness, + guidelineShape, + ) return remember(label, indicator, guideline) { object : MarkerComponent(label, indicator, guideline) { init { diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseScreen.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseScreen.kt index db24809f5..77dc9fb3e 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseScreen.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseScreen.kt @@ -76,10 +76,11 @@ internal fun ShowcaseScreen(viewModel: ShowcaseViewModel = viewModel()) { ) { paddingValues -> Crossfade(viewModel.uiSystem) { uiSystem -> LazyColumn( - state = when (uiSystem) { - UISystem.Compose -> composeShowcaseState - UISystem.Views -> viewShowcaseState - }, + state = + when (uiSystem) { + UISystem.Compose -> composeShowcaseState + UISystem.Views -> viewShowcaseState + }, contentPadding = paddingValues + PaddingValues(padding), verticalArrangement = Arrangement.spacedBy(padding), ) { @@ -89,7 +90,10 @@ internal fun ShowcaseScreen(viewModel: ShowcaseViewModel = viewModel()) { } } -private fun LazyListScope.chartItems(uiSystem: UISystem, viewModel: ShowcaseViewModel) { +private fun LazyListScope.chartItems( + uiSystem: UISystem, + viewModel: ShowcaseViewModel, +) { cardItem { Chart9(uiSystem, viewModel.positiveAndNegativeChartEntryModelProducer) } cardItem { Chart1(uiSystem, viewModel.customStepChartEntryModelProducer) } cardItem { Chart2(uiSystem, viewModel.chartEntryModelProducer) } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseViewModel.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseViewModel.kt index 21eae0e0f..00077bdae 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseViewModel.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseViewModel.kt @@ -30,21 +30,23 @@ import kotlinx.coroutines.isActive import kotlinx.coroutines.launch internal class ShowcaseViewModel : ViewModel() { + private val generator = + RandomEntriesGenerator( + xRange = 0..GENERATOR_X_RANGE_TOP, + yRange = GENERATOR_Y_RANGE_BOTTOM..GENERATOR_Y_RANGE_TOP, + ) - private val generator = RandomEntriesGenerator( - xRange = 0..GENERATOR_X_RANGE_TOP, - yRange = GENERATOR_Y_RANGE_BOTTOM..GENERATOR_Y_RANGE_TOP, - ) + private val customStepGenerator = + RandomEntriesGenerator( + xRange = IntProgression.fromClosedRange(rangeStart = 0, rangeEnd = GENERATOR_X_RANGE_TOP, step = 2), + yRange = GENERATOR_Y_RANGE_BOTTOM..GENERATOR_Y_RANGE_TOP, + ) - private val customStepGenerator = RandomEntriesGenerator( - xRange = IntProgression.fromClosedRange(rangeStart = 0, rangeEnd = GENERATOR_X_RANGE_TOP, step = 2), - yRange = GENERATOR_Y_RANGE_BOTTOM..GENERATOR_Y_RANGE_TOP, - ) - - private val positiveAndNegativeValuesGenerator = RandomEntriesGenerator( - xRange = 0..GENERATOR_X_RANGE_TOP, - yRange = -10..GENERATOR_Y_RANGE_TOP, - ) + private val positiveAndNegativeValuesGenerator = + RandomEntriesGenerator( + xRange = 0..GENERATOR_X_RANGE_TOP, + yRange = -10..GENERATOR_Y_RANGE_TOP, + ) internal val chartEntryModelProducer: ChartEntryModelProducer = ChartEntryModelProducer() diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/UISystem.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/UISystem.kt index 1722c1a08..83e23f210 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/UISystem.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/UISystem.kt @@ -20,7 +20,10 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes import com.patrykandpatrick.vico.R -internal enum class UISystem(@StringRes val labelResourceID: Int, @DrawableRes val iconResourceID: Int) { +internal enum class UISystem( + @StringRes val labelResourceID: Int, + @DrawableRes val iconResourceID: Int, +) { Compose(R.string.compose, R.drawable.ic_compose), Views(R.string.views, R.drawable.ic_views), } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart1.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart1.kt index 34e2d997b..15999688d 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart1.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart1.kt @@ -33,7 +33,10 @@ import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable -internal fun Chart1(uiSystem: UISystem, chartEntryModelProducer: ChartEntryModelProducer) { +internal fun Chart1( + uiSystem: UISystem, + chartEntryModelProducer: ChartEntryModelProducer, +) { when (uiSystem) { UISystem.Compose -> ComposeChart1(chartEntryModelProducer) UISystem.Views -> ViewChart1(chartEntryModelProducer) diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart2.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart2.kt index 77f63b773..e1bc87a44 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart2.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart2.kt @@ -49,7 +49,10 @@ import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable -internal fun Chart2(uiSystem: UISystem, chartEntryModelProducer: ChartEntryModelProducer) { +internal fun Chart2( + uiSystem: UISystem, + chartEntryModelProducer: ChartEntryModelProducer, +) { when (uiSystem) { UISystem.Compose -> ComposeChart2(chartEntryModelProducer) UISystem.Views -> ViewChart2(chartEntryModelProducer) @@ -62,14 +65,16 @@ private fun ComposeChart2(chartEntryModelProducer: ChartEntryModelProducer) { ProvideChartStyle(rememberChartStyle(chartColors)) { val defaultColumns = currentChartStyle.columnChart.columns Chart( - chart = columnChart( - columns = remember(defaultColumns) { - defaultColumns.map { defaultColumn -> - LineComponent(defaultColumn.color, COLUMN_WIDTH_DP, defaultColumn.shape) - } - }, - decorations = remember(thresholdLine) { listOf(thresholdLine) }, - ), + chart = + columnChart( + columns = + remember(defaultColumns) { + defaultColumns.map { defaultColumn -> + LineComponent(defaultColumn.color, COLUMN_WIDTH_DP, defaultColumn.shape) + } + }, + decorations = remember(thresholdLine) { listOf(thresholdLine) }, + ), chartModelProducer = chartEntryModelProducer, startAxis = rememberStartAxis(valueFormatter = startAxisValueFormatter, itemPlacer = startAxisItemPlacer), bottomAxis = rememberBottomAxis(itemPlacer = bottomAxisItemPlacer), @@ -102,13 +107,14 @@ private fun ViewChart2(chartEntryModelProducer: ChartEntryModelProducer) { @Composable private fun rememberThresholdLine(): ThresholdLine { val line = shapeComponent(color = color2) - val label = textComponent( - color = Color.Black, - background = shapeComponent(Shapes.pillShape, color2), - padding = thresholdLineLabelPadding, - margins = thresholdLineLabelMargins, - typeface = Typeface.MONOSPACE, - ) + val label = + textComponent( + color = Color.Black, + background = shapeComponent(Shapes.pillShape, color2), + padding = thresholdLineLabelPadding, + margins = thresholdLineLabelMargins, + typeface = Typeface.MONOSPACE, + ) return remember(line, label) { ThresholdLine(thresholdValue = THRESHOLD_LINE_VALUE, lineComponent = line, labelComponent = label) } @@ -132,9 +138,10 @@ private val thresholdLineLabelPadding = dimensionsOf(thresholdLineLabelHorizontalPaddingValue, thresholdLineLabelVerticalPaddingValue) private val thresholdLineLabelMargins = dimensionsOf(thresholdLineLabelMarginValue) private val startAxisValueFormatter = PercentageFormatAxisValueFormatter() -private val horizontalLayout = HorizontalLayout.FullWidth( - scalableStartPaddingDp = DefaultDimens.COLUMN_OUTSIDE_SPACING.half, - scalableEndPaddingDp = DefaultDimens.COLUMN_OUTSIDE_SPACING.half, -) +private val horizontalLayout = + HorizontalLayout.FullWidth( + scalableStartPaddingDp = DefaultDimens.COLUMN_OUTSIDE_SPACING.half, + scalableEndPaddingDp = DefaultDimens.COLUMN_OUTSIDE_SPACING.half, + ) private val startAxisItemPlacer = AxisItemPlacer.Vertical.default(MAX_START_AXIS_ITEM_COUNT) private val bottomAxisItemPlacer = AxisItemPlacer.Horizontal.default(BOTTOM_AXIS_ITEM_SPACING, BOTTOM_AXIS_ITEM_OFFSET) diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart3.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart3.kt index 9f7b22257..bcbf83e1b 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart3.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart3.kt @@ -45,7 +45,10 @@ import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable -internal fun Chart3(uiSystem: UISystem, chartEntryModelProducer: ChartEntryModelProducer) { +internal fun Chart3( + uiSystem: UISystem, + chartEntryModelProducer: ChartEntryModelProducer, +) { when (uiSystem) { UISystem.Compose -> ComposeChart3(chartEntryModelProducer) UISystem.Views -> ViewChart3(chartEntryModelProducer) @@ -58,28 +61,32 @@ private fun ComposeChart3(chartEntryModelProducer: ChartEntryModelProducer) { Chart( chart = lineChart(axisValuesOverrider = axisValueOverrider), chartModelProducer = chartEntryModelProducer, - startAxis = rememberStartAxis( - guideline = null, - horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside, - titleComponent = textComponent( - color = Color.Black, - background = shapeComponent(Shapes.pillShape, color1), - padding = axisTitlePadding, - margins = startAxisTitleMargins, - typeface = Typeface.MONOSPACE, + startAxis = + rememberStartAxis( + guideline = null, + horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside, + titleComponent = + textComponent( + color = Color.Black, + background = shapeComponent(Shapes.pillShape, color1), + padding = axisTitlePadding, + margins = startAxisTitleMargins, + typeface = Typeface.MONOSPACE, + ), + title = stringResource(R.string.y_axis), ), - title = stringResource(R.string.y_axis), - ), - bottomAxis = rememberBottomAxis( - titleComponent = textComponent( - background = shapeComponent(Shapes.pillShape, color2), - color = Color.White, - padding = axisTitlePadding, - margins = bottomAxisTitleMargins, - typeface = Typeface.MONOSPACE, + bottomAxis = + rememberBottomAxis( + titleComponent = + textComponent( + background = shapeComponent(Shapes.pillShape, color2), + color = Color.White, + padding = axisTitlePadding, + margins = bottomAxisTitleMargins, + typeface = Typeface.MONOSPACE, + ), + title = stringResource(R.string.x_axis), ), - title = stringResource(R.string.x_axis), - ), marker = rememberMarker(), runInitialAnimation = false, fadingEdges = rememberFadingEdges(), diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart4.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart4.kt index 0265dc2d8..a3ec02270 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart4.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart4.kt @@ -41,7 +41,10 @@ import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable -internal fun Chart4(uiSystem: UISystem, chartEntryModelProducer: ComposedChartEntryModelProducer) { +internal fun Chart4( + uiSystem: UISystem, + chartEntryModelProducer: ComposedChartEntryModelProducer, +) { when (uiSystem) { UISystem.Compose -> ComposeChart4(chartEntryModelProducer) UISystem.Views -> ViewChart4(chartEntryModelProducer) @@ -53,22 +56,24 @@ private fun ComposeChart4(chartEntryModelProducer: ComposedChartEntryModelProduc ProvideChartStyle(rememberChartStyle(columnChartColors, lineChartColors)) { val defaultColumns = currentChartStyle.columnChart.columns val defaultLines = currentChartStyle.lineChart.lines - val columnChart = columnChart( - remember(defaultColumns) { - defaultColumns.map { defaultColumn -> - LineComponent( - defaultColumn.color, - defaultColumn.thicknessDp, - Shapes.roundedCornerShape(columnCornerRadius), - ) - } - }, - ) - val lineChart = lineChart( - remember(defaultLines) { - defaultLines.map { defaultLine -> defaultLine.copy(pointConnector = pointConnector) } - }, - ) + val columnChart = + columnChart( + remember(defaultColumns) { + defaultColumns.map { defaultColumn -> + LineComponent( + defaultColumn.color, + defaultColumn.thicknessDp, + Shapes.roundedCornerShape(columnCornerRadius), + ) + } + }, + ) + val lineChart = + lineChart( + remember(defaultLines) { + defaultLines.map { defaultLine -> defaultLine.copy(pointConnector = pointConnector) } + }, + ) Chart( chart = remember(columnChart, lineChart) { columnChart + lineChart }, chartModelProducer = chartEntryModelProducer, diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart5.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart5.kt index 26d0e04f4..75602d707 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart5.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart5.kt @@ -39,7 +39,10 @@ import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable -internal fun Chart5(uiSystem: UISystem, chartEntryModelProducer: ChartEntryModelProducer) { +internal fun Chart5( + uiSystem: UISystem, + chartEntryModelProducer: ChartEntryModelProducer, +) { when (uiSystem) { UISystem.Compose -> ComposeChart5(chartEntryModelProducer) UISystem.Views -> ViewChart5(chartEntryModelProducer) @@ -51,31 +54,35 @@ private fun ComposeChart5(chartEntryModelProducer: ChartEntryModelProducer) { ProvideChartStyle(rememberChartStyle(chartColors)) { val defaultColumns = currentChartStyle.columnChart.columns Chart( - chart = columnChart( - columns = remember(defaultColumns) { - defaultColumns.mapIndexed { index, defaultColumn -> - val topCornerRadiusPercent = - if (index == defaultColumns.lastIndex) DefaultDimens.COLUMN_ROUNDNESS_PERCENT else 0 - val bottomCornerRadiusPercent = if (index == 0) DefaultDimens.COLUMN_ROUNDNESS_PERCENT else 0 - LineComponent( - defaultColumn.color, - defaultColumn.thicknessDp, - Shapes.roundedCornerShape( - topCornerRadiusPercent, - topCornerRadiusPercent, - bottomCornerRadiusPercent, - bottomCornerRadiusPercent, - ), - ) - } - }, - mergeMode = ColumnChart.MergeMode.Stack, - ), + chart = + columnChart( + columns = + remember(defaultColumns) { + defaultColumns.mapIndexed { index, defaultColumn -> + val topCornerRadiusPercent = + if (index == defaultColumns.lastIndex) DefaultDimens.COLUMN_ROUNDNESS_PERCENT else 0 + val bottomCornerRadiusPercent = + if (index == 0) DefaultDimens.COLUMN_ROUNDNESS_PERCENT else 0 + LineComponent( + defaultColumn.color, + defaultColumn.thicknessDp, + Shapes.roundedCornerShape( + topCornerRadiusPercent, + topCornerRadiusPercent, + bottomCornerRadiusPercent, + bottomCornerRadiusPercent, + ), + ) + } + }, + mergeMode = ColumnChart.MergeMode.Stack, + ), chartModelProducer = chartEntryModelProducer, - startAxis = rememberStartAxis( - itemPlacer = startAxisItemPlacer, - labelRotationDegrees = AXIS_LABEL_ROTATION_DEGREES, - ), + startAxis = + rememberStartAxis( + itemPlacer = startAxisItemPlacer, + labelRotationDegrees = AXIS_LABEL_ROTATION_DEGREES, + ), bottomAxis = rememberBottomAxis(labelRotationDegrees = AXIS_LABEL_ROTATION_DEGREES), marker = rememberMarker(), runInitialAnimation = false, diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart6.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart6.kt index 4d8add880..abc53e17c 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart6.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart6.kt @@ -45,7 +45,10 @@ import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable -internal fun Chart6(uiSystem: UISystem, chartEntryModelProducer: ChartEntryModelProducer) { +internal fun Chart6( + uiSystem: UISystem, + chartEntryModelProducer: ChartEntryModelProducer, +) { when (uiSystem) { UISystem.Compose -> ComposeChart6(chartEntryModelProducer) UISystem.Views -> ViewChart6(chartEntryModelProducer) @@ -58,19 +61,21 @@ private fun ComposeChart6(chartEntryModelProducer: ChartEntryModelProducer) { ProvideChartStyle(rememberChartStyle(chartColors)) { val defaultColumns = currentChartStyle.columnChart.columns Chart( - chart = columnChart( - columns = remember(defaultColumns) { - defaultColumns.map { defaultColumn -> - LineComponent( - defaultColumn.color, - defaultColumn.thicknessDp, - Shapes.cutCornerShape(topLeftPercent = COLUMN_CORNER_CUT_SIZE_PERCENT), - ) - } - }, - mergeMode = ColumnChart.MergeMode.Grouped, - decorations = remember(thresholdLine) { listOf(thresholdLine) }, - ), + chart = + columnChart( + columns = + remember(defaultColumns) { + defaultColumns.map { defaultColumn -> + LineComponent( + defaultColumn.color, + defaultColumn.thicknessDp, + Shapes.cutCornerShape(topLeftPercent = COLUMN_CORNER_CUT_SIZE_PERCENT), + ) + } + }, + mergeMode = ColumnChart.MergeMode.Grouped, + decorations = remember(thresholdLine) { listOf(thresholdLine) }, + ), chartModelProducer = chartEntryModelProducer, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(valueFormatter = bottomAxisValueFormatter), @@ -98,13 +103,14 @@ private fun ViewChart6(chartEntryModelProducer: ChartEntryModelProducer) { @Composable private fun rememberThresholdLine(): ThresholdLine { - val label = textComponent( - color = Color.Black, - background = shapeComponent(Shapes.rectShape, color4), - padding = thresholdLineLabelPadding, - margins = thresholdLineLabelMargins, - typeface = Typeface.MONOSPACE, - ) + val label = + textComponent( + color = Color.Black, + background = shapeComponent(Shapes.rectShape, color4), + padding = thresholdLineLabelPadding, + margins = thresholdLineLabelMargins, + typeface = Typeface.MONOSPACE, + ) val line = shapeComponent(color = thresholdLineColor) return remember(label, line) { ThresholdLine(thresholdRange = thresholdLineValueRange, labelComponent = label, lineComponent = line) diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart7.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart7.kt index 5f4035d2f..3054f75d1 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart7.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart7.kt @@ -48,7 +48,10 @@ import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable -internal fun Chart7(uiSystem: UISystem, chartEntryModelProducer: ChartEntryModelProducer) { +internal fun Chart7( + uiSystem: UISystem, + chartEntryModelProducer: ChartEntryModelProducer, +) { when (uiSystem) { UISystem.Compose -> ComposeChart7(chartEntryModelProducer) UISystem.Views -> ViewChart7(chartEntryModelProducer) @@ -60,16 +63,18 @@ private fun ComposeChart7(chartEntryModelProducer: ChartEntryModelProducer) { ProvideChartStyle(rememberChartStyle(chartColors)) { val defaultLines = currentChartStyle.lineChart.lines Chart( - chart = lineChart( - remember(defaultLines) { - defaultLines.map { defaultLine -> defaultLine.copy(lineBackgroundFill = null) } - }, - ), + chart = + lineChart( + remember(defaultLines) { + defaultLines.map { defaultLine -> defaultLine.copy(lineBackgroundFill = null) } + }, + ), chartModelProducer = chartEntryModelProducer, - startAxis = rememberStartAxis( - label = rememberStartAxisLabel(), - horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside, - ), + startAxis = + rememberStartAxis( + label = rememberStartAxisLabel(), + horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside, + ), bottomAxis = rememberBottomAxis(), marker = rememberMarker(), legend = rememberLegend(), @@ -96,33 +101,37 @@ private fun ViewChart7(chartEntryModelProducer: ChartEntryModelProducer) { } @Composable -private fun rememberStartAxisLabel() = axisLabelComponent( - color = Color.Black, - verticalPadding = startAxisLabelVerticalPaddingValue, - horizontalPadding = startAxisLabelHorizontalPaddingValue, - verticalMargin = startAxisLabelMarginValue, - horizontalMargin = startAxisLabelMarginValue, - background = shapeComponent(Shapes.roundedCornerShape(startAxisLabelBackgroundCornerRadius), color4), -) +private fun rememberStartAxisLabel() = + axisLabelComponent( + color = Color.Black, + verticalPadding = startAxisLabelVerticalPaddingValue, + horizontalPadding = startAxisLabelHorizontalPaddingValue, + verticalMargin = startAxisLabelMarginValue, + horizontalMargin = startAxisLabelMarginValue, + background = shapeComponent(Shapes.roundedCornerShape(startAxisLabelBackgroundCornerRadius), color4), + ) @Composable -private fun rememberLegend() = verticalLegend( - items = chartColors.mapIndexed { index, chartColor -> - legendItem( - icon = shapeComponent(Shapes.pillShape, chartColor), - label = textComponent( - color = currentChartStyle.axis.axisLabelColor, - textSize = legendItemLabelTextSize, - typeface = Typeface.MONOSPACE, - ), - labelText = stringResource(R.string.data_set_x, index + 1), - ) - }, - iconSize = legendItemIconSize, - iconPadding = legendItemIconPaddingValue, - spacing = legendItemSpacing, - padding = legendPadding, -) +private fun rememberLegend() = + verticalLegend( + items = + chartColors.mapIndexed { index, chartColor -> + legendItem( + icon = shapeComponent(Shapes.pillShape, chartColor), + label = + textComponent( + color = currentChartStyle.axis.axisLabelColor, + textSize = legendItemLabelTextSize, + typeface = Typeface.MONOSPACE, + ), + labelText = stringResource(R.string.data_set_x, index + 1), + ) + }, + iconSize = legendItemIconSize, + iconPadding = legendItemIconPaddingValue, + spacing = legendItemSpacing, + padding = legendPadding, + ) private const val COLOR_1_CODE = 0xffb983ff private const val COLOR_2_CODE = 0xff91b1fd diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart8.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart8.kt index 062e631dc..a72d6dccd 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart8.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart8.kt @@ -39,7 +39,10 @@ import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable -internal fun Chart8(uiSystem: UISystem, chartEntryModelProducer: ComposedChartEntryModelProducer) { +internal fun Chart8( + uiSystem: UISystem, + chartEntryModelProducer: ComposedChartEntryModelProducer, +) { when (uiSystem) { UISystem.Compose -> ComposeChart8(chartEntryModelProducer) UISystem.Views -> ViewChart8(chartEntryModelProducer) @@ -49,10 +52,11 @@ internal fun Chart8(uiSystem: UISystem, chartEntryModelProducer: ComposedChartEn @Composable private fun ComposeChart8(chartEntryModelProducer: ComposedChartEntryModelProducer) { ProvideChartStyle(rememberChartStyle(columnChartColors, lineChartColors)) { - val columnChart = columnChart( - mergeMode = ColumnChart.MergeMode.Stack, - targetVerticalAxisPosition = AxisPosition.Vertical.Start, - ) + val columnChart = + columnChart( + mergeMode = ColumnChart.MergeMode.Stack, + targetVerticalAxisPosition = AxisPosition.Vertical.Start, + ) val lineChart = lineChart(targetVerticalAxisPosition = AxisPosition.Vertical.End) Chart( chart = remember(columnChart, lineChart) { columnChart + lineChart }, diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart9.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart9.kt index e61e652b9..c78dee52b 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart9.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart9.kt @@ -57,7 +57,10 @@ import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable -internal fun Chart9(uiSystem: UISystem, chartEntryModelProducer: ChartEntryModelProducer) { +internal fun Chart9( + uiSystem: UISystem, + chartEntryModelProducer: ChartEntryModelProducer, +) { when (uiSystem) { UISystem.Compose -> ComposeChart9(chartEntryModelProducer) UISystem.Views -> ViewChart9(chartEntryModelProducer) @@ -69,74 +72,86 @@ private fun ComposeChart9(chartEntryModelProducer: ChartEntryModelProducer) { val marker = rememberMarker() ProvideChartStyle(rememberChartStyle(chartColors)) { Chart( - chart = lineChart( - lines = listOf( - lineSpec( - lineShader = DynamicShaders.split(chartColors[0], chartColors[1]), - lineBackgroundShader = DynamicShaders.splitShader( - DynamicShaders.composeShader( - DynamicShaders.fromComponent( - componentSize = 6.dp, - component = shapeComponent( - shape = Shapes.pillShape, - color = chartColors[0], - margins = remember { dimensionsOf(1.dp) }, - ), - ), - verticalGradient(arrayOf(Color.Black, Color.Transparent)), - PorterDuff.Mode.DST_IN, - ), - DynamicShaders.composeShader( - DynamicShaders.fromComponent( - componentSize = 5.dp, - component = shapeComponent( - shape = Shapes.rectShape, - color = chartColors[1], - margins = remember { dimensionsOf(horizontal = 2.dp) }, + chart = + lineChart( + lines = + listOf( + lineSpec( + lineShader = DynamicShaders.split(chartColors[0], chartColors[1]), + lineBackgroundShader = + DynamicShaders.splitShader( + DynamicShaders.composeShader( + DynamicShaders.fromComponent( + componentSize = 6.dp, + component = + shapeComponent( + shape = Shapes.pillShape, + color = chartColors[0], + margins = remember { dimensionsOf(1.dp) }, + ), + ), + verticalGradient(arrayOf(Color.Black, Color.Transparent)), + PorterDuff.Mode.DST_IN, + ), + DynamicShaders.composeShader( + DynamicShaders.fromComponent( + componentSize = 5.dp, + component = + shapeComponent( + shape = Shapes.rectShape, + color = chartColors[1], + margins = remember { dimensionsOf(horizontal = 2.dp) }, + ), + checkeredArrangement = false, + ), + verticalGradient(arrayOf(Color.Transparent, Color.Black)), + PorterDuff.Mode.DST_IN, + ), ), - checkeredArrangement = false, - ), - verticalGradient(arrayOf(Color.Transparent, Color.Black)), - PorterDuff.Mode.DST_IN, ), ), - ), ), - ), chartModelProducer = chartEntryModelProducer, - startAxis = rememberStartAxis( - label = axisLabelComponent( - color = MaterialTheme.colorScheme.onBackground, - background = shapeComponent( - shape = Shapes.pillShape, - color = MaterialTheme.colorScheme.background, - strokeColor = MaterialTheme.colorScheme.outlineVariant, - strokeWidth = 1.dp, - ), - padding = remember { dimensionsOf(horizontal = 6.dp, vertical = 2.dp) }, - margins = remember { dimensionsOf(end = 8.dp) }, - ), - axis = null, - tick = null, - guideline = lineComponent( - thickness = 1.dp, - color = MaterialTheme.colorScheme.outlineVariant, - shape = remember { - Shapes.dashedShape( - shape = Shapes.pillShape, - dashLength = 4.dp, - gapLength = 8.dp, - ) - }, + startAxis = + rememberStartAxis( + label = + axisLabelComponent( + color = MaterialTheme.colorScheme.onBackground, + background = + shapeComponent( + shape = Shapes.pillShape, + color = MaterialTheme.colorScheme.background, + strokeColor = MaterialTheme.colorScheme.outlineVariant, + strokeWidth = 1.dp, + ), + padding = remember { dimensionsOf(horizontal = 6.dp, vertical = 2.dp) }, + margins = remember { dimensionsOf(end = 8.dp) }, + ), + axis = null, + tick = null, + guideline = + lineComponent( + thickness = 1.dp, + color = MaterialTheme.colorScheme.outlineVariant, + shape = + remember { + Shapes.dashedShape( + shape = Shapes.pillShape, + dashLength = 4.dp, + gapLength = 8.dp, + ) + }, + ), + itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 4) }, ), - itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 4) }, - ), - bottomAxis = rememberBottomAxis( - guideline = null, - itemPlacer = AxisItemPlacer.Horizontal.default( - spacing = 3, + bottomAxis = + rememberBottomAxis( + guideline = null, + itemPlacer = + AxisItemPlacer.Horizontal.default( + spacing = 3, + ), ), - ), marker = marker, runInitialAnimation = false, horizontalLayout = HorizontalLayout.fullWidth(), @@ -155,38 +170,42 @@ private fun ViewChart9(chartEntryModelProducer: ChartEntryModelProducer) { (bottomAxis as Axis).guideline = null this.marker = marker with(chart as LineChart) { - lines = listOf( - LineChart.LineSpec( - lineShader = DynamicShaders.split(colors[0], colors[1]), - lineBackgroundShader = DynamicShaders.splitShader( - DynamicShaders.composeShader( - DynamicShaders.fromComponent( - componentSize = 6.dp, - component = ShapeComponent( - shape = Shapes.pillShape, - color = colors[0].toArgb(), - margins = dimensionsOf(1.dp), + lines = + listOf( + LineChart.LineSpec( + lineShader = DynamicShaders.split(colors[0], colors[1]), + lineBackgroundShader = + DynamicShaders.splitShader( + DynamicShaders.composeShader( + DynamicShaders.fromComponent( + componentSize = 6.dp, + component = + ShapeComponent( + shape = Shapes.pillShape, + color = colors[0].toArgb(), + margins = dimensionsOf(1.dp), + ), + ), + verticalGradient(arrayOf(Color.Black, Color.Transparent)), + PorterDuff.Mode.DST_IN, ), - ), - verticalGradient(arrayOf(Color.Black, Color.Transparent)), - PorterDuff.Mode.DST_IN, - ), - DynamicShaders.composeShader( - DynamicShaders.fromComponent( - componentSize = 5.dp, - component = ShapeComponent( - shape = Shapes.rectShape, - color = colors[1].toArgb(), - margins = dimensionsOf(horizontal = 2.dp), + DynamicShaders.composeShader( + DynamicShaders.fromComponent( + componentSize = 5.dp, + component = + ShapeComponent( + shape = Shapes.rectShape, + color = colors[1].toArgb(), + margins = dimensionsOf(horizontal = 2.dp), + ), + checkeredArrangement = false, + ), + verticalGradient(arrayOf(Color.Transparent, Color.Black)), + PorterDuff.Mode.DST_IN, ), - checkeredArrangement = false, ), - verticalGradient(arrayOf(Color.Transparent, Color.Black)), - PorterDuff.Mode.DST_IN, - ), ), - ), - ) + ) } } } @@ -195,7 +214,8 @@ private fun ViewChart9(chartEntryModelProducer: ChartEntryModelProducer) { private val chartColors @ReadOnlyComposable @Composable - get() = listOf( - colorResource(id = R.color.chart_9_color_positive), - colorResource(id = R.color.chart_9_color_negative), - ) + get() = + listOf( + colorResource(id = R.color.chart_9_color_positive), + colorResource(id = R.color.chart_9_color_negative), + ) diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/utils/PaddingValues.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/utils/PaddingValues.kt index d275a9929..14ace0f81 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/utils/PaddingValues.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/utils/PaddingValues.kt @@ -19,14 +19,15 @@ package com.patrykandpatrick.vico.sample.utils import androidx.compose.foundation.layout.PaddingValues import androidx.compose.ui.unit.LayoutDirection -internal operator fun PaddingValues.plus(other: PaddingValues) = object : PaddingValues { - override fun calculateLeftPadding(layoutDirection: LayoutDirection) = - this@plus.calculateLeftPadding(layoutDirection) + other.calculateLeftPadding(layoutDirection) +internal operator fun PaddingValues.plus(other: PaddingValues) = + object : PaddingValues { + override fun calculateLeftPadding(layoutDirection: LayoutDirection) = + this@plus.calculateLeftPadding(layoutDirection) + other.calculateLeftPadding(layoutDirection) - override fun calculateTopPadding() = this@plus.calculateTopPadding() + other.calculateTopPadding() + override fun calculateTopPadding() = this@plus.calculateTopPadding() + other.calculateTopPadding() - override fun calculateRightPadding(layoutDirection: LayoutDirection) = - this@plus.calculateRightPadding(layoutDirection) + other.calculateRightPadding(layoutDirection) + override fun calculateRightPadding(layoutDirection: LayoutDirection) = + this@plus.calculateRightPadding(layoutDirection) + other.calculateRightPadding(layoutDirection) - override fun calculateBottomPadding() = this@plus.calculateBottomPadding() + other.calculateBottomPadding() -} + override fun calculateBottomPadding() = this@plus.calculateBottomPadding() + other.calculateBottomPadding() + } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/utils/VicoTheme.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/utils/VicoTheme.kt index df72fb89a..7d72a5a1c 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/utils/VicoTheme.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/utils/VicoTheme.kt @@ -26,14 +26,16 @@ import androidx.compose.ui.graphics.Color @Composable fun VicoTheme(content: @Composable () -> Unit) { - val darkColorScheme = darkColorScheme( - surface = Color(color = DARK_SURFACE), - background = Color.Black, - ) - val lightColorScheme = lightColorScheme( - surface = Color.White, - background = Color(color = LIGHT_BACKGROUND), - ) + val darkColorScheme = + darkColorScheme( + surface = Color(color = DARK_SURFACE), + background = Color.Black, + ) + val lightColorScheme = + lightColorScheme( + surface = Color.White, + background = Color(color = LIGHT_BACKGROUND), + ) MaterialTheme( colorScheme = if (isSystemInDarkTheme()) darkColorScheme else lightColorScheme, typography = Typography(), diff --git a/sample/src/test/kotlin/com/patrykandpatrick/vico/sample/PaparazziTest.kt b/sample/src/test/kotlin/com/patrykandpatrick/vico/sample/PaparazziTest.kt index 1c05aa2ed..97aa4d70f 100644 --- a/sample/src/test/kotlin/com/patrykandpatrick/vico/sample/PaparazziTest.kt +++ b/sample/src/test/kotlin/com/patrykandpatrick/vico/sample/PaparazziTest.kt @@ -32,17 +32,17 @@ import org.junit.Rule import org.junit.Test public class PaparazziTest { - - private val defaultCharts = listOf Unit>>( - "LineChart" to { DefaultLineChart() }, - "LineChart Long Scrollable" to { DefaultLineChartLongScrollable() }, - "LineChart Long Scrollable with initial scroll end" to { DefaultLineChartLongScrollableEnd() }, - "LineChart Long Not Scrollable" to { DefaultLineChartLongNonScrollable() }, - "ColumnChart" to { DefaultColumnChart() }, - "ColumnChart Long Scrollable" to { DefaultColumnChartLongScrollable() }, - "ColumnChart Long Scrollable with initial scroll end" to { DefaultColumnChartLongScrollableEnd() }, - "ColumnChart Long Not Scrollable" to { DefaultColumnChartLongNonScrollable() }, - ) + private val defaultCharts = + listOf Unit>>( + "LineChart" to { DefaultLineChart() }, + "LineChart Long Scrollable" to { DefaultLineChartLongScrollable() }, + "LineChart Long Scrollable with initial scroll end" to { DefaultLineChartLongScrollableEnd() }, + "LineChart Long Not Scrollable" to { DefaultLineChartLongNonScrollable() }, + "ColumnChart" to { DefaultColumnChart() }, + "ColumnChart Long Scrollable" to { DefaultColumnChartLongScrollable() }, + "ColumnChart Long Scrollable with initial scroll end" to { DefaultColumnChartLongScrollableEnd() }, + "ColumnChart Long Not Scrollable" to { DefaultColumnChartLongNonScrollable() }, + ) @get:Rule public val paparazzi: Paparazzi = Paparazzi(deviceConfig = lightConfig) diff --git a/vico/compose-m2/src/main/java/com/patrykandpatrick/vico/compose/m2/style/M2ChartStyle.kt b/vico/compose-m2/src/main/java/com/patrykandpatrick/vico/compose/m2/style/M2ChartStyle.kt index 5dfaec5db..5ca177716 100644 --- a/vico/compose-m2/src/main/java/com/patrykandpatrick/vico/compose/m2/style/M2ChartStyle.kt +++ b/vico/compose-m2/src/main/java/com/patrykandpatrick/vico/compose/m2/style/M2ChartStyle.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. @@ -32,25 +32,27 @@ public fun m2ChartStyle( axisLabelColor: Color = MaterialTheme.colors.onBackground, axisGuidelineColor: Color = MaterialTheme.colors.onBackground.copy(alpha = LINE_ALPHA), axisLineColor: Color = MaterialTheme.colors.onBackground.copy(alpha = LINE_ALPHA), - entityColors: List = listOf( - MaterialTheme.colors.primary, - MaterialTheme.colors.secondary, - ), + entityColors: List = + listOf( + MaterialTheme.colors.primary, + MaterialTheme.colors.secondary, + ), elevationOverlayColor: Color = if (isSystemInDarkTheme()) MaterialTheme.colors.onBackground else Color.Transparent, -): ChartStyle = remember( - axisLabelColor, - axisGuidelineColor, - axisLineColor, - entityColors, - elevationOverlayColor, -) { - ChartStyle.fromColors( - axisLabelColor = axisLabelColor, - axisGuidelineColor = axisGuidelineColor, - axisLineColor = axisLineColor, - entityColors = entityColors, - elevationOverlayColor = elevationOverlayColor, - ) -} +): ChartStyle = + remember( + axisLabelColor, + axisGuidelineColor, + axisLineColor, + entityColors, + elevationOverlayColor, + ) { + ChartStyle.fromColors( + axisLabelColor = axisLabelColor, + axisGuidelineColor = axisGuidelineColor, + axisLineColor = axisLineColor, + entityColors = entityColors, + elevationOverlayColor = elevationOverlayColor, + ) + } private const val LINE_ALPHA = 0.2f diff --git a/vico/compose-m3/src/main/java/com/patrykandpatrick/vico/compose/m3/style/M3ChartStyle.kt b/vico/compose-m3/src/main/java/com/patrykandpatrick/vico/compose/m3/style/M3ChartStyle.kt index 42be13689..d62aec59a 100644 --- a/vico/compose-m3/src/main/java/com/patrykandpatrick/vico/compose/m3/style/M3ChartStyle.kt +++ b/vico/compose-m3/src/main/java/com/patrykandpatrick/vico/compose/m3/style/M3ChartStyle.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. @@ -31,24 +31,26 @@ public fun m3ChartStyle( axisLabelColor: Color = MaterialTheme.colorScheme.onBackground, axisGuidelineColor: Color = MaterialTheme.colorScheme.outline, axisLineColor: Color = MaterialTheme.colorScheme.outline, - entityColors: List = listOf( - MaterialTheme.colorScheme.primary, - MaterialTheme.colorScheme.secondary, - MaterialTheme.colorScheme.tertiary, - ), + entityColors: List = + listOf( + MaterialTheme.colorScheme.primary, + MaterialTheme.colorScheme.secondary, + MaterialTheme.colorScheme.tertiary, + ), elevationOverlayColor: Color = MaterialTheme.colorScheme.primary, -): ChartStyle = remember( - axisLabelColor, - axisGuidelineColor, - axisLineColor, - entityColors, - elevationOverlayColor, -) { - ChartStyle.fromColors( - axisLabelColor = axisLabelColor, - axisGuidelineColor = axisGuidelineColor, - axisLineColor = axisLineColor, - entityColors = entityColors, - elevationOverlayColor = elevationOverlayColor, - ) -} +): ChartStyle = + remember( + axisLabelColor, + axisGuidelineColor, + axisLineColor, + entityColors, + elevationOverlayColor, + ) { + ChartStyle.fromColors( + axisLabelColor = axisLabelColor, + axisGuidelineColor = axisGuidelineColor, + axisLineColor = axisLineColor, + entityColors = entityColors, + elevationOverlayColor = elevationOverlayColor, + ) + } diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/axis/AxisComponents.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/axis/AxisComponents.kt index 20a044640..bedc0b797 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/axis/AxisComponents.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/axis/AxisComponents.kt @@ -71,17 +71,18 @@ public fun axisLabelComponent( horizontalMargin: Dp = currentChartStyle.axis.axisLabelHorizontalMargin, typeface: Typeface = currentChartStyle.axis.axisLabelTypeface, textAlignment: Layout.Alignment = currentChartStyle.axis.axisLabelTextAlignment, -): TextComponent = textComponent( - color, - textSize, - background, - ellipsize, - lineCount, - dimensionsOf(horizontalPadding, verticalPadding), - dimensionsOf(horizontalMargin, verticalMargin), - typeface, - textAlignment, -) +): TextComponent = + textComponent( + color, + textSize, + background, + ellipsize, + lineCount, + dimensionsOf(horizontalPadding, verticalPadding), + dimensionsOf(horizontalMargin, verticalMargin), + typeface, + textAlignment, + ) /** * Creates a [TextComponent] to be used for axis labels. @@ -103,27 +104,30 @@ public fun axisLabelComponent( background: ShapeComponent? = currentChartStyle.axis.axisLabelBackground, ellipsize: TextUtils.TruncateAt = TextUtils.TruncateAt.END, lineCount: Int = currentChartStyle.axis.axisLabelLineCount, - padding: MutableDimensions = dimensionsOf( - horizontal = currentChartStyle.axis.axisLabelHorizontalPadding, - vertical = currentChartStyle.axis.axisLabelVerticalPadding, - ), - margins: MutableDimensions = dimensionsOf( - horizontal = currentChartStyle.axis.axisLabelHorizontalMargin, - vertical = currentChartStyle.axis.axisLabelVerticalMargin, - ), + padding: MutableDimensions = + dimensionsOf( + horizontal = currentChartStyle.axis.axisLabelHorizontalPadding, + vertical = currentChartStyle.axis.axisLabelVerticalPadding, + ), + margins: MutableDimensions = + dimensionsOf( + horizontal = currentChartStyle.axis.axisLabelHorizontalMargin, + vertical = currentChartStyle.axis.axisLabelVerticalMargin, + ), typeface: Typeface = currentChartStyle.axis.axisLabelTypeface, textAlignment: Layout.Alignment = currentChartStyle.axis.axisLabelTextAlignment, -): TextComponent = textComponent( - color, - textSize, - background, - ellipsize, - lineCount, - padding, - margins, - typeface, - textAlignment, -) +): TextComponent = + textComponent( + color, + textSize, + background, + ellipsize, + lineCount, + padding, + margins, + typeface, + textAlignment, + ) /** * Creates a [LineComponent] styled as an axis line. @@ -145,15 +149,16 @@ public fun axisLineComponent( strokeColor: Color = Color.Transparent, dynamicShader: DynamicShader? = null, margins: Dimensions = emptyDimensions(), -): LineComponent = lineComponent( - color = color, - thickness = thickness, - dynamicShader = dynamicShader, - shape = shape, - margins = margins, - strokeWidth = strokeWidth, - strokeColor = strokeColor, -) +): LineComponent = + lineComponent( + color = color, + thickness = thickness, + dynamicShader = dynamicShader, + shape = shape, + margins = margins, + strokeWidth = strokeWidth, + strokeColor = strokeColor, + ) /** * Creates a [LineComponent] styled as an axis line. @@ -175,15 +180,16 @@ public fun axisLineComponent( strokeColor: Color = Color.Transparent, brush: Brush? = null, margins: Dimensions = emptyDimensions(), -): LineComponent = lineComponent( - color = color, - thickness = thickness, - dynamicShader = brush?.let(::BrushShader), - shape = shape, - margins = margins, - strokeWidth = strokeWidth, - strokeColor = strokeColor, -) +): LineComponent = + lineComponent( + color = color, + thickness = thickness, + dynamicShader = brush?.let(::BrushShader), + shape = shape, + margins = margins, + strokeWidth = strokeWidth, + strokeColor = strokeColor, + ) /** * Creates a [LineComponent] styled as a tick line. @@ -203,14 +209,15 @@ public fun axisTickComponent( strokeWidth: Dp = 0.dp, strokeColor: Color = Color.Transparent, dynamicShader: DynamicShader? = null, -): LineComponent = lineComponent( - color = color, - thickness = thickness, - dynamicShader = dynamicShader, - shape = shape, - strokeWidth = strokeWidth, - strokeColor = strokeColor, -) +): LineComponent = + lineComponent( + color = color, + thickness = thickness, + dynamicShader = dynamicShader, + shape = shape, + strokeWidth = strokeWidth, + strokeColor = strokeColor, + ) /** * Creates a [LineComponent] styled as a tick line. @@ -230,14 +237,15 @@ public fun axisTickComponent( strokeWidth: Dp = 0.dp, strokeColor: Color = Color.Transparent, brush: Brush? = null, -): LineComponent = lineComponent( - color = color, - thickness = thickness, - dynamicShader = brush?.let(::BrushShader), - shape = shape.chartShape(), - strokeWidth = strokeWidth, - strokeColor = strokeColor, -) +): LineComponent = + lineComponent( + color = color, + thickness = thickness, + dynamicShader = brush?.let(::BrushShader), + shape = shape.chartShape(), + strokeWidth = strokeWidth, + strokeColor = strokeColor, + ) /** * Creates an axis guideline. @@ -259,12 +267,13 @@ public fun axisGuidelineComponent( strokeColor: Color = Color.Transparent, dynamicShader: DynamicShader? = null, margins: Dimensions = emptyDimensions(), -): LineComponent = lineComponent( - color = color, - thickness = thickness, - dynamicShader = dynamicShader, - shape = shape, - margins = margins, - strokeWidth = strokeWidth, - strokeColor = strokeColor, -) +): LineComponent = + lineComponent( + color = color, + thickness = thickness, + dynamicShader = dynamicShader, + shape = shape, + margins = margins, + strokeWidth = strokeWidth, + strokeColor = strokeColor, + ) diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/axis/vertical/VerticalAxis.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/axis/vertical/VerticalAxis.kt index aa9215176..77948388a 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/axis/vertical/VerticalAxis.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/axis/vertical/VerticalAxis.kt @@ -66,21 +66,22 @@ public fun rememberStartAxis( labelRotationDegrees: Float = currentChartStyle.axis.axisLabelRotationDegrees, titleComponent: TextComponent? = null, title: CharSequence? = null, -): VerticalAxis = remember { createVerticalAxis() }.apply { - this.label = label - axisLine = axis - this.tick = tick - this.guideline = guideline - this.valueFormatter = valueFormatter - tickLengthDp = tickLength.value - this.sizeConstraint = sizeConstraint - this.horizontalLabelPosition = horizontalLabelPosition - this.verticalLabelPosition = verticalLabelPosition - this.itemPlacer = itemPlacer - this.labelRotationDegrees = labelRotationDegrees - this.titleComponent = titleComponent - this.title = title -} +): VerticalAxis = + remember { createVerticalAxis() }.apply { + this.label = label + axisLine = axis + this.tick = tick + this.guideline = guideline + this.valueFormatter = valueFormatter + tickLengthDp = tickLength.value + this.sizeConstraint = sizeConstraint + this.horizontalLabelPosition = horizontalLabelPosition + this.verticalLabelPosition = verticalLabelPosition + this.itemPlacer = itemPlacer + this.labelRotationDegrees = labelRotationDegrees + this.titleComponent = titleComponent + this.title = title + } /** * Creates and remembers an end axis (i.e., a [VerticalAxis] with [AxisPosition.Vertical.End]). @@ -114,18 +115,19 @@ public fun rememberEndAxis( labelRotationDegrees: Float = currentChartStyle.axis.axisLabelRotationDegrees, titleComponent: TextComponent? = null, title: CharSequence? = null, -): VerticalAxis = remember { createVerticalAxis() }.apply { - this.label = label - axisLine = axis - this.tick = tick - this.guideline = guideline - this.valueFormatter = valueFormatter - tickLengthDp = tickLength.value - this.sizeConstraint = sizeConstraint - this.horizontalLabelPosition = horizontalLabelPosition - this.verticalLabelPosition = verticalLabelPosition - this.itemPlacer = itemPlacer - this.labelRotationDegrees = labelRotationDegrees - this.titleComponent = titleComponent - this.title = title -} +): VerticalAxis = + remember { createVerticalAxis() }.apply { + this.label = label + axisLine = axis + this.tick = tick + this.guideline = guideline + this.valueFormatter = valueFormatter + tickLengthDp = tickLength.value + this.sizeConstraint = sizeConstraint + this.horizontalLabelPosition = horizontalLabelPosition + this.verticalLabelPosition = verticalLabelPosition + this.itemPlacer = itemPlacer + this.labelRotationDegrees = labelRotationDegrees + this.titleComponent = titleComponent + this.title = title + } 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 1e5b796a5..cdef84178 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 @@ -250,7 +250,6 @@ public fun Chart( } } -@Suppress("LongMethod") @Composable internal fun ChartImpl( chart: Chart, @@ -276,13 +275,14 @@ internal fun ChartImpl( val markerTouchPoint = remember { mutableStateOf(null) } val zoom = remember { mutableFloatStateOf(0f) } val wasZoomOverridden = remember { mutableStateOf(false) } - val measureContext = getMeasureContext( - chartScrollSpec.isScrollEnabled, - bounds, - horizontalLayout, - with(LocalContext.current) { ::spToPx }, - chartValuesProvider, - ) + val measureContext = + getMeasureContext( + chartScrollSpec.isScrollEnabled, + bounds, + horizontalLayout, + with(LocalContext.current) { ::spToPx }, + chartValuesProvider, + ) val scrollListener = rememberScrollListener(markerTouchPoint) val lastMarkerEntryModels = remember { mutableStateOf(emptyList()) } @@ -296,27 +296,30 @@ internal fun ChartImpl( var previousModelID by remember { ValueWrapper(model.id) } val horizontalDimensions = remember { MutableHorizontalDimensions() } - val onZoom = rememberZoomState( - zoom = zoom, - wasZoomOverridden = wasZoomOverridden, - getScroll = { chartScrollState.value }, - scrollBy = { value -> coroutineScope.launch { chartScrollState.scrollBy(value) } }, - chartBounds = chart.bounds, - ) + val onZoom = + rememberZoomState( + zoom = zoom, + wasZoomOverridden = wasZoomOverridden, + getScroll = { chartScrollState.value }, + scrollBy = { value -> coroutineScope.launch { chartScrollState.scrollBy(value) } }, + chartBounds = chart.bounds, + ) Canvas( - modifier = Modifier - .fillMaxSize() - .chartTouchEvent( - setTouchPoint = remember(marker == null) { - markerTouchPoint - .component2() - .takeIf { marker != null } - }, - isScrollEnabled = chartScrollSpec.isScrollEnabled, - scrollableState = chartScrollState, - onZoom = onZoom.takeIf { isZoomEnabled }, - ), + modifier = + Modifier + .fillMaxSize() + .chartTouchEvent( + setTouchPoint = + remember(marker == null) { + markerTouchPoint + .component2() + .takeIf { marker != null } + }, + isScrollEnabled = chartScrollSpec.isScrollEnabled, + scrollableState = chartScrollState, + onZoom = onZoom.takeIf { isZoomEnabled }, + ), ) { bounds.set(left = 0, top = 0, right = size.width, bottom = size.height) @@ -350,11 +353,12 @@ internal fun ChartImpl( if (chartScrollSpec.isScrollEnabled) zoom.floatValue = finalZoom } - chartScrollState.maxValue = measureContext.getMaxScrollDistance( - chartWidth = chart.bounds.width(), - horizontalDimensions = horizontalDimensions, - zoom = finalZoom, - ) + chartScrollState.maxValue = + measureContext.getMaxScrollDistance( + chartWidth = chart.bounds.width(), + horizontalDimensions = horizontalDimensions, + zoom = finalZoom, + ) if (model.id != previousModelID) { coroutineScope.launch { chartScrollSpec.performAutoScroll(model, oldModel, chartScrollState) } @@ -363,16 +367,17 @@ internal fun ChartImpl( chartScrollState.handleInitialScroll(initialScroll = chartScrollSpec.initialScroll) - val chartDrawContext = chartDrawContext( - canvas = drawContext.canvas.nativeCanvas, - elevationOverlayColor = elevationOverlayColor, - measureContext = measureContext, - markerTouchPoint = markerTouchPoint.value, - horizontalDimensions = horizontalDimensions, - chartBounds = chart.bounds, - horizontalScroll = chartScrollState.value, - zoom = finalZoom, - ) + val chartDrawContext = + chartDrawContext( + canvas = drawContext.canvas.nativeCanvas, + elevationOverlayColor = elevationOverlayColor, + measureContext = measureContext, + markerTouchPoint = markerTouchPoint.value, + horizontalDimensions = horizontalDimensions, + chartBounds = chart.bounds, + horizontalScroll = chartScrollState.value, + zoom = finalZoom, + ) val count = if (fadingEdges != null) chartDrawContext.saveLayer() else -1 @@ -417,21 +422,25 @@ internal fun ChartBox( } @Composable -internal fun rememberScrollListener(touchPoint: MutableState): ScrollListener = remember { - object : ScrollListener { - override fun onValueChanged(oldValue: Float, newValue: Float) { - touchPoint.value?.let { point -> - touchPoint.value = point.copy(x = point.x + oldValue - newValue) +internal fun rememberScrollListener(touchPoint: MutableState): ScrollListener = + remember { + object : ScrollListener { + override fun onValueChanged( + oldValue: Float, + newValue: Float, + ) { + touchPoint.value?.let { point -> + touchPoint.value = point.copy(x = point.x + oldValue - newValue) + } } - } - override fun onScrollNotConsumed(delta: Float) { - touchPoint.value?.let { point -> - touchPoint.value = point.copy(x = point.x - delta) + override fun onScrollNotConsumed(delta: Float) { + touchPoint.value?.let { point -> + touchPoint.value = point.copy(x = point.x - delta) + } } } } -} @Composable internal fun rememberZoomState( @@ -440,14 +449,15 @@ internal fun rememberZoomState( getScroll: () -> Float, scrollBy: (value: Float) -> Unit, chartBounds: RectF, -): OnZoom = remember { - onZoom@{ centroid, zoomChange -> - val newZoom = zoom.floatValue * zoomChange - if (newZoom !in DEF_MIN_ZOOM..DEF_MAX_ZOOM) return@onZoom - val transformationAxisX = getScroll() + centroid.x - chartBounds.left - val zoomedTransformationAxisX = transformationAxisX * zoomChange - zoom.floatValue = newZoom - scrollBy(zoomedTransformationAxisX - transformationAxisX) - wasZoomOverridden.value = true +): OnZoom = + remember { + onZoom@{ centroid, zoomChange -> + val newZoom = zoom.floatValue * zoomChange + if (newZoom !in DEF_MIN_ZOOM..DEF_MAX_ZOOM) return@onZoom + val transformationAxisX = getScroll() + centroid.x - chartBounds.left + val zoomedTransformationAxisX = transformationAxisX * zoomChange + zoom.floatValue = newZoom + scrollBy(zoomedTransformationAxisX - transformationAxisX) + wasZoomOverridden.value = true + } } -} diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/column/ColumnChart.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/column/ColumnChart.kt index b55bec4b4..55b268901 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/column/ColumnChart.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/column/ColumnChart.kt @@ -78,18 +78,19 @@ public fun columnChart( axisValuesOverrider: AxisValuesOverrider? = null, drawingModelInterpolator: DrawingModelInterpolator = remember { DefaultDrawingModelInterpolator() }, -): ColumnChart = remember { ColumnChart() }.apply { - this.columns = columns - this.spacingDp = spacing.value - this.innerSpacingDp = innerSpacing.value - this.mergeMode = mergeMode - this.dataLabel = dataLabel - this.dataLabelVerticalPosition = dataLabelVerticalPosition - this.dataLabelValueFormatter = dataLabelValueFormatter - this.dataLabelRotationDegrees = dataLabelRotationDegrees - this.axisValuesOverrider = axisValuesOverrider - this.targetVerticalAxisPosition = targetVerticalAxisPosition - this.drawingModelInterpolator = drawingModelInterpolator - decorations?.also(::setDecorations) - persistentMarkers?.also(::setPersistentMarkers) -} +): ColumnChart = + remember { ColumnChart() }.apply { + this.columns = columns + this.spacingDp = spacing.value + this.innerSpacingDp = innerSpacing.value + this.mergeMode = mergeMode + this.dataLabel = dataLabel + this.dataLabelVerticalPosition = dataLabelVerticalPosition + this.dataLabelValueFormatter = dataLabelValueFormatter + this.dataLabelRotationDegrees = dataLabelRotationDegrees + this.axisValuesOverrider = axisValuesOverrider + this.targetVerticalAxisPosition = targetVerticalAxisPosition + this.drawingModelInterpolator = drawingModelInterpolator + decorations?.also(::setDecorations) + persistentMarkers?.also(::setPersistentMarkers) + } diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/edges/FadingEdgesExtensions.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/edges/FadingEdgesExtensions.kt index 25b07fa88..20a6d481a 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/edges/FadingEdgesExtensions.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/edges/FadingEdgesExtensions.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. @@ -42,11 +42,12 @@ public fun FadingEdges( endEdgeWidth: Dp = startEdgeWidth, visibilityThreshold: Dp = FadingEdgesDefaults.visibilityThreshold, visibilityEasing: Easing = FadingEdgesDefaults.visibilityEasing, -): FadingEdges = FadingEdges( - startEdgeWidthDp = startEdgeWidth.value, - endEdgeWidthDp = endEdgeWidth.value, - visibilityThresholdDp = visibilityThreshold.value, -) { input -> visibilityEasing.transform(input) } +): FadingEdges = + FadingEdges( + startEdgeWidthDp = startEdgeWidth.value, + endEdgeWidthDp = endEdgeWidth.value, + visibilityThresholdDp = visibilityThreshold.value, + ) { input -> visibilityEasing.transform(input) } /** * Creates and remembers a [FadingEdges] instance. @@ -65,13 +66,14 @@ public fun rememberFadingEdges( endEdgeWidth: Dp = startEdgeWidth, visibilityThreshold: Dp = FadingEdgesDefaults.visibilityThreshold, visibilityEasing: Easing = FadingEdgesDefaults.visibilityEasing, -): FadingEdges = remember { FadingEdges() } - .apply { - startEdgeWidthDp = startEdgeWidth.value - endEdgeWidthDp = endEdgeWidth.value - visibilityThresholdDp = visibilityThreshold.value - this.visibilityInterpolator = remember(visibilityEasing) { TimeInterpolator(visibilityEasing::transform) } - } +): FadingEdges = + remember { FadingEdges() } + .apply { + startEdgeWidthDp = startEdgeWidth.value + endEdgeWidthDp = endEdgeWidth.value + visibilityThresholdDp = visibilityThreshold.value + this.visibilityInterpolator = remember(visibilityEasing) { TimeInterpolator(visibilityEasing::transform) } + } /** * Creates and remembers a [FadingEdges] instance. @@ -88,18 +90,18 @@ public fun rememberFadingEdges( edgeWidth: Dp = FadingEdgesDefaults.edgeWidth, visibilityThreshold: Dp = FadingEdgesDefaults.visibilityThreshold, visibilityEasing: Easing = FadingEdgesDefaults.visibilityEasing, -): FadingEdges = rememberFadingEdges( - startEdgeWidth = edgeWidth, - endEdgeWidth = edgeWidth, - visibilityThreshold = visibilityThreshold, - visibilityEasing = visibilityEasing, -) +): FadingEdges = + rememberFadingEdges( + startEdgeWidth = edgeWidth, + endEdgeWidth = edgeWidth, + visibilityThreshold = visibilityThreshold, + visibilityEasing = visibilityEasing, + ) /** * The default values for [FadingEdges]. */ public object FadingEdgesDefaults { - /** * The width of the fade overlays (in dp). */ 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 0c0b2f859..d95014194 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 @@ -81,35 +81,39 @@ public fun ChartModelProducer.collectAsState( (chartEntryModelWrapperState.value.chartEntryModel != null || runInitialAnimation) ) { isAnimationRunning = true - mainAnimationJob = scope.launch(dispatcher) { - animate( - initialValue = Animation.range.start, - targetValue = Animation.range.endInclusive, - animationSpec = animationSpec, - ) { fraction, _ -> - when { - !isAnimationRunning -> return@animate - !isAnimationFrameGenerationRunning -> { - isAnimationFrameGenerationRunning = true - animationFrameJob = scope.launch(dispatcher) { - transformModel(chart, fraction) - isAnimationFrameGenerationRunning = false + mainAnimationJob = + scope.launch(dispatcher) { + animate( + initialValue = Animation.range.start, + targetValue = Animation.range.endInclusive, + animationSpec = animationSpec, + ) { fraction, _ -> + when { + !isAnimationRunning -> return@animate + !isAnimationFrameGenerationRunning -> { + isAnimationFrameGenerationRunning = true + animationFrameJob = + scope.launch(dispatcher) { + transformModel(chart, fraction) + isAnimationFrameGenerationRunning = false + } } - } - fraction == 1f -> { - finalAnimationFrameJob = scope.launch(dispatcher) { - animationFrameJob?.cancelAndJoin() - transformModel(chart, fraction) - isAnimationFrameGenerationRunning = false + fraction == 1f -> { + finalAnimationFrameJob = + scope.launch(dispatcher) { + animationFrameJob?.cancelAndJoin() + transformModel(chart, fraction) + isAnimationFrameGenerationRunning = false + } } } } } - } } else { - finalAnimationFrameJob = scope.launch(dispatcher) { - transformModel(chart, Animation.range.endInclusive) - } + finalAnimationFrameJob = + scope.launch(dispatcher) { + transformModel(chart, Animation.range.endInclusive) + } } } scope.launch(dispatcher) { diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/layout/HorizontalLayout.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/layout/HorizontalLayout.kt index f18a0b464..f30d33176 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/layout/HorizontalLayout.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/layout/HorizontalLayout.kt @@ -33,9 +33,10 @@ public fun HorizontalLayout.Companion.fullWidth( scalableEndPadding: Dp = 0.dp, unscalableStartPadding: Dp = 0.dp, unscalableEndPadding: Dp = 0.dp, -): HorizontalLayout.FullWidth = HorizontalLayout.FullWidth( - scalableStartPadding.value, - scalableEndPadding.value, - unscalableStartPadding.value, - unscalableEndPadding.value, -) +): HorizontalLayout.FullWidth = + HorizontalLayout.FullWidth( + scalableStartPadding.value, + scalableEndPadding.value, + unscalableStartPadding.value, + unscalableEndPadding.value, + ) diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/line/LineChart.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/line/LineChart.kt index 4932508a9..660226609 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/line/LineChart.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/line/LineChart.kt @@ -81,15 +81,16 @@ public fun lineChart( targetVerticalAxisPosition: AxisPosition.Vertical? = null, drawingModelInterpolator: DrawingModelInterpolator = remember { DefaultDrawingModelInterpolator() }, -): LineChart = remember { LineChart() }.apply { - this.lines = lines - this.spacingDp = spacing.value - this.axisValuesOverrider = axisValuesOverrider - this.targetVerticalAxisPosition = targetVerticalAxisPosition - this.drawingModelInterpolator = drawingModelInterpolator - decorations?.also(::setDecorations) - persistentMarkers?.also(::setPersistentMarkers) -} +): LineChart = + remember { LineChart() }.apply { + this.lines = lines + this.spacingDp = spacing.value + this.axisValuesOverrider = axisValuesOverrider + this.targetVerticalAxisPosition = targetVerticalAxisPosition + this.drawingModelInterpolator = drawingModelInterpolator + decorations?.also(::setDecorations) + persistentMarkers?.also(::setPersistentMarkers) + } /** * Creates a [LineChart.LineSpec] for use in [LineChart]s. @@ -112,14 +113,16 @@ public fun lineChart( public fun lineSpec( lineColor: Color, lineThickness: Dp = DefaultDimens.LINE_THICKNESS.dp, - lineBackgroundShader: DynamicShader? = DynamicShaders.fromBrush( - brush = Brush.verticalGradient( - listOf( - lineColor.copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_START), - lineColor.copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_END), - ), + lineBackgroundShader: DynamicShader? = + DynamicShaders.fromBrush( + brush = + Brush.verticalGradient( + listOf( + lineColor.copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_START), + lineColor.copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_END), + ), + ), ), - ), lineCap: StrokeCap = StrokeCap.Round, point: Component? = null, pointSize: Dp = DefaultDimens.POINT_SIZE.dp, @@ -128,19 +131,20 @@ public fun lineSpec( dataLabelValueFormatter: ValueFormatter = DecimalFormatValueFormatter(), dataLabelRotationDegrees: Float = 0f, pointConnector: LineSpec.PointConnector = DefaultPointConnector(), -): LineSpec = LineSpec( - lineShader = SolidShader(lineColor.toArgb()), - lineThicknessDp = lineThickness.value, - lineBackgroundShader = lineBackgroundShader, - lineCap = lineCap.paintCap, - point = point, - pointSizeDp = pointSize.value, - dataLabel = dataLabel, - dataLabelVerticalPosition = dataLabelVerticalPosition, - dataLabelValueFormatter = dataLabelValueFormatter, - dataLabelRotationDegrees = dataLabelRotationDegrees, - pointConnector = pointConnector, -) +): LineSpec = + LineSpec( + lineShader = SolidShader(lineColor.toArgb()), + lineThicknessDp = lineThickness.value, + lineBackgroundShader = lineBackgroundShader, + lineCap = lineCap.paintCap, + point = point, + pointSizeDp = pointSize.value, + dataLabel = dataLabel, + dataLabelVerticalPosition = dataLabelVerticalPosition, + dataLabelValueFormatter = dataLabelValueFormatter, + dataLabelRotationDegrees = dataLabelRotationDegrees, + pointConnector = pointConnector, + ) /** * Creates a [LineChart.LineSpec] for use in [LineChart]s. @@ -172,67 +176,79 @@ public fun lineSpec( dataLabelValueFormatter: ValueFormatter = DecimalFormatValueFormatter(), dataLabelRotationDegrees: Float = 0f, pointConnector: LineSpec.PointConnector = DefaultPointConnector(), -): LineSpec = LineSpec( - lineShader = lineShader, - lineThicknessDp = lineThickness.value, - lineBackgroundShader = lineBackgroundShader, - lineCap = lineCap.paintCap, - point = point, - pointSizeDp = pointSize.value, - dataLabel = dataLabel, - dataLabelVerticalPosition = dataLabelVerticalPosition, - dataLabelValueFormatter = dataLabelValueFormatter, - dataLabelRotationDegrees = dataLabelRotationDegrees, - pointConnector = pointConnector, -) +): LineSpec = + LineSpec( + lineShader = lineShader, + lineThicknessDp = lineThickness.value, + lineBackgroundShader = lineBackgroundShader, + lineCap = lineCap.paintCap, + point = point, + pointSizeDp = pointSize.value, + dataLabel = dataLabel, + dataLabelVerticalPosition = dataLabelVerticalPosition, + dataLabelValueFormatter = dataLabelValueFormatter, + dataLabelRotationDegrees = dataLabelRotationDegrees, + pointConnector = pointConnector, + ) private fun DynamicShader.getBackwardCompatibleBackgroundShader(): DynamicShader? = when (this) { - is SolidShader -> HorizontalSplitShader.Double( - topShader = DynamicShaders.fromBrush( - brush = Brush.verticalGradient( - listOf( - Color(color).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_START), - Color(color).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_END), + is SolidShader -> + HorizontalSplitShader.Double( + topShader = + DynamicShaders.fromBrush( + brush = + Brush.verticalGradient( + listOf( + Color(color).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_START), + Color(color).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_END), + ), + ), ), - ), - ), - bottomShader = DynamicShaders.fromBrush( - brush = Brush.verticalGradient( - listOf( - Color(color).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_END), - Color(color).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_START), + bottomShader = + DynamicShaders.fromBrush( + brush = + Brush.verticalGradient( + listOf( + Color(color).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_END), + Color(color).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_START), + ), + ), ), - ), - ), - ) + ) - is HorizontalSplitShader.Solid -> HorizontalSplitShader.Double( - topShader = DynamicShaders.fromBrush( - brush = Brush.verticalGradient( - listOf( - Color(colorTop).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_START), - Color(colorTop).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_END), + is HorizontalSplitShader.Solid -> + HorizontalSplitShader.Double( + topShader = + DynamicShaders.fromBrush( + brush = + Brush.verticalGradient( + listOf( + Color(colorTop).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_START), + Color(colorTop).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_END), + ), + ), ), - ), - ), - bottomShader = DynamicShaders.fromBrush( - brush = Brush.verticalGradient( - listOf( - Color(colorBottom).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_END), - Color(colorBottom).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_START), + bottomShader = + DynamicShaders.fromBrush( + brush = + Brush.verticalGradient( + listOf( + Color(colorBottom).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_END), + Color(colorBottom).copy(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_START), + ), + ), ), - ), - ), - ) + ) else -> null } private val StrokeCap.paintCap: Paint.Cap - get() = when (this) { - StrokeCap.Butt -> Paint.Cap.BUTT - StrokeCap.Round -> Paint.Cap.ROUND - StrokeCap.Square -> Paint.Cap.SQUARE - else -> throw IllegalArgumentException("Not `StrokeCap.Butt`, `StrokeCap.Round`, or `StrokeCap.Square`.") - } + get() = + when (this) { + StrokeCap.Butt -> Paint.Cap.BUTT + StrokeCap.Round -> Paint.Cap.ROUND + StrokeCap.Square -> Paint.Cap.SQUARE + else -> throw IllegalArgumentException("Not `StrokeCap.Butt`, `StrokeCap.Round`, or `StrokeCap.Square`.") + } diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/scroll/ChartScrollSpec.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/scroll/ChartScrollSpec.kt index 1cc6d6d29..1d6f751e4 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/scroll/ChartScrollSpec.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/scroll/ChartScrollSpec.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. @@ -43,7 +43,6 @@ public class ChartScrollSpec( public val autoScrollCondition: AutoScrollCondition, public val autoScrollAnimationSpec: AnimationSpec, ) { - /** * Performs an automatic scroll. */ @@ -58,10 +57,11 @@ public class ChartScrollSpec( } chartScrollState.animateScrollBy( - value = when (initialScroll) { - InitialScroll.Start -> -chartScrollState.value - InitialScroll.End -> -chartScrollState.value + chartScrollState.maxValue - }, + value = + when (initialScroll) { + InitialScroll.Start -> -chartScrollState.value + InitialScroll.End -> -chartScrollState.value + chartScrollState.maxValue + }, animationSpec = autoScrollAnimationSpec, ) } @@ -77,16 +77,17 @@ public fun rememberChartScrollSpec( initialScroll: InitialScroll = InitialScroll.Start, autoScrollCondition: AutoScrollCondition = AutoScrollCondition.Never, autoScrollAnimationSpec: AnimationSpec = spring(), -): ChartScrollSpec = remember( - isScrollEnabled, - initialScroll, - autoScrollCondition, - autoScrollAnimationSpec, -) { - ChartScrollSpec( - isScrollEnabled = isScrollEnabled, - initialScroll = initialScroll, - autoScrollCondition = autoScrollCondition, - autoScrollAnimationSpec = autoScrollAnimationSpec, - ) -} +): ChartScrollSpec = + remember( + isScrollEnabled, + initialScroll, + autoScrollCondition, + autoScrollAnimationSpec, + ) { + ChartScrollSpec( + isScrollEnabled = isScrollEnabled, + initialScroll = initialScroll, + autoScrollCondition = autoScrollCondition, + autoScrollAnimationSpec = autoScrollAnimationSpec, + ) + } diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/scroll/ChartScrollState.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/scroll/ChartScrollState.kt index 1f74a6009..d96fe554f 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/scroll/ChartScrollState.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/scroll/ChartScrollState.kt @@ -33,7 +33,6 @@ import kotlin.math.abs * Houses information on a [Chart]’s scroll state. Allows for programmatic scrolling. */ public class ChartScrollState : ScrollableState, ScrollListenerHost { - private val _value = mutableFloatStateOf(0f) private val _maxValue = mutableFloatStateOf(0f) private val scrollListeners: MutableSet = mutableSetOf() @@ -62,22 +61,26 @@ public class ChartScrollState : ScrollableState, ScrollListenerHost { scrollListeners.forEach { scrollListener -> scrollListener.onMaxValueChanged(oldMaxValue, newMaxValue) } } - private val scrollableState = ScrollableState { delta -> - val unlimitedValue = value + delta - val limitedValue = unlimitedValue.coerceIn(0f.rangeWith(maxValue)) - val consumedValue = limitedValue - value - value += consumedValue + private val scrollableState = + ScrollableState { delta -> + val unlimitedValue = value + delta + val limitedValue = unlimitedValue.coerceIn(0f.rangeWith(maxValue)) + val consumedValue = limitedValue - value + value += consumedValue - val unconsumedScroll = delta - consumedValue - if (unconsumedScroll != 0f) notifyUnconsumedScroll(unconsumedScroll) + val unconsumedScroll = delta - consumedValue + if (unconsumedScroll != 0f) notifyUnconsumedScroll(unconsumedScroll) - if (unlimitedValue != limitedValue) consumedValue else delta - } + if (unlimitedValue != limitedValue) consumedValue else delta + } override val isScrollInProgress: Boolean get() = scrollableState.isScrollInProgress - override suspend fun scroll(scrollPriority: MutatePriority, block: suspend ScrollScope.() -> Unit) { + override suspend fun scroll( + scrollPriority: MutatePriority, + block: suspend ScrollScope.() -> Unit, + ) { scrollableState.scroll(scrollPriority, block) } @@ -97,10 +100,11 @@ public class ChartScrollState : ScrollableState, ScrollListenerHost { internal fun handleInitialScroll(initialScroll: InitialScroll) { if (initialScrollHandled) return - value = when (initialScroll) { - InitialScroll.Start -> 0f - InitialScroll.End -> maxValue - } + value = + when (initialScroll) { + InitialScroll.Start -> 0f + InitialScroll.End -> maxValue + } initialScrollHandled = true } diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/Components.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/Components.kt index 298b4b84f..b497df2f1 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/Components.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/Components.kt @@ -61,25 +61,26 @@ public fun lineComponent( margins: Dimensions = emptyDimensions(), strokeWidth: Dp = 0.dp, strokeColor: Color = Color.Transparent, -): LineComponent = remember( - color, - thickness, - shape, - dynamicShader, - margins, - strokeWidth, - strokeColor, -) { - LineComponent( - color = color.toArgb(), - thicknessDp = thickness.value, - shape = shape, - dynamicShader = dynamicShader, - margins = margins, - strokeWidthDp = strokeWidth.value, - strokeColor = strokeColor.toArgb(), - ) -} +): LineComponent = + remember( + color, + thickness, + shape, + dynamicShader, + margins, + strokeWidth, + strokeColor, + ) { + LineComponent( + color = color.toArgb(), + thicknessDp = thickness.value, + shape = shape, + dynamicShader = dynamicShader, + margins = margins, + strokeWidthDp = strokeWidth.value, + strokeColor = strokeColor.toArgb(), + ) + } /** * Creates a [LineComponent] with the specified properties. @@ -93,15 +94,16 @@ public fun lineComponent( margins: Dimensions = emptyDimensions(), strokeWidth: Dp = 0.dp, strokeColor: Color = Color.Transparent, -): LineComponent = lineComponent( - color = color, - thickness = thickness, - shape = shape.chartShape(), - dynamicShader = dynamicShader, - margins = margins, - strokeWidth = strokeWidth, - strokeColor = strokeColor, -) +): LineComponent = + lineComponent( + color = color, + thickness = thickness, + shape = shape.chartShape(), + dynamicShader = dynamicShader, + margins = margins, + strokeWidth = strokeWidth, + strokeColor = strokeColor, + ) /** * Creates a [ShapeComponent] with the specified properties. @@ -114,23 +116,24 @@ public fun shapeComponent( margins: Dimensions = emptyDimensions(), strokeWidth: Dp = 0.dp, strokeColor: Color = Color.Transparent, -): ShapeComponent = remember( - shape, - color, - dynamicShader, - margins, - strokeWidth, - strokeColor, -) { - ShapeComponent( - shape = shape, - color = color.toArgb(), - dynamicShader = dynamicShader, - margins = margins, - strokeWidthDp = strokeWidth.value, - strokeColor = strokeColor.toArgb(), - ) -} +): ShapeComponent = + remember( + shape, + color, + dynamicShader, + margins, + strokeWidth, + strokeColor, + ) { + ShapeComponent( + shape = shape, + color = color.toArgb(), + dynamicShader = dynamicShader, + margins = margins, + strokeWidthDp = strokeWidth.value, + strokeColor = strokeColor.toArgb(), + ) + } /** * Creates a [ShapeComponent] with the specified properties. @@ -143,14 +146,15 @@ public fun shapeComponent( margins: Dimensions = emptyDimensions(), strokeWidth: Dp = 0.dp, strokeColor: Color = Color.Transparent, -): ShapeComponent = shapeComponent( - shape = shape.chartShape(), - color = color, - dynamicShader = dynamicShader, - margins = margins, - strokeWidth = strokeWidth, - strokeColor = strokeColor, -) +): ShapeComponent = + shapeComponent( + shape = shape.chartShape(), + color = color, + dynamicShader = dynamicShader, + margins = margins, + strokeWidth = strokeWidth, + strokeColor = strokeColor, + ) /** * Creates a [ShapeComponent] with the specified properties. @@ -163,14 +167,15 @@ public fun shapeComponent( margins: Dimensions = emptyDimensions(), strokeWidth: Dp = 0.dp, strokeColor: Color = Color.Transparent, -): ShapeComponent = shapeComponent( - shape = shape, - color = color, - dynamicShader = brush.toDynamicShader(), - margins = margins, - strokeWidth = strokeWidth, - strokeColor = strokeColor, -) +): ShapeComponent = + shapeComponent( + shape = shape, + color = color, + dynamicShader = brush.toDynamicShader(), + margins = margins, + strokeWidth = strokeWidth, + strokeColor = strokeColor, + ) /** * Creates an [OverlayingComponent]. @@ -190,23 +195,24 @@ public fun overlayingComponent( innerPaddingTop: Dp = 0.dp, innerPaddingBottom: Dp = 0.dp, innerPaddingEnd: Dp = 0.dp, -): OverlayingComponent = remember( - outer, - inner, - innerPaddingStart, - innerPaddingTop, - innerPaddingBottom, - innerPaddingEnd, -) { - OverlayingComponent( - outer = outer, - inner = inner, - innerPaddingStartDp = innerPaddingStart.value, - innerPaddingTopDp = innerPaddingTop.value, - innerPaddingBottomDp = innerPaddingBottom.value, - innerPaddingEndDp = innerPaddingEnd.value, - ) -} +): OverlayingComponent = + remember( + outer, + inner, + innerPaddingStart, + innerPaddingTop, + innerPaddingBottom, + innerPaddingEnd, + ) { + OverlayingComponent( + outer = outer, + inner = inner, + innerPaddingStartDp = innerPaddingStart.value, + innerPaddingTopDp = innerPaddingTop.value, + innerPaddingBottomDp = innerPaddingBottom.value, + innerPaddingEndDp = innerPaddingEnd.value, + ) + } /** * Creates an [OverlayingComponent]. @@ -220,14 +226,15 @@ public fun overlayingComponent( outer: Component, inner: Component, innerPaddingAll: Dp, -): OverlayingComponent = overlayingComponent( - outer = outer, - inner = inner, - innerPaddingStart = innerPaddingAll, - innerPaddingTop = innerPaddingAll, - innerPaddingBottom = innerPaddingAll, - innerPaddingEnd = innerPaddingAll, -) +): OverlayingComponent = + overlayingComponent( + outer = outer, + inner = inner, + innerPaddingStart = innerPaddingAll, + innerPaddingTop = innerPaddingAll, + innerPaddingBottom = innerPaddingAll, + innerPaddingEnd = innerPaddingAll, + ) /** * Creates a [TextComponent]. diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/dimension/Padding.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/dimension/Padding.kt index 4abac3155..e08c5d4ee 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/dimension/Padding.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/dimension/Padding.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. @@ -32,14 +32,15 @@ public fun

P.setPadding( top: Dp = 0.dp, end: Dp = 0.dp, bottom: Dp = 0.dp, -): P = apply { - padding.set( - startDp = start.value, - topDp = top.value, - endDp = end.value, - bottomDp = bottom.value, - ) -} +): P = + apply { + padding.set( + startDp = start.value, + topDp = top.value, + endDp = end.value, + bottomDp = bottom.value, + ) + } /** * Sets the horizontal and vertical padding for the rectangle. @@ -48,26 +49,26 @@ public fun

P.setPadding( public fun

P.setPadding( horizontal: Dp = 0.dp, vertical: Dp = 0.dp, -): P = apply { - padding.set( - startDp = horizontal.value, - topDp = vertical.value, - endDp = horizontal.value, - bottomDp = vertical.value, - ) -} +): P = + apply { + padding.set( + startDp = horizontal.value, + topDp = vertical.value, + endDp = horizontal.value, + bottomDp = vertical.value, + ) + } /** * Sets a common padding value for each edge of the rectangle. */ @Composable -public fun

P.setPadding( - all: Dp = 0.dp, -): P = apply { - padding.set( - startDp = all.value, - topDp = all.value, - endDp = all.value, - bottomDp = all.value, - ) -} +public fun

P.setPadding(all: Dp = 0.dp): P = + apply { + padding.set( + startDp = all.value, + topDp = all.value, + endDp = all.value, + bottomDp = all.value, + ) + } diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/marker/MarkerComponent.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/marker/MarkerComponent.kt index 785b4c39b..7c41f8f36 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/marker/MarkerComponent.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/marker/MarkerComponent.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. @@ -34,8 +34,9 @@ public fun markerComponent( label: TextComponent, indicator: Component, guideline: LineComponent, -): MarkerComponent = MarkerComponent( - label = label, - indicator = indicator, - guideline = guideline, -) +): MarkerComponent = + MarkerComponent( + label = label, + indicator = indicator, + guideline = guideline, + ) diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/Shapes.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/Shapes.kt index 5688d1881..dcd2d53d4 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/Shapes.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/Shapes.kt @@ -50,73 +50,82 @@ private const val RADII_ARRAY_SIZE = 8 * Converts this [androidx.compose.ui.graphics.Shape] to an instance of * [com.patrykandpatrick.vico.core.component.shape.Shape]. */ -public fun ComposeShape.chartShape(): Shape = object : Shape { - private val radii by lazy { FloatArray(RADII_ARRAY_SIZE) } - private val matrix: Matrix by lazy { Matrix() } +public fun ComposeShape.chartShape(): Shape = + object : Shape { + private val radii by lazy { FloatArray(RADII_ARRAY_SIZE) } + private val matrix: Matrix by lazy { Matrix() } - override fun drawShape( - context: DrawContext, - paint: Paint, - path: Path, - left: Float, - top: Float, - right: Float, - bottom: Float, - ) { - val outline = createOutline( - size = Size( - width = right - left, - height = bottom - top, - ), - layoutDirection = if (context.isLtr) LayoutDirection.Ltr else LayoutDirection.Rtl, - density = Density(context.density, 1f), - ) - when (outline) { - is Outline.Rectangle -> path.addRect( - left, - top, - right, - bottom, - Path.Direction.CCW, - ) + override fun drawShape( + context: DrawContext, + paint: Paint, + path: Path, + left: Float, + top: Float, + right: Float, + bottom: Float, + ) { + val outline = + createOutline( + size = + Size( + width = right - left, + height = bottom - top, + ), + layoutDirection = if (context.isLtr) LayoutDirection.Ltr else LayoutDirection.Rtl, + density = Density(context.density, 1f), + ) + when (outline) { + is Outline.Rectangle -> + path.addRect( + left, + top, + right, + bottom, + Path.Direction.CCW, + ) - is Outline.Rounded -> path.addRoundRect( - left = left, - top = top, - right = right, - bottom = bottom, - rect = outline.roundRect, - radii = radii, - ) + is Outline.Rounded -> + path.addRoundRect( + left = left, + top = top, + right = right, + bottom = bottom, + rect = outline.roundRect, + radii = radii, + ) - is Outline.Generic -> { - matrix.setTranslate(left, top) - path.addPath(outline.path.asAndroidPath(), matrix) + is Outline.Generic -> { + matrix.setTranslate(left, top) + path.addPath(outline.path.asAndroidPath(), matrix) + } } + context.canvas.drawPath(path, paint) } - context.canvas.drawPath(path, paint) } -} /** * Converts this [CorneredShape] to an instance of [androidx.compose.ui.graphics.Shape]. */ -public fun CorneredShape.composeShape(): ComposeShape = object : ComposeShape { +public fun CorneredShape.composeShape(): ComposeShape = + object : ComposeShape { + override fun createOutline( + size: Size, + layoutDirection: LayoutDirection, + density: Density, + ): Outline { + val path = ComposePath() - override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline { - val path = ComposePath() - - createPath( - density = density.density, - path = path.asAndroidPath(), - left = 0f, - top = 0f, - right = size.width, - bottom = size.height, - ) - return Outline.Generic(path) + createPath( + density = density.density, + path = path.asAndroidPath(), + left = 0f, + top = 0f, + right = size.width, + bottom = size.height, + ) + return Outline.Generic(path) + } } -} /** * Adds a rounded rectangle to the receiver [Path]. @@ -128,7 +137,6 @@ public fun CorneredShape.composeShape(): ComposeShape = object : ComposeShape { * @param rect the rounded rectangle to be drawn. * @param radii used to store the corner radii. This array must be mutable. */ -@Suppress("MagicNumber") public fun Path.addRoundRect( left: Float, top: Float, @@ -151,14 +159,13 @@ public fun Path.addRoundRect( /** * Creates a [CorneredShape] with rounded corners of the provided size. */ -public fun Shapes.roundedCornerShape( - all: Dp = 0.dp, -): CorneredShape = CorneredShape( - Corner.Absolute(all.value, RoundedCornerTreatment), - Corner.Absolute(all.value, RoundedCornerTreatment), - Corner.Absolute(all.value, RoundedCornerTreatment), - Corner.Absolute(all.value, RoundedCornerTreatment), -) +public fun Shapes.roundedCornerShape(all: Dp = 0.dp): CorneredShape = + CorneredShape( + Corner.Absolute(all.value, RoundedCornerTreatment), + Corner.Absolute(all.value, RoundedCornerTreatment), + Corner.Absolute(all.value, RoundedCornerTreatment), + Corner.Absolute(all.value, RoundedCornerTreatment), + ) /** * Creates a [CorneredShape] with rounded corners of the provided sizes. @@ -168,24 +175,24 @@ public fun Shapes.roundedCornerShape( topRight: Dp = 0.dp, bottomRight: Dp = 0.dp, bottomLeft: Dp = 0.dp, -): CorneredShape = CorneredShape( - Corner.Absolute(topLeft.value, RoundedCornerTreatment), - Corner.Absolute(topRight.value, RoundedCornerTreatment), - Corner.Absolute(bottomRight.value, RoundedCornerTreatment), - Corner.Absolute(bottomLeft.value, RoundedCornerTreatment), -) +): CorneredShape = + CorneredShape( + Corner.Absolute(topLeft.value, RoundedCornerTreatment), + Corner.Absolute(topRight.value, RoundedCornerTreatment), + Corner.Absolute(bottomRight.value, RoundedCornerTreatment), + Corner.Absolute(bottomLeft.value, RoundedCornerTreatment), + ) /** * Creates a [CorneredShape] with cut corners of the provided size. */ -public fun Shapes.cutCornerShape( - all: Dp = 0.dp, -): CorneredShape = CorneredShape( - Corner.Absolute(all.value, CutCornerTreatment), - Corner.Absolute(all.value, CutCornerTreatment), - Corner.Absolute(all.value, CutCornerTreatment), - Corner.Absolute(all.value, CutCornerTreatment), -) +public fun Shapes.cutCornerShape(all: Dp = 0.dp): CorneredShape = + CorneredShape( + Corner.Absolute(all.value, CutCornerTreatment), + Corner.Absolute(all.value, CutCornerTreatment), + Corner.Absolute(all.value, CutCornerTreatment), + Corner.Absolute(all.value, CutCornerTreatment), + ) /** * Creates a [CorneredShape] with cut corners of the provided sizes. @@ -195,12 +202,13 @@ public fun Shapes.cutCornerShape( topRight: Dp = 0.dp, bottomRight: Dp = 0.dp, bottomLeft: Dp = 0.dp, -): CorneredShape = CorneredShape( - Corner.Absolute(topLeft.value, CutCornerTreatment), - Corner.Absolute(topRight.value, CutCornerTreatment), - Corner.Absolute(bottomRight.value, CutCornerTreatment), - Corner.Absolute(bottomLeft.value, CutCornerTreatment), -) +): CorneredShape = + CorneredShape( + Corner.Absolute(topLeft.value, CutCornerTreatment), + Corner.Absolute(topRight.value, CutCornerTreatment), + Corner.Absolute(bottomRight.value, CutCornerTreatment), + Corner.Absolute(bottomLeft.value, CutCornerTreatment), + ) /** * Creates a [MarkerCorneredShape]. @@ -217,13 +225,14 @@ public fun Shapes.markerCorneredShape( bottomRight: Corner, bottomLeft: Corner, tickSizeDp: Dp = DEF_MARKER_TICK_SIZE.dp, -): MarkerCorneredShape = MarkerCorneredShape( - topLeft = topLeft, - topRight = topRight, - bottomRight = bottomRight, - bottomLeft = bottomLeft, - tickSizeDp = tickSizeDp.value, -) +): MarkerCorneredShape = + MarkerCorneredShape( + topLeft = topLeft, + topRight = topRight, + bottomRight = bottomRight, + bottomLeft = bottomLeft, + tickSizeDp = tickSizeDp.value, + ) /** * Creates a [MarkerCorneredShape]. @@ -234,13 +243,14 @@ public fun Shapes.markerCorneredShape( public fun Shapes.markerCorneredShape( all: Corner, tickSizeDp: Dp = DEF_MARKER_TICK_SIZE.dp, -): MarkerCorneredShape = MarkerCorneredShape( - topLeft = all, - topRight = all, - bottomRight = all, - bottomLeft = all, - tickSizeDp = tickSizeDp.value, -) +): MarkerCorneredShape = + MarkerCorneredShape( + topLeft = all, + topRight = all, + bottomRight = all, + bottomLeft = all, + tickSizeDp = tickSizeDp.value, + ) /** * Creates a [MarkerCorneredShape] out of a regular [CorneredShape]. @@ -251,13 +261,14 @@ public fun Shapes.markerCorneredShape( public fun Shapes.markerCorneredShape( corneredShape: CorneredShape, tickSizeDp: Dp = DEF_MARKER_TICK_SIZE.dp, -): MarkerCorneredShape = MarkerCorneredShape( - topLeft = corneredShape.topLeft, - topRight = corneredShape.topRight, - bottomRight = corneredShape.bottomRight, - bottomLeft = corneredShape.bottomLeft, - tickSizeDp = tickSizeDp.value, -) +): MarkerCorneredShape = + MarkerCorneredShape( + topLeft = corneredShape.topLeft, + topRight = corneredShape.topRight, + bottomRight = corneredShape.bottomRight, + bottomLeft = corneredShape.bottomLeft, + tickSizeDp = tickSizeDp.value, + ) /** * Creates a [DashedShape]. @@ -272,12 +283,13 @@ public fun Shapes.dashedShape( dashLength: Dp, gapLength: Dp, fitStrategy: DashedShape.FitStrategy = DashedShape.FitStrategy.Resize, -): DashedShape = DashedShape( - shape = shape.chartShape(), - dashLengthDp = dashLength.value, - gapLengthDp = gapLength.value, - fitStrategy = fitStrategy, -) +): DashedShape = + DashedShape( + shape = shape.chartShape(), + dashLengthDp = dashLength.value, + gapLengthDp = gapLength.value, + fitStrategy = fitStrategy, + ) /** * Creates a [DashedShape]. @@ -292,9 +304,10 @@ public fun Shapes.dashedShape( dashLength: Dp, gapLength: Dp, fitStrategy: DashedShape.FitStrategy = DashedShape.FitStrategy.Resize, -): DashedShape = DashedShape( - shape = shape, - dashLengthDp = dashLength.value, - gapLengthDp = gapLength.value, - fitStrategy = fitStrategy, -) +): DashedShape = + DashedShape( + shape = shape, + dashLengthDp = dashLength.value, + gapLengthDp = gapLength.value, + fitStrategy = fitStrategy, + ) diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/BrushShader.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/BrushShader.kt index 3e7b5048e..c134b28fd 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/BrushShader.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/BrushShader.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. @@ -32,7 +32,6 @@ import kotlin.math.abs * @property brush the [Brush] to be converted to a [Shader]. */ public class BrushShader(private val brush: Brush) : CacheableDynamicShader() { - private val matrix = Matrix() override fun createShader( @@ -44,10 +43,11 @@ public class BrushShader(private val brush: Brush) : CacheableDynamicShader() { ): Shader { val tempPaint = Paint() brush.applyTo( - size = Size( - abs(left - right), - abs(top - bottom), - ), + size = + Size( + abs(left - right), + abs(top - bottom), + ), p = tempPaint, alpha = 1f, ) diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/DynamicShaders.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/DynamicShaders.kt index cebf286f7..96dd7db6c 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/DynamicShaders.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/DynamicShaders.kt @@ -43,13 +43,14 @@ public fun DynamicShaders.fromComponent( checkeredArrangement: Boolean = true, tileXMode: Shader.TileMode = Shader.TileMode.REPEAT, tileYMode: Shader.TileMode = tileXMode, -): ComponentShader = ComponentShader( - component = component, - componentSizeDp = componentSize.value, - checkeredArrangement = checkeredArrangement, - tileXMode = tileXMode, - tileYMode = tileYMode, -) +): ComponentShader = + ComponentShader( + component = component, + componentSizeDp = componentSize.value, + checkeredArrangement = checkeredArrangement, + tileXMode = tileXMode, + tileYMode = tileYMode, + ) /** * Creates a [BrushShader] using the given [Brush]. @@ -67,5 +68,7 @@ public fun DynamicShaders.solid(color: Color): SolidShader = SolidShader(color.t * Creates a [DynamicShader] with split colors. The positive color is used for values above the zero line, * and the negative color is used for values below the zero line. */ -public fun DynamicShaders.split(positiveColor: Color, negativeColor: Color): HorizontalSplitShader.Solid = - HorizontalSplitShader.Solid(positiveColor.toArgb(), negativeColor.toArgb()) +public fun DynamicShaders.split( + positiveColor: Color, + negativeColor: Color, +): HorizontalSplitShader.Solid = HorizontalSplitShader.Solid(positiveColor.toArgb(), negativeColor.toArgb()) diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/LinearGradientShader.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/LinearGradientShader.kt index 89bad5564..81d19c491 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/LinearGradientShader.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/component/shape/shader/LinearGradientShader.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. @@ -36,25 +36,25 @@ import com.patrykandpatrick.vico.core.context.DrawContext public fun horizontalGradient( colors: Array, positions: FloatArray? = null, -): DynamicShader = object : CacheableDynamicShader() { - - override fun createShader( - context: DrawContext, - left: Float, - top: Float, - right: Float, - bottom: Float, - ): Shader = - LinearGradient( - left, - top, - right, - top, - IntArray(colors.size) { index -> colors[index].toArgb() }, - positions, - Shader.TileMode.CLAMP, - ) -} +): DynamicShader = + object : CacheableDynamicShader() { + override fun createShader( + context: DrawContext, + left: Float, + top: Float, + right: Float, + bottom: Float, + ): Shader = + LinearGradient( + left, + top, + right, + top, + IntArray(colors.size) { index -> colors[index].toArgb() }, + positions, + Shader.TileMode.CLAMP, + ) + } /** * Creates a [DynamicShader] in the form of a vertical gradient. @@ -68,22 +68,22 @@ public fun horizontalGradient( public fun verticalGradient( colors: Array, positions: FloatArray? = null, -): DynamicShader = object : CacheableDynamicShader() { - - override fun createShader( - context: DrawContext, - left: Float, - top: Float, - right: Float, - bottom: Float, - ): Shader = - LinearGradient( - left, - top, - left, - bottom, - IntArray(colors.size) { index -> colors[index].toArgb() }, - positions, - Shader.TileMode.CLAMP, - ) -} +): DynamicShader = + object : CacheableDynamicShader() { + override fun createShader( + context: DrawContext, + left: Float, + top: Float, + right: Float, + bottom: Float, + ): Shader = + LinearGradient( + left, + top, + left, + bottom, + IntArray(colors.size) { index -> colors[index].toArgb() }, + positions, + Shader.TileMode.CLAMP, + ) + } diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/dimensions/DimensionExtensions.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/dimensions/DimensionExtensions.kt index 44d64d130..b5e8eb211 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/dimensions/DimensionExtensions.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/dimensions/DimensionExtensions.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. @@ -23,12 +23,13 @@ import com.patrykandpatrick.vico.core.dimensions.MutableDimensions /** * Creates a [MutableDimensions] instance with a common value for each coordinate. */ -public fun dimensionsOf(all: Dp): MutableDimensions = dimensionsOf( - start = all, - top = all, - end = all, - bottom = all, -) +public fun dimensionsOf(all: Dp): MutableDimensions = + dimensionsOf( + start = all, + top = all, + end = all, + bottom = all, + ) /** * Creates a [MutableDimensions] instance using the provided measurements. @@ -36,12 +37,13 @@ public fun dimensionsOf(all: Dp): MutableDimensions = dimensionsOf( public fun dimensionsOf( horizontal: Dp = 0.dp, vertical: Dp = 0.dp, -): MutableDimensions = MutableDimensions( - startDp = horizontal.value, - topDp = vertical.value, - endDp = horizontal.value, - bottomDp = vertical.value, -) +): MutableDimensions = + MutableDimensions( + startDp = horizontal.value, + topDp = vertical.value, + endDp = horizontal.value, + bottomDp = vertical.value, + ) /** * Creates a [MutableDimensions] instance using the provided measurements. @@ -51,9 +53,10 @@ public fun dimensionsOf( top: Dp = 0.dp, end: Dp = 0.dp, bottom: Dp = 0.dp, -): MutableDimensions = MutableDimensions( - startDp = start.value, - topDp = top.value, - endDp = end.value, - bottomDp = bottom.value, -) +): MutableDimensions = + MutableDimensions( + startDp = start.value, + topDp = top.value, + endDp = end.value, + bottomDp = bottom.value, + ) diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/extension/ComponentExtensions.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/extension/ComponentExtensions.kt index 842968e33..d03569d83 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/extension/ComponentExtensions.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/extension/ComponentExtensions.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. @@ -31,7 +31,9 @@ import com.patrykandpatrick.vico.core.component.shape.ShapeComponent */ public var MarkerComponent.indicatorSize: Dp get() = indicatorSizeDp.dp - set(value) { indicatorSizeDp = value.value } + set(value) { + indicatorSizeDp = value.value + } /** * Applies a drop shadow to this [ShapeComponent]. @@ -49,10 +51,11 @@ public fun T.setShadow( dy: Dp = 0.dp, color: Color = Color(DEF_SHADOW_COLOR), applyElevationOverlay: Boolean = false, -): T = setShadow( - radius = radius.value, - dx = dx.value, - dy = dy.value, - color = color.toArgb(), - applyElevationOverlay = applyElevationOverlay, -) as T +): T = + setShadow( + radius = radius.value, + dx = dx.value, + dy = dy.value, + color = color.toArgb(), + applyElevationOverlay = applyElevationOverlay, + ) as T diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/gesture/Zoomable.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/gesture/Zoomable.kt index 10f3b7375..0b11b7d4c 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/gesture/Zoomable.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/gesture/Zoomable.kt @@ -41,19 +41,22 @@ public typealias OnZoom = (centroid: Offset, zoomChange: Float) -> Unit public fun Modifier.zoomable( onZoom: OnZoom, enabled: Boolean = true, -): Modifier = composed( - factory = { - val block: suspend PointerInputScope.() -> Unit = remember { - { - detectZoomGestures { centroid, zoom -> - onZoom(centroid, zoom) +): Modifier = + composed( + factory = { + val block: suspend PointerInputScope.() -> Unit = + remember { + { + detectZoomGestures { centroid, zoom -> + onZoom(centroid, zoom) + } + } } - } - } - if (enabled) Modifier.pointerInput(Unit, block) else Modifier - }, - inspectorInfo = debugInspectorInfo { - name = "zoomable" - properties["enabled"] = enabled - }, -) + if (enabled) Modifier.pointerInput(Unit, block) else Modifier + }, + inspectorInfo = + debugInspectorInfo { + name = "zoomable" + properties["enabled"] = enabled + }, + ) 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 b20687319..fd30a8f1b 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 @@ -44,19 +44,20 @@ public fun getMeasureContext( horizontalLayout: HorizontalLayout, spToPx: (Float) -> Float, chartValuesProvider: ChartValuesProvider, -): MutableMeasureContext = remember { - MutableMeasureContext( - canvasBounds = canvasBounds, - density = 0f, - isLtr = true, - spToPx = spToPx, - 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 -} +): MutableMeasureContext = + remember { + MutableMeasureContext( + canvasBounds = canvasBounds, + density = 0f, + isLtr = true, + spToPx = spToPx, + 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/legend/Legends.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/legend/Legends.kt index 856c81e44..29d999fdc 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/legend/Legends.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/legend/Legends.kt @@ -45,15 +45,16 @@ public fun verticalLegend( iconPadding: Dp, spacing: Dp = 0.dp, padding: MutableDimensions = emptyDimensions(), -): VerticalLegend = remember(items, iconSize, iconPadding, spacing, padding) { - VerticalLegend( - items = items, - iconSizeDp = iconSize.value, - iconPaddingDp = iconPadding.value, - spacingDp = spacing.value, - padding = padding, - ) -} +): VerticalLegend = + remember(items, iconSize, iconPadding, spacing, padding) { + VerticalLegend( + items = items, + iconSizeDp = iconSize.value, + iconPaddingDp = iconPadding.value, + spacingDp = spacing.value, + padding = padding, + ) + } /** * Defines the appearance of an item of a [Legend]. @@ -67,13 +68,14 @@ public fun legendItem( icon: Component, label: TextComponent, labelText: CharSequence, -): LegendItem = remember(icon, label, labelText) { - LegendItem( - icon = icon, - label = label, - labelText = labelText, - ) -} +): LegendItem = + remember(icon, label, labelText) { + LegendItem( + icon = icon, + label = label, + labelText = labelText, + ) + } /** * Creates a [HorizontalLegend]. @@ -93,13 +95,14 @@ public fun horizontalLegend( lineSpacing: Dp = 0.dp, spacing: Dp = 0.dp, padding: MutableDimensions = emptyDimensions(), -): HorizontalLegend = remember(items, iconSize, iconPadding, spacing, padding) { - HorizontalLegend( - items = items, - iconSizeDp = iconSize.value, - iconPaddingDp = iconPadding.value, - lineSpacingDp = lineSpacing.value, - spacingDp = spacing.value, - padding = padding, - ) -} +): HorizontalLegend = + remember(items, iconSize, iconPadding, spacing, padding) { + HorizontalLegend( + items = items, + iconSizeDp = iconSize.value, + iconPaddingDp = iconPadding.value, + lineSpacingDp = lineSpacing.value, + spacingDp = spacing.value, + padding = padding, + ) + } 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 index dc5580210..ea5578c15 100644 --- 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 @@ -57,7 +57,10 @@ internal class ChartEntryModelWrapperState : State>(ChartEntryModelWrapper()) private set - fun set(chartEntryModel: T?, chartValuesProvider: ChartValuesProvider) { + 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/compose/src/main/java/com/patrykandpatrick/vico/compose/style/ChartStyle.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/style/ChartStyle.kt index fcf8cace1..eeebfa77f 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/style/ChartStyle.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/style/ChartStyle.kt @@ -109,11 +109,12 @@ public data class ChartStyle( val axisLabelTextAlignment: Layout.Alignment = Layout.Alignment.ALIGN_NORMAL, val axisGuidelineColor: Color, val axisGuidelineWidth: Dp = DefaultDimens.AXIS_GUIDELINE_WIDTH.dp, - val axisGuidelineShape: Shape = Shapes.dashedShape( - shape = Shapes.rectShape, - dashLength = DefaultDimens.DASH_LENGTH.dp, - gapLength = DefaultDimens.DASH_GAP.dp, - ), + val axisGuidelineShape: Shape = + Shapes.dashedShape( + shape = Shapes.rectShape, + dashLength = DefaultDimens.DASH_LENGTH.dp, + gapLength = DefaultDimens.DASH_GAP.dp, + ), val axisLineColor: Color, val axisLineWidth: Dp = DefaultDimens.AXIS_LINE_WIDTH.dp, val axisLineShape: Shape = Shapes.rectShape, @@ -177,7 +178,6 @@ public data class ChartStyle( ) public companion object { - /** * Creates a base implementation of [ChartStyle] using the provided colors. */ @@ -187,43 +187,54 @@ public data class ChartStyle( axisLineColor: Color, entityColors: List, elevationOverlayColor: Color, - ): ChartStyle = ChartStyle( - axis = Axis( - axisLabelColor = axisLabelColor, - axisGuidelineColor = axisGuidelineColor, - axisLineColor = axisLineColor, - ), - columnChart = ColumnChart( - columns = entityColors.map { entityColor -> - LineComponent( - color = entityColor.toArgb(), - thicknessDp = DefaultDimens.COLUMN_WIDTH, - shape = Shapes.roundedCornerShape(allPercent = DefaultDimens.COLUMN_ROUNDNESS_PERCENT), - ) - }, - ), - lineChart = LineChart( - lines = entityColors.map { entityColor -> - lineSpec( - lineShader = DynamicShaders.solid(entityColor), - ) - }, - ), - marker = Marker(), - elevationOverlayColor = elevationOverlayColor, - ) + ): ChartStyle = + ChartStyle( + axis = + Axis( + axisLabelColor = axisLabelColor, + axisGuidelineColor = axisGuidelineColor, + axisLineColor = axisLineColor, + ), + columnChart = + ColumnChart( + columns = + entityColors.map { entityColor -> + LineComponent( + color = entityColor.toArgb(), + thicknessDp = DefaultDimens.COLUMN_WIDTH, + shape = + Shapes.roundedCornerShape( + allPercent = DefaultDimens.COLUMN_ROUNDNESS_PERCENT, + ), + ) + }, + ), + lineChart = + LineChart( + lines = + entityColors.map { entityColor -> + lineSpec( + lineShader = DynamicShaders.solid(entityColor), + ) + }, + ), + marker = Marker(), + elevationOverlayColor = elevationOverlayColor, + ) - internal fun fromDefaultColors(defaultColors: DefaultColors) = fromColors( - axisLabelColor = Color(defaultColors.axisLabelColor), - axisGuidelineColor = Color(defaultColors.axisGuidelineColor), - axisLineColor = Color(defaultColors.axisLineColor), - entityColors = listOf( - defaultColors.entity1Color, - defaultColors.entity2Color, - defaultColors.entity3Color, - ).map(::Color), - elevationOverlayColor = Color(defaultColors.elevationOverlayColor), - ) + internal fun fromDefaultColors(defaultColors: DefaultColors) = + fromColors( + axisLabelColor = Color(defaultColors.axisLabelColor), + axisGuidelineColor = Color(defaultColors.axisGuidelineColor), + axisLineColor = Color(defaultColors.axisLineColor), + entityColors = + listOf( + defaultColors.entity1Color, + defaultColors.entity2Color, + defaultColors.entity3Color, + ).map(::Color), + elevationOverlayColor = Color(defaultColors.elevationOverlayColor), + ) } } @@ -231,7 +242,6 @@ public data class ChartStyle( * Provides a [ChartStyle] instance. */ public object LocalChartStyle { - internal val default: ChartStyle @Composable get() { diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/Defaults.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/Defaults.kt index 6f55fc4a2..e187e5f80 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/Defaults.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/Defaults.kt @@ -223,9 +223,7 @@ public object DefaultDimens { /** * The default chart colors. */ -@Suppress("MagicNumber") public interface DefaultColors { - /** * The default color for elevation overlays. Its opacity is modified depending on the elevation. */ @@ -273,7 +271,6 @@ public interface DefaultColors { * The default chart colors for light mode. */ public object Light : DefaultColors { - override val elevationOverlayColor: Long = 0x00000000 override val axisLabelColor: Long = 0xDE000000 @@ -291,7 +288,6 @@ public interface DefaultColors { * The default chart colors for dark mode. */ public object Dark : DefaultColors { - override val elevationOverlayColor: Long = 0xFFFFFFFF override val axisLabelColor: Long = 0xFFFFFFFF diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/Axis.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/Axis.kt index 1451ac395..94301af45 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/Axis.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/Axis.kt @@ -37,7 +37,6 @@ import com.patrykandpatrick.vico.core.extension.setAll * @see VerticalAxis */ public abstract class Axis : AxisRenderer { - private val restrictedBounds: MutableList = mutableListOf() override val bounds: RectF = RectF() @@ -113,9 +112,10 @@ public abstract class Axis : AxisRenderer { top: Float, right: Float, bottom: Float, - ): Boolean = restrictedBounds.none { - it.contains(left, top, right, bottom) || it.intersects(left, top, right, bottom) - } + ): Boolean = + restrictedBounds.none { + it.contains(left, top, right, bottom) || it.intersects(left, top, right, bottom) + } /** * Used to construct [Axis] instances. @@ -182,7 +182,6 @@ public abstract class Axis : AxisRenderer { * @see [HorizontalAxis] */ public sealed class SizeConstraint { - /** * The axis will measure itself and use as much space as it needs, but no less than [minSizeDp], and no more * than [maxSizeDp]. diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisItemPlacer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisItemPlacer.kt index 8a6813e13..5fa26d39c 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisItemPlacer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisItemPlacer.kt @@ -36,14 +36,11 @@ public interface AxisItemPlacer { * An [AxisItemPlacer] subinterface for [HorizontalAxis] instances. */ public interface Horizontal : AxisItemPlacer { - /** * Whether ticks whose _x_ values are bounds of the _x_-axis value range should be shifted to the edges of the * axis bounds, to be aligned with the vertical axes. */ - public fun getShiftExtremeTicks( - context: ChartDrawContext, - ): Boolean = true + public fun getShiftExtremeTicks(context: ChartDrawContext): Boolean = true /** * Returns a boolean indicating whether the [HorizontalAxis] should reserve room for a label for @@ -206,8 +203,10 @@ public interface AxisItemPlacer { * they’re immediately above the [Chart]’s bounds. If the chart has a top axis, the shifted tick will then * be aligned with this axis, and the shifted guideline will be hidden. */ - public fun default(maxItemCount: Int = DEF_LABEL_COUNT, shiftTopLines: Boolean = true): Vertical = - DefaultVerticalAxisItemPlacer(maxItemCount, shiftTopLines) + public fun default( + maxItemCount: Int = DEF_LABEL_COUNT, + shiftTopLines: Boolean = true, + ): Vertical = DefaultVerticalAxisItemPlacer(maxItemCount, shiftTopLines) } } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisManager.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisManager.kt index 9e0c690db..05973bdc6 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisManager.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisManager.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. @@ -30,7 +30,6 @@ import com.patrykandpatrick.vico.core.context.MeasureContext * @see AxisRenderer */ public open class AxisManager { - internal val axisCache = ArrayList>(MAX_AXIS_COUNT) /** diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisPosition.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisPosition.kt index c4c57191e..2e8826b22 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisPosition.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisPosition.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. @@ -22,7 +22,6 @@ import com.patrykandpatrick.vico.core.chart.Chart * Defines the position of an axis relative to its [Chart]. */ public sealed class AxisPosition { - /** * Whether the axis is at the top of its [Chart]. */ @@ -50,20 +49,17 @@ public sealed class AxisPosition { /** * Whether the axis is on the left of its [Chart]. The layout direction is considered here. */ - public fun isLeft(isLtr: Boolean): Boolean = - this is Vertical.Start && isLtr || this is Vertical.End && isLtr.not() + public fun isLeft(isLtr: Boolean): Boolean = this is Vertical.Start && isLtr || this is Vertical.End && isLtr.not() /** * Whether the axis is on the right of its [Chart]. The layout direction is considered here. */ - public fun isRight(isLtr: Boolean): Boolean = - this is Vertical.End && isLtr || this is Vertical.Start && isLtr.not() + public fun isRight(isLtr: Boolean): Boolean = this is Vertical.End && isLtr || this is Vertical.Start && isLtr.not() /** * Defines the position of a horizontal axis relative to its [Chart]. */ public sealed class Horizontal : AxisPosition() { - /** * The horizontal axis will be placed at the top of its [Chart]. */ @@ -79,7 +75,6 @@ public sealed class AxisPosition { * Defines the position of a vertical axis relative to its [Chart]. */ public sealed class Vertical : AxisPosition() { - /** * The vertical axis will be placed at the start of its [Chart]. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisRenderer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisRenderer.kt index 5a5d19e10..f9c267cc6 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisRenderer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/AxisRenderer.kt @@ -28,7 +28,6 @@ import com.patrykandpatrick.vico.core.dimensions.BoundsAware * Defines the minimal set of properties and functions required by other parts of the library to draw an axis. */ public interface AxisRenderer : BoundsAware, ChartInsetter { - /** * Defines the position of the axis relative to the [Chart]. */ @@ -59,5 +58,8 @@ public interface AxisRenderer : BoundsAware, ChartInset /** * Updates the chart’s [MutableHorizontalDimensions] instance. */ - public fun updateHorizontalDimensions(context: MeasureContext, horizontalDimensions: MutableHorizontalDimensions) + public fun updateHorizontalDimensions( + context: MeasureContext, + horizontalDimensions: MutableHorizontalDimensions, + ) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/formatter/DecimalFormatAxisValueFormatter.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/formatter/DecimalFormatAxisValueFormatter.kt index 205c88b2f..865879e2d 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/formatter/DecimalFormatAxisValueFormatter.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/formatter/DecimalFormatAxisValueFormatter.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. @@ -26,7 +26,6 @@ import java.text.DecimalFormat */ public class DecimalFormatAxisValueFormatter(decimalFormat: DecimalFormat) : AxisValueFormatter, DecimalFormatValueFormatter(decimalFormat = decimalFormat) { - /** * Creates a [DecimalFormatAxisValueFormatter] using the default pattern. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/formatter/PercentageFormatAxisValueFormatter.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/formatter/PercentageFormatAxisValueFormatter.kt index dae75ebe5..466fc5e6c 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/formatter/PercentageFormatAxisValueFormatter.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/axis/formatter/PercentageFormatAxisValueFormatter.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. @@ -28,7 +28,6 @@ import java.text.DecimalFormat */ public class PercentageFormatAxisValueFormatter(pattern: String) : AxisValueFormatter, PercentageFormatValueFormatter(pattern = pattern) { - /** * Creates a [PercentageFormatAxisValueFormatter] using the default percentage pattern. */ 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 842e4b631..ab0a54784 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 @@ -30,7 +30,6 @@ internal class DefaultHorizontalAxisItemPlacer( private val shiftExtremeTicks: Boolean, private val addExtremeLabelPadding: Boolean, ) : AxisItemPlacer.Horizontal { - override fun getShiftExtremeTicks(context: ChartDrawContext): Boolean = shiftExtremeTicks override fun getAddFirstLabelPadding(context: MeasureContext) = @@ -42,7 +41,6 @@ internal class DefaultHorizontalAxisItemPlacer( (chartValues.maxX - chartValues.minX - chartValues.xStep * offset) % (chartValues.xStep * spacing) == 0f } - @Suppress("LoopWithTooManyJumpStatements") override fun getLabelValues( context: ChartDrawContext, visibleXRange: ClosedFloatingPointRange, @@ -75,7 +73,6 @@ internal class DefaultHorizontalAxisItemPlacer( return listOf(chartValues.minX, (chartValues.minX + chartValues.maxX).half, chartValues.maxX) } - @Suppress("LoopWithTooManyJumpStatements") override fun getLineValues( context: ChartDrawContext, visibleXRange: ClosedFloatingPointRange, 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 172a5a35d..34fb6eeeb 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 @@ -45,7 +45,6 @@ import kotlin.math.min public class HorizontalAxis( override val position: Position, ) : Axis() { - private val AxisPosition.Horizontal.textVerticalPosition: VerticalPosition get() = if (isBottom) VerticalPosition.Bottom else VerticalPosition.Top @@ -54,97 +53,104 @@ public class HorizontalAxis( */ public var itemPlacer: AxisItemPlacer.Horizontal = AxisItemPlacer.Horizontal.default() - override fun drawBehindChart(context: ChartDrawContext): Unit = with(context) { - val clipRestoreCount = canvas.save() - val tickMarkTop = if (position.isBottom) bounds.top else bounds.bottom - axisThickness - tickLength - val tickMarkBottom = tickMarkTop + axisThickness + tickLength - val chartValues = chartValuesProvider.getChartValues() + override fun drawBehindChart(context: ChartDrawContext): Unit = + with(context) { + val clipRestoreCount = canvas.save() + val tickMarkTop = if (position.isBottom) bounds.top else bounds.bottom - axisThickness - tickLength + val tickMarkBottom = tickMarkTop + axisThickness + tickLength + val chartValues = chartValuesProvider.getChartValues() + + canvas.clipRect( + bounds.left - itemPlacer.getStartHorizontalAxisInset(this, horizontalDimensions, tickThickness), + minOf(bounds.top, chartBounds.top), + bounds.right + itemPlacer.getEndHorizontalAxisInset(this, horizontalDimensions, tickThickness), + maxOf(bounds.bottom, chartBounds.bottom), + ) - canvas.clipRect( - bounds.left - itemPlacer.getStartHorizontalAxisInset(this, horizontalDimensions, tickThickness), - minOf(bounds.top, chartBounds.top), - bounds.right + itemPlacer.getEndHorizontalAxisInset(this, horizontalDimensions, tickThickness), - maxOf(bounds.bottom, chartBounds.bottom), - ) + val textY = if (position.isBottom) tickMarkBottom else tickMarkTop + val fullXRange = getFullXRange(horizontalDimensions) + val baseCanvasX = + bounds.getStart(isLtr) - horizontalScroll + horizontalDimensions.startPadding * + layoutDirectionMultiplier + val firstVisibleX = + fullXRange.start + horizontalScroll / horizontalDimensions.xSpacing * chartValues.xStep * + layoutDirectionMultiplier + val lastVisibleX = firstVisibleX + bounds.width() / horizontalDimensions.xSpacing * chartValues.xStep + val visibleXRange = firstVisibleX..lastVisibleX + val labelValues = itemPlacer.getLabelValues(this, visibleXRange, fullXRange) + val lineValues = itemPlacer.getLineValues(this, visibleXRange, fullXRange) + + labelValues.forEachIndexed { index, x -> + val canvasX = + baseCanvasX + (x - chartValues.minX) / chartValues.xStep * horizontalDimensions.xSpacing * + layoutDirectionMultiplier + val previousX = labelValues.getOrNull(index - 1) ?: (fullXRange.start.doubled - x) + val nextX = labelValues.getOrNull(index + 1) ?: (fullXRange.endInclusive.doubled - x) + val maxWidth = + (min(x - previousX, nextX - x) / chartValues.xStep * horizontalDimensions.xSpacing).ceil.toInt() + + label?.drawText( + context = context, + text = valueFormatter.formatValue(x, chartValues), + textX = canvasX, + textY = textY, + verticalPosition = position.textVerticalPosition, + maxTextWidth = maxWidth, + maxTextHeight = (bounds.height() - tickLength - axisThickness.half).toInt(), + rotationDegrees = labelRotationDegrees, + ) - val textY = if (position.isBottom) tickMarkBottom else tickMarkTop - val fullXRange = getFullXRange(horizontalDimensions) - val baseCanvasX = bounds.getStart(isLtr) - horizontalScroll + horizontalDimensions.startPadding * - layoutDirectionMultiplier - val firstVisibleX = fullXRange.start + horizontalScroll / horizontalDimensions.xSpacing * chartValues.xStep * - layoutDirectionMultiplier - val lastVisibleX = firstVisibleX + bounds.width() / horizontalDimensions.xSpacing * chartValues.xStep - val visibleXRange = firstVisibleX..lastVisibleX - val labelValues = itemPlacer.getLabelValues(this, visibleXRange, fullXRange) - val lineValues = itemPlacer.getLineValues(this, visibleXRange, fullXRange) - - labelValues.forEachIndexed { index, x -> - val canvasX = baseCanvasX + (x - chartValues.minX) / chartValues.xStep * horizontalDimensions.xSpacing * - layoutDirectionMultiplier - val previousX = labelValues.getOrNull(index - 1) ?: (fullXRange.start.doubled - x) - val nextX = labelValues.getOrNull(index + 1) ?: (fullXRange.endInclusive.doubled - x) - val maxWidth = - (min(x - previousX, nextX - x) / chartValues.xStep * horizontalDimensions.xSpacing).ceil.toInt() - - label?.drawText( - context = context, - text = valueFormatter.formatValue(x, chartValues), - textX = canvasX, - textY = textY, - verticalPosition = position.textVerticalPosition, - maxTextWidth = maxWidth, - maxTextHeight = (bounds.height() - tickLength - axisThickness.half).toInt(), - rotationDegrees = labelRotationDegrees, - ) + if (lineValues == null) { + tick?.drawVertical( + context = this, + top = tickMarkTop, + bottom = tickMarkBottom, + centerX = canvasX + getLinesCorrectionX(x, fullXRange), + ) + } + } - if (lineValues == null) { + lineValues?.forEach { x -> tick?.drawVertical( context = this, top = tickMarkTop, bottom = tickMarkBottom, - centerX = canvasX + getLinesCorrectionX(x, fullXRange), + centerX = + baseCanvasX + (x - chartValues.minX) / chartValues.xStep * horizontalDimensions.xSpacing * + layoutDirectionMultiplier + getLinesCorrectionX(x, fullXRange), ) } - } - - lineValues?.forEach { x -> - tick?.drawVertical( - context = this, - top = tickMarkTop, - bottom = tickMarkBottom, - centerX = baseCanvasX + (x - chartValues.minX) / chartValues.xStep * horizontalDimensions.xSpacing * - layoutDirectionMultiplier + getLinesCorrectionX(x, fullXRange), - ) - } - - val axisLineExtend = if (itemPlacer.getShiftExtremeTicks(context)) { - tickThickness - } else { - tickThickness.half - } - axisLine?.drawHorizontal( - context = context, - left = chartBounds.left - axisLineExtend, - right = chartBounds.right + axisLineExtend, - centerY = if (position.isBottom) bounds.top + axisThickness.half else bounds.bottom - axisThickness.half, - ) + val axisLineExtend = + if (itemPlacer.getShiftExtremeTicks(context)) { + tickThickness + } else { + tickThickness.half + } - title?.let { title -> - titleComponent?.drawText( + axisLine?.drawHorizontal( context = context, - textX = bounds.centerX(), - textY = if (position.isTop) bounds.top else bounds.bottom, - verticalPosition = if (position.isTop) VerticalPosition.Bottom else VerticalPosition.Top, - maxTextWidth = bounds.width().toInt(), - text = title, + left = chartBounds.left - axisLineExtend, + right = chartBounds.right + axisLineExtend, + centerY = + if (position.isBottom) bounds.top + axisThickness.half else bounds.bottom - axisThickness.half, ) - } - if (clipRestoreCount >= 0) canvas.restoreToCount(clipRestoreCount) + title?.let { title -> + titleComponent?.drawText( + context = context, + textX = bounds.centerX(), + textY = if (position.isTop) bounds.top else bounds.bottom, + verticalPosition = if (position.isTop) VerticalPosition.Bottom else VerticalPosition.Top, + maxTextWidth = bounds.width().toInt(), + text = title, + ) + } - drawGuidelines(baseCanvasX, fullXRange, labelValues, lineValues) - } + if (clipRestoreCount >= 0) canvas.restoreToCount(clipRestoreCount) + + drawGuidelines(baseCanvasX, fullXRange, labelValues, lineValues) + } private fun ChartDrawContext.drawGuidelines( baseCanvasX: Float, @@ -160,8 +166,9 @@ public class HorizontalAxis( if (lineValues == null) { labelValues.forEach { x -> - val canvasX = baseCanvasX + (x - chartValues.minX) / chartValues.xStep * horizontalDimensions.xSpacing * - layoutDirectionMultiplier + val canvasX = + baseCanvasX + (x - chartValues.minX) / chartValues.xStep * horizontalDimensions.xSpacing * + layoutDirectionMultiplier guideline .takeUnless { x.isBoundOf(fullXRange) } @@ -169,8 +176,9 @@ public class HorizontalAxis( } } else { lineValues.forEach { x -> - val canvasX = baseCanvasX + (x - chartValues.minX) / chartValues.xStep * horizontalDimensions.xSpacing * - layoutDirectionMultiplier + getLinesCorrectionX(x, fullXRange) + val canvasX = + baseCanvasX + (x - chartValues.minX) / chartValues.xStep * horizontalDimensions.xSpacing * + layoutDirectionMultiplier + getLinesCorrectionX(x, fullXRange) guideline .takeUnless { x.isBoundOf(fullXRange) } @@ -200,24 +208,26 @@ public class HorizontalAxis( ) { val chartValues = context.chartValuesProvider.getChartValues() horizontalDimensions.ensureValuesAtLeast( - unscalableStartPadding = label - .takeIf { itemPlacer.getAddFirstLabelPadding(context) } - ?.getWidth( - context = context, - text = valueFormatter.formatValue(chartValues.minX, chartValues), - pad = true, - ) - ?.half - .orZero, - unscalableEndPadding = label - .takeIf { itemPlacer.getAddLastLabelPadding(context) } - ?.getWidth( - context = context, - text = valueFormatter.formatValue(chartValues.maxX, chartValues), - pad = true, - ) - ?.half - .orZero, + unscalableStartPadding = + label + .takeIf { itemPlacer.getAddFirstLabelPadding(context) } + ?.getWidth( + context = context, + text = valueFormatter.formatValue(chartValues.minX, chartValues), + pad = true, + ) + ?.half + .orZero, + unscalableEndPadding = + label + .takeIf { itemPlacer.getAddLastLabelPadding(context) } + ?.getWidth( + context = context, + text = valueFormatter.formatValue(chartValues.maxX, chartValues), + pad = true, + ) + ?.half + .orZero, ) } @@ -225,64 +235,73 @@ public class HorizontalAxis( context: MeasureContext, outInsets: Insets, horizontalDimensions: HorizontalDimensions, - ): Unit = with(outInsets) { - start = itemPlacer.getStartHorizontalAxisInset(context, horizontalDimensions, context.tickThickness) - end = itemPlacer.getEndHorizontalAxisInset(context, horizontalDimensions, context.tickThickness) - top = if (position.isTop) getDesiredHeight(context, horizontalDimensions) else 0f - bottom = if (position.isBottom) getDesiredHeight(context, horizontalDimensions) else 0f - } + ): Unit = + with(outInsets) { + start = itemPlacer.getStartHorizontalAxisInset(context, horizontalDimensions, context.tickThickness) + end = itemPlacer.getEndHorizontalAxisInset(context, horizontalDimensions, context.tickThickness) + top = if (position.isTop) getDesiredHeight(context, horizontalDimensions) else 0f + bottom = if (position.isBottom) getDesiredHeight(context, horizontalDimensions) else 0f + } private fun MeasureContext.getFullXRange( horizontalDimensions: HorizontalDimensions, - ): ClosedFloatingPointRange = with(horizontalDimensions) { - val chartValues = chartValuesProvider.getChartValues() - val start = chartValues.minX - startPadding / xSpacing * chartValues.xStep - val end = chartValues.maxX + endPadding / xSpacing * chartValues.xStep - start..end - } + ): ClosedFloatingPointRange = + with(horizontalDimensions) { + val chartValues = chartValuesProvider.getChartValues() + val start = chartValues.minX - startPadding / xSpacing * chartValues.xStep + val end = chartValues.maxX + endPadding / xSpacing * chartValues.xStep + start..end + } private fun getDesiredHeight( context: MeasureContext, horizontalDimensions: HorizontalDimensions, - ): Float = with(context) { - val chartValues = chartValuesProvider.getChartValues() - val fullXRange = getFullXRange(horizontalDimensions) - - when (val constraint = sizeConstraint) { - is SizeConstraint.Auto -> { - val labelHeight = label?.let { label -> - itemPlacer - .getMeasuredLabelValues(this, horizontalDimensions, fullXRange) - .map { valueFormatter.formatValue(it, chartValues) } - .maxOf { labelText -> - label.getHeight( - context = this, - text = labelText, - rotationDegrees = labelRotationDegrees, - pad = true, - ).orZero - } - }.orZero - val titleComponentHeight = title?.let { title -> - titleComponent?.getHeight( - context = context, - width = bounds.width().toInt(), - text = title, - ) - }.orZero - (labelHeight + titleComponentHeight + (if (position.isBottom) axisThickness else 0f) + tickLength) - .coerceAtMost(maximumValue = canvasBounds.height() / MAX_HEIGHT_DIVISOR) - .coerceIn(minimumValue = constraint.minSizeDp.pixels, maximumValue = constraint.maxSizeDp.pixels) + ): Float = + with(context) { + val chartValues = chartValuesProvider.getChartValues() + val fullXRange = getFullXRange(horizontalDimensions) + + when (val constraint = sizeConstraint) { + is SizeConstraint.Auto -> { + val labelHeight = + label?.let { label -> + itemPlacer + .getMeasuredLabelValues(this, horizontalDimensions, fullXRange) + .map { valueFormatter.formatValue(it, chartValues) } + .maxOf { labelText -> + label.getHeight( + context = this, + text = labelText, + rotationDegrees = labelRotationDegrees, + pad = true, + ).orZero + } + }.orZero + val titleComponentHeight = + title?.let { title -> + titleComponent?.getHeight( + context = context, + width = bounds.width().toInt(), + text = title, + ) + }.orZero + (labelHeight + titleComponentHeight + (if (position.isBottom) axisThickness else 0f) + tickLength) + .coerceAtMost(maximumValue = canvasBounds.height() / MAX_HEIGHT_DIVISOR) + .coerceIn( + minimumValue = constraint.minSizeDp.pixels, + maximumValue = constraint.maxSizeDp.pixels, + ) + } + is SizeConstraint.Exact -> constraint.sizeDp.pixels + is SizeConstraint.Fraction -> canvasBounds.height() * constraint.fraction + is SizeConstraint.TextWidth -> + label?.getHeight( + context = this, + text = constraint.text, + rotationDegrees = labelRotationDegrees, + ).orZero } - is SizeConstraint.Exact -> constraint.sizeDp.pixels - is SizeConstraint.Fraction -> canvasBounds.height() * constraint.fraction - is SizeConstraint.TextWidth -> label?.getHeight( - context = this, - text = constraint.text, - rotationDegrees = labelRotationDegrees, - ).orZero } - } /** * A subclass of [Axis.Builder] used to build [HorizontalAxis] instances. @@ -290,7 +309,6 @@ public class HorizontalAxis( public class Builder( builder: Axis.Builder? = null, ) : Axis.Builder(builder) { - /** * Determines for what _x_ values the [HorizontalAxis] is to display labels, ticks, and guidelines. */ @@ -301,11 +319,12 @@ public class HorizontalAxis( */ @Suppress("UNCHECKED_CAST") public inline fun build(): HorizontalAxis { - val position = when (T::class.java) { - AxisPosition.Horizontal.Top::class.java -> AxisPosition.Horizontal.Top - AxisPosition.Horizontal.Bottom::class.java -> AxisPosition.Horizontal.Bottom - else -> throw UnknownAxisPositionException(T::class.java) - } as Position + val position = + when (T::class.java) { + AxisPosition.Horizontal.Top::class.java -> AxisPosition.Horizontal.Top + AxisPosition.Horizontal.Bottom::class.java -> AxisPosition.Horizontal.Bottom + else -> throw UnknownAxisPositionException(T::class.java) + } as Position return setTo(HorizontalAxis(position = position)).also { it.itemPlacer = itemPlacer } as HorizontalAxis } } 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 06d723685..56e723c36 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 @@ -28,7 +28,6 @@ internal class DefaultVerticalAxisItemPlacer( private val maxItemCount: Int, private val shiftTopLines: Boolean, ) : AxisItemPlacer.Vertical { - init { require(maxItemCount >= 0) { "`maxItemCount` must be nonnegative." } } @@ -85,17 +84,22 @@ internal class DefaultVerticalAxisItemPlacer( verticalLabelPosition: VerticalAxis.VerticalLabelPosition, maxLabelHeight: Float, maxLineThickness: Float, - ): Float = when { - maxItemCount == 0 -> 0f - verticalLabelPosition == VerticalAxis.VerticalLabelPosition.Top -> maxLineThickness + ): Float = + when { + maxItemCount == 0 -> 0f + verticalLabelPosition == VerticalAxis.VerticalLabelPosition.Top -> maxLineThickness - verticalLabelPosition == VerticalAxis.VerticalLabelPosition.Center -> - (maxOf(maxLabelHeight, maxLineThickness) + maxLineThickness).half + verticalLabelPosition == VerticalAxis.VerticalLabelPosition.Center -> + (maxOf(maxLabelHeight, maxLineThickness) + maxLineThickness).half - else -> maxLabelHeight + maxLineThickness.half - } + else -> maxLabelHeight + maxLineThickness.half + } - private fun getSimpleLabelValues(axisHeight: Float, maxLabelHeight: Float, chartValues: ChartValues): List { + private fun getSimpleLabelValues( + axisHeight: Float, + maxLabelHeight: Float, + chartValues: ChartValues, + ): List { val values = mutableListOf(chartValues.minY) if (maxItemCount == 1) return values val extraItemCount = (axisHeight / maxLabelHeight).toInt().coerceAtMost(maxItemCount - 1) @@ -104,7 +108,11 @@ internal class DefaultVerticalAxisItemPlacer( return values } - private fun getMixedLabelValues(axisHeight: Float, maxLabelHeight: Float, chartValues: ChartValues): List { + private fun getMixedLabelValues( + axisHeight: Float, + maxLabelHeight: Float, + chartValues: ChartValues, + ): List { val values = mutableListOf(0f) if (maxItemCount == 1) return values val topHeight = chartValues.maxY / chartValues.lengthY * axisHeight 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 9bbc9b544..c4a04aa7d 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 @@ -51,10 +51,10 @@ private const val TITLE_ABS_ROTATION_DEGREES = 90f public class VerticalAxis( override val position: Position, ) : Axis() { - private val areLabelsOutsideAtStartOrInsideAtEnd - get() = horizontalLabelPosition == Outside && position is AxisPosition.Vertical.Start || - horizontalLabelPosition == Inside && position is AxisPosition.Vertical.End + get() = + horizontalLabelPosition == Outside && position is AxisPosition.Vertical.Start || + horizontalLabelPosition == Inside && position is AxisPosition.Vertical.End private val textHorizontalPosition: HorizontalPosition get() = if (areLabelsOutsideAtStartOrInsideAtEnd) HorizontalPosition.Start else HorizontalPosition.End @@ -74,88 +74,90 @@ public class VerticalAxis( */ public var verticalLabelPosition: VerticalLabelPosition = Center - override fun drawBehindChart( - context: ChartDrawContext, - ): Unit = with(context) { - var centerY: Float - val chartValues = chartValuesProvider.getChartValues(position) - val maxLabelHeight = getMaxLabelHeight() - val lineValues = itemPlacer.getLineValues(this, bounds.height(), maxLabelHeight, position) - ?: itemPlacer.getLabelValues(this, bounds.height(), maxLabelHeight, position) - - lineValues.forEach { lineValue -> - centerY = bounds.bottom - bounds.height() * (lineValue - chartValues.minY) / chartValues.lengthY + - getLineCanvasYCorrection(guidelineThickness, lineValue) - - guideline?.takeIf { - isNotInRestrictedBounds( + override fun drawBehindChart(context: ChartDrawContext): Unit = + with(context) { + var centerY: Float + val chartValues = chartValuesProvider.getChartValues(position) + val maxLabelHeight = getMaxLabelHeight() + val lineValues = + itemPlacer.getLineValues(this, bounds.height(), maxLabelHeight, position) + ?: itemPlacer.getLabelValues(this, bounds.height(), maxLabelHeight, position) + + lineValues.forEach { lineValue -> + centerY = bounds.bottom - bounds.height() * (lineValue - chartValues.minY) / chartValues.lengthY + + getLineCanvasYCorrection(guidelineThickness, lineValue) + + guideline?.takeIf { + isNotInRestrictedBounds( + left = chartBounds.left, + top = centerY - guidelineThickness.half, + right = chartBounds.right, + bottom = centerY + guidelineThickness.half, + ) + }?.drawHorizontal( + context = context, left = chartBounds.left, - top = centerY - guidelineThickness.half, right = chartBounds.right, - bottom = centerY + guidelineThickness.half, + centerY = centerY, ) - }?.drawHorizontal( + } + val axisLineExtensionLength = if (itemPlacer.getShiftTopLines(this)) tickThickness else 0f + axisLine?.drawVertical( context = context, - left = chartBounds.left, - right = chartBounds.right, - centerY = centerY, + top = bounds.top - axisLineExtensionLength, + bottom = bounds.bottom + axisLineExtensionLength, + centerX = + if (position.isLeft(isLtr = isLtr)) { + bounds.right - axisThickness.half + } else { + bounds.left + axisThickness.half + }, ) } - val axisLineExtensionLength = if (itemPlacer.getShiftTopLines(this)) tickThickness else 0f - axisLine?.drawVertical( - context = context, - top = bounds.top - axisLineExtensionLength, - bottom = bounds.bottom + axisLineExtensionLength, - centerX = if (position.isLeft(isLtr = isLtr)) { - bounds.right - axisThickness.half - } else { - bounds.left + axisThickness.half - }, - ) - } - override fun drawAboveChart(context: ChartDrawContext): Unit = with(context) { - val label = label - val labelValues = itemPlacer.getLabelValues(this, bounds.height(), getMaxLabelHeight(), position) - val tickLeftX = getTickLeftX() - val tickRightX = tickLeftX + axisThickness + tickLength - val labelX = if (areLabelsOutsideAtStartOrInsideAtEnd == isLtr) tickLeftX else tickRightX - var tickCenterY: Float - val chartValues = chartValuesProvider.getChartValues(position) - - labelValues.forEach { labelValue -> - tickCenterY = bounds.bottom - bounds.height() * (labelValue - chartValues.minY) / chartValues.lengthY + - getLineCanvasYCorrection(tickThickness, labelValue) - - tick?.drawHorizontal( - context = context, - left = tickLeftX, - right = tickRightX, - centerY = tickCenterY, - ) + override fun drawAboveChart(context: ChartDrawContext): Unit = + with(context) { + val label = label + val labelValues = itemPlacer.getLabelValues(this, bounds.height(), getMaxLabelHeight(), position) + val tickLeftX = getTickLeftX() + val tickRightX = tickLeftX + axisThickness + tickLength + val labelX = if (areLabelsOutsideAtStartOrInsideAtEnd == isLtr) tickLeftX else tickRightX + var tickCenterY: Float + val chartValues = chartValuesProvider.getChartValues(position) + + labelValues.forEach { labelValue -> + tickCenterY = bounds.bottom - bounds.height() * (labelValue - chartValues.minY) / chartValues.lengthY + + getLineCanvasYCorrection(tickThickness, labelValue) + + tick?.drawHorizontal( + context = context, + left = tickLeftX, + right = tickRightX, + centerY = tickCenterY, + ) - label ?: return@forEach - drawLabel( - label = label, - labelText = valueFormatter.formatValue(labelValue, chartValues), - labelX = labelX, - tickCenterY = tickCenterY, - ) - } + label ?: return@forEach + drawLabel( + label = label, + labelText = valueFormatter.formatValue(labelValue, chartValues), + labelX = labelX, + tickCenterY = tickCenterY, + ) + } - title?.let { title -> - titleComponent?.drawText( - context = this, - text = title, - textX = if (position.isStart) bounds.getStart(isLtr = isLtr) else bounds.getEnd(isLtr = isLtr), - textY = bounds.centerY(), - horizontalPosition = if (position.isStart) HorizontalPosition.End else HorizontalPosition.Start, - verticalPosition = VerticalPosition.Center, - rotationDegrees = TITLE_ABS_ROTATION_DEGREES * if (position.isStart) -1f else 1f, - maxTextHeight = bounds.height().toInt(), - ) + title?.let { title -> + titleComponent?.drawText( + context = this, + text = title, + textX = if (position.isStart) bounds.getStart(isLtr = isLtr) else bounds.getEnd(isLtr = isLtr), + textY = bounds.centerY(), + horizontalPosition = if (position.isStart) HorizontalPosition.End else HorizontalPosition.Start, + verticalPosition = VerticalPosition.Center, + rotationDegrees = TITLE_ABS_ROTATION_DEGREES * if (position.isStart) -1f else 1f, + maxTextHeight = bounds.height().toInt(), + ) + } } - } override fun updateHorizontalDimensions( context: MeasureContext, @@ -168,12 +170,13 @@ public class VerticalAxis( labelX: Float, tickCenterY: Float, ) { - val textBounds = label.getTextBounds(this, labelText, rotationDegrees = labelRotationDegrees).apply { - translate( - x = labelX, - y = tickCenterY - centerY(), - ) - } + val textBounds = + label.getTextBounds(this, labelText, rotationDegrees = labelRotationDegrees).apply { + translate( + x = labelX, + y = tickCenterY - centerY(), + ) + } if ( horizontalLabelPosition == Outside || @@ -192,11 +195,12 @@ public class VerticalAxis( horizontalPosition = textHorizontalPosition, verticalPosition = verticalLabelPosition.textPosition, rotationDegrees = labelRotationDegrees, - maxTextWidth = when (sizeConstraint) { - // Let the `TextComponent` use as much width as it needs, based on the measuring phase. - is SizeConstraint.Auto -> Int.MAX_VALUE - else -> (bounds.width() - tickLength - axisThickness).toInt() - }, + maxTextWidth = + when (sizeConstraint) { + // Let the `TextComponent` use as much width as it needs, based on the measuring phase. + is SizeConstraint.Auto -> Int.MAX_VALUE + else -> (bounds.width() - tickLength - axisThickness).toInt() + }, ) } } @@ -217,72 +221,83 @@ public class VerticalAxis( context: MeasureContext, availableHeight: Float, outInsets: HorizontalInsets, - ): Unit = with(context) { - val desiredWidth = getDesiredWidth(availableHeight) + ): Unit = + with(context) { + val desiredWidth = getDesiredWidth(availableHeight) - outInsets.set( - start = if (position.isStart) desiredWidth else 0f, - end = if (position.isEnd) desiredWidth else 0f, - ) - } + outInsets.set( + start = if (position.isStart) desiredWidth else 0f, + end = if (position.isEnd) desiredWidth else 0f, + ) + } override fun getInsets( context: MeasureContext, outInsets: Insets, horizontalDimensions: HorizontalDimensions, - ): Unit = with(context) { - val maxLabelHeight = getMaxLabelHeight() - val maxLineThickness = maxOf(axisThickness, tickThickness) - outInsets.set( - top = itemPlacer.getTopVerticalAxisInset(verticalLabelPosition, maxLabelHeight, maxLineThickness), - bottom = itemPlacer.getBottomVerticalAxisInset(verticalLabelPosition, maxLabelHeight, maxLineThickness), - ) - } + ): Unit = + with(context) { + val maxLabelHeight = getMaxLabelHeight() + val maxLineThickness = maxOf(axisThickness, tickThickness) + outInsets.set( + top = itemPlacer.getTopVerticalAxisInset(verticalLabelPosition, maxLabelHeight, maxLineThickness), + bottom = itemPlacer.getBottomVerticalAxisInset(verticalLabelPosition, maxLabelHeight, maxLineThickness), + ) + } /** * Calculates the optimal width for this [VerticalAxis], accounting for the value of [sizeConstraint]. */ - private fun MeasureContext.getDesiredWidth(height: Float) = when (val constraint = sizeConstraint) { - is SizeConstraint.Auto -> { - val titleComponentWidth = title?.let { title -> - titleComponent?.getWidth( - context = this, - text = title, - rotationDegrees = TITLE_ABS_ROTATION_DEGREES, - height = bounds.height().toInt(), - ) - }.orZero - val labelSpace = when (horizontalLabelPosition) { - Outside -> getMaxLabelWidth(height) - Inside -> 0f + private fun MeasureContext.getDesiredWidth(height: Float) = + when (val constraint = sizeConstraint) { + is SizeConstraint.Auto -> { + val titleComponentWidth = + title?.let { title -> + titleComponent?.getWidth( + context = this, + text = title, + rotationDegrees = TITLE_ABS_ROTATION_DEGREES, + height = bounds.height().toInt(), + ) + }.orZero + val labelSpace = + when (horizontalLabelPosition) { + Outside -> getMaxLabelWidth(height) + Inside -> 0f + } + (labelSpace + titleComponentWidth + axisThickness + tickLength) + .coerceIn(minimumValue = constraint.minSizeDp.pixels, maximumValue = constraint.maxSizeDp.pixels) } - (labelSpace + titleComponentWidth + axisThickness + tickLength) - .coerceIn(minimumValue = constraint.minSizeDp.pixels, maximumValue = constraint.maxSizeDp.pixels) + is SizeConstraint.Exact -> constraint.sizeDp.pixels + is SizeConstraint.Fraction -> canvasBounds.width() * constraint.fraction + is SizeConstraint.TextWidth -> + label?.getWidth( + context = this, + text = constraint.text, + rotationDegrees = labelRotationDegrees, + ).orZero + tickLength + axisThickness.half } - is SizeConstraint.Exact -> constraint.sizeDp.pixels - is SizeConstraint.Fraction -> canvasBounds.width() * constraint.fraction - is SizeConstraint.TextWidth -> label?.getWidth( - context = this, - text = constraint.text, - rotationDegrees = labelRotationDegrees, - ).orZero + tickLength + axisThickness.half - } - - private fun MeasureContext.getMaxLabelHeight() = label?.let { label -> - 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 = 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 { + private fun MeasureContext.getMaxLabelHeight() = + label?.let { label -> + 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 = 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 = chartValuesProvider.getChartValues(position) return if (y == chartValues.maxY && itemPlacer.getShiftTopLines(this)) -thickness.half else thickness.half } @@ -291,7 +306,8 @@ public class VerticalAxis( * Defines the horizontal position of each of a vertical axis’s labels relative to the axis line. */ public enum class HorizontalLabelPosition { - Outside, Inside + Outside, + Inside, } /** @@ -333,11 +349,12 @@ public class VerticalAxis( */ @Suppress("UNCHECKED_CAST") public inline fun build(): VerticalAxis { - val position = when (T::class.java) { - AxisPosition.Vertical.Start::class.java -> AxisPosition.Vertical.Start - AxisPosition.Vertical.End::class.java -> AxisPosition.Vertical.End - else -> throw UnknownAxisPositionException(T::class.java) - } as Position + val position = + when (T::class.java) { + AxisPosition.Vertical.Start::class.java -> AxisPosition.Vertical.Start + AxisPosition.Vertical.End::class.java -> AxisPosition.Vertical.End + else -> throw UnknownAxisPositionException(T::class.java) + } as Position return setTo(VerticalAxis(position)).also { axis -> axis.itemPlacer = itemPlacer axis.horizontalLabelPosition = horizontalLabelPosition 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 15390a491..7ba2f07f9 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 @@ -35,7 +35,6 @@ import com.patrykandpatrick.vico.core.marker.Marker * @see Chart */ public abstract class BaseChart : Chart, BoundsAware { - private val decorations = ArrayList() private val insets: Insets = Insets() @@ -59,7 +58,10 @@ public abstract class BaseChart : Chart, Boun override fun removeDecoration(decoration: Decoration): Boolean = decorations.remove(decoration) - override fun addPersistentMarker(x: Float, marker: Marker) { + override fun addPersistentMarker( + x: Float, + marker: Marker, + ) { persistentMarkers[x] = marker } @@ -74,35 +76,37 @@ public abstract class BaseChart : Chart, Boun override fun drawScrollableContent( context: ChartDrawContext, model: Model, - ): Unit = with(context) { - insets.clear() - getInsets(this, insets, horizontalDimensions) - drawChartInternal(context, model) - } + ): Unit = + with(context) { + insets.clear() + getInsets(this, insets, horizontalDimensions) + drawChartInternal(context, model) + } override fun drawNonScrollableContent( context: ChartDrawContext, model: Model, - ): Unit = with(context) { - canvas.inClip( - left = bounds.left, - top = 0f, - right = bounds.right, - bottom = context.canvas.height.toFloat(), - ) { - drawDecorationAboveChart(context) - } - persistentMarkers.forEach { (x, marker) -> - entryLocationMap.getEntryModel(x)?.also { markerModel -> - marker.draw( - context = context, - bounds = bounds, - markedEntries = markerModel, - chartValuesProvider = chartValuesProvider, - ) + ): Unit = + with(context) { + canvas.inClip( + left = bounds.left, + top = 0f, + right = bounds.right, + bottom = context.canvas.height.toFloat(), + ) { + drawDecorationAboveChart(context) + } + persistentMarkers.forEach { (x, marker) -> + entryLocationMap.getEntryModel(x)?.also { markerModel -> + marker.draw( + context = context, + bounds = bounds, + markedEntries = markerModel, + chartValuesProvider = chartValuesProvider, + ) + } } } - } /** * An internal function that draws both [Decoration]s behind the chart and the chart itself in the clip bounds. @@ -110,19 +114,20 @@ public abstract class BaseChart : Chart, Boun protected open fun drawChartInternal( context: ChartDrawContext, model: Model, - ): Unit = with(context) { - canvas.inClip( - left = bounds.left - insets.getLeft(isLtr), - top = bounds.top - insets.top, - right = bounds.right + insets.getRight(isLtr), - bottom = bounds.bottom + insets.bottom, - ) { - drawDecorationBehindChart(context) - if (model.entries.isNotEmpty()) { - drawChart(context, model) + ): Unit = + with(context) { + canvas.inClip( + left = bounds.left - insets.getLeft(isLtr), + top = bounds.top - insets.top, + right = bounds.right + insets.getRight(isLtr), + bottom = bounds.bottom + insets.bottom, + ) { + drawDecorationBehindChart(context) + if (model.entries.isNotEmpty()) { + drawChart(context, model) + } } } - } protected abstract fun drawChart( context: ChartDrawContext, 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 69c58d38d..6d0f9fdd2 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 @@ -39,7 +39,6 @@ import com.patrykandpatrick.vico.core.marker.Marker * Defines the minimal set of properties and functions required by other parts of the library to draw a chart. */ public interface Chart : BoundsAware, ChartInsetter { - /** * Links x-axis values to [Marker.EntryModel]s. A [Marker.EntryModel] holds the data needed to draw a [Marker]. */ @@ -117,15 +116,17 @@ public interface Chart : BoundsAware, ChartInsetter { * @see removeDecoration * @see Decoration */ - public fun removeDecorations(decorations: List): Boolean = - decorations.all(::removeDecoration) + public fun removeDecorations(decorations: List): Boolean = decorations.all(::removeDecoration) /** * Adds a persistent [Marker] to this [Chart]. The [Marker] will be anchored to the given [x] value on the x-axis. * * @see Marker */ - public fun addPersistentMarker(x: Float, marker: Marker) + public fun addPersistentMarker( + x: Float, + marker: Marker, + ) /** * Replaces the current map of markers with the provided [markers]. @@ -158,7 +159,11 @@ public interface Chart : BoundsAware, ChartInsetter { * @param model holds data about the [Chart]’s entries. * @param xStep the overridden _x_ step (or `null` if no override has occurred). */ - public fun updateChartValues(chartValuesManager: ChartValuesManager, model: Model, xStep: Float?) + public fun updateChartValues( + chartValuesManager: ChartValuesManager, + model: Model, + xStep: Float?, + ) /** * Provides the [Chart]’s [ModelTransformer]. @@ -197,7 +202,10 @@ public interface Chart : BoundsAware, ChartInsetter { /** * Carries out the pending difference animation. [fraction] is the animation progress. */ - public abstract suspend fun transform(extraStore: MutableExtraStore, fraction: Float) + public abstract suspend fun transform( + extraStore: MutableExtraStore, + fraction: Float, + ) } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/DefaultPointConnector.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/DefaultPointConnector.kt index d7dab7aaf..d861cd1c6 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/DefaultPointConnector.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/DefaultPointConnector.kt @@ -35,7 +35,6 @@ import kotlin.math.abs public class DefaultPointConnector( private val cubicStrength: Float = DefaultDimens.CUBIC_STRENGTH, ) : LineChart.LineSpec.PointConnector { - public override fun connect( path: Path, prevX: Float, @@ -50,13 +49,13 @@ public class DefaultPointConnector( prevY = prevY, x = x, y = y, - curvature = abs(x - prevX).half * cubicStrength * - (abs(x = y - prevY) / bounds.bottom * CUBIC_Y_MULTIPLIER).coerceAtMost(maximumValue = 1f), + curvature = + abs(x - prevX).half * cubicStrength * + (abs(x = y - prevY) / bounds.bottom * CUBIC_Y_MULTIPLIER).coerceAtMost(maximumValue = 1f), ) } private companion object { - const val CUBIC_Y_MULTIPLIER = 4 } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/EntryModelExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/EntryModelExtensions.kt index dbfffde04..b54fa27af 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/EntryModelExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/EntryModelExtensions.kt @@ -22,7 +22,10 @@ import com.patrykandpatrick.vico.core.entry.ChartEntry * For each [ChartEntry] in the list such that [ChartEntry.x] belongs to the provided range, calls the [action] function * block with the [ChartEntry] as the block’s argument. */ -public inline fun List.forEachIn(range: ClosedFloatingPointRange, action: (ChartEntry) -> Unit) { +public inline fun List.forEachIn( + range: ClosedFloatingPointRange, + action: (ChartEntry) -> Unit, +) { forEach { if (it.x in range) action(it) } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/LineSpecExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/LineSpecExtensions.kt index 8bfc2830e..e1004f388 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/LineSpecExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/LineSpecExtensions.kt @@ -39,16 +39,17 @@ public fun LineChart.LineSpec.copy( dataLabelValueFormatter: ValueFormatter = this.dataLabelValueFormatter, dataLabelRotationDegrees: Float = this.dataLabelRotationDegrees, pointConnector: LineChart.LineSpec.PointConnector = this.pointConnector, -): LineChart.LineSpec = LineChart.LineSpec( - lineShader = lineFill, - lineThicknessDp = lineThicknessDp, - lineBackgroundShader = lineBackgroundFill, - lineCap = lineCap, - point = point, - pointSizeDp = pointSizeDp, - dataLabel = dataLabel, - dataLabelVerticalPosition = dataLabelVerticalPosition, - dataLabelValueFormatter = dataLabelValueFormatter, - dataLabelRotationDegrees = dataLabelRotationDegrees, - pointConnector = pointConnector, -) +): LineChart.LineSpec = + LineChart.LineSpec( + lineShader = lineFill, + lineThicknessDp = lineThicknessDp, + lineBackgroundShader = lineBackgroundFill, + lineCap = lineCap, + point = point, + pointSizeDp = pointSizeDp, + dataLabel = dataLabel, + dataLabelVerticalPosition = dataLabelVerticalPosition, + dataLabelValueFormatter = dataLabelValueFormatter, + dataLabelRotationDegrees = dataLabelRotationDegrees, + pointConnector = pointConnector, + ) 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 8ff083013..b0cb328c7 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 @@ -82,7 +82,6 @@ public open class ColumnChart( ColumnChartDrawingModel, > = DefaultDrawingModelInterpolator(), ) : BaseChart() { - /** * Creates a [ColumnChart] with a common style for all columns. * @@ -122,15 +121,16 @@ public open class ColumnChart( override fun drawChart( context: ChartDrawContext, model: ChartEntryModel, - ): Unit = with(context) { - entryLocationMap.clear() - drawChartInternal( - chartValues = chartValuesProvider.getChartValues(axisPosition = targetVerticalAxisPosition), - model = model, - drawingModel = model.extraStore.getOrNull(drawingModelKey), - ) - heightMap.clear() - } + ): Unit = + with(context) { + entryLocationMap.clear() + drawChartInternal( + chartValues = chartValuesProvider.getChartValues(axisPosition = targetVerticalAxisPosition), + model = model, + drawingModel = model.extraStore.getOrNull(drawingModelKey), + ) + heightMap.clear() + } protected open fun ChartDrawContext.drawChartInternal( chartValues: ChartValues, @@ -266,44 +266,49 @@ public open class ColumnChart( val canUseXSpacing = mergeMode == MergeMode.Stack || mergeMode == MergeMode.Grouped && modelEntriesSize == 1 - var maxWidth = when { - canUseXSpacing -> horizontalDimensions.xSpacing - mergeMode == MergeMode.Grouped -> - (columnThicknessDp + minOf(spacingDp, innerSpacingDp).half).pixels * zoom + var maxWidth = + when { + canUseXSpacing -> horizontalDimensions.xSpacing + mergeMode == MergeMode.Grouped -> + (columnThicknessDp + minOf(spacingDp, innerSpacingDp).half).pixels * zoom - else -> error(message = "Encountered an unexpected `MergeMode`.") - } + else -> error(message = "Encountered an unexpected `MergeMode`.") + } if (isFirst && horizontalLayout is HorizontalLayout.FullWidth) { maxWidth = maxWidth.coerceAtMost(horizontalDimensions.startPadding.doubled) } if (isLast && horizontalLayout is HorizontalLayout.FullWidth) { maxWidth = maxWidth.coerceAtMost(horizontalDimensions.endPadding.doubled) } - val text = dataLabelValueFormatter.formatValue( - value = dataLabelValue, - chartValues = chartValuesProvider.getChartValues(axisPosition = targetVerticalAxisPosition), - ) - val dataLabelWidth = textComponent.getWidth( - context = this, - text = text, - rotationDegrees = dataLabelRotationDegrees, - ).coerceAtMost(maximumValue = maxWidth) + val text = + dataLabelValueFormatter.formatValue( + value = dataLabelValue, + chartValues = chartValuesProvider.getChartValues(axisPosition = targetVerticalAxisPosition), + ) + val dataLabelWidth = + textComponent.getWidth( + context = this, + text = text, + rotationDegrees = dataLabelRotationDegrees, + ).coerceAtMost(maximumValue = maxWidth) if (x - dataLabelWidth.half > bounds.right || x + dataLabelWidth.half < bounds.left) return val labelVerticalPosition = if (dataLabelValue < 0f) dataLabelVerticalPosition.negative() else dataLabelVerticalPosition - val verticalPosition = labelVerticalPosition.inBounds( - y = y, - bounds = bounds, - componentHeight = textComponent.getHeight( - context = this, - text = text, - width = maxWidth.toInt(), - rotationDegrees = dataLabelRotationDegrees, - ), - ) + val verticalPosition = + labelVerticalPosition.inBounds( + y = y, + bounds = bounds, + componentHeight = + textComponent.getHeight( + context = this, + text = text, + width = maxWidth.toInt(), + rotationDegrees = dataLabelRotationDegrees, + ), + ) textComponent.drawText( context = this, text = text, @@ -334,7 +339,11 @@ public open class ColumnChart( } } - override fun updateChartValues(chartValuesManager: ChartValuesManager, model: ChartEntryModel, xStep: Float?) { + override fun updateChartValues( + chartValuesManager: ChartValuesManager, + model: ChartEntryModel, + xStep: Float?, + ) { chartValuesManager.tryUpdate( minX = axisValuesOverrider?.getMinX(model) ?: model.minX, maxX = axisValuesOverrider?.getMaxX(model) ?: model.maxX, @@ -366,8 +375,9 @@ public open class ColumnChart( is HorizontalLayout.FullWidth -> { horizontalDimensions.ensureValuesAtLeast( xSpacing = xSpacing, - scalableStartPadding = columnCollectionWidth.half + - horizontalLayout.scalableStartPaddingDp.pixels, + scalableStartPadding = + columnCollectionWidth.half + + horizontalLayout.scalableStartPaddingDp.pixels, scalableEndPadding = columnCollectionWidth.half + horizontalLayout.scalableEndPaddingDp.pixels, unscalableStartPadding = horizontalLayout.unscalableStartPaddingDp.pixels, unscalableEndPadding = horizontalLayout.unscalableEndPaddingDp.pixels, @@ -377,34 +387,42 @@ public open class ColumnChart( } } - override val modelTransformerProvider: Chart.ModelTransformerProvider = object : Chart.ModelTransformerProvider { - private val modelTransformer = - ColumnChartModelTransformer(drawingModelKey, { targetVerticalAxisPosition }, { drawingModelInterpolator }) + override val modelTransformerProvider: Chart.ModelTransformerProvider = + object : Chart.ModelTransformerProvider { + private val modelTransformer = + ColumnChartModelTransformer( + drawingModelKey, + { targetVerticalAxisPosition }, + { drawingModelInterpolator }, + ) - override fun getModelTransformer(): Chart.ModelTransformer = modelTransformer - } - - protected open fun MeasureContext.getColumnCollectionWidth( - entryCollectionSize: Int, - ): Float = when (mergeMode) { - MergeMode.Stack -> - columns.take(entryCollectionSize).maxOf { it.thicknessDp.pixels } + override fun getModelTransformer(): Chart.ModelTransformer = modelTransformer + } - MergeMode.Grouped -> - getCumulatedThickness(entryCollectionSize) + innerSpacingDp.pixels * (entryCollectionSize - 1) - } + protected open fun MeasureContext.getColumnCollectionWidth(entryCollectionSize: Int): Float = + when (mergeMode) { + MergeMode.Stack -> + columns.take(entryCollectionSize).maxOf { it.thicknessDp.pixels } - protected open fun ChartDrawContext.getDrawingStart(entryCollectionIndex: Int, entryCollectionCount: Int): Float { - val mergeModeComponent = when (mergeMode) { MergeMode.Grouped -> - getCumulatedThickness(entryCollectionIndex) + innerSpacingDp.pixels * entryCollectionIndex - - MergeMode.Stack -> 0f + getCumulatedThickness(entryCollectionSize) + innerSpacingDp.pixels * (entryCollectionSize - 1) } + + protected open fun ChartDrawContext.getDrawingStart( + entryCollectionIndex: Int, + entryCollectionCount: Int, + ): Float { + val mergeModeComponent = + when (mergeMode) { + MergeMode.Grouped -> + getCumulatedThickness(entryCollectionIndex) + innerSpacingDp.pixels * entryCollectionIndex + + MergeMode.Stack -> 0f + } return bounds.getStart(isLtr) + ( horizontalDimensions.startPadding + (mergeModeComponent - getColumnCollectionWidth(entryCollectionCount).half) * zoom - ) * layoutDirectionMultiplier + ) * layoutDirectionMultiplier } protected open fun MeasureContext.getCumulatedThickness(count: Int): Float { @@ -419,7 +437,6 @@ public open class ColumnChart( * Defines how a [ColumnChart] should draw columns in column collections. */ public enum class MergeMode { - /** * Columns with the same x-axis values will be placed next to each other in groups. */ @@ -435,18 +452,20 @@ public open class ColumnChart( /** * Returns the minimum y-axis value, taking into account the current [MergeMode]. */ - public fun getMinY(model: ChartEntryModel): Float = when (this) { - Grouped -> model.minY.coerceAtMost(0f) - Stack -> model.stackedNegativeY.coerceAtMost(0f) - } + public fun getMinY(model: ChartEntryModel): Float = + when (this) { + Grouped -> model.minY.coerceAtMost(0f) + Stack -> model.stackedNegativeY.coerceAtMost(0f) + } /** * Returns the maximum y-axis value, taking into account the current [MergeMode]. */ - public fun getMaxY(model: ChartEntryModel): Float = when (this) { - Grouped -> model.maxY - Stack -> model.stackedPositiveY - } + public fun getMaxY(model: ChartEntryModel): Float = + when (this) { + Grouped -> model.maxY + Stack -> model.stackedPositiveY + } } protected class ColumnChartModelTransformer( @@ -457,7 +476,6 @@ public open class ColumnChart( ColumnChartDrawingModel, >, ) : Chart.ModelTransformer() { - override fun prepareForTransformation( oldModel: ChartEntryModel?, newModel: ChartEntryModel?, @@ -470,19 +488,23 @@ public open class ColumnChart( ) } - override suspend fun transform(extraStore: MutableExtraStore, fraction: Float) { + override suspend fun transform( + extraStore: MutableExtraStore, + fraction: Float, + ) { getDrawingModelInterpolator() .transform(fraction) ?.let { extraStore[key] = it } ?: extraStore.remove(key) } - private fun ChartEntryModel.toDrawingModel(chartValues: ChartValues): ColumnChartDrawingModel = entries - .map { series -> - series.associate { entry -> - entry.x to ColumnChartDrawingModel.ColumnInfo(abs(entry.y) / chartValues.lengthY) + private fun ChartEntryModel.toDrawingModel(chartValues: ChartValues): ColumnChartDrawingModel = + entries + .map { series -> + series.associate { entry -> + entry.x to ColumnChartDrawingModel.ColumnInfo(abs(entry.y) / chartValues.lengthY) + } } - } - .let(::ColumnChartDrawingModel) + .let(::ColumnChartDrawingModel) } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/column/ColumnChartDrawingModel.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/column/ColumnChartDrawingModel.kt index a0619ab25..38bd68fbb 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/column/ColumnChartDrawingModel.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/column/ColumnChartDrawingModel.kt @@ -25,7 +25,6 @@ import com.patrykandpatrick.vico.core.extension.orZero */ public class ColumnChartDrawingModel(entries: List>, public val opacity: Float = 1f) : DrawingModel(entries) { - override fun transform( drawingInfo: List>, from: DrawingModel?, @@ -40,7 +39,10 @@ public class ColumnChartDrawingModel(entries: List>, publ * of the [ColumnChart]’s height. */ public class ColumnInfo(public val height: Float) : DrawingInfo { - override fun transform(from: DrawingInfo?, fraction: Float): DrawingInfo { + override fun transform( + from: DrawingInfo?, + fraction: Float, + ): DrawingInfo { val oldHeight = (from as? ColumnInfo)?.height.orZero return ColumnInfo(oldHeight.lerp(height, fraction)) } 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 e203a43fe..66cbb5ff4 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 @@ -42,7 +42,6 @@ import java.util.TreeMap public class ComposedChart( charts: List>, ) : BaseChart>() { - public constructor(vararg charts: Chart) : this(charts.toList()) /** @@ -57,7 +56,12 @@ public class ComposedChart( override val chartInsetters: Collection get() = charts.map { it.chartInsetters }.flatten() + persistentMarkers.values - override fun setBounds(left: Number, top: Number, right: Number, bottom: Number) { + override fun setBounds( + left: Number, + top: Number, + right: Number, + bottom: Number, + ) { this.bounds.set(left, top, right, bottom) charts.forEach { chart -> chart.setBounds(left, top, right, bottom) } } @@ -73,7 +77,10 @@ public class ComposedChart( } } - override fun drawChartInternal(context: ChartDrawContext, model: ComposedChartEntryModel) { + override fun drawChartInternal( + context: ChartDrawContext, + model: ComposedChartEntryModel, + ) { drawDecorationBehindChart(context) if (model.entries.isNotEmpty()) { drawChart(context, model) @@ -111,7 +118,11 @@ public class ComposedChart( } } - override fun getHorizontalInsets(context: MeasureContext, availableHeight: Float, outInsets: HorizontalInsets) { + override fun getHorizontalInsets( + context: MeasureContext, + availableHeight: Float, + outInsets: HorizontalInsets, + ) { charts.forEach { chart -> chart.getHorizontalInsets(context, availableHeight, tempInsets) outInsets.setValuesIfGreater(start = tempInsets.start, end = tempInsets.end) @@ -130,17 +141,17 @@ public class ComposedChart( } } - override val modelTransformerProvider: ModelTransformerProvider = object : ModelTransformerProvider { - private val modelTransformer = - ComposedModelTransformer { charts.map { it.modelTransformerProvider.getModelTransformer() } } + override val modelTransformerProvider: ModelTransformerProvider = + object : ModelTransformerProvider { + private val modelTransformer = + ComposedModelTransformer { charts.map { it.modelTransformerProvider.getModelTransformer() } } - override fun getModelTransformer(): Chart.ModelTransformer = modelTransformer - } + override fun getModelTransformer(): Chart.ModelTransformer = modelTransformer + } private class ComposedModelTransformer( private val getModelTransformers: () -> List>, ) : Chart.ModelTransformer() { - override val key: ExtraStore.Key = ExtraStore.Key() override fun prepareForTransformation( @@ -160,7 +171,10 @@ public class ComposedChart( } } - override suspend fun transform(extraStore: MutableExtraStore, fraction: Float) { + override suspend fun transform( + extraStore: MutableExtraStore, + fraction: Float, + ) { getModelTransformers().forEach { transformer -> transformer.transform(extraStore, fraction) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChartEntryModel.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChartEntryModel.kt index 3085a359e..8db92a86f 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChartEntryModel.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChartEntryModel.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. @@ -22,7 +22,6 @@ import com.patrykandpatrick.vico.core.entry.ChartEntryModel * An extended [ChartEntryModel] that can compose multiple [ChartEntryModel]s. It is used by [ComposedChart]. */ public interface ComposedChartEntryModel : ChartEntryModel { - /** * A list of the [ChartEntryModel]s that make up this model. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChartExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChartExtensions.kt index 3784a7db1..cfcca4826 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChartExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChartExtensions.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. @@ -22,17 +22,13 @@ import com.patrykandpatrick.vico.core.entry.ChartEntryModel /** * Combines this [Chart] with another one to create a [ComposedChart]. */ -public operator fun Chart.plus( - other: Chart, -): ComposedChart = +public operator fun Chart.plus(other: Chart): ComposedChart = ComposedChart(listOf(this, other)) /** * Combines this [ComposedChart] with a [Chart] to create a [ComposedChart]. */ -public operator fun ComposedChart.plus( - other: Chart, -): ComposedChart = +public operator fun ComposedChart.plus(other: Chart): ComposedChart = ComposedChart(charts + other) /** @@ -40,5 +36,4 @@ public operator fun ComposedChart.plus( */ public operator fun ComposedChart.plus( other: ComposedChart, -): ComposedChart = - ComposedChart(charts + other.charts) +): ComposedChart = ComposedChart(charts + other.charts) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/decoration/Decoration.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/decoration/Decoration.kt index 30258f415..ac99316ed 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/decoration/Decoration.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/decoration/Decoration.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. @@ -28,14 +28,16 @@ import com.patrykandpatrick.vico.core.chart.draw.ChartDrawContext * @see [ThresholdLine] */ public interface Decoration { - /** * Called before the [Chart] starts drawing itself. * * @param [context] holds the information needed to draw the [Chart]. * @param [bounds] the bounding box of the [Chart]. */ - public fun onDrawBehindChart(context: ChartDrawContext, bounds: RectF): Unit = Unit + public fun onDrawBehindChart( + context: ChartDrawContext, + bounds: RectF, + ): Unit = Unit /** * Called immediately after the [Chart] finishes drawing itself. @@ -43,5 +45,8 @@ public interface Decoration { * @param [context] holds the information needed to draw the [Chart]. * @param [bounds] the bounding box of the [Chart]. */ - public fun onDrawAboveChart(context: ChartDrawContext, bounds: RectF): Unit = Unit + public fun onDrawAboveChart( + context: ChartDrawContext, + bounds: RectF, + ): Unit = Unit } 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 67cbd88ce..39cb34326 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 @@ -50,10 +50,11 @@ import java.text.DecimalFormat */ public data class ThresholdLine( val thresholdRange: ClosedFloatingPointRange, - val thresholdLabel: CharSequence = RANGE_FORMAT.format( - decimalFormat.format(thresholdRange.start), - decimalFormat.format(thresholdRange.endInclusive), - ), + val thresholdLabel: CharSequence = + RANGE_FORMAT.format( + decimalFormat.format(thresholdRange.start), + decimalFormat.format(thresholdRange.endInclusive), + ), val lineComponent: ShapeComponent = ShapeComponent(), val minimumLineThicknessDp: Float = DefaultDimens.THRESHOLD_LINE_THICKNESS, val labelComponent: TextComponent = textComponent(), @@ -61,7 +62,6 @@ public data class ThresholdLine( val labelVerticalPosition: LabelVerticalPosition = LabelVerticalPosition.Top, val labelRotationDegrees: Float = 0f, ) : Decoration { - /** * An alternative constructor that accepts a single y-axis value as opposed to a range. * @@ -98,55 +98,62 @@ public data class ThresholdLine( override fun onDrawAboveChart( context: ChartDrawContext, bounds: RectF, - ): Unit = with(context) { - val chartValues = chartValuesProvider.getChartValues() + ): Unit = + with(context) { + val chartValues = chartValuesProvider.getChartValues() - val valueRange = chartValues.maxY - chartValues.minY + val valueRange = chartValues.maxY - chartValues.minY - val centerY = bounds.bottom - (thresholdRange.median - chartValues.minY) / valueRange * bounds.height() + val centerY = bounds.bottom - (thresholdRange.median - chartValues.minY) / valueRange * bounds.height() - val topY = minOf( - bounds.bottom - (thresholdRange.endInclusive - chartValues.minY) / valueRange * bounds.height(), - centerY - minimumLineThicknessDp.pixels.half, - ).ceil - val bottomY = maxOf( - bounds.bottom - (thresholdRange.start - chartValues.minY) / valueRange * bounds.height(), - centerY + minimumLineThicknessDp.pixels.half, - ).floor - val textY = when (labelVerticalPosition) { - LabelVerticalPosition.Top -> topY - LabelVerticalPosition.Bottom -> bottomY - } + val topY = + minOf( + bounds.bottom - (thresholdRange.endInclusive - chartValues.minY) / valueRange * bounds.height(), + centerY - minimumLineThicknessDp.pixels.half, + ).ceil + val bottomY = + maxOf( + bounds.bottom - (thresholdRange.start - chartValues.minY) / valueRange * bounds.height(), + centerY + minimumLineThicknessDp.pixels.half, + ).floor + val textY = + when (labelVerticalPosition) { + LabelVerticalPosition.Top -> topY + LabelVerticalPosition.Bottom -> bottomY + } - lineComponent.draw( - context = context, - left = bounds.left, - right = bounds.right, - top = topY, - bottom = bottomY, - ) - labelComponent.drawText( - context = context, - text = thresholdLabel, - maxTextWidth = bounds.width().toInt(), - textX = when (labelHorizontalPosition) { - LabelHorizontalPosition.Start -> bounds.getStart(isLtr = isLtr) - LabelHorizontalPosition.End -> bounds.getEnd(isLtr = isLtr) - }, - textY = textY, - horizontalPosition = labelHorizontalPosition.position, - verticalPosition = labelVerticalPosition.position.inBounds( - bounds = bounds, - componentHeight = labelComponent.getHeight( - context = context, - text = thresholdLabel, - rotationDegrees = labelRotationDegrees, - ), - y = textY, - ), - rotationDegrees = labelRotationDegrees, - ) - } + lineComponent.draw( + context = context, + left = bounds.left, + right = bounds.right, + top = topY, + bottom = bottomY, + ) + labelComponent.drawText( + context = context, + text = thresholdLabel, + maxTextWidth = bounds.width().toInt(), + textX = + when (labelHorizontalPosition) { + LabelHorizontalPosition.Start -> bounds.getStart(isLtr = isLtr) + LabelHorizontalPosition.End -> bounds.getEnd(isLtr = isLtr) + }, + textY = textY, + horizontalPosition = labelHorizontalPosition.position, + verticalPosition = + labelVerticalPosition.position.inBounds( + bounds = bounds, + componentHeight = + labelComponent.getHeight( + context = context, + text = thresholdLabel, + rotationDegrees = labelRotationDegrees, + ), + y = textY, + ), + rotationDegrees = labelRotationDegrees, + ) + } /** * Defines the horizontal position of a [ThresholdLine]’s label. diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/dimensions/HorizontalDimensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/dimensions/HorizontalDimensions.kt index b9d731307..0665d4d4c 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/dimensions/HorizontalDimensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/dimensions/HorizontalDimensions.kt @@ -88,13 +88,14 @@ public interface HorizontalDimensions { /** * Creates a new [HorizontalDimensions] instance by multiplying this one’s scalable values by the given factor. */ - public fun scaled(scale: Float): HorizontalDimensions = HorizontalDimensions( - xSpacing * scale, - scalableStartPadding * scale, - scalableEndPadding * scale, - unscalableStartPadding, - unscalableEndPadding, - ) + public fun scaled(scale: Float): HorizontalDimensions = + HorizontalDimensions( + xSpacing * scale, + scalableStartPadding * scale, + scalableEndPadding * scale, + unscalableStartPadding, + unscalableEndPadding, + ) } /** @@ -106,10 +107,11 @@ public fun HorizontalDimensions( scalableEndPadding: Float, unscalableStartPadding: Float, unscalableEndPadding: Float, -): HorizontalDimensions = object : HorizontalDimensions { - override val xSpacing: Float = xSpacing - override val scalableStartPadding: Float = scalableStartPadding - override val scalableEndPadding: Float = scalableEndPadding - override val unscalableStartPadding: Float = unscalableStartPadding - override val unscalableEndPadding: Float = unscalableEndPadding -} +): HorizontalDimensions = + object : HorizontalDimensions { + override val xSpacing: Float = xSpacing + override val scalableStartPadding: Float = scalableStartPadding + override val scalableEndPadding: Float = scalableEndPadding + override val unscalableStartPadding: Float = unscalableStartPadding + override val unscalableEndPadding: Float = unscalableEndPadding + } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/dimensions/MutableHorizontalDimensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/dimensions/MutableHorizontalDimensions.kt index 2dedaca95..7293b34b8 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/dimensions/MutableHorizontalDimensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/dimensions/MutableHorizontalDimensions.kt @@ -35,13 +35,14 @@ public data class MutableHorizontalDimensions( scalableEndPadding: Float, unscalableStartPadding: Float, unscalableEndPadding: Float, - ): MutableHorizontalDimensions = apply { - this.xSpacing = xSpacing - this.scalableStartPadding = scalableStartPadding - this.scalableEndPadding = scalableEndPadding - this.unscalableStartPadding = unscalableStartPadding - this.unscalableEndPadding = unscalableEndPadding - } + ): MutableHorizontalDimensions = + apply { + this.xSpacing = xSpacing + this.scalableStartPadding = scalableStartPadding + this.scalableEndPadding = scalableEndPadding + this.unscalableStartPadding = unscalableStartPadding + this.unscalableEndPadding = unscalableEndPadding + } /** * Ensures that the stored values are no smaller than the provided ones. @@ -52,13 +53,14 @@ public data class MutableHorizontalDimensions( scalableEndPadding: Float = 0f, unscalableStartPadding: Float = 0f, unscalableEndPadding: Float = 0f, - ): MutableHorizontalDimensions = set( - this.xSpacing.coerceAtLeast(xSpacing), - this.scalableStartPadding.coerceAtLeast(scalableStartPadding), - this.scalableEndPadding.coerceAtLeast(scalableEndPadding), - this.unscalableStartPadding.coerceAtLeast(unscalableStartPadding), - this.unscalableEndPadding.coerceAtLeast(unscalableEndPadding), - ) + ): MutableHorizontalDimensions = + set( + this.xSpacing.coerceAtLeast(xSpacing), + this.scalableStartPadding.coerceAtLeast(scalableStartPadding), + this.scalableEndPadding.coerceAtLeast(scalableEndPadding), + this.unscalableStartPadding.coerceAtLeast(unscalableStartPadding), + this.unscalableEndPadding.coerceAtLeast(unscalableEndPadding), + ) /** * Clears the stored values. 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 d0da5bef0..36627c3f3 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 @@ -29,7 +29,6 @@ import com.patrykandpatrick.vico.core.model.Point * An extension of [DrawContext] that holds additional data required to render a [Chart]. */ public interface ChartDrawContext : DrawContext { - /** * The bounds in which the [Chart] will be drawn. */ @@ -64,9 +63,10 @@ public fun MeasureContext.getMaxScrollDistance( horizontalDimensions: HorizontalDimensions, zoom: Float? = null, ): Float { - val contentWidth = horizontalDimensions - .run { if (zoom != null) scaled(zoom) else this } - .getContentWidth(chartValuesProvider.getChartValues().getMaxMajorEntryCount()) + val contentWidth = + horizontalDimensions + .run { if (zoom != null) scaled(zoom) else this } + .getContentWidth(chartValuesProvider.getChartValues().getMaxMajorEntryCount()) return (layoutDirectionMultiplier * (contentWidth - chartWidth)).run { if (isLtr) coerceAtLeast(minimumValue = 0f) else coerceAtMost(maximumValue = 0f) 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 8d2e9cd2a..45d40cce9 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 @@ -52,29 +52,32 @@ public fun chartDrawContext( chartBounds: RectF, horizontalScroll: Float, zoom: Float, -): ChartDrawContext = object : ChartDrawContext, MeasureContext by measureContext { +): ChartDrawContext = + object : ChartDrawContext, MeasureContext by measureContext { + override val chartBounds: RectF = chartBounds - override val chartBounds: RectF = chartBounds + override var canvas: Canvas = canvas - override var canvas: Canvas = canvas + override val elevationOverlayColor: Long = elevationOverlayColor.toLong() - override val elevationOverlayColor: Long = elevationOverlayColor.toLong() + override val markerTouchPoint: Point? = markerTouchPoint - override val markerTouchPoint: Point? = markerTouchPoint + override val zoom: Float = zoom - override val zoom: Float = zoom + override val horizontalDimensions: HorizontalDimensions = horizontalDimensions.scaled(zoom) - override val horizontalDimensions: HorizontalDimensions = horizontalDimensions.scaled(zoom) + override val horizontalScroll: Float = horizontalScroll - override val horizontalScroll: Float = horizontalScroll - - override fun withOtherCanvas(canvas: Canvas, block: (DrawContext) -> Unit) { - val originalCanvas = this.canvas - this.canvas = canvas - block(this) - this.canvas = originalCanvas + override fun withOtherCanvas( + canvas: Canvas, + block: (DrawContext) -> Unit, + ) { + val originalCanvas = this.canvas + this.canvas = canvas + block(this) + this.canvas = originalCanvas + } } -} /** * Draws the provided [marker] on top of the chart at the given [markerTouchPoint] and notifies the @@ -125,5 +128,5 @@ public fun ChartDrawContext.drawMarker( } private fun List.xPosition(): Float? = firstOrNull()?.entry?.x -private fun List.hasMoved(other: List): Boolean = - xPosition() != other.xPosition() + +private fun List.hasMoved(other: List): Boolean = xPosition() != other.xPosition() diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/edges/FadingEdges.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/edges/FadingEdges.kt index 4427ce9bf..520cd6106 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/edges/FadingEdges.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/edges/FadingEdges.kt @@ -50,7 +50,6 @@ public open class FadingEdges( public var visibilityThresholdDp: Float = FADING_EDGE_VISIBILITY_THRESHOLD_DP, public var visibilityInterpolator: TimeInterpolator = AccelerateDecelerateInterpolator(), ) { - private val paint: Paint = Paint() private val rect: RectF = RectF() @@ -90,36 +89,37 @@ public open class FadingEdges( public fun applyFadingEdges( context: ChartDrawContext, bounds: RectF, - ): Unit = with(context) { - val maxScroll = getMaxScrollDistance() - var fadeAlphaFraction: Float - - if (isHorizontalScrollEnabled && startEdgeWidthDp > 0f && horizontalScroll > 0f) { - fadeAlphaFraction = (horizontalScroll / visibilityThresholdDp.pixels).coerceAtMost(1f) - - drawFadingEdge( - left = bounds.left, - top = bounds.top, - right = bounds.left + startEdgeWidthDp.pixels, - bottom = bounds.bottom, - direction = -1, - alpha = (visibilityInterpolator.getInterpolation(fadeAlphaFraction) * FULL_ALPHA).toInt(), - ) - } - - if (isHorizontalScrollEnabled && endEdgeWidthDp > 0f && horizontalScroll < maxScroll) { - fadeAlphaFraction = ((maxScroll - horizontalScroll) / visibilityThresholdDp.pixels).coerceAtMost(1f) - - drawFadingEdge( - left = bounds.right - endEdgeWidthDp.pixels, - top = bounds.top, - right = bounds.right, - bottom = bounds.bottom, - direction = 1, - alpha = (visibilityInterpolator.getInterpolation(fadeAlphaFraction) * FULL_ALPHA).toInt(), - ) + ): Unit = + with(context) { + val maxScroll = getMaxScrollDistance() + var fadeAlphaFraction: Float + + if (isHorizontalScrollEnabled && startEdgeWidthDp > 0f && horizontalScroll > 0f) { + fadeAlphaFraction = (horizontalScroll / visibilityThresholdDp.pixels).coerceAtMost(1f) + + drawFadingEdge( + left = bounds.left, + top = bounds.top, + right = bounds.left + startEdgeWidthDp.pixels, + bottom = bounds.bottom, + direction = -1, + alpha = (visibilityInterpolator.getInterpolation(fadeAlphaFraction) * FULL_ALPHA).toInt(), + ) + } + + if (isHorizontalScrollEnabled && endEdgeWidthDp > 0f && horizontalScroll < maxScroll) { + fadeAlphaFraction = ((maxScroll - horizontalScroll) / visibilityThresholdDp.pixels).coerceAtMost(1f) + + drawFadingEdge( + left = bounds.right - endEdgeWidthDp.pixels, + top = bounds.top, + right = bounds.right, + bottom = bounds.bottom, + direction = 1, + alpha = (visibilityInterpolator.getInterpolation(fadeAlphaFraction) * FULL_ALPHA).toInt(), + ) + } } - } private fun ChartDrawContext.drawFadingEdge( left: Float, @@ -133,15 +133,16 @@ public open class FadingEdges( val faded = FULL_FADE.copyColor(alpha = alpha) - paint.shader = LinearGradient( - rect.left, - 0f, - rect.right, - 0f, - if (direction < 0) faded else NO_FADE, - if (direction < 0) NO_FADE else faded, - Shader.TileMode.CLAMP, - ) + paint.shader = + LinearGradient( + rect.left, + 0f, + rect.right, + 0f, + if (direction < 0) faded else NO_FADE, + if (direction < 0) NO_FADE else faded, + Shader.TileMode.CLAMP, + ) canvas.drawRect(rect, paint) } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/insets/ChartInsetter.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/insets/ChartInsetter.kt index 07fc5fc37..aa1257077 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/insets/ChartInsetter.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/insets/ChartInsetter.kt @@ -27,7 +27,6 @@ import com.patrykandpatrick.vico.core.marker.Marker * like. */ public interface ChartInsetter { - /** * Called during the measurement phase, before [getHorizontalInsets]. Both horizontal and vertical insets can be * requested from this function. The final inset for a given edge of the associated [Chart] is the largest of the diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/insets/HorizontalInsets.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/insets/HorizontalInsets.kt index da1c25434..b1c0da128 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/insets/HorizontalInsets.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/insets/HorizontalInsets.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. @@ -25,15 +25,20 @@ import com.patrykandpatrick.vico.core.chart.Chart * @see Insets */ public interface HorizontalInsets { - /** * Sets the start and end insets. */ - public fun set(start: Float, end: Float) + public fun set( + start: Float, + end: Float, + ) /** * For the start and end insets, updates the value of the inset to the corresponding * provided value if the provided value is greater than the current value. */ - public fun setValuesIfGreater(start: Float, end: Float) + public fun setValuesIfGreater( + start: Float, + end: Float, + ) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/insets/Insets.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/insets/Insets.kt index 3a6a1941e..161e28c65 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/insets/Insets.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/insets/Insets.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. @@ -35,7 +35,6 @@ public class Insets( public var end: Float = 0f, public var bottom: Float = 0f, ) : HorizontalInsets { - /** * The sum of the sizes of the start inset and the end inset. */ @@ -67,17 +66,21 @@ public class Insets( top: Float = 0f, end: Float = 0f, bottom: Float = 0f, - ): Insets = apply { - this.start = start - this.top = top - this.end = end - this.bottom = bottom - } + ): Insets = + apply { + this.start = start + this.top = top + this.end = end + this.bottom = bottom + } /** * Updates the sizes of the start inset and the end inset. */ - override fun set(start: Float, end: Float) { + override fun set( + start: Float, + end: Float, + ) { this.start = start this.end = end } @@ -100,21 +103,26 @@ public class Insets( * Sets the sizes of the start inset and the end inset. [value] represents the sum of the two insets’ sizes, meaning * the size of either inset will be half of [value]. */ - public fun setHorizontal(value: Float): Insets = apply { - start = value.half - end = value.half - } + public fun setHorizontal(value: Float): Insets = + apply { + start = value.half + end = value.half + } /** * Sets the sizes of the top inset and the bottom inset. [value] represents the sum of the two insets’ sizes, * meaning the size of either inset will be half of [value]. */ - public fun setVertical(value: Float): Insets = apply { - top = value.half - bottom = value.half - } - - override fun setValuesIfGreater(start: Float, end: Float) { + public fun setVertical(value: Float): Insets = + apply { + top = value.half + bottom = value.half + } + + override fun setValuesIfGreater( + start: Float, + end: Float, + ) { this.start = max(start, this.start) this.end = max(end, this.end) } 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 f50922a81..7caa4613a 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 @@ -85,7 +85,6 @@ public open class LineChart( LineChartDrawingModel, > = DefaultDrawingModelInterpolator(), ) : BaseChart() { - /** * Creates a [LineChart] with a common style for all lines. * @@ -128,7 +127,6 @@ public open class LineChart( public var dataLabelRotationDegrees: Float = 0f, public var pointConnector: PointConnector = DefaultPointConnector(), ) { - /** * @param lineColor the color of the line. * @param lineThicknessDp the thickness of the line (in dp). @@ -174,10 +172,11 @@ public open class LineChart( public val hasLineBackgroundShader: Boolean get() = lineBackgroundShader != null - protected val linePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { - style = Paint.Style.STROKE - strokeCap = lineCap - } + protected val linePaint: Paint = + Paint(Paint.ANTI_ALIAS_FLAG).apply { + style = Paint.Style.STROKE + strokeCap = lineCap + } protected val lineBackgroundPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG) @@ -201,9 +200,10 @@ public open class LineChart( context: DrawContext, x: Float, y: Float, - ): Unit = with(context) { - point?.drawPoint(context, x, y, pointSizeDp.pixels.half) - } + ): Unit = + with(context) { + point?.drawPoint(context, x, y, pointSizeDp.pixels.half) + } /** * Draws the line. @@ -270,7 +270,6 @@ public open class LineChart( * @see DefaultPointConnector */ public interface PointConnector { - /** * Draws a line between two points. */ @@ -303,83 +302,90 @@ public open class LineChart( override fun drawChart( context: ChartDrawContext, model: ChartEntryModel, - ): Unit = with(context) { - resetTempData() - - val drawingModel = model.extraStore.getOrNull(drawingModelKey) - val chartValues = chartValuesProvider.getChartValues(targetVerticalAxisPosition) - val zeroLineYFraction = 1f - (drawingModel?.zeroY ?: abs(chartValues.minY / chartValues.lengthY)) - - model.entries.forEachIndexed { entryListIndex, entries -> - - val pointInfoMap = drawingModel?.getOrNull(entryListIndex) - - linePath.rewind() - lineBackgroundPath.rewind() - val component = lines.getRepeating(entryListIndex) - - var prevX = bounds.getStart(isLtr = isLtr) - var prevY = bounds.bottom - - val drawingStartAlignmentCorrection = layoutDirectionMultiplier * horizontalDimensions.startPadding - - val drawingStart = bounds.getStart(isLtr = isLtr) + drawingStartAlignmentCorrection - horizontalScroll - - forEachPointWithinBoundsIndexed( - entries = entries, - drawingStart = drawingStart, - pointInfoMap = pointInfoMap, - ) { entryIndex, entry, x, y, _, _ -> - if (linePath.isEmpty) { - linePath.moveTo(x, y) - } else { - component.pointConnector.connect( - path = linePath, - prevX = prevX, - prevY = prevY, - x = x, - y = y, - horizontalDimensions = horizontalDimensions, - bounds = bounds, - ) + ): Unit = + with(context) { + resetTempData() + + val drawingModel = model.extraStore.getOrNull(drawingModelKey) + val chartValues = chartValuesProvider.getChartValues(targetVerticalAxisPosition) + val zeroLineYFraction = 1f - (drawingModel?.zeroY ?: abs(chartValues.minY / chartValues.lengthY)) + + model.entries.forEachIndexed { entryListIndex, entries -> + + val pointInfoMap = drawingModel?.getOrNull(entryListIndex) + + linePath.rewind() + lineBackgroundPath.rewind() + val component = lines.getRepeating(entryListIndex) + + var prevX = bounds.getStart(isLtr = isLtr) + var prevY = bounds.bottom + + val drawingStartAlignmentCorrection = layoutDirectionMultiplier * horizontalDimensions.startPadding + + val drawingStart = bounds.getStart(isLtr = isLtr) + drawingStartAlignmentCorrection - horizontalScroll + + forEachPointWithinBoundsIndexed( + entries = entries, + drawingStart = drawingStart, + pointInfoMap = pointInfoMap, + ) { entryIndex, entry, x, y, _, _ -> + if (linePath.isEmpty) { + linePath.moveTo(x, y) + } else { + component.pointConnector.connect( + path = linePath, + prevX = prevX, + prevY = prevY, + x = x, + y = y, + horizontalDimensions = horizontalDimensions, + bounds = bounds, + ) + } + prevX = x + prevY = y + + if (x > bounds.left - 1 && x < bounds.right + 1) { + val coercedY = y.coerceIn(bounds.top, bounds.bottom) + entryLocationMap.put( + x = x, + y = coercedY, + entry = entry, + color = + component.lineShader.getColorAt( + Point(x, coercedY), + context, + bounds, + zeroLineYFraction, + ), + index = entryIndex, + ) + } } - prevX = x - prevY = y - - if (x > bounds.left - 1 && x < bounds.right + 1) { - val coercedY = y.coerceIn(bounds.top, bounds.bottom) - entryLocationMap.put( - x = x, - y = coercedY, - entry = entry, - color = component.lineShader.getColorAt(Point(x, coercedY), context, bounds, zeroLineYFraction), - index = entryIndex, + + if (component.hasLineBackgroundShader) { + lineBackgroundPath.addPath(linePath) + lineBackgroundPath.lineTo(prevX, bounds.bottom) + component.drawBackgroundLine( + context, + bounds, + zeroLineYFraction, + lineBackgroundPath, + drawingModel?.opacity ?: 1f, ) } - } - if (component.hasLineBackgroundShader) { - lineBackgroundPath.addPath(linePath) - lineBackgroundPath.lineTo(prevX, bounds.bottom) - component.drawBackgroundLine( - context, - bounds, - zeroLineYFraction, - lineBackgroundPath, - drawingModel?.opacity ?: 1f, + component.drawLine(context, bounds, zeroLineYFraction, linePath, drawingModel?.opacity ?: 1f) + + drawPointsAndDataLabels( + lineSpec = component, + entries = entries, + drawingStart = drawingStart, + pointInfoMap = pointInfoMap, ) } - - component.drawLine(context, bounds, zeroLineYFraction, linePath, drawingModel?.opacity ?: 1f) - - drawPointsAndDataLabels( - lineSpec = component, - entries = entries, - drawingStart = drawingStart, - pointInfoMap = pointInfoMap, - ) } - } /** * Draws a line’s points ([LineSpec.point]) and their corresponding data labels ([LineSpec.dataLabel]). @@ -408,32 +414,38 @@ public open class LineChart( chartEntry.x == chartValues.maxX && horizontalDimensions.endPadding > 0 }?.let { textComponent -> - val distanceFromLine = maxOf( - a = lineSpec.lineThicknessDp, - b = lineSpec.pointSizeDpOrZero, - ).half.pixels + val distanceFromLine = + maxOf( + a = lineSpec.lineThicknessDp, + b = lineSpec.pointSizeDpOrZero, + ).half.pixels - val text = lineSpec.dataLabelValueFormatter.formatValue( - value = chartEntry.y, - chartValues = chartValues, - ) + val text = + lineSpec.dataLabelValueFormatter.formatValue( + value = chartEntry.y, + chartValues = chartValues, + ) val maxWidth = getMaxDataLabelWidth(chartEntry, x, previousX, nextX) - val verticalPosition = lineSpec.dataLabelVerticalPosition.inBounds( - bounds = bounds, - distanceFromPoint = distanceFromLine, - componentHeight = textComponent.getHeight( - context = this, - text = text, - width = maxWidth, - rotationDegrees = lineSpec.dataLabelRotationDegrees, - ), - y = y, - ) - val dataLabelY = y + when (verticalPosition) { - VerticalPosition.Top -> -distanceFromLine - VerticalPosition.Center -> 0f - VerticalPosition.Bottom -> distanceFromLine - } + val verticalPosition = + lineSpec.dataLabelVerticalPosition.inBounds( + bounds = bounds, + distanceFromPoint = distanceFromLine, + componentHeight = + textComponent.getHeight( + context = this, + text = text, + width = maxWidth, + rotationDegrees = lineSpec.dataLabelRotationDegrees, + ), + y = y, + ) + val dataLabelY = + y + + when (verticalPosition) { + VerticalPosition.Top -> -distanceFromLine + VerticalPosition.Center -> 0f + VerticalPosition.Bottom -> distanceFromLine + } textComponent.drawText( context = this, textX = x, @@ -461,20 +473,22 @@ public open class LineChart( min(horizontalDimensions.startPadding, horizontalDimensions.endPadding).doubled nextX != null -> { - val extraSpace = when (horizontalLayout) { - is HorizontalLayout.Segmented -> horizontalDimensions.xSpacing.half - is HorizontalLayout.FullWidth -> horizontalDimensions.startPadding - } + val extraSpace = + when (horizontalLayout) { + is HorizontalLayout.Segmented -> horizontalDimensions.xSpacing.half + is HorizontalLayout.FullWidth -> horizontalDimensions.startPadding + } ((entry.x - chartValues.minX) / chartValues.xStep * horizontalDimensions.xSpacing + extraSpace) .doubled .coerceAtMost(nextX - x) } else -> { - val extraSpace = when (horizontalLayout) { - is HorizontalLayout.Segmented -> horizontalDimensions.xSpacing.half - is HorizontalLayout.FullWidth -> horizontalDimensions.endPadding - } + val extraSpace = + when (horizontalLayout) { + is HorizontalLayout.Segmented -> horizontalDimensions.xSpacing.half + is HorizontalLayout.FullWidth -> horizontalDimensions.endPadding + } ((chartValues.maxX - entry.x) / chartValues.xStep * horizontalDimensions.xSpacing + extraSpace) .doubled .coerceAtMost(x - previousX!!) @@ -516,8 +530,9 @@ public open class LineChart( val boundsStart = bounds.getStart(isLtr = isLtr) val boundsEnd = boundsStart + layoutDirectionMultiplier * bounds.width() - fun getDrawX(entry: ChartEntry): Float = drawingStart + layoutDirectionMultiplier * - horizontalDimensions.xSpacing * (entry.x - minX) / xStep + fun getDrawX(entry: ChartEntry): Float = + drawingStart + layoutDirectionMultiplier * + horizontalDimensions.xSpacing * (entry.x - minX) / xStep fun getDrawY(entry: ChartEntry): Float = bounds.bottom - (pointInfoMap?.get(entry.x)?.y ?: ((entry.y - chartValues.minY) / chartValues.lengthY)) * @@ -580,7 +595,11 @@ public open class LineChart( } } - override fun updateChartValues(chartValuesManager: ChartValuesManager, model: ChartEntryModel, xStep: Float?) { + override fun updateChartValues( + chartValuesManager: ChartValuesManager, + model: ChartEntryModel, + xStep: Float?, + ) { chartValuesManager.tryUpdate( minX = axisValuesOverrider?.getMinX(model) ?: model.minX, maxX = axisValuesOverrider?.getMaxX(model) ?: model.maxX, @@ -596,20 +615,23 @@ public open class LineChart( context: MeasureContext, outInsets: Insets, horizontalDimensions: HorizontalDimensions, - ): Unit = with(context) { - outInsets.setVertical( - value = lines.maxOf { - if (it.point != null) max(a = it.lineThicknessDp, b = it.pointSizeDp) else it.lineThicknessDp - }.pixels, - ) - } + ): Unit = + with(context) { + outInsets.setVertical( + value = + lines.maxOf { + if (it.point != null) max(a = it.lineThicknessDp, b = it.pointSizeDp) else it.lineThicknessDp + }.pixels, + ) + } - override val modelTransformerProvider: Chart.ModelTransformerProvider = object : Chart.ModelTransformerProvider { - private val modelTransformer = - LineChartModelTransformer(drawingModelKey, { targetVerticalAxisPosition }, { drawingModelInterpolator }) + override val modelTransformerProvider: Chart.ModelTransformerProvider = + object : Chart.ModelTransformerProvider { + private val modelTransformer = + LineChartModelTransformer(drawingModelKey, { targetVerticalAxisPosition }, { drawingModelInterpolator }) - override fun getModelTransformer(): Chart.ModelTransformer = modelTransformer - } + override fun getModelTransformer(): Chart.ModelTransformer = modelTransformer + } protected class LineChartModelTransformer( override val key: ExtraStore.Key, @@ -619,7 +641,6 @@ public open class LineChart( LineChartDrawingModel, >, ) : Chart.ModelTransformer() { - override fun prepareForTransformation( oldModel: ChartEntryModel?, newModel: ChartEntryModel?, @@ -632,21 +653,25 @@ public open class LineChart( ) } - override suspend fun transform(extraStore: MutableExtraStore, fraction: Float) { + override suspend fun transform( + extraStore: MutableExtraStore, + fraction: Float, + ) { getDrawingModelInterpolator() .transform(fraction) ?.let { extraStore[key] = it } ?: extraStore.remove(key) } - private fun ChartEntryModel.toDrawingModel(chartValues: ChartValues): LineChartDrawingModel = entries - .map { series -> - series.associate { entry -> - entry.x to LineChartDrawingModel.PointInfo((entry.y - chartValues.minY) / chartValues.lengthY) + private fun ChartEntryModel.toDrawingModel(chartValues: ChartValues): LineChartDrawingModel = + entries + .map { series -> + series.associate { entry -> + entry.x to LineChartDrawingModel.PointInfo((entry.y - chartValues.minY) / chartValues.lengthY) + } + } + .let { pointInfo -> + LineChartDrawingModel(pointInfo, abs(chartValues.minY / chartValues.lengthY)) } - } - .let { pointInfo -> - LineChartDrawingModel(pointInfo, abs(chartValues.minY / chartValues.lengthY)) - } } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/line/LineChartDrawingModel.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/line/LineChartDrawingModel.kt index bfcbf2572..906784cd0 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/line/LineChartDrawingModel.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/line/LineChartDrawingModel.kt @@ -30,7 +30,6 @@ public class LineChartDrawingModel( public val opacity: Float = 1f, ) : DrawingModel(pointInfo) { - override fun transform( drawingInfo: List>, from: DrawingModel?, @@ -46,7 +45,10 @@ public class LineChartDrawingModel( * of the [LineChart] as a fraction of the [LineChart]’s height. */ public class PointInfo(public val y: Float) : DrawingInfo { - override fun transform(from: DrawingInfo?, fraction: Float): DrawingInfo { + override fun transform( + from: DrawingInfo?, + fraction: Float, + ): DrawingInfo { val oldY = (from as? PointInfo)?.y.orZero return PointInfo(oldY.lerp(y, fraction)) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/scale/AutoScaleUp.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/scale/AutoScaleUp.kt index ee4c8c670..b9364085e 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/scale/AutoScaleUp.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/scale/AutoScaleUp.kt @@ -21,7 +21,6 @@ package com.patrykandpatrick.vico.core.chart.scale * an empty space would be visible near the end edge of the chart. */ public enum class AutoScaleUp { - /** * Scales up the chart to prevent any empty space from being visible. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/AxisValuesOverrider.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/AxisValuesOverrider.kt index 2f78911ef..fc7cd8b7c 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/AxisValuesOverrider.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/AxisValuesOverrider.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. @@ -26,7 +26,6 @@ import kotlin.math.abs * [com.patrykandpatrick.vico.compose.chart.Chart] composable. */ public interface AxisValuesOverrider { - /** * The minimum value shown on the x-axis. If `null` is returned, the chart will fall back to the default value. * @@ -56,7 +55,6 @@ public interface AxisValuesOverrider { public fun getMaxY(model: Model): Float? = null public companion object { - /** * Creates an [AxisValuesOverrider] with fixed values for [minX], [maxX], [minY], and [maxY]. If one of the * values is `null`, the chart will fall back to the default value. @@ -66,16 +64,16 @@ public interface AxisValuesOverrider { maxX: Float? = null, minY: Float? = null, maxY: Float? = null, - ): AxisValuesOverrider = object : AxisValuesOverrider { + ): AxisValuesOverrider = + object : AxisValuesOverrider { + override fun getMinX(model: ChartEntryModel): Float? = minX - override fun getMinX(model: ChartEntryModel): Float? = minX + override fun getMaxX(model: ChartEntryModel): Float? = maxX - override fun getMaxX(model: ChartEntryModel): Float? = maxX + override fun getMinY(model: ChartEntryModel): Float? = minY - override fun getMinY(model: ChartEntryModel): Float? = minY - - override fun getMaxY(model: ChartEntryModel): Float? = maxY - } + override fun getMaxY(model: ChartEntryModel): Float? = maxY + } /** * Creates an [AxisValuesOverrider] with adaptive minimum and maximum y-axis values. The overridden maximum @@ -85,20 +83,20 @@ public interface AxisValuesOverrider { public fun adaptiveYValues( yFraction: Float, round: Boolean = false, - ): AxisValuesOverrider = object : AxisValuesOverrider { + ): AxisValuesOverrider = + object : AxisValuesOverrider { + init { + require(yFraction > 0f) + } - init { - require(yFraction > 0f) - } - - override fun getMinY(model: ChartEntryModel): Float { - val difference = abs(getMaxY(model) - model.maxY) - return (model.minY - difference).maybeRound().coerceAtLeast(0f) - } + override fun getMinY(model: ChartEntryModel): Float { + val difference = abs(getMaxY(model) - model.maxY) + return (model.minY - difference).maybeRound().coerceAtLeast(0f) + } - override fun getMaxY(model: ChartEntryModel): Float = (model.maxY * yFraction).maybeRound() + override fun getMaxY(model: ChartEntryModel): Float = (model.maxY * yFraction).maybeRound() - private fun Float.maybeRound() = if (round) this.round else this - } + private fun Float.maybeRound() = if (round) this.round else this + } } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/ChartValues.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/ChartValues.kt index 4480f9c19..28ee96f87 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/ChartValues.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/ChartValues.kt @@ -29,7 +29,6 @@ import kotlin.math.ceil * but you can use [AxisValuesOverrider] to override these values. */ public interface ChartValues { - /** * The minimum value displayed on the x-axis. By default, this is equal to [ChartEntryModel.minX] (the * [ChartEntryModel] instance being [chartEntryModel]), but you can use [AxisValuesOverrider] to override this @@ -84,6 +83,5 @@ public interface ChartValues { /** * Returns the maximum number of major entries that can be present, based on [minX], [maxX], and [xStep]. */ - public fun getMaxMajorEntryCount(): Int = - ceil(abs(maxX - minX) / xStep + 1).toInt() + public fun getMaxMajorEntryCount(): Int = ceil(abs(maxX - minX) / xStep + 1).toInt() } 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 044e5df3a..3f1907f38 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 @@ -37,7 +37,6 @@ import com.patrykandpatrick.vico.core.entry.ChartEntryModel * @see LineChart.targetVerticalAxisPosition */ public class ChartValuesManager : ChartValuesProvider { - internal val chartValues: MutableMap = mutableMapOf() override fun getChartValues(axisPosition: AxisPosition.Vertical?): ChartValues = @@ -96,12 +95,14 @@ public class ChartValuesManager : ChartValuesProvider { * 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() +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) -} + 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 9f5204e29..85f4a34c4 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 @@ -22,7 +22,6 @@ import com.patrykandpatrick.vico.core.axis.AxisPosition * Provides a chart’s [ChartValues] instances. */ public interface ChartValuesProvider { - /** * Returns the [ChartValues] instance associated with the specified [AxisPosition.Vertical] subclass. If * [axisPosition] is `null`, the chart’s main [ChartValues] instance is returned. 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 9245d2470..916d78147 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 @@ -24,7 +24,6 @@ import com.patrykandpatrick.vico.core.extension.orZero * An implementation of [ChartValues] whose every property is mutable. */ public class MutableChartValues : ChartValues { - private var _minX: Float? = null private var _maxX: Float? = null @@ -71,14 +70,15 @@ public class MutableChartValues : ChartValues { maxY: Float? = null, xStep: Float? = null, chartEntryModel: ChartEntryModel = this.chartEntryModel, - ): MutableChartValues = apply { - if (minX != null) _minX = if (_minX != null) minOf(this.minX, minX) else minX - if (maxX != null) _maxX = if (_maxX != null) maxOf(this.maxX, maxX) else maxX - if (minY != null) _minY = if (_minY != null) minOf(this.minY, minY) else minY - if (maxY != null) _maxY = if (_maxY != null) maxOf(this.maxY, maxY) else maxY - if (xStep != null) _xStep = xStep - this.chartEntryModel = chartEntryModel - } + ): MutableChartValues = + apply { + if (minX != null) _minX = if (_minX != null) minOf(this.minX, minX) else minX + if (maxX != null) _maxX = if (_maxX != null) maxOf(this.maxX, maxX) else maxX + if (minY != null) _minY = if (_minY != null) minOf(this.minY, minY) else minY + if (maxY != null) _maxY = if (_maxY != null) maxOf(this.maxY, maxY) else maxY + if (xStep != null) _xStep = xStep + this.chartEntryModel = chartEntryModel + } /** * Sets [minX], [maxX], [minY], and [maxY] to 0. @@ -93,28 +93,29 @@ public class MutableChartValues : ChartValues { } private companion object { - - fun emptyChartEntryModel(): ChartEntryModel = object : ChartEntryModel { - override val entries: List> = emptyList() - override val minX: Float = 0f - override val maxX: Float = 0f - override val minY: Float = 0f - override val maxY: Float = 0f - override val stackedPositiveY: Float = 0f - override val stackedNegativeY: Float = 0f - override val xGcd: Float = 1f - } + fun emptyChartEntryModel(): ChartEntryModel = + object : ChartEntryModel { + override val entries: List> = emptyList() + override val minX: Float = 0f + override val maxX: Float = 0f + override val minY: Float = 0f + override val maxY: Float = 0f + override val stackedPositiveY: Float = 0f + override val stackedNegativeY: Float = 0f + override val xGcd: Float = 1f + } } } /** * 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() -} +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/collections/ArrayDelegates.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/collections/ArrayDelegates.kt index 1cfc3470b..aed675ea7 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/collections/ArrayDelegates.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/collections/ArrayDelegates.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. @@ -25,9 +25,16 @@ internal fun ?> cacheInList(): ReadWriteProperty { var field: T? = null - override fun getValue(thisRef: AxisManager, property: KProperty<*>): T? = field + override fun getValue( + thisRef: AxisManager, + property: KProperty<*>, + ): T? = field - override fun setValue(thisRef: AxisManager, property: KProperty<*>, value: T?) { + override fun setValue( + thisRef: AxisManager, + property: KProperty<*>, + value: T?, + ) { if (field == value) return field?.let(thisRef.axisCache::remove) field = value diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/Component.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/Component.kt index 68b73ea55..217bbc573 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/Component.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/Component.kt @@ -25,7 +25,6 @@ import com.patrykandpatrick.vico.core.context.DrawContext * Its subclasses are used throughout the library. */ public abstract class Component : Margins by DefaultMargins() { - /** * Instructs the [Component] to draw itself at the given coordinates. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/OverlayingComponent.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/OverlayingComponent.kt index e6136980b..03c326bc7 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/OverlayingComponent.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/OverlayingComponent.kt @@ -36,7 +36,6 @@ public class OverlayingComponent( public val innerPaddingEndDp: Float = 0f, public val innerPaddingBottomDp: Float = 0f, ) : Component() { - public constructor( outer: Component, inner: Component, @@ -66,21 +65,22 @@ public class OverlayingComponent( right: Float, bottom: Float, opacity: Float, - ): Unit = with(context) { - val leftWithMargin = left + margins.startDp.pixels - val topWithMargin = top + margins.topDp.pixels - val rightWithMargin = right - margins.endDp.pixels - val bottomWithMargin = bottom - margins.bottomDp.pixels + ): Unit = + with(context) { + val leftWithMargin = left + margins.startDp.pixels + val topWithMargin = top + margins.topDp.pixels + val rightWithMargin = right - margins.endDp.pixels + val bottomWithMargin = bottom - margins.bottomDp.pixels - outer.draw(context, leftWithMargin, topWithMargin, rightWithMargin, bottomWithMargin, opacity) - inner.draw(context, leftWithMargin, topWithMargin, rightWithMargin, bottomWithMargin, opacity) + outer.draw(context, leftWithMargin, topWithMargin, rightWithMargin, bottomWithMargin, opacity) + inner.draw(context, leftWithMargin, topWithMargin, rightWithMargin, bottomWithMargin, opacity) - DebugHelper.drawDebugBounds( - context = context, - left = left, - top = top, - right = right, - bottom = bottom, - ) - } + DebugHelper.drawDebugBounds( + context = context, + left = left, + top = top, + right = right, + bottom = bottom, + ) + } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/dimension/Margins.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/dimension/Margins.kt index 5bb05f41b..ee655621a 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/dimension/Margins.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/dimension/Margins.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. @@ -23,7 +23,6 @@ import com.patrykandpatrick.vico.core.dimensions.MutableDimensions * Allows a component to implement margins. */ public interface Margins { - /** * The current margins. */ @@ -44,9 +43,7 @@ public interface Margins { /** * Sets a common size for each margin. */ - public fun setMargins( - all: Float = 0f, - ) { + public fun setMargins(all: Float = 0f) { margins.set(all) } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/dimension/Padding.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/dimension/Padding.kt index fb22a2345..aac9f76d4 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/dimension/Padding.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/dimension/Padding.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. @@ -22,7 +22,6 @@ import com.patrykandpatrick.vico.core.dimensions.MutableDimensions * Allows a component to implement padding. */ public interface Padding { - /** * The current padding. */ @@ -53,9 +52,7 @@ public interface Padding { /** * Sets a common padding value for each side. */ - public fun setPadding( - all: Float = 0f, - ) { + public fun setPadding(all: Float = 0f) { padding.set(all) } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/marker/MarkerComponent.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/marker/MarkerComponent.kt index d4d54f061..40caf5bab 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/marker/MarkerComponent.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/marker/MarkerComponent.kt @@ -50,7 +50,6 @@ public open class MarkerComponent( public val indicator: Component?, public val guideline: LineComponent?, ) : Marker { - private val tempBounds = RectF() private val TextComponent.tickSizeDp: Float @@ -77,56 +76,64 @@ public open class MarkerComponent( bounds: RectF, markedEntries: List, chartValuesProvider: ChartValuesProvider, - ): Unit = with(context) { - drawGuideline(context, bounds, markedEntries) - val halfIndicatorSize = indicatorSizeDp.half.pixels + ): Unit = + with(context) { + drawGuideline(context, bounds, markedEntries) + val halfIndicatorSize = indicatorSizeDp.half.pixels - markedEntries.forEachIndexed { _, model -> - onApplyEntryColor?.invoke(model.color) - indicator?.draw( - context, - model.location.x - halfIndicatorSize, - model.location.y - halfIndicatorSize, - model.location.x + halfIndicatorSize, - model.location.y + halfIndicatorSize, - ) + markedEntries.forEachIndexed { _, model -> + onApplyEntryColor?.invoke(model.color) + indicator?.draw( + context, + model.location.x - halfIndicatorSize, + model.location.y - halfIndicatorSize, + model.location.x + halfIndicatorSize, + model.location.y + halfIndicatorSize, + ) + } + drawLabel(context, bounds, markedEntries, chartValuesProvider.getChartValues()) } - drawLabel(context, bounds, markedEntries, chartValuesProvider.getChartValues()) - } private fun drawLabel( context: DrawContext, bounds: RectF, markedEntries: List, chartValues: ChartValues, - ): Unit = with(context) { - val text = labelFormatter.getLabel(markedEntries, chartValues) - val entryX = markedEntries.averageOf { it.location.x } - val labelBounds = - label.getTextBounds(context = context, text = text, width = bounds.width().toInt(), outRect = tempBounds) - val halfOfTextWidth = labelBounds.width().half - val x = overrideXPositionToFit(entryX, bounds, halfOfTextWidth) - this[MarkerCorneredShape.tickXKey] = entryX + ): Unit = + with(context) { + val text = labelFormatter.getLabel(markedEntries, chartValues) + val entryX = markedEntries.averageOf { it.location.x } + val labelBounds = + label.getTextBounds( + context = context, + text = text, + width = bounds.width().toInt(), + outRect = tempBounds, + ) + val halfOfTextWidth = labelBounds.width().half + val x = overrideXPositionToFit(entryX, bounds, halfOfTextWidth) + this[MarkerCorneredShape.TICK_X_KEY] = entryX - label.drawText( - context = context, - text = text, - textX = x, - textY = bounds.top - labelBounds.height() - label.tickSizeDp.pixels, - verticalPosition = VerticalPosition.Bottom, - maxTextWidth = minOf(bounds.right - x, x - bounds.left).doubled.ceil.toInt(), - ) - } + label.drawText( + context = context, + text = text, + textX = x, + textY = bounds.top - labelBounds.height() - label.tickSizeDp.pixels, + verticalPosition = VerticalPosition.Bottom, + maxTextWidth = minOf(bounds.right - x, x - bounds.left).doubled.ceil.toInt(), + ) + } private fun overrideXPositionToFit( xPosition: Float, bounds: RectF, halfOfTextWidth: Float, - ): Float = when { - xPosition - halfOfTextWidth < bounds.left -> bounds.left + halfOfTextWidth - xPosition + halfOfTextWidth > bounds.right -> bounds.right - halfOfTextWidth - else -> xPosition - } + ): Float = + when { + xPosition - halfOfTextWidth < bounds.left -> bounds.left + halfOfTextWidth + xPosition + halfOfTextWidth > bounds.right -> bounds.right - halfOfTextWidth + else -> xPosition + } private fun drawGuideline( context: DrawContext, @@ -150,7 +157,8 @@ public open class MarkerComponent( context: MeasureContext, outInsets: Insets, horizontalDimensions: HorizontalDimensions, - ): Unit = with(context) { - outInsets.top = label.getHeight(context) + label.tickSizeDp.pixels - } + ): Unit = + with(context) { + outInsets.top = label.getHeight(context) + label.tickSizeDp.pixels + } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/DashedShape.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/DashedShape.kt index 9a04b76e9..1ed807286 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/DashedShape.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/DashedShape.kt @@ -36,7 +36,6 @@ public class DashedShape( public val gapLengthDp: Float = DefaultDimens.DASH_GAP, public val fitStrategy: FitStrategy = FitStrategy.Resize, ) : Shape { - private var drawDashLength = dashLengthDp private var drawGapLength = gapLengthDp @@ -70,21 +69,22 @@ public class DashedShape( var index = 0 var drawnLength = 0f while (right - left - drawnLength > 0) { - drawnLength += if (index % 2 == 0) { - path.reset() - shape.drawShape( - context = context, - paint = paint, - path = path, - left = left + drawnLength, - top = top, - right = left + drawnLength + drawDashLength, - bottom = bottom, - ) - drawDashLength - } else { - drawGapLength - } + drawnLength += + if (index % 2 == 0) { + path.reset() + shape.drawShape( + context = context, + paint = paint, + path = path, + left = left + drawnLength, + top = top, + right = left + drawnLength + drawDashLength, + bottom = bottom, + ) + drawDashLength + } else { + drawGapLength + } index++ } } @@ -103,28 +103,33 @@ public class DashedShape( var index = 0 var drawnLength = 0f while (bottom - top - drawnLength > 0) { - drawnLength += if (index % 2 == 0) { - path.reset() - shape.drawShape( - context = context, - paint = paint, - path = path, - left = left, - top = top + drawnLength, - right = right, - bottom = top + drawnLength + drawDashLength, - ) - drawDashLength - } else { - drawGapLength - } + drawnLength += + if (index % 2 == 0) { + path.reset() + shape.drawShape( + context = context, + paint = paint, + path = path, + left = left, + top = top + drawnLength, + right = right, + bottom = top + drawnLength + drawDashLength, + ) + drawDashLength + } else { + drawGapLength + } index++ } } - private fun calculateDrawLengths(context: DrawContext, length: Float): Unit = with(context) { - calculateDrawLengths(dashLengthDp.pixels, gapLengthDp.pixels, length) - } + private fun calculateDrawLengths( + context: DrawContext, + length: Float, + ): Unit = + with(context) { + calculateDrawLengths(dashLengthDp.pixels, gapLengthDp.pixels, length) + } private fun calculateDrawLengths( dashLength: Float, @@ -136,18 +141,19 @@ public class DashedShape( return } when (fitStrategy) { - FitStrategy.Resize -> when { - length < dashLength + gapLength -> { - drawDashLength = length - drawGapLength = 0f - } - else -> { - val gapAndDashLength = gapLength + dashLength - val ratio = length / (dashLength + (length / gapAndDashLength).ceil * gapAndDashLength) - drawDashLength = dashLength * ratio - drawGapLength = gapLength * ratio + FitStrategy.Resize -> + when { + length < dashLength + gapLength -> { + drawDashLength = length + drawGapLength = 0f + } + else -> { + val gapAndDashLength = gapLength + dashLength + val ratio = length / (dashLength + (length / gapAndDashLength).ceil * gapAndDashLength) + drawDashLength = dashLength * ratio + drawGapLength = gapLength * ratio + } } - } FitStrategy.Fixed -> { drawDashLength = dashLength drawGapLength = gapLength diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/LineComponent.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/LineComponent.kt index de7f77740..6e11335d7 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/LineComponent.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/LineComponent.kt @@ -44,7 +44,6 @@ public open class LineComponent( strokeWidthDp: Float = 0f, strokeColor: Int = Color.TRANSPARENT, ) : ShapeComponent(shape, color, dynamicShader, margins, strokeWidthDp, strokeColor) { - private val MeasureContext.thickness: Float get() = thicknessDp.pixels @@ -58,16 +57,17 @@ public open class LineComponent( centerY: Float, thicknessScale: Float = 1f, opacity: Float = 1f, - ): Unit = with(context) { - draw( - context, - left = left, - top = centerY - thickness * thicknessScale / 2, - right = right, - bottom = centerY + thickness * thicknessScale / 2, - opacity = opacity, - ) - } + ): Unit = + with(context) { + draw( + context, + left = left, + top = centerY - thickness * thicknessScale / 2, + right = right, + bottom = centerY + thickness * thicknessScale / 2, + opacity = opacity, + ) + } /** * Checks whether the [LineComponent] fits horizontally within the given [boundingBox] with its current @@ -80,14 +80,15 @@ public open class LineComponent( centerY: Float, boundingBox: RectF, thicknessScale: Float = 1f, - ): Boolean = with(context) { - boundingBox.contains( - left, - centerY - thickness * thicknessScale / 2, - right, - centerY + thickness * thicknessScale / 2, - ) - } + ): Boolean = + with(context) { + boundingBox.contains( + left, + centerY - thickness * thicknessScale / 2, + right, + centerY + thickness * thicknessScale / 2, + ) + } /** * A convenience function for [draw] that draws the [LineComponent] vertically. @@ -99,16 +100,17 @@ public open class LineComponent( centerX: Float, thicknessScale: Float = 1f, opacity: Float = 1f, - ): Unit = with(context) { - draw( - context, - left = centerX - thickness * thicknessScale / 2, - top = top, - right = centerX + thickness * thicknessScale / 2, - bottom = bottom, - opacity = opacity, - ) - } + ): Unit = + with(context) { + draw( + context, + left = centerX - thickness * thicknessScale / 2, + top = top, + right = centerX + thickness * thicknessScale / 2, + bottom = bottom, + opacity = opacity, + ) + } /** * Checks whether the [LineComponent] fits vertically within the given [boundingBox] with its current [thicknessDp]. @@ -120,14 +122,15 @@ public open class LineComponent( centerX: Float, boundingBox: RectF, thicknessScale: Float = 1f, - ): Boolean = with(context) { - boundingBox.contains( - centerX - thickness * thicknessScale / 2, - top, - centerX + thickness * thicknessScale / 2, - bottom, - ) - } + ): Boolean = + with(context) { + boundingBox.contains( + centerX - thickness * thicknessScale / 2, + top, + centerX + thickness * thicknessScale / 2, + bottom, + ) + } /** * Checks whether the [LineComponent] vertically intersects the given [boundingBox] with its current [thicknessDp]. @@ -139,12 +142,13 @@ public open class LineComponent( centerX: Float, boundingBox: RectF, thicknessScale: Float = 1f, - ): Boolean = with(context) { - boundingBox.intersects( - centerX - thickness * thicknessScale / 2, - top, - centerX + thickness * thicknessScale / 2, - bottom, - ) - } + ): Boolean = + with(context) { + boundingBox.intersects( + centerX - thickness * thicknessScale / 2, + top, + centerX + thickness * thicknessScale / 2, + bottom, + ) + } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/Shape.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/Shape.kt index 8a119ad65..8765010dc 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/Shape.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/Shape.kt @@ -24,7 +24,6 @@ import com.patrykandpatrick.vico.core.context.DrawContext * Defines a shape that can be drawn on a canvas. */ public interface Shape { - /** * Draws the [Shape] on the canvas. * diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/ShapeComponent.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/ShapeComponent.kt index 653cd3458..ff1769f86 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/ShapeComponent.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/ShapeComponent.kt @@ -52,7 +52,6 @@ public open class ShapeComponent( public val strokeWidthDp: Float = 0f, strokeColor: Int = Color.TRANSPARENT, ) : Component() { - private val paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG) private val strokePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG) private val shadowProperties: ComponentShadow = ComponentShadow() @@ -87,41 +86,42 @@ public open class ShapeComponent( right: Float, bottom: Float, opacity: Float, - ): Unit = with(context) { - if (left == right || top == bottom) return // Skip drawing shape that will be invisible. - path.rewind() - applyShader(context, left, top, right, bottom) - val centerX = (left + right).half - val centerY = (top + bottom).half - shadowProperties.maybeUpdateShadowLayer(context = this, paint = paint, backgroundColor = color) - - val strokeWidth = strokeWidthDp.pixels - strokePaint.strokeWidth = strokeWidth - - fun drawShape(paint: Paint) { - shape.drawShape( + ): Unit = + with(context) { + if (left == right || top == bottom) return // Skip drawing shape that will be invisible. + path.rewind() + applyShader(context, left, top, right, bottom) + val centerX = (left + right).half + val centerY = (top + bottom).half + shadowProperties.maybeUpdateShadowLayer(context = this, paint = paint, backgroundColor = color) + + val strokeWidth = strokeWidthDp.pixels + strokePaint.strokeWidth = strokeWidth + + fun drawShape(paint: Paint) { + shape.drawShape( + context = context, + paint = paint, + path = path, + left = minOf(left + margins.startDp.pixels + strokeWidth.half, centerX), + top = minOf(top + margins.topDp.pixels + strokeWidth.half, centerY), + right = maxOf(right - margins.endDp.pixels - strokeWidth.half, centerX), + bottom = maxOf(bottom - margins.bottomDp.pixels - strokeWidth.half, centerY), + ) + } + + paint.withOpacity(opacity, ::drawShape) + if (strokeWidth > 0f && strokeColor.alpha > 0) strokePaint.withOpacity(opacity, ::drawShape) + + DebugHelper.drawDebugBounds( context = context, - paint = paint, - path = path, - left = minOf(left + margins.startDp.pixels + strokeWidth.half, centerX), - top = minOf(top + margins.topDp.pixels + strokeWidth.half, centerY), - right = maxOf(right - margins.endDp.pixels - strokeWidth.half, centerX), - bottom = maxOf(bottom - margins.bottomDp.pixels - strokeWidth.half, centerY), + left = left, + top = top, + right = right, + bottom = bottom, ) } - paint.withOpacity(opacity, ::drawShape) - if (strokeWidth > 0f && strokeColor.alpha > 0) strokePaint.withOpacity(opacity, ::drawShape) - - DebugHelper.drawDebugBounds( - context = context, - left = left, - top = top, - right = right, - bottom = bottom, - ) - } - protected fun applyShader( context: DrawContext, left: Float, @@ -149,25 +149,27 @@ public open class ShapeComponent( dy: Float = 0f, color: Int = DEF_SHADOW_COLOR, applyElevationOverlay: Boolean = false, - ): ShapeComponent = apply { - shadowProperties.apply { - this.radius = radius - this.dx = dx - this.dy = dy - this.color = color - this.applyElevationOverlay = applyElevationOverlay + ): ShapeComponent = + apply { + shadowProperties.apply { + this.radius = radius + this.dx = dx + this.dy = dy + this.color = color + this.applyElevationOverlay = applyElevationOverlay + } } - } /** * Removes this [ShapeComponent]’s drop shadow. */ - public fun clearShadow(): ShapeComponent = apply { - shadowProperties.apply { - this.radius = 0f - this.dx = 0f - this.dy = 0f - this.color = 0 + public fun clearShadow(): ShapeComponent = + apply { + shadowProperties.apply { + this.radius = 0f + this.dx = 0f + this.dy = 0f + this.color = 0 + } } - } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/Shapes.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/Shapes.kt index fbb1b4f60..cdc8efe28 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/Shapes.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/Shapes.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. @@ -33,7 +33,6 @@ import com.patrykandpatrick.vico.core.extension.setBounds * Houses utilities for creating [Shape]s. */ public object Shapes { - /** * A shape whose each corner is fully rounded. */ @@ -42,25 +41,25 @@ public object Shapes { /** * A rectangle with sharp corners. */ - public val rectShape: Shape = object : Shape { - - override fun drawShape( - context: DrawContext, - paint: Paint, - path: Path, - left: Float, - top: Float, - right: Float, - bottom: Float, - ) { - path.moveTo(left, top) - path.lineTo(right, top) - path.lineTo(right, bottom) - path.lineTo(left, bottom) - path.close() - context.canvas.drawPath(path, paint) + public val rectShape: Shape = + object : Shape { + override fun drawShape( + context: DrawContext, + paint: Paint, + path: Path, + left: Float, + top: Float, + right: Float, + bottom: Float, + ) { + path.moveTo(left, top) + path.lineTo(right, top) + path.lineTo(right, bottom) + path.lineTo(left, bottom) + path.close() + context.canvas.drawPath(path, paint) + } } - } /** * Creates a [Shape] with all corners rounded. @@ -83,12 +82,13 @@ public object Shapes { topRightPercent: Int = 0, bottomRightPercent: Int = 0, bottomLeftPercent: Int = 0, - ): CorneredShape = CorneredShape( - Corner.Relative(topLeftPercent, RoundedCornerTreatment), - Corner.Relative(topRightPercent, RoundedCornerTreatment), - Corner.Relative(bottomRightPercent, RoundedCornerTreatment), - Corner.Relative(bottomLeftPercent, RoundedCornerTreatment), - ) + ): CorneredShape = + CorneredShape( + Corner.Relative(topLeftPercent, RoundedCornerTreatment), + Corner.Relative(topRightPercent, RoundedCornerTreatment), + Corner.Relative(bottomRightPercent, RoundedCornerTreatment), + Corner.Relative(bottomLeftPercent, RoundedCornerTreatment), + ) /** * Creates a [Shape] with all corners cut. @@ -111,12 +111,13 @@ public object Shapes { topRightPercent: Int = 0, bottomRightPercent: Int = 0, bottomLeftPercent: Int = 0, - ): CorneredShape = CorneredShape( - Corner.Relative(topLeftPercent, CutCornerTreatment), - Corner.Relative(topRightPercent, CutCornerTreatment), - Corner.Relative(bottomRightPercent, CutCornerTreatment), - Corner.Relative(bottomLeftPercent, CutCornerTreatment), - ) + ): CorneredShape = + CorneredShape( + Corner.Relative(topLeftPercent, CutCornerTreatment), + Corner.Relative(topRightPercent, CutCornerTreatment), + Corner.Relative(bottomRightPercent, CutCornerTreatment), + Corner.Relative(bottomLeftPercent, CutCornerTreatment), + ) /** * Creates a [Shape] out of a [Drawable]. @@ -130,69 +131,70 @@ public object Shapes { tintDrawable: Boolean = true, keepAspectRatio: Boolean = false, otherShape: Shape? = rectShape, - ): Shape = object : Shape { - - private val ratio: Float = drawable.intrinsicWidth.coerceAtLeast(1) / - drawable.intrinsicHeight.coerceAtLeast(1).toFloat() - - override fun drawShape( - context: DrawContext, - paint: Paint, - path: Path, - left: Float, - top: Float, - right: Float, - bottom: Float, - ) { - if (bottom - top == 0f || left - right == 0f) return - val width = right - left - val height = bottom - top - - var otherComponentLeft = left - var otherComponentTop = top - - if (tintDrawable) drawable.setTintCompat(paint.color) - - if (height > width) { - val drawableHeight = if (keepAspectRatio) width / ratio else height - val topWithoutClipping = minOf(top, bottom - drawableHeight) - drawable.setBounds( - left = left, - top = topWithoutClipping, - right = right, - bottom = topWithoutClipping + drawableHeight, - ) - - otherComponentTop = topWithoutClipping + drawableHeight - } else { - val drawableWidth = if (keepAspectRatio) height * ratio else width - val leftWithoutClipping = minOf(left, right - drawableWidth) - drawable.setBounds( - left = leftWithoutClipping, - top = top, - right = leftWithoutClipping + drawableWidth, - bottom = bottom, - ) - - otherComponentLeft = leftWithoutClipping + drawableWidth - } - - drawable.draw(context.canvas) - otherShape ?: return - - if (bottom - otherComponentTop > 0 && right - otherComponentLeft > 0) { - otherShape.drawShape( - context = context, - paint = paint, - path = path, - left = otherComponentLeft, - top = otherComponentTop, - right = right, - bottom = bottom, - ) + ): Shape = + object : Shape { + private val ratio: Float = + drawable.intrinsicWidth.coerceAtLeast(1) / + drawable.intrinsicHeight.coerceAtLeast(1).toFloat() + + override fun drawShape( + context: DrawContext, + paint: Paint, + path: Path, + left: Float, + top: Float, + right: Float, + bottom: Float, + ) { + if (bottom - top == 0f || left - right == 0f) return + val width = right - left + val height = bottom - top + + var otherComponentLeft = left + var otherComponentTop = top + + if (tintDrawable) drawable.setTintCompat(paint.color) + + if (height > width) { + val drawableHeight = if (keepAspectRatio) width / ratio else height + val topWithoutClipping = minOf(top, bottom - drawableHeight) + drawable.setBounds( + left = left, + top = topWithoutClipping, + right = right, + bottom = topWithoutClipping + drawableHeight, + ) + + otherComponentTop = topWithoutClipping + drawableHeight + } else { + val drawableWidth = if (keepAspectRatio) height * ratio else width + val leftWithoutClipping = minOf(left, right - drawableWidth) + drawable.setBounds( + left = leftWithoutClipping, + top = top, + right = leftWithoutClipping + drawableWidth, + bottom = bottom, + ) + + otherComponentLeft = leftWithoutClipping + drawableWidth + } + + drawable.draw(context.canvas) + otherShape ?: return + + if (bottom - otherComponentTop > 0 && right - otherComponentLeft > 0) { + otherShape.drawShape( + context = context, + paint = paint, + path = path, + left = otherComponentLeft, + top = otherComponentTop, + right = right, + bottom = bottom, + ) + } } } - } } private fun Drawable.setTintCompat(tint: Int) { diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/Corner.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/Corner.kt index 7620d124a..2595034ba 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/Corner.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/Corner.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. @@ -26,7 +26,6 @@ import com.patrykandpatrick.vico.core.throwable.IllegalPercentageException public sealed class Corner( public val cornerTreatment: CornerTreatment, ) { - /** * Calculates the size of the corner. * @@ -35,7 +34,10 @@ public sealed class Corner( * * @return the size of the corner (in pixels). */ - public abstract fun getCornerSize(availableCornerSize: Float, density: Float): Float + public abstract fun getCornerSize( + availableCornerSize: Float, + density: Float, + ): Float /** * Defines an absolute size for a corner (in dp). @@ -46,9 +48,10 @@ public sealed class Corner( public val sizeDp: Float, cornerTreatment: CornerTreatment, ) : Corner(cornerTreatment) { - - override fun getCornerSize(availableCornerSize: Float, density: Float): Float = - sizeDp * density + override fun getCornerSize( + availableCornerSize: Float, + density: Float, + ): Float = sizeDp * density } /** @@ -60,13 +63,14 @@ public sealed class Corner( public val percentage: Int, cornerTreatment: CornerTreatment, ) : Corner(cornerTreatment) { - init { if (percentage !in 0..MAX_PERCENTAGE) throw IllegalPercentageException(percentage) } - override fun getCornerSize(availableCornerSize: Float, density: Float): Float = - availableCornerSize / MAX_PERCENTAGE * percentage + override fun getCornerSize( + availableCornerSize: Float, + density: Float, + ): Float = availableCornerSize / MAX_PERCENTAGE * percentage } public companion object { diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/CornerTreatment.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/CornerTreatment.kt index bbba1743d..9821965bb 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/CornerTreatment.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/CornerTreatment.kt @@ -22,7 +22,6 @@ import android.graphics.Path * Defines a shape corner style. */ public interface CornerTreatment { - /** * Draws a shape corner of the appropriate style. * @param x1 the horizontal coordinate of the starting point. diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/CornerTreatments.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/CornerTreatments.kt index 066c3da05..9b85d145c 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/CornerTreatments.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/CornerTreatments.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. @@ -24,7 +24,6 @@ import com.patrykandpatrick.vico.core.extension.piRad * Creates sharp corners. */ public object SharpCornerTreatment : CornerTreatment { - public override fun createCorner( x1: Float, y1: Float, @@ -32,27 +31,27 @@ public object SharpCornerTreatment : CornerTreatment { y2: Float, cornerLocation: CornerLocation, path: Path, - ): Unit = when (cornerLocation) { - CornerLocation.TopLeft -> { - path.lineTo(x1, y2) - } - CornerLocation.TopRight -> { - path.lineTo(x2, y1) - } - CornerLocation.BottomRight -> { - path.lineTo(x1, y2) - } - CornerLocation.BottomLeft -> { - path.lineTo(x2, y1) + ): Unit = + when (cornerLocation) { + CornerLocation.TopLeft -> { + path.lineTo(x1, y2) + } + CornerLocation.TopRight -> { + path.lineTo(x2, y1) + } + CornerLocation.BottomRight -> { + path.lineTo(x1, y2) + } + CornerLocation.BottomLeft -> { + path.lineTo(x2, y1) + } } - } } /** * Creates cut corners. */ public object CutCornerTreatment : CornerTreatment { - override fun createCorner( x1: Float, y1: Float, @@ -70,7 +69,6 @@ public object CutCornerTreatment : CornerTreatment { * Creates rounded corners. */ public object RoundedCornerTreatment : CornerTreatment { - private val tempRect = RectF() override fun createCorner( diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/CorneredShape.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/CorneredShape.kt index e7cc91685..c1573aba5 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/CorneredShape.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/CorneredShape.kt @@ -36,7 +36,6 @@ public open class CorneredShape( public val bottomRight: Corner = Corner.Sharp, public val bottomLeft: Corner = Corner.Sharp, ) : Shape { - private val Float.nonZero: Float get() = if (this == 0f) 1f else this @@ -48,7 +47,11 @@ public open class CorneredShape( * @param height the height of the [Shape]. * @param density the pixel density of the screen (used in pixel size calculation). */ - public fun getCornerScale(width: Float, height: Float, density: Float): Float { + public fun getCornerScale( + width: Float, + height: Float, + density: Float, + ): Float { val availableSize = minOf(width, height) val tL = topLeft.getCornerSize(availableSize, density) val tR = topRight.getCornerSize(availableSize, density) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/MarkerCorneredShape.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/MarkerCorneredShape.kt index 2294d415b..749211de7 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/MarkerCorneredShape.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/cornered/MarkerCorneredShape.kt @@ -41,7 +41,6 @@ public open class MarkerCorneredShape( bottomLeft: Corner, public val tickSizeDp: Float = DEF_MARKER_TICK_SIZE, ) : CorneredShape(topLeft, topRight, bottomRight, bottomLeft) { - public constructor( all: Corner, tickSizeDp: Float = DEF_MARKER_TICK_SIZE, @@ -66,41 +65,42 @@ public open class MarkerCorneredShape( top: Float, right: Float, bottom: Float, - ): Unit = with(context) { - val tickX: Float? = context[tickXKey] - if (tickX != null) { - createPath( - context = context, - path = path, - left = left, - top = top, - right = right, - bottom = bottom, - ) - val tickSize = context.dpToPx(tickSizeDp) - val availableCornerSize = minOf(right - left, bottom - top) - val cornerScale = getCornerScale(right - left, bottom - top, density) + ): Unit = + with(context) { + val tickX: Float? = context[TICK_X_KEY] + if (tickX != null) { + createPath( + context = context, + path = path, + left = left, + top = top, + right = right, + bottom = bottom, + ) + val tickSize = context.dpToPx(tickSizeDp) + val availableCornerSize = minOf(right - left, bottom - top) + val cornerScale = getCornerScale(right - left, bottom - top, density) - val minLeft = left + bottomLeft.getCornerSize(availableCornerSize, density) * cornerScale - val maxLeft = right - bottomRight.getCornerSize(availableCornerSize, density) * cornerScale + val minLeft = left + bottomLeft.getCornerSize(availableCornerSize, density) * cornerScale + val maxLeft = right - bottomRight.getCornerSize(availableCornerSize, density) * cornerScale - val coercedTickSize = tickSize.coerceAtMost((maxLeft - minLeft).half.coerceAtLeast(0f)) + val coercedTickSize = tickSize.coerceAtMost((maxLeft - minLeft).half.coerceAtLeast(0f)) - (tickX - coercedTickSize) - .takeIf { minLeft < maxLeft } - ?.coerceIn(minLeft, maxLeft - coercedTickSize.doubled) - ?.also { tickTopLeft -> - path.moveTo(tickTopLeft, bottom) - path.lineTo(tickX, bottom + tickSize) - path.lineTo(tickTopLeft + coercedTickSize.doubled, bottom) - } + (tickX - coercedTickSize) + .takeIf { minLeft < maxLeft } + ?.coerceIn(minLeft, maxLeft - coercedTickSize.doubled) + ?.also { tickTopLeft -> + path.moveTo(tickTopLeft, bottom) + path.lineTo(tickX, bottom + tickSize) + path.lineTo(tickTopLeft + coercedTickSize.doubled, bottom) + } - path.close() - context.canvas.drawPath(path, paint) - } else { - super.drawShape(context, paint, path, left, top, right, bottom) + path.close() + context.canvas.drawPath(path, paint) + } else { + super.drawShape(context, paint, path, left, top, right, bottom) + } } - } public companion object { /** @@ -108,6 +108,6 @@ public open class MarkerCorneredShape( * * @see Extras */ - public const val tickXKey: String = "tickX" + public const val TICK_X_KEY: String = "tickX" } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/CacheableDynamicShader.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/CacheableDynamicShader.kt index 3611d2edd..3d637e2ca 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/CacheableDynamicShader.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/CacheableDynamicShader.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. @@ -23,7 +23,6 @@ import com.patrykandpatrick.vico.core.context.DrawContext * [CacheableDynamicShader] can cache created [Shader] instances for reuse between identical sets of bounds. */ public abstract class CacheableDynamicShader : DynamicShader { - private val cache = HashMap(1) override fun provideShader( diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/ComponentShader.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/ComponentShader.kt index 289641019..1e824169a 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/ComponentShader.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/ComponentShader.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. @@ -40,34 +40,34 @@ public class ComponentShader( private val tileXMode: Shader.TileMode = Shader.TileMode.REPEAT, private val tileYMode: Shader.TileMode = tileXMode, ) : CacheableDynamicShader() { - override fun createShader( context: DrawContext, left: Float, top: Float, right: Float, bottom: Float, - ): Shader = with(context) { - val size = componentSizeDp.pixels.toInt() * if (checkeredArrangement) 2 else 1 - val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) + ): Shader = + with(context) { + val size = componentSizeDp.pixels.toInt() * if (checkeredArrangement) 2 else 1 + val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) - val canvas = Canvas(bitmap) - context.withOtherCanvas(canvas) { - if (checkeredArrangement) { - val halfSize = componentSizeDp.pixels.half - with(component) { - draw(context, -halfSize, -halfSize, componentSizeDp.pixels) - draw(context, -halfSize, size - halfSize, componentSizeDp.pixels) - draw(context, size - halfSize, -halfSize, componentSizeDp.pixels) - draw(context, size - halfSize, size - halfSize, componentSizeDp.pixels) - draw(context, halfSize, halfSize, componentSizeDp.pixels) + val canvas = Canvas(bitmap) + context.withOtherCanvas(canvas) { + if (checkeredArrangement) { + val halfSize = componentSizeDp.pixels.half + with(component) { + draw(context, -halfSize, -halfSize, componentSizeDp.pixels) + draw(context, -halfSize, size - halfSize, componentSizeDp.pixels) + draw(context, size - halfSize, -halfSize, componentSizeDp.pixels) + draw(context, size - halfSize, size - halfSize, componentSizeDp.pixels) + draw(context, halfSize, halfSize, componentSizeDp.pixels) + } + } else { + component.draw(context, 0f, 0f, componentSizeDp.pixels, componentSizeDp.pixels) } - } else { - component.draw(context, 0f, 0f, componentSizeDp.pixels, componentSizeDp.pixels) } + return BitmapShader(bitmap, tileXMode, tileYMode) } - return BitmapShader(bitmap, tileXMode, tileYMode) - } private fun Component.draw( context: DrawContext, diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/DynamicShader.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/DynamicShader.kt index 068814357..9a7e4542d 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/DynamicShader.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/DynamicShader.kt @@ -32,7 +32,6 @@ import kotlin.math.roundToInt * @see Shader */ public fun interface DynamicShader { - private val bitmapPixelExtractionKey: Any get() = "bitmapPixelExtractionKey${hashCode()}" @@ -42,13 +41,14 @@ public fun interface DynamicShader { public fun provideShader( context: DrawContext, bounds: RectF, - ): Shader = provideShader( - context = context, - left = bounds.left, - top = bounds.top, - right = bounds.right, - bottom = bounds.bottom, - ) + ): Shader = + provideShader( + context = context, + left = bounds.left, + top = bounds.top, + right = bounds.right, + bottom = bounds.bottom, + ) /** * Creates a [Shader] by using the provided [left], [top], [right], and [bottom] bounds. @@ -69,7 +69,12 @@ public fun interface DynamicShader { * @param rectF the [RectF] holding coordinates of the area to apply the style to. * @param zeroLineYFraction the fraction of the height of the area to apply the style to, that the zero line is at. */ - public fun applyTo(paint: Paint, drawContext: DrawContext, rectF: RectF, zeroLineYFraction: Float) { + public fun applyTo( + paint: Paint, + drawContext: DrawContext, + rectF: RectF, + zeroLineYFraction: Float, + ) { applyTo(paint, drawContext, rectF.left, rectF.top, rectF.right, rectF.bottom, zeroLineYFraction) } @@ -104,11 +109,17 @@ public fun interface DynamicShader { * @param rectF the [RectF] holding coordinates of the area to apply the style to. * @param zeroLineYFraction the fraction of the height of the area to apply the style to, that the zero line is at. */ - public fun getColorAt(point: Point, drawContext: DrawContext, rectF: RectF, zeroLineYFraction: Float): Int { - val bitmap = drawContext.getOrPutExtra(bitmapPixelExtractionKey) { - val paint = Paint(Paint.ANTI_ALIAS_FLAG) - getBitmap(drawContext, paint, rectF) - } + public fun getColorAt( + point: Point, + drawContext: DrawContext, + rectF: RectF, + zeroLineYFraction: Float, + ): Int { + val bitmap = + drawContext.getOrPutExtra(bitmapPixelExtractionKey) { + val paint = Paint(Paint.ANTI_ALIAS_FLAG) + getBitmap(drawContext, paint, rectF) + } return bitmap.getPixel( (point.x - rectF.left).toInt().coerceIn(0, rectF.width().toInt() - 1), (point.y - rectF.top).toInt().coerceIn(0, rectF.height().toInt() - 1), @@ -116,8 +127,11 @@ public fun interface DynamicShader { } public companion object { - - private fun DynamicShader.getBitmap(drawContext: DrawContext, paint: Paint, rectF: RectF): Bitmap { + private fun DynamicShader.getBitmap( + drawContext: DrawContext, + paint: Paint, + rectF: RectF, + ): Bitmap { val width = rectF.width().roundToInt() val height = rectF.height().roundToInt() paint.shader = provideShader(drawContext, 0f, 0f, width.toFloat(), height.toFloat()) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/DynamicShaders.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/DynamicShaders.kt index 6ba8e93eb..1c370cf3b 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/DynamicShaders.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/DynamicShaders.kt @@ -30,7 +30,6 @@ import com.patrykandpatrick.vico.core.context.DrawContext * An object that holds simple, anonymous implementations of [DynamicShader]. */ public object DynamicShaders { - /** * Creates a [DynamicShader] out of the given [bitmap]. */ @@ -38,15 +37,16 @@ public object DynamicShaders { bitmap: Bitmap, tileXMode: Shader.TileMode = Shader.TileMode.REPEAT, tileYMode: Shader.TileMode = tileXMode, - ): DynamicShader = object : CacheableDynamicShader() { - override fun createShader( - context: DrawContext, - left: Float, - top: Float, - right: Float, - bottom: Float, - ): Shader = BitmapShader(bitmap, tileXMode, tileYMode) - } + ): DynamicShader = + object : CacheableDynamicShader() { + override fun createShader( + context: DrawContext, + left: Float, + top: Float, + right: Float, + bottom: Float, + ): Shader = BitmapShader(bitmap, tileXMode, tileYMode) + } /** * Creates a [ComposeShader] out of two [DynamicShader]s by using a [BlendMode]. @@ -56,13 +56,14 @@ public object DynamicShaders { first: DynamicShader, second: DynamicShader, mode: BlendMode, - ): DynamicShader = DynamicShader { context, left, top, right, bottom -> - ComposeShader( - first.provideShader(context, left, top, right, bottom), - second.provideShader(context, left, top, right, bottom), - mode, - ) - } + ): DynamicShader = + DynamicShader { context, left, top, right, bottom -> + ComposeShader( + first.provideShader(context, left, top, right, bottom), + second.provideShader(context, left, top, right, bottom), + mode, + ) + } /** * Creates a [ComposeShader] out of two [DynamicShader]s by using a [PorterDuff.Mode]. @@ -71,13 +72,14 @@ public object DynamicShaders { first: DynamicShader, second: DynamicShader, mode: PorterDuff.Mode, - ): DynamicShader = DynamicShader { context, left, top, right, bottom -> - ComposeShader( - first.provideShader(context, left, top, right, bottom), - second.provideShader(context, left, top, right, bottom), - mode, - ) - } + ): DynamicShader = + DynamicShader { context, left, top, right, bottom -> + ComposeShader( + first.provideShader(context, left, top, right, bottom), + second.provideShader(context, left, top, right, bottom), + mode, + ) + } } /** diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/HorizontalSplitShader.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/HorizontalSplitShader.kt index 985098e11..b56ce7124 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/HorizontalSplitShader.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/HorizontalSplitShader.kt @@ -34,10 +34,15 @@ import com.patrykandpatrick.vico.core.model.Point public abstract class HorizontalSplitShader( public var dividerYFraction: Float = 0f, ) : CacheableDynamicShader() { - private val paint = Paint() - override fun createShader(context: DrawContext, left: Float, top: Float, right: Float, bottom: Float): Shader { + override fun createShader( + context: DrawContext, + left: Float, + top: Float, + right: Float, + bottom: Float, + ): Shader { val height = (bottom - top).toInt() val width = (right - left).toInt() val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) @@ -94,8 +99,12 @@ public abstract class HorizontalSplitShader( bottom: Float, ) - override fun createKey(left: Float, top: Float, right: Float, bottom: Float): String = - super.createKey(left, top, right, bottom) + ",$dividerYFraction" + override fun createKey( + left: Float, + top: Float, + right: Float, + bottom: Float, + ): String = super.createKey(left, top, right, bottom) + ",$dividerYFraction" override fun applyTo( paint: Paint, @@ -110,7 +119,12 @@ public abstract class HorizontalSplitShader( super.applyTo(paint, drawContext, left, top, right, bottom, zeroLineYFraction) } - override fun getColorAt(point: Point, drawContext: DrawContext, rectF: RectF, zeroLineYFraction: Float): Int { + override fun getColorAt( + point: Point, + drawContext: DrawContext, + rectF: RectF, + zeroLineYFraction: Float, + ): Int { dividerYFraction = zeroLineYFraction return super.getColorAt(point, drawContext, rectF, zeroLineYFraction) } @@ -127,7 +141,6 @@ public abstract class HorizontalSplitShader( public var colorBottom: Int, dividerYFraction: Float = 0f, ) : HorizontalSplitShader(dividerYFraction) { - override fun applyTopTo( paint: Paint, drawContext: DrawContext, @@ -152,8 +165,12 @@ public abstract class HorizontalSplitShader( paint.shader = null } - override fun createKey(left: Float, top: Float, right: Float, bottom: Float): String = - super.createKey(left, top, right, bottom) + ",$colorTop,$colorBottom" + override fun createKey( + left: Float, + top: Float, + right: Float, + bottom: Float, + ): String = super.createKey(left, top, right, bottom) + ",$colorTop,$colorBottom" override fun equals(other: Any?): Boolean = when { @@ -185,7 +202,6 @@ public abstract class HorizontalSplitShader( public var bottomShader: DynamicShader, dividerYFraction: Float = 0f, ) : HorizontalSplitShader(dividerYFraction) { - override fun applyTopTo( paint: Paint, drawContext: DrawContext, @@ -208,8 +224,12 @@ public abstract class HorizontalSplitShader( paint.shader = bottomShader.provideShader(drawContext, left, top, right, bottom) } - override fun createKey(left: Float, top: Float, right: Float, bottom: Float): String = - super.createKey(left, top, right, bottom) + ",${topShader.hashCode()},${bottomShader.hashCode()}" + override fun createKey( + left: Float, + top: Float, + right: Float, + bottom: Float, + ): String = super.createKey(left, top, right, bottom) + ",${topShader.hashCode()},${bottomShader.hashCode()}" override fun equals(other: Any?): Boolean = when { @@ -221,7 +241,6 @@ public abstract class HorizontalSplitShader( dividerYFraction == other.dividerYFraction } - override fun hashCode(): Int = - 31 * topShader.hashCode() + bottomShader.hashCode() + override fun hashCode(): Int = 31 * topShader.hashCode() + bottomShader.hashCode() } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/SolidShader.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/SolidShader.kt index 1da7b4cda..fc07e092e 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/SolidShader.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/SolidShader.kt @@ -30,7 +30,6 @@ import com.patrykandpatrick.vico.core.model.Point * A [DynamicShader] that fills the area with the solid [color]. */ public class SolidShader(public val color: Int) : DynamicShader { - private val shader: Shader = getShaderImplementation() private fun getShaderImplementation(): Shader { @@ -39,15 +38,21 @@ public class SolidShader(public val color: Int) : DynamicShader { setColorUniform(INPUT_COLOR, color) } } else { - val bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888).apply { - setPixel(0, 0, color) - } + val bitmap = + Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888).apply { + setPixel(0, 0, color) + } BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) } } - override fun provideShader(context: DrawContext, left: Float, top: Float, right: Float, bottom: Float): Shader = - shader + override fun provideShader( + context: DrawContext, + left: Float, + top: Float, + right: Float, + bottom: Float, + ): Shader = shader override fun applyTo( paint: Paint, @@ -61,11 +66,14 @@ public class SolidShader(public val color: Int) : DynamicShader { paint.shader = shader } - override fun getColorAt(point: Point, drawContext: DrawContext, rectF: RectF, zeroLineYFraction: Float): Int = - color + override fun getColorAt( + point: Point, + drawContext: DrawContext, + rectF: RectF, + zeroLineYFraction: Float, + ): Int = color private companion object { - private const val INPUT_COLOR = "inputColor" private val SHADER_CODE = diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/StaticShader.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/StaticShader.kt index 09e86300f..937c61175 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/StaticShader.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/StaticShader.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. @@ -25,7 +25,6 @@ import com.patrykandpatrick.vico.core.context.DrawContext * @property shader the [Shader] that will always be provided, regardless of the [provideShader] function’s arguments. */ public class StaticShader(private val shader: Shader) : DynamicShader { - override fun provideShader( context: DrawContext, left: Float, diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shadow/ComponentShadow.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shadow/ComponentShadow.kt index 53f4b975d..6e72c698b 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shadow/ComponentShadow.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shadow/ComponentShadow.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. @@ -29,7 +29,6 @@ import com.patrykandpatrick.vico.core.extension.applyElevationOverlayToColor * @property color the shadow color. * @property applyElevationOverlay whether to apply an elevation overlay to the component casting the shadow. */ -@Suppress("ComplexCondition") public data class ComponentShadow( var radius: Float = 0f, var dx: Float = 0f, @@ -50,11 +49,12 @@ public data class ComponentShadow( context: DrawContext, paint: Paint, backgroundColor: Int, - ): Unit = with(context) { - if (shouldUpdateShadowLayer()) { - updateShadowLayer(paint, backgroundColor) + ): Unit = + with(context) { + if (shouldUpdateShadowLayer()) { + updateShadowLayer(paint, backgroundColor) + } } - } private fun DrawContext.updateShadowLayer( paint: Paint, @@ -63,11 +63,12 @@ public data class ComponentShadow( if (color == 0 || radius == 0f && dx == 0f && dy == 0f) { paint.clearShadowLayer() } else { - paint.color = if (applyElevationOverlay) { - applyElevationOverlayToColor(color = backgroundColor, elevationDp = radius) - } else { - backgroundColor - } + paint.color = + if (applyElevationOverlay) { + applyElevationOverlayToColor(color = backgroundColor, elevationDp = radius) + } else { + backgroundColor + } paint.setShadowLayer( radius.pixels, dx.pixels, diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/text/TextComponent.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/text/TextComponent.kt index fd015ea41..0fb1e0f90 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/text/TextComponent.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/text/TextComponent.kt @@ -67,7 +67,6 @@ private const val DEF_LAYOUT_SIZE = 100000 * @see [textComponent] */ public open class TextComponent protected constructor() : Padding, Margins { - private val textPaint: TextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG) private val tempMeasureBounds = RectF() @@ -149,89 +148,92 @@ public open class TextComponent protected constructor() : Padding, Margins { maxTextWidth: Int = DEF_LAYOUT_SIZE, maxTextHeight: Int = DEF_LAYOUT_SIZE, rotationDegrees: Float = 0f, - ): Unit = with(context) { - if (text.isBlank()) return - layout = getLayout(text, maxTextWidth, maxTextHeight, rotationDegrees) - - val shouldRotate = rotationDegrees % 2f.piRad != 0f - val textStartPosition = horizontalPosition.getTextStartPosition(context, textX, layout.widestLineWidth) - val textTopPosition = verticalPosition.getTextTopPosition(context, textY, layout.height.toFloat()) - - context.withCanvas { - save() - - val bounds = layout.getBounds(tempMeasureBounds) - val textAlignmentCorrection = getTextAlignmentCorrection(bounds.width()) - - with(receiver = bounds) { - left -= padding.getLeftDp(isLtr).pixels - top -= padding.topDp.pixels - right += padding.getRightDp(isLtr).pixels - bottom += padding.bottomDp.pixels - } - - var xCorrection = 0f - var yCorrection = 0f + ): Unit = + with(context) { + if (text.isBlank()) return + layout = getLayout(text, maxTextWidth, maxTextHeight, rotationDegrees) + + val shouldRotate = rotationDegrees % 2f.piRad != 0f + val textStartPosition = horizontalPosition.getTextStartPosition(context, textX, layout.widestLineWidth) + val textTopPosition = verticalPosition.getTextTopPosition(context, textY, layout.height.toFloat()) + + context.withCanvas { + save() + + val bounds = layout.getBounds(tempMeasureBounds) + val textAlignmentCorrection = getTextAlignmentCorrection(bounds.width()) + + with(receiver = bounds) { + left -= padding.getLeftDp(isLtr).pixels + top -= padding.topDp.pixels + right += padding.getRightDp(isLtr).pixels + bottom += padding.bottomDp.pixels + } - if (shouldRotate) { - val boundsPostRotation = bounds.copy().rotate(rotationDegrees) - val heightDelta = bounds.height() - boundsPostRotation.height() - val widthDelta = bounds.width() - boundsPostRotation.width() + var xCorrection = 0f + var yCorrection = 0f + + if (shouldRotate) { + val boundsPostRotation = bounds.copy().rotate(rotationDegrees) + val heightDelta = bounds.height() - boundsPostRotation.height() + val widthDelta = bounds.width() - boundsPostRotation.width() + + xCorrection = when (horizontalPosition) { + HorizontalPosition.Start -> widthDelta.half + HorizontalPosition.End -> -widthDelta.half + else -> 0f + } * context.layoutDirectionMultiplier + + yCorrection = + when (verticalPosition) { + VerticalPosition.Top -> heightDelta.half + VerticalPosition.Bottom -> -heightDelta.half + else -> 0f + } + } - xCorrection = when (horizontalPosition) { - HorizontalPosition.Start -> widthDelta.half - HorizontalPosition.End -> -widthDelta.half - else -> 0f - } * context.layoutDirectionMultiplier + bounds.translate( + x = textStartPosition + xCorrection, + y = textTopPosition + yCorrection, + ) - yCorrection = when (verticalPosition) { - VerticalPosition.Top -> heightDelta.half - VerticalPosition.Bottom -> -heightDelta.half - else -> 0f + if (shouldRotate) { + rotate(rotationDegrees, bounds.centerX(), bounds.centerY()) } - } - - bounds.translate( - x = textStartPosition + xCorrection, - y = textTopPosition + yCorrection, - ) - if (shouldRotate) { - rotate(rotationDegrees, bounds.centerX(), bounds.centerY()) + background?.draw( + context = context, + left = bounds.left, + top = bounds.top, + right = bounds.right, + bottom = bounds.bottom, + ) + + translate( + bounds.left + padding.getLeftDp(isLtr).pixels + textAlignmentCorrection, + bounds.top + padding.topDp.pixels, + ) + + layout.draw(this) + restore() } - - background?.draw( - context = context, - left = bounds.left, - top = bounds.top, - right = bounds.right, - bottom = bounds.bottom, - ) - - translate( - bounds.left + padding.getLeftDp(isLtr).pixels + textAlignmentCorrection, - bounds.top + padding.topDp.pixels, - ) - - layout.draw(this) - restore() } - } private fun HorizontalPosition.getTextStartPosition( context: MeasureContext, baseXPosition: Float, width: Float, - ): Float = with(context) { - when (this@getTextStartPosition) { - HorizontalPosition.Start -> - if (isLtr) getTextRightPosition(baseXPosition, width) else getTextLeftPosition(baseXPosition) - HorizontalPosition.Center -> - baseXPosition - width.half - HorizontalPosition.End -> - if (isLtr) getTextLeftPosition(baseXPosition) else getTextRightPosition(baseXPosition, width) + ): Float = + with(context) { + when (this@getTextStartPosition) { + HorizontalPosition.Start -> + if (isLtr) getTextRightPosition(baseXPosition, width) else getTextLeftPosition(baseXPosition) + HorizontalPosition.Center -> + baseXPosition - width.half + HorizontalPosition.End -> + if (isLtr) getTextLeftPosition(baseXPosition) else getTextRightPosition(baseXPosition, width) + } } - } private fun MeasureContext.getTextLeftPosition(baseXPosition: Float): Float = baseXPosition + padding.getLeftDp(isLtr).pixels + margins.getLeftDp(isLtr).pixels @@ -242,15 +244,16 @@ public open class TextComponent protected constructor() : Padding, Margins { ): Float = baseXPosition - padding.getRightDp(isLtr).pixels - margins.getRightDp(isLtr).pixels - width private fun getTextAlignmentCorrection(width: Float): Float { - val ltrAlignment = if (layout.getParagraphDirection(0) == Layout.DIR_LEFT_TO_RIGHT) { - textAlignment - } else { - when (textAlignment) { - Layout.Alignment.ALIGN_NORMAL -> Layout.Alignment.ALIGN_OPPOSITE - Layout.Alignment.ALIGN_OPPOSITE -> Layout.Alignment.ALIGN_NORMAL - Layout.Alignment.ALIGN_CENTER -> Layout.Alignment.ALIGN_CENTER + val ltrAlignment = + if (layout.getParagraphDirection(0) == Layout.DIR_LEFT_TO_RIGHT) { + textAlignment + } else { + when (textAlignment) { + Layout.Alignment.ALIGN_NORMAL -> Layout.Alignment.ALIGN_OPPOSITE + Layout.Alignment.ALIGN_OPPOSITE -> Layout.Alignment.ALIGN_NORMAL + Layout.Alignment.ALIGN_CENTER -> Layout.Alignment.ALIGN_CENTER + } } - } return when (ltrAlignment) { Layout.Alignment.ALIGN_NORMAL -> 0f Layout.Alignment.ALIGN_OPPOSITE -> width - layout.width @@ -263,13 +266,15 @@ public open class TextComponent protected constructor() : Padding, Margins { context: MeasureContext, textY: Float, layoutHeight: Float, - ): Float = with(context) { - textY + when (this@getTextTopPosition) { - VerticalPosition.Top -> -layoutHeight - padding.bottomDp.pixels - margins.bottomDp.pixels - VerticalPosition.Center -> -layoutHeight.half - VerticalPosition.Bottom -> padding.topDp.pixels + margins.topDp.pixels + ): Float = + with(context) { + textY + + when (this@getTextTopPosition) { + VerticalPosition.Top -> -layoutHeight - padding.bottomDp.pixels - margins.bottomDp.pixels + VerticalPosition.Center -> -layoutHeight.half + VerticalPosition.Bottom -> padding.topDp.pixels + margins.topDp.pixels + } } - } /** * Returns the width of this [TextComponent] for the given [text] and the available [width] and [height]. [pad] @@ -282,14 +287,15 @@ public open class TextComponent protected constructor() : Padding, Margins { height: Int = DEF_LAYOUT_SIZE, rotationDegrees: Float = 0f, pad: Boolean = text == null, - ): Float = getTextBounds( - context = context, - text = text, - width = width, - height = height, - rotationDegrees = rotationDegrees, - pad = pad, - ).width() + ): Float = + getTextBounds( + context = context, + text = text, + width = width, + height = height, + rotationDegrees = rotationDegrees, + pad = pad, + ).width() /** * Returns the height of this [TextComponent] for the given [text] and the available [width] and [height]. [pad] @@ -302,14 +308,15 @@ public open class TextComponent protected constructor() : Padding, Margins { height: Int = DEF_LAYOUT_SIZE, rotationDegrees: Float = 0f, pad: Boolean = text == null, - ): Float = getTextBounds( - context = context, - text = text, - width = width, - height = height, - rotationDegrees = rotationDegrees, - pad = pad, - ).height() + ): Float = + getTextBounds( + context = context, + text = text, + width = width, + height = height, + rotationDegrees = rotationDegrees, + pad = pad, + ).height() /** * Returns the bounds ([RectF]) of this [TextComponent] for the given [text] and the available [width] and [height]. @@ -324,25 +331,26 @@ public open class TextComponent protected constructor() : Padding, Margins { includePaddingAndMargins: Boolean = true, rotationDegrees: Float = 0f, pad: Boolean = text == null, - ): RectF = with(context) { - var measuredText = text?.toString().orEmpty() - if (pad) repeat((lineCount - measuredText.lines().size).coerceAtLeast(0)) { measuredText += '\n' } - getLayout(measuredText, width, height, rotationDegrees) - .getBounds(outRect) - .apply { - if (includePaddingAndMargins) { - right += padding.horizontalDp.pixels - bottom += padding.verticalDp.pixels + ): RectF = + with(context) { + var measuredText = text?.toString().orEmpty() + if (pad) repeat((lineCount - measuredText.lines().size).coerceAtLeast(0)) { measuredText += '\n' } + getLayout(measuredText, width, height, rotationDegrees) + .getBounds(outRect) + .apply { + if (includePaddingAndMargins) { + right += padding.horizontalDp.pixels + bottom += padding.verticalDp.pixels + } } - } - .rotate(rotationDegrees) - .apply { - if (includePaddingAndMargins) { - right += margins.horizontalDp.pixels - bottom += margins.verticalDp.pixels + .rotate(rotationDegrees) + .apply { + if (includePaddingAndMargins) { + right += margins.horizontalDp.pixels + bottom += margins.verticalDp.pixels + } } - } - } + } private fun MeasureContext.getLayout( text: CharSequence, @@ -353,20 +361,21 @@ public open class TextComponent protected constructor() : Padding, Margins { val widthWithoutMargins = width - margins.horizontalDp.wholePixels val heightWithoutMargins = height - margins.verticalDp.wholePixels - val correctedWidth = ( - when { - rotationDegrees % 1f.piRad == 0f -> widthWithoutMargins - rotationDegrees % .5f.piRad == 0f -> heightWithoutMargins - else -> { - val cumulatedHeight = lineCount * textPaint.lineHeight + padding.verticalDp.wholePixels - val alpha = Math.toRadians(rotationDegrees.toDouble()) - val absSinAlpha = sin(alpha).absoluteValue - val absCosAlpha = cos(alpha).absoluteValue - val basedOnWidth = (widthWithoutMargins - cumulatedHeight * absSinAlpha) / absCosAlpha - val basedOnHeight = (heightWithoutMargins - cumulatedHeight * absCosAlpha) / absSinAlpha - min(basedOnWidth, basedOnHeight).toInt() - } - } - padding.horizontalDp.wholePixels + val correctedWidth = + ( + when { + rotationDegrees % 1f.piRad == 0f -> widthWithoutMargins + rotationDegrees % .5f.piRad == 0f -> heightWithoutMargins + else -> { + val cumulatedHeight = lineCount * textPaint.lineHeight + padding.verticalDp.wholePixels + val alpha = Math.toRadians(rotationDegrees.toDouble()) + val absSinAlpha = sin(alpha).absoluteValue + val absCosAlpha = cos(alpha).absoluteValue + val basedOnWidth = (widthWithoutMargins - cumulatedHeight * absSinAlpha) / absCosAlpha + val basedOnHeight = (heightWithoutMargins - cumulatedHeight * absCosAlpha) / absSinAlpha + min(basedOnWidth, basedOnHeight).toInt() + } + } - padding.horizontalDp.wholePixels ).coerceAtLeast(0) val key = LAYOUT_KEY_PREFIX + text + correctedWidth + rotationDegrees + textPaint.hashCode() @@ -388,7 +397,6 @@ public open class TextComponent protected constructor() : Padding, Margins { * @see textComponent */ public class Builder { - /** * @see [TextComponent.color] */ @@ -437,17 +445,18 @@ public open class TextComponent protected constructor() : Padding, Margins { /** * Creates a new instance of [TextComponent] with the supplied properties. */ - public fun build(): TextComponent = TextComponent().apply { - color = this@Builder.color - textSizeSp = this@Builder.textSizeSp - typeface = this@Builder.typeface - ellipsize = this@Builder.ellipsize - lineCount = this@Builder.lineCount - background = this@Builder.background - textAlignment = this@Builder.textAlignment - padding.set(this@Builder.padding) - margins.set(this@Builder.margins) - } + public fun build(): TextComponent = + TextComponent().apply { + color = this@Builder.color + textSizeSp = this@Builder.textSizeSp + typeface = this@Builder.typeface + ellipsize = this@Builder.ellipsize + lineCount = this@Builder.lineCount + background = this@Builder.background + textAlignment = this@Builder.textAlignment + padding.set(this@Builder.padding) + margins.set(this@Builder.margins) + } } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/text/VerticalPosition.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/text/VerticalPosition.kt index 22d108cfe..e2ca837ac 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/text/VerticalPosition.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/text/VerticalPosition.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. @@ -28,9 +28,10 @@ public enum class VerticalPosition { /** * Returns the inverse of this [VerticalPosition]. */ - public fun negative(): VerticalPosition = when (this) { - Top -> Bottom - Center -> Center - Bottom -> Top - } + public fun negative(): VerticalPosition = + when (this) { + Top -> Bottom + Center -> Center + Bottom -> Top + } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/text/VerticalPositionExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/text/VerticalPositionExtensions.kt index 8e14fa55b..fa66eb1ce 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/text/VerticalPositionExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/component/text/VerticalPositionExtensions.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. @@ -32,10 +32,11 @@ internal fun VerticalPosition.inBounds( return when (this) { VerticalPosition.Top -> if (topFits) this else VerticalPosition.Bottom VerticalPosition.Bottom -> if (bottomFits) this else VerticalPosition.Top - VerticalPosition.Center -> when { - centerFits -> this - topFits -> VerticalPosition.Top - else -> VerticalPosition.Bottom - } + VerticalPosition.Center -> + when { + centerFits -> this + topFits -> VerticalPosition.Top + else -> VerticalPosition.Bottom + } } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/DefaultExtras.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/DefaultExtras.kt index cfeeb7384..8e531ab3c 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/DefaultExtras.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/DefaultExtras.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. @@ -24,10 +24,12 @@ private const val INITIAL_CAPACITY = 8 */ @Suppress("UNCHECKED_CAST") public class DefaultExtras : Extras { - private val extrasMap: HashMap = HashMap(INITIAL_CAPACITY) - public override fun putExtra(key: Any, value: Any) { + public override fun putExtra( + key: Any, + value: Any, + ) { extrasMap[key] = value } @@ -35,9 +37,10 @@ public class DefaultExtras : Extras { override fun getExtra(key: Any): T = extrasMap[key] as T - public override fun consumeExtra(key: Any): T = getExtra(key).also { - extrasMap.remove(key) - } + public override fun consumeExtra(key: Any): T = + getExtra(key).also { + extrasMap.remove(key) + } override fun clearExtras() { extrasMap.clear() diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/DrawContext.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/DrawContext.kt index 4091b5760..615e42243 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/DrawContext.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/DrawContext.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. @@ -26,7 +26,6 @@ import com.patrykandpatrick.vico.core.extension.saveLayer * It also defines helpful drawing functions. */ public interface DrawContext : MeasureContext { - /** * The elevation overlay color, applied to [ShapeComponent]s that cast shadows. */ @@ -47,14 +46,22 @@ public interface DrawContext : MeasureContext { /** * Temporarily swaps the [Canvas] and yields [DrawContext] as the [block]’s receiver. */ - public fun withOtherCanvas(canvas: Canvas, block: (DrawContext) -> Unit) + public fun withOtherCanvas( + canvas: Canvas, + block: (DrawContext) -> Unit, + ) /** * Clips the [Canvas] to the specified rectangle. * * @see Canvas.clipRect */ - public fun clipRect(left: Float, top: Float, right: Float, bottom: Float) { + public fun clipRect( + left: Float, + top: Float, + right: Float, + bottom: Float, + ) { canvas.clipRect(left, top, right, bottom) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/Extras.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/Extras.kt index b5ac5b0c7..eba959917 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/Extras.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/context/Extras.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. @@ -25,14 +25,16 @@ import kotlin.collections.Map * Extras are kept in memory while measuring or drawing is taking place. Afterwards, they are removed. */ public interface Extras { - /** * Saves an extra. * * @param key the extra’s unique identifier. * @param value the extra’s value. */ - public fun putExtra(key: Any, value: Any) + public fun putExtra( + key: Any, + value: Any, + ) /** * Checks whether an extra with the given key exists. @@ -59,7 +61,10 @@ public interface Extras { * * @see putExtra */ - public operator fun set(key: Any, value: Any): Unit = putExtra(key, value) + public operator fun set( + key: Any, + value: Any, + ): Unit = putExtra(key, value) /** * Operator function for [consumeExtra]. 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 67d526536..4ddb51173 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 @@ -25,7 +25,6 @@ import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider * [MeasureContext] holds data used by various chart components during the measuring and drawing phases. */ public interface MeasureContext : Extras { - /** * The bounds of the canvas that will be used to draw the chart and its components. */ 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 6922d90fa..76d32b6db 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 @@ -34,7 +34,6 @@ public data class MutableMeasureContext( public var spToPx: (Float) -> Float, override var chartValuesProvider: ChartValuesProvider, ) : MeasureContext, Extras by DefaultExtras() { - override fun reset() { clearExtras() } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/debug/DebugHelper.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/debug/DebugHelper.kt index f3aca9dd9..1ce7b6e9d 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/debug/DebugHelper.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/debug/DebugHelper.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. @@ -24,10 +24,11 @@ internal object DebugHelper { public var enabled: Boolean = false public var strokeWidthDp: Float = 1f - public var debugPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { - style = Paint.Style.STROKE - color = Color.MAGENTA - } + public var debugPaint = + Paint(Paint.ANTI_ALIAS_FLAG).apply { + style = Paint.Style.STROKE + color = Color.MAGENTA + } public fun drawDebugBounds( context: DrawContext, @@ -35,9 +36,10 @@ internal object DebugHelper { top: Float, right: Float, bottom: Float, - ): Unit = with(context) { - if (!enabled) return@with - debugPaint.strokeWidth = strokeWidthDp.pixels - canvas.drawRect(left, top, right, bottom, debugPaint) - } + ): Unit = + with(context) { + if (!enabled) return@with + debugPaint.strokeWidth = strokeWidthDp.pixels + canvas.drawRect(left, top, right, bottom, debugPaint) + } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/dimensions/BoundsAware.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/dimensions/BoundsAware.kt index d7998fb30..933392bc8 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/dimensions/BoundsAware.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/dimensions/BoundsAware.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. @@ -23,7 +23,6 @@ import com.patrykandpatrick.vico.core.extension.set * Defines an abstract component that has some physical bounds. */ public interface BoundsAware { - /** * The bounds of the abstract component. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/dimensions/Dimensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/dimensions/Dimensions.kt index 64072a073..3c17b8770 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/dimensions/Dimensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/dimensions/Dimensions.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. @@ -21,7 +21,6 @@ package com.patrykandpatrick.vico.core.dimensions * Used to store measurements such as padding or margin values. */ public interface Dimensions { - /** * The value for the start edge in the dp unit. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/dimensions/MutableDimensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/dimensions/MutableDimensions.kt index 929d030c7..0da79eaaf 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/dimensions/MutableDimensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/dimensions/MutableDimensions.kt @@ -25,7 +25,6 @@ public data class MutableDimensions( override var endDp: Float, override var bottomDp: Float, ) : Dimensions { - public constructor( horizontalDp: Float, verticalDp: Float, @@ -51,14 +50,12 @@ public data class MutableDimensions( /** * Updates these [MutableDimensions] to match the provided [Dimensions]. */ - public fun set(other: Dimensions): MutableDimensions = - set(other.startDp, other.topDp, other.endDp, other.bottomDp) + public fun set(other: Dimensions): MutableDimensions = set(other.startDp, other.topDp, other.endDp, other.bottomDp) /** * Sets a common value for each coordinate. */ - public fun set(all: Float): MutableDimensions = - set(all, all, all, all) + public fun set(all: Float): MutableDimensions = set(all, all, all, all) /** * Updates the coordinates to the provided values. @@ -68,28 +65,31 @@ public data class MutableDimensions( topDp: Float = 0f, endDp: Float = 0f, bottomDp: Float = 0f, - ): MutableDimensions = apply { - this.startDp = startDp - this.topDp = topDp - this.endDp = endDp - this.bottomDp = bottomDp - } + ): MutableDimensions = + apply { + this.startDp = startDp + this.topDp = topDp + this.endDp = endDp + this.bottomDp = bottomDp + } /** * Evenly distributes the provided measurement between [startDp] and [endDp]. */ - public fun setHorizontal(valueDp: Float): MutableDimensions = apply { - startDp = if (valueDp == 0f) valueDp else valueDp / 2 - endDp = if (valueDp == 0f) valueDp else valueDp / 2 - } + public fun setHorizontal(valueDp: Float): MutableDimensions = + apply { + startDp = if (valueDp == 0f) valueDp else valueDp / 2 + endDp = if (valueDp == 0f) valueDp else valueDp / 2 + } /** * Evenly distributes the provided measurement between [topDp] and [bottomDp]. */ - public fun setVertical(valueDp: Float): MutableDimensions = apply { - topDp = if (valueDp == 0f) valueDp else valueDp / 2 - bottomDp = if (valueDp == 0f) valueDp else valueDp / 2 - } + public fun setVertical(valueDp: Float): MutableDimensions = + apply { + topDp = if (valueDp == 0f) valueDp else valueDp / 2 + bottomDp = if (valueDp == 0f) valueDp else valueDp / 2 + } /** * Sets all coordinates to 0. 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 29f24a168..14c31b544 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 @@ -47,26 +47,30 @@ public fun drawContext( isLtr: Boolean = true, elevationOverlayColor: Long = DefaultColors.Light.elevationOverlayColor, spToPx: (Float) -> Float = { it }, -): DrawContext = object : DrawContext, Extras by DefaultExtras() { - override val canvasBounds: RectF = RectF(0f, 0f, canvas.width.toFloat(), canvas.height.toFloat()) - override val elevationOverlayColor: Long = elevationOverlayColor - override var canvas: Canvas = canvas - override val density: Float = density - override val isLtr: Boolean = isLtr - override val isHorizontalScrollEnabled: Boolean = false - override val chartValuesProvider: ChartValuesProvider = ChartValuesProvider.Empty - override val horizontalLayout: HorizontalLayout = HorizontalLayout.Segmented +): DrawContext = + object : DrawContext, Extras by DefaultExtras() { + override val canvasBounds: RectF = RectF(0f, 0f, canvas.width.toFloat(), canvas.height.toFloat()) + override val elevationOverlayColor: Long = elevationOverlayColor + override var canvas: Canvas = canvas + override val density: Float = density + override val isLtr: Boolean = isLtr + override val isHorizontalScrollEnabled: Boolean = false + override val chartValuesProvider: ChartValuesProvider = ChartValuesProvider.Empty + override val horizontalLayout: HorizontalLayout = HorizontalLayout.Segmented - override fun withOtherCanvas(canvas: Canvas, block: (DrawContext) -> Unit) { - val originalCanvas = this.canvas - this.canvas = canvas - block(this) - this.canvas = originalCanvas - } + override fun withOtherCanvas( + canvas: Canvas, + block: (DrawContext) -> Unit, + ) { + val originalCanvas = this.canvas + this.canvas = canvas + block(this) + this.canvas = originalCanvas + } - override fun reset() { - clearExtras() - } + override fun reset() { + clearExtras() + } - override fun spToPx(sp: Float): Float = spToPx(sp) -} + override fun spToPx(sp: Float): Float = spToPx(sp) + } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntry.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntry.kt index 6996a1200..eb0d12fe9 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntry.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntry.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. @@ -23,7 +23,6 @@ import com.patrykandpatrick.vico.core.chart.Chart * It holds information about the location of the chart entry on the x-axis and y-axis. */ public interface ChartEntry { - /** * The position of this [ChartEntry] on the x-axis. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryExtensions.kt index 135e4cb8f..ee743a1b6 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryExtensions.kt @@ -29,7 +29,10 @@ import kotlin.math.abs * * @see [entriesOf] */ -public fun entryOf(x: Float, y: Float): FloatEntry = FloatEntry(x, y) +public fun entryOf( + x: Float, + y: Float, +): FloatEntry = FloatEntry(x, y) /** * Creates a [FloatEntry] instance. @@ -39,7 +42,10 @@ public fun entryOf(x: Float, y: Float): FloatEntry = FloatEntry(x, y) * * @see [entriesOf] */ -public fun entryOf(x: Number, y: Number): FloatEntry = entryOf(x.toFloat(), y.toFloat()) +public fun entryOf( + x: Number, + y: Number, +): FloatEntry = entryOf(x.toFloat(), y.toFloat()) /** * Creates a [List] of [FloatEntry] instances. Each of the provided [Pair]s corresponds to a single [FloatEntry], with @@ -56,8 +62,7 @@ public fun entryOf(x: Number, y: Number): FloatEntry = entryOf(x.toFloat(), y.to * * @see [entryOf] */ -public fun entriesOf(vararg pairs: Pair): List = - pairs.map { (x, y) -> entryOf(x, y) } +public fun entriesOf(vararg pairs: Pair): List = pairs.map { (x, y) -> entryOf(x, y) } /** * Creates a [List] of [FloatEntry] instances out of an array of y-axis values. @@ -79,8 +84,7 @@ public fun entriesOf(vararg pairs: Pair): List = * * @see [entryOf] */ -public fun entriesOf(vararg yValues: Number): List = - yValues.mapIndexed { index, y -> entryOf(index, y) } +public fun entriesOf(vararg yValues: Number): List = yValues.mapIndexed { index, y -> entryOf(index, y) } internal inline val Iterable>.yRange: ClosedFloatingPointRange get() = flatten().rangeOfOrNull { it.y } ?: 0f..0f @@ -88,11 +92,14 @@ internal inline val Iterable>.yRange: ClosedFloatingPointRa internal inline val Iterable>.xRange: ClosedFloatingPointRange get() = flatten().rangeOfOrNull { it.x } ?: 0f..0f -internal fun Iterable>.calculateXGcd() = flatten() - .zipWithNext { firstEntry, secondEntry -> abs(secondEntry.x - firstEntry.x) } - .fold(null) { gcd, delta -> gcd?.gcdWith(delta) ?: delta } - ?.also { require(it != 0f) { "The precision of the x values is too large. The maximum is two decimal places." } } - ?: 1f +internal fun Iterable>.calculateXGcd() = + flatten() + .zipWithNext { firstEntry, secondEntry -> abs(secondEntry.x - firstEntry.x) } + .fold(null) { gcd, delta -> gcd?.gcdWith(delta) ?: delta } + ?.also { + require(it != 0f) { "The precision of the x values is too large. The maximum is two decimal places." } + } + ?: 1f internal fun Iterable>.calculateStackedYRange(): ClosedFloatingPointRange = flatten().fold(HashMap>()) { map, entry -> 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 5ace93f79..be8802d9c 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 @@ -39,7 +39,6 @@ import com.patrykandpatrick.vico.core.entry.diff.ExtraStore * @see [ComposedChartEntryModelProducer]. */ public interface ChartEntryModel { - /** * The [ChartEntryModel]’s identifier. Different [ChartEntryModel] instances don’t necessarily have different * identifiers. [ChartEntryModelProducer] and [ComposedChartEntryModelProducer] use the same [id] for all 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 9e63c2679..b40ca2634 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 @@ -46,7 +46,6 @@ public class ChartEntryModelProducer( entryCollections: List>, dispatcher: CoroutineDispatcher = Dispatchers.Default, ) : ChartModelProducer { - private var series = emptyList>() private var cachedInternalModel: InternalModel? = null private val mutex = Mutex() @@ -69,14 +68,18 @@ public class ChartEntryModelProducer( * behavior, use [setEntriesSuspending]. [updateExtras] allows for adding auxiliary data, which can later be * retrieved via [ChartEntryModel.extraStore]. */ - public fun setEntries(entries: List>, updateExtras: (MutableExtraStore) -> Unit = {}): Boolean { + public fun setEntries( + entries: List>, + updateExtras: (MutableExtraStore) -> Unit = {}, + ): Boolean { if (!mutex.tryLock()) return false series = entries.copy() updateExtras(extraStore) cachedInternalModel = null - val deferredUpdates = updateReceivers.values.map { updateReceiver -> - coroutineScope.async { updateReceiver.handleUpdate() } - } + val deferredUpdates = + updateReceivers.values.map { updateReceiver -> + coroutineScope.async { updateReceiver.handleUpdate() } + } coroutineScope.launch { deferredUpdates.awaitAll() mutex.unlock() @@ -99,9 +102,10 @@ public class ChartEntryModelProducer( updateExtras(extraStore) cachedInternalModel = null val completableDeferred = CompletableDeferred() - val deferredUpdates = updateReceivers.values.map { updateReceiver -> - coroutineScope.async { updateReceiver.handleUpdate() } - } + val deferredUpdates = + updateReceivers.values.map { updateReceiver -> + coroutineScope.async { updateReceiver.handleUpdate() } + } coroutineScope.launch { deferredUpdates.awaitAll() mutex.unlock() @@ -116,8 +120,10 @@ public class ChartEntryModelProducer( * behavior, use [setEntriesSuspending]. [updateExtras] allows for adding auxiliary data, which can later be * retrieved via [ChartEntryModel.extraStore]. */ - public fun setEntries(vararg entries: List, updateExtras: (MutableExtraStore) -> Unit = {}): Boolean = - setEntries(entries.toList(), updateExtras) + public fun setEntries( + vararg entries: List, + updateExtras: (MutableExtraStore) -> Unit = {}, + ): Boolean = setEntries(entries.toList(), updateExtras) /** * Updates the data set. Unlike [setEntries], this function suspends the current coroutine and waits until an update @@ -157,7 +163,10 @@ public class ChartEntryModelProducer( override fun getModel(): ChartEntryModel? = getInternalModel() - override suspend fun transformModel(key: Any, fraction: Float) { + override suspend fun transformModel( + key: Any, + fraction: Float, + ) { with(updateReceivers[key] ?: return) { modelTransformer?.transform(extraStore, fraction) val internalModel = getInternalModel(extraStore.copy()) 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 8f35b4e5b..fef077573 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 @@ -29,7 +29,6 @@ import com.patrykandpatrick.vico.core.entry.diff.MutableExtraStore * @see ComposedChartEntryModelProducer */ public interface ChartModelProducer { - /** * Returns the [ChartEntryModel] or, if no [ChartEntryModel] is available, `null`. */ @@ -44,7 +43,10 @@ public interface ChartModelProducer { * Creates an intermediate [ChartEntryModel] for difference animations. [fraction] is the balance between the * initial and target [ChartEntryModel]s. */ - public suspend fun transformModel(key: Any, fraction: Float) + public suspend fun transformModel( + key: Any, + fraction: Float, + ) /** * Registers an update listener associated with a [key]. [cancelAnimation] and [startAnimation] are diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/FloatEntry.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/FloatEntry.kt index 469fbb097..2fc45a206 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/FloatEntry.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/FloatEntry.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. @@ -23,9 +23,9 @@ public data class FloatEntry( override val x: Float, override val y: Float, ) : ChartEntry { - - override fun withY(y: Float): ChartEntry = FloatEntry( - x = x, - y = y, - ) + override fun withY(y: Float): ChartEntry = + FloatEntry( + x = x, + y = y, + ) } 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 885692e65..acaa5d8f4 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 @@ -52,292 +52,305 @@ import kotlinx.coroutines.sync.Mutex */ public class ComposedChartEntryModelProducer private constructor(dispatcher: CoroutineDispatcher) : ChartModelProducer> { + private var dataSets = emptyList>>() + private var cachedInternalComposedModel: InternalComposedModel? = null + private val mutex = Mutex() + private val coroutineScope = CoroutineScope(dispatcher) + private val updateReceivers = mutableMapOf() + private val extraStore = MutableExtraStore() - private var dataSets = emptyList>>() - private var cachedInternalComposedModel: InternalComposedModel? = null - private val mutex = Mutex() - private val coroutineScope = CoroutineScope(dispatcher) - private val updateReceivers = mutableMapOf() - private val extraStore = MutableExtraStore() - - private fun setDataSets(dataSets: List>>): Boolean { - if (!mutex.tryLock()) return false - this.dataSets = dataSets.copy() - cachedInternalComposedModel = null - val deferredUpdates = updateReceivers.values.map { updateReceiver -> - coroutineScope.async { updateReceiver.handleUpdate() } - } - coroutineScope.launch { - deferredUpdates.awaitAll() - mutex.unlock() + private fun setDataSets(dataSets: List>>): Boolean { + if (!mutex.tryLock()) return false + this.dataSets = dataSets.copy() + cachedInternalComposedModel = null + val deferredUpdates = + updateReceivers.values.map { updateReceiver -> + coroutineScope.async { updateReceiver.handleUpdate() } + } + coroutineScope.launch { + deferredUpdates.awaitAll() + mutex.unlock() + } + return true } - return true - } - private suspend fun setDataSetsSuspending(dataSets: List>>): Deferred { - mutex.lock() - this.dataSets = dataSets.copy() - cachedInternalComposedModel = null - val completableDeferred = CompletableDeferred() - val deferredUpdates = updateReceivers.values.map { updateReceiver -> - coroutineScope.async { updateReceiver.handleUpdate() } - } - coroutineScope.launch { - deferredUpdates.awaitAll() - mutex.unlock() - completableDeferred.complete(Unit) + private suspend fun setDataSetsSuspending(dataSets: List>>): Deferred { + mutex.lock() + this.dataSets = dataSets.copy() + cachedInternalComposedModel = null + val completableDeferred = CompletableDeferred() + val deferredUpdates = + updateReceivers.values.map { updateReceiver -> + coroutineScope.async { updateReceiver.handleUpdate() } + } + coroutineScope.launch { + deferredUpdates.awaitAll() + mutex.unlock() + completableDeferred.complete(Unit) + } + return completableDeferred } - return completableDeferred - } - private fun getInternalModel(extraStore: ExtraStore? = null) = - if (dataSets.isEmpty()) { - null - } else { - val mergedExtraStore = this.extraStore.let { if (extraStore != null) it + extraStore else it } - cachedInternalComposedModel - ?.let { composedModel -> - composedModel.copy( - composedEntryCollections = composedModel.composedEntryCollections - .map { model -> model.copy(extraStore = mergedExtraStore) }, - extraStore = mergedExtraStore, - ) - } - ?: run { - val models = dataSets.map { dataSet -> - val xRange = dataSet.xRange - val yRange = dataSet.yRange - val aggregateYRange = dataSet.calculateStackedYRange() - InternalModel( - entries = dataSet, - minX = xRange.start, - maxX = xRange.endInclusive, - minY = yRange.start, - maxY = yRange.endInclusive, - stackedPositiveY = aggregateYRange.endInclusive, - stackedNegativeY = aggregateYRange.start, - xGcd = dataSet.calculateXGcd(), + private fun getInternalModel(extraStore: ExtraStore? = null) = + if (dataSets.isEmpty()) { + null + } else { + val mergedExtraStore = this.extraStore.let { if (extraStore != null) it + extraStore else it } + cachedInternalComposedModel + ?.let { composedModel -> + composedModel.copy( + composedEntryCollections = + composedModel.composedEntryCollections + .map { model -> model.copy(extraStore = mergedExtraStore) }, extraStore = mergedExtraStore, ) } - InternalComposedModel( - composedEntryCollections = models, - entries = models.map { it.entries }.flatten(), - minX = models.minOf { it.minX }, - maxX = models.maxOf { it.maxX }, - minY = models.minOf { it.minY }, - maxY = models.maxOf { it.maxY }, - stackedPositiveY = models.maxOf { it.stackedPositiveY }, - stackedNegativeY = models.minOf { it.stackedNegativeY }, - xGcd = models.fold(null) { gcd, model -> - gcd?.gcdWith(model.xGcd) ?: model.xGcd - } ?: 1f, - id = models.map { it.id }.hashCode(), - extraStore = mergedExtraStore, - ).also { cachedInternalComposedModel = it } - } - } + ?: run { + val models = + dataSets.map { dataSet -> + val xRange = dataSet.xRange + val yRange = dataSet.yRange + val aggregateYRange = dataSet.calculateStackedYRange() + InternalModel( + entries = dataSet, + minX = xRange.start, + maxX = xRange.endInclusive, + minY = yRange.start, + maxY = yRange.endInclusive, + stackedPositiveY = aggregateYRange.endInclusive, + stackedNegativeY = aggregateYRange.start, + xGcd = dataSet.calculateXGcd(), + extraStore = mergedExtraStore, + ) + } + InternalComposedModel( + composedEntryCollections = models, + entries = models.map { it.entries }.flatten(), + minX = models.minOf { it.minX }, + maxX = models.maxOf { it.maxX }, + minY = models.minOf { it.minY }, + maxY = models.maxOf { it.maxY }, + stackedPositiveY = models.maxOf { it.stackedPositiveY }, + stackedNegativeY = models.minOf { it.stackedNegativeY }, + xGcd = + models.fold(null) { gcd, model -> + gcd?.gcdWith(model.xGcd) ?: model.xGcd + } ?: 1f, + id = models.map { it.id }.hashCode(), + extraStore = mergedExtraStore, + ).also { cachedInternalComposedModel = it } + } + } - override fun getModel(): ComposedChartEntryModel? = getInternalModel() + override fun getModel(): ComposedChartEntryModel? = getInternalModel() - override suspend fun transformModel(key: Any, fraction: Float) { - with(updateReceivers[key] ?: return) { - modelTransformer?.transform(extraStore, fraction) - val internalModel = getInternalModel(extraStore.copy()) - currentCoroutineContext().ensureActive() - onModelCreated(internalModel) + override suspend fun transformModel( + key: Any, + fraction: Float, + ) { + with(updateReceivers[key] ?: return) { + modelTransformer?.transform(extraStore, fraction) + val internalModel = getInternalModel(extraStore.copy()) + currentCoroutineContext().ensureActive() + onModelCreated(internalModel) + } } - } - @WorkerThread - override fun registerForUpdates( - key: Any, - cancelAnimation: () -> Unit, - startAnimation: (transformModel: suspend (chartKey: Any, fraction: Float) -> Unit) -> Unit, - getOldModel: () -> ComposedChartEntryModel?, - modelTransformerProvider: Chart.ModelTransformerProvider?, - extraStore: MutableExtraStore, - updateChartValues: (ComposedChartEntryModel?) -> ChartValuesProvider, - onModelCreated: (ComposedChartEntryModel?) -> Unit, - ) { - UpdateReceiver( - cancelAnimation, - startAnimation, - onModelCreated, - extraStore, - modelTransformerProvider?.getModelTransformer(), - getOldModel, - updateChartValues, - ).run { - updateReceivers[key] = this - handleUpdate() + @WorkerThread + override fun registerForUpdates( + key: Any, + cancelAnimation: () -> Unit, + startAnimation: (transformModel: suspend (chartKey: Any, fraction: Float) -> Unit) -> Unit, + getOldModel: () -> ComposedChartEntryModel?, + modelTransformerProvider: Chart.ModelTransformerProvider?, + extraStore: MutableExtraStore, + updateChartValues: (ComposedChartEntryModel?) -> ChartValuesProvider, + onModelCreated: (ComposedChartEntryModel?) -> Unit, + ) { + UpdateReceiver( + cancelAnimation, + startAnimation, + onModelCreated, + extraStore, + modelTransformerProvider?.getModelTransformer(), + getOldModel, + updateChartValues, + ).run { + updateReceivers[key] = this + handleUpdate() + } } - } - override fun isRegistered(key: Any): Boolean = updateReceivers.containsKey(key) + override fun isRegistered(key: Any): Boolean = updateReceivers.containsKey(key) - override fun unregisterFromUpdates(key: Any) { - updateReceivers.remove(key) - } - - /** - * Creates a [Transaction] instance. - */ - public fun createTransaction(): Transaction = Transaction() - - /** - * Creates a [Transaction], runs [block], and calls [Transaction.commit], returning its output. For suspending - * behavior, use [runTransactionSuspending]. - */ - public fun runTransaction(block: Transaction.() -> Unit): Boolean = createTransaction().also(block).commit() - - /** - * Creates a [Transaction], runs [block], and calls [Transaction.commitSuspending], returning its output. - */ - public suspend fun runTransactionSuspending(block: Transaction.() -> Unit): Deferred = - createTransaction().also(block).commitSuspending() - - /** - * Handles data updates. An initially empty list of data sets is created and can be updated via the class’s - * functions. Each data set corresponds to a single nested [Chart]. - */ - public inner class Transaction internal constructor() { - private val newDataSets = mutableListOf>>() - - /** - * Populates the new list of data sets with the current data sets. - */ - public fun populate() { - newDataSets.setAll(dataSets) + override fun unregisterFromUpdates(key: Any) { + updateReceivers.remove(key) } /** - * Replaces the data set at the specified index ([Pair.first]) with the provided data set ([Pair.second]). + * Creates a [Transaction] instance. */ - public fun set(pair: Pair>>) { - set(pair.first, pair.second) - } + public fun createTransaction(): Transaction = Transaction() /** - * Removes the data set at the specified index. + * Creates a [Transaction], runs [block], and calls [Transaction.commit], returning its output. For suspending + * behavior, use [runTransactionSuspending]. */ - public fun removeAt(index: Int) { - newDataSets.removeAt(index) - } + public fun runTransaction(block: Transaction.() -> Unit): Boolean = createTransaction().also(block).commit() /** - * Replaces the data set at the specified index with the provided data set. + * Creates a [Transaction], runs [block], and calls [Transaction.commitSuspending], returning its output. */ - public fun set(index: Int, dataSet: List>) { - newDataSets[index] = dataSet - } + public suspend fun runTransactionSuspending(block: Transaction.() -> Unit): Deferred = + createTransaction().also(block).commitSuspending() /** - * Adds a data set. + * Handles data updates. An initially empty list of data sets is created and can be updated via the class’s + * functions. Each data set corresponds to a single nested [Chart]. */ - public fun add(dataSet: List>) { - newDataSets.add(dataSet) - } + public inner class Transaction internal constructor() { + private val newDataSets = mutableListOf>>() - /** - * Adds a data set. - */ - public fun add(index: Int, dataSet: List>) { - newDataSets.add(index, dataSet) - } + /** + * Populates the new list of data sets with the current data sets. + */ + public fun populate() { + newDataSets.setAll(dataSets) + } - /** - * Adds a data set comprising the provided series. - */ - public fun add(vararg series: List) { - add(series.toList()) - } + /** + * Replaces the data set at the specified index ([Pair.first]) with the provided data set ([Pair.second]). + */ + public fun set(pair: Pair>>) { + set(pair.first, pair.second) + } - /** - * Clears the new list of data sets. - */ - public fun clear() { - newDataSets.clear() - } + /** + * Removes the data set at the specified index. + */ + public fun removeAt(index: Int) { + newDataSets.removeAt(index) + } - /** - * Allows for adding auxiliary values, which can later be retrieved via [ChartEntryModel.extraStore]. - */ - public fun updateExtras(block: (MutableExtraStore) -> Unit) { - block(extraStore) - } + /** + * Replaces the data set at the specified index with the provided data set. + */ + public fun set( + index: Int, + dataSet: List>, + ) { + newDataSets[index] = dataSet + } - /** - * Requests a data update. If the update is accepted, `true` is returned. If the update is rejected, which - * occurs when there’s already an update in progress, `false` is returned. For suspending behavior, use - * [commitSuspending]. - */ - public fun commit(): Boolean = setDataSets(newDataSets) + /** + * Adds a data set. + */ + public fun add(dataSet: List>) { + newDataSets.add(dataSet) + } - /** - * Runs a data update. Unlike [commit], this function suspends the current coroutine and waits until an update - * can be run, meaning the update cannot be rejected. The returned [Deferred] implementation is marked as - * completed once the update has been processed. - */ - public suspend fun commitSuspending(): Deferred = setDataSetsSuspending(newDataSets) - } + /** + * Adds a data set. + */ + public fun add( + index: Int, + dataSet: List>, + ) { + newDataSets.add(index, dataSet) + } + + /** + * Adds a data set comprising the provided series. + */ + public fun add(vararg series: List) { + add(series.toList()) + } + + /** + * Clears the new list of data sets. + */ + public fun clear() { + newDataSets.clear() + } + + /** + * Allows for adding auxiliary values, which can later be retrieved via [ChartEntryModel.extraStore]. + */ + public fun updateExtras(block: (MutableExtraStore) -> Unit) { + block(extraStore) + } + + /** + * Requests a data update. If the update is accepted, `true` is returned. If the update is rejected, which + * occurs when there’s already an update in progress, `false` is returned. For suspending behavior, use + * [commitSuspending]. + */ + public fun commit(): Boolean = setDataSets(newDataSets) - private inner class UpdateReceiver( - val cancelAnimation: () -> Unit, - val startAnimation: (transformModel: suspend (chartKey: Any, fraction: Float) -> Unit) -> Unit, - val onModelCreated: (ComposedChartEntryModel?) -> Unit, - val extraStore: MutableExtraStore, - val modelTransformer: Chart.ModelTransformer>?, - val getOldModel: () -> ComposedChartEntryModel?, - val updateChartValues: (ComposedChartEntryModel?) -> ChartValuesProvider, - ) { - fun handleUpdate() { - cancelAnimation() - modelTransformer?.prepareForTransformation( - oldModel = getOldModel(), - newModel = getModel(), - extraStore = extraStore, - chartValuesProvider = updateChartValues(getModel()), - ) - startAnimation(::transformModel) + /** + * Runs a data update. Unlike [commit], this function suspends the current coroutine and waits until an update + * can be run, meaning the update cannot be rejected. The returned [Deferred] implementation is marked as + * completed once the update has been processed. + */ + public suspend fun commitSuspending(): Deferred = setDataSetsSuspending(newDataSets) } - } - private data class InternalModel( - override val entries: List>, - override val minX: Float, - override val maxX: Float, - override val minY: Float, - override val maxY: Float, - override val stackedPositiveY: Float, - override val stackedNegativeY: Float, - override val xGcd: Float, - override val extraStore: ExtraStore, - ) : ChartEntryModel + private inner class UpdateReceiver( + val cancelAnimation: () -> Unit, + val startAnimation: (transformModel: suspend (chartKey: Any, fraction: Float) -> Unit) -> Unit, + val onModelCreated: (ComposedChartEntryModel?) -> Unit, + val extraStore: MutableExtraStore, + val modelTransformer: Chart.ModelTransformer>?, + val getOldModel: () -> ComposedChartEntryModel?, + val updateChartValues: (ComposedChartEntryModel?) -> ChartValuesProvider, + ) { + fun handleUpdate() { + cancelAnimation() + modelTransformer?.prepareForTransformation( + oldModel = getOldModel(), + newModel = getModel(), + extraStore = extraStore, + chartValuesProvider = updateChartValues(getModel()), + ) + startAnimation(::transformModel) + } + } - private data class InternalComposedModel( - override val composedEntryCollections: List, - override val entries: List>, - override val minX: Float, - override val maxX: Float, - override val minY: Float, - override val maxY: Float, - override val stackedPositiveY: Float, - override val stackedNegativeY: Float, - override val xGcd: Float, - override val id: Int, - override val extraStore: ExtraStore, - ) : ComposedChartEntryModel + private data class InternalModel( + override val entries: List>, + override val minX: Float, + override val maxX: Float, + override val minY: Float, + override val maxY: Float, + override val stackedPositiveY: Float, + override val stackedNegativeY: Float, + override val xGcd: Float, + override val extraStore: ExtraStore, + ) : ChartEntryModel - public companion object { - /** - * Creates a [ComposedChartEntryModelProducer], running an initial [Transaction]. [dispatcher] is the - * [CoroutineDispatcher] to be used for update handling. - */ - public fun build( - dispatcher: CoroutineDispatcher = Dispatchers.Default, - transaction: Transaction.() -> Unit = {}, - ): ComposedChartEntryModelProducer = - ComposedChartEntryModelProducer(dispatcher).also { it.runTransaction(transaction) } + private data class InternalComposedModel( + override val composedEntryCollections: List, + override val entries: List>, + override val minX: Float, + override val maxX: Float, + override val minY: Float, + override val maxY: Float, + override val stackedPositiveY: Float, + override val stackedNegativeY: Float, + override val xGcd: Float, + override val id: Int, + override val extraStore: ExtraStore, + ) : ComposedChartEntryModel + + public companion object { + /** + * Creates a [ComposedChartEntryModelProducer], running an initial [Transaction]. [dispatcher] is the + * [CoroutineDispatcher] to be used for update handling. + */ + public fun build( + dispatcher: CoroutineDispatcher = Dispatchers.Default, + transaction: Transaction.() -> Unit = {}, + ): ComposedChartEntryModelProducer = + ComposedChartEntryModelProducer(dispatcher).also { it.runTransaction(transaction) } + } } -} diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DefaultDrawingModelInterpolator.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DefaultDrawingModelInterpolator.kt index e0518b847..fea534420 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DefaultDrawingModelInterpolator.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DefaultDrawingModelInterpolator.kt @@ -27,12 +27,14 @@ import kotlin.math.max @Suppress("UNCHECKED_CAST") public class DefaultDrawingModelInterpolator> : DrawingModelInterpolator { - private var transformationMaps = emptyList>>() private var oldDrawingModel: R? = null private var newDrawingModel: R? = null - override fun setModels(old: R?, new: R?) { + override fun setModels( + old: R?, + new: R?, + ) { synchronized(this) { oldDrawingModel = old newDrawingModel = new @@ -40,33 +42,36 @@ public class DefaultDrawingModelInterpolator - map - .mapNotNull { (x, model) -> - currentCoroutineContext().ensureActive() - model.transform(fraction)?.let { drawingInfo -> x to drawingInfo } - } - .takeIf { list -> list.isNotEmpty() } - ?.toMap() - }, - from = oldDrawingModel, - fraction = fraction, - ) as R? + override suspend fun transform(fraction: Float): R? = + newDrawingModel?.transform( + drawingInfo = + transformationMaps.mapNotNull { map -> + map + .mapNotNull { (x, model) -> + currentCoroutineContext().ensureActive() + model.transform(fraction)?.let { drawingInfo -> x to drawingInfo } + } + .takeIf { list -> list.isNotEmpty() } + ?.toMap() + }, + from = oldDrawingModel, + fraction = fraction, + ) as R? private fun updateTransformationMap() { - transformationMaps = buildList { - repeat(max(oldDrawingModel?.size.orZero, newDrawingModel?.size.orZero)) { index -> - val map = mutableMapOf>() - oldDrawingModel - ?.getOrNull(index) - ?.forEach { (x, drawingInfo) -> map[x] = TransformationModel(drawingInfo) } - newDrawingModel - ?.getOrNull(index) - ?.forEach { (x, drawingInfo) -> map[x] = TransformationModel(map[x]?.old, drawingInfo) } - add(map) + transformationMaps = + buildList { + repeat(max(oldDrawingModel?.size.orZero, newDrawingModel?.size.orZero)) { index -> + val map = mutableMapOf>() + oldDrawingModel + ?.getOrNull(index) + ?.forEach { (x, drawingInfo) -> map[x] = TransformationModel(drawingInfo) } + newDrawingModel + ?.getOrNull(index) + ?.forEach { (x, drawingInfo) -> map[x] = TransformationModel(map[x]?.old, drawingInfo) } + add(map) + } } - } } private class TransformationModel(val old: T?, val new: T? = null) { diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DrawingModel.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DrawingModel.kt index b02ffbfaf..a6b613409 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DrawingModel.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DrawingModel.kt @@ -23,7 +23,6 @@ import com.patrykandpatrick.vico.core.chart.Chart */ public abstract class DrawingModel(private val drawingInfo: List>) : List> by drawingInfo { - /** * Returns an intermediate [DrawingModel] between this one and [from]. The returned drawing model includes the * provided [DrawingInfo] list. [fraction] is the balance between [from] and this [DrawingModel], with 0 @@ -46,6 +45,9 @@ public abstract class DrawingModel(private val dra * this [DrawingInfo] implementation. The returned object should be an instance of the [DrawingInfo] * implementation to which this function belongs. */ - public fun transform(from: DrawingInfo?, fraction: Float): DrawingInfo + public fun transform( + from: DrawingInfo?, + fraction: Float, + ): DrawingInfo } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DrawingModelInterpolator.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DrawingModelInterpolator.kt index 28668325b..0644ade2d 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DrawingModelInterpolator.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/diff/DrawingModelInterpolator.kt @@ -23,7 +23,10 @@ public interface DrawingModelInterpolator, Any /** * Saves the provided value to this [MutableExtraStore], associating the value with the given key. */ - public operator fun set(key: Key, value: T) { + public operator fun set( + key: Key, + value: T, + ) { mapDelegate[key] = value } @@ -96,10 +99,11 @@ public class MutableExtraStore internal constructor(mapDelegate: Map, Any destination.putAll(mapDelegate) } - override operator fun plus(other: ExtraStore): ExtraStore = MutableExtraStore( - buildMap { - putAll(mapDelegate) - other.copyContentTo(this) - }, - ) + override operator fun plus(other: ExtraStore): ExtraStore = + MutableExtraStore( + buildMap { + putAll(mapDelegate) + other.copyContentTo(this) + }, + ) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/CollectionExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/CollectionExtensions.kt index 8cc4fdcba..dfd99418c 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/CollectionExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/CollectionExtensions.kt @@ -65,8 +65,7 @@ private fun ArrayList>.ensureSize(size: Int) { /** * Creates a copy of this [ArrayList] and each child [ArrayList] contained in this [ArrayList]. */ -public fun ArrayList>.copy(): List> = - List(size) { index -> ArrayList(get(index)) } +public fun ArrayList>.copy(): List> = List(size) { index -> ArrayList(get(index)) } /** * Replaces all of the elements of this [MutableMap] with the elements of the provided map. @@ -116,11 +115,12 @@ internal fun Collection.findClosestPositiveValue(value: Float): Float? { if (isEmpty()) return null var closestValue: Float? = null forEach { checkedValue -> - closestValue = when { - closestValue == null -> checkedValue - abs(closestValue!! - value) > abs(checkedValue - value) -> checkedValue - else -> closestValue - } + closestValue = + when { + closestValue == null -> checkedValue + abs(closestValue!! - value) > abs(checkedValue - value) -> checkedValue + else -> closestValue + } } return closestValue } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ColorExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ColorExtensions.kt index 5f2f1e1bb..49c0c4306 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ColorExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ColorExtensions.kt @@ -31,10 +31,11 @@ public fun Int.copyColor( red: Int = this.extractColorChannel(RED_BIT_SHIFT), green: Int = this.extractColorChannel(GREEN_BIT_SHIFT), blue: Int = this.extractColorChannel(BLUE_BIT_SHIFT), -): Int = alpha shl ALPHA_BIT_SHIFT or - (red shl RED_BIT_SHIFT) or - (green shl GREEN_BIT_SHIFT) or - (blue shl BLUE_BIT_SHIFT) +): Int = + alpha shl ALPHA_BIT_SHIFT or + (red shl RED_BIT_SHIFT) or + (green shl GREEN_BIT_SHIFT) or + (blue shl BLUE_BIT_SHIFT) /** * Copies this color, updating any or all of the color channels. @@ -44,17 +45,18 @@ public fun Int.copyColor( red: Float = this.extractColorChannel(RED_BIT_SHIFT) / MAX_HEX_VALUE, green: Float = this.extractColorChannel(GREEN_BIT_SHIFT) / MAX_HEX_VALUE, blue: Float = this.extractColorChannel(BLUE_BIT_SHIFT) / MAX_HEX_VALUE, -): Int = copyColor( - alpha = (alpha * MAX_HEX_VALUE).toInt(), - red = (red * MAX_HEX_VALUE).toInt(), - green = (green * MAX_HEX_VALUE).toInt(), - blue = (blue * MAX_HEX_VALUE).toInt(), -) +): Int = + copyColor( + alpha = (alpha * MAX_HEX_VALUE).toInt(), + red = (red * MAX_HEX_VALUE).toInt(), + green = (green * MAX_HEX_VALUE).toInt(), + blue = (blue * MAX_HEX_VALUE).toInt(), + ) /** * The hex code for this color. */ -@Suppress("MagicNumber", "ImplicitDefaultLocale") +@Suppress("ImplicitDefaultLocale") public val Int.colorHex: String get() = String.format("#%08X", 0xFFFFFFFF and this.toLong()) @@ -64,5 +66,4 @@ public val Int.colorHex: String public val Int.alpha: Int get() = extractColorChannel(ALPHA_BIT_SHIFT) -private fun Int.extractColorChannel(bitShift: Int): Int = - this shr bitShift and COLOR_MASK +private fun Int.extractColorChannel(bitShift: Int): Int = this shr bitShift and COLOR_MASK diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ColorOverlayExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ColorOverlayExtensions.kt index ea3534f33..c72536dfb 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ColorOverlayExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ColorOverlayExtensions.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. @@ -19,23 +19,23 @@ package com.patrykandpatrick.vico.core.extension import android.graphics.Color import com.patrykandpatrick.vico.core.context.DrawContext -@Suppress("MagicNumber") private fun getElevationOverlayColorWithCorrectAlpha( elevationOverlayColor: Long, elevationDp: Float, ): Int { - val overlayPercentage = when { - elevationDp < 1f -> 0.00f - elevationDp < 2f -> 0.05f - elevationDp < 3f -> 0.07f - elevationDp < 4f -> 0.08f - elevationDp < 6f -> 0.09f - elevationDp < 8f -> 0.11f - elevationDp < 12f -> 0.12f - elevationDp < 16f -> 0.14f - elevationDp < 24f -> 0.15f - else -> 0.16f - } + val overlayPercentage = + when { + elevationDp < 1f -> 0.00f + elevationDp < 2f -> 0.05f + elevationDp < 3f -> 0.07f + elevationDp < 4f -> 0.08f + elevationDp < 6f -> 0.09f + elevationDp < 8f -> 0.11f + elevationDp < 12f -> 0.12f + elevationDp < 16f -> 0.14f + elevationDp < 24f -> 0.15f + else -> 0.16f + } return if (elevationOverlayColor.alpha == 0f) { Color.TRANSPARENT } else { @@ -47,7 +47,10 @@ private fun getElevationOverlayColorWithCorrectAlpha( * Overlays the given [color] with [DrawContext.elevationOverlayColor], changing the opacity of * [DrawContext.elevationOverlayColor] depending on the value of [elevationDp]. */ -public fun DrawContext.applyElevationOverlayToColor(color: Int, elevationDp: Float): Int = +public fun DrawContext.applyElevationOverlayToColor( + color: Int, + elevationDp: Float, +): Int = color.overlayColor( getElevationOverlayColorWithCorrectAlpha( elevationOverlayColor = elevationOverlayColor, diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ColorUtils.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ColorUtils.kt index 445ebca42..050067761 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ColorUtils.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ColorUtils.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. @@ -20,7 +20,6 @@ import android.graphics.Color private const val BYTE_MAX_VALUE: Int = 0xFF -@Suppress("MagicNumber") internal val Long.alpha: Float get() = if (this and 0x3fL == 0L) (this shr 56 and 0xff) / 255.0f else (this shr 6 and 0x3ff) / 1023.0f @@ -34,8 +33,15 @@ internal fun Int.overlayColor(overlayingColor: Int): Int { return Color.argb(alpha, red, green, blue) } -private fun compositeAlpha(foregroundAlpha: Int, backgroundAlpha: Int): Int = - BYTE_MAX_VALUE - (BYTE_MAX_VALUE - backgroundAlpha) * (BYTE_MAX_VALUE - foregroundAlpha) / BYTE_MAX_VALUE +private fun compositeAlpha( + foregroundAlpha: Int, + backgroundAlpha: Int, +): Int = BYTE_MAX_VALUE - (BYTE_MAX_VALUE - backgroundAlpha) * (BYTE_MAX_VALUE - foregroundAlpha) / BYTE_MAX_VALUE -private fun compositeComponent(fgC: Int, fgA: Int, bgC: Int, bgA: Int, a: Int): Int = - if (a == 0) 0 else (BYTE_MAX_VALUE * fgC * fgA + bgC * bgA * (BYTE_MAX_VALUE - fgA)) / (a * BYTE_MAX_VALUE) +private fun compositeComponent( + fgC: Int, + fgA: Int, + bgC: Int, + bgA: Int, + a: Int, +): Int = if (a == 0) 0 else (BYTE_MAX_VALUE * fgC * fgA + bgC * bgA * (BYTE_MAX_VALUE - fgA)) / (a * BYTE_MAX_VALUE) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/CommonExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/CommonExtensions.kt index ca3c20c6f..881360f2e 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/CommonExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/CommonExtensions.kt @@ -20,7 +20,11 @@ package com.patrykandpatrick.vico.core.extension * Calls the specified function block with [t1] and [t2] as its arguments if [t1] and [t2] are not null. * Returns the function block’s result if it was called, and `null` if it wasn’t. */ -public inline fun ifNotNull(t1: T1?, t2: T2?, onNotNull: (T1, T2) -> R): R? = +public inline fun ifNotNull( + t1: T1?, + t2: T2?, + onNotNull: (T1, T2) -> R, +): R? = if (t1 != null && t2 != null) { onNotNull(t1, t2) } else { @@ -31,7 +35,12 @@ public inline fun ifNotNull(t1: T1?, t2: T2?, onNotNull: (T1, T2) -> * Calls the specified function block with [t1], [t2], and [t3] as its arguments if [t1], [t2], and [t3] are not null. * Returns the function block’s result if it was called, and `null` if it wasn’t. */ -public inline fun ifNotNull(t1: T1?, t2: T2?, t3: T3?, onNotNull: (T1, T2, T3) -> R): R? = +public inline fun ifNotNull( + t1: T1?, + t2: T2?, + t3: T3?, + onNotNull: (T1, T2, T3) -> R, +): R? = if (t1 != null && t2 != null && t3 != null) { onNotNull(t1, t2, t3) } else { diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/DrawableExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/DrawableExtensions.kt index 553719e5f..43391ba31 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/DrawableExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/DrawableExtensions.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. @@ -21,6 +21,11 @@ import android.graphics.drawable.Drawable /** * Sets a bounding box for this [Drawable]. */ -public fun Drawable.setBounds(left: Float, top: Float, right: Float, bottom: Float) { +public fun Drawable.setBounds( + left: Float, + top: Float, + right: Float, + bottom: Float, +) { setBounds(left.toInt(), top.toInt(), right.toInt(), bottom.toInt()) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/MapExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/MapExtensions.kt index dee58c98f..bc8a449e2 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/MapExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/MapExtensions.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. @@ -38,12 +38,11 @@ public fun Map>.getClosestMarkerEntryModel( * * @see Marker.EntryModel */ -public fun Map>.getEntryModel( - xValue: Float, -): List? = values - .mapNotNull { entries -> entries.takeIf { it.firstOrNull()?.entry?.x == xValue } } - .flatten() - .takeIf { it.isNotEmpty() } +public fun Map>.getEntryModel(xValue: Float): List? = + values + .mapNotNull { entries -> entries.takeIf { it.firstOrNull()?.entry?.x == xValue } } + .flatten() + .takeIf { it.isNotEmpty() } /** * Updates the receiver [TreeMap] with the contents of another [Map]. diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/NumberExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/NumberExtensions.kt index 0869b477b..8e5e5de53 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/NumberExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/NumberExtensions.kt @@ -34,16 +34,21 @@ private fun Float.round(decimals: Int): Float { return (this * multiplier).round / multiplier } -private fun Float.gcdWithImpl(other: Float, threshold: Float): Float = when { - this < other -> other.gcdWithImpl(other = this, threshold = threshold) - abs(x = other) < threshold -> this - else -> other.gcdWithImpl(other = this - (this / other).floor * other, threshold = threshold) -} - -internal fun Float.gcdWith(other: Float): Float = gcdWithImpl( - other = other, - threshold = 10f.pow(n = -FLOAT_GCD_DECIMALS - 1), -).round(decimals = FLOAT_GCD_DECIMALS) +private fun Float.gcdWithImpl( + other: Float, + threshold: Float, +): Float = + when { + this < other -> other.gcdWithImpl(other = this, threshold = threshold) + abs(x = other) < threshold -> this + else -> other.gcdWithImpl(other = this - (this / other).floor * other, threshold = threshold) + } + +internal fun Float.gcdWith(other: Float): Float = + gcdWithImpl( + other = other, + threshold = 10f.pow(n = -FLOAT_GCD_DECIMALS - 1), + ).round(decimals = FLOAT_GCD_DECIMALS) internal fun Float.toPrettyString(): String = if (this < 0f) "−${-this}" else this.toString() @@ -144,4 +149,7 @@ public fun firstNonNegativeOf(vararg floats: Float): Float? = floats.firstOrNull public fun Float.rangeWith(other: Float): ClosedFloatingPointRange = if (other > this) this..other else other..this -internal fun Float.lerp(to: Float, fraction: Float): Float = this + (to - this) * fraction +internal fun Float.lerp( + to: Float, + fraction: Float, +): Float = this + (to - this) * fraction diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/PaintExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/PaintExtensions.kt index 20a135b6d..86999ccb0 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/PaintExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/PaintExtensions.kt @@ -20,7 +20,10 @@ import android.graphics.Paint private val fm: Paint.FontMetrics = Paint.FontMetrics() -internal fun Paint.withOpacity(opacity: Float, action: (Paint) -> Unit) { +internal fun Paint.withOpacity( + opacity: Float, + action: (Paint) -> Unit, +) { val previousOpacity = this.alpha color = color.copyColor(opacity * previousOpacity / MAX_HEX_VALUE) action(this) @@ -48,5 +51,4 @@ public val Paint.textHeight: Float /** * Returns the width of the provided text. */ -public fun Paint.measureText(text: CharSequence): Float = - measureText(text, 0, text.length) +public fun Paint.measureText(text: CharSequence): Float = measureText(text, 0, text.length) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/RectExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/RectExtensions.kt index f35bb092a..1568525ed 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/RectExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/RectExtensions.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. @@ -127,12 +127,16 @@ public fun RectF.rotate(degrees: Float): RectF { /** * Moves this [RectF] horizontally and vertically by the specified distances. */ -public fun RectF.translate(x: Float, y: Float): RectF = apply { - left += x - top += y - right += x - bottom += y -} +public fun RectF.translate( + x: Float, + y: Float, +): RectF = + apply { + left += x + top += y + right += x + bottom += y + } /** * Returns [RectF.left] if [isLtr] is true, and [RectF.right] otherwise. diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ReflectionExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ReflectionExtensions.kt index 606831a39..337f6a640 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ReflectionExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/extension/ReflectionExtensions.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. @@ -20,7 +20,10 @@ package com.patrykandpatrick.vico.core.extension * Updates the value of the field with the specified name to the given value. * This involves temporarily setting the `accessible` flag to `true`. */ -public inline fun T.setFieldValue(fieldName: String, value: V) { +public inline fun T.setFieldValue( + fieldName: String, + value: V, +) { val field = T::class.java.getDeclaredField(fieldName) val wasAccessible = field.isAccessible field.isAccessible = true diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/formatter/DecimalFormatValueFormatter.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/formatter/DecimalFormatValueFormatter.kt index 195e70714..6fd60783b 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/formatter/DecimalFormatValueFormatter.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/formatter/DecimalFormatValueFormatter.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. @@ -24,7 +24,6 @@ import java.text.DecimalFormat * A [ValueFormatter] implementation that formats values using a [DecimalFormat] with a given pattern. */ public open class DecimalFormatValueFormatter(private val decimalFormat: DecimalFormat) : ValueFormatter { - /** * Creates a [DecimalFormatValueFormatter] using the default pattern. */ @@ -44,7 +43,6 @@ public open class DecimalFormatValueFormatter(private val decimalFormat: Decimal ): String = decimalFormat.format(value) public companion object { - /** * The default pattern for the [DecimalFormat]. */ @@ -56,8 +54,9 @@ public open class DecimalFormatValueFormatter(private val decimalFormat: Decimal public fun getDecimalFormat( pattern: String, roundingMode: RoundingMode, - ): DecimalFormat = DecimalFormat(pattern).apply { - this.roundingMode = roundingMode - } + ): DecimalFormat = + DecimalFormat(pattern).apply { + this.roundingMode = roundingMode + } } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/formatter/PercentageFormatValueFormatter.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/formatter/PercentageFormatValueFormatter.kt index 3f2340524..f591cde1c 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/formatter/PercentageFormatValueFormatter.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/formatter/PercentageFormatValueFormatter.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. @@ -26,7 +26,6 @@ import java.text.DecimalFormat * @param pattern the pattern used by [DecimalFormat] to format values as percentages. */ public open class PercentageFormatValueFormatter(pattern: String) : ValueFormatter { - private val decimalFormat = DecimalFormat(pattern) /** @@ -43,7 +42,6 @@ public open class PercentageFormatValueFormatter(pattern: String) : ValueFormatt } public companion object { - /** * The default percentage pattern. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/formatter/ValueFormatter.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/formatter/ValueFormatter.kt index e816afc40..7ec7da6ba 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/formatter/ValueFormatter.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/formatter/ValueFormatter.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. @@ -22,7 +22,6 @@ import com.patrykandpatrick.vico.core.chart.values.ChartValues * Formats values for display. */ public interface ValueFormatter { - /** * Called to format axis labels and data labels. * diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/layout/VirtualLayout.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/layout/VirtualLayout.kt index 74a5b37b9..40226c351 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/layout/VirtualLayout.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/layout/VirtualLayout.kt @@ -35,7 +35,6 @@ import com.patrykandpatrick.vico.core.legend.Legend public open class VirtualLayout( private val axisManager: AxisManager, ) { - private val tempInsetters = ArrayList(TEMP_INSETTERS_INITIAL_SIZE) private val finalInsets: Insets = Insets() @@ -61,56 +60,58 @@ public open class VirtualLayout( legend: Legend?, horizontalDimensions: HorizontalDimensions, vararg chartInsetter: ChartInsetter?, - ): RectF = with(context) { - tempInsetters.clear() - finalInsets.clear() - tempInsets.clear() - - val legendHeight = legend?.getHeight(context, contentBounds.width()).orZero - - axisManager.addInsetters(tempInsetters) - chartInsetter.filterNotNull().forEach(tempInsetters::add) - tempInsetters.addAll(chart.chartInsetters) - tempInsetters.add(chart) - - tempInsetters.forEach { insetter -> - insetter.getInsets(context, tempInsets, horizontalDimensions) - finalInsets.setValuesIfGreater(tempInsets) - } - - val availableHeight = contentBounds.height() - finalInsets.vertical - legendHeight - - tempInsetters.forEach { insetter -> - insetter.getHorizontalInsets(context, availableHeight, tempInsets) - finalInsets.setValuesIfGreater(tempInsets) + ): RectF = + with(context) { + tempInsetters.clear() + finalInsets.clear() + tempInsets.clear() + + val legendHeight = legend?.getHeight(context, contentBounds.width()).orZero + + axisManager.addInsetters(tempInsetters) + chartInsetter.filterNotNull().forEach(tempInsetters::add) + tempInsetters.addAll(chart.chartInsetters) + tempInsetters.add(chart) + + tempInsetters.forEach { insetter -> + insetter.getInsets(context, tempInsets, horizontalDimensions) + finalInsets.setValuesIfGreater(tempInsets) + } + + val availableHeight = contentBounds.height() - finalInsets.vertical - legendHeight + + tempInsetters.forEach { insetter -> + insetter.getHorizontalInsets(context, availableHeight, tempInsets) + finalInsets.setValuesIfGreater(tempInsets) + } + + val chartBounds = + RectF().apply { + left = contentBounds.left + finalInsets.getLeft(isLtr) + top = contentBounds.top + finalInsets.top + right = contentBounds.right - finalInsets.getRight(isLtr) + bottom = contentBounds.bottom - finalInsets.bottom - legendHeight + } + + chart.setBounds( + left = chartBounds.left, + top = chartBounds.top, + right = chartBounds.right, + bottom = chartBounds.bottom, + ) + + axisManager.setAxesBounds(context, contentBounds, chartBounds, finalInsets) + + legend?.setBounds( + left = contentBounds.left, + top = chart.bounds.bottom + finalInsets.bottom, + right = contentBounds.right, + bottom = chart.bounds.bottom + finalInsets.bottom + legendHeight, + ) + + chartBounds } - val chartBounds = RectF().apply { - left = contentBounds.left + finalInsets.getLeft(isLtr) - top = contentBounds.top + finalInsets.top - right = contentBounds.right - finalInsets.getRight(isLtr) - bottom = contentBounds.bottom - finalInsets.bottom - legendHeight - } - - chart.setBounds( - left = chartBounds.left, - top = chartBounds.top, - right = chartBounds.right, - bottom = chartBounds.bottom, - ) - - axisManager.setAxesBounds(context, contentBounds, chartBounds, finalInsets) - - legend?.setBounds( - left = contentBounds.left, - top = chart.bounds.bottom + finalInsets.bottom, - right = contentBounds.right, - bottom = chart.bounds.bottom + finalInsets.bottom + legendHeight, - ) - - chartBounds - } - private companion object { const val TEMP_INSETTERS_INITIAL_SIZE = 5 } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/HorizontalLegend.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/HorizontalLegend.kt index 4c31b7894..26efafd71 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/HorizontalLegend.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/HorizontalLegend.kt @@ -44,90 +44,99 @@ public open class HorizontalLegend( public var spacingDp: Float = 0f, override val padding: MutableDimensions = emptyDimensions(), ) : Legend, Padding { - private val heights = mutableListOf() private val lines = mutableListOf>(mutableListOf()) override val bounds: RectF = RectF() - override fun getHeight(context: MeasureContext, availableWidth: Float): Float = with(context) { - if (items.isEmpty()) return@with 0f - lines.clear() - lines.add(mutableListOf()) - var height = maxOf( - items.first().getLabelHeight(context, availableWidth, iconPaddingDp, iconSizeDp), - iconSizeDp.pixels, - ) - heights.add(height) - buildLines(context, availableWidth) { - val currentHeight = - maxOf(it.getLabelHeight(context, availableWidth, iconPaddingDp, iconSizeDp), iconSizeDp.pixels) - heights.add(currentHeight) - height += currentHeight - } - height + (lines.size - 1) * lineSpacingDp.pixels + padding.verticalDp.pixels - } - - override fun draw(context: ChartDrawContext): Unit = with(context) { - var currentTop = bounds.top + padding.topDp.pixels - // isLtr? startX means the line starts at X from left : it starts at X from right - val startX = if (isLtr) { - chartBounds.left + padding.startDp.pixels - } else { - chartBounds.right - padding.startDp.pixels - iconSizeDp.pixels - } - val availableWidth = chartBounds.width() - if (lines.isEmpty()) { - buildLines(context, availableWidth) - } - - lines.forEachIndexed { index, item -> - var currentStart = 0f - val currentLineHeight = heights.getOrElse(index) { - item.first().getLabelHeight(context, availableWidth, iconPaddingDp, iconSizeDp) + override fun getHeight( + context: MeasureContext, + availableWidth: Float, + ): Float = + with(context) { + if (items.isEmpty()) return@with 0f + lines.clear() + lines.add(mutableListOf()) + var height = + maxOf( + items.first().getLabelHeight(context, availableWidth, iconPaddingDp, iconSizeDp), + iconSizeDp.pixels, + ) + heights.add(height) + buildLines(context, availableWidth) { + val currentHeight = + maxOf(it.getLabelHeight(context, availableWidth, iconPaddingDp, iconSizeDp), iconSizeDp.pixels) + heights.add(currentHeight) + height += currentHeight } - val centerY = currentTop + currentLineHeight.half + height + (lines.size - 1) * lineSpacingDp.pixels + padding.verticalDp.pixels + } - item.forEach { - it.icon.draw( - context = context, - left = startX + currentStart, - top = centerY - iconSizeDp.half.pixels, - right = startX + iconSizeDp.pixels + currentStart, - bottom = centerY + iconSizeDp.half.pixels, - ) - currentStart += if (isLtr) { - (iconSizeDp + iconPaddingDp).pixels + override fun draw(context: ChartDrawContext): Unit = + with(context) { + var currentTop = bounds.top + padding.topDp.pixels + // isLtr? startX means the line starts at X from left : it starts at X from right + val startX = + if (isLtr) { + chartBounds.left + padding.startDp.pixels } else { - -iconPaddingDp.pixels + chartBounds.right - padding.startDp.pixels - iconSizeDp.pixels } - it.label.drawText( - context = context, - text = it.labelText, - textX = startX + currentStart, - textY = centerY, - horizontalPosition = HorizontalPosition.End, - verticalPosition = VerticalPosition.Center, - maxTextWidth = - (chartBounds.width() - (iconSizeDp + iconPaddingDp + padding.horizontalDp).pixels).toInt(), - ) - currentStart += if (isLtr) { - it.getLabelWidth(context, availableWidth, iconPaddingDp, iconSizeDp) + spacingDp.pixels - } else { - -( - it.getLabelWidth( - context, - availableWidth, - iconPaddingDp, - iconSizeDp, - ) + spacingDp.pixels + iconSizeDp.pixels - ) + val availableWidth = chartBounds.width() + if (lines.isEmpty()) { + buildLines(context, availableWidth) + } + + lines.forEachIndexed { index, item -> + var currentStart = 0f + val currentLineHeight = + heights.getOrElse(index) { + item.first().getLabelHeight(context, availableWidth, iconPaddingDp, iconSizeDp) + } + val centerY = currentTop + currentLineHeight.half + + item.forEach { + it.icon.draw( + context = context, + left = startX + currentStart, + top = centerY - iconSizeDp.half.pixels, + right = startX + iconSizeDp.pixels + currentStart, + bottom = centerY + iconSizeDp.half.pixels, + ) + currentStart += + if (isLtr) { + (iconSizeDp + iconPaddingDp).pixels + } else { + -iconPaddingDp.pixels + } + it.label.drawText( + context = context, + text = it.labelText, + textX = startX + currentStart, + textY = centerY, + horizontalPosition = HorizontalPosition.End, + verticalPosition = VerticalPosition.Center, + maxTextWidth = + (chartBounds.width() - (iconSizeDp + iconPaddingDp + padding.horizontalDp).pixels).toInt(), + ) + currentStart += + if (isLtr) { + it.getLabelWidth(context, availableWidth, iconPaddingDp, iconSizeDp) + spacingDp.pixels + } else { + -( + it.getLabelWidth( + context, + availableWidth, + iconPaddingDp, + iconSizeDp, + ) + spacingDp.pixels + iconSizeDp.pixels + ) + } } + currentTop += currentLineHeight + lineSpacingDp.pixels } - currentTop += currentLineHeight + lineSpacingDp.pixels } - } protected fun buildLines( context: MeasureContext, @@ -152,12 +161,13 @@ public open class HorizontalLegend( } currentLine++ - remainWidth = availableWidth - it.getWidth( - context, - availableWidth, - iconPaddingDp, - iconSizeDp, - ) - spacingDp.pixels + remainWidth = availableWidth - + it.getWidth( + context, + availableWidth, + iconPaddingDp, + iconSizeDp, + ) - spacingDp.pixels lines.add(mutableListOf(it)) callback(it) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/Legend.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/Legend.kt index 5daae0901..9e5956d17 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/Legend.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/Legend.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. @@ -24,11 +24,13 @@ import com.patrykandpatrick.vico.core.dimensions.BoundsAware * Defines the functions required by the library to draw a chart legend. */ public interface Legend : BoundsAware { - /** * Returns the height of the legend. */ - public fun getHeight(context: MeasureContext, availableWidth: Float): Float + public fun getHeight( + context: MeasureContext, + availableWidth: Float, + ): Float /** * Draws the legend. diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/LegendItem.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/LegendItem.kt index 4a6839870..901efe48f 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/LegendItem.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/LegendItem.kt @@ -45,13 +45,14 @@ public open class LegendItem( availableWidth: Float, iconPaddingDp: Float, iconSizeDp: Float, - ): Float = with(context) { - label.getHeight( - context = context, - text = labelText, - width = (availableWidth - iconSizeDp.pixels - iconPaddingDp.pixels).toInt(), - ) - } + ): Float = + with(context) { + label.getHeight( + context = context, + text = labelText, + width = (availableWidth - iconSizeDp.pixels - iconPaddingDp.pixels).toInt(), + ) + } /** * Measures the width of the label. @@ -66,13 +67,14 @@ public open class LegendItem( availableWidth: Float, iconPaddingDp: Float, iconSizeDp: Float, - ): Float = with(context) { - label.getWidth( - context = context, - text = labelText, - width = (availableWidth - iconSizeDp.pixels - iconPaddingDp.pixels).toInt(), - ) - } + ): Float = + with(context) { + label.getWidth( + context = context, + text = labelText, + width = (availableWidth - iconSizeDp.pixels - iconPaddingDp.pixels).toInt(), + ) + } /** * Measures the width of this [LegendItem]. @@ -87,12 +89,13 @@ public open class LegendItem( availableWidth: Float, iconPaddingDp: Float, iconSizeDp: Float, - ): Float = with(context) { - getLabelWidth( - context, - availableWidth, - iconPaddingDp, - iconSizeDp, - ) + (iconSizeDp + iconPaddingDp).pixels - } + ): Float = + with(context) { + getLabelWidth( + context, + availableWidth, + iconPaddingDp, + iconSizeDp, + ) + (iconSizeDp + iconPaddingDp).pixels + } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/VerticalLegend.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/VerticalLegend.kt index 6f670480e..efe091e2c 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/VerticalLegend.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/legend/VerticalLegend.kt @@ -41,60 +41,69 @@ public open class VerticalLegend( public var spacingDp: Float = 0f, override val padding: MutableDimensions = emptyDimensions(), ) : Legend, Padding { - private val heights: HashMap = HashMap() override val bounds: RectF = RectF() - override fun getHeight(context: MeasureContext, availableWidth: Float): Float = with(context) { - items.fold(0f) { sum, item -> - sum + maxOf( - iconSizeDp.pixels, - item.getLabelHeight(context, availableWidth, iconPaddingDp, iconSizeDp), - ).also { height -> heights[item] = height } - } + (padding.verticalDp + spacingDp * (items.size - 1)).pixels - } + override fun getHeight( + context: MeasureContext, + availableWidth: Float, + ): Float = + with(context) { + items.fold(0f) { sum, item -> + sum + + maxOf( + iconSizeDp.pixels, + item.getLabelHeight(context, availableWidth, iconPaddingDp, iconSizeDp), + ).also { height -> heights[item] = height } + } + (padding.verticalDp + spacingDp * (items.size - 1)).pixels + } - override fun draw(context: ChartDrawContext): Unit = with(context) { - var currentTop = bounds.top + padding.topDp.pixels + override fun draw(context: ChartDrawContext): Unit = + with(context) { + var currentTop = bounds.top + padding.topDp.pixels - items.forEach { item -> + items.forEach { item -> - val height = heights.getOrPut(item) { - item.getLabelHeight(this, chartBounds.width(), iconPaddingDp, iconSizeDp) - } - val centerY = currentTop + height.half - var startX = if (isLtr) { - chartBounds.left + padding.startDp.pixels - } else { - chartBounds.right - padding.startDp.pixels - iconSizeDp.pixels - } + val height = + heights.getOrPut(item) { + item.getLabelHeight(this, chartBounds.width(), iconPaddingDp, iconSizeDp) + } + val centerY = currentTop + height.half + var startX = + if (isLtr) { + chartBounds.left + padding.startDp.pixels + } else { + chartBounds.right - padding.startDp.pixels - iconSizeDp.pixels + } - item.icon.draw( - context = context, - left = startX, - top = centerY - iconSizeDp.half.pixels, - right = startX + iconSizeDp.pixels, - bottom = centerY + iconSizeDp.half.pixels, - ) + item.icon.draw( + context = context, + left = startX, + top = centerY - iconSizeDp.half.pixels, + right = startX + iconSizeDp.pixels, + bottom = centerY + iconSizeDp.half.pixels, + ) - startX += if (isLtr) { - (iconSizeDp + iconPaddingDp).pixels - } else { - -iconPaddingDp.pixels - } + startX += + if (isLtr) { + (iconSizeDp + iconPaddingDp).pixels + } else { + -iconPaddingDp.pixels + } - item.label.drawText( - context = context, - text = item.labelText, - textX = startX, - textY = centerY, - horizontalPosition = HorizontalPosition.End, - maxTextWidth = (chartBounds.width() - (iconSizeDp + iconPaddingDp + padding.horizontalDp).pixels) - .toInt(), - ) + item.label.drawText( + context = context, + text = item.labelText, + textX = startX, + textY = centerY, + horizontalPosition = HorizontalPosition.End, + maxTextWidth = + (chartBounds.width() - (iconSizeDp + iconPaddingDp + padding.horizontalDp).pixels) + .toInt(), + ) - currentTop += height + spacingDp.pixels + currentTop += height + spacingDp.pixels + } } - } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/DefaultMarkerLabelFormatter.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/DefaultMarkerLabelFormatter.kt index b6e98aab0..f638feb79 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/DefaultMarkerLabelFormatter.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/DefaultMarkerLabelFormatter.kt @@ -30,25 +30,25 @@ import com.patrykandpatrick.vico.core.extension.transformToSpannable * @see MarkerLabelFormatter */ public class DefaultMarkerLabelFormatter(private val colorCode: Boolean = true) : MarkerLabelFormatter { - override fun getLabel( markedEntries: List, chartValues: ChartValues, - ): CharSequence = markedEntries.transformToSpannable( - prefix = if (markedEntries.size > 1) PATTERN.format(markedEntries.sumOf { it.entry.y }) + " (" else "", - postfix = if (markedEntries.size > 1) ")" else "", - separator = "; ", - ) { model -> - if (colorCode) { - appendCompat( - PATTERN.format(model.entry.y), - ForegroundColorSpan(model.color), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE, - ) - } else { - append(PATTERN.format(model.entry.y)) + ): CharSequence = + markedEntries.transformToSpannable( + prefix = if (markedEntries.size > 1) PATTERN.format(markedEntries.sumOf { it.entry.y }) + " (" else "", + postfix = if (markedEntries.size > 1) ")" else "", + separator = "; ", + ) { model -> + if (colorCode) { + appendCompat( + PATTERN.format(model.entry.y), + ForegroundColorSpan(model.color), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE, + ) + } else { + append(PATTERN.format(model.entry.y)) + } } - } private companion object { const val PATTERN = "%.02f" diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/Marker.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/Marker.kt index c4a35a535..8f1315c74 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/Marker.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/Marker.kt @@ -28,7 +28,6 @@ import com.patrykandpatrick.vico.core.model.Point * Highlights points on a chart and displays their corresponding values in a bubble. */ public interface Marker : ChartInsetter { - /** * Draws the marker. * @param context the [DrawContext] used to draw the marker. diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/MarkerLabelFormatter.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/MarkerLabelFormatter.kt index db4043aa6..069b56b21 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/MarkerLabelFormatter.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/MarkerLabelFormatter.kt @@ -22,7 +22,6 @@ import com.patrykandpatrick.vico.core.chart.values.ChartValues * Formats marker labels. */ public fun interface MarkerLabelFormatter { - /** * Creates a formatted label for the given list of marked entries. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/MarkerVisibilityChangeListener.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/MarkerVisibilityChangeListener.kt index 472a87999..d0e6aaded 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/MarkerVisibilityChangeListener.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/marker/MarkerVisibilityChangeListener.kt @@ -20,7 +20,6 @@ package com.patrykandpatrick.vico.core.marker * Allows for listening to [Marker] visibility changes. */ public interface MarkerVisibilityChangeListener { - /** * Called when the linked [Marker] is shown. * diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/model/Point.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/model/Point.kt index 895820062..c38dd55c2 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/model/Point.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/model/Point.kt @@ -24,19 +24,24 @@ import com.patrykandpatrick.vico.core.util.unpackFloat2 /** * Creates a new [Point] with the provided coordinates. */ -public fun Point(x: Float, y: Float): Point = Point(packFloats(x, y)) +public fun Point( + x: Float, + y: Float, +): Point = Point(packFloats(x, y)) /** * Creates a new [Point] with the provided coordinates. */ -public fun Point(x: Int, y: Int): Point = Point(packInts(x, y)) +public fun Point( + x: Int, + y: Int, +): Point = Point(packInts(x, y)) /** * Represents a point in a coordinate system. */ @JvmInline public value class Point internal constructor(private val packedValue: Long) { - /** * The _x_ coordinate. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/AutoScrollCondition.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/AutoScrollCondition.kt index 253c9609e..563929bdb 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/AutoScrollCondition.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/AutoScrollCondition.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. @@ -23,14 +23,15 @@ import com.patrykandpatrick.vico.core.extension.ifNotNull * Defines when an automatic scroll should be performed. */ public fun interface AutoScrollCondition { - /** * Given a chart’s new and old models, defines whether an automatic scroll should be performed. */ - public fun shouldPerformAutoScroll(newModel: Model, oldModel: Model?): Boolean + public fun shouldPerformAutoScroll( + newModel: Model, + oldModel: Model?, + ): Boolean public companion object { - /** * Prevents any automatic scrolling from occurring. */ @@ -40,18 +41,19 @@ public fun interface AutoScrollCondition { * Triggers an automatic scroll when the size of the model increases (that is, the contents of the chart become * wider). */ - public val OnModelSizeIncreased: AutoScrollCondition = AutoScrollCondition { n, o -> - if (o != null) { - val new = n.entries - val old = o.entries - new.size > old.size || - ifNotNull( - t1 = new.maxOfOrNull { entries -> entries.size }, - t2 = old.maxOfOrNull { entries -> entries.size }, - ) { t1, t2 -> t1 > t2 } == true - } else { - false + public val OnModelSizeIncreased: AutoScrollCondition = + AutoScrollCondition { n, o -> + if (o != null) { + val new = n.entries + val old = o.entries + new.size > old.size || + ifNotNull( + t1 = new.maxOfOrNull { entries -> entries.size }, + t2 = old.maxOfOrNull { entries -> entries.size }, + ) { t1, t2 -> t1 > t2 } == true + } else { + false + } } - } } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/ScrollHandler.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/ScrollHandler.kt index 0861f1c4d..532d11763 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/ScrollHandler.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/ScrollHandler.kt @@ -25,7 +25,6 @@ import kotlin.properties.Delegates * @param initialMaxValue the initial maximum scroll amount. */ public class ScrollHandler(initialMaxValue: Float = 0f) : ScrollListenerHost { - private var initialScrollHandled: Boolean = false private val scrollListeners: MutableSet = mutableSetOf() @@ -71,10 +70,11 @@ public class ScrollHandler(initialMaxValue: Float = 0f) : ScrollListenerHost { */ public fun handleInitialScroll(initialScroll: InitialScroll) { if (initialScrollHandled) return - value = when (initialScroll) { - InitialScroll.Start -> 0f - InitialScroll.End -> maxValue - } + value = + when (initialScroll) { + InitialScroll.Start -> 0f + InitialScroll.End -> maxValue + } initialScrollHandled = true } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/ScrollListener.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/ScrollListener.kt index b23d24698..c456de9ec 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/ScrollListener.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/ScrollListener.kt @@ -20,16 +20,21 @@ package com.patrykandpatrick.vico.core.scroll * Allows for listening to a chart’s scroll state. */ public interface ScrollListener { - /** * Called when the scroll amount changes. */ - public fun onValueChanged(oldValue: Float, newValue: Float): Unit = Unit + public fun onValueChanged( + oldValue: Float, + newValue: Float, + ): Unit = Unit /** * Called when the maximum scroll amount changes. */ - public fun onMaxValueChanged(oldMaxValue: Float, newMaxValue: Float): Unit = Unit + public fun onMaxValueChanged( + oldMaxValue: Float, + newMaxValue: Float, + ): Unit = Unit /** * Called when the scroll has reached the limit and cannot be consumed further. diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/ScrollListenerHost.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/ScrollListenerHost.kt index 3a1a4736d..f07e82ac7 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/ScrollListenerHost.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/scroll/ScrollListenerHost.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. @@ -20,7 +20,6 @@ package com.patrykandpatrick.vico.core.scroll * Declares functions for registering and removing [ScrollListener]s. */ public interface ScrollListenerHost { - /** * Registers a [ScrollListener]. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/text/StaticLayoutExtensions.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/text/StaticLayoutExtensions.kt index 6c747bc9b..98300b62a 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/text/StaticLayoutExtensions.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/text/StaticLayoutExtensions.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. @@ -73,9 +73,10 @@ internal fun staticLayout( ).setLineCount(maxLines) } -internal fun StaticLayout.setLineCount(count: Int) = apply { - setFieldValue(LINE_COUNT_FIELD, count) -} +internal fun StaticLayout.setLineCount(count: Int) = + apply { + setFieldValue(LINE_COUNT_FIELD, count) + } internal fun Layout.getBounds(outBounds: RectF): RectF = outBounds.apply { diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/InlineClassHelper.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/InlineClassHelper.kt index ba9b7d897..44521b58f 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/InlineClassHelper.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/InlineClassHelper.kt @@ -13,15 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. * - * Modifications copyright 2022 by Patryk Goworowski and Patrick Michalik. + * Modifications copyright 2023 by Patryk Goworowski and Patrick Michalik. */ -@file:Suppress("NOTHING_TO_INLINE", "MagicNumber") +@file:Suppress("NOTHING_TO_INLINE") package com.patrykandpatrick.vico.core.util + /** * Packs two [Float] values into one [Long] value for use in inline classes. */ -internal inline fun packFloats(val1: Float, val2: Float): Long { +internal inline fun packFloats( + val1: Float, + val2: Float, +): Long { val v1 = val1.toBits().toLong() val v2 = val2.toBits().toLong() return v1.shl(32) or (v2 and 0xFFFFFFFF) @@ -40,7 +44,10 @@ internal inline fun unpackFloat2(value: Long) = Float.fromBits(value.and(0xFFFFF /** * Packs two [Int] values into one [Long] value for use in inline classes. */ -internal inline fun packInts(val1: Int, val2: Int) = val1.toLong().shl(32) or (val2.toLong() and 0xFFFFFFFF) +internal inline fun packInts( + val1: Int, + val2: Int, +) = val1.toLong().shl(32) or (val2.toLong() and 0xFFFFFFFF) /** * Unpacks the first [Int] value in [packInts] from its returned [ULong]. diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/RandomEntriesGenerator.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/RandomEntriesGenerator.kt index 6d3dadf41..9a7daca1c 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/RandomEntriesGenerator.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/util/RandomEntriesGenerator.kt @@ -55,17 +55,17 @@ public class RandomEntriesGenerator( * Creates a [ChartEntryModel] containing a collection of [FloatEntry] instances with randomized _y_ values. * The size of the collection is equal to the number of values in [xRange]. */ - public fun randomEntryModel(): ChartEntryModel = - getChartEntryModelProducer().requireModel() + public fun randomEntryModel(): ChartEntryModel = getChartEntryModelProducer().requireModel() /** * Creates a [ComposedChartEntryModel] with three [ChartEntryModelProducer]s, each containing a collection of * [FloatEntry] instances with randomized y values. The size of each collection is equal to the number of values in * [xRange]. */ - public fun randomComposedEntryModel(): ComposedChartEntryModel = ComposedChartEntryModelProducer - .build { repeat(modelCount) { add(List(seriesCount) { generateRandomEntries() }) } } - .requireModel() + public fun randomComposedEntryModel(): ComposedChartEntryModel = + ComposedChartEntryModelProducer + .build { repeat(modelCount) { add(List(seriesCount) { generateRandomEntries() }) } } + .requireModel() @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public companion object { 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 776ea0483..300f7f8b2 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 @@ -23,10 +23,17 @@ import kotlin.reflect.KProperty public class ValueWrapper(public var value: T) @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -public operator fun ValueWrapper.getValue(thisObj: Any?, property: KProperty<*>): T = value +public operator fun ValueWrapper.getValue( + thisObj: Any?, + property: KProperty<*>, +): T = value @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -public operator fun ValueWrapper.setValue(thisObj: Any?, property: KProperty<*>, value: T) { +public operator fun ValueWrapper.setValue( + thisObj: Any?, + property: KProperty<*>, + value: T, +) { this.value = value } diff --git a/vico/core/src/test/java/com/patrykandpatrick/vico/core/ChartEntryModelProducerTest.kt b/vico/core/src/test/java/com/patrykandpatrick/vico/core/ChartEntryModelProducerTest.kt index 6df03efed..d99315149 100644 --- a/vico/core/src/test/java/com/patrykandpatrick/vico/core/ChartEntryModelProducerTest.kt +++ b/vico/core/src/test/java/com/patrykandpatrick/vico/core/ChartEntryModelProducerTest.kt @@ -22,7 +22,6 @@ import org.junit.Test import kotlin.test.assertEquals public class ChartEntryModelProducerTest { - private val minX = 0f private val maxX = 3f private val minY = 1f diff --git a/vico/core/src/test/java/com/patrykandpatrick/vico/core/CollectionExtensionsTest.kt b/vico/core/src/test/java/com/patrykandpatrick/vico/core/CollectionExtensionsTest.kt index ae77cec73..fa9c54b26 100644 --- a/vico/core/src/test/java/com/patrykandpatrick/vico/core/CollectionExtensionsTest.kt +++ b/vico/core/src/test/java/com/patrykandpatrick/vico/core/CollectionExtensionsTest.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. @@ -22,7 +22,6 @@ import kotlin.test.assertEquals import kotlin.test.fail public class CollectionExtensionsTest { - private val format = "index=%d, isFirst=%s, isLast=%s, value=%d" @Test @@ -30,10 +29,11 @@ public class CollectionExtensionsTest { (0..<1).forEachIndexedExtended { index, isFirst, isLast, int -> val actual = format.format(index, isFirst, isLast, int) when (int) { - 0 -> assertEquals( - expected = format.format(0, true, true, 0), - actual = actual, - ) + 0 -> + assertEquals( + expected = format.format(0, true, true, 0), + actual = actual, + ) else -> fail("Unexpected value") } } @@ -44,14 +44,16 @@ public class CollectionExtensionsTest { (0..<2).forEachIndexedExtended { index, isFirst, isLast, int -> val actual = format.format(index, isFirst, isLast, int) when (int) { - 0 -> assertEquals( - expected = format.format(0, true, false, 0), - actual = actual, - ) - 1 -> assertEquals( - expected = format.format(1, false, true, 1), - actual = actual, - ) + 0 -> + assertEquals( + expected = format.format(0, true, false, 0), + actual = actual, + ) + 1 -> + assertEquals( + expected = format.format(1, false, true, 1), + actual = actual, + ) else -> fail("Unexpected value") } } @@ -62,18 +64,21 @@ public class CollectionExtensionsTest { (0..<3).forEachIndexedExtended { index, isFirst, isLast, int -> val actual = format.format(index, isFirst, isLast, int) when (int) { - 0 -> assertEquals( - expected = format.format(0, true, false, 0), - actual = actual, - ) - 1 -> assertEquals( - expected = format.format(1, false, false, 1), - actual = actual, - ) - 2 -> assertEquals( - expected = format.format(2, false, true, 2), - actual = actual, - ) + 0 -> + assertEquals( + expected = format.format(0, true, false, 0), + actual = actual, + ) + 1 -> + assertEquals( + expected = format.format(1, false, false, 1), + actual = actual, + ) + 2 -> + assertEquals( + expected = format.format(2, false, true, 2), + actual = actual, + ) else -> fail("Unexpected value") } } diff --git a/vico/core/src/test/java/com/patrykandpatrick/vico/core/extension/RectFExtensionsTest.kt b/vico/core/src/test/java/com/patrykandpatrick/vico/core/extension/RectFExtensionsTest.kt index e0f7b9732..bfce1f2b0 100644 --- a/vico/core/src/test/java/com/patrykandpatrick/vico/core/extension/RectFExtensionsTest.kt +++ b/vico/core/src/test/java/com/patrykandpatrick/vico/core/extension/RectFExtensionsTest.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. @@ -26,7 +26,6 @@ import org.junit.Test import kotlin.math.sqrt public class RectFExtensionsTest { - @MockK private lateinit var rect: RectF 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 da3ef6519..ef0696816 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 @@ -100,7 +100,6 @@ public abstract class BaseChartView internal constructo defStyleAttr: Int = 0, chartType: ThemeHandler.ChartType, ) : FrameLayout(context, attrs, defStyleAttr), ScrollListenerHost { - private val contentBounds = RectF() private val scrollHandler = ScrollHandler() @@ -113,22 +112,24 @@ public abstract class BaseChartView internal constructo private val chartValuesManager = ChartValuesManager() - private val motionEventHandler = MotionEventHandler( - scroller = scroller, - scrollHandler = scrollHandler, - density = resources.displayMetrics.density, - onTouchPoint = ::handleTouchEvent, - requestInvalidate = ::invalidate, - ) - - private val measureContext = MutableMeasureContext( - canvasBounds = contentBounds, - density = context.density, - isLtr = context.isLtr, - isHorizontalScrollEnabled = false, - spToPx = context::spToPx, - chartValuesProvider = ChartValuesProvider.Empty, - ) + private val motionEventHandler = + MotionEventHandler( + scroller = scroller, + scrollHandler = scrollHandler, + density = resources.displayMetrics.density, + onTouchPoint = ::handleTouchEvent, + requestInvalidate = ::invalidate, + ) + + private val measureContext = + MutableMeasureContext( + canvasBounds = contentBounds, + density = context.density, + isLtr = context.isLtr, + isHorizontalScrollEnabled = false, + spToPx = context::spToPx, + chartValuesProvider = ChartValuesProvider.Empty, + ) private val scaleGestureListener: ScaleGestureDetector.OnScaleGestureListener = ChartScaleGestureListener( @@ -284,9 +285,10 @@ public abstract class BaseChartView internal constructo animator.start() } } else { - finalAnimationFrameJob = coroutineScope?.launch(dispatcher) { - transformModel(this@BaseChartView, Animation.range.endInclusive) - } + finalAnimationFrameJob = + coroutineScope?.launch(dispatcher) { + transformModel(this@BaseChartView, Animation.range.endInclusive) + } } }, getOldModel = { model }, @@ -374,7 +376,10 @@ public abstract class BaseChartView internal constructo setModel(model = model, updateChartValues = true) } - private fun setModel(model: Model?, updateChartValues: Boolean) { + private fun setModel( + model: Model?, + updateChartValues: Boolean, + ) { val oldModel = this.model this.model = model updatePlaceholderVisibility() @@ -390,7 +395,11 @@ public abstract class BaseChartView internal constructo } } - protected fun tryInvalidate(chart: Chart?, model: Model?, updateChartValues: Boolean) { + protected fun tryInvalidate( + chart: Chart?, + model: Model?, + updateChartValues: Boolean, + ) { if (chart == null || model == null) return if (updateChartValues) { chartValuesManager.resetChartValues() @@ -412,11 +421,12 @@ public abstract class BaseChartView internal constructo } override fun onTouchEvent(event: MotionEvent): Boolean { - val scaleHandled = if (isZoomEnabled && event.pointerCount > 1 && chartScrollSpec.isScrollEnabled) { - scaleGestureDetector.onTouchEvent(event) - } else { - false - } + val scaleHandled = + if (isZoomEnabled && event.pointerCount > 1 && chartScrollSpec.isScrollEnabled) { + scaleGestureDetector.onTouchEvent(event) + } else { + false + } val touchHandled = motionEventHandler.handleMotionEvent(event) if (scrollDirectionResolved.not() && event.historySize > 0) { @@ -431,7 +441,10 @@ public abstract class BaseChartView internal constructo return touchHandled || scaleHandled } - private fun handleZoom(focusX: Float, zoomChange: Float) { + private fun handleZoom( + focusX: Float, + zoomChange: Float, + ) { val chart = chart ?: return val newZoom = zoom * zoomChange if (newZoom !in DEF_MIN_ZOOM..DEF_MAX_ZOOM) return @@ -489,24 +502,26 @@ public abstract class BaseChartView internal constructo if (chartScrollSpec.isScrollEnabled) zoom = finalZoom } - scrollHandler.maxValue = measureContext.getMaxScrollDistance( - chartWidth = chart.bounds.width(), - horizontalDimensions = horizontalDimensions, - zoom = finalZoom, - ) + scrollHandler.maxValue = + measureContext.getMaxScrollDistance( + chartWidth = chart.bounds.width(), + horizontalDimensions = horizontalDimensions, + zoom = finalZoom, + ) scrollHandler.handleInitialScroll(initialScroll = chartScrollSpec.initialScroll) - val chartDrawContext = chartDrawContext( - canvas = canvas, - elevationOverlayColor = elevationOverlayColor, - measureContext = measureContext, - markerTouchPoint = markerTouchPoint, - horizontalDimensions = horizontalDimensions, - chartBounds = chart.bounds, - horizontalScroll = scrollHandler.value, - zoom = finalZoom, - ) + val chartDrawContext = + chartDrawContext( + canvas = canvas, + elevationOverlayColor = elevationOverlayColor, + measureContext = measureContext, + markerTouchPoint = markerTouchPoint, + horizontalDimensions = horizontalDimensions, + chartBounds = chart.bounds, + horizontalScroll = scrollHandler.value, + zoom = finalZoom, + ) val count = if (fadingEdges != null) chartDrawContext.saveLayer() else -1 @@ -543,29 +558,35 @@ public abstract class BaseChartView internal constructo !isAnimationRunning -> return !isAnimationFrameGenerationRunning -> { isAnimationFrameGenerationRunning = true - animationFrameJob = coroutineScope?.launch(dispatcher) { - entryProducer?.transformModel(this@BaseChartView, fraction) - isAnimationFrameGenerationRunning = false - } + animationFrameJob = + coroutineScope?.launch(dispatcher) { + entryProducer?.transformModel(this@BaseChartView, fraction) + isAnimationFrameGenerationRunning = false + } } fraction == 1f -> { - finalAnimationFrameJob = coroutineScope?.launch(dispatcher) { - animationFrameJob?.cancelAndJoin() - entryProducer?.transformModel(this@BaseChartView, fraction) - isAnimationFrameGenerationRunning = false - } + finalAnimationFrameJob = + coroutineScope?.launch(dispatcher) { + animationFrameJob?.cancelAndJoin() + entryProducer?.transformModel(this@BaseChartView, fraction) + isAnimationFrameGenerationRunning = false + } } } } - override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + override fun onMeasure( + widthMeasureSpec: Int, + heightMeasureSpec: Int, + ) { val width = widthMeasureSpec.specSize.coerceAtLeast(suggestedMinimumWidth) val defaultHeight = DefaultDimens.CHART_HEIGHT.dpInt + verticalPadding - val height = when (heightMeasureSpec.specMode) { - MeasureSpec.EXACTLY -> heightMeasureSpec.specSize - MeasureSpec.AT_MOST -> defaultHeight.coerceAtMost(heightMeasureSpec.specSize) - else -> defaultHeight - }.coerceAtLeast(suggestedMinimumHeight) + val height = + when (heightMeasureSpec.specMode) { + MeasureSpec.EXACTLY -> heightMeasureSpec.specSize + MeasureSpec.AT_MOST -> defaultHeight.coerceAtMost(heightMeasureSpec.specSize) + else -> defaultHeight + }.coerceAtLeast(suggestedMinimumHeight) super.onMeasure( MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY), @@ -574,7 +595,11 @@ public abstract class BaseChartView internal constructo .set(left = paddingLeft, top = paddingTop, right = width - paddingRight, bottom = height - paddingBottom) } - override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams?) { + override fun addView( + child: View, + index: Int, + params: ViewGroup.LayoutParams?, + ) { check(childCount == 0) { "Only one placeholder can be added." } super.addView(child, index, params) placeholder = child @@ -589,7 +614,10 @@ public abstract class BaseChartView internal constructo /** * Updates the placeholder, which is shown when no [ChartEntryModel] is available. */ - public fun setPlaceholder(view: View?, params: LayoutParams? = null) { + public fun setPlaceholder( + view: View?, + params: LayoutParams? = null, + ) { if (view === placeholder) return removeAllViews() if (view != null) addView(view, params) diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ChartView.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ChartView.kt index c5963b018..15a618520 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ChartView.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ChartView.kt @@ -29,35 +29,38 @@ import com.patrykandpatrick.vico.views.theme.ThemeHandler /** * A subclass of [BaseChartView] that displays charts that use [ChartEntryModel]. */ -public class ChartView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0, -) : BaseChartView( - context = context, - attrs = attrs, - defStyleAttr = defStyleAttr, - chartType = ThemeHandler.ChartType.Single, -) { - init { - chart = themeHandler.chart - if (isInEditMode && attrs != null) setPreviewModel(attrs) - } +public class ChartView + @JvmOverloads + constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + ) : BaseChartView( + context = context, + attrs = attrs, + defStyleAttr = defStyleAttr, + chartType = ThemeHandler.ChartType.Single, + ) { + init { + chart = themeHandler.chart + if (isInEditMode && attrs != null) setPreviewModel(attrs) + } - private fun setPreviewModel(attrs: AttributeSet) { - context.obtainStyledAttributes(attrs, R.styleable.ChartView).use { typedArray -> - val seriesCount = typedArray.getInt(R.styleable.ChartView_previewSeriesCount, SERIES_COUNT) - val minX = typedArray.getInt(R.styleable.ChartView_previewMinX, 0) - val maxX = typedArray.getInt(R.styleable.ChartView_previewMaxX, X_RANGE_TOP) - val minY = typedArray.getInt(R.styleable.ChartView_previewMinY, 0) - val maxY = typedArray.getInt(R.styleable.ChartView_previewMaxY, Y_RANGE_TOP) - setModel( - model = RandomEntriesGenerator( - xRange = minX..maxX, - yRange = minY..maxY, - seriesCount = seriesCount, - ).randomEntryModel(), - ) + private fun setPreviewModel(attrs: AttributeSet) { + context.obtainStyledAttributes(attrs, R.styleable.ChartView).use { typedArray -> + val seriesCount = typedArray.getInt(R.styleable.ChartView_previewSeriesCount, SERIES_COUNT) + val minX = typedArray.getInt(R.styleable.ChartView_previewMinX, 0) + val maxX = typedArray.getInt(R.styleable.ChartView_previewMaxX, X_RANGE_TOP) + val minY = typedArray.getInt(R.styleable.ChartView_previewMinY, 0) + val maxY = typedArray.getInt(R.styleable.ChartView_previewMaxY, Y_RANGE_TOP) + setModel( + model = + RandomEntriesGenerator( + xRange = minX..maxX, + yRange = minY..maxY, + seriesCount = seriesCount, + ).randomEntryModel(), + ) + } } } -} diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ComposedChartView.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ComposedChartView.kt index 6e577098e..47f9bdf40 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ComposedChartView.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ComposedChartView.kt @@ -31,38 +31,40 @@ import com.patrykandpatrick.vico.views.theme.ThemeHandler /** * A subclass of [BaseChartView] that displays charts that use [ComposedChartEntryModel]. */ -public class ComposedChartView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0, -) : BaseChartView>( - context = context, - attrs = attrs, - defStyleAttr = defStyleAttr, - chartType = ThemeHandler.ChartType.Composed, -) { - - init { - chart = themeHandler.composedChart - if (isInEditMode && attrs != null) setPreviewModel(attrs) - } +public class ComposedChartView + @JvmOverloads + constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + ) : BaseChartView>( + context = context, + attrs = attrs, + defStyleAttr = defStyleAttr, + chartType = ThemeHandler.ChartType.Composed, + ) { + init { + chart = themeHandler.composedChart + if (isInEditMode && attrs != null) setPreviewModel(attrs) + } - private fun setPreviewModel(attrs: AttributeSet) { - context.obtainStyledAttributes(attrs, R.styleable.ComposedChartView).use { typedArray -> - val modelCount = typedArray.getInt(R.styleable.ComposedChartView_previewModelCount, MODEL_COUNT) - val seriesCount = typedArray.getInt(R.styleable.ComposedChartView_previewSeriesCount, SERIES_COUNT) - val minX = typedArray.getInt(R.styleable.ComposedChartView_previewMinX, 0) - val maxX = typedArray.getInt(R.styleable.ComposedChartView_previewMaxX, X_RANGE_TOP) - val minY = typedArray.getInt(R.styleable.ComposedChartView_previewMinY, 0) - val maxY = typedArray.getInt(R.styleable.ComposedChartView_previewMaxY, Y_RANGE_TOP) - setModel( - model = RandomEntriesGenerator( - xRange = minX..maxX, - yRange = minY..maxY, - seriesCount = seriesCount, - modelCount = modelCount, - ).randomComposedEntryModel(), - ) + private fun setPreviewModel(attrs: AttributeSet) { + context.obtainStyledAttributes(attrs, R.styleable.ComposedChartView).use { typedArray -> + val modelCount = typedArray.getInt(R.styleable.ComposedChartView_previewModelCount, MODEL_COUNT) + val seriesCount = typedArray.getInt(R.styleable.ComposedChartView_previewSeriesCount, SERIES_COUNT) + val minX = typedArray.getInt(R.styleable.ComposedChartView_previewMinX, 0) + val maxX = typedArray.getInt(R.styleable.ComposedChartView_previewMaxX, X_RANGE_TOP) + val minY = typedArray.getInt(R.styleable.ComposedChartView_previewMinY, 0) + val maxY = typedArray.getInt(R.styleable.ComposedChartView_previewMaxY, Y_RANGE_TOP) + setModel( + model = + RandomEntriesGenerator( + xRange = minX..maxX, + yRange = minY..maxY, + seriesCount = seriesCount, + modelCount = modelCount, + ).randomComposedEntryModel(), + ) + } } } -} diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/column/ColumnChartExtensions.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/column/ColumnChartExtensions.kt index 64ca93b77..0e8b70f74 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/column/ColumnChartExtensions.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/column/ColumnChartExtensions.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. @@ -76,28 +76,31 @@ public fun columnChart( ): ColumnChart { val tempArray = IntArray(1) - val columns = listOf(R.attr.column1, R.attr.column2, R.attr.column3) - .map { themeAttrResItem -> columnLineComponent(context, themeAttrResItem, styleResId) } + val columns = + listOf(R.attr.column1, R.attr.column2, R.attr.column3) + .map { themeAttrResItem -> columnLineComponent(context, themeAttrResItem, styleResId) } tempArray[0] = R.styleable.ColumnChartStyle_columnOuterSpacing - val spacingDp = context.obtainStyledAttributes(null, tempArray) - .use { typedArray -> - typedArray.getRawDimension( - context = context, - index = R.styleable.ColumnChartStyle_columnOuterSpacing, - defaultValue = DefaultDimens.COLUMN_OUTSIDE_SPACING, - ) - } + val spacingDp = + context.obtainStyledAttributes(null, tempArray) + .use { typedArray -> + typedArray.getRawDimension( + context = context, + index = R.styleable.ColumnChartStyle_columnOuterSpacing, + defaultValue = DefaultDimens.COLUMN_OUTSIDE_SPACING, + ) + } - val innerSpacingDp = context.obtainStyledAttributes(null, tempArray) - .use { typedArray -> - typedArray.getRawDimension( - context = context, - index = R.styleable.ColumnChartStyle_columnInnerSpacing, - defaultValue = DefaultDimens.COLUMN_INSIDE_SPACING, - ) - } + val innerSpacingDp = + context.obtainStyledAttributes(null, tempArray) + .use { typedArray -> + typedArray.getRawDimension( + context = context, + index = R.styleable.ColumnChartStyle_columnInnerSpacing, + defaultValue = DefaultDimens.COLUMN_INSIDE_SPACING, + ) + } return ColumnChart( columns = columns, diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/line/LineChartExtensions.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/line/LineChartExtensions.kt index 08822c3c4..155382aa5 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/line/LineChartExtensions.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/line/LineChartExtensions.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. @@ -73,18 +73,20 @@ public fun lineChart( ): LineChart { val tempArray = IntArray(1) - val lineSpecs = listOf(R.attr.line1Spec, R.attr.line2Spec, R.attr.line3Spec) - .map { themeAttrResItem -> lineSpec(context, themeAttrResItem, styleResId) } + val lineSpecs = + listOf(R.attr.line1Spec, R.attr.line2Spec, R.attr.line3Spec) + .map { themeAttrResItem -> lineSpec(context, themeAttrResItem, styleResId) } tempArray[0] = R.styleable.LineChartStyle_spacing - val spacingDp = context.obtainStyledAttributes(null, tempArray) - .use { typedArray -> - typedArray.getRawDimension( - context = context, - index = R.styleable.LineChartStyle_spacing, - defaultValue = DefaultDimens.POINT_SPACING, - ) - } + val spacingDp = + context.obtainStyledAttributes(null, tempArray) + .use { typedArray -> + typedArray.getRawDimension( + context = context, + index = R.styleable.LineChartStyle_spacing, + defaultValue = DefaultDimens.POINT_SPACING, + ) + } return LineChart( lines = lineSpecs, diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/component/shape/ShapeDrawable.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/component/shape/ShapeDrawable.kt index e8102f9a8..c76f4a1ad 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/component/shape/ShapeDrawable.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/component/shape/ShapeDrawable.kt @@ -48,7 +48,6 @@ public class ShapeDrawable( private val width: Int = 0, private val height: Int = 0, ) : Drawable() { - public constructor( context: Context, shape: Shape, @@ -69,9 +68,10 @@ public class ShapeDrawable( /** * The [Paint] used to draw the shape. */ - public val paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { - color = DEF_COLOR - } + public val paint: Paint = + Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = DEF_COLOR + } init { setBounds(0, 0, width, height) @@ -94,11 +94,12 @@ public class ShapeDrawable( path.reset() } - private fun isLtr(): Boolean = if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) { - layoutDirection == LayoutDirection.LTR - } else { - isLtr - } + private fun isLtr(): Boolean = + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) { + layoutDirection == LayoutDirection.LTR + } else { + isLtr + } override fun setAlpha(alpha: Int) { paint.alpha = alpha diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/component/shape/Shapes.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/component/shape/Shapes.kt index 3a9232dc8..1e4b248fd 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/component/shape/Shapes.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/component/shape/Shapes.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. @@ -28,8 +28,7 @@ import com.patrykandpatrick.vico.core.component.shape.cornered.RoundedCornerTrea /** * Creates a [CorneredShape] with rounded corners of the provided size. */ -public fun Shapes.roundedCornerShape(all: Float): Shape = - roundedCornerShape(all, all, all, all) +public fun Shapes.roundedCornerShape(all: Float): Shape = roundedCornerShape(all, all, all, all) /** * Creates a [CorneredShape] with rounded corners of the provided sizes. @@ -39,18 +38,18 @@ public fun Shapes.roundedCornerShape( topRight: Float = 0f, bottomRight: Float = 0f, bottomLeft: Float = 0f, -): CorneredShape = CorneredShape( - Corner.Absolute(topLeft, RoundedCornerTreatment), - Corner.Absolute(topRight, RoundedCornerTreatment), - Corner.Absolute(bottomRight, RoundedCornerTreatment), - Corner.Absolute(bottomLeft, RoundedCornerTreatment), -) +): CorneredShape = + CorneredShape( + Corner.Absolute(topLeft, RoundedCornerTreatment), + Corner.Absolute(topRight, RoundedCornerTreatment), + Corner.Absolute(bottomRight, RoundedCornerTreatment), + Corner.Absolute(bottomLeft, RoundedCornerTreatment), + ) /** * Creates a [CorneredShape] with cut corners of the provided size. */ -public fun Shapes.cutCornerShape(all: Float): Shape = - cutCornerShape(all, all, all, all) +public fun Shapes.cutCornerShape(all: Float): Shape = cutCornerShape(all, all, all, all) /** * Creates a [CorneredShape] with cut corners of the provided sizes. @@ -60,9 +59,10 @@ public fun Shapes.cutCornerShape( topRight: Float = 0f, bottomRight: Float = 0f, bottomLeft: Float = 0f, -): CorneredShape = CorneredShape( - Corner.Absolute(topLeft, CutCornerTreatment), - Corner.Absolute(topRight, CutCornerTreatment), - Corner.Absolute(bottomRight, CutCornerTreatment), - Corner.Absolute(bottomLeft, CutCornerTreatment), -) +): CorneredShape = + CorneredShape( + Corner.Absolute(topLeft, CutCornerTreatment), + Corner.Absolute(topRight, CutCornerTreatment), + Corner.Absolute(bottomRight, CutCornerTreatment), + Corner.Absolute(bottomLeft, CutCornerTreatment), + ) diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/component/shape/shader/DynamicShaders.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/component/shape/shader/DynamicShaders.kt index 56db49014..4d4bff195 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/component/shape/shader/DynamicShaders.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/component/shape/shader/DynamicShaders.kt @@ -45,22 +45,21 @@ public fun DynamicShaders.fromComponent( checkeredArrangement: Boolean = true, tileXMode: Shader.TileMode = Shader.TileMode.REPEAT, tileYMode: Shader.TileMode = tileXMode, -): ComponentShader = ComponentShader( - component = component, - componentSizeDp = componentSizeDp, - checkeredArrangement = checkeredArrangement, - tileXMode = tileXMode, - tileYMode = tileYMode, -) +): ComponentShader = + ComponentShader( + component = component, + componentSizeDp = componentSizeDp, + checkeredArrangement = checkeredArrangement, + tileXMode = tileXMode, + tileYMode = tileYMode, + ) /** * Creates a [DynamicShader] in the form of a horizontal gradient. * * @param colors the sRGB colors to be distributed along the gradient line. */ -public fun DynamicShaders.horizontalGradient( - vararg colors: Int, -): DynamicShader = horizontalGradient(colors) +public fun DynamicShaders.horizontalGradient(vararg colors: Int): DynamicShader = horizontalGradient(colors) /** * Creates a [DynamicShader] in the form of a horizontal gradient. @@ -74,37 +73,43 @@ public fun DynamicShaders.horizontalGradient( public fun DynamicShaders.horizontalGradient( colors: IntArray, positions: FloatArray? = null, -): DynamicShader = object : CacheableDynamicShader() { - - override fun createShader( - context: DrawContext, - left: Float, - top: Float, - right: Float, - bottom: Float, - ): Shader = - LinearGradient( - left, - top, - right, - top, - colors, - positions, - Shader.TileMode.CLAMP, - ) +): DynamicShader = + object : CacheableDynamicShader() { + override fun createShader( + context: DrawContext, + left: Float, + top: Float, + right: Float, + bottom: Float, + ): Shader = + LinearGradient( + left, + top, + right, + top, + colors, + positions, + Shader.TileMode.CLAMP, + ) - override fun createKey(left: Float, top: Float, right: Float, bottom: Float): String = - "%s,%s".format(left, right) -} + override fun createKey( + left: Float, + top: Float, + right: Float, + bottom: Float, + ): String = + "%s,%s".format( + left, + right, + ) + } /** * Creates a [DynamicShader] in the form of a vertical gradient. * * @param colors the sRGB colors to be distributed along the gradient line. */ -public fun DynamicShaders.verticalGradient( - vararg colors: Int, -): DynamicShader = verticalGradient(colors) +public fun DynamicShaders.verticalGradient(vararg colors: Int): DynamicShader = verticalGradient(colors) /** * Creates a [DynamicShader] in the form of a vertical gradient. @@ -118,33 +123,43 @@ public fun DynamicShaders.verticalGradient( public fun DynamicShaders.verticalGradient( colors: IntArray, positions: FloatArray? = null, -): DynamicShader = object : CacheableDynamicShader() { - - override fun createShader( - context: DrawContext, - left: Float, - top: Float, - right: Float, - bottom: Float, - ): Shader = - LinearGradient( - left, - top, - left, - bottom, - colors, - positions, - Shader.TileMode.CLAMP, - ) +): DynamicShader = + object : CacheableDynamicShader() { + override fun createShader( + context: DrawContext, + left: Float, + top: Float, + right: Float, + bottom: Float, + ): Shader = + LinearGradient( + left, + top, + left, + bottom, + colors, + positions, + Shader.TileMode.CLAMP, + ) - override fun createKey(left: Float, top: Float, right: Float, bottom: Float): String = - "%s,%s".format(top, bottom) -} + override fun createKey( + left: Float, + top: Float, + right: Float, + bottom: Float, + ): String = + "%s,%s".format( + top, + bottom, + ) + } /** * Creates a [DynamicShader] that fills the area with a solid color. */ -public fun DynamicShaders.solid(@ColorInt color: Int): SolidShader = SolidShader(color) +public fun DynamicShaders.solid( + @ColorInt color: Int, +): SolidShader = SolidShader(color) /** * Creates a [DynamicShader] with split colors. The positive color is used for values above the zero line, diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/dimensions/DimensionsExtensions.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/dimensions/DimensionsExtensions.kt index e2e35a25b..8323ee2e2 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/dimensions/DimensionsExtensions.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/dimensions/DimensionsExtensions.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. @@ -21,12 +21,13 @@ import com.patrykandpatrick.vico.core.dimensions.MutableDimensions /** * Creates a [MutableDimensions] instance with a common value for each coordinate. */ -public fun dimensionsOf(allDp: Float): MutableDimensions = dimensionsOf( - startDp = allDp, - topDp = allDp, - endDp = allDp, - bottomDp = allDp, -) +public fun dimensionsOf(allDp: Float): MutableDimensions = + dimensionsOf( + startDp = allDp, + topDp = allDp, + endDp = allDp, + bottomDp = allDp, + ) /** * Creates a [MutableDimensions] instance using the provided measurements. @@ -36,12 +37,13 @@ public fun dimensionsOf( topDp: Float = 0f, endDp: Float = 0f, bottomDp: Float = 0f, -): MutableDimensions = MutableDimensions( - startDp = startDp, - topDp = topDp, - endDp = endDp, - bottomDp = bottomDp, -) +): MutableDimensions = + MutableDimensions( + startDp = startDp, + topDp = topDp, + endDp = endDp, + bottomDp = bottomDp, + ) /** * Creates a [MutableDimensions] instance using the provided measurements. @@ -49,9 +51,10 @@ public fun dimensionsOf( public fun dimensionsOf( verticalDp: Float = 0f, horizontalDp: Float = 0f, -): MutableDimensions = MutableDimensions( - startDp = horizontalDp, - topDp = verticalDp, - endDp = horizontalDp, - bottomDp = verticalDp, -) +): MutableDimensions = + MutableDimensions( + startDp = horizontalDp, + topDp = verticalDp, + endDp = horizontalDp, + bottomDp = verticalDp, + ) diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/extension/ContextExtensions.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/extension/ContextExtensions.kt index dcb2f3b36..b6d88933d 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/extension/ContextExtensions.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/extension/ContextExtensions.kt @@ -26,11 +26,12 @@ internal val Context.density: Float get() = resources.displayMetrics.density internal val Context.isLtr: Boolean - get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - resources.configuration.layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR - } else { - true - } + get() = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + resources.configuration.layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR + } else { + true + } internal val Context.isDarkMode: Boolean get() = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/extension/ViewExtensions.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/extension/ViewExtensions.kt index 75870e1e8..1ee228779 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/extension/ViewExtensions.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/extension/ViewExtensions.kt @@ -25,7 +25,10 @@ import androidx.core.view.updatePadding import com.patrykandpatrick.vico.core.model.Point import kotlin.math.min -internal fun View.measureDimension(desiredSize: Int, measureSpec: Int): Int { +internal fun View.measureDimension( + desiredSize: Int, + measureSpec: Int, +): Int { val specMode = View.MeasureSpec.getMode(measureSpec) val specSize = View.MeasureSpec.getSize(measureSpec) return when (specMode) { diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/gestures/ChartScaleGestureListener.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/gestures/ChartScaleGestureListener.kt index e9660be07..01028dd87 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/gestures/ChartScaleGestureListener.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/gestures/ChartScaleGestureListener.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. @@ -23,7 +23,6 @@ internal class ChartScaleGestureListener( private val getChartBounds: () -> RectF?, private val onZoom: (focusX: Float, zoomChange: Float) -> Unit, ) : ScaleGestureDetector.OnScaleGestureListener { - override fun onScale(detector: ScaleGestureDetector): Boolean { onZoom(detector.focusX, detector.scaleFactor) return true diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/gestures/MotionEventHandler.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/gestures/MotionEventHandler.kt index 9ab6245bc..29affbc66 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/gestures/MotionEventHandler.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/gestures/MotionEventHandler.kt @@ -41,7 +41,6 @@ public open class MotionEventHandler( private val onTouchPoint: (Point?) -> Unit, private val requestInvalidate: () -> Unit, ) { - private val velocityUnits = (VELOCITY_PIXELS * density).toInt() private val dragThreshold = DRAG_THRESHOLD_PIXELS * density private var initialX = -dragThreshold @@ -114,12 +113,10 @@ public open class MotionEventHandler( } private class VelocityTrackerHelper { - private var tracker: VelocityTracker? = null @SuppressLint("Recycle") - fun get(): VelocityTracker = - tracker ?: VelocityTracker.obtain().also { tracker = it } + fun get(): VelocityTracker = tracker ?: VelocityTracker.obtain().also { tracker = it } fun clear() { tracker?.recycle() diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/scroll/ChartScrollSpec.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/scroll/ChartScrollSpec.kt index 3d6a2a146..65d5a2bf1 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/scroll/ChartScrollSpec.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/scroll/ChartScrollSpec.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. @@ -41,13 +41,14 @@ public class ChartScrollSpec( public val autoScrollInterpolator: TimeInterpolator = AccelerateDecelerateInterpolator(), public val autoScrollDuration: Long = Animation.DIFF_DURATION.toLong(), ) { - private val animator: ValueAnimator = ValueAnimator.ofFloat( - Animation.range.start, - Animation.range.endInclusive, - ).apply { - duration = autoScrollDuration - interpolator = autoScrollInterpolator - } + private val animator: ValueAnimator = + ValueAnimator.ofFloat( + Animation.range.start, + Animation.range.endInclusive, + ).apply { + duration = autoScrollDuration + interpolator = autoScrollInterpolator + } /** * Performs an automatic scroll. @@ -63,12 +64,13 @@ public class ChartScrollSpec( removeAllUpdateListeners() addUpdateListener { scrollHandler.handleScroll( - targetScroll = when (initialScroll) { - InitialScroll.Start -> (1 - it.animatedFraction) * scrollHandler.value - InitialScroll.End -> - scrollHandler.value + it.animatedFraction * - (scrollHandler.maxValue - scrollHandler.value) - }, + targetScroll = + when (initialScroll) { + InitialScroll.Start -> (1 - it.animatedFraction) * scrollHandler.value + InitialScroll.End -> + scrollHandler.value + it.animatedFraction * + (scrollHandler.maxValue - scrollHandler.value) + }, ) } start() @@ -85,10 +87,11 @@ public fun ChartScrollSpec.copy( autoScrollCondition: AutoScrollCondition = this.autoScrollCondition, autoScrollInterpolator: TimeInterpolator = this.autoScrollInterpolator, autoScrollDuration: Long = this.autoScrollDuration, -): ChartScrollSpec = ChartScrollSpec( - isScrollEnabled = isScrollEnabled, - initialScroll = initialScroll, - autoScrollCondition = autoScrollCondition, - autoScrollInterpolator = autoScrollInterpolator, - autoScrollDuration = autoScrollDuration, -) +): ChartScrollSpec = + ChartScrollSpec( + isScrollEnabled = isScrollEnabled, + initialScroll = initialScroll, + autoScrollCondition = autoScrollCondition, + autoScrollInterpolator = autoScrollInterpolator, + autoScrollDuration = autoScrollDuration, + ) diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ChartStyleExtensions.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ChartStyleExtensions.kt index 1a85dc941..f6bd0398f 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ChartStyleExtensions.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ChartStyleExtensions.kt @@ -33,108 +33,118 @@ internal fun TypedArray.getColumnChart( @StyleableRes resourceId: Int = R.styleable.BaseChartView_columnChartStyle, @StyleableRes styleableResourceId: IntArray = R.styleable.ColumnChartStyle, mergeMode: MergeMode, -): ColumnChart = getNestedTypedArray(context, resourceId, styleableResourceId).run { - val defaultShape = Shapes.roundedCornerShape(allPercent = DefaultDimens.COLUMN_ROUNDNESS_PERCENT) - ColumnChart( - columns = listOf( - getNestedTypedArray( - context = context, - resourceId = R.styleable.ColumnChartStyle_column1, - styleableResourceId = R.styleable.LineComponent, - ).getLineComponent( - context = context, - defaultColor = context.defaultColors.entity1Color.toInt(), - defaultThickness = DefaultDimens.COLUMN_WIDTH, - defaultShape = defaultShape, - ), - getNestedTypedArray( - context = context, - resourceId = R.styleable.ColumnChartStyle_column2, - styleableResourceId = R.styleable.LineComponent, - ).getLineComponent( - context = context, - defaultColor = context.defaultColors.entity2Color.toInt(), - defaultThickness = DefaultDimens.COLUMN_WIDTH, - defaultShape = defaultShape, - ), - getNestedTypedArray( - context = context, - resourceId = R.styleable.ColumnChartStyle_column3, - styleableResourceId = R.styleable.LineComponent, - ).getLineComponent( - context = context, - defaultColor = context.defaultColors.entity3Color.toInt(), - defaultThickness = DefaultDimens.COLUMN_WIDTH, - defaultShape = defaultShape, - ), - ), - spacingDp = getRawDimension( - context = context, - index = R.styleable.ColumnChartStyle_columnOuterSpacing, - defaultValue = DefaultDimens.COLUMN_OUTSIDE_SPACING, - ), - innerSpacingDp = getRawDimension( - context = context, - index = R.styleable.ColumnChartStyle_columnInnerSpacing, - defaultValue = DefaultDimens.COLUMN_INSIDE_SPACING, - ), - mergeMode = mergeMode, - dataLabel = if (getBoolean(R.styleable.ColumnChartStyle_showDataLabels, false)) { - getNestedTypedArray( - context = context, - resourceId = R.styleable.ColumnChartStyle_dataLabelStyle, - styleableResourceId = R.styleable.TextComponentStyle, - ).getTextComponent(context = context) - } else { - null - }, - dataLabelVerticalPosition = getInteger(R.styleable.ColumnChartStyle_dataLabelVerticalPosition, 0).let { value -> - val values = VerticalPosition.values() - values[value % values.size] - }, - dataLabelRotationDegrees = getFloat( - R.styleable.ColumnChartStyle_dataLabelRotationDegrees, - 0f, - ), - ) -} +): ColumnChart = + getNestedTypedArray(context, resourceId, styleableResourceId).run { + val defaultShape = Shapes.roundedCornerShape(allPercent = DefaultDimens.COLUMN_ROUNDNESS_PERCENT) + ColumnChart( + columns = + listOf( + getNestedTypedArray( + context = context, + resourceId = R.styleable.ColumnChartStyle_column1, + styleableResourceId = R.styleable.LineComponent, + ).getLineComponent( + context = context, + defaultColor = context.defaultColors.entity1Color.toInt(), + defaultThickness = DefaultDimens.COLUMN_WIDTH, + defaultShape = defaultShape, + ), + getNestedTypedArray( + context = context, + resourceId = R.styleable.ColumnChartStyle_column2, + styleableResourceId = R.styleable.LineComponent, + ).getLineComponent( + context = context, + defaultColor = context.defaultColors.entity2Color.toInt(), + defaultThickness = DefaultDimens.COLUMN_WIDTH, + defaultShape = defaultShape, + ), + getNestedTypedArray( + context = context, + resourceId = R.styleable.ColumnChartStyle_column3, + styleableResourceId = R.styleable.LineComponent, + ).getLineComponent( + context = context, + defaultColor = context.defaultColors.entity3Color.toInt(), + defaultThickness = DefaultDimens.COLUMN_WIDTH, + defaultShape = defaultShape, + ), + ), + spacingDp = + getRawDimension( + context = context, + index = R.styleable.ColumnChartStyle_columnOuterSpacing, + defaultValue = DefaultDimens.COLUMN_OUTSIDE_SPACING, + ), + innerSpacingDp = + getRawDimension( + context = context, + index = R.styleable.ColumnChartStyle_columnInnerSpacing, + defaultValue = DefaultDimens.COLUMN_INSIDE_SPACING, + ), + mergeMode = mergeMode, + dataLabel = + if (getBoolean(R.styleable.ColumnChartStyle_showDataLabels, false)) { + getNestedTypedArray( + context = context, + resourceId = R.styleable.ColumnChartStyle_dataLabelStyle, + styleableResourceId = R.styleable.TextComponentStyle, + ).getTextComponent(context = context) + } else { + null + }, + dataLabelVerticalPosition = + getInteger(R.styleable.ColumnChartStyle_dataLabelVerticalPosition, 0).let { value -> + val values = VerticalPosition.values() + values[value % values.size] + }, + dataLabelRotationDegrees = + getFloat( + R.styleable.ColumnChartStyle_dataLabelRotationDegrees, + 0f, + ), + ) + } internal fun TypedArray.getLineChart( context: Context, @StyleableRes resourceId: Int = R.styleable.BaseChartView_lineChartStyle, @StyleableRes styleableResourceId: IntArray = R.styleable.LineChartStyle, -): LineChart = getNestedTypedArray(context, resourceId, styleableResourceId).run { - LineChart( - lines = listOf( - getNestedTypedArray( - context = context, - resourceId = R.styleable.LineChartStyle_line1Spec, - styleableResourceId = R.styleable.LineSpec, - ).getLineSpec( - context = context, - defaultColor = context.defaultColors.entity1Color.toInt(), - ), - getNestedTypedArray( - context = context, - resourceId = R.styleable.LineChartStyle_line2Spec, - styleableResourceId = R.styleable.LineSpec, - ).getLineSpec( - context = context, - defaultColor = context.defaultColors.entity2Color.toInt(), - ), - getNestedTypedArray( - context = context, - resourceId = R.styleable.LineChartStyle_line3Spec, - styleableResourceId = R.styleable.LineSpec, - ).getLineSpec( - context = context, - defaultColor = context.defaultColors.entity3Color.toInt(), - ), - ), - spacingDp = getRawDimension( - context = context, - index = R.styleable.LineChartStyle_spacing, - defaultValue = DefaultDimens.POINT_SPACING, - ), - ) -} +): LineChart = + getNestedTypedArray(context, resourceId, styleableResourceId).run { + LineChart( + lines = + listOf( + getNestedTypedArray( + context = context, + resourceId = R.styleable.LineChartStyle_line1Spec, + styleableResourceId = R.styleable.LineSpec, + ).getLineSpec( + context = context, + defaultColor = context.defaultColors.entity1Color.toInt(), + ), + getNestedTypedArray( + context = context, + resourceId = R.styleable.LineChartStyle_line2Spec, + styleableResourceId = R.styleable.LineSpec, + ).getLineSpec( + context = context, + defaultColor = context.defaultColors.entity2Color.toInt(), + ), + getNestedTypedArray( + context = context, + resourceId = R.styleable.LineChartStyle_line3Spec, + styleableResourceId = R.styleable.LineSpec, + ).getLineSpec( + context = context, + defaultColor = context.defaultColors.entity3Color.toInt(), + ), + ), + spacingDp = + getRawDimension( + context = context, + index = R.styleable.LineChartStyle_spacing, + defaultValue = DefaultDimens.POINT_SPACING, + ), + ) + } diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ComponentStyleExtensions.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ComponentStyleExtensions.kt index 74d2dc363..d7f390725 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ComponentStyleExtensions.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ComponentStyleExtensions.kt @@ -43,186 +43,211 @@ internal fun TypedArray.getLineComponent( defaultColor: Int = context.defaultColors.axisLineColor.toInt(), defaultThickness: Float = DefaultDimens.AXIS_LINE_WIDTH, defaultShape: Shape = Shapes.rectShape, -): LineComponent = use { array -> - LineComponent( - color = array.getColorExtended( - index = R.styleable.LineComponent_color, - defaultColor = defaultColor, - ), - thicknessDp = array.getRawDimension( - context = context, - index = R.styleable.LineComponent_thickness, - defaultValue = defaultThickness, - ), - shape = if (hasValue(R.styleable.LineComponent_shapeStyle)) { - getNestedTypedArray( - context = context, - resourceId = R.styleable.LineComponent_shapeStyle, - styleableResourceId = R.styleable.Shape, - ).getShape( - context = context, - ) - } else { - defaultShape - }, - strokeColor = array.getColorExtended( - index = R.styleable.LineComponent_strokeColor, - defaultColor = Color.TRANSPARENT, - ), - strokeWidthDp = array.getRawDimension( - context = context, - index = R.styleable.LineComponent_strokeWidth, - defaultValue = 0f, - ), - ) -} - -internal fun TypedArray.getComponent( - context: Context, -): Component? = use { array -> - - if (!hasValue(R.styleable.ComponentStyle_color)) { - return@use null - } - - val overlayingComponent = if (hasValue(R.styleable.ComponentStyle_overlayingComponentStyle)) { - getNestedTypedArray( - context = context, - resourceId = R.styleable.ComponentStyle_overlayingComponentStyle, - styleableResourceId = R.styleable.ComponentStyle, - ).getComponent(context) - } else { - null +): LineComponent = + use { array -> + LineComponent( + color = + array.getColorExtended( + index = R.styleable.LineComponent_color, + defaultColor = defaultColor, + ), + thicknessDp = + array.getRawDimension( + context = context, + index = R.styleable.LineComponent_thickness, + defaultValue = defaultThickness, + ), + shape = + if (hasValue(R.styleable.LineComponent_shapeStyle)) { + getNestedTypedArray( + context = context, + resourceId = R.styleable.LineComponent_shapeStyle, + styleableResourceId = R.styleable.Shape, + ).getShape( + context = context, + ) + } else { + defaultShape + }, + strokeColor = + array.getColorExtended( + index = R.styleable.LineComponent_strokeColor, + defaultColor = Color.TRANSPARENT, + ), + strokeWidthDp = + array.getRawDimension( + context = context, + index = R.styleable.LineComponent_strokeWidth, + defaultValue = 0f, + ), + ) } - val baseComponent = ShapeComponent( - color = array.getColorExtended(index = R.styleable.ComponentStyle_color), - shape = getNestedTypedArray( - context = context, - resourceId = R.styleable.ComponentStyle_shapeStyle, - styleableResourceId = R.styleable.Shape, - ).getShape(context), - strokeColor = array.getColorExtended( - index = R.styleable.ComponentStyle_strokeColor, - defaultColor = Color.TRANSPARENT, - ), - strokeWidthDp = array.getRawDimension( - context = context, - index = R.styleable.ComponentStyle_strokeWidth, - defaultValue = 0f, - ), - ) +internal fun TypedArray.getComponent(context: Context): Component? = + use { array -> + + if (!hasValue(R.styleable.ComponentStyle_color)) { + return@use null + } + + val overlayingComponent = + if (hasValue(R.styleable.ComponentStyle_overlayingComponentStyle)) { + getNestedTypedArray( + context = context, + resourceId = R.styleable.ComponentStyle_overlayingComponentStyle, + styleableResourceId = R.styleable.ComponentStyle, + ).getComponent(context) + } else { + null + } + + val baseComponent = + ShapeComponent( + color = array.getColorExtended(index = R.styleable.ComponentStyle_color), + shape = + getNestedTypedArray( + context = context, + resourceId = R.styleable.ComponentStyle_shapeStyle, + styleableResourceId = R.styleable.Shape, + ).getShape(context), + strokeColor = + array.getColorExtended( + index = R.styleable.ComponentStyle_strokeColor, + defaultColor = Color.TRANSPARENT, + ), + strokeWidthDp = + array.getRawDimension( + context = context, + index = R.styleable.ComponentStyle_strokeWidth, + defaultValue = 0f, + ), + ) - if (overlayingComponent != null) { - OverlayingComponent( - outer = baseComponent, - inner = overlayingComponent, - innerPaddingAllDp = getRawDimension( - context = context, - index = R.styleable.ComponentStyle_overlayingComponentPadding, - defaultValue = 0f, - ), - ) - } else { - baseComponent + if (overlayingComponent != null) { + OverlayingComponent( + outer = baseComponent, + inner = overlayingComponent, + innerPaddingAllDp = + getRawDimension( + context = context, + index = R.styleable.ComponentStyle_overlayingComponentPadding, + defaultValue = 0f, + ), + ) + } else { + baseComponent + } } -} internal fun TypedArray.getLineSpec( context: Context, defaultColor: Int = context.defaultColors.entity1Color.toInt(), ): LineChart.LineSpec { - val positiveLineColor = getColor( - R.styleable.LineSpec_positiveColor, - getColor(R.styleable.LineSpec_color, defaultColor), - ) + val positiveLineColor = + getColor( + R.styleable.LineSpec_positiveColor, + getColor(R.styleable.LineSpec_color, defaultColor), + ) val negativeLineColor = getColorExtended(R.styleable.LineSpec_negativeColor) - val lineShader = if (hasValue(R.styleable.LineSpec_negativeColor)) { - HorizontalSplitShader.Solid(positiveLineColor, negativeLineColor) - } else { - SolidShader(positiveLineColor) - } - - val positiveShader = if ( - hasValue(R.styleable.LineSpec_gradientTopColor) || - hasValue(R.styleable.LineSpec_gradientBottomColor) || - hasValue(R.styleable.LineSpec_positiveGradientTopColor) || - hasValue(R.styleable.LineSpec_positiveGradientBottomColor) - ) { - val gradientTopColor = getColorExtended( - index = R.styleable.LineSpec_positiveGradientTopColor, - defaultColor = getColorExtended(R.styleable.LineSpec_gradientTopColor), - ) - val gradientBottomColor = getColorExtended( - index = R.styleable.LineSpec_positiveGradientBottomColor, - defaultColor = getColorExtended(R.styleable.LineSpec_gradientBottomColor), - ) + val lineShader = + if (hasValue(R.styleable.LineSpec_negativeColor)) { + HorizontalSplitShader.Solid(positiveLineColor, negativeLineColor) + } else { + SolidShader(positiveLineColor) + } + + val positiveShader = + if ( + hasValue(R.styleable.LineSpec_gradientTopColor) || + hasValue(R.styleable.LineSpec_gradientBottomColor) || + hasValue(R.styleable.LineSpec_positiveGradientTopColor) || + hasValue(R.styleable.LineSpec_positiveGradientBottomColor) + ) { + val gradientTopColor = + getColorExtended( + index = R.styleable.LineSpec_positiveGradientTopColor, + defaultColor = getColorExtended(R.styleable.LineSpec_gradientTopColor), + ) + val gradientBottomColor = + getColorExtended( + index = R.styleable.LineSpec_positiveGradientBottomColor, + defaultColor = getColorExtended(R.styleable.LineSpec_gradientBottomColor), + ) + + DynamicShaders.verticalGradient(gradientTopColor, gradientBottomColor) + } else { + DynamicShaders.verticalGradient( + positiveLineColor.copyColor(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_START), + positiveLineColor.copyColor(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_END), + ) + } - DynamicShaders.verticalGradient(gradientTopColor, gradientBottomColor) - } else { - DynamicShaders.verticalGradient( - positiveLineColor.copyColor(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_START), - positiveLineColor.copyColor(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_END), - ) - } + val negativeShader = + if ( + hasValue(R.styleable.LineSpec_negativeGradientTopColor) || + hasValue(R.styleable.LineSpec_negativeGradientBottomColor) + ) { + val gradientTopColor = getColorExtended(R.styleable.LineSpec_negativeGradientTopColor) + val gradientBottomColor = getColorExtended(R.styleable.LineSpec_negativeGradientBottomColor) - val negativeShader = if ( - hasValue(R.styleable.LineSpec_negativeGradientTopColor) || - hasValue(R.styleable.LineSpec_negativeGradientBottomColor) - ) { - val gradientTopColor = getColorExtended(R.styleable.LineSpec_negativeGradientTopColor) - val gradientBottomColor = getColorExtended(R.styleable.LineSpec_negativeGradientBottomColor) - - DynamicShaders.verticalGradient(gradientTopColor, gradientBottomColor) - } else { - DynamicShaders.verticalGradient( - negativeLineColor.copyColor(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_END), - negativeLineColor.copyColor(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_START), - ) - } + DynamicShaders.verticalGradient(gradientTopColor, gradientBottomColor) + } else { + DynamicShaders.verticalGradient( + negativeLineColor.copyColor(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_END), + negativeLineColor.copyColor(alpha = DefaultAlpha.LINE_BACKGROUND_SHADER_START), + ) + } return LineChart.LineSpec( lineShader = lineShader, - point = getNestedTypedArray( - context = context, - resourceId = R.styleable.LineSpec_pointStyle, - styleableResourceId = R.styleable.ComponentStyle, - ).getComponent(context), - pointSizeDp = getRawDimension( - context = context, - index = R.styleable.LineSpec_pointSize, - defaultValue = DefaultDimens.POINT_SIZE, - ), - lineThicknessDp = getRawDimension( - context = context, - index = R.styleable.LineSpec_lineThickness, - defaultValue = DefaultDimens.LINE_THICKNESS, - ), - lineBackgroundShader = HorizontalSplitShader.Double(positiveShader, negativeShader), - dataLabel = if (getBoolean(R.styleable.LineSpec_showDataLabels, false)) { + point = getNestedTypedArray( context = context, - resourceId = R.styleable.LineSpec_dataLabelStyle, - styleableResourceId = R.styleable.TextComponentStyle, - ).getTextComponent(context = context) - } else { - null - }, - dataLabelVerticalPosition = getInteger(R.styleable.LineSpec_dataLabelVerticalPosition, 0).let { value -> - val values = VerticalPosition.entries - values[value % values.size] - }, - dataLabelRotationDegrees = getFloat( - R.styleable.LineSpec_dataLabelRotationDegrees, - 0f, - ), - pointConnector = DefaultPointConnector( - cubicStrength = getFraction( - index = R.styleable.LineSpec_cubicStrength, - defaultValue = DefaultDimens.CUBIC_STRENGTH, + resourceId = R.styleable.LineSpec_pointStyle, + styleableResourceId = R.styleable.ComponentStyle, + ).getComponent(context), + pointSizeDp = + getRawDimension( + context = context, + index = R.styleable.LineSpec_pointSize, + defaultValue = DefaultDimens.POINT_SIZE, + ), + lineThicknessDp = + getRawDimension( + context = context, + index = R.styleable.LineSpec_lineThickness, + defaultValue = DefaultDimens.LINE_THICKNESS, + ), + lineBackgroundShader = HorizontalSplitShader.Double(positiveShader, negativeShader), + dataLabel = + if (getBoolean(R.styleable.LineSpec_showDataLabels, false)) { + getNestedTypedArray( + context = context, + resourceId = R.styleable.LineSpec_dataLabelStyle, + styleableResourceId = R.styleable.TextComponentStyle, + ).getTextComponent(context = context) + } else { + null + }, + dataLabelVerticalPosition = + getInteger(R.styleable.LineSpec_dataLabelVerticalPosition, 0).let { value -> + val values = VerticalPosition.entries + values[value % values.size] + }, + dataLabelRotationDegrees = + getFloat( + R.styleable.LineSpec_dataLabelRotationDegrees, + 0f, + ), + pointConnector = + DefaultPointConnector( + cubicStrength = + getFraction( + index = R.styleable.LineSpec_cubicStrength, + defaultValue = DefaultDimens.CUBIC_STRENGTH, + ), ), - ), ) } diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ShapeStyleExtensions.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ShapeStyleExtensions.kt index 42b330ed9..b29325fe5 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ShapeStyleExtensions.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ShapeStyleExtensions.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. @@ -31,42 +31,47 @@ import com.patrykandpatrick.vico.views.R private const val ONE_HUNDRED_PERCENT = 100 -internal fun TypedArray.getShape( - context: Context, -): Shape { - val shape = CorneredShape( - topLeft = getCorner( - context = context, - sizeIndex = R.styleable.Shape_topStartCornerSize, - treatmentIndex = R.styleable.Shape_topStartCornerTreatment, - ), - topRight = getCorner( - context = context, - sizeIndex = R.styleable.Shape_topEndCornerSize, - treatmentIndex = R.styleable.Shape_topEndCornerTreatment, - ), - bottomLeft = getCorner( +internal fun TypedArray.getShape(context: Context): Shape { + val shape = + CorneredShape( + topLeft = + getCorner( + context = context, + sizeIndex = R.styleable.Shape_topStartCornerSize, + treatmentIndex = R.styleable.Shape_topStartCornerTreatment, + ), + topRight = + getCorner( + context = context, + sizeIndex = R.styleable.Shape_topEndCornerSize, + treatmentIndex = R.styleable.Shape_topEndCornerTreatment, + ), + bottomLeft = + getCorner( + context = context, + sizeIndex = R.styleable.Shape_bottomStartCornerSize, + treatmentIndex = R.styleable.Shape_bottomStartCornerTreatment, + ), + bottomRight = + getCorner( + context = context, + sizeIndex = R.styleable.Shape_bottomEndCornerSize, + treatmentIndex = R.styleable.Shape_bottomEndCornerTreatment, + ), + ) + + val dashLength = + getRawDimension( context = context, - sizeIndex = R.styleable.Shape_bottomStartCornerSize, - treatmentIndex = R.styleable.Shape_bottomStartCornerTreatment, - ), - bottomRight = getCorner( + index = R.styleable.Shape_dashLength, + defaultValue = 0f, + ) + val dashGapLength = + getRawDimension( context = context, - sizeIndex = R.styleable.Shape_bottomEndCornerSize, - treatmentIndex = R.styleable.Shape_bottomEndCornerTreatment, - ), - ) - - val dashLength = getRawDimension( - context = context, - index = R.styleable.Shape_dashLength, - defaultValue = 0f, - ) - val dashGapLength = getRawDimension( - context = context, - index = R.styleable.Shape_dashGapLength, - defaultValue = 0f, - ) + index = R.styleable.Shape_dashGapLength, + defaultValue = 0f, + ) return if (dashLength == 0f) { shape @@ -84,38 +89,41 @@ private fun TypedArray.getCorner( @StyleableRes sizeIndex: Int, @StyleableRes treatmentIndex: Int, handleNullSizeIndex: Boolean = true, -): Corner = when { - !hasValue(sizeIndex) && handleNullSizeIndex -> { - getCorner( - context = context, - sizeIndex = R.styleable.Shape_cornerSize, - treatmentIndex = treatmentIndex, - handleNullSizeIndex = false, - ) - } - isFraction(sizeIndex) -> { - val percentage = (getFraction(sizeIndex, defaultValue = 0f) * ONE_HUNDRED_PERCENT).toInt() - Corner.Relative( - percentage = percentage, - cornerTreatment = if (percentage == 0) { - SharpCornerTreatment - } else { - getCornerTreatment(treatmentIndex) - }, - ) +): Corner = + when { + !hasValue(sizeIndex) && handleNullSizeIndex -> { + getCorner( + context = context, + sizeIndex = R.styleable.Shape_cornerSize, + treatmentIndex = treatmentIndex, + handleNullSizeIndex = false, + ) + } + isFraction(sizeIndex) -> { + val percentage = (getFraction(sizeIndex, defaultValue = 0f) * ONE_HUNDRED_PERCENT).toInt() + Corner.Relative( + percentage = percentage, + cornerTreatment = + if (percentage == 0) { + SharpCornerTreatment + } else { + getCornerTreatment(treatmentIndex) + }, + ) + } + else -> { + val sizeDp = getRawDimension(context, sizeIndex, defaultValue = 0f) + Corner.Absolute( + sizeDp = sizeDp, + cornerTreatment = + if (sizeDp == 0f) { + SharpCornerTreatment + } else { + getCornerTreatment(treatmentIndex) + }, + ) + } } - else -> { - val sizeDp = getRawDimension(context, sizeIndex, defaultValue = 0f) - Corner.Absolute( - sizeDp = sizeDp, - cornerTreatment = if (sizeDp == 0f) { - SharpCornerTreatment - } else { - getCornerTreatment(treatmentIndex) - }, - ) - } -} private fun TypedArray.getCornerTreatment( @StyleableRes index: Int, diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/TextComponentStyleExtensions.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/TextComponentStyleExtensions.kt index e3b300c17..d97333a99 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/TextComponentStyleExtensions.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/TextComponentStyleExtensions.kt @@ -38,32 +38,33 @@ import com.patrykandpatrick.vico.views.extension.defaultColors private const val FONT_WEIGHT_NORMAL = 400 -internal fun TypedArray.getTextComponent( - context: Context, -): TextComponent = use { - val color = getColor(R.styleable.TextComponentStyle_labelColor, context.defaultColors.axisLabelColor.toInt()) - val background = getNestedTypedArray( - context = context, - resourceId = R.styleable.TextComponentStyle_backgroundStyle, - styleableResourceId = R.styleable.ComponentStyle, - ).getComponent(context) +internal fun TypedArray.getTextComponent(context: Context): TextComponent = + use { + val color = getColor(R.styleable.TextComponentStyle_labelColor, context.defaultColors.axisLabelColor.toInt()) + val background = + getNestedTypedArray( + context = context, + resourceId = R.styleable.TextComponentStyle_backgroundStyle, + styleableResourceId = R.styleable.ComponentStyle, + ).getComponent(context) - textComponent { - this.color = color - this.background = background - this.padding = getPadding(context) - this.margins = getMargins(context) - this.textSizeSp = getRawDimension( - context = context, - index = R.styleable.TextComponentStyle_android_textSize, - defaultValue = TEXT_COMPONENT_TEXT_SIZE, - ) - this.lineCount = getInteger(R.styleable.TextComponentStyle_android_maxLines, DEF_LABEL_LINE_COUNT) - this.ellipsize = getTruncateAt() - getTypeface(context)?.let { this.typeface = it } - this.textAlignment = getTextAlignment() + textComponent { + this.color = color + this.background = background + this.padding = getPadding(context) + this.margins = getMargins(context) + this.textSizeSp = + getRawDimension( + context = context, + index = R.styleable.TextComponentStyle_android_textSize, + defaultValue = TEXT_COMPONENT_TEXT_SIZE, + ) + this.lineCount = getInteger(R.styleable.TextComponentStyle_android_maxLines, DEF_LABEL_LINE_COUNT) + this.ellipsize = getTruncateAt() + getTypeface(context)?.let { this.typeface = it } + this.textAlignment = getTextAlignment() + } } -} private fun TypedArray.getTruncateAt(): TextUtils.TruncateAt { val int = getInt(R.styleable.TextComponentStyle_android_ellipsize, TextUtils.TruncateAt.END.ordinal) @@ -74,33 +75,35 @@ private fun TypedArray.getTruncateAt(): TextUtils.TruncateAt { } } -@Suppress("MagicNumber") private fun TypedArray.getTypeface(context: Context): Typeface? { - val fontIndex = if (hasValue(R.styleable.TextComponentStyle_android_fontFamily)) { - R.styleable.TextComponentStyle_android_fontFamily - } else { - R.styleable.TextComponentStyle_fontFamily - } + val fontIndex = + if (hasValue(R.styleable.TextComponentStyle_android_fontFamily)) { + R.styleable.TextComponentStyle_android_fontFamily + } else { + R.styleable.TextComponentStyle_fontFamily + } - val fontStyle = if (hasValue(R.styleable.TextComponentStyle_android_fontStyle)) { - R.styleable.TextComponentStyle_android_fontStyle - } else { - R.styleable.TextComponentStyle_fontStyle - } + val fontStyle = + if (hasValue(R.styleable.TextComponentStyle_android_fontStyle)) { + R.styleable.TextComponentStyle_android_fontStyle + } else { + R.styleable.TextComponentStyle_fontStyle + } val fontResId = getResourceId(fontIndex, 0) val fontWeight = getInteger(R.styleable.TextComponentStyle_android_textFontWeight, FONT_WEIGHT_NORMAL) - val family = if (fontResId > 0) { - ResourcesCompat.getFont(context, fontResId) - } else { - when (getInteger(R.styleable.TextComponentStyle_typeface, 3)) { - 0 -> Typeface.DEFAULT - 1 -> Typeface.SANS_SERIF - 2 -> Typeface.SERIF - else -> Typeface.MONOSPACE + val family = + if (fontResId > 0) { + ResourcesCompat.getFont(context, fontResId) + } else { + when (getInteger(R.styleable.TextComponentStyle_typeface, 3)) { + 0 -> Typeface.DEFAULT + 1 -> Typeface.SANS_SERIF + 2 -> Typeface.SERIF + else -> Typeface.MONOSPACE + } } - } return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { Typeface.create(family, fontWeight, fontStyle == 1) } else { @@ -109,8 +112,9 @@ private fun TypedArray.getTypeface(context: Context): Typeface? { } private fun TypedArray.getPadding(context: Context): MutableDimensions { - fun getDpDimension(@StyleableRes index: Int): Float = - getRawDimension(context, index, -1f) + fun getDpDimension( + @StyleableRes index: Int, + ): Float = getRawDimension(context, index, -1f) val padding = getDpDimension(R.styleable.TextComponentStyle_android_padding) val paddingVertical = getDpDimension(R.styleable.TextComponentStyle_android_paddingVertical) @@ -120,20 +124,25 @@ private fun TypedArray.getPadding(context: Context): MutableDimensions { val paddingEnd = getDpDimension(R.styleable.TextComponentStyle_android_paddingEnd) val paddingBottom = getDpDimension(R.styleable.TextComponentStyle_android_paddingBottom) return MutableDimensions( - startDp = firstNonNegativeOf(paddingStart, paddingHorizontal, padding) - ?: AXIS_LABEL_HORIZONTAL_PADDING.toFloat(), - topDp = firstNonNegativeOf(paddingTop, paddingVertical, padding) - ?: AXIS_LABEL_VERTICAL_PADDING.toFloat(), - endDp = firstNonNegativeOf(paddingEnd, paddingHorizontal, padding) - ?: AXIS_LABEL_HORIZONTAL_PADDING.toFloat(), - bottomDp = firstNonNegativeOf(paddingBottom, paddingVertical, padding) - ?: AXIS_LABEL_VERTICAL_PADDING.toFloat(), + startDp = + firstNonNegativeOf(paddingStart, paddingHorizontal, padding) + ?: AXIS_LABEL_HORIZONTAL_PADDING.toFloat(), + topDp = + firstNonNegativeOf(paddingTop, paddingVertical, padding) + ?: AXIS_LABEL_VERTICAL_PADDING.toFloat(), + endDp = + firstNonNegativeOf(paddingEnd, paddingHorizontal, padding) + ?: AXIS_LABEL_HORIZONTAL_PADDING.toFloat(), + bottomDp = + firstNonNegativeOf(paddingBottom, paddingVertical, padding) + ?: AXIS_LABEL_VERTICAL_PADDING.toFloat(), ) } private fun TypedArray.getMargins(context: Context): MutableDimensions { - fun getDpDimension(@StyleableRes index: Int): Float = - getRawDimension(context, index, -1f) + fun getDpDimension( + @StyleableRes index: Int, + ): Float = getRawDimension(context, index, -1f) val padding = getDpDimension(R.styleable.TextComponentStyle_margin) val paddingVertical = getDpDimension(R.styleable.TextComponentStyle_marginVertical) diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ThemeHandler.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ThemeHandler.kt index 0480a1dd5..24f49fb30 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ThemeHandler.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/ThemeHandler.kt @@ -52,7 +52,6 @@ internal class ThemeHandler( attrs: AttributeSet?, chartType: ChartType, ) { - public var startAxis: Axis? = null private set @@ -86,33 +85,39 @@ internal class ThemeHandler( init { context.obtainStyledAttributes(attrs, R.styleable.BaseChartView).use { typedArray -> if (typedArray.getBoolean(R.styleable.BaseChartView_showStartAxis, false)) { - startAxis = typedArray.getAxisBuilder( - R.styleable.BaseChartView_startAxisStyle, - VerticalAxis.Builder(), - ).build() + startAxis = + typedArray.getAxisBuilder( + R.styleable.BaseChartView_startAxisStyle, + VerticalAxis.Builder(), + ).build() } if (typedArray.getBoolean(R.styleable.BaseChartView_showTopAxis, false)) { - topAxis = typedArray.getAxisBuilder( - R.styleable.BaseChartView_topAxisStyle, - HorizontalAxis.Builder(), - ).build() + topAxis = + typedArray.getAxisBuilder( + R.styleable.BaseChartView_topAxisStyle, + HorizontalAxis.Builder(), + ).build() } if (typedArray.getBoolean(R.styleable.BaseChartView_showEndAxis, false)) { - endAxis = typedArray.getAxisBuilder( - R.styleable.BaseChartView_endAxisStyle, - VerticalAxis.Builder(), - ).build() + endAxis = + typedArray.getAxisBuilder( + R.styleable.BaseChartView_endAxisStyle, + VerticalAxis.Builder(), + ).build() } if (typedArray.getBoolean(R.styleable.BaseChartView_showBottomAxis, false)) { - bottomAxis = typedArray.getAxisBuilder( - R.styleable.BaseChartView_bottomAxisStyle, - HorizontalAxis.Builder(), - ).build() + bottomAxis = + typedArray.getAxisBuilder( + R.styleable.BaseChartView_bottomAxisStyle, + HorizontalAxis.Builder(), + ).build() } - isHorizontalScrollEnabled = typedArray - .getBoolean(R.styleable.BaseChartView_chartHorizontalScrollingEnabled, true) - isChartZoomEnabled = typedArray - .getBoolean(R.styleable.BaseChartView_chartZoomEnabled, true) + isHorizontalScrollEnabled = + typedArray + .getBoolean(R.styleable.BaseChartView_chartHorizontalScrollingEnabled, true) + isChartZoomEnabled = + typedArray + .getBoolean(R.styleable.BaseChartView_chartZoomEnabled, true) fadingEdges = typedArray.getFadingEdges() horizontalLayout = typedArray.getHorizontalLayout() @@ -149,83 +154,94 @@ internal class ThemeHandler( styleableResourceId = styleableResourceId, ).getLineComponent(context = context, defaultShape = defaultShape) - val axisStyle = getNestedTypedArray( - context = context, - resourceId = if (hasValue(styleAttrId)) styleAttrId else R.styleable.BaseChartView_axisStyle, - styleableResourceId = R.styleable.Axis, - ) + val axisStyle = + getNestedTypedArray( + context = context, + resourceId = if (hasValue(styleAttrId)) styleAttrId else R.styleable.BaseChartView_axisStyle, + styleableResourceId = R.styleable.Axis, + ) return builder.apply { - axis = axisStyle - .takeIf { it.getBoolean(R.styleable.Axis_showAxisLine, true) } - ?.getLineComponent( - resourceId = R.styleable.Axis_axisLineStyle, - styleableResourceId = R.styleable.LineComponent, - ) - tick = axisStyle - .takeIf { it.getBoolean(R.styleable.Axis_showTick, true) } - ?.getLineComponent( - resourceId = R.styleable.Axis_axisTickStyle, - styleableResourceId = R.styleable.LineComponent, + axis = + axisStyle + .takeIf { it.getBoolean(R.styleable.Axis_showAxisLine, true) } + ?.getLineComponent( + resourceId = R.styleable.Axis_axisLineStyle, + styleableResourceId = R.styleable.LineComponent, + ) + tick = + axisStyle + .takeIf { it.getBoolean(R.styleable.Axis_showTick, true) } + ?.getLineComponent( + resourceId = R.styleable.Axis_axisTickStyle, + styleableResourceId = R.styleable.LineComponent, + ) + tickLengthDp = + axisStyle.getRawDimension( + context = context, + R.styleable.Axis_axisTickLength, + defaultValue = DefaultDimens.AXIS_TICK_LENGTH, ) - tickLengthDp = axisStyle.getRawDimension( - context = context, - R.styleable.Axis_axisTickLength, - defaultValue = DefaultDimens.AXIS_TICK_LENGTH, - ) - guideline = axisStyle - .takeIf { it.getBoolean(R.styleable.Axis_showGuideline, true) } - ?.getLineComponent( - resourceId = R.styleable.Axis_axisGuidelineStyle, - styleableResourceId = R.styleable.LineComponent, - defaultShape = DashedShape(), + guideline = + axisStyle + .takeIf { it.getBoolean(R.styleable.Axis_showGuideline, true) } + ?.getLineComponent( + resourceId = R.styleable.Axis_axisGuidelineStyle, + styleableResourceId = R.styleable.LineComponent, + defaultShape = DashedShape(), + ) + labelRotationDegrees = + axisStyle.getFloat( + R.styleable.Axis_labelRotationDegrees, + 0f, ) - labelRotationDegrees = axisStyle.getFloat( - R.styleable.Axis_labelRotationDegrees, - 0f, - ) - label = axisStyle.getNestedTypedArray( - context = context, - resourceId = R.styleable.Axis_axisLabelStyle, - styleableResourceId = R.styleable.TextComponentStyle, - ).getTextComponent(context = context) - titleComponent = if (axisStyle.getBoolean(R.styleable.Axis_showTitle, false)) { + label = axisStyle.getNestedTypedArray( context = context, - resourceId = R.styleable.Axis_titleStyle, + resourceId = R.styleable.Axis_axisLabelStyle, styleableResourceId = R.styleable.TextComponentStyle, ).getTextComponent(context = context) - } else { - null - } + titleComponent = + if (axisStyle.getBoolean(R.styleable.Axis_showTitle, false)) { + axisStyle.getNestedTypedArray( + context = context, + resourceId = R.styleable.Axis_titleStyle, + styleableResourceId = R.styleable.TextComponentStyle, + ).getTextComponent(context = context) + } else { + null + } title = axisStyle.getString(R.styleable.Axis_title) when (this) { is VerticalAxis.Builder<*> -> { - horizontalLabelPosition = axisStyle - .getInteger(R.styleable.Axis_verticalAxisHorizontalLabelPosition, 0) - .let { value -> - val values = VerticalAxis.HorizontalLabelPosition.entries - values[value % values.size] - } - - verticalLabelPosition = axisStyle - .getInteger(R.styleable.Axis_verticalAxisVerticalLabelPosition, 0) - .let { value -> - val values = VerticalAxis.VerticalLabelPosition.entries - values[value % values.size] - } + horizontalLabelPosition = + axisStyle + .getInteger(R.styleable.Axis_verticalAxisHorizontalLabelPosition, 0) + .let { value -> + val values = VerticalAxis.HorizontalLabelPosition.entries + values[value % values.size] + } + + verticalLabelPosition = + axisStyle + .getInteger(R.styleable.Axis_verticalAxisVerticalLabelPosition, 0) + .let { value -> + val values = VerticalAxis.VerticalLabelPosition.entries + values[value % values.size] + } itemPlacer = axisStyle.getVerticalAxisItemPlacer() } is HorizontalAxis.Builder<*> -> { - itemPlacer = AxisItemPlacer.Horizontal.default( - axisStyle.getInteger(R.styleable.Axis_horizontalAxisLabelSpacing, 1), - axisStyle.getInteger(R.styleable.Axis_horizontalAxisLabelOffset, 0), - axisStyle.getBoolean(R.styleable.Axis_shiftExtremeHorizontalAxisTicks, true), - axisStyle.getBoolean(R.styleable.Axis_addExtremeHorizontalAxisLabelPadding, false), - ) + itemPlacer = + AxisItemPlacer.Horizontal.default( + axisStyle.getInteger(R.styleable.Axis_horizontalAxisLabelSpacing, 1), + axisStyle.getInteger(R.styleable.Axis_horizontalAxisLabelOffset, 0), + axisStyle.getBoolean(R.styleable.Axis_shiftExtremeHorizontalAxisTicks, true), + axisStyle.getBoolean(R.styleable.Axis_addExtremeHorizontalAxisLabelPadding, false), + ) } } }.also { axisStyle.recycle() } @@ -245,12 +261,13 @@ internal class ThemeHandler( private fun TypedArray.getHorizontalLayout(): HorizontalLayout = when (getInt(R.styleable.BaseChartView_horizontalLayout, 0)) { 0 -> HorizontalLayout.Segmented - else -> HorizontalLayout.FullWidth( - getRawDimension(context, R.styleable.BaseChartView_scalableStartContentPadding, 0f), - getRawDimension(context, R.styleable.BaseChartView_scalableEndContentPadding, 0f), - getRawDimension(context, R.styleable.BaseChartView_unscalableStartContentPadding, 0f), - getRawDimension(context, R.styleable.BaseChartView_unscalableEndContentPadding, 0f), - ) + else -> + HorizontalLayout.FullWidth( + getRawDimension(context, R.styleable.BaseChartView_scalableStartContentPadding, 0f), + getRawDimension(context, R.styleable.BaseChartView_scalableEndContentPadding, 0f), + getRawDimension(context, R.styleable.BaseChartView_unscalableStartContentPadding, 0f), + getRawDimension(context, R.styleable.BaseChartView_unscalableEndContentPadding, 0f), + ) } private fun getComposedChart( @@ -259,19 +276,21 @@ internal class ThemeHandler( ): Chart>? { val chartFlags = composedChartViewTypedArray.getInt(R.styleable.ComposedChartView_charts, 0) - val columnChart: ColumnChart? = if (chartFlags.hasAnyFlagOf(COLUMN_CHART, STACKED_COLUMN_CHART)) { - baseTypedArray.getColumnChart( - context, - mergeMode = if (chartFlags.hasFlag(STACKED_COLUMN_CHART)) MergeMode.Stack else MergeMode.Grouped, - ) - } else { - null - } - val lineChart = if (chartFlags.hasFlag(LINE_CHART)) { - baseTypedArray.getLineChart(context) - } else { - null - } + val columnChart: ColumnChart? = + if (chartFlags.hasAnyFlagOf(COLUMN_CHART, STACKED_COLUMN_CHART)) { + baseTypedArray.getColumnChart( + context, + mergeMode = if (chartFlags.hasFlag(STACKED_COLUMN_CHART)) MergeMode.Stack else MergeMode.Grouped, + ) + } else { + null + } + val lineChart = + if (chartFlags.hasFlag(LINE_CHART)) { + baseTypedArray.getLineChart(context) + } else { + null + } return when { columnChart != null && lineChart != null -> ComposedChart(columnChart, lineChart) @@ -281,35 +300,36 @@ internal class ThemeHandler( } } - @Suppress("TooGenericExceptionCaught") private fun TypedArray.getFadingEdges(): FadingEdges? { val edgesLength = getRawDimension(context, R.styleable.BaseChartView_fadingEdgeWidth, 0f) val startLength = getRawDimension(context, R.styleable.BaseChartView_startFadingEdgeWidth, edgesLength) val endLength = getRawDimension(context, R.styleable.BaseChartView_endFadingEdgeWidth, edgesLength) - val threshold = getRawDimension( - context, - R.styleable.BaseChartView_fadingEdgeVisibilityThreshold, - FADING_EDGE_VISIBILITY_THRESHOLD_DP, - ) + val threshold = + getRawDimension( + context, + R.styleable.BaseChartView_fadingEdgeVisibilityThreshold, + FADING_EDGE_VISIBILITY_THRESHOLD_DP, + ) return if (startLength > 0f || endLength > 0f) { val interpolatorClassName = getString(R.styleable.BaseChartView_fadingEdgeVisibilityInterpolator) - val interpolator = if (interpolatorClassName != null) { - try { - context.classLoader.loadClass(interpolatorClassName).getDeclaredConstructor().newInstance() - as? TimeInterpolator - } catch (e: Exception) { - Log.e( - "ChartView", - "Caught exception when trying to instantiate $interpolatorClassName " + - "as fade interpolator.", - ) + val interpolator = + if (interpolatorClassName != null) { + try { + context.classLoader.loadClass(interpolatorClassName).getDeclaredConstructor().newInstance() + as? TimeInterpolator + } catch (e: Exception) { + Log.e( + "ChartView", + "Caught exception when trying to instantiate $interpolatorClassName " + + "as fade interpolator.", + ) + null + } + } else { null } - } else { - null - } FadingEdges( startEdgeWidthDp = startLength, diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/TypedArrayExtensions.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/TypedArrayExtensions.kt index 860096433..775665ca9 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/TypedArrayExtensions.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/theme/TypedArrayExtensions.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. @@ -31,8 +31,7 @@ private val lock = Any() /** * Calls the given function block with this [TypedArray] as its argument, then recycles this [TypedArray]. */ -public inline fun TypedArray.use(block: (TypedArray) -> R): R = - block(this).also { recycle() } +public inline fun TypedArray.use(block: (TypedArray) -> R): R = block(this).also { recycle() } /** * Retrieves the color at the given index. @@ -50,14 +49,15 @@ public fun TypedArray.getRawDimension( context: Context, @StyleableRes index: Int, defaultValue: Float, -): Float = synchronized(lock) { - if (getValue(index, rawValueTypedValue)) { - rawValueTypedValue.getDimension(context.resources.displayMetrics) / context.density - TypedValue.complexToFloat(rawValueTypedValue.data) - } else { - defaultValue +): Float = + synchronized(lock) { + if (getValue(index, rawValueTypedValue)) { + rawValueTypedValue.getDimension(context.resources.displayMetrics) / context.density + TypedValue.complexToFloat(rawValueTypedValue.data) + } else { + defaultValue + } } -} /** * Returns a [TypedArray] nested inside the receiver [TypedArray]. @@ -73,19 +73,25 @@ public fun TypedArray.getNestedTypedArray( /** * Retrieves the fraction at the given index as a [Float]. */ -public fun TypedArray.getFraction(@StyleableRes index: Int, defaultValue: Float = -1f): Float = - getFraction(index, 1, 1, defaultValue) +public fun TypedArray.getFraction( + @StyleableRes index: Int, + defaultValue: Float = -1f, +): Float = getFraction(index, 1, 1, defaultValue) /** * Returns a boolean indicating whether the value at the given index is a fraction. */ -public fun TypedArray.isFraction(@StyleableRes index: Int): Boolean = - getTypeCompat(index) == TypedValue.TYPE_FRACTION +public fun TypedArray.isFraction( + @StyleableRes index: Int, +): Boolean = getTypeCompat(index) == TypedValue.TYPE_FRACTION /** * Returns the type of the value at the given index. */ -public fun TypedArray.getTypeCompat(@StyleableRes index: Int): Int = synchronized(lock) { - getValue(index, typeCompatTypedValue) - typeCompatTypedValue.type -} +public fun TypedArray.getTypeCompat( + @StyleableRes index: Int, +): Int = + synchronized(lock) { + getValue(index, typeCompatTypedValue) + typeCompatTypedValue.type + } From 84e011588585b3ff33b518b6903b7afe87dfe4a0 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Fri, 1 Dec 2023 18:24:57 +0100 Subject: [PATCH 15/39] Rework selected core APIs Co-authored-by: Patryk Goworowski --- .../vico/sample/previews/AxisLinePreviews.kt | 32 +- .../ColumnChartsWithNegativeValuesPreviews.kt | 50 +- .../sample/previews/ContainedChartsPreview.kt | 84 +- .../vico/sample/previews/LineChartPreviews.kt | 133 ++-- .../LineChartsWithNegativeValuesPreviews.kt | 82 +- .../vico/sample/previews/Previews.kt | 56 +- ...dColumnChartsWithNegativeValuesPreviews.kt | 80 +- .../sample/previews/ThresholdLinePreviews.kt | 280 +++---- .../composables/column/ColumnCharts.kt | 27 +- .../previews/composables/line/LineCharts.kt | 23 +- .../sample/previews/resource/SampleModels.kt | 15 +- .../vico/sample/showcase/ChartStyle.kt | 25 +- .../vico/sample/showcase/ShowcaseScreen.kt | 22 +- .../vico/sample/showcase/ShowcaseViewModel.kt | 73 +- .../vico/sample/showcase/charts/Chart1.kt | 31 +- .../vico/sample/showcase/charts/Chart2.kt | 34 +- .../vico/sample/showcase/charts/Chart3.kt | 37 +- .../vico/sample/showcase/charts/Chart4.kt | 62 +- .../vico/sample/showcase/charts/Chart5.kt | 76 +- .../vico/sample/showcase/charts/Chart6.kt | 36 +- .../vico/sample/showcase/charts/Chart7.kt | 35 +- .../vico/sample/showcase/charts/Chart8.kt | 52 +- .../vico/sample/showcase/charts/Chart9.kt | 126 +-- sample/src/main/res/layout/chart_1.xml | 7 +- sample/src/main/res/layout/chart_2.xml | 6 +- sample/src/main/res/layout/chart_3.xml | 6 +- sample/src/main/res/layout/chart_4.xml | 8 +- sample/src/main/res/layout/chart_5.xml | 6 +- sample/src/main/res/layout/chart_6.xml | 6 +- sample/src/main/res/layout/chart_7.xml | 6 +- sample/src/main/res/layout/chart_8.xml | 8 +- sample/src/main/res/layout/chart_9.xml | 8 +- .../main/res/values-night/chart_9_styles.xml | 4 +- sample/src/main/res/values/chart_1_styles.xml | 2 +- sample/src/main/res/values/chart_2_styles.xml | 2 +- sample/src/main/res/values/chart_3_styles.xml | 2 +- sample/src/main/res/values/chart_4_styles.xml | 4 +- sample/src/main/res/values/chart_5_styles.xml | 3 +- sample/src/main/res/values/chart_6_styles.xml | 2 +- sample/src/main/res/values/chart_7_styles.xml | 2 +- sample/src/main/res/values/chart_8_styles.xml | 5 +- sample/src/main/res/values/chart_9_styles.xml | 3 +- sample/src/main/res/values/strings.xml | 2 +- .../vico/compose/chart/CartesianChart.kt | 44 + .../{Charts.kt => CartesianChartHost.kt} | 122 ++- .../vico/compose/chart/column/ColumnChart.kt | 96 --- .../chart/layer/ColumnCartesianLayer.kt | 85 ++ .../compose/chart/layer/LineCartesianLayer.kt | 192 +++++ .../vico/compose/chart/line/LineChart.kt | 254 ------ .../compose/chart/scroll/ChartScrollSpec.kt | 16 +- .../compose/chart/scroll/ChartScrollState.kt | 4 +- .../component/shape/shader/DynamicShaders.kt | 19 +- .../compose/extension/ModifierExtensions.kt | 2 +- .../layout/MeasureContextExtensions.kt | 11 +- .../ChartEntryModelExtensions.kt | 61 +- .../state/CartesianChartModelWrapper.kt | 67 ++ .../compose/state/ChartEntryModelWrapper.kt | 68 -- .../vico/compose/style/ChartStyle.kt | 38 +- .../vico/core/axis/AxisItemPlacer.kt | 15 +- .../vico/core/axis/AxisManager.kt | 10 +- .../vico/core/axis/AxisPosition.kt | 28 +- .../vico/core/axis/AxisRenderer.kt | 11 +- .../DefaultHorizontalAxisItemPlacer.kt | 82 +- .../core/axis/horizontal/HorizontalAxis.kt | 32 +- .../vertical/DefaultVerticalAxisItemPlacer.kt | 30 +- .../vico/core/axis/vertical/VerticalAxis.kt | 25 +- .../vico/core/chart/BaseChart.kt | 144 ---- .../vico/core/chart/CartesianChart.kt | 309 ++++++++ .../patrykandpatrick/vico/core/chart/Chart.kt | 221 ------ .../vico/core/chart/ChartExtensions.kt | 41 - .../vico/core/chart/DefaultPointConnector.kt | 8 +- .../vico/core/chart/EntryModelExtensions.kt | 86 -- .../vico/core/chart/LineSpecExtensions.kt | 28 +- .../vico/core/chart/composed/ComposedChart.kt | 183 ----- .../chart/composed/ComposedChartEntryModel.kt | 29 - .../chart/composed/ComposedChartExtensions.kt | 39 - .../vico/core/chart/decoration/Decoration.kt | 16 +- .../core/chart/decoration/ThresholdLine.kt | 19 +- .../chart/dimensions/HorizontalDimensions.kt | 11 +- .../vico/core/chart/draw/ChartDrawContext.kt | 14 +- .../chart/draw/ChartDrawContextExtensions.kt | 17 +- .../vico/core/chart/insets/ChartInsetter.kt | 14 +- .../core/chart/insets/HorizontalInsets.kt | 4 +- .../core/chart/layer/BaseCartesianLayer.kt | 59 ++ .../vico/core/chart/layer/CartesianLayer.kt | 87 ++ .../ColumnCartesianLayer.kt} | 223 +++--- .../LineCartesianLayer.kt} | 328 ++++---- .../LineCartesianLayerExtensions.kt} | 6 +- .../core/chart/layout/HorizontalLayout.kt | 18 +- ...luesOverrider.kt => AxisValueOverrider.kt} | 50 +- .../vico/core/chart/values/ChartValues.kt | 89 ++- .../core/chart/values/ChartValuesManager.kt | 108 --- .../core/chart/values/ChartValuesProvider.kt | 38 - .../core/chart/values/MutableChartValues.kt | 105 +-- .../core/component/marker/MarkerComponent.kt | 5 +- .../component/shape/shader/ColorShader.kt | 50 ++ .../component/shape/shader/DynamicShader.kt | 50 +- .../component/shape/shader/DynamicShaders.kt | 13 +- .../shape/shader/HorizontalSplitShader.kt | 246 ------ .../component/shape/shader/SolidShader.kt | 87 -- .../component/shape/shader/TopBottomShader.kt | 87 ++ .../vico/core/context/MeasureContext.kt | 7 +- .../core/context/MutableMeasureContext.kt | 4 +- .../vico/core/draw/DrawContextExtensions.kt | 4 +- .../vico/core/entry/ChartEntry.kt | 50 -- .../vico/core/entry/ChartEntryExtensions.kt | 109 --- .../vico/core/entry/ChartEntryModel.kt | 103 --- .../core/entry/ChartEntryModelProducer.kt | 242 ------ .../vico/core/entry/ChartModelProducer.kt | 79 -- .../vico/core/entry/EntryListExtensions.kt | 48 -- .../vico/core/entry/FloatEntry.kt | 31 - .../ComposedChartEntryModelProducer.kt | 356 --------- .../composed/ComposedEntryListExtensions.kt | 35 - .../vico/core/extension/MapExtensions.kt | 7 +- .../vico/core/extension/NumberExtensions.kt | 6 + .../formatter/DecimalFormatValueFormatter.kt | 2 + .../core/formatter/DefaultValueFormatter.kt | 4 +- .../PercentageFormatValueFormatter.kt | 4 +- .../vico/core/formatter/ValueFormatter.kt | 9 +- .../vico/core/layout/VirtualLayout.kt | 7 +- .../marker/DefaultMarkerLabelFormatter.kt | 11 + .../vico/core/marker/Marker.kt | 31 +- .../vico/core/model/CartesianChartModel.kt | 109 +++ .../core/model/CartesianChartModelProducer.kt | 229 ++++++ .../vico/core/model/CartesianLayerModel.kt | 109 +++ .../core/model/ColumnCartesianLayerModel.kt | 189 +++++ .../core/{entry/diff => model}/ExtraStore.kt | 2 +- .../core/model/LineCartesianLayerModel.kt | 150 ++++ .../ColumnCartesianLayerDrawingModel.kt} | 18 +- .../DefaultDrawingModelInterpolator.kt | 2 +- .../diff => model/drawing}/DrawingModel.kt | 8 +- .../drawing}/DrawingModelInterpolator.kt | 2 +- .../LineCartesianLayerDrawingModel.kt} | 25 +- .../vico/core/scroll/AutoScrollCondition.kt | 27 +- .../vico/core/{model => util}/Point.kt | 7 +- .../util/RandomCartesianModelGenerator.kt | 71 ++ .../vico/core/util/RandomEntriesGenerator.kt | 80 -- .../vico/core/ChartEntryModelProducerTest.kt | 43 - .../vico/views/chart/BaseChartView.kt | 705 ----------------- .../vico/views/chart/CartesianChartView.kt | 749 ++++++++++++++++++ .../vico/views/chart/ChartView.kt | 66 -- .../vico/views/chart/ComposedChartView.kt | 70 -- .../ColumnChartExtensions.kt | 22 +- .../{line => layer}/LineChartExtensions.kt | 23 +- .../component/shape/shader/DynamicShaders.kt | 33 +- .../vico/views/extension/ViewExtensions.kt | 33 +- .../vico/views/gestures/MotionEventHandler.kt | 2 +- .../vico/views/scroll/ChartScrollSpec.kt | 16 +- .../vico/views/theme/ChartStyleExtensions.kt | 63 +- .../views/theme/ComponentStyleExtensions.kt | 20 +- .../theme/TextComponentStyleExtensions.kt | 4 +- .../vico/views/theme/ThemeHandler.kt | 140 ++-- vico/views/src/main/res/values/attrs.xml | 44 +- .../main/res/values/column_chart_attrs.xml | 10 +- .../src/main/res/values/line_chart_attrs.xml | 4 +- 155 files changed, 4328 insertions(+), 5603 deletions(-) create mode 100644 vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/CartesianChart.kt rename vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/{Charts.kt => CartesianChartHost.kt} (83%) delete mode 100644 vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/column/ColumnChart.kt create mode 100644 vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/layer/ColumnCartesianLayer.kt create mode 100644 vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/layer/LineCartesianLayer.kt delete mode 100644 vico/compose/src/main/java/com/patrykandpatrick/vico/compose/chart/line/LineChart.kt rename vico/compose/src/main/java/com/patrykandpatrick/vico/compose/{chart/entry => model}/ChartEntryModelExtensions.kt (71%) create mode 100644 vico/compose/src/main/java/com/patrykandpatrick/vico/compose/state/CartesianChartModelWrapper.kt delete mode 100644 vico/compose/src/main/java/com/patrykandpatrick/vico/compose/state/ChartEntryModelWrapper.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/BaseChart.kt create mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/CartesianChart.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/Chart.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/ChartExtensions.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/EntryModelExtensions.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChart.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChartEntryModel.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/composed/ComposedChartExtensions.kt create mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/layer/BaseCartesianLayer.kt create mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/layer/CartesianLayer.kt rename vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/{column/ColumnChart.kt => layer/ColumnCartesianLayer.kt} (68%) rename vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/{line/LineChart.kt => layer/LineCartesianLayer.kt} (64%) rename vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/{line/LineChartExtensions.kt => layer/LineCartesianLayerExtensions.kt} (84%) rename vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/{AxisValuesOverrider.kt => AxisValueOverrider.kt} (59%) delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/ChartValuesManager.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/values/ChartValuesProvider.kt create mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/ColorShader.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/HorizontalSplitShader.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/SolidShader.kt create mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/component/shape/shader/TopBottomShader.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntry.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryExtensions.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModel.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartEntryModelProducer.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/ChartModelProducer.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/EntryListExtensions.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/FloatEntry.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/composed/ComposedChartEntryModelProducer.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/entry/composed/ComposedEntryListExtensions.kt create mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/model/CartesianChartModel.kt create mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/model/CartesianChartModelProducer.kt create mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/model/CartesianLayerModel.kt create mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/model/ColumnCartesianLayerModel.kt rename vico/core/src/main/java/com/patrykandpatrick/vico/core/{entry/diff => model}/ExtraStore.kt (98%) create mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/model/LineCartesianLayerModel.kt rename vico/core/src/main/java/com/patrykandpatrick/vico/core/{chart/column/ColumnChartDrawingModel.kt => model/drawing/ColumnCartesianLayerDrawingModel.kt} (62%) rename vico/core/src/main/java/com/patrykandpatrick/vico/core/{entry/diff => model/drawing}/DefaultDrawingModelInterpolator.kt (98%) rename vico/core/src/main/java/com/patrykandpatrick/vico/core/{entry/diff => model/drawing}/DrawingModel.kt (87%) rename vico/core/src/main/java/com/patrykandpatrick/vico/core/{entry/diff => model/drawing}/DrawingModelInterpolator.kt (95%) rename vico/core/src/main/java/com/patrykandpatrick/vico/core/{chart/line/LineChartDrawingModel.kt => model/drawing/LineCartesianLayerDrawingModel.kt} (59%) rename vico/core/src/main/java/com/patrykandpatrick/vico/core/{model => util}/Point.kt (87%) create mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/util/RandomCartesianModelGenerator.kt delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/util/RandomEntriesGenerator.kt delete mode 100644 vico/core/src/test/java/com/patrykandpatrick/vico/core/ChartEntryModelProducerTest.kt delete mode 100644 vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/BaseChartView.kt create mode 100644 vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/CartesianChartView.kt delete mode 100644 vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ChartView.kt delete mode 100644 vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/ComposedChartView.kt rename vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/{column => layer}/ColumnChartExtensions.kt (84%) rename vico/views/src/main/java/com/patrykandpatrick/vico/views/chart/{line => layer}/LineChartExtensions.kt (82%) diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/AxisLinePreviews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/AxisLinePreviews.kt index e3b3c2568..885e6ed41 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/AxisLinePreviews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/AxisLinePreviews.kt @@ -25,8 +25,9 @@ import com.patrykandpatrick.vico.compose.axis.axisLabelComponent import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberEndAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.column.columnChart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.rememberColumnCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.component.lineComponent import com.patrykandpatrick.vico.compose.component.shapeComponent import com.patrykandpatrick.vico.compose.style.LocalChartStyle @@ -36,9 +37,10 @@ import com.patrykandpatrick.vico.core.component.shape.cornered.Corner import com.patrykandpatrick.vico.core.component.shape.cornered.CorneredShape import com.patrykandpatrick.vico.core.component.shape.cornered.CutCornerTreatment import com.patrykandpatrick.vico.core.component.shape.cornered.RoundedCornerTreatment -import com.patrykandpatrick.vico.core.entry.entryModelOf +import com.patrykandpatrick.vico.core.model.CartesianChartModel +import com.patrykandpatrick.vico.core.model.ColumnCartesianLayerModel -private val model = entryModelOf(1, 2, 3, 4) +private val model = CartesianChartModel(ColumnCartesianLayerModel.build { series(1, 2, 3, 4) }) @Composable private fun ProvidePreviewChartStyle(content: @Composable () -> Unit) { @@ -50,10 +52,10 @@ private fun ProvidePreviewChartStyle(content: @Composable () -> Unit) { axisLineColor = Color.Black.copy(alpha = 0.5f), axisGuidelineColor = Color.Black.copy(alpha = 0.2f), ), - columnChart = - LocalChartStyle.current.columnChart.copy( + columnLayer = + LocalChartStyle.current.columnLayer.copy( columns = - LocalChartStyle.current.columnChart.columns.map { + LocalChartStyle.current.columnLayer.columns.map { lineComponent( color = Color.Gray, thickness = it.thicknessDp.dp, @@ -97,8 +99,8 @@ public fun HorizontalAxisTextInside() { verticalMargin = 4.dp, horizontalMargin = 4.dp, ) - Chart( - chart = columnChart(), + CartesianChartHost( + chart = rememberCartesianChart(rememberColumnCartesianLayer()), model = model, startAxis = rememberStartAxis( @@ -131,8 +133,8 @@ public fun HorizontalAxisTextInsideAndBottomAxis() { verticalMargin = 4.dp, horizontalMargin = 4.dp, ) - Chart( - chart = columnChart(), + CartesianChartHost( + chart = rememberCartesianChart(rememberColumnCartesianLayer()), model = model, startAxis = rememberStartAxis( @@ -154,8 +156,8 @@ public fun HorizontalAxisTextInsideAndBottomAxis() { @Preview(showBackground = true, widthDp = 250) public fun HorizontalAxisTextOutside() { ProvidePreviewChartStyle { - Chart( - chart = columnChart(), + CartesianChartHost( + chart = rememberCartesianChart(rememberColumnCartesianLayer()), model = model, startAxis = rememberStartAxis( @@ -174,8 +176,8 @@ public fun HorizontalAxisTextOutside() { @Preview(showBackground = true, widthDp = 250) public fun HorizontalAxisGuidelineDoesNotOverlayBottomAxisLine() { ProvidePreviewChartStyle { - Chart( - chart = columnChart(), + CartesianChartHost( + chart = rememberCartesianChart(rememberColumnCartesianLayer()), model = model, startAxis = rememberStartAxis( diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ColumnChartsWithNegativeValuesPreviews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ColumnChartsWithNegativeValuesPreviews.kt index fb60f2a0a..2eb5c16b0 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ColumnChartsWithNegativeValuesPreviews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ColumnChartsWithNegativeValuesPreviews.kt @@ -25,30 +25,29 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.column.columnChart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.rememberColumnCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.core.axis.AxisItemPlacer -import com.patrykandpatrick.vico.core.chart.values.AxisValuesOverrider +import com.patrykandpatrick.vico.core.chart.values.AxisValueOverrider import com.patrykandpatrick.vico.core.component.text.textComponent -import com.patrykandpatrick.vico.core.entry.entryModelOf +import com.patrykandpatrick.vico.core.model.CartesianChartModel +import com.patrykandpatrick.vico.core.model.LineCartesianLayerModel import com.patrykandpatrick.vico.sample.showcase.rememberMarker -private val model = entryModelOf(2f, -1f, 4f, -2f, 1f, 5f, -3f) +private val model = CartesianChartModel(LineCartesianLayerModel.build { series(2, -1, 4, -2, 1, 5, -3) }) @Preview @Composable public fun SingleColumnChartWithNegativeValues() { val marker = rememberMarker() Surface { - Chart( + CartesianChartHost( modifier = Modifier.height(250.dp), chart = - columnChart( - persistentMarkers = - mapOf( - 2f to marker, - 3f to marker, - ), + rememberCartesianChart( + rememberColumnCartesianLayer(), + persistentMarkers = mapOf(2f to marker, 3f to marker), ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 9) }), @@ -61,11 +60,8 @@ public fun SingleColumnChartWithNegativeValues() { @Composable public fun SingleColumnChartWithNegativeValuesAndDataLabels() { Surface { - Chart( - chart = - columnChart( - dataLabel = textComponent(), - ), + CartesianChartHost( + chart = rememberCartesianChart(rememberColumnCartesianLayer(dataLabel = textComponent())), model = model, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), @@ -77,14 +73,10 @@ public fun SingleColumnChartWithNegativeValuesAndDataLabels() { @Composable public fun SingleColumnChartWithNegativeValuesAndAxisValuesOverridden() { Surface { - Chart( + CartesianChartHost( chart = - columnChart( - axisValuesOverrider = - AxisValuesOverrider.fixed( - minY = 1f, - maxY = 4f, - ), + rememberCartesianChart( + rememberColumnCartesianLayer(axisValueOverrider = AxisValueOverrider.fixed(minY = 1f, maxY = 4f)), ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 4) }), @@ -97,14 +89,10 @@ public fun SingleColumnChartWithNegativeValuesAndAxisValuesOverridden() { @Composable public fun SingleColumnChartWithNegativeValuesAndAxisValuesOverridden2() { Surface { - Chart( + CartesianChartHost( chart = - columnChart( - axisValuesOverrider = - AxisValuesOverrider.fixed( - minY = -2f, - maxY = 0f, - ), + rememberCartesianChart( + rememberColumnCartesianLayer(axisValueOverrider = AxisValueOverrider.fixed(minY = -2f, maxY = 0f)), ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 3) }), diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ContainedChartsPreview.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ContainedChartsPreview.kt index 986550528..8d2fac1b0 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ContainedChartsPreview.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ContainedChartsPreview.kt @@ -25,11 +25,13 @@ import androidx.compose.ui.unit.dp import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberEndAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.column.columnChart -import com.patrykandpatrick.vico.compose.chart.line.lineChart -import com.patrykandpatrick.vico.compose.chart.line.lineSpec +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.lineSpec +import com.patrykandpatrick.vico.compose.chart.layer.rememberColumnCartesianLayer +import com.patrykandpatrick.vico.compose.chart.layer.rememberLineCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.component.lineComponent +import com.patrykandpatrick.vico.compose.component.shape.shader.color import com.patrykandpatrick.vico.compose.component.shape.shader.verticalGradient import com.patrykandpatrick.vico.compose.component.textComponent import com.patrykandpatrick.vico.core.axis.Axis @@ -37,27 +39,26 @@ import com.patrykandpatrick.vico.core.axis.AxisItemPlacer import com.patrykandpatrick.vico.core.axis.AxisPosition.Vertical import com.patrykandpatrick.vico.core.axis.AxisPosition.Vertical.End import com.patrykandpatrick.vico.core.axis.AxisPosition.Vertical.Start -import com.patrykandpatrick.vico.core.chart.column.ColumnChart -import com.patrykandpatrick.vico.core.chart.composed.plus -import com.patrykandpatrick.vico.core.chart.line.LineChart import com.patrykandpatrick.vico.core.component.shape.Shapes -import com.patrykandpatrick.vico.core.entry.composed.plus -import com.patrykandpatrick.vico.core.entry.entryModelOf +import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders import com.patrykandpatrick.vico.core.marker.Marker +import com.patrykandpatrick.vico.core.model.CartesianChartModel +import com.patrykandpatrick.vico.core.model.ColumnCartesianLayerModel +import com.patrykandpatrick.vico.core.model.LineCartesianLayerModel import com.patrykandpatrick.vico.sample.showcase.rememberMarker -private val model1 = entryModelOf(0 to 1, 1 to 2, 2 to 4, 3 to 1, 4 to 4) -private val model2 = entryModelOf(1 to 4, 2 to 1, 3 to 8, 4 to 12, 5 to 5) +private val model = + CartesianChartModel( + ColumnCartesianLayerModel.build { series(1, 2, 4, 1, 4) }, + LineCartesianLayerModel.build { series(4, 1, 8, 12, 5) }, + ) private val markerMap: Map @Composable get() = mapOf(4f to rememberMarker()) @Composable -private fun getColumnChart( - markerMap: Map = emptyMap(), - targetVerticalAxisPosition: Vertical? = null, -): ColumnChart = - columnChart( +private fun getColumnLayer(verticalAxisPosition: Vertical? = null) = + rememberColumnCartesianLayer( columns = listOf( lineComponent( @@ -66,28 +67,23 @@ private fun getColumnChart( shape = Shapes.pillShape, ), ), - persistentMarkers = markerMap, - targetVerticalAxisPosition = targetVerticalAxisPosition, + verticalAxisPosition = verticalAxisPosition, ) @Composable -private fun getLineChart( - markerMap: Map = emptyMap(), - targetVerticalAxisPosition: Vertical? = null, -): LineChart = - lineChart( +private fun getLineLayer(verticalAxisPosition: Vertical? = null) = + rememberLineCartesianLayer( lines = listOf( lineSpec( - lineColor = Color.DarkGray, - lineBackgroundShader = + shader = DynamicShaders.color(Color.DarkGray), + backgroundShader = verticalGradient( arrayOf(Color.DarkGray, Color.DarkGray.copy(alpha = 0f)), ), ), ), - persistentMarkers = markerMap, - targetVerticalAxisPosition = targetVerticalAxisPosition, + verticalAxisPosition = verticalAxisPosition, ) private val startAxis: Axis @@ -107,15 +103,9 @@ private val endAxis: Axis @Composable @Preview("Chart with independent axes", widthDp = 350) public fun ChartWithIndependentAxes(modifier: Modifier = Modifier) { - val composedChart = - getColumnChart(targetVerticalAxisPosition = Start) + - getLineChart(targetVerticalAxisPosition = End) - - composedChart.setPersistentMarkers(markerMap) - - Chart( - chart = composedChart, - model = model1 + model2, + CartesianChartHost( + chart = rememberCartesianChart(getColumnLayer(Start), getLineLayer(End)), + model = model, startAxis = startAxis, bottomAxis = rememberBottomAxis(), endAxis = endAxis, @@ -126,13 +116,9 @@ public fun ChartWithIndependentAxes(modifier: Modifier = Modifier) { @Composable @Preview("Chart with dependent axes", widthDp = 350) public fun ChartWithDependentAxes(modifier: Modifier = Modifier) { - val composedChart = getColumnChart() + getLineChart() - - composedChart.setPersistentMarkers(markerMap) - - Chart( - chart = composedChart, - model = model1 + model2, + CartesianChartHost( + chart = rememberCartesianChart(getColumnLayer(), getLineLayer(), persistentMarkers = markerMap), + model = model, startAxis = startAxis, bottomAxis = rememberBottomAxis(), endAxis = endAxis, @@ -143,9 +129,9 @@ public fun ChartWithDependentAxes(modifier: Modifier = Modifier) { @Composable @Preview("Column chart", widthDp = 350) public fun ColumnChart(modifier: Modifier = Modifier) { - Chart( - chart = getColumnChart(markerMap = markerMap), - model = model1, + CartesianChartHost( + chart = rememberCartesianChart(getColumnLayer(), persistentMarkers = markerMap), + model = model, startAxis = startAxis, bottomAxis = rememberBottomAxis(), modifier = modifier, @@ -155,9 +141,9 @@ public fun ColumnChart(modifier: Modifier = Modifier) { @Composable @Preview("Line chart", widthDp = 350) public fun LineChart(modifier: Modifier = Modifier) { - Chart( - chart = getLineChart(markerMap = markerMap), - model = model2, + CartesianChartHost( + chart = rememberCartesianChart(getLineLayer(), persistentMarkers = markerMap), + model = model, startAxis = startAxis, bottomAxis = rememberBottomAxis(), modifier = modifier, diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartPreviews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartPreviews.kt index f98dce63d..85384d6e5 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartPreviews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartPreviews.kt @@ -25,19 +25,32 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.line.lineChart -import com.patrykandpatrick.vico.compose.chart.line.lineSpec +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.lineSpec +import com.patrykandpatrick.vico.compose.chart.layer.rememberLineCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart +import com.patrykandpatrick.vico.compose.component.shape.shader.color import com.patrykandpatrick.vico.compose.component.shape.shader.verticalGradient -import com.patrykandpatrick.vico.core.chart.composed.plus -import com.patrykandpatrick.vico.core.chart.values.AxisValuesOverrider -import com.patrykandpatrick.vico.core.entry.composed.plus -import com.patrykandpatrick.vico.core.entry.entriesOf -import com.patrykandpatrick.vico.core.entry.entryModelOf +import com.patrykandpatrick.vico.core.chart.values.AxisValueOverrider +import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders +import com.patrykandpatrick.vico.core.model.CartesianChartModel +import com.patrykandpatrick.vico.core.model.LineCartesianLayerModel -private val model1 = entryModelOf(0, 2, 4, 0, 2) -private val model2 = entryModelOf(1, 3, 4, 1, 3) -private val model3 = entryModelOf(entriesOf(3, 2, 2, 3, 1), entriesOf(1, 3, 1, 2, 3)) +private val model1 = CartesianChartModel(LineCartesianLayerModel.build { series(0, 2, 4, 0, 2) }) + +private val model2 = + CartesianChartModel( + LineCartesianLayerModel.build { series(0, 2, 4, 0, 2) }, + LineCartesianLayerModel.build { series(1, 3, 4, 1, 3) }, + ) + +private val model3 = + CartesianChartModel( + LineCartesianLayerModel.build { + series(3, 2, 2, 3, 1) + series(1, 3, 1, 2, 3) + }, + ) @Preview("Line Chart Dark", widthDp = 200) @Composable @@ -49,28 +62,29 @@ public fun LineChartDark() { val yellow = Color(0xFFFFAA4A) val pink = Color(0xFFFF4AAA) - Chart( + CartesianChartHost( modifier = Modifier.padding(8.dp), chart = - lineChart( - lines = + rememberCartesianChart( + rememberLineCartesianLayer( listOf( lineSpec( - lineColor = yellow, - lineBackgroundShader = + shader = DynamicShaders.color(yellow), + backgroundShader = verticalGradient( - arrayOf(yellow.copy(0.5f), yellow.copy(alpha = 0f)), + arrayOf(yellow.copy(alpha = 0.5f), yellow.copy(alpha = 0f)), ), ), lineSpec( - lineColor = pink, - lineBackgroundShader = + shader = DynamicShaders.color(pink), + backgroundShader = verticalGradient( - arrayOf(pink.copy(0.5f), pink.copy(alpha = 0f)), + arrayOf(pink.copy(alpha = 0.5f), pink.copy(alpha = 0f)), ), ), ), - axisValuesOverrider = AxisValuesOverrider.fixed(maxY = 4f), + axisValueOverrider = AxisValueOverrider.fixed(maxY = 4f), + ), ), model = model3, ) @@ -80,8 +94,8 @@ public fun LineChartDark() { @Preview("Line Chart", widthDp = 200) @Composable public fun RegularLineChart() { - Chart( - chart = lineChart(), + CartesianChartHost( + chart = rememberCartesianChart(rememberLineCartesianLayer()), model = model1, startAxis = rememberStartAxis(), ) @@ -90,14 +104,10 @@ public fun RegularLineChart() { @Preview("Line Chart Expanded", widthDp = 200) @Composable public fun RegularLineChartExpanded() { - Chart( + CartesianChartHost( chart = - lineChart( - axisValuesOverrider = - AxisValuesOverrider.fixed( - minY = -1f, - maxY = 5f, - ), + rememberCartesianChart( + rememberLineCartesianLayer(axisValueOverrider = AxisValueOverrider.fixed(minY = -1f, maxY = 5f)), ), model = model1, startAxis = rememberStartAxis(), @@ -107,14 +117,10 @@ public fun RegularLineChartExpanded() { @Preview("Line Chart Collapsed", widthDp = 200) @Composable public fun RegularLineChartCollapsed() { - Chart( + CartesianChartHost( chart = - lineChart( - axisValuesOverrider = - AxisValuesOverrider.fixed( - minY = 1f, - maxY = 3f, - ), + rememberCartesianChart( + rememberLineCartesianLayer(axisValueOverrider = AxisValueOverrider.fixed(minY = 1f, maxY = 3f)), ), model = model1, startAxis = rememberStartAxis(), @@ -124,26 +130,23 @@ public fun RegularLineChartCollapsed() { @Preview("Composed Chart", widthDp = 200) @Composable public fun ComposedLineChart() { - Chart( + CartesianChartHost( chart = - lineChart() + - lineChart( - lines = - listOf( - lineSpec( - lineColor = Color.Blue, - lineBackgroundShader = - verticalGradient( - colors = - arrayOf( - Color.Blue.copy(alpha = 0.4f), - Color.Blue.copy(alpha = 0f), - ), - ), - ), + rememberCartesianChart( + rememberLineCartesianLayer(), + rememberLineCartesianLayer( + listOf( + lineSpec( + shader = DynamicShaders.color(Color.Blue), + backgroundShader = + verticalGradient( + arrayOf(Color.Blue.copy(alpha = 0.4f), Color.Blue.copy(alpha = 0f)), + ), ), + ), ), - model = model1 + model2, + ), + model = model2, startAxis = rememberStartAxis(), ) } @@ -151,23 +154,13 @@ public fun ComposedLineChart() { @Preview("Composed Chart Collapsed", widthDp = 200) @Composable public fun ComposedLineChartCollapsed() { - Chart( + CartesianChartHost( chart = - lineChart( - axisValuesOverrider = - AxisValuesOverrider.fixed( - minY = 1f, - maxY = 3f, - ), - ) + - lineChart( - axisValuesOverrider = - AxisValuesOverrider.fixed( - minY = 1f, - maxY = 3f, - ), - ), - model = model1 + model2, + rememberCartesianChart( + rememberLineCartesianLayer(axisValueOverrider = AxisValueOverrider.fixed(minY = 1f, maxY = 3f)), + rememberLineCartesianLayer(axisValueOverrider = AxisValueOverrider.fixed(minY = 1f, maxY = 3f)), + ), + model = model2, startAxis = rememberStartAxis(), ) } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartsWithNegativeValuesPreviews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartsWithNegativeValuesPreviews.kt index 94460afcd..f3c067c6a 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartsWithNegativeValuesPreviews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/LineChartsWithNegativeValuesPreviews.kt @@ -27,46 +27,46 @@ import androidx.compose.ui.unit.dp import com.patrykandpatrick.vico.compose.axis.axisLineComponent import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.lineSpec +import com.patrykandpatrick.vico.compose.chart.layer.rememberLineCartesianLayer import com.patrykandpatrick.vico.compose.chart.layout.fullWidth -import com.patrykandpatrick.vico.compose.chart.line.lineChart -import com.patrykandpatrick.vico.compose.chart.line.lineSpec -import com.patrykandpatrick.vico.compose.component.shape.shader.solid -import com.patrykandpatrick.vico.compose.component.shape.shader.split +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart +import com.patrykandpatrick.vico.compose.component.shape.shader.color import com.patrykandpatrick.vico.compose.component.textComponent import com.patrykandpatrick.vico.core.axis.AxisItemPlacer import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout -import com.patrykandpatrick.vico.core.chart.values.AxisValuesOverrider +import com.patrykandpatrick.vico.core.chart.values.AxisValueOverrider import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders -import com.patrykandpatrick.vico.core.entry.entryModelOf +import com.patrykandpatrick.vico.core.component.shape.shader.TopBottomShader +import com.patrykandpatrick.vico.core.model.CartesianChartModel +import com.patrykandpatrick.vico.core.model.LineCartesianLayerModel import com.patrykandpatrick.vico.sample.showcase.rememberMarker -private val model = entryModelOf(-2f, -1f, 4f, -2f, 1f, 5f, -3f) +private val model = CartesianChartModel(LineCartesianLayerModel.build { series(-2, -1, 4, -2, 1, 5, -3) }) @Preview @Composable public fun SingleLineChartWithNegativeValues() { val marker = rememberMarker() Surface { - Chart( + CartesianChartHost( modifier = Modifier.height(250.dp), chart = - lineChart( - lines = - listOf( - lineSpec( - lineShader = - DynamicShaders.split( - positiveColor = Color(0xFF25BE53), - negativeColor = Color(0xFFE73B3B), - ), + rememberCartesianChart( + rememberLineCartesianLayer( + lines = + listOf( + lineSpec( + shader = + TopBottomShader( + DynamicShaders.color(Color(0xFF25BE53)), + DynamicShaders.color(Color(0xFFE73B3B)), + ), + ), ), - ), - persistentMarkers = - mapOf( - 2f to marker, - 3f to marker, - ), + ), + persistentMarkers = mapOf(2f to marker, 3f to marker), ), model = model, startAxis = @@ -91,13 +91,15 @@ public fun SingleLineChartWithNegativeValues() { @Composable public fun SingleLineChartWithNegativeValuesAndDataLabels() { Surface { - Chart( + CartesianChartHost( chart = - lineChart( - lines = - listOf( - lineSpec(lineShader = DynamicShaders.solid(Color.DarkGray), dataLabel = textComponent()), - ), + rememberCartesianChart( + rememberLineCartesianLayer( + lines = + listOf( + lineSpec(shader = DynamicShaders.color(Color.DarkGray), dataLabel = textComponent()), + ), + ), ), model = model, startAxis = rememberStartAxis(), @@ -110,14 +112,10 @@ public fun SingleLineChartWithNegativeValuesAndDataLabels() { @Composable public fun SingleLineChartWithNegativeValuesAndAxisValuesOverridden() { Surface { - Chart( + CartesianChartHost( chart = - lineChart( - axisValuesOverrider = - AxisValuesOverrider.fixed( - minY = 1f, - maxY = 4f, - ), + rememberCartesianChart( + rememberLineCartesianLayer(axisValueOverrider = AxisValueOverrider.fixed(minY = 1f, maxY = 4f)), ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 4) }), @@ -130,14 +128,10 @@ public fun SingleLineChartWithNegativeValuesAndAxisValuesOverridden() { @Composable public fun SingleLineChartWithNegativeValuesAndAxisValuesOverridden2() { Surface { - Chart( + CartesianChartHost( chart = - lineChart( - axisValuesOverrider = - AxisValuesOverrider.fixed( - minY = -2f, - maxY = 0f, - ), + rememberCartesianChart( + rememberLineCartesianLayer(axisValueOverrider = AxisValueOverrider.fixed(minY = -2f, maxY = 0f)), ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 3) }), diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/Previews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/Previews.kt index fb920ab43..171699c44 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/Previews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/Previews.kt @@ -34,11 +34,13 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.column.columnChart -import com.patrykandpatrick.vico.compose.chart.line.lineChart -import com.patrykandpatrick.vico.compose.chart.line.lineSpec +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.lineSpec +import com.patrykandpatrick.vico.compose.chart.layer.rememberColumnCartesianLayer +import com.patrykandpatrick.vico.compose.chart.layer.rememberLineCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.component.lineComponent +import com.patrykandpatrick.vico.compose.component.shape.shader.color import com.patrykandpatrick.vico.compose.component.shape.shader.fromComponent import com.patrykandpatrick.vico.compose.component.shape.shader.verticalGradient import com.patrykandpatrick.vico.compose.component.shapeComponent @@ -47,13 +49,15 @@ import com.patrykandpatrick.vico.compose.dimensions.dimensionsOf import com.patrykandpatrick.vico.core.axis.horizontal.createHorizontalAxis import com.patrykandpatrick.vico.core.axis.vertical.VerticalAxis import com.patrykandpatrick.vico.core.axis.vertical.createVerticalAxis -import com.patrykandpatrick.vico.core.chart.values.AxisValuesOverrider +import com.patrykandpatrick.vico.core.chart.values.AxisValueOverrider import com.patrykandpatrick.vico.core.component.shape.DashedShape import com.patrykandpatrick.vico.core.component.shape.LineComponent import com.patrykandpatrick.vico.core.component.shape.Shapes.pillShape import com.patrykandpatrick.vico.core.component.shape.Shapes.rectShape import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders -import com.patrykandpatrick.vico.core.entry.entryModelOf +import com.patrykandpatrick.vico.core.model.CartesianChartModel +import com.patrykandpatrick.vico.core.model.ColumnCartesianLayerModel +import com.patrykandpatrick.vico.core.model.LineCartesianLayerModel import com.patrykandpatrick.vico.sample.utils.VicoTheme private val chartModifier = Modifier.height(100.dp) @@ -65,19 +69,20 @@ public fun ColumnChartCard(): Unit = val colors = MaterialTheme.colors SampleCard { - Chart( + CartesianChartHost( modifier = chartModifier, chart = - columnChart( - columns = + rememberCartesianChart( + rememberColumnCartesianLayer( listOf( lineComponent( - colors.primary, + color = colors.primary, thickness = 8.dp, shape = RoundedCornerShape(4.dp), dynamicShader = verticalGradient(arrayOf(colors.primary, colors.secondary)), ), ), + ), ), startAxis = createVerticalAxis { @@ -106,7 +111,7 @@ public fun ColumnChartCard(): Unit = 1.dp.value, ) }, - model = entryModelOf(1, 2, 3, 2), + model = CartesianChartModel(ColumnCartesianLayerModel.build { series(1, 2, 3, 2) }), ) } } @@ -118,32 +123,33 @@ public fun LineChartCard(): Unit = val colors = MaterialTheme.colors SampleCard { - Chart( + CartesianChartHost( modifier = Modifier.height(100.dp), chart = - lineChart( - lines = + rememberCartesianChart( + rememberLineCartesianLayer( listOf( lineSpec( point = null, - lineColor = colors.primary, - lineBackgroundShader = + shader = DynamicShaders.color(colors.primary), + backgroundShader = DynamicShaders.fromComponent( componentSize = 4.dp, component = - shapeComponent(shape = pillShape, color = colors.primary).apply { - setMargins(0.5.dp.value) - }, + shapeComponent(shape = pillShape, color = colors.primary) + .apply { setMargins(0.5.dp.value) }, ), ), ), - axisValuesOverrider = - AxisValuesOverrider.fixed( - minX = 0f, - maxY = 3f, - ), + axisValueOverrider = AxisValueOverrider.fixed(minX = 0f, maxY = 3f), + ), + ), + model = + CartesianChartModel( + LineCartesianLayerModel.build { + series(x = listOf(-1, 0, 1, 2, 3, 4, 5), y = listOf(0, 0, 1, 2, 0, 2, 1)) + }, ), - model = entryModelOf(-1 to 0, 0 to 0, 1 to 1, 2 to 2, 3 to 0, 4 to 2, 5 to 1), startAxis = createVerticalAxis { label = diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/StackedColumnChartsWithNegativeValuesPreviews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/StackedColumnChartsWithNegativeValuesPreviews.kt index a2fddb4d6..c445d90f0 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/StackedColumnChartsWithNegativeValuesPreviews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/StackedColumnChartsWithNegativeValuesPreviews.kt @@ -26,23 +26,26 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.column.columnChart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.rememberColumnCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.component.lineComponent import com.patrykandpatrick.vico.core.axis.AxisItemPlacer -import com.patrykandpatrick.vico.core.chart.column.ColumnChart.MergeMode.Stack -import com.patrykandpatrick.vico.core.chart.values.AxisValuesOverrider +import com.patrykandpatrick.vico.core.chart.layer.ColumnCartesianLayer +import com.patrykandpatrick.vico.core.chart.values.AxisValueOverrider import com.patrykandpatrick.vico.core.component.shape.LineComponent import com.patrykandpatrick.vico.core.component.text.textComponent -import com.patrykandpatrick.vico.core.entry.entriesOf -import com.patrykandpatrick.vico.core.entry.entryModelOf +import com.patrykandpatrick.vico.core.model.CartesianChartModel +import com.patrykandpatrick.vico.core.model.ColumnCartesianLayerModel import com.patrykandpatrick.vico.sample.showcase.rememberMarker private val model = - entryModelOf( - entriesOf(2f, -1f, -4f, 2f, 1f, -5f, -2f, -3f), - entriesOf(3f, -2f, 2f, -1f, 2f, -3f, -4f, -1f), - entriesOf(1f, -2f, 2f, 1f, -1f, 4f, 4f, -2f), + CartesianChartModel( + ColumnCartesianLayerModel.build { + series(2, -1, -4, 2, 1, -5, -2, -3) + series(3, -2, 2, -1, 2, -3, -4, -1) + series(1, -2, 2, 1, -1, 4, 4, -2) + }, ) private val columns: List @@ -59,17 +62,12 @@ private val columns: List public fun StackedColumnChartWithNegativeValues() { val marker = rememberMarker() Surface { - Chart( + CartesianChartHost( modifier = Modifier.height(250.dp), chart = - columnChart( - columns = columns, - persistentMarkers = - mapOf( - 2f to marker, - 3f to marker, - ), - mergeMode = Stack, + rememberCartesianChart( + rememberColumnCartesianLayer(columns = columns, mergeMode = ColumnCartesianLayer.MergeMode.Stacked), + persistentMarkers = mapOf(2f to marker, 3f to marker), ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 8) }), @@ -82,12 +80,14 @@ public fun StackedColumnChartWithNegativeValues() { @Composable public fun StackedColumnChartWithNegativeValuesAndDataLabels() { Surface { - Chart( + CartesianChartHost( chart = - columnChart( - columns = columns, - dataLabel = textComponent(), - mergeMode = Stack, + rememberCartesianChart( + rememberColumnCartesianLayer( + columns = columns, + dataLabel = textComponent(), + mergeMode = ColumnCartesianLayer.MergeMode.Stacked, + ), ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 8) }), @@ -100,16 +100,14 @@ public fun StackedColumnChartWithNegativeValuesAndDataLabels() { @Composable public fun StackedColumnChartWithNegativeValuesAndAxisValuesOverridden() { Surface { - Chart( + CartesianChartHost( chart = - columnChart( - columns = columns, - axisValuesOverrider = - AxisValuesOverrider.fixed( - minY = 1f, - maxY = 4f, - ), - mergeMode = Stack, + rememberCartesianChart( + rememberColumnCartesianLayer( + columns = columns, + axisValueOverrider = AxisValueOverrider.fixed(minY = 1f, maxY = 4f), + mergeMode = ColumnCartesianLayer.MergeMode.Stacked, + ), ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 4) }), @@ -122,16 +120,14 @@ public fun StackedColumnChartWithNegativeValuesAndAxisValuesOverridden() { @Composable public fun StackedColumnChartWithNegativeValuesAndAxisValuesOverridden2() { Surface { - Chart( + CartesianChartHost( chart = - columnChart( - columns = columns, - axisValuesOverrider = - AxisValuesOverrider.fixed( - minY = -2f, - maxY = 0f, - ), - mergeMode = Stack, + rememberCartesianChart( + rememberColumnCartesianLayer( + columns = columns, + axisValueOverrider = AxisValueOverrider.fixed(minY = -2f, maxY = 0f), + mergeMode = ColumnCartesianLayer.MergeMode.Stacked, + ), ), model = model, startAxis = rememberStartAxis(itemPlacer = remember { AxisItemPlacer.Vertical.default(maxItemCount = 3) }), diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ThresholdLinePreviews.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ThresholdLinePreviews.kt index a1c6f3940..40c0adf35 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ThresholdLinePreviews.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/ThresholdLinePreviews.kt @@ -29,8 +29,9 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.column.columnChart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.rememberColumnCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollSpec import com.patrykandpatrick.vico.compose.component.lineComponent import com.patrykandpatrick.vico.compose.component.shape.shader.toDynamicShader @@ -41,9 +42,10 @@ import com.patrykandpatrick.vico.compose.style.LocalChartStyle import com.patrykandpatrick.vico.core.chart.decoration.ThresholdLine import com.patrykandpatrick.vico.core.component.shape.Shapes import com.patrykandpatrick.vico.core.component.shape.shader.ComponentShader -import com.patrykandpatrick.vico.core.entry.entryModelOf +import com.patrykandpatrick.vico.core.model.CartesianChartModel +import com.patrykandpatrick.vico.core.model.ColumnCartesianLayerModel -private val model = entryModelOf(1, 2, 3, 4) +private val model = CartesianChartModel(ColumnCartesianLayerModel.build { series(1, 2, 3, 4) }) public val Color.Companion.DimmedGray: Color get() = Color(0xFFAAAAAA) @@ -59,10 +61,10 @@ private fun ProvidePreviewChartStyle(content: @Composable () -> Unit) { axisTickColor = Color.DimmedGray, axisGuidelineColor = Color.DimmedGray, ), - columnChart = - LocalChartStyle.current.columnChart.copy( + columnLayer = + LocalChartStyle.current.columnLayer.copy( columns = - LocalChartStyle.current.columnChart.columns.map { + LocalChartStyle.current.columnLayer.columns.map { lineComponent( color = Color.DimmedGray, thickness = it.thicknessDp.dp, @@ -88,18 +90,20 @@ private fun ProvidePreviewChartStyle(content: @Composable () -> Unit) { @Composable public fun ThresholdLine() { ProvidePreviewChartStyle { - Chart( + CartesianChartHost( modifier = Modifier, chart = - columnChart().apply { - addDecoration( - ThresholdLine( - thresholdValue = 2f, - lineComponent = shapeComponent(color = Color.Black), - labelComponent = textComponent(Color.Black, padding = dimensionsOf(horizontal = 8.dp)), + rememberCartesianChart( + rememberColumnCartesianLayer(), + decorations = + listOf( + ThresholdLine( + thresholdValue = 2f, + lineComponent = shapeComponent(color = Color.Black), + labelComponent = textComponent(Color.Black, padding = dimensionsOf(horizontal = 8.dp)), + ), ), - ) - }, + ), model = model, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), @@ -112,66 +116,70 @@ public fun ThresholdLine() { @Composable public fun ThresholdLineWithCustomText() { ProvidePreviewChartStyle { - Chart( + CartesianChartHost( modifier = Modifier, chart = - columnChart().apply { - addDecoration( - ThresholdLine( - thresholdValue = 2f, - thresholdLabel = "Threshold line 1 📐", - lineComponent = shapeComponent(color = Color.Black), - labelComponent = - textComponent( - color = Color.White, - lineCount = 3, - background = - shapeComponent( - shape = - Shapes.roundedCornerShape( - bottomLeftPercent = 25, - bottomRightPercent = 25, - ), - color = Color.Black, - ), - padding = - dimensionsOf( - start = 8.dp, - top = 2.dp, - end = 8.dp, - bottom = 4.dp, - ), - margins = dimensionsOf(horizontal = 4.dp), - ), - labelVerticalPosition = ThresholdLine.LabelVerticalPosition.Bottom, - ), - ) - addDecoration( - ThresholdLine( - thresholdValue = 3f, - thresholdLabel = "Threshold line 2 📐", - lineComponent = shapeComponent(color = Color.DarkGray), - labelComponent = - textComponent( - color = Color.White, - lineCount = 3, - background = - shapeComponent( - shape = Shapes.cutCornerShape(topLeftPercent = 25, topRightPercent = 25), - color = Color.DarkGray, - ), - padding = - dimensionsOf( - start = 8.dp, - top = 4.dp, - end = 8.dp, - bottom = 2.dp, - ), - margins = dimensionsOf(horizontal = 4.dp), - ), + rememberCartesianChart( + rememberColumnCartesianLayer(), + decorations = + listOf( + ThresholdLine( + thresholdValue = 2f, + thresholdLabel = "Threshold line 1 📐", + lineComponent = shapeComponent(color = Color.Black), + labelComponent = + textComponent( + color = Color.White, + lineCount = 3, + background = + shapeComponent( + shape = + Shapes.roundedCornerShape( + bottomLeftPercent = 25, + bottomRightPercent = 25, + ), + color = Color.Black, + ), + padding = + dimensionsOf( + start = 8.dp, + top = 2.dp, + end = 8.dp, + bottom = 4.dp, + ), + margins = dimensionsOf(horizontal = 4.dp), + ), + labelVerticalPosition = ThresholdLine.LabelVerticalPosition.Bottom, + ), + ThresholdLine( + thresholdValue = 3f, + thresholdLabel = "Threshold line 2 📐", + lineComponent = shapeComponent(color = Color.DarkGray), + labelComponent = + textComponent( + color = Color.White, + lineCount = 3, + background = + shapeComponent( + shape = + Shapes.cutCornerShape( + topLeftPercent = 25, + topRightPercent = 25, + ), + color = Color.DarkGray, + ), + padding = + dimensionsOf( + start = 8.dp, + top = 4.dp, + end = 8.dp, + bottom = 2.dp, + ), + margins = dimensionsOf(horizontal = 4.dp), + ), + ), ), - ) - }, + ), model = model, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), @@ -184,22 +192,24 @@ public fun ThresholdLineWithCustomText() { @Composable public fun RangedThresholdLine() { ProvidePreviewChartStyle { - Chart( + CartesianChartHost( modifier = Modifier, chart = - columnChart().apply { - addDecoration( - ThresholdLine( - thresholdRange = 2f..3f, - lineComponent = shapeComponent(color = Color.Black.copy(alpha = 0.5f)), - labelComponent = - textComponent( - color = Color.Black, - padding = dimensionsOf(horizontal = 8.dp), - ), + rememberCartesianChart( + rememberColumnCartesianLayer(), + decorations = + listOf( + ThresholdLine( + thresholdRange = 2f..3f, + lineComponent = shapeComponent(color = Color.Black.copy(alpha = 0.5f)), + labelComponent = + textComponent( + color = Color.Black, + padding = dimensionsOf(horizontal = 8.dp), + ), + ), ), - ) - }, + ), model = model, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), @@ -212,33 +222,35 @@ public fun RangedThresholdLine() { @Composable public fun RangedThresholdLineWithBrushShader() { ProvidePreviewChartStyle { - Chart( + CartesianChartHost( modifier = Modifier, chart = - columnChart().apply { - addDecoration( - ThresholdLine( - thresholdRange = 2f..3f, - lineComponent = - shapeComponent( - color = Color.Black, - dynamicShader = - Brush.verticalGradient( - colors = - listOf( - Color.Black.copy(0.75f), - Color.Black.copy(0.25f), - ), - ).toDynamicShader(), - ), - labelComponent = - textComponent( - color = Color.Black, - padding = dimensionsOf(horizontal = 8.dp), - ), + rememberCartesianChart( + rememberColumnCartesianLayer(), + decorations = + listOf( + ThresholdLine( + thresholdRange = 2f..3f, + lineComponent = + shapeComponent( + color = Color.Black, + dynamicShader = + Brush.verticalGradient( + colors = + listOf( + Color.Black.copy(0.75f), + Color.Black.copy(0.25f), + ), + ).toDynamicShader(), + ), + labelComponent = + textComponent( + color = Color.Black, + padding = dimensionsOf(horizontal = 8.dp), + ), + ), ), - ) - }, + ), model = model, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), @@ -251,32 +263,34 @@ public fun RangedThresholdLineWithBrushShader() { @Composable public fun RangedThresholdLineWithComponentShader() { ProvidePreviewChartStyle { - Chart( + CartesianChartHost( modifier = Modifier, chart = - columnChart().apply { - addDecoration( - ThresholdLine( - thresholdRange = 2f..3f, - lineComponent = - shapeComponent( - color = Color.Black, - dynamicShader = - ComponentShader( - shapeComponent(shape = Shapes.pillShape, color = Color.Black), - componentSizeDp = 4f, - ), - strokeWidth = 2.dp, - strokeColor = Color.Black, - ), - labelComponent = - textComponent( - color = Color.Black, - padding = dimensionsOf(horizontal = 8.dp), - ), + rememberCartesianChart( + rememberColumnCartesianLayer(), + decorations = + listOf( + ThresholdLine( + thresholdRange = 2f..3f, + lineComponent = + shapeComponent( + color = Color.Black, + dynamicShader = + ComponentShader( + shapeComponent(shape = Shapes.pillShape, color = Color.Black), + componentSizeDp = 4f, + ), + strokeWidth = 2.dp, + strokeColor = Color.Black, + ), + labelComponent = + textComponent( + color = Color.Black, + padding = dimensionsOf(horizontal = 8.dp), + ), + ), ), - ) - }, + ), model = model, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/composables/column/ColumnCharts.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/composables/column/ColumnCharts.kt index c2f22f1b7..2c9e37db0 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/composables/column/ColumnCharts.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/composables/column/ColumnCharts.kt @@ -19,29 +19,30 @@ package com.patrykandpatrick.vico.sample.previews.composables.column import androidx.compose.runtime.Composable import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.column.columnChart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.rememberColumnCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollSpec -import com.patrykandpatrick.vico.core.entry.ChartEntryModel +import com.patrykandpatrick.vico.core.model.CartesianChartModel import com.patrykandpatrick.vico.core.scroll.AutoScrollCondition import com.patrykandpatrick.vico.core.scroll.InitialScroll import com.patrykandpatrick.vico.sample.previews.annotation.ChartPreview import com.patrykandpatrick.vico.sample.previews.resource.PreviewSurface -import com.patrykandpatrick.vico.sample.previews.resource.mediumEntryModel -import com.patrykandpatrick.vico.sample.previews.resource.shortEntryModel +import com.patrykandpatrick.vico.sample.previews.resource.mediumColumnModel +import com.patrykandpatrick.vico.sample.previews.resource.shortColumnModel @ChartPreview @Composable public fun DefaultColumnChart( - model: ChartEntryModel = shortEntryModel, - oldModel: ChartEntryModel? = null, + model: CartesianChartModel = shortColumnModel, + oldModel: CartesianChartModel? = null, scrollable: Boolean = true, initialScroll: InitialScroll = InitialScroll.Start, - autoScrollCondition: AutoScrollCondition = AutoScrollCondition.Never, + autoScrollCondition: AutoScrollCondition = AutoScrollCondition.Never, ) { PreviewSurface { - Chart( - chart = columnChart(), + CartesianChartHost( + chart = rememberCartesianChart(rememberColumnCartesianLayer()), model = model, oldModel = oldModel, startAxis = rememberStartAxis(), @@ -59,17 +60,17 @@ public fun DefaultColumnChart( @ChartPreview @Composable public fun DefaultColumnChartLongScrollable() { - DefaultColumnChart(model = mediumEntryModel) + DefaultColumnChart(model = mediumColumnModel) } @ChartPreview @Composable public fun DefaultColumnChartLongScrollableEnd() { - DefaultColumnChart(model = mediumEntryModel, initialScroll = InitialScroll.End) + DefaultColumnChart(model = mediumColumnModel, initialScroll = InitialScroll.End) } @ChartPreview @Composable public fun DefaultColumnChartLongNonScrollable() { - DefaultColumnChart(model = mediumEntryModel, scrollable = false) + DefaultColumnChart(model = mediumColumnModel, scrollable = false) } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/composables/line/LineCharts.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/composables/line/LineCharts.kt index 77eae7c41..fcc942a2e 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/composables/line/LineCharts.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/composables/line/LineCharts.kt @@ -19,26 +19,27 @@ package com.patrykandpatrick.vico.sample.previews.composables.line import androidx.compose.runtime.Composable import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.line.lineChart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.rememberLineCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollSpec -import com.patrykandpatrick.vico.core.entry.ChartEntryModel +import com.patrykandpatrick.vico.core.model.CartesianChartModel import com.patrykandpatrick.vico.core.scroll.InitialScroll import com.patrykandpatrick.vico.sample.previews.annotation.ChartPreview import com.patrykandpatrick.vico.sample.previews.resource.PreviewSurface -import com.patrykandpatrick.vico.sample.previews.resource.mediumEntryModel -import com.patrykandpatrick.vico.sample.previews.resource.shortEntryModel +import com.patrykandpatrick.vico.sample.previews.resource.mediumLineModel +import com.patrykandpatrick.vico.sample.previews.resource.shortLineModel @ChartPreview @Composable public fun DefaultLineChart( - model: ChartEntryModel = shortEntryModel, + model: CartesianChartModel = shortLineModel, scrollable: Boolean = true, initialScroll: InitialScroll = InitialScroll.Start, ) { PreviewSurface { - Chart( - chart = lineChart(), + CartesianChartHost( + chart = rememberCartesianChart(rememberLineCartesianLayer()), model = model, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(), @@ -50,17 +51,17 @@ public fun DefaultLineChart( @ChartPreview @Composable public fun DefaultLineChartLongScrollable() { - DefaultLineChart(model = mediumEntryModel) + DefaultLineChart(model = mediumLineModel) } @ChartPreview @Composable public fun DefaultLineChartLongScrollableEnd() { - DefaultLineChart(model = mediumEntryModel, initialScroll = InitialScroll.End) + DefaultLineChart(model = mediumLineModel, initialScroll = InitialScroll.End) } @ChartPreview @Composable public fun DefaultLineChartLongNonScrollable() { - DefaultLineChart(model = mediumEntryModel, scrollable = false) + DefaultLineChart(model = mediumLineModel, scrollable = false) } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/resource/SampleModels.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/resource/SampleModels.kt index 90134f735..4a10d3053 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/resource/SampleModels.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/previews/resource/SampleModels.kt @@ -16,9 +16,16 @@ package com.patrykandpatrick.vico.sample.previews.resource -import com.patrykandpatrick.vico.core.entry.ChartEntryModel -import com.patrykandpatrick.vico.core.entry.entryModelOf +import com.patrykandpatrick.vico.core.model.CartesianChartModel +import com.patrykandpatrick.vico.core.model.ColumnCartesianLayerModel +import com.patrykandpatrick.vico.core.model.LineCartesianLayerModel -public val shortEntryModel: ChartEntryModel = entryModelOf(1, 2, 4, 8, 3) +public val shortColumnModel = CartesianChartModel(ColumnCartesianLayerModel.build { series(1, 2, 4, 8, 3) }) -public val mediumEntryModel: ChartEntryModel = entryModelOf(1, 2, 4, 8, 3, 10, 4, 7, 2, 6, 4, 8) +public val mediumColumnModel = + CartesianChartModel(ColumnCartesianLayerModel.build { series(1, 2, 4, 8, 3, 10, 4, 7, 2, 6, 4, 8) }) + +public val shortLineModel = CartesianChartModel(LineCartesianLayerModel.build { series(1, 2, 4, 8, 3) }) + +public val mediumLineModel = + CartesianChartModel(LineCartesianLayerModel.build { series(1, 2, 4, 8, 3, 10, 4, 7, 2, 6, 4, 8) }) diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ChartStyle.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ChartStyle.kt index 6f84e962b..24600a6aa 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ChartStyle.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ChartStyle.kt @@ -22,23 +22,24 @@ import androidx.compose.runtime.remember import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb +import com.patrykandpatrick.vico.compose.component.shape.shader.color import com.patrykandpatrick.vico.compose.component.shape.shader.fromBrush import com.patrykandpatrick.vico.compose.style.ChartStyle import com.patrykandpatrick.vico.core.DefaultAlpha import com.patrykandpatrick.vico.core.DefaultColors import com.patrykandpatrick.vico.core.DefaultDimens -import com.patrykandpatrick.vico.core.chart.line.LineChart +import com.patrykandpatrick.vico.core.chart.layer.LineCartesianLayer import com.patrykandpatrick.vico.core.component.shape.LineComponent import com.patrykandpatrick.vico.core.component.shape.Shapes import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders @Composable internal fun rememberChartStyle( - columnChartColors: List, - lineChartColors: List, + columnLayerColors: List, + lineLayerColors: List, ): ChartStyle { val isSystemInDarkTheme = isSystemInDarkTheme() - return remember(columnChartColors, lineChartColors, isSystemInDarkTheme) { + return remember(columnLayerColors, lineLayerColors, isSystemInDarkTheme) { val defaultColors = if (isSystemInDarkTheme) DefaultColors.Dark else DefaultColors.Light ChartStyle( ChartStyle.Axis( @@ -46,8 +47,8 @@ internal fun rememberChartStyle( axisGuidelineColor = Color(defaultColors.axisGuidelineColor), axisLineColor = Color(defaultColors.axisLineColor), ), - ChartStyle.ColumnChart( - columnChartColors.map { columnChartColor -> + ChartStyle.ColumnLayer( + columnLayerColors.map { columnChartColor -> LineComponent( columnChartColor.toArgb(), DefaultDimens.COLUMN_WIDTH, @@ -55,11 +56,11 @@ internal fun rememberChartStyle( ) }, ), - ChartStyle.LineChart( - lineChartColors.map { lineChartColor -> - LineChart.LineSpec( - lineColor = lineChartColor.toArgb(), - lineBackgroundShader = + ChartStyle.LineLayer( + lineLayerColors.map { lineChartColor -> + LineCartesianLayer.LineSpec( + shader = DynamicShaders.color(lineChartColor), + backgroundShader = DynamicShaders.fromBrush( Brush.verticalGradient( listOf( @@ -79,4 +80,4 @@ internal fun rememberChartStyle( @Composable internal fun rememberChartStyle(chartColors: List) = - rememberChartStyle(columnChartColors = chartColors, lineChartColors = chartColors) + rememberChartStyle(columnLayerColors = chartColors, lineLayerColors = chartColors) diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseScreen.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseScreen.kt index 77dc9fb3e..edaf6a561 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseScreen.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseScreen.kt @@ -26,7 +26,6 @@ import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.NavigationBar @@ -51,14 +50,13 @@ import com.patrykandpatrick.vico.sample.showcase.charts.Chart9 import com.patrykandpatrick.vico.sample.utils.plus @Composable -@OptIn(ExperimentalMaterial3Api::class) internal fun ShowcaseScreen(viewModel: ShowcaseViewModel = viewModel()) { val composeShowcaseState = rememberLazyListState() val viewShowcaseState = rememberLazyListState() Scaffold( bottomBar = { NavigationBar { - UISystem.values().forEach { uiSystem -> + UISystem.entries.forEach { uiSystem -> NavigationBarItem( selected = viewModel.uiSystem == uiSystem, onClick = { viewModel.setUISystem(uiSystem) }, @@ -94,15 +92,15 @@ private fun LazyListScope.chartItems( uiSystem: UISystem, viewModel: ShowcaseViewModel, ) { - cardItem { Chart9(uiSystem, viewModel.positiveAndNegativeChartEntryModelProducer) } - cardItem { Chart1(uiSystem, viewModel.customStepChartEntryModelProducer) } - cardItem { Chart2(uiSystem, viewModel.chartEntryModelProducer) } - cardItem { Chart3(uiSystem, viewModel.chartEntryModelProducer) } - cardItem { Chart4(uiSystem, viewModel.composedChartEntryModelProducer) } - cardItem { Chart5(uiSystem, viewModel.multiDataSetChartEntryModelProducer) } - cardItem { Chart6(uiSystem, viewModel.multiDataSetChartEntryModelProducer) } - cardItem { Chart7(uiSystem, viewModel.multiDataSetChartEntryModelProducer) } - cardItem { Chart8(uiSystem, viewModel.composedChartEntryModelProducer) } + cardItem { Chart1(uiSystem, viewModel.modelProducer1) } + cardItem { Chart2(uiSystem, viewModel.modelProducer2) } + cardItem { Chart3(uiSystem, viewModel.modelProducer1) } + cardItem { Chart4(uiSystem, viewModel.modelProducer3) } + cardItem { Chart5(uiSystem, viewModel.modelProducer4) } + cardItem { Chart6(uiSystem, viewModel.modelProducer4) } + cardItem { Chart7(uiSystem, viewModel.modelProducer5) } + cardItem { Chart8(uiSystem, viewModel.modelProducer3) } + cardItem { Chart9(uiSystem, viewModel.modelProducer6) } } private fun LazyListScope.cardItem(content: @Composable () -> Unit) { diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseViewModel.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseViewModel.kt index 00077bdae..93dbc3e6e 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseViewModel.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/ShowcaseViewModel.kt @@ -21,59 +21,46 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer -import com.patrykandpatrick.vico.core.entry.composed.ComposedChartEntryModelProducer -import com.patrykandpatrick.vico.core.util.RandomEntriesGenerator +import com.patrykandpatrick.vico.core.model.CartesianChartModelProducer +import com.patrykandpatrick.vico.core.util.RandomCartesianModelGenerator +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch internal class ShowcaseViewModel : ViewModel() { - private val generator = - RandomEntriesGenerator( - xRange = 0..GENERATOR_X_RANGE_TOP, - yRange = GENERATOR_Y_RANGE_BOTTOM..GENERATOR_Y_RANGE_TOP, - ) - - private val customStepGenerator = - RandomEntriesGenerator( - xRange = IntProgression.fromClosedRange(rangeStart = 0, rangeEnd = GENERATOR_X_RANGE_TOP, step = 2), - yRange = GENERATOR_Y_RANGE_BOTTOM..GENERATOR_Y_RANGE_TOP, - ) - - private val positiveAndNegativeValuesGenerator = - RandomEntriesGenerator( - xRange = 0..GENERATOR_X_RANGE_TOP, - yRange = -10..GENERATOR_Y_RANGE_TOP, - ) - - internal val chartEntryModelProducer: ChartEntryModelProducer = ChartEntryModelProducer() - - internal val positiveAndNegativeChartEntryModelProducer: ChartEntryModelProducer = ChartEntryModelProducer() - - internal val customStepChartEntryModelProducer: ChartEntryModelProducer = ChartEntryModelProducer() - - internal val multiDataSetChartEntryModelProducer: ChartEntryModelProducer = ChartEntryModelProducer() - - internal val composedChartEntryModelProducer = ComposedChartEntryModelProducer.build() + internal val modelProducer1 = CartesianChartModelProducer.build() + internal val modelProducer2 = CartesianChartModelProducer.build() + internal val modelProducer3 = CartesianChartModelProducer.build() + internal val modelProducer4 = CartesianChartModelProducer.build() + internal val modelProducer5 = CartesianChartModelProducer.build() + internal val modelProducer6 = CartesianChartModelProducer.build() var uiSystem by mutableStateOf(UISystem.Compose) private set init { - viewModelScope.launch { + viewModelScope.launch(Dispatchers.Default) { while (currentCoroutineContext().isActive) { - val randomSeries = generator.generateRandomEntries() - val randomDataSet = List(MULTI_ENTRIES_COMBINED) { generator.generateRandomEntries() } - chartEntryModelProducer.setEntries(randomSeries) - positiveAndNegativeChartEntryModelProducer - .setEntries(positiveAndNegativeValuesGenerator.generateRandomEntries()) - multiDataSetChartEntryModelProducer.setEntries(randomDataSet) - customStepChartEntryModelProducer.setEntries(customStepGenerator.generateRandomEntries()) - composedChartEntryModelProducer.runTransaction { - add(randomDataSet) - add(randomSeries) + val singleSeriesLineLayerModelPartial = + RandomCartesianModelGenerator.getRandomLineLayerModelPartial() + val tripleSeriesColumnLayerModelPartial = + RandomCartesianModelGenerator.getRandomColumnLayerModelPartial(seriesCount = 3) + modelProducer1.tryRunTransaction { add(singleSeriesLineLayerModelPartial) } + modelProducer2.tryRunTransaction { + add(RandomCartesianModelGenerator.getRandomColumnLayerModelPartial()) + } + modelProducer3.tryRunTransaction { + add(tripleSeriesColumnLayerModelPartial) + add(singleSeriesLineLayerModelPartial) + } + modelProducer4.tryRunTransaction { add(tripleSeriesColumnLayerModelPartial) } + modelProducer5.tryRunTransaction { + add(RandomCartesianModelGenerator.getRandomLineLayerModelPartial(seriesCount = 3)) + } + modelProducer6.tryRunTransaction { + add(RandomCartesianModelGenerator.getRandomLineLayerModelPartial(y = -10f..20f)) } delay(UPDATE_FREQUENCY) } @@ -85,10 +72,6 @@ internal class ShowcaseViewModel : ViewModel() { } private companion object { - const val MULTI_ENTRIES_COMBINED = 3 - const val GENERATOR_X_RANGE_TOP = 96 - const val GENERATOR_Y_RANGE_BOTTOM = 2 - const val GENERATOR_Y_RANGE_TOP = 20 const val UPDATE_FREQUENCY = 2000L } } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart1.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart1.kt index 15999688d..fb51175e8 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart1.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart1.kt @@ -22,11 +22,12 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.viewinterop.AndroidViewBinding import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.line.lineChart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.rememberLineCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.style.ProvideChartStyle import com.patrykandpatrick.vico.core.axis.Axis -import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer +import com.patrykandpatrick.vico.core.model.CartesianChartModelProducer import com.patrykandpatrick.vico.databinding.Chart1Binding import com.patrykandpatrick.vico.sample.showcase.UISystem import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle @@ -35,21 +36,25 @@ import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable internal fun Chart1( uiSystem: UISystem, - chartEntryModelProducer: ChartEntryModelProducer, + modelProducer: CartesianChartModelProducer, ) { when (uiSystem) { - UISystem.Compose -> ComposeChart1(chartEntryModelProducer) - UISystem.Views -> ViewChart1(chartEntryModelProducer) + UISystem.Compose -> ComposeChart1(modelProducer) + UISystem.Views -> ViewChart1(modelProducer) } } @Composable -private fun ComposeChart1(chartEntryModelProducer: ChartEntryModelProducer) { +private fun ComposeChart1(modelProducer: CartesianChartModelProducer) { val marker = rememberMarker() ProvideChartStyle(rememberChartStyle(chartColors)) { - Chart( - chart = lineChart(persistentMarkers = remember(marker) { mapOf(PERSISTENT_MARKER_X to marker) }), - chartModelProducer = chartEntryModelProducer, + CartesianChartHost( + chart = + rememberCartesianChart( + rememberLineCartesianLayer(), + persistentMarkers = remember(marker) { mapOf(PERSISTENT_MARKER_X to marker) }, + ), + modelProducer = modelProducer, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(guideline = null), marker = marker, @@ -59,13 +64,13 @@ private fun ComposeChart1(chartEntryModelProducer: ChartEntryModelProducer) { } @Composable -private fun ViewChart1(chartEntryModelProducer: ChartEntryModelProducer) { +private fun ViewChart1(modelProducer: CartesianChartModelProducer) { val marker = rememberMarker() AndroidViewBinding(Chart1Binding::inflate) { with(chartView) { chart?.addPersistentMarker(PERSISTENT_MARKER_X, marker) runInitialAnimation = false - entryProducer = chartEntryModelProducer + this.modelProducer = modelProducer (bottomAxis as Axis).guideline = null this.marker = marker } @@ -73,7 +78,7 @@ private fun ViewChart1(chartEntryModelProducer: ChartEntryModelProducer) { } private const val COLOR_1_CODE = 0xffa485e0 -private const val PERSISTENT_MARKER_X = 10f +private const val PERSISTENT_MARKER_X = 5f private val color1 = Color(COLOR_1_CODE) private val chartColors = listOf(color1) diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart2.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart2.kt index e1bc87a44..1d4de3064 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart2.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart2.kt @@ -24,8 +24,9 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidViewBinding import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.column.columnChart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.rememberColumnCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.component.shapeComponent import com.patrykandpatrick.vico.compose.component.textComponent import com.patrykandpatrick.vico.compose.dimensions.dimensionsOf @@ -41,8 +42,8 @@ import com.patrykandpatrick.vico.core.chart.decoration.ThresholdLine import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout import com.patrykandpatrick.vico.core.component.shape.LineComponent import com.patrykandpatrick.vico.core.component.shape.Shapes -import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer import com.patrykandpatrick.vico.core.extension.half +import com.patrykandpatrick.vico.core.model.CartesianChartModelProducer import com.patrykandpatrick.vico.databinding.Chart2Binding import com.patrykandpatrick.vico.sample.showcase.UISystem import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle @@ -51,31 +52,30 @@ import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable internal fun Chart2( uiSystem: UISystem, - chartEntryModelProducer: ChartEntryModelProducer, + modelProducer: CartesianChartModelProducer, ) { when (uiSystem) { - UISystem.Compose -> ComposeChart2(chartEntryModelProducer) - UISystem.Views -> ViewChart2(chartEntryModelProducer) + UISystem.Compose -> ComposeChart2(modelProducer) + UISystem.Views -> ViewChart2(modelProducer) } } @Composable -private fun ComposeChart2(chartEntryModelProducer: ChartEntryModelProducer) { +private fun ComposeChart2(modelProducer: CartesianChartModelProducer) { val thresholdLine = rememberThresholdLine() ProvideChartStyle(rememberChartStyle(chartColors)) { - val defaultColumns = currentChartStyle.columnChart.columns - Chart( + val defaultColumns = currentChartStyle.columnLayer.columns + CartesianChartHost( chart = - columnChart( - columns = + rememberCartesianChart( + rememberColumnCartesianLayer( remember(defaultColumns) { - defaultColumns.map { defaultColumn -> - LineComponent(defaultColumn.color, COLUMN_WIDTH_DP, defaultColumn.shape) - } + defaultColumns.map { LineComponent(it.color, COLUMN_WIDTH_DP, it.shape) } }, + ), decorations = remember(thresholdLine) { listOf(thresholdLine) }, ), - chartModelProducer = chartEntryModelProducer, + modelProducer = modelProducer, startAxis = rememberStartAxis(valueFormatter = startAxisValueFormatter, itemPlacer = startAxisItemPlacer), bottomAxis = rememberBottomAxis(itemPlacer = bottomAxisItemPlacer), marker = rememberMarker(), @@ -86,14 +86,14 @@ private fun ComposeChart2(chartEntryModelProducer: ChartEntryModelProducer) { } @Composable -private fun ViewChart2(chartEntryModelProducer: ChartEntryModelProducer) { +private fun ViewChart2(modelProducer: CartesianChartModelProducer) { val thresholdLine = rememberThresholdLine() val marker = rememberMarker() AndroidViewBinding(Chart2Binding::inflate) { with(chartView) { chart?.addDecoration(thresholdLine) runInitialAnimation = false - entryProducer = chartEntryModelProducer + this.modelProducer = modelProducer with(startAxis as VerticalAxis) { itemPlacer = startAxisItemPlacer valueFormatter = startAxisValueFormatter diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart3.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart3.kt index bcbf83e1b..73ada2230 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart3.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart3.kt @@ -25,20 +25,22 @@ import androidx.compose.ui.viewinterop.AndroidViewBinding import com.patrykandpatrick.vico.R import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost import com.patrykandpatrick.vico.compose.chart.edges.rememberFadingEdges +import com.patrykandpatrick.vico.compose.chart.layer.rememberLineCartesianLayer import com.patrykandpatrick.vico.compose.chart.layout.fullWidth -import com.patrykandpatrick.vico.compose.chart.line.lineChart +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.component.shapeComponent import com.patrykandpatrick.vico.compose.component.textComponent import com.patrykandpatrick.vico.compose.dimensions.dimensionsOf import com.patrykandpatrick.vico.compose.style.ProvideChartStyle import com.patrykandpatrick.vico.core.axis.vertical.VerticalAxis +import com.patrykandpatrick.vico.core.chart.layer.LineCartesianLayer import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout -import com.patrykandpatrick.vico.core.chart.line.LineChart -import com.patrykandpatrick.vico.core.chart.values.AxisValuesOverrider +import com.patrykandpatrick.vico.core.chart.values.AxisValueOverrider import com.patrykandpatrick.vico.core.component.shape.Shapes -import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer +import com.patrykandpatrick.vico.core.model.CartesianChartModelProducer +import com.patrykandpatrick.vico.core.model.LineCartesianLayerModel import com.patrykandpatrick.vico.databinding.Chart3Binding import com.patrykandpatrick.vico.sample.showcase.UISystem import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle @@ -47,20 +49,20 @@ import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable internal fun Chart3( uiSystem: UISystem, - chartEntryModelProducer: ChartEntryModelProducer, + modelProducer: CartesianChartModelProducer, ) { when (uiSystem) { - UISystem.Compose -> ComposeChart3(chartEntryModelProducer) - UISystem.Views -> ViewChart3(chartEntryModelProducer) + UISystem.Compose -> ComposeChart3(modelProducer) + UISystem.Views -> ViewChart3(modelProducer) } } @Composable -private fun ComposeChart3(chartEntryModelProducer: ChartEntryModelProducer) { +private fun ComposeChart3(modelProducer: CartesianChartModelProducer) { ProvideChartStyle(rememberChartStyle(chartColors)) { - Chart( - chart = lineChart(axisValuesOverrider = axisValueOverrider), - chartModelProducer = chartEntryModelProducer, + CartesianChartHost( + chart = rememberCartesianChart(rememberLineCartesianLayer(axisValueOverrider = axisValueOverrider)), + modelProducer = modelProducer, startAxis = rememberStartAxis( guideline = null, @@ -96,13 +98,13 @@ private fun ComposeChart3(chartEntryModelProducer: ChartEntryModelProducer) { } @Composable -private fun ViewChart3(chartEntryModelProducer: ChartEntryModelProducer) { +private fun ViewChart3(modelProducer: CartesianChartModelProducer) { val marker = rememberMarker() AndroidViewBinding(Chart3Binding::inflate) { with(chartView) { - (chart as LineChart).axisValuesOverrider = axisValueOverrider + (chart?.layers?.get(0) as LineCartesianLayer?)?.axisValueOverrider = axisValueOverrider runInitialAnimation = false - entryProducer = chartEntryModelProducer + this.modelProducer = modelProducer this.marker = marker } } @@ -116,7 +118,10 @@ private val color1 = Color(COLOR_1_CODE) private val color2 = Color(COLOR_2_CODE) private val chartColors = listOf(color1, color2) private val axisValueOverrider = - AxisValuesOverrider.adaptiveYValues(yFraction = AXIS_VALUE_OVERRIDER_Y_FRACTION, round = true) + AxisValueOverrider.adaptiveYValues( + yFraction = AXIS_VALUE_OVERRIDER_Y_FRACTION, + round = true, + ) private val axisTitleHorizontalPaddingValue = 8.dp private val axisTitleVerticalPaddingValue = 2.dp private val axisTitlePadding = dimensionsOf(axisTitleHorizontalPaddingValue, axisTitleVerticalPaddingValue) diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart4.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart4.kt index a3ec02270..375cac2fb 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart4.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart4.kt @@ -23,18 +23,18 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidViewBinding import com.patrykandpatrick.vico.compose.axis.horizontal.rememberTopAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberEndAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.column.columnChart -import com.patrykandpatrick.vico.compose.chart.line.lineChart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.rememberColumnCartesianLayer +import com.patrykandpatrick.vico.compose.chart.layer.rememberLineCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.component.shape.roundedCornerShape import com.patrykandpatrick.vico.compose.style.ProvideChartStyle import com.patrykandpatrick.vico.compose.style.currentChartStyle import com.patrykandpatrick.vico.core.chart.DefaultPointConnector -import com.patrykandpatrick.vico.core.chart.composed.plus import com.patrykandpatrick.vico.core.chart.copy import com.patrykandpatrick.vico.core.component.shape.LineComponent import com.patrykandpatrick.vico.core.component.shape.Shapes -import com.patrykandpatrick.vico.core.entry.composed.ComposedChartEntryModelProducer +import com.patrykandpatrick.vico.core.model.CartesianChartModelProducer import com.patrykandpatrick.vico.databinding.Chart4Binding import com.patrykandpatrick.vico.sample.showcase.UISystem import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle @@ -43,40 +43,34 @@ import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable internal fun Chart4( uiSystem: UISystem, - chartEntryModelProducer: ComposedChartEntryModelProducer, + modelProducer: CartesianChartModelProducer, ) { when (uiSystem) { - UISystem.Compose -> ComposeChart4(chartEntryModelProducer) - UISystem.Views -> ViewChart4(chartEntryModelProducer) + UISystem.Compose -> ComposeChart4(modelProducer) + UISystem.Views -> ViewChart4(modelProducer) } } @Composable -private fun ComposeChart4(chartEntryModelProducer: ComposedChartEntryModelProducer) { +private fun ComposeChart4(modelProducer: CartesianChartModelProducer) { ProvideChartStyle(rememberChartStyle(columnChartColors, lineChartColors)) { - val defaultColumns = currentChartStyle.columnChart.columns - val defaultLines = currentChartStyle.lineChart.lines - val columnChart = - columnChart( - remember(defaultColumns) { - defaultColumns.map { defaultColumn -> - LineComponent( - defaultColumn.color, - defaultColumn.thicknessDp, - Shapes.roundedCornerShape(columnCornerRadius), - ) - } - }, - ) - val lineChart = - lineChart( - remember(defaultLines) { - defaultLines.map { defaultLine -> defaultLine.copy(pointConnector = pointConnector) } - }, - ) - Chart( - chart = remember(columnChart, lineChart) { columnChart + lineChart }, - chartModelProducer = chartEntryModelProducer, + val defaultColumns = currentChartStyle.columnLayer.columns + val defaultLines = currentChartStyle.lineLayer.lines + CartesianChartHost( + chart = + rememberCartesianChart( + rememberColumnCartesianLayer( + remember(defaultColumns) { + defaultColumns.map { + LineComponent(it.color, it.thicknessDp, Shapes.roundedCornerShape(columnCornerRadius)) + } + }, + ), + rememberLineCartesianLayer( + remember(defaultLines) { defaultLines.map { it.copy(pointConnector = pointConnector) } }, + ), + ), + modelProducer = modelProducer, topAxis = rememberTopAxis(), endAxis = rememberEndAxis(), marker = rememberMarker(), @@ -86,12 +80,12 @@ private fun ComposeChart4(chartEntryModelProducer: ComposedChartEntryModelProduc } @Composable -private fun ViewChart4(chartEntryModelProducer: ComposedChartEntryModelProducer) { +private fun ViewChart4(modelProducer: CartesianChartModelProducer) { val marker = rememberMarker() AndroidViewBinding(Chart4Binding::inflate) { with(chartView) { runInitialAnimation = false - entryProducer = chartEntryModelProducer + this.modelProducer = modelProducer this.marker = marker } } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart5.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart5.kt index 75602d707..c2a21771e 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart5.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart5.kt @@ -22,17 +22,18 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.viewinterop.AndroidViewBinding import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.column.columnChart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.rememberColumnCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.style.ProvideChartStyle import com.patrykandpatrick.vico.compose.style.currentChartStyle import com.patrykandpatrick.vico.core.DefaultDimens import com.patrykandpatrick.vico.core.axis.AxisItemPlacer import com.patrykandpatrick.vico.core.axis.vertical.VerticalAxis -import com.patrykandpatrick.vico.core.chart.column.ColumnChart +import com.patrykandpatrick.vico.core.chart.layer.ColumnCartesianLayer import com.patrykandpatrick.vico.core.component.shape.LineComponent import com.patrykandpatrick.vico.core.component.shape.Shapes -import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer +import com.patrykandpatrick.vico.core.model.CartesianChartModelProducer import com.patrykandpatrick.vico.databinding.Chart5Binding import com.patrykandpatrick.vico.sample.showcase.UISystem import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle @@ -41,43 +42,49 @@ import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable internal fun Chart5( uiSystem: UISystem, - chartEntryModelProducer: ChartEntryModelProducer, + modelProducer: CartesianChartModelProducer, ) { when (uiSystem) { - UISystem.Compose -> ComposeChart5(chartEntryModelProducer) - UISystem.Views -> ViewChart5(chartEntryModelProducer) + UISystem.Compose -> ComposeChart5(modelProducer) + UISystem.Views -> ViewChart5(modelProducer) } } @Composable -private fun ComposeChart5(chartEntryModelProducer: ChartEntryModelProducer) { +private fun ComposeChart5(modelProducer: CartesianChartModelProducer) { ProvideChartStyle(rememberChartStyle(chartColors)) { - val defaultColumns = currentChartStyle.columnChart.columns - Chart( + val defaultColumns = currentChartStyle.columnLayer.columns + CartesianChartHost( chart = - columnChart( - columns = - remember(defaultColumns) { - defaultColumns.mapIndexed { index, defaultColumn -> - val topCornerRadiusPercent = - if (index == defaultColumns.lastIndex) DefaultDimens.COLUMN_ROUNDNESS_PERCENT else 0 - val bottomCornerRadiusPercent = - if (index == 0) DefaultDimens.COLUMN_ROUNDNESS_PERCENT else 0 - LineComponent( - defaultColumn.color, - defaultColumn.thicknessDp, - Shapes.roundedCornerShape( - topCornerRadiusPercent, - topCornerRadiusPercent, - bottomCornerRadiusPercent, - bottomCornerRadiusPercent, - ), - ) - } - }, - mergeMode = ColumnChart.MergeMode.Stack, + rememberCartesianChart( + rememberColumnCartesianLayer( + columns = + remember(defaultColumns) { + defaultColumns.mapIndexed { index, defaultColumn -> + val topCornerRadiusPercent = + if (index == defaultColumns.lastIndex) { + DefaultDimens.COLUMN_ROUNDNESS_PERCENT + } else { + 0 + } + val bottomCornerRadiusPercent = + if (index == 0) DefaultDimens.COLUMN_ROUNDNESS_PERCENT else 0 + LineComponent( + defaultColumn.color, + defaultColumn.thicknessDp, + Shapes.roundedCornerShape( + topCornerRadiusPercent, + topCornerRadiusPercent, + bottomCornerRadiusPercent, + bottomCornerRadiusPercent, + ), + ) + } + }, + mergeMode = ColumnCartesianLayer.MergeMode.Stacked, + ), ), - chartModelProducer = chartEntryModelProducer, + modelProducer = modelProducer, startAxis = rememberStartAxis( itemPlacer = startAxisItemPlacer, @@ -91,13 +98,12 @@ private fun ComposeChart5(chartEntryModelProducer: ChartEntryModelProducer) { } @Composable -private fun ViewChart5(chartEntryModelProducer: ChartEntryModelProducer) { +private fun ViewChart5(modelProducer: CartesianChartModelProducer) { val marker = rememberMarker() AndroidViewBinding(Chart5Binding::inflate) { with(chartView) { - (chart as ColumnChart).mergeMode = ColumnChart.MergeMode.Stack runInitialAnimation = false - entryProducer = chartEntryModelProducer + this.modelProducer = modelProducer (startAxis as VerticalAxis).itemPlacer = startAxisItemPlacer this.marker = marker } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart6.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart6.kt index abc53e17c..351048359 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart6.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart6.kt @@ -24,8 +24,9 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidViewBinding import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.column.columnChart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.rememberColumnCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.component.shapeComponent import com.patrykandpatrick.vico.compose.component.textComponent import com.patrykandpatrick.vico.compose.dimensions.dimensionsOf @@ -34,11 +35,11 @@ import com.patrykandpatrick.vico.compose.style.currentChartStyle import com.patrykandpatrick.vico.core.axis.AxisPosition import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter import com.patrykandpatrick.vico.core.axis.horizontal.HorizontalAxis -import com.patrykandpatrick.vico.core.chart.column.ColumnChart import com.patrykandpatrick.vico.core.chart.decoration.ThresholdLine +import com.patrykandpatrick.vico.core.chart.layer.ColumnCartesianLayer import com.patrykandpatrick.vico.core.component.shape.LineComponent import com.patrykandpatrick.vico.core.component.shape.Shapes -import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer +import com.patrykandpatrick.vico.core.model.CartesianChartModelProducer import com.patrykandpatrick.vico.databinding.Chart6Binding import com.patrykandpatrick.vico.sample.showcase.UISystem import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle @@ -47,23 +48,23 @@ import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable internal fun Chart6( uiSystem: UISystem, - chartEntryModelProducer: ChartEntryModelProducer, + modelProducer: CartesianChartModelProducer, ) { when (uiSystem) { - UISystem.Compose -> ComposeChart6(chartEntryModelProducer) - UISystem.Views -> ViewChart6(chartEntryModelProducer) + UISystem.Compose -> ComposeChart6(modelProducer) + UISystem.Views -> ViewChart6(modelProducer) } } @Composable -private fun ComposeChart6(chartEntryModelProducer: ChartEntryModelProducer) { +private fun ComposeChart6(modelProducer: CartesianChartModelProducer) { val thresholdLine = rememberThresholdLine() ProvideChartStyle(rememberChartStyle(chartColors)) { - val defaultColumns = currentChartStyle.columnChart.columns - Chart( + val defaultColumns = currentChartStyle.columnLayer.columns + CartesianChartHost( chart = - columnChart( - columns = + rememberCartesianChart( + rememberColumnCartesianLayer( remember(defaultColumns) { defaultColumns.map { defaultColumn -> LineComponent( @@ -73,10 +74,11 @@ private fun ComposeChart6(chartEntryModelProducer: ChartEntryModelProducer) { ) } }, - mergeMode = ColumnChart.MergeMode.Grouped, + mergeMode = ColumnCartesianLayer.MergeMode.Grouped, + ), decorations = remember(thresholdLine) { listOf(thresholdLine) }, ), - chartModelProducer = chartEntryModelProducer, + modelProducer = modelProducer, startAxis = rememberStartAxis(), bottomAxis = rememberBottomAxis(valueFormatter = bottomAxisValueFormatter), marker = rememberMarker(), @@ -86,7 +88,7 @@ private fun ComposeChart6(chartEntryModelProducer: ChartEntryModelProducer) { } @Composable -private fun ViewChart6(chartEntryModelProducer: ChartEntryModelProducer) { +private fun ViewChart6(modelProducer: CartesianChartModelProducer) { val thresholdLine = rememberThresholdLine() val decorations = remember(thresholdLine) { listOf(thresholdLine) } val marker = rememberMarker() @@ -94,7 +96,7 @@ private fun ViewChart6(chartEntryModelProducer: ChartEntryModelProducer) { with(chartView) { chart?.setDecorations(decorations) runInitialAnimation = false - entryProducer = chartEntryModelProducer + this.modelProducer = modelProducer (bottomAxis as? HorizontalAxis)?.valueFormatter = bottomAxisValueFormatter this.marker = marker } @@ -141,4 +143,4 @@ private val thresholdLineLabelMargins = dimensionsOf(thresholdLineLabelMarginVal private val thresholdLineColor = color4.copy(THRESHOLD_LINE_ALPHA) private val daysOfWeek = listOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun") private val bottomAxisValueFormatter = - AxisValueFormatter { x, _ -> daysOfWeek[x.toInt() % daysOfWeek.size] } + AxisValueFormatter { x, _, _ -> daysOfWeek[x.toInt() % daysOfWeek.size] } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart7.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart7.kt index 3054f75d1..a7e97968a 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart7.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart7.kt @@ -28,8 +28,9 @@ import com.patrykandpatrick.vico.R import com.patrykandpatrick.vico.compose.axis.axisLabelComponent import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.line.lineChart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.rememberLineCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.component.shape.roundedCornerShape import com.patrykandpatrick.vico.compose.component.shapeComponent import com.patrykandpatrick.vico.compose.component.textComponent @@ -41,7 +42,7 @@ import com.patrykandpatrick.vico.compose.style.currentChartStyle import com.patrykandpatrick.vico.core.axis.vertical.VerticalAxis import com.patrykandpatrick.vico.core.chart.copy import com.patrykandpatrick.vico.core.component.shape.Shapes -import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer +import com.patrykandpatrick.vico.core.model.CartesianChartModelProducer import com.patrykandpatrick.vico.databinding.Chart7Binding import com.patrykandpatrick.vico.sample.showcase.UISystem import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle @@ -50,26 +51,26 @@ import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable internal fun Chart7( uiSystem: UISystem, - chartEntryModelProducer: ChartEntryModelProducer, + modelProducer: CartesianChartModelProducer, ) { when (uiSystem) { - UISystem.Compose -> ComposeChart7(chartEntryModelProducer) - UISystem.Views -> ViewChart7(chartEntryModelProducer) + UISystem.Compose -> ComposeChart7(modelProducer) + UISystem.Views -> ViewChart7(modelProducer) } } @Composable -private fun ComposeChart7(chartEntryModelProducer: ChartEntryModelProducer) { +private fun ComposeChart7(modelProducer: CartesianChartModelProducer) { ProvideChartStyle(rememberChartStyle(chartColors)) { - val defaultLines = currentChartStyle.lineChart.lines - Chart( + val defaultLines = currentChartStyle.lineLayer.lines + CartesianChartHost( chart = - lineChart( - remember(defaultLines) { - defaultLines.map { defaultLine -> defaultLine.copy(lineBackgroundFill = null) } - }, + rememberCartesianChart( + rememberLineCartesianLayer( + remember(defaultLines) { defaultLines.map { it.copy(backgroundShader = null) } }, + ), ), - chartModelProducer = chartEntryModelProducer, + modelProducer = modelProducer, startAxis = rememberStartAxis( label = rememberStartAxisLabel(), @@ -84,14 +85,14 @@ private fun ComposeChart7(chartEntryModelProducer: ChartEntryModelProducer) { } @Composable -private fun ViewChart7(chartEntryModelProducer: ChartEntryModelProducer) { +private fun ViewChart7(modelProducer: CartesianChartModelProducer) { val startAxisLabel = rememberStartAxisLabel() val marker = rememberMarker() val legend = rememberLegend() AndroidViewBinding(Chart7Binding::inflate) { with(chartView) { runInitialAnimation = false - entryProducer = chartEntryModelProducer + this.modelProducer = modelProducer (startAxis as VerticalAxis).horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside (startAxis as VerticalAxis).label = startAxisLabel this.marker = marker @@ -124,7 +125,7 @@ private fun rememberLegend() = textSize = legendItemLabelTextSize, typeface = Typeface.MONOSPACE, ), - labelText = stringResource(R.string.data_set_x, index + 1), + labelText = stringResource(R.string.series_x, index + 1), ) }, iconSize = legendItemIconSize, diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart8.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart8.kt index a72d6dccd..bdb175bd3 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart8.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart8.kt @@ -17,22 +17,20 @@ package com.patrykandpatrick.vico.sample.showcase.charts import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.graphics.Color import androidx.compose.ui.viewinterop.AndroidViewBinding import com.patrykandpatrick.vico.compose.axis.vertical.rememberEndAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.column.columnChart -import com.patrykandpatrick.vico.compose.chart.line.lineChart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.rememberColumnCartesianLayer +import com.patrykandpatrick.vico.compose.chart.layer.rememberLineCartesianLayer +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.style.ProvideChartStyle import com.patrykandpatrick.vico.core.axis.Axis import com.patrykandpatrick.vico.core.axis.AxisPosition -import com.patrykandpatrick.vico.core.chart.column.ColumnChart -import com.patrykandpatrick.vico.core.chart.composed.ComposedChart -import com.patrykandpatrick.vico.core.chart.composed.plus -import com.patrykandpatrick.vico.core.chart.line.LineChart -import com.patrykandpatrick.vico.core.entry.composed.ComposedChartEntryModelProducer +import com.patrykandpatrick.vico.core.chart.layer.ColumnCartesianLayer +import com.patrykandpatrick.vico.core.chart.layer.LineCartesianLayer +import com.patrykandpatrick.vico.core.model.CartesianChartModelProducer import com.patrykandpatrick.vico.databinding.Chart8Binding import com.patrykandpatrick.vico.sample.showcase.UISystem import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle @@ -41,26 +39,27 @@ import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable internal fun Chart8( uiSystem: UISystem, - chartEntryModelProducer: ComposedChartEntryModelProducer, + modelProducer: CartesianChartModelProducer, ) { when (uiSystem) { - UISystem.Compose -> ComposeChart8(chartEntryModelProducer) - UISystem.Views -> ViewChart8(chartEntryModelProducer) + UISystem.Compose -> ComposeChart8(modelProducer) + UISystem.Views -> ViewChart8(modelProducer) } } @Composable -private fun ComposeChart8(chartEntryModelProducer: ComposedChartEntryModelProducer) { +private fun ComposeChart8(modelProducer: CartesianChartModelProducer) { ProvideChartStyle(rememberChartStyle(columnChartColors, lineChartColors)) { - val columnChart = - columnChart( - mergeMode = ColumnChart.MergeMode.Stack, - targetVerticalAxisPosition = AxisPosition.Vertical.Start, - ) - val lineChart = lineChart(targetVerticalAxisPosition = AxisPosition.Vertical.End) - Chart( - chart = remember(columnChart, lineChart) { columnChart + lineChart }, - chartModelProducer = chartEntryModelProducer, + CartesianChartHost( + chart = + rememberCartesianChart( + rememberColumnCartesianLayer( + mergeMode = ColumnCartesianLayer.MergeMode.Stacked, + verticalAxisPosition = AxisPosition.Vertical.Start, + ), + rememberLineCartesianLayer(verticalAxisPosition = AxisPosition.Vertical.End), + ), + modelProducer = modelProducer, startAxis = rememberStartAxis(guideline = null), endAxis = rememberEndAxis(), marker = rememberMarker(), @@ -70,15 +69,14 @@ private fun ComposeChart8(chartEntryModelProducer: ComposedChartEntryModelProduc } @Composable -private fun ViewChart8(chartEntryModelProducer: ComposedChartEntryModelProducer) { +private fun ViewChart8(modelProducer: CartesianChartModelProducer) { val marker = rememberMarker() AndroidViewBinding(Chart8Binding::inflate) { with(chartView) { - ((chart as ComposedChart).charts[0] as ColumnChart).mergeMode = ColumnChart.MergeMode.Stack - ((chart as ComposedChart).charts[0] as ColumnChart).targetVerticalAxisPosition = AxisPosition.Vertical.Start - ((chart as ComposedChart).charts[1] as LineChart).targetVerticalAxisPosition = AxisPosition.Vertical.End + (chart?.layers?.get(0) as ColumnCartesianLayer).verticalAxisPosition = AxisPosition.Vertical.Start + (chart?.layers?.get(1) as LineCartesianLayer).verticalAxisPosition = AxisPosition.Vertical.End runInitialAnimation = false - entryProducer = chartEntryModelProducer + this.modelProducer = modelProducer (startAxis as Axis).guideline = null this.marker = marker } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart9.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart9.kt index c78dee52b..71c95ddc3 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart9.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart9.kt @@ -30,27 +30,28 @@ import com.patrykandpatrick.vico.R import com.patrykandpatrick.vico.compose.axis.axisLabelComponent import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart +import com.patrykandpatrick.vico.compose.chart.CartesianChartHost +import com.patrykandpatrick.vico.compose.chart.layer.lineSpec +import com.patrykandpatrick.vico.compose.chart.layer.rememberLineCartesianLayer import com.patrykandpatrick.vico.compose.chart.layout.fullWidth -import com.patrykandpatrick.vico.compose.chart.line.lineChart -import com.patrykandpatrick.vico.compose.chart.line.lineSpec +import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.component.lineComponent import com.patrykandpatrick.vico.compose.component.shape.dashedShape +import com.patrykandpatrick.vico.compose.component.shape.shader.color import com.patrykandpatrick.vico.compose.component.shape.shader.fromComponent -import com.patrykandpatrick.vico.compose.component.shape.shader.split import com.patrykandpatrick.vico.compose.component.shape.shader.verticalGradient import com.patrykandpatrick.vico.compose.component.shapeComponent import com.patrykandpatrick.vico.compose.dimensions.dimensionsOf import com.patrykandpatrick.vico.compose.style.ProvideChartStyle import com.patrykandpatrick.vico.core.axis.Axis import com.patrykandpatrick.vico.core.axis.AxisItemPlacer +import com.patrykandpatrick.vico.core.chart.layer.LineCartesianLayer import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout -import com.patrykandpatrick.vico.core.chart.line.LineChart import com.patrykandpatrick.vico.core.component.shape.ShapeComponent import com.patrykandpatrick.vico.core.component.shape.Shapes import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders -import com.patrykandpatrick.vico.core.component.shape.shader.splitShader -import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer +import com.patrykandpatrick.vico.core.component.shape.shader.TopBottomShader +import com.patrykandpatrick.vico.core.model.CartesianChartModelProducer import com.patrykandpatrick.vico.databinding.Chart9Binding import com.patrykandpatrick.vico.sample.showcase.UISystem import com.patrykandpatrick.vico.sample.showcase.rememberChartStyle @@ -59,59 +60,65 @@ import com.patrykandpatrick.vico.sample.showcase.rememberMarker @Composable internal fun Chart9( uiSystem: UISystem, - chartEntryModelProducer: ChartEntryModelProducer, + modelProducer: CartesianChartModelProducer, ) { when (uiSystem) { - UISystem.Compose -> ComposeChart9(chartEntryModelProducer) - UISystem.Views -> ViewChart9(chartEntryModelProducer) + UISystem.Compose -> ComposeChart9(modelProducer) + UISystem.Views -> ViewChart9(modelProducer) } } @Composable -private fun ComposeChart9(chartEntryModelProducer: ChartEntryModelProducer) { +private fun ComposeChart9(modelProducer: CartesianChartModelProducer) { val marker = rememberMarker() ProvideChartStyle(rememberChartStyle(chartColors)) { - Chart( + CartesianChartHost( chart = - lineChart( - lines = - listOf( - lineSpec( - lineShader = DynamicShaders.split(chartColors[0], chartColors[1]), - lineBackgroundShader = - DynamicShaders.splitShader( - DynamicShaders.composeShader( - DynamicShaders.fromComponent( - componentSize = 6.dp, - component = - shapeComponent( - shape = Shapes.pillShape, - color = chartColors[0], - margins = remember { dimensionsOf(1.dp) }, - ), - ), - verticalGradient(arrayOf(Color.Black, Color.Transparent)), - PorterDuff.Mode.DST_IN, + rememberCartesianChart( + rememberLineCartesianLayer( + lines = + listOf( + lineSpec( + shader = + TopBottomShader( + DynamicShaders.color(chartColors[0]), + DynamicShaders.color(chartColors[1]), ), - DynamicShaders.composeShader( - DynamicShaders.fromComponent( - componentSize = 5.dp, - component = - shapeComponent( - shape = Shapes.rectShape, - color = chartColors[1], - margins = remember { dimensionsOf(horizontal = 2.dp) }, - ), - checkeredArrangement = false, + backgroundShader = + TopBottomShader( + DynamicShaders.composeShader( + DynamicShaders.fromComponent( + componentSize = 6.dp, + component = + shapeComponent( + shape = Shapes.pillShape, + color = chartColors[0], + margins = remember { dimensionsOf(1.dp) }, + ), + ), + verticalGradient(arrayOf(Color.Black, Color.Transparent)), + PorterDuff.Mode.DST_IN, + ), + DynamicShaders.composeShader( + DynamicShaders.fromComponent( + componentSize = 5.dp, + component = + shapeComponent( + shape = Shapes.rectShape, + color = chartColors[1], + margins = remember { dimensionsOf(horizontal = 2.dp) }, + ), + checkeredArrangement = false, + ), + verticalGradient(arrayOf(Color.Transparent, Color.Black)), + PorterDuff.Mode.DST_IN, ), - verticalGradient(arrayOf(Color.Transparent, Color.Black)), - PorterDuff.Mode.DST_IN, ), - ), + ), ), - ), + ), ), - chartModelProducer = chartEntryModelProducer, + modelProducer = modelProducer, startAxis = rememberStartAxis( label = @@ -148,9 +155,12 @@ private fun ComposeChart9(chartEntryModelProducer: ChartEntryModelProducer) { rememberBottomAxis( guideline = null, itemPlacer = - AxisItemPlacer.Horizontal.default( - spacing = 3, - ), + remember { + AxisItemPlacer.Horizontal.default( + spacing = 3, + addExtremeLabelPadding = true, + ) + }, ), marker = marker, runInitialAnimation = false, @@ -160,22 +170,26 @@ private fun ComposeChart9(chartEntryModelProducer: ChartEntryModelProducer) { } @Composable -private fun ViewChart9(chartEntryModelProducer: ChartEntryModelProducer) { +private fun ViewChart9(modelProducer: CartesianChartModelProducer) { val marker = rememberMarker() val colors = chartColors AndroidViewBinding(Chart9Binding::inflate) { with(chartView) { runInitialAnimation = false - entryProducer = chartEntryModelProducer + this.modelProducer = modelProducer (bottomAxis as Axis).guideline = null this.marker = marker - with(chart as LineChart) { + with(chart?.layers?.get(0) as LineCartesianLayer) { lines = listOf( - LineChart.LineSpec( - lineShader = DynamicShaders.split(colors[0], colors[1]), - lineBackgroundShader = - DynamicShaders.splitShader( + LineCartesianLayer.LineSpec( + shader = + TopBottomShader( + DynamicShaders.color(colors[0]), + DynamicShaders.color(colors[1]), + ), + backgroundShader = + TopBottomShader( DynamicShaders.composeShader( DynamicShaders.fromComponent( componentSize = 6.dp, diff --git a/sample/src/main/res/layout/chart_1.xml b/sample/src/main/res/layout/chart_1.xml index d725c7366..487d80dc6 100644 --- a/sample/src/main/res/layout/chart_1.xml +++ b/sample/src/main/res/layout/chart_1.xml @@ -19,13 +19,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content"> - diff --git a/sample/src/main/res/layout/chart_2.xml b/sample/src/main/res/layout/chart_2.xml index 27dab5533..0b803d99d 100644 --- a/sample/src/main/res/layout/chart_2.xml +++ b/sample/src/main/res/layout/chart_2.xml @@ -19,13 +19,13 @@ android:layout_width="wrap_content" android:layout_height="wrap_content"> - - diff --git a/sample/src/main/res/layout/chart_4.xml b/sample/src/main/res/layout/chart_4.xml index ff8308348..2cddd33e7 100644 --- a/sample/src/main/res/layout/chart_4.xml +++ b/sample/src/main/res/layout/chart_4.xml @@ -19,13 +19,13 @@ android:layout_width="wrap_content" android:layout_height="wrap_content"> - diff --git a/sample/src/main/res/layout/chart_5.xml b/sample/src/main/res/layout/chart_5.xml index 1aaf69d00..6e060bb88 100644 --- a/sample/src/main/res/layout/chart_5.xml +++ b/sample/src/main/res/layout/chart_5.xml @@ -19,13 +19,13 @@ android:layout_width="wrap_content" android:layout_height="wrap_content"> - diff --git a/sample/src/main/res/layout/chart_6.xml b/sample/src/main/res/layout/chart_6.xml index 62426d299..b05159b7e 100644 --- a/sample/src/main/res/layout/chart_6.xml +++ b/sample/src/main/res/layout/chart_6.xml @@ -19,12 +19,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content"> - diff --git a/sample/src/main/res/layout/chart_7.xml b/sample/src/main/res/layout/chart_7.xml index f29e9408b..029ab4dec 100644 --- a/sample/src/main/res/layout/chart_7.xml +++ b/sample/src/main/res/layout/chart_7.xml @@ -19,12 +19,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content"> - diff --git a/sample/src/main/res/layout/chart_8.xml b/sample/src/main/res/layout/chart_8.xml index 79618a81d..28bf86aee 100644 --- a/sample/src/main/res/layout/chart_8.xml +++ b/sample/src/main/res/layout/chart_8.xml @@ -19,13 +19,13 @@ android:layout_width="wrap_content" android:layout_height="wrap_content"> - diff --git a/sample/src/main/res/layout/chart_9.xml b/sample/src/main/res/layout/chart_9.xml index 81a618baa..bc0ffd4c8 100644 --- a/sample/src/main/res/layout/chart_9.xml +++ b/sample/src/main/res/layout/chart_9.xml @@ -19,20 +19,18 @@ android:layout_width="wrap_content" android:layout_height="wrap_content"> - diff --git a/sample/src/main/res/values-night/chart_9_styles.xml b/sample/src/main/res/values-night/chart_9_styles.xml index 5097869de..240d9d53f 100644 --- a/sample/src/main/res/values-night/chart_9_styles.xml +++ b/sample/src/main/res/values-night/chart_9_styles.xml @@ -15,6 +15,6 @@ --> - #C7FF6C - #A46CFF + #735cff + #ff337d diff --git a/sample/src/main/res/values/chart_1_styles.xml b/sample/src/main/res/values/chart_1_styles.xml index 246dcb589..64408d525 100644 --- a/sample/src/main/res/values/chart_1_styles.xml +++ b/sample/src/main/res/values/chart_1_styles.xml @@ -21,7 +21,7 @@ @color/chart_1_color_1 - diff --git a/sample/src/main/res/values/chart_2_styles.xml b/sample/src/main/res/values/chart_2_styles.xml index 6be808647..8afc7aed7 100644 --- a/sample/src/main/res/values/chart_2_styles.xml +++ b/sample/src/main/res/values/chart_2_styles.xml @@ -22,7 +22,7 @@ 16dp - diff --git a/sample/src/main/res/values/chart_3_styles.xml b/sample/src/main/res/values/chart_3_styles.xml index 8dbcb282e..ea172ec95 100644 --- a/sample/src/main/res/values/chart_3_styles.xml +++ b/sample/src/main/res/values/chart_3_styles.xml @@ -68,7 +68,7 @@ @string/x_axis - diff --git a/sample/src/main/res/values/chart_4_styles.xml b/sample/src/main/res/values/chart_4_styles.xml index c13939d66..b6ea074b3 100644 --- a/sample/src/main/res/values/chart_4_styles.xml +++ b/sample/src/main/res/values/chart_4_styles.xml @@ -45,13 +45,13 @@ 0% - - diff --git a/sample/src/main/res/values/chart_5_styles.xml b/sample/src/main/res/values/chart_5_styles.xml index 35150e750..adbd4351d 100644 --- a/sample/src/main/res/values/chart_5_styles.xml +++ b/sample/src/main/res/values/chart_5_styles.xml @@ -56,9 +56,10 @@ 45 - diff --git a/sample/src/main/res/values/chart_6_styles.xml b/sample/src/main/res/values/chart_6_styles.xml index 124b06e1d..1bcb7c47f 100644 --- a/sample/src/main/res/values/chart_6_styles.xml +++ b/sample/src/main/res/values/chart_6_styles.xml @@ -40,7 +40,7 @@ @color/chart_6_color_3 - - - - diff --git a/sample/src/main/res/values/chart_9_styles.xml b/sample/src/main/res/values/chart_9_styles.xml index 378a4ae60..bffef869d 100644 --- a/sample/src/main/res/values/chart_9_styles.xml +++ b/sample/src/main/res/values/chart_9_styles.xml @@ -23,13 +23,14 @@ ?colorSecondary -