From f71321a3b2c4e3a14e46a33e103c0b7c0c862660 Mon Sep 17 00:00:00 2001 From: Patryk Goworowski Date: Wed, 27 Mar 2024 19:54:56 +0100 Subject: [PATCH 01/97] Show a `Marker` for columns with y-axis value equal to 0 (cherry picked from commit 1a408c00b50cbbf5d564c6e201d44a54989817f2) --- .../vico/core/component/shape/LineComponent.kt | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) 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..35d55810e 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 @@ -1,5 +1,5 @@ /* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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. @@ -139,12 +139,10 @@ 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) { + val left = centerX - thickness * thicknessScale / 2 + val right = centerX + thickness * thicknessScale / 2 + boundingBox.left < right && left < boundingBox.right + } } From 5657a656e50fded431169df63fa4c409d6d584f0 Mon Sep 17 00:00:00 2001 From: Patryk Goworowski Date: Sun, 21 Apr 2024 13:10:38 +0200 Subject: [PATCH 02/97] `ColumnCartesianLayer`: Fix issues affecting data labels Co-authored-by: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> --- .../vico/core/chart/column/ColumnChart.kt | 126 ++++++++++++------ 1 file changed, 86 insertions(+), 40 deletions(-) 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 223701daf..ec4aed592 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 @@ -1,5 +1,5 @@ /* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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. @@ -108,8 +108,11 @@ public open class ColumnChart( * containing the bottom coordinate of the collection’s bottommost column and the top coordinate of the collection’s * topmost column. This hash map is used by [drawChart] and [drawChartInternal]. */ + @Deprecated("Use `stackInfo` instead.", ReplaceWith("stackInfo")) protected val heightMap: HashMap> = HashMap() + protected val stackInfo: MutableMap = mutableMapOf() + /** * Holds information on the [ColumnChart]’s horizontal dimensions. */ @@ -130,6 +133,7 @@ public open class ColumnChart( drawingModel = model.extraStore.getOrNull(drawingModelKey), ) heightMap.clear() + stackInfo.clear() } protected open fun ChartDrawContext.drawChartInternal( @@ -165,15 +169,16 @@ public open class ColumnChart( when (mergeMode) { MergeMode.Stack -> { + val stackInfo = stackInfo.getOrPut(columnCenterX) { StackInfo() } val (stackedNegHeight, stackedPosHeight) = heightMap.getOrElse(entry.x) { 0f to 0f } - columnBottom = zeroLinePosition + - if (entry.y < 0f) { - height + stackedNegHeight + columnBottom = + if (entry.y >= 0f) { + zeroLinePosition - stackInfo.topHeight } else { - -stackedPosHeight + zeroLinePosition + stackInfo.bottomHeight + height } - columnTop = (columnBottom - height).coerceAtMost(columnBottom) + stackInfo.update(entry.y, height) heightMap[entry.x] = if (entry.y < 0f) { stackedNegHeight + height to stackedPosHeight @@ -214,12 +219,12 @@ public open class ColumnChart( isLast = index == model.entries.lastIndex && entry.x == chartValues.maxX, ) } else if (index == model.entries.lastIndex) { - val yValues = heightMap[entry.x] + val (positiveY, negativeY) = stackInfo.getValue(columnCenterX) drawStackedDataLabel( modelEntriesSize = model.entries.size, columnThicknessDp = column.thicknessDp, - negativeY = yValues?.first, - positiveY = yValues?.second, + negativeY = negativeY, + positiveY = positiveY, x = columnCenterX, zeroLinePosition = zeroLinePosition, heightMultiplier = heightMultiplier, @@ -242,13 +247,28 @@ public open class ColumnChart( isFirst: Boolean, isLast: Boolean, ) { - if (positiveY != null && positiveY > 0f) { - val y = zeroLinePosition - positiveY * heightMultiplier - drawDataLabel(modelEntriesSize, columnThicknessDp, positiveY, x, y, isFirst, isLast) + val stackInfo = stackInfo.getValue(x) + if (stackInfo.topY > 0f) { + drawDataLabel( + modelEntriesSize = modelEntriesSize, + columnThicknessDp = columnThicknessDp, + dataLabelValue = stackInfo.topY, + x = x, + y = zeroLinePosition - stackInfo.topHeight, + isFirst = isFirst, + isLast = isLast, + ) } - if (negativeY != null && negativeY < 0f) { - val y = zeroLinePosition + abs(negativeY) * heightMultiplier - drawDataLabel(modelEntriesSize, columnThicknessDp, negativeY, x, y, isFirst, isLast) + if (stackInfo.bottomY < 0f) { + drawDataLabel( + modelEntriesSize = modelEntriesSize, + columnThicknessDp = columnThicknessDp, + dataLabelValue = stackInfo.bottomY, + x = x, + y = zeroLinePosition + stackInfo.bottomHeight, + isFirst = isFirst, + isLast = isLast, + ) } } @@ -266,44 +286,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, @@ -366,6 +391,7 @@ public open class ColumnChart( scalableEndPadding = xSpacing.half, ) } + is HorizontalLayout.FullWidth -> { horizontalDimensions.ensureValuesAtLeast( xSpacing = xSpacing, @@ -488,4 +514,24 @@ public open class ColumnChart( } .let(::ColumnChartDrawingModel) } + + protected data class StackInfo( + var topY: Float = 0f, + var bottomY: Float = 0f, + var topHeight: Float = 0f, + var bottomHeight: Float = 0f, + ) { + public fun update( + y: Float, + height: Float, + ) { + if (y >= 0f) { + topY += y + topHeight += height + } else { + bottomY += y + bottomHeight += height + } + } + } } From efb62c63d249a3bba8c5e19d08b4b7af24f67a8b Mon Sep 17 00:00:00 2001 From: Patryk Goworowski Date: Fri, 29 Mar 2024 19:49:04 +0100 Subject: [PATCH 03/97] Fix `TextComponent` styling bug Co-authored-by: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> --- .../vico/core/component/text/TextComponent.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 551b96fea..955541c1c 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 @@ -1,5 +1,5 @@ /* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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,6 +22,7 @@ import android.graphics.Paint import android.graphics.RectF import android.graphics.Typeface import android.text.Layout +import android.text.SpannableStringBuilder import android.text.Spanned import android.text.StaticLayout import android.text.TextPaint @@ -333,8 +334,8 @@ public open class TextComponent protected constructor() : Padding, Margins { 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' } + val measuredText = SpannableStringBuilder(text ?: "") + if (pad) repeat((lineCount - measuredText.lines().size).coerceAtLeast(0)) { measuredText.append('\n') } getLayout(measuredText, width, height, rotationDegrees) .getBounds(outRect) .apply { @@ -377,7 +378,7 @@ public open class TextComponent protected constructor() : Padding, Margins { } - padding.horizontalDp.wholePixels ).coerceAtLeast(0) - val key = LAYOUT_KEY_PREFIX + text + correctedWidth + rotationDegrees + textPaint.hashCode() + val key = LAYOUT_KEY_PREFIX + text.hashCode() + correctedWidth + rotationDegrees + textPaint.hashCode() return getOrPutExtra(key = key) { textPaint.textSize = spToPx(textSizeSp) staticLayout( From 377f354d234643f16adbd30d58bcdbb29248be4d Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Sat, 27 Apr 2024 15:47:12 +0200 Subject: [PATCH 04/97] `ComposedChartEntryModelProducer.build`: Make `transaction` nullable --- .../core/entry/composed/ComposedChartEntryModelProducer.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 4dc7fa277..616998702 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 @@ -383,8 +383,8 @@ public class ComposedChartEntryModelProducer private constructor(dispatcher: Cor */ public fun build( dispatcher: CoroutineDispatcher = Dispatchers.Default, - transaction: Transaction.() -> Unit = {}, + transaction: (Transaction.() -> Unit)? = null, ): ComposedChartEntryModelProducer = - ComposedChartEntryModelProducer(dispatcher).also { it.runTransaction(transaction) } + ComposedChartEntryModelProducer(dispatcher).apply { if (transaction != null) runTransaction(transaction) } } } From a4444a01bec2b129307856c78103b7a569592458 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Sat, 27 Apr 2024 16:04:01 +0200 Subject: [PATCH 05/97] `ChartEntryModelProducer`: Make `entryCollections` parameter of primary constructor nullable --- .../vico/core/entry/ChartEntryModelProducer.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 530da87e8..9752d8775 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 @@ -43,7 +43,7 @@ import kotlinx.coroutines.sync.Mutex * @see ChartModelProducer */ public class ChartEntryModelProducer( - entryCollections: List>, + entryCollections: List>? = null, dispatcher: CoroutineDispatcher = Dispatchers.Default, ) : ChartModelProducer { @@ -60,7 +60,7 @@ public class ChartEntryModelProducer( ) : this(entryCollections.toList(), dispatcher) init { - setEntries(entryCollections) + if (entryCollections != null) setEntries(entryCollections) } /** From 37ffa414b0ea60d233081382a5447b0aac46eda5 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Sat, 27 Apr 2024 16:06:48 +0200 Subject: [PATCH 06/97] Update `version_name` --- versions.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.gradle b/versions.gradle index 40d7d36e6..13bc52f8f 100644 --- a/versions.gradle +++ b/versions.gradle @@ -18,7 +18,7 @@ ext { library = [ groupId : "com.patrykandpatrick.vico", - version_name : "1.14.0", + version_name : "1.15.0-alpha.1", version_code : 1, target_sdk : 34, min_sdk : 16, From c23a8212ddfd0f3528aec22c30a71e76a90e13b5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 May 2024 16:01:06 +0000 Subject: [PATCH 07/97] Update dependency io.mockk:mockk to v1.13.11 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8a161f429..1799a9876 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ jUnitExt = "1.1.5" kotlin = "1.9.22" lifecycle = "2.7.0" material = "1.11.0" -mockK = "1.13.9" +mockK = "1.13.11" paparazziGradlePlugin = "1.3.1" testCore = "1.5.0" From e077e81a942297a49d26771ddf4527dad3ed975d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 May 2024 13:56:31 +0000 Subject: [PATCH 08/97] Update dependency org.jetbrains.dokka to v1.9.20 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1799a9876..8f21ae889 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ composeBom = "2024.02.00" composeCompiler = "1.5.8" coroutines = "1.7.3" detekt = "1.23.4" -dokka = "1.9.10" +dokka = "1.9.20" jUnit = "4.13.2" jUnitExt = "1.1.5" kotlin = "1.9.22" From fc94273b64d0d2ca6ed032e62e9f4e0ce8be0b79 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 May 2024 14:01:14 +0000 Subject: [PATCH 09/97] Update detekt to v1.23.6 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8f21ae889..5de4c6178 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ appcompat = "1.6.1" composeBom = "2024.02.00" composeCompiler = "1.5.8" coroutines = "1.7.3" -detekt = "1.23.4" +detekt = "1.23.6" dokka = "1.9.20" jUnit = "4.13.2" jUnitExt = "1.1.5" From e971639f9d42431d031f4a61d8580fc13ab9a7ed Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Tue, 21 May 2024 11:10:43 +0200 Subject: [PATCH 10/97] Fix `VerticalAxis` applying excessive maximum width to labels (cherry picked from commit 8427692853c7574fb17296c21bd07ecd04db2401) --- .../vico/core/axis/vertical/VerticalAxis.kt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) 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 30337bcf9..7b46a203f 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 @@ -1,5 +1,5 @@ /* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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. @@ -218,11 +218,7 @@ 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 = (bounds.width() - tickLength - axisThickness).toInt(), ) } } From 69141535b93c1dbf4b5dc9cb1c69984b5c42fe28 Mon Sep 17 00:00:00 2001 From: Patryk Goworowski Date: Wed, 22 May 2024 19:19:33 +0200 Subject: [PATCH 11/97] Fix `VerticalAxis` incorrectly using `tickLength` for horizontal insets (cherry picked from commit a877f1a39e82ef9913543af5e30222c991fafe40) --- .../patrykandpatrick/vico/core/axis/vertical/VerticalAxis.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 7b46a203f..84cbf34fd 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 @@ -275,10 +275,10 @@ public class VerticalAxis( ) }.orZero val labelSpace = when (horizontalLabelPosition) { - Outside -> getMaxLabelWidth(height) + Outside -> getMaxLabelWidth(height) + tickLength Inside -> 0f } - (labelSpace + titleComponentWidth + axisThickness + tickLength) + (labelSpace + titleComponentWidth + axisThickness) .coerceIn(minimumValue = constraint.minSizeDp.pixels, maximumValue = constraint.maxSizeDp.pixels) } is SizeConstraint.Exact -> constraint.sizeDp.pixels From b9fc993b11a1f9118eb9e5d554dec24515774296 Mon Sep 17 00:00:00 2001 From: Patryk Goworowski Date: Wed, 15 May 2024 19:06:10 +0200 Subject: [PATCH 12/97] Fix `BaseChartView.onTouchEvent()` The view now invokes click listeners and does not handle touch events when in disabled state (cherry picked from commit e1439bfaee3f7139c8568f414a3ae1688d3a2b38) --- .../com/patrykandpatrick/vico/views/chart/BaseChartView.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 c43faafd2..6f94484ba 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 @@ -411,6 +411,8 @@ public abstract class BaseChartView internal constructo } override fun onTouchEvent(event: MotionEvent): Boolean { + val superHandled = super.onTouchEvent(event) + if (!isEnabled) return superHandled val scaleHandled = if (isZoomEnabled && event.pointerCount > 1 && chartScrollSpec.isScrollEnabled) { scaleGestureDetector.onTouchEvent(event) } else { @@ -427,7 +429,7 @@ public abstract class BaseChartView internal constructo scrollDirectionResolved = false } - return touchHandled || scaleHandled + return touchHandled || scaleHandled || superHandled } private fun handleZoom(focusX: Float, zoomChange: Float) { From 18f628f3564e84eed5b99d8ddf4b4cc2b608231d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 May 2024 20:38:56 +0000 Subject: [PATCH 13/97] Update dependency androidx.activity:activity-compose to v1.9.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5de4c6178..9bef23a73 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] accompanist = "0.34.0" -activity = "1.8.2" +activity = "1.9.0" agp = "8.2.2" androidXAnnotation = "1.7.1" androidXCore = "1.12.0" From 83a4d3fff4a7b911688101a77542cabb1eb41f72 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 May 2024 20:48:56 +0000 Subject: [PATCH 14/97] Update dependency androidx.annotation:annotation to v1.8.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9bef23a73..d6f64d417 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ accompanist = "0.34.0" activity = "1.9.0" agp = "8.2.2" -androidXAnnotation = "1.7.1" +androidXAnnotation = "1.8.0" androidXCore = "1.12.0" appcompat = "1.6.1" composeBom = "2024.02.00" From 33e56f86113779d1c92510c6ccd1864ba8a85833 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 May 2024 20:55:36 +0000 Subject: [PATCH 15/97] Update dependency androidx.core:core-ktx to v1.13.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d6f64d417..7817f1a32 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ accompanist = "0.34.0" activity = "1.9.0" agp = "8.2.2" androidXAnnotation = "1.8.0" -androidXCore = "1.12.0" +androidXCore = "1.13.1" appcompat = "1.6.1" composeBom = "2024.02.00" composeCompiler = "1.5.8" From 4fe57451d69836a02ec16bebdb2c9525b8f6a824 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 07:42:04 +0000 Subject: [PATCH 16/97] Update dependency androidx.appcompat:appcompat to v1.7.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7817f1a32..d6d2bd126 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ activity = "1.9.0" agp = "8.2.2" androidXAnnotation = "1.8.0" androidXCore = "1.13.1" -appcompat = "1.6.1" +appcompat = "1.7.0" composeBom = "2024.02.00" composeCompiler = "1.5.8" coroutines = "1.7.3" From ac61fadce35d338aef6b75a663d5cce37927935d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 07:54:22 +0000 Subject: [PATCH 17/97] Update dependency androidx.compose:compose-bom to v2024.05.00 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d6d2bd126..61f666243 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ agp = "8.2.2" androidXAnnotation = "1.8.0" androidXCore = "1.13.1" appcompat = "1.7.0" -composeBom = "2024.02.00" +composeBom = "2024.05.00" composeCompiler = "1.5.8" coroutines = "1.7.3" detekt = "1.23.6" From edfb3146faeb69f40868355bfa4476832142e86c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 09:59:09 +0000 Subject: [PATCH 18/97] Update dependency com.google.android.material:material to v1.12.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 61f666243..c00723072 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ jUnit = "4.13.2" jUnitExt = "1.1.5" kotlin = "1.9.22" lifecycle = "2.7.0" -material = "1.11.0" +material = "1.12.0" mockK = "1.13.11" paparazziGradlePlugin = "1.3.1" testCore = "1.5.0" From 53286dbabb4c13d4f1be0e3dbcb766989ca6e5b2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 12:03:20 +0000 Subject: [PATCH 19/97] Update dependency gradle to v8.7 --- gradle/wrapper/gradle-wrapper.jar | Bin 63721 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 14 +++++++------- gradlew.bat | 20 ++++++++++---------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c49b765f8051ef9d0a6055ff8e46073d8..e6441136f3d4ba8a0da8d277868979cfbc8ad796 100644 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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鯊%1Et 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⁢ zv5o9#E7!G0XmdzfsS{efPNi`1b44~SZ4Z8fuX!I}#8g+(wxzQwUT#Xb2(tbY1+EUhGKoT@KEU9Ktl>_0 z%bjDJg;#*gtJZv!-Zs`?^}v5eKmnbjqlvnSzE@_SP|LG_PJ6CYU+6zY6>92%E+ z=j@TZf-iW4(%U{lnYxQA;7Q!b;^brF8n0D>)`q5>|WDDXLrqYU_tKN2>=#@~OE7grMnNh?UOz-O~6 z6%rHy{#h9K0AT+lDC7q4{hw^|q6*Ry;;L%Q@)Ga}$60_q%D)rv(CtS$CQbpq9|y1e zRSrN4;$Jyl{m5bZw`$8TGvb}(LpY{-cQ)fcyJv7l3S52TLXVDsphtv&aPuDk1OzCA z4A^QtC(!11`IsNx_HnSy?>EKpHJWT^wmS~hc^p^zIIh@9f6U@I2 zC=Mve{j2^)mS#U$e{@Q?SO6%LDsXz@SY+=cK_QMmXBIU)j!$ajc-zLx3V60EXJ!qC zi<%2x8Q24YN+&8U@CIlN zrZkcT9yh%LrlGS9`G)KdP(@9Eo-AQz@8GEFWcb7U=a0H^ZVbLmz{+&M7W(nXJ4sN8 zJLR7eeK(K8`2-}j(T7JsO`L!+CvbueT%izanm-^A1Dn{`1Nw`9P?cq;7no+XfC`K(GO9?O^5zNIt4M+M8LM0=7Gz8UA@Z0N+lg+cX)NfazRu z5D)~HA^(u%w^cz+@2@_#S|u>GpB+j4KzQ^&Wcl9f z&hG#bCA(Yk0D&t&aJE^xME^&E-&xGHhXn%}psEIj641H+Nl-}boj;)Zt*t(4wZ5DN z@GXF$bL=&pBq-#vkTkh>7hl%K5|3 z{`Vn9b$iR-SoGENp}bn4;fR3>9sA%X2@1L3aE9yTra;Wb#_`xWwLSLdfu+PAu+o3| zGVnpzPr=ch{uuoHjtw7+_!L_2;knQ!DuDl0R`|%jr+}jFzXtrHIKc323?JO{l&;VF z*L1+}JU7%QJOg|5|Tc|D8fN zJORAg=_vsy{ak|o);@)Yh8Lkcg@$FG3k@ep36BRa^>~UmnRPziS>Z=`Jb2x*Q#`%A zU*i3&Vg?TluO@X0O;r2Jl6LKLUOVhSqg1*qOt^|8*c7 zo(298@+r$k_wQNGHv{|$tW(T8L+4_`FQ{kEW5Jgg{yf7ey4ss_(SNKfz(N9lx&a;< je(UuV8hP?p&}TPdm1I$XmG#(RzlD&B2izSj9sl%y5~4qc diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3fa8f862f..b82aa23a4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 0adc8e1a5..1aa94a426 100755 --- a/gradlew +++ b/gradlew @@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -202,11 +202,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index 93e3f59f1..25da30dbd 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail From c96c385f49ea008a0cdb26d9307c68f1ccf1d015 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 13:31:08 +0000 Subject: [PATCH 20/97] Update dependency com.android.tools.build:gradle to v8.4.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c00723072..069784aea 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] accompanist = "0.34.0" activity = "1.9.0" -agp = "8.2.2" +agp = "8.4.1" androidXAnnotation = "1.8.0" androidXCore = "1.13.1" appcompat = "1.7.0" From 37aa5f7b43d46ec554282b527a18da7718f0161a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 14:46:12 +0000 Subject: [PATCH 21/97] Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-core to v1.8.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 069784aea..63934e755 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ androidXCore = "1.13.1" appcompat = "1.7.0" composeBom = "2024.05.00" composeCompiler = "1.5.8" -coroutines = "1.7.3" +coroutines = "1.8.1" detekt = "1.23.6" dokka = "1.9.20" jUnit = "4.13.2" From a6b862edc5b3968ef0e9ee571edbe2c4dc131223 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 15:09:53 +0000 Subject: [PATCH 22/97] Update lifecycle to v2.8.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 63934e755..74322c895 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,7 +13,7 @@ dokka = "1.9.20" jUnit = "4.13.2" jUnitExt = "1.1.5" kotlin = "1.9.22" -lifecycle = "2.7.0" +lifecycle = "2.8.1" material = "1.12.0" mockK = "1.13.11" paparazziGradlePlugin = "1.3.1" From 885d1b5a90da03d128b0c6995fc4e7d054116aca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 16:00:36 +0000 Subject: [PATCH 23/97] Update actions/checkout action to v4 --- .github/workflows/build-debug-apk.yml | 2 +- .github/workflows/release-update.yml | 2 +- .github/workflows/run-detekt.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-debug-apk.yml b/.github/workflows/build-debug-apk.yml index 8ab3a8321..2221f94c2 100644 --- a/.github/workflows/build-debug-apk.yml +++ b/.github/workflows/build-debug-apk.yml @@ -6,7 +6,7 @@ jobs: build-debug-apk: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: java-version: 17 diff --git a/.github/workflows/release-update.yml b/.github/workflows/release-update.yml index 866438ba4..e9ea7c10f 100644 --- a/.github/workflows/release-update.yml +++ b/.github/workflows/release-update.yml @@ -8,7 +8,7 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: path: vico - uses: actions/setup-java@v3 diff --git a/.github/workflows/run-detekt.yml b/.github/workflows/run-detekt.yml index cecfbcdaa..2d4853202 100644 --- a/.github/workflows/run-detekt.yml +++ b/.github/workflows/run-detekt.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest continue-on-error: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: java-version: 17 From bcc3af5b8b00d57cf8a1434e1bd2ac84429156b7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 16:07:16 +0000 Subject: [PATCH 24/97] Update actions/setup-java action to v4 --- .github/workflows/build-debug-apk.yml | 2 +- .github/workflows/release-update.yml | 2 +- .github/workflows/run-detekt.yml | 2 +- .github/workflows/run-tests.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-debug-apk.yml b/.github/workflows/build-debug-apk.yml index 2221f94c2..b94ad3c95 100644 --- a/.github/workflows/build-debug-apk.yml +++ b/.github/workflows/build-debug-apk.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: java-version: 17 distribution: zulu diff --git a/.github/workflows/release-update.yml b/.github/workflows/release-update.yml index e9ea7c10f..12b92b0f8 100644 --- a/.github/workflows/release-update.yml +++ b/.github/workflows/release-update.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v4 with: path: vico - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: java-version: 17 distribution: zulu diff --git a/.github/workflows/run-detekt.yml b/.github/workflows/run-detekt.yml index 2d4853202..a5e831b24 100644 --- a/.github/workflows/run-detekt.yml +++ b/.github/workflows/run-detekt.yml @@ -8,7 +8,7 @@ jobs: continue-on-error: true steps: - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: java-version: 17 distribution: zulu diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index a6b3f4732..687c32f03 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,7 +9,7 @@ jobs: - uses: actions/checkout@v4 with: lfs: true - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: java-version: 17 distribution: adopt From 7442403a7be14d36db14249b4b288b7dc6327218 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 19:04:32 +0000 Subject: [PATCH 25/97] Update actions/upload-artifact action to v4 --- .github/workflows/build-debug-apk.yml | 2 +- .github/workflows/run-tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-debug-apk.yml b/.github/workflows/build-debug-apk.yml index b94ad3c95..f61032364 100644 --- a/.github/workflows/build-debug-apk.yml +++ b/.github/workflows/build-debug-apk.yml @@ -13,7 +13,7 @@ jobs: distribution: zulu - uses: gradle/gradle-build-action@v2 - run: ./gradlew assembleDebug - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: Debug APK path: sample/build/outputs/apk/debug/**.apk diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 687c32f03..6e1e358aa 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -19,7 +19,7 @@ jobs: - id: paparazzi if: ${{ success() || steps.unit-tests.conclusion == 'failure' }} run: ./gradlew sample:verifyPaparazziDebug - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: ${{ !cancelled() && steps.paparazzi.conclusion == 'failure' }} with: name: Paparazzi deltas From cd14d535b7fae988139fc40c0785fd791d60ea20 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 21:03:12 +0000 Subject: [PATCH 26/97] Update gradle/gradle-build-action action to v3 --- .github/workflows/build-debug-apk.yml | 2 +- .github/workflows/release-update.yml | 2 +- .github/workflows/run-detekt.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-debug-apk.yml b/.github/workflows/build-debug-apk.yml index f61032364..5e0bd55e0 100644 --- a/.github/workflows/build-debug-apk.yml +++ b/.github/workflows/build-debug-apk.yml @@ -11,7 +11,7 @@ jobs: with: java-version: 17 distribution: zulu - - uses: gradle/gradle-build-action@v2 + - uses: gradle/gradle-build-action@v3 - run: ./gradlew assembleDebug - uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/release-update.yml b/.github/workflows/release-update.yml index 12b92b0f8..44769a96d 100644 --- a/.github/workflows/release-update.yml +++ b/.github/workflows/release-update.yml @@ -15,7 +15,7 @@ jobs: with: java-version: 17 distribution: zulu - - uses: gradle/gradle-build-action@v2 + - uses: gradle/gradle-build-action@v3 - run: | git config --global user.email "96002241+patrykandpatrickbot@users.noreply.github.com" git config --global user.name "Patryk & Patrick Bot" diff --git a/.github/workflows/run-detekt.yml b/.github/workflows/run-detekt.yml index a5e831b24..1d5919b47 100644 --- a/.github/workflows/run-detekt.yml +++ b/.github/workflows/run-detekt.yml @@ -12,5 +12,5 @@ jobs: with: java-version: 17 distribution: zulu - - uses: gradle/gradle-build-action@v2 + - uses: gradle/gradle-build-action@v3 - run: ./gradlew detekt From 50f35e011036eda63e17f0849627c9722d7b5a9f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 01:56:55 +0000 Subject: [PATCH 27/97] Update dependency gradle to v8.8 --- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23a4..a4413138c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a426..b740cf133 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. From a8772798267b6a89bf1e5ba9bc879c2156a9d8a0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 07:55:10 +0000 Subject: [PATCH 28/97] Update softprops/action-gh-release action to v2 --- .github/workflows/release-update.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-update.yml b/.github/workflows/release-update.yml index 44769a96d..217c066f2 100644 --- a/.github/workflows/release-update.yml +++ b/.github/workflows/release-update.yml @@ -28,7 +28,7 @@ jobs: echo $VERSION_NAME | grep -q "alpha\|beta" && IS_PRERELEASE=true echo "IS_PRERELEASE=$IS_PRERELEASE" >> $GITHUB_ENV ./gradlew assembleDebug - - uses: softprops/action-gh-release@v1 + - uses: softprops/action-gh-release@v2 with: tag_name: ${{ env.TAG_NAME }} token: ${{ secrets.BOT_PAT }} From 73a7f7c7094fd5c1e8e5a733937e8f94ae3cb5ae Mon Sep 17 00:00:00 2001 From: Patryk Goworowski Date: Fri, 31 May 2024 20:16:34 +0200 Subject: [PATCH 29/97] Handle precision errors when calculating `xStepMultiplier` (cherry picked from commit 8b60c62345a233d203aef0c3e876ad7180daedda) --- .../vico/core/chart/column/ColumnChart.kt | 4 ++-- .../vico/core/chart/values/ChartValues.kt | 13 ++++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) 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 ec4aed592..3a2b284a7 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/column/ColumnChart.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/chart/column/ColumnChart.kt @@ -30,6 +30,7 @@ import com.patrykandpatrick.vico.core.chart.put import com.patrykandpatrick.vico.core.chart.values.ChartValues import com.patrykandpatrick.vico.core.chart.values.ChartValuesManager import com.patrykandpatrick.vico.core.chart.values.ChartValuesProvider +import com.patrykandpatrick.vico.core.chart.values.getXSpacingMultiplier import com.patrykandpatrick.vico.core.component.shape.LineComponent import com.patrykandpatrick.vico.core.component.text.TextComponent import com.patrykandpatrick.vico.core.component.text.VerticalPosition @@ -161,8 +162,7 @@ public open class ColumnChart( val columnInfo = drawingModel?.getOrNull(index)?.get(entry.x) height = (columnInfo?.height ?: (abs(entry.y) / chartValues.lengthY)) * bounds.height() - val xSpacingMultiplier = (entry.x - chartValues.minX) / chartValues.xStep - check(xSpacingMultiplier % 1f == 0f) { "Each entry’s x value must be a multiple of the x step." } + val xSpacingMultiplier = chartValues.getXSpacingMultiplier(entry.x) columnCenterX = drawingStart + (horizontalDimensions.xSpacing * xSpacingMultiplier + column.thicknessDp.half.pixels * zoom) * layoutDirectionMultiplier 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 f2c5ac789..6b1f201f0 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 @@ -1,5 +1,5 @@ /* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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,8 +18,11 @@ package com.patrykandpatrick.vico.core.chart.values import com.patrykandpatrick.vico.core.chart.Chart import com.patrykandpatrick.vico.core.entry.ChartEntryModel +import com.patrykandpatrick.vico.core.extension.round import kotlin.math.abs +import kotlin.math.absoluteValue import kotlin.math.ceil +import kotlin.math.ulp /** * Where [Chart]s get their data from. @@ -94,3 +97,11 @@ public interface ChartValues { public fun getMaxMajorEntryCount(): Int = ceil(abs(maxX - minX) / xStep + 1).toInt() } + +internal fun ChartValues.getXSpacingMultiplier(entryX: Float): Float { + val xSpacingMultiplier = (entryX - minX) / xStep + check((xSpacingMultiplier - xSpacingMultiplier.round).absoluteValue <= xSpacingMultiplier.ulp) { + "Each entry’s x value must be a multiple of the x step." + } + return xSpacingMultiplier +} From eec7bb73b574c49a8abe3c6f868647a426dea339 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Fri, 31 May 2024 11:33:00 +0200 Subject: [PATCH 30/97] Make `VerticalAxis`-related fixes (cherry picked from commit c001b70ca7d8dd1b6e4bae8448f064c2f25053f1) --- .../vico/core/axis/vertical/VerticalAxis.kt | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) 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 84cbf34fd..2d51d9cf4 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 @@ -35,6 +35,8 @@ import com.patrykandpatrick.vico.core.component.text.HorizontalPosition import com.patrykandpatrick.vico.core.component.text.TextComponent import com.patrykandpatrick.vico.core.component.text.VerticalPosition import com.patrykandpatrick.vico.core.context.MeasureContext +import com.patrykandpatrick.vico.core.context.getExtraOr +import com.patrykandpatrick.vico.core.extension.ceil import com.patrykandpatrick.vico.core.extension.getEnd import com.patrykandpatrick.vico.core.extension.getStart import com.patrykandpatrick.vico.core.extension.half @@ -43,6 +45,7 @@ import com.patrykandpatrick.vico.core.extension.translate import com.patrykandpatrick.vico.core.throwable.UnknownAxisPositionException private const val TITLE_ABS_ROTATION_DEGREES = 90f +private const val MAX_LABEL_WIDTH_KEY = "maxLabelWidthKey" /** * An implementation of [AxisRenderer] used for vertical axes. This class extends [Axis]. @@ -218,7 +221,7 @@ public class VerticalAxis( horizontalPosition = textHorizontalPosition, verticalPosition = verticalLabelPosition.textPosition, rotationDegrees = labelRotationDegrees, - maxTextWidth = (bounds.width() - tickLength - axisThickness).toInt(), + maxTextWidth = getExtraOr(MAX_LABEL_WIDTH_KEY) { chartBounds.width().half - tickLength }.toInt(), ) } } @@ -275,12 +278,18 @@ public class VerticalAxis( ) }.orZero val labelSpace = when (horizontalLabelPosition) { - Outside -> getMaxLabelWidth(height) + tickLength + Outside -> { + val maxLabelWidth = getMaxLabelWidth(height).ceil + putExtra(MAX_LABEL_WIDTH_KEY, maxLabelWidth) + maxLabelWidth + tickLength + } + Inside -> 0f } (labelSpace + titleComponentWidth + axisThickness) .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( @@ -294,14 +303,26 @@ public class VerticalAxis( val chartValues = chartValuesProvider.getChartValues(position) itemPlacer .getHeightMeasurementLabelValues(this, position) - .maxOfOrNull { value -> label.getHeight(this, valueFormatter.formatValue(value, chartValues)) } + .maxOfOrNull { value -> + label.getHeight( + context = this, + text = valueFormatter.formatValue(value, chartValues), + rotationDegrees = labelRotationDegrees, + ) + } }.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)) } + .maxOfOrNull { value -> + label.getWidth( + context = this, + text = valueFormatter.formatValue(value, chartValues), + rotationDegrees = labelRotationDegrees, + ) + } }.orZero private fun ChartDrawContext.getLineCanvasYCorrection(thickness: Float, y: Float): Float { From aec4bdc4732d9f6e2bab9f167a84cb29eca298e1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 17:21:49 +0000 Subject: [PATCH 31/97] Update kotlin monorepo to v2 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 74322c895..12762d3e0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,7 +12,7 @@ detekt = "1.23.6" dokka = "1.9.20" jUnit = "4.13.2" jUnitExt = "1.1.5" -kotlin = "1.9.22" +kotlin = "2.0.0" lifecycle = "2.8.1" material = "1.12.0" mockK = "1.13.11" From 87e2e1785c3c82b0524126cd8751cdd26f03394c Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Tue, 21 May 2024 18:28:38 +0200 Subject: [PATCH 32/97] Update Compose Compiler --- build.gradle.kts | 3 ++- gradle/libs.versions.toml | 2 +- sample/build.gradle | 9 +++------ vico/compose-m2/build.gradle | 7 ++----- vico/compose-m3/build.gradle | 7 ++----- vico/compose/build.gradle | 7 ++----- 6 files changed, 12 insertions(+), 23 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index eeb60f332..659bebb99 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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,6 +25,7 @@ buildscript { } plugins { + alias(libs.plugins.composeCompiler) apply false alias(libs.plugins.detekt) alias(libs.plugins.dokka) apply false } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 12762d3e0..9797be9ec 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,6 @@ androidXAnnotation = "1.8.0" androidXCore = "1.13.1" appcompat = "1.7.0" composeBom = "2024.05.00" -composeCompiler = "1.5.8" coroutines = "1.8.1" detekt = "1.23.6" dokka = "1.9.20" @@ -48,5 +47,6 @@ testCore = { group = "androidx.test", name = "core-ktx", version.ref = "testCore viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" } [plugins] +composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } diff --git a/sample/build.gradle b/sample/build.gradle index 679895904..6efff97b5 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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. @@ -15,9 +15,10 @@ */ plugins { + id "app.cash.paparazzi" id "com.android.application" id "kotlin-android" - id "app.cash.paparazzi" + id "org.jetbrains.kotlin.plugin.compose" } android { @@ -50,10 +51,6 @@ android { jvmTarget = JavaVersion.VERSION_1_8.toString() } - composeOptions { - kotlinCompilerExtensionVersion libs.versions.composeCompiler.get() - } - namespace "com.patrykandpatrick.vico" } diff --git a/vico/compose-m2/build.gradle b/vico/compose-m2/build.gradle index e7ad11851..7fda3e724 100644 --- a/vico/compose-m2/build.gradle +++ b/vico/compose-m2/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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,6 +18,7 @@ plugins { id "com.android.library" id "kotlin-android" + id "org.jetbrains.kotlin.plugin.compose" } apply from: "$rootDir/common-scripts.gradle" @@ -32,10 +33,6 @@ android { compose true } - composeOptions { - kotlinCompilerExtensionVersion libs.versions.composeCompiler.get() - } - namespace = getNamespace(project) group = library.groupId diff --git a/vico/compose-m3/build.gradle b/vico/compose-m3/build.gradle index a943d5173..bfeb96fff 100644 --- a/vico/compose-m3/build.gradle +++ b/vico/compose-m3/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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,6 +18,7 @@ plugins { id "com.android.library" id "kotlin-android" + id "org.jetbrains.kotlin.plugin.compose" } apply from: "$rootDir/common-scripts.gradle" @@ -32,10 +33,6 @@ android { compose true } - composeOptions { - kotlinCompilerExtensionVersion libs.versions.composeCompiler.get() - } - namespace = getNamespace(project) group = library.groupId diff --git a/vico/compose/build.gradle b/vico/compose/build.gradle index dc0b3c129..9741c6b84 100644 --- a/vico/compose/build.gradle +++ b/vico/compose/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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,6 +18,7 @@ plugins { id "com.android.library" id "kotlin-android" + id "org.jetbrains.kotlin.plugin.compose" } apply from: "$rootDir/common-scripts.gradle" @@ -32,10 +33,6 @@ android { compose true } - composeOptions { - kotlinCompilerExtensionVersion libs.versions.composeCompiler.get() - } - namespace = getNamespace(project) group = library.groupId From 095d05d21c47ddb2d47b66740a38a8a058573059 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Sat, 1 Jun 2024 18:39:26 +0200 Subject: [PATCH 33/97] Update `kotlinc.xml` --- .idea/kotlinc.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 5bf19d378..a79b310cf 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - From ed347e67f9cf4f9839f9a6e05e354f0f900e9d5a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 5 Jun 2024 08:57:47 +0000 Subject: [PATCH 34/97] Update dependency app.cash.paparazzi:paparazzi-gradle-plugin to v1.3.4 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9797be9ec..e61c7d8e8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ kotlin = "2.0.0" lifecycle = "2.8.1" material = "1.12.0" mockK = "1.13.11" -paparazziGradlePlugin = "1.3.1" +paparazziGradlePlugin = "1.3.4" testCore = "1.5.0" [libraries] From dc5352ecb278bffe7c418a3b7a464fa5ba5e0118 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Wed, 5 Jun 2024 16:04:53 +0200 Subject: [PATCH 35/97] Update Paparazzi setup and reference images --- .../com/patrykandpatrick/vico/sample/PaparazziTest.kt | 9 +++++++-- ...azziTest_Test default charts in NIGHT_columnchart.png | 4 ++-- ...t charts in NIGHT_columnchart_long_not_scrollable.png | 4 ++-- ...fault charts in NIGHT_columnchart_long_scrollable.png | 4 ++-- ...lumnchart_long_scrollable_with_initial_scroll_end.png | 4 ++-- ...arazziTest_Test default charts in NIGHT_linechart.png | 4 ++-- ...ult charts in NIGHT_linechart_long_not_scrollable.png | 4 ++-- ...default charts in NIGHT_linechart_long_scrollable.png | 4 ++-- ...linechart_long_scrollable_with_initial_scroll_end.png | 4 ++-- ...Test_Test default charts in NOT NIGHT_columnchart.png | 4 ++-- ...arts in NOT NIGHT_columnchart_long_not_scrollable.png | 4 ++-- ...t charts in NOT NIGHT_columnchart_long_scrollable.png | 4 ++-- ...lumnchart_long_scrollable_with_initial_scroll_end.png | 4 ++-- ...ziTest_Test default charts in NOT NIGHT_linechart.png | 4 ++-- ...charts in NOT NIGHT_linechart_long_not_scrollable.png | 4 ++-- ...ult charts in NOT NIGHT_linechart_long_scrollable.png | 4 ++-- ...linechart_long_scrollable_with_initial_scroll_end.png | 4 ++-- 17 files changed, 39 insertions(+), 34 deletions(-) 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..18121e19b 100644 --- a/sample/src/test/kotlin/com/patrykandpatrick/vico/sample/PaparazziTest.kt +++ b/sample/src/test/kotlin/com/patrykandpatrick/vico/sample/PaparazziTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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,6 +18,7 @@ package com.patrykandpatrick.vico.sample import androidx.compose.runtime.Composable import app.cash.paparazzi.Paparazzi +import com.android.ide.common.rendering.api.SessionParams import com.patrykandpatrick.vico.sample.paparazzi.lightConfig import com.patrykandpatrick.vico.sample.paparazzi.nightConfig import com.patrykandpatrick.vico.sample.previews.composables.column.DefaultColumnChart @@ -45,7 +46,11 @@ public class PaparazziTest { ) @get:Rule - public val paparazzi: Paparazzi = Paparazzi(deviceConfig = lightConfig) + public val paparazzi = Paparazzi( + deviceConfig = lightConfig, + renderingMode = SessionParams.RenderingMode.SHRINK, + maxPercentDifference = .2, + ) private fun List Unit>>.snapshotAll() { forEach { (name, composable) -> diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart.png index ecd6726c3..07fe63169 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f99772eebdc358be43ba37e27cdde42029b2fa56c1c39244e159c4a0a033481 -size 87121 +oid sha256:bfb2b9f52059dd484302933cfc52c4ba27b4e9f09a56e50618caa67cd90668df +size 21370 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart_long_not_scrollable.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart_long_not_scrollable.png index c5859b95d..b670cad7e 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart_long_not_scrollable.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart_long_not_scrollable.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1569faa59ad765fe2205a474cbf48e8add925142488ccf822a44365f047323a0 -size 106875 +oid sha256:024707d1fa82d7bdd8e5de10f628d8658e964aa85b7b0796ff6f1382a73fb482 +size 24551 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart_long_scrollable.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart_long_scrollable.png index c5859b95d..416d30244 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart_long_scrollable.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart_long_scrollable.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1569faa59ad765fe2205a474cbf48e8add925142488ccf822a44365f047323a0 -size 106875 +oid sha256:fc5c3b152b9d3ff8ef61d583f0b968595321e7e46bc319241aae0793b7e984a1 +size 21472 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart_long_scrollable_with_initial_scroll_end.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart_long_scrollable_with_initial_scroll_end.png index c5859b95d..50f8315cb 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart_long_scrollable_with_initial_scroll_end.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_columnchart_long_scrollable_with_initial_scroll_end.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1569faa59ad765fe2205a474cbf48e8add925142488ccf822a44365f047323a0 -size 106875 +oid sha256:3301e142e1f0a7b230e429fcecfd684b88f1b7097471fa55492caa1a37332527 +size 21661 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart.png index c0d44adbc..b21067020 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1983764174bb1fdcb5f440fcb56043b5fa703517ea49d9880a0d86bc0220e61a -size 110811 +oid sha256:ff6db54ef47c743d548c452a172c2745b0896bd63f313f34c22512a5823eee2d +size 29876 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart_long_not_scrollable.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart_long_not_scrollable.png index d6ce43e40..958589b0e 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart_long_not_scrollable.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart_long_not_scrollable.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20a9a746115244d4ffcc7cfb231a5de42b94d522003f3d0a6aa5addd4bc30b37 -size 153408 +oid sha256:b4a0f005f0a73b9f4cbc321ad0e0d8d558c6b498779a7c6b6a89725a92fc7838 +size 40898 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart_long_scrollable.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart_long_scrollable.png index d6ce43e40..152c1f6fd 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart_long_scrollable.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart_long_scrollable.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20a9a746115244d4ffcc7cfb231a5de42b94d522003f3d0a6aa5addd4bc30b37 -size 153408 +oid sha256:a400563ca805fa86ae385dcc3f32856da1f9bce1a7a1a94dee6ff3993f832610 +size 37067 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart_long_scrollable_with_initial_scroll_end.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart_long_scrollable_with_initial_scroll_end.png index d6ce43e40..2bee66892 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart_long_scrollable_with_initial_scroll_end.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NIGHT_linechart_long_scrollable_with_initial_scroll_end.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20a9a746115244d4ffcc7cfb231a5de42b94d522003f3d0a6aa5addd4bc30b37 -size 153408 +oid sha256:13aead3b30cf25950bc185169a42e5bd08733a934ac4dd478274542da17a3421 +size 37078 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart.png index 80f976113..867db59cf 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f28913e1d05e9e7b17431b06b4020b8811fd136e27100f25187e1a5c0c99c02d -size 80073 +oid sha256:a0f01a1596f289a8017749d8498cde428be42c8a7f5db880f8c44fa1c0d90920 +size 21395 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart_long_not_scrollable.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart_long_not_scrollable.png index fe5d4cee3..697b4858c 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart_long_not_scrollable.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart_long_not_scrollable.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:350f5b976bb4aeb5a3f354423e7465afea05a1d4e403406f53574de10dfebd54 -size 97131 +oid sha256:8b5acf3b39b5fa6c387d79623adb420423671e07f2359ec27a9f359f8f2c9ab9 +size 24587 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart_long_scrollable.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart_long_scrollable.png index fe5d4cee3..95c03ac9d 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart_long_scrollable.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart_long_scrollable.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:350f5b976bb4aeb5a3f354423e7465afea05a1d4e403406f53574de10dfebd54 -size 97131 +oid sha256:6e6d42376f489a307964d7d465ba0fc972fbba9ba09d1b023a6ad6e97aa9d0be +size 21348 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart_long_scrollable_with_initial_scroll_end.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart_long_scrollable_with_initial_scroll_end.png index fe5d4cee3..56c4a2d76 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart_long_scrollable_with_initial_scroll_end.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_columnchart_long_scrollable_with_initial_scroll_end.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:350f5b976bb4aeb5a3f354423e7465afea05a1d4e403406f53574de10dfebd54 -size 97131 +oid sha256:d6ad517973b22c12aaf2cdfbb7dccaae04a110d2c451796fb3124ff1fdd5e4d4 +size 21565 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart.png index 9ea1dce0c..bde6e66fb 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3847e8b30f494ba84100b58848cbb0e5c7a130d343b6397884d1542b9d000faf -size 95928 +oid sha256:711bc9ee703e40df440dcceb1dda589f1237a971bed213f074255b97e1570b6b +size 27928 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart_long_not_scrollable.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart_long_not_scrollable.png index 6249d11a8..fbdd69001 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart_long_not_scrollable.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart_long_not_scrollable.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15b6ed782073a30065b585166a259470e3fbf129812fdccf750b63c48ce5089c -size 127565 +oid sha256:b86951080fcbb601628f0c424ca67793ff0273ce28ac77040a8bfcd85bf93bf6 +size 37209 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart_long_scrollable.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart_long_scrollable.png index 6249d11a8..64ea190b7 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart_long_scrollable.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart_long_scrollable.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15b6ed782073a30065b585166a259470e3fbf129812fdccf750b63c48ce5089c -size 127565 +oid sha256:74084911ac598f79110690907bfea96bd5b6f9dc74563e5d7ee21278df3ae70e +size 34134 diff --git a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart_long_scrollable_with_initial_scroll_end.png b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart_long_scrollable_with_initial_scroll_end.png index 6249d11a8..69673ac1d 100644 --- a/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart_long_scrollable_with_initial_scroll_end.png +++ b/sample/src/test/snapshots/images/com.patrykandpatrick.vico.sample_PaparazziTest_Test default charts in NOT NIGHT_linechart_long_scrollable_with_initial_scroll_end.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15b6ed782073a30065b585166a259470e3fbf129812fdccf750b63c48ce5089c -size 127565 +oid sha256:4f4ac95db65353528ab9b3b4f2daaf6e0a7bc780829e9134d95e03ef95354c96 +size 34053 From d120b2f312aa2299b2bbda1a2cd6e52be45367bc Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Wed, 5 Jun 2024 16:11:30 +0200 Subject: [PATCH 36/97] Update `version_name` --- versions.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.gradle b/versions.gradle index 13bc52f8f..92726070d 100644 --- a/versions.gradle +++ b/versions.gradle @@ -18,7 +18,7 @@ ext { library = [ groupId : "com.patrykandpatrick.vico", - version_name : "1.15.0-alpha.1", + version_name : "1.15.0-alpha.2", version_code : 1, target_sdk : 34, min_sdk : 16, From ac0804fed9d780e852cc784bdb7870b3b6594fe4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 18:33:04 +0000 Subject: [PATCH 37/97] Update dependency com.android.tools.build:gradle to v8.4.2 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 746e7d981..4c2148bbf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] accompanist = "0.34.0" activity = "1.9.0" -agp = "8.4.1" +agp = "8.4.2" androidXAnnotation = "1.8.0" androidXCore = "1.13.1" appcompat = "1.7.0" From 2a009113c63062092847710cc7be156ccead23c9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 18:33:07 +0000 Subject: [PATCH 38/97] Update dependency com.android.tools.build:gradle to v8.4.2 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e61c7d8e8..418bdc834 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] accompanist = "0.34.0" activity = "1.9.0" -agp = "8.4.1" +agp = "8.4.2" androidXAnnotation = "1.8.0" androidXCore = "1.13.1" appcompat = "1.7.0" From cc735d112ebb39ed1f10bb80db5b9883b15a2335 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 17:40:00 +0000 Subject: [PATCH 39/97] Update lifecycle to v2.8.2 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4c2148bbf..0267ac784 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,7 +13,7 @@ jUnit = "4.13.2" jUnitExt = "1.1.5" jupiter = "5.10.2" kotlin = "2.0.0" -lifecycle = "2.8.1" +lifecycle = "2.8.2" material = "1.12.0" mockK = "1.13.11" paparazziGradlePlugin = "1.3.4" From c31c3d5dcc57fc65e3dce42f50cc94772b087653 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 19:28:24 +0000 Subject: [PATCH 40/97] Update dependency androidx.compose:compose-bom to v2024.06.00 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0267ac784..80fded736 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ agp = "8.4.2" androidXAnnotation = "1.8.0" androidXCore = "1.13.1" appcompat = "1.7.0" -composeBom = "2024.05.00" +composeBom = "2024.06.00" composeNavigation = "2.7.7" coroutines = "1.8.1" dokka = "1.9.20" From 566121b0dc0fe1ff8863d7de873eb71d918c00cc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 19:28:28 +0000 Subject: [PATCH 41/97] Update lifecycle to v2.8.2 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 418bdc834..667848369 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,7 +12,7 @@ dokka = "1.9.20" jUnit = "4.13.2" jUnitExt = "1.1.5" kotlin = "2.0.0" -lifecycle = "2.8.1" +lifecycle = "2.8.2" material = "1.12.0" mockK = "1.13.11" paparazziGradlePlugin = "1.3.4" From 3b33640d150fc8a637bd68acbaa091c777dd2c54 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Jun 2024 05:03:50 +0000 Subject: [PATCH 42/97] Update dependency androidx.compose:compose-bom to v2024.06.00 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 667848369..24f23c754 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ agp = "8.4.2" androidXAnnotation = "1.8.0" androidXCore = "1.13.1" appcompat = "1.7.0" -composeBom = "2024.05.00" +composeBom = "2024.06.00" coroutines = "1.8.1" detekt = "1.23.6" dokka = "1.9.20" From b387caadcc26a1187408b9319179a8727a4e8eee Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Jun 2024 18:03:07 +0000 Subject: [PATCH 43/97] Update dependency com.android.tools.build:gradle to v8.5.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 80fded736..162340bd9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] accompanist = "0.34.0" activity = "1.9.0" -agp = "8.4.2" +agp = "8.5.0" androidXAnnotation = "1.8.0" androidXCore = "1.13.1" appcompat = "1.7.0" From 2321b71a6d018c1ea5d71d6f495815061515fc74 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Jun 2024 18:03:11 +0000 Subject: [PATCH 44/97] Update dependency com.android.tools.build:gradle to v8.5.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 24f23c754..e1e21dc8c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] accompanist = "0.34.0" activity = "1.9.0" -agp = "8.4.2" +agp = "8.5.0" androidXAnnotation = "1.8.0" androidXCore = "1.13.1" appcompat = "1.7.0" From b9addcecb368740706a5f9106747b133d47ef6e1 Mon Sep 17 00:00:00 2001 From: Patryk Goworowski Date: Wed, 19 Jun 2024 19:12:52 +0200 Subject: [PATCH 45/97] Change the constant `MAX_LABEL_WIDTH_KEY` in `VerticalAxis` to an instance-dependent key Co-authored-by: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> --- .../vico/core/axis/vertical/VerticalAxis.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 2d51d9cf4..29813d026 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 @@ -45,7 +45,6 @@ import com.patrykandpatrick.vico.core.extension.translate import com.patrykandpatrick.vico.core.throwable.UnknownAxisPositionException private const val TITLE_ABS_ROTATION_DEGREES = 90f -private const val MAX_LABEL_WIDTH_KEY = "maxLabelWidthKey" /** * An implementation of [AxisRenderer] used for vertical axes. This class extends [Axis]. @@ -64,6 +63,8 @@ public class VerticalAxis( private val textHorizontalPosition: HorizontalPosition get() = if (areLabelsOutsideAtStartOrInsideAtEnd) HorizontalPosition.Start else HorizontalPosition.End + private val maxLabelWidthKey = System.identityHashCode(this) + /** * The maximum label count. */ @@ -221,7 +222,7 @@ public class VerticalAxis( horizontalPosition = textHorizontalPosition, verticalPosition = verticalLabelPosition.textPosition, rotationDegrees = labelRotationDegrees, - maxTextWidth = getExtraOr(MAX_LABEL_WIDTH_KEY) { chartBounds.width().half - tickLength }.toInt(), + maxTextWidth = getExtraOr(maxLabelWidthKey) { chartBounds.width().half - tickLength }.toInt(), ) } } @@ -280,7 +281,7 @@ public class VerticalAxis( val labelSpace = when (horizontalLabelPosition) { Outside -> { val maxLabelWidth = getMaxLabelWidth(height).ceil - putExtra(MAX_LABEL_WIDTH_KEY, maxLabelWidth) + putExtra(maxLabelWidthKey, maxLabelWidth) maxLabelWidth + tickLength } From 2c103d1456fbc1fd5d60f9545acfba56a94d2519 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Wed, 19 Jun 2024 19:47:46 +0200 Subject: [PATCH 46/97] Update `version_name` --- versions.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.gradle b/versions.gradle index 92726070d..3c3a2c372 100644 --- a/versions.gradle +++ b/versions.gradle @@ -18,7 +18,7 @@ ext { library = [ groupId : "com.patrykandpatrick.vico", - version_name : "1.15.0-alpha.2", + version_name : "1.15.0-beta.1", version_code : 1, target_sdk : 34, min_sdk : 16, From ee5c73696754eb631494f026f90a68174f8c4745 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Thu, 20 Jun 2024 17:17:12 +0200 Subject: [PATCH 47/97] Make publishing-related updates --- .github/workflows/release-update.yml | 7 ++++--- common-scripts.gradle | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release-update.yml b/.github/workflows/release-update.yml index 217c066f2..142f9d3b6 100644 --- a/.github/workflows/release-update.yml +++ b/.github/workflows/release-update.yml @@ -53,7 +53,8 @@ jobs: env: ORG_GRADLE_PROJECT_GPG_KEY: ${{ secrets.GPG_KEY }} ORG_GRADLE_PROJECT_GPG_KEY_PASSWORD: ${{ secrets.GPG_KEY_PASSWORD }} - ORG_GRADLE_PROJECT_OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + ORG_GRADLE_PROJECT_OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }} + ORG_GRADLE_PROJECT_OSSRH_TOKEN_PASSWORD: ${{ secrets.OSSRH_TOKEN_PASSWORD }} - run: | cd "${{ github.workspace }}/vico" ./gradlew clean @@ -64,9 +65,9 @@ jobs: find . -not -path "*/.*" -type f | while read path; do sed -i "s/com\.patrykandpatrick/com\.patrykandpatryk/" "$path"; done find . -not -path "*/.*" -type f | while read path; do sed -i "s/vico\.views/vico\.view/" "$path"; done find -type f \( -name "*.gradle" -o -name "*.gradle.kts" \) -exec sed -i s/vico:views/vico:view/ "{}" + - sed -i "s/\(username\s*=\s*\)\"patrykandpatrick\"/\1\"patrykandpatryk\"/" common-scripts.gradle ./gradlew publish env: ORG_GRADLE_PROJECT_GPG_KEY: ${{ secrets.LEGACY_GPG_KEY }} ORG_GRADLE_PROJECT_GPG_KEY_PASSWORD: ${{ secrets.LEGACY_GPG_KEY_PASSWORD }} - ORG_GRADLE_PROJECT_OSSRH_PASSWORD: ${{ secrets.LEGACY_OSSRH_PASSWORD }} + ORG_GRADLE_PROJECT_OSSRH_TOKEN: ${{ secrets.LEGACY_OSSRH_TOKEN }} + ORG_GRADLE_PROJECT_OSSRH_TOKEN_PASSWORD: ${{ secrets.LEGACY_OSSRH_TOKEN_PASSWORD }} diff --git a/common-scripts.gradle b/common-scripts.gradle index 08fdb90a6..722601112 100644 --- a/common-scripts.gradle +++ b/common-scripts.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2022 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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. @@ -109,8 +109,8 @@ void setUpRepositories(RepositoryHandler handler) { url = isReleaseVersion() ? releaseUrl : snapshotUrl credentials { - username = "patrykandpatrick" - password = findProperty("OSSRH_PASSWORD") + username = findProperty("OSSRH_TOKEN") + password = findProperty("OSSRH_TOKEN_PASSWORD") } } } From b154c7cdb4478472f39875d6760dab4660f6d258 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Thu, 20 Jun 2024 17:17:12 +0200 Subject: [PATCH 48/97] Make publishing-related updates --- .github/workflows/release-update.yml | 3 ++- common-scripts.gradle | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-update.yml b/.github/workflows/release-update.yml index 350e3dd2a..96ecc76ce 100644 --- a/.github/workflows/release-update.yml +++ b/.github/workflows/release-update.yml @@ -53,4 +53,5 @@ jobs: env: ORG_GRADLE_PROJECT_GPG_KEY: ${{ secrets.GPG_KEY }} ORG_GRADLE_PROJECT_GPG_KEY_PASSWORD: ${{ secrets.GPG_KEY_PASSWORD }} - ORG_GRADLE_PROJECT_OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + ORG_GRADLE_PROJECT_OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }} + ORG_GRADLE_PROJECT_OSSRH_TOKEN_PASSWORD: ${{ secrets.OSSRH_TOKEN_PASSWORD }} diff --git a/common-scripts.gradle b/common-scripts.gradle index 08fdb90a6..722601112 100644 --- a/common-scripts.gradle +++ b/common-scripts.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2022 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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. @@ -109,8 +109,8 @@ void setUpRepositories(RepositoryHandler handler) { url = isReleaseVersion() ? releaseUrl : snapshotUrl credentials { - username = "patrykandpatrick" - password = findProperty("OSSRH_PASSWORD") + username = findProperty("OSSRH_TOKEN") + password = findProperty("OSSRH_TOKEN_PASSWORD") } } } From ff6f2ed3d3053ab8256923e16bc7ffd790cfe56e Mon Sep 17 00:00:00 2001 From: Patryk Goworowski Date: Fri, 21 Jun 2024 18:47:46 +0200 Subject: [PATCH 49/97] Support nested scrolling in the `compose` module --- .../vico/compose/cartesian/VicoScrollState.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/VicoScrollState.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/VicoScrollState.kt index 5ddd8deee..7aab96984 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/VicoScrollState.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/VicoScrollState.kt @@ -63,8 +63,12 @@ public class VicoScrollState { val oldValue = value value += delta val consumedValue = value - oldValue - if (consumedValue != delta) pointerXDeltas.tryEmit(consumedValue - delta) - delta + if (oldValue + delta == value) { + delta + } else { + pointerXDeltas.tryEmit(consumedValue - delta) + consumedValue + } } /** The current scroll value (in pixels). */ From b931d963fc7ac3adcd23f70b59dda6d6e0350179 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Sun, 23 Jun 2024 10:22:19 +0200 Subject: [PATCH 50/97] Update `version_name` --- versions.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/versions.gradle b/versions.gradle index 3c3a2c372..9bdef3adc 100644 --- a/versions.gradle +++ b/versions.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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,7 @@ ext { library = [ groupId : "com.patrykandpatrick.vico", - version_name : "1.15.0-beta.1", + version_name : "1.15.0", version_code : 1, target_sdk : 34, min_sdk : 16, From a93978b9f04c8a9de88b7c5797ad340e5b0ab675 Mon Sep 17 00:00:00 2001 From: Patryk Goworowski Date: Mon, 24 Jun 2024 21:44:51 +0200 Subject: [PATCH 51/97] Deprecate `rememberAxisTickComponent` that takes a Compose `Shape` Co-authored-by: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> --- .../vico/compose/cartesian/axis/AxisComponents.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/axis/AxisComponents.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/axis/AxisComponents.kt index e83f03b4b..49a9415ce 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/axis/AxisComponents.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/axis/AxisComponents.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:Suppress("DeprecatedCallableAddReplaceWith") + package com.patrykandpatrick.vico.compose.cartesian.axis import android.graphics.Typeface @@ -153,6 +155,12 @@ public fun rememberAxisTickComponent( * @param strokeColor the stroke color. * @param brush an optional [Brush] to apply to the line. */ +@Deprecated( + message = + "Use the `rememberAxisTickComponent` overload that takes a " + + "`com.patrykandpatrick.vico.core.component.shape.Shape`. " + + "Convert the Compose shape using `androidx.compose.ui.graphics.Shape.toVicoShape()`." +) @Composable public fun rememberAxisTickComponent( color: Color, From deaa7019d50d6618b4a4376ddb1185e595e4d546 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 21:21:07 +0000 Subject: [PATCH 52/97] Update dependency androidx.test:core-ktx to v1.6.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 162340bd9..d7d266a22 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ lifecycle = "2.8.2" material = "1.12.0" mockK = "1.13.11" paparazziGradlePlugin = "1.3.4" -testCore = "1.5.0" +testCore = "1.6.0" [libraries] activityCompose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity" } From d48f11530de3105b53faf9530192d5faa9959ef3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2024 01:12:19 +0000 Subject: [PATCH 53/97] Update dependency androidx.test:core-ktx to v1.6.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d7d266a22..a01a71841 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ lifecycle = "2.8.2" material = "1.12.0" mockK = "1.13.11" paparazziGradlePlugin = "1.3.4" -testCore = "1.6.0" +testCore = "1.6.1" [libraries] activityCompose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity" } From 8e6b826ed99fd7aafa5a406b14f7746d0f8defd8 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:20:34 +0200 Subject: [PATCH 54/97] Rename selected XML attributes --- sample/src/main/res/values/chart_3_styles.xml | 6 +++--- sample/src/main/res/values/chart_9_styles.xml | 2 +- .../vico/views/common/theme/ShapeStyle.kt | 17 ++++++++++++----- .../views/common/theme/TextComponentStyle.kt | 5 ++++- .../vico/views/common/theme/ThemeHandler.kt | 13 +++++++++++-- vico/views/src/main/res/values/attrs.xml | 8 ++++++++ 6 files changed, 39 insertions(+), 12 deletions(-) diff --git a/sample/src/main/res/values/chart_3_styles.xml b/sample/src/main/res/values/chart_3_styles.xml index ea172ec95..7ec773570 100644 --- a/sample/src/main/res/values/chart_3_styles.xml +++ b/sample/src/main/res/values/chart_3_styles.xml @@ -1,5 +1,5 @@ + + @@ -115,6 +119,8 @@ + + @@ -184,6 +190,8 @@ + + From fea78801421a5bb2a9c627f386c6b4cdd808d87c Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:54:39 +0200 Subject: [PATCH 55/97] Update `rememberHorizontalBox` and `rememberHorizontalLine` documentation --- .../vico/compose/cartesian/decoration/HorizontalBox.kt | 2 +- .../vico/compose/cartesian/decoration/HorizontalLine.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/decoration/HorizontalBox.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/decoration/HorizontalBox.kt index ce01d3014..bb134fba2 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/decoration/HorizontalBox.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/decoration/HorizontalBox.kt @@ -26,7 +26,7 @@ import com.patrykandpatrick.vico.core.common.component.ShapeComponent import com.patrykandpatrick.vico.core.common.component.TextComponent import com.patrykandpatrick.vico.core.common.data.ExtraStore -/** Creates and remembers a [HorizontalBox] */ +/** Creates and remembers a [HorizontalBox]. */ @Composable public fun rememberHorizontalBox( y: (ExtraStore) -> ClosedFloatingPointRange, diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/decoration/HorizontalLine.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/decoration/HorizontalLine.kt index 1e46ee407..df721e984 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/decoration/HorizontalLine.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/decoration/HorizontalLine.kt @@ -26,7 +26,7 @@ import com.patrykandpatrick.vico.core.common.component.LineComponent import com.patrykandpatrick.vico.core.common.component.TextComponent import com.patrykandpatrick.vico.core.common.data.ExtraStore -/** Creates and remembers a [HorizontalLine] */ +/** Creates and remembers a [HorizontalLine]. */ @Composable public fun rememberHorizontalLine( y: (ExtraStore) -> Float, From 785a88e1604c8916d9fa911b0bdf1d1676b0bffc Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:56:46 +0200 Subject: [PATCH 56/97] `CartesianChartModelProducer`: Make constructor public, and deprecate `build` --- .../patrykandpatrick/vico/sample/showcase/charts/Chart1.kt | 2 +- .../vico/sample/showcase/charts/Chart10.kt | 2 +- .../patrykandpatrick/vico/sample/showcase/charts/Chart2.kt | 2 +- .../patrykandpatrick/vico/sample/showcase/charts/Chart3.kt | 2 +- .../patrykandpatrick/vico/sample/showcase/charts/Chart4.kt | 2 +- .../patrykandpatrick/vico/sample/showcase/charts/Chart5.kt | 2 +- .../patrykandpatrick/vico/sample/showcase/charts/Chart6.kt | 2 +- .../patrykandpatrick/vico/sample/showcase/charts/Chart7.kt | 2 +- .../patrykandpatrick/vico/sample/showcase/charts/Chart8.kt | 2 +- .../patrykandpatrick/vico/sample/showcase/charts/Chart9.kt | 2 +- .../core/cartesian/data/CartesianChartModelProducer.kt | 7 ++++++- 11 files changed, 16 insertions(+), 11 deletions(-) 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 8720b0d5c..25e998307 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 @@ -43,7 +43,7 @@ import kotlinx.coroutines.withContext @Composable internal fun Chart1(uiFramework: UIFramework, modifier: Modifier) { - val modelProducer = remember { CartesianChartModelProducer.build() } + val modelProducer = remember { CartesianChartModelProducer() } LaunchedEffect(Unit) { withContext(Dispatchers.Default) { modelProducer.tryRunTransaction { diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart10.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart10.kt index 09cfa4b25..b942d58fe 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart10.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart10.kt @@ -42,7 +42,7 @@ import kotlinx.coroutines.withContext @Composable internal fun Chart10(uiFramework: UIFramework, modifier: Modifier) { - val modelProducer = remember { CartesianChartModelProducer.build() } + val modelProducer = remember { CartesianChartModelProducer() } LaunchedEffect(key1 = Unit) { withContext(Dispatchers.Default) { while (isActive) { 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 e552d0df4..2d0cb379d 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 @@ -62,7 +62,7 @@ import kotlinx.coroutines.withContext @Composable internal fun Chart2(uiFramework: UIFramework, modifier: Modifier) { - val modelProducer = remember { CartesianChartModelProducer.build() } + val modelProducer = remember { CartesianChartModelProducer() } LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { 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 290fc2cf4..0263e8724 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 @@ -61,7 +61,7 @@ import kotlinx.coroutines.withContext @Composable internal fun Chart3(uiFramework: UIFramework, modifier: Modifier) { - val modelProducer = remember { CartesianChartModelProducer.build() } + val modelProducer = remember { CartesianChartModelProducer() } LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { 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 d6fe313c9..167b8a9df 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 @@ -53,7 +53,7 @@ import kotlinx.coroutines.withContext @Composable internal fun Chart4(uiFramework: UIFramework, modifier: Modifier) { - val modelProducer = remember { CartesianChartModelProducer.build() } + val modelProducer = remember { CartesianChartModelProducer() } LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { 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 b76b1253b..d37bedb72 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 @@ -48,7 +48,7 @@ import kotlinx.coroutines.withContext @Composable internal fun Chart5(uiFramework: UIFramework, modifier: Modifier) { - val modelProducer = remember { CartesianChartModelProducer.build() } + val modelProducer = remember { CartesianChartModelProducer() } LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { 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 61310f128..144c9a987 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 @@ -59,7 +59,7 @@ import kotlinx.coroutines.withContext @Composable internal fun Chart6(uiFramework: UIFramework, modifier: Modifier) { - val modelProducer = remember { CartesianChartModelProducer.build() } + val modelProducer = remember { CartesianChartModelProducer() } LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { 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 d682dd21b..f527d33aa 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 @@ -63,7 +63,7 @@ import kotlinx.coroutines.withContext @Composable internal fun Chart7(uiFramework: UIFramework, modifier: Modifier) { - val modelProducer = remember { CartesianChartModelProducer.build() } + val modelProducer = remember { CartesianChartModelProducer() } LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { 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 edc808c7a..8e7d2f40f 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 @@ -55,7 +55,7 @@ import kotlinx.coroutines.withContext @Composable internal fun Chart8(uiFramework: UIFramework, modifier: Modifier) { - val modelProducer = remember { CartesianChartModelProducer.build() } + val modelProducer = remember { CartesianChartModelProducer() } LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { 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 b38eb0d70..08dd2ee93 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 @@ -67,7 +67,7 @@ import kotlinx.coroutines.withContext @Composable internal fun Chart9(uiFramework: UIFramework, modifier: Modifier) { - val modelProducer = remember { CartesianChartModelProducer.build() } + val modelProducer = remember { CartesianChartModelProducer() } LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt index 981ecbb31..797ee295f 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt @@ -33,7 +33,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex /** Creates [CartesianChartModel]s and handles difference animations. */ -public class CartesianChartModelProducer private constructor(dispatcher: CoroutineDispatcher) { +public class CartesianChartModelProducer(dispatcher: CoroutineDispatcher = Dispatchers.Default) { private var partials = emptyList() private var extraStore = MutableExtraStore() private var cachedModel: CartesianChartModel? = null @@ -230,6 +230,11 @@ public class CartesianChartModelProducer private constructor(dispatcher: Corouti * Creates a [CartesianChartModelProducer], running an initial [Transaction]. [dispatcher] is * the [CoroutineDispatcher] to be used for update handling. */ + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated( + "To create a `CartesianChartModelProducer`, use the constructor. To run an initial " + + "`Transaction`, use `tryRunTransaction` immediately after the constructor invocation." + ) public fun build( dispatcher: CoroutineDispatcher = Dispatchers.Default, transaction: (Transaction.() -> Unit)? = null, From 5a44afd6f29d22370062836ede381b7e47d2c93d Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Thu, 27 Jun 2024 16:08:57 +0200 Subject: [PATCH 57/97] Remove `FULL_DEGREES` --- .../java/com/patrykandpatrick/vico/core/common/Constants.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Constants.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Constants.kt index de7a63722..d925213a0 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Constants.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Constants.kt @@ -16,8 +16,6 @@ package com.patrykandpatrick.vico.core.common -internal const val FULL_DEGREES: Float = 360f - internal const val ERR_REPEATING_COLLECTION_EMPTY = "Cannot get repeated item from empty collection." From c0d0b4a2f0f00234d6098773c4d328d64c4f77aa Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Thu, 27 Jun 2024 16:09:14 +0200 Subject: [PATCH 58/97] Restrict `NEW_PRODUCER_ERROR_MESSAGE` visibility --- .../com/patrykandpatrick/vico/core/common/Constants.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Constants.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Constants.kt index d925213a0..47f8d4a70 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Constants.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Constants.kt @@ -16,11 +16,14 @@ package com.patrykandpatrick.vico.core.common +import androidx.annotation.RestrictTo + internal const val ERR_REPEATING_COLLECTION_EMPTY = "Cannot get repeated item from empty collection." -internal const val ELLIPSIS: String = "…" +internal const val ELLIPSIS = "…" +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public const val NEW_PRODUCER_ERROR_MESSAGE: String = - "A new `CartesianChartModelProducer` was provided. Run data " + - "updates via `tryRunTransaction` or `runTransaction`, not by creating new `CartesianChartModelProducer`s." + "A new `CartesianChartModelProducer` was provided. Run data updates via `tryRunTransaction` or " + + "`runTransaction`, not by creating new `CartesianChartModelProducer`s." From 2b3cd4e5b404eb24ab925bc17757c46383c88caa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:32:34 +0000 Subject: [PATCH 59/97] Update junit5 monorepo to v5.10.3 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a01a71841..af636ed21 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ coroutines = "1.8.1" dokka = "1.9.20" jUnit = "4.13.2" jUnitExt = "1.1.5" -jupiter = "5.10.2" +jupiter = "5.10.3" kotlin = "2.0.0" lifecycle = "2.8.2" material = "1.12.0" From ccd73186e936c4a982a13bf4bc1ed81dbb712f37 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:19:10 +0200 Subject: [PATCH 60/97] `CartesianChartModelProducer.Transaction`: Rename `updateExtras` to `extras` --- .../cartesian/data/CartesianChartModelProducer.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt index 797ee295f..1e961f443 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt @@ -188,10 +188,19 @@ public class CartesianChartModelProducer(dispatcher: CoroutineDispatcher = Dispa * Allows for adding auxiliary values, which can later be retrieved via * [CartesianChartModel.extraStore]. */ - public fun updateExtras(block: (MutableExtraStore) -> Unit) { + public fun extras(block: (MutableExtraStore) -> Unit) { block(newExtraStore) } + /** + * Allows for adding auxiliary values, which can later be retrieved via + * [CartesianChartModel.extraStore]. + */ + @Deprecated("Use `extras`.", ReplaceWith("extras(block)")) + public fun updateExtras(block: (MutableExtraStore) -> Unit) { + extras(block) + } + /** * 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 From b8c180238895f4b1192dbdbcb99e565225af9c39 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:45:13 +0200 Subject: [PATCH 61/97] Make scroll- and zoom-related updates --- .../vico/compose/cartesian/CartesianChartHost.kt | 3 ++- .../vico/compose/cartesian/MeasureContext.kt | 9 +++++++-- .../vico/compose/cartesian/VicoScrollState.kt | 2 +- .../vico/compose/cartesian/VicoZoomState.kt | 2 +- .../vico/core/cartesian/CartesianDrawContext.kt | 15 ++++++++++----- .../core/cartesian/CartesianMeasureContext.kt | 5 ++++- .../vico/core/cartesian/FadingEdges.kt | 9 ++++----- .../cartesian/MutableCartesianMeasureContext.kt | 5 +++-- .../vico/core/cartesian/axis/HorizontalAxis.kt | 6 ++---- .../cartesian/layer/CandlestickCartesianLayer.kt | 2 +- .../core/cartesian/layer/ColumnCartesianLayer.kt | 2 +- .../core/cartesian/layer/LineCartesianLayer.kt | 3 +-- .../vico/views/cartesian/CartesianChartView.kt | 10 +++++++--- .../vico/views/cartesian/ScrollHandler.kt | 2 +- .../vico/views/cartesian/ZoomHandler.kt | 2 +- 15 files changed, 46 insertions(+), 31 deletions(-) diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/CartesianChartHost.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/CartesianChartHost.kt index 28042b9ed..723a85601 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/CartesianChartHost.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/CartesianChartHost.kt @@ -205,6 +205,7 @@ internal fun CartesianChartHostImpl( val measureContext = rememberCartesianMeasureContext( scrollState.scrollEnabled, + zoomState.zoomEnabled, bounds, horizontalLayout, with(LocalContext.current) { ::spToPx }, @@ -273,7 +274,7 @@ internal fun CartesianChartHostImpl( markerTouchPoint = markerTouchPoint.value, horizontalDimensions = horizontalDimensions, chartBounds = chart.bounds, - horizontalScroll = scrollState.value, + scroll = scrollState.value, zoom = zoomState.value, ) diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/MeasureContext.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/MeasureContext.kt index 752d477e4..a5fb98c7f 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/MeasureContext.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/MeasureContext.kt @@ -29,6 +29,7 @@ import com.patrykandpatrick.vico.core.cartesian.data.ChartValues @Composable internal fun rememberCartesianMeasureContext( scrollEnabled: Boolean, + zoomEnabled: Boolean, canvasBounds: RectF, horizontalLayout: HorizontalLayout, spToPx: (Float) -> Float, @@ -39,15 +40,19 @@ internal fun rememberCartesianMeasureContext( canvasBounds = canvasBounds, density = 0f, isLtr = true, - spToPx = spToPx, + scrollEnabled = scrollEnabled, + zoomEnabled = zoomEnabled, + horizontalLayout = horizontalLayout, chartValues = chartValues, + spToPx = spToPx, ) } .apply { this.density = LocalDensity.current.density this.isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr this.scrollEnabled = scrollEnabled + this.zoomEnabled = zoomEnabled this.horizontalLayout = horizontalLayout - this.spToPx = spToPx this.chartValues = chartValues + this.spToPx = spToPx } diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/VicoScrollState.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/VicoScrollState.kt index 7aab96984..2f182c312 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/VicoScrollState.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/VicoScrollState.kt @@ -111,7 +111,7 @@ public class VicoScrollState { * Houses information on a [CartesianChart]’s scroll value. Allows for scroll customization and * programmatic scrolling. * - * @param scrollEnabled whether scrolling is enabled. + * @param scrollEnabled whether scroll is enabled. * @param initialScroll represents the initial scroll value. * @param autoScroll represents the scroll value or delta for automatic scrolling. * @param autoScrollCondition defines when an automatic scroll should occur. diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/VicoZoomState.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/VicoZoomState.kt index b240f7319..1718cedf5 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/VicoZoomState.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/VicoZoomState.kt @@ -77,7 +77,7 @@ public class VicoZoomState { /** * Houses information on a [CartesianChart]’s zoom factor. Allows for zoom customization. * - * @param zoomEnabled whether zooming is enabled. + * @param zoomEnabled whether zoom is enabled. * @param initialZoom represents the initial zoom factor. * @param minZoom represents the minimum zoom factor. * @param maxZoom represents the maximum zoom factor. diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianDrawContext.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianDrawContext.kt index 7b629d936..0d2bce1e7 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianDrawContext.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianDrawContext.kt @@ -37,8 +37,13 @@ public interface CartesianDrawContext : DrawContext, CartesianMeasureContext { /** The point inside the chart’s coordinates where physical touch is occurring. */ public val markerTouchPoint: Point? - /** The current amount of horizontal scroll. */ + /** The scroll value (in pixels). */ + public val scroll: Float + + /** The scroll value (in pixels). */ + @Deprecated("Use `scroll`.", ReplaceWith("scroll")) public val horizontalScroll: Float + get() = scroll /** The zoom factor. */ public val zoom: Float @@ -66,7 +71,7 @@ public fun CartesianDrawContext( markerTouchPoint: Point?, horizontalDimensions: HorizontalDimensions, chartBounds: RectF, - horizontalScroll: Float, + scroll: Float, zoom: Float, ): CartesianDrawContext = object : CartesianDrawContext, CartesianMeasureContext by measureContext { @@ -78,11 +83,11 @@ public fun CartesianDrawContext( override val markerTouchPoint: Point? = markerTouchPoint - override val zoom: Float = zoom - override val horizontalDimensions: HorizontalDimensions = horizontalDimensions - override val horizontalScroll: Float = horizontalScroll + override val scroll: Float = scroll + + override val zoom: Float = zoom override fun withOtherCanvas(canvas: Canvas, block: (DrawContext) -> Unit) { val originalCanvas = this.canvas diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianMeasureContext.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianMeasureContext.kt index b2bae633d..cbfde7021 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianMeasureContext.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianMeasureContext.kt @@ -27,9 +27,12 @@ public interface CartesianMeasureContext : MeasureContext { /** The chart’s [ChartValues]. */ public val chartValues: ChartValues - /** Whether horizontal scrolling is enabled. */ + /** Whether scroll is enabled. */ public val scrollEnabled: Boolean + /** Whether zoom is enabled. */ + public val zoomEnabled: Boolean + /** Defines how the chart’s content is positioned horizontally. */ public val horizontalLayout: HorizontalLayout } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/FadingEdges.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/FadingEdges.kt index dd8d56f5c..37cd19b1b 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/FadingEdges.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/FadingEdges.kt @@ -94,8 +94,8 @@ public open class FadingEdges( val maxScroll = getMaxScrollDistance() var fadeAlphaFraction: Float - if (scrollEnabled && startEdgeWidthDp > 0f && horizontalScroll > 0f) { - fadeAlphaFraction = (horizontalScroll / visibilityThresholdDp.pixels).coerceAtMost(1f) + if (scrollEnabled && startEdgeWidthDp > 0f && scroll > 0f) { + fadeAlphaFraction = (scroll / visibilityThresholdDp.pixels).coerceAtMost(1f) drawFadingEdge( left = bounds.left, @@ -107,9 +107,8 @@ public open class FadingEdges( ) } - if (scrollEnabled && endEdgeWidthDp > 0f && horizontalScroll < maxScroll) { - fadeAlphaFraction = - ((maxScroll - horizontalScroll) / visibilityThresholdDp.pixels).coerceAtMost(1f) + if (scrollEnabled && endEdgeWidthDp > 0f && scroll < maxScroll) { + fadeAlphaFraction = ((maxScroll - scroll) / visibilityThresholdDp.pixels).coerceAtMost(1f) drawFadingEdge( left = bounds.right - endEdgeWidthDp.pixels, diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/MutableCartesianMeasureContext.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/MutableCartesianMeasureContext.kt index 77a2aa20f..8ef44f612 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/MutableCartesianMeasureContext.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/MutableCartesianMeasureContext.kt @@ -28,8 +28,9 @@ public class MutableCartesianMeasureContext( override val canvasBounds: RectF, override var density: Float, override var isLtr: Boolean, - override var scrollEnabled: Boolean = false, - override var horizontalLayout: HorizontalLayout = HorizontalLayout.Segmented, + override var scrollEnabled: Boolean, + override var zoomEnabled: Boolean, + override var horizontalLayout: HorizontalLayout, override var chartValues: ChartValues, spToPx: (Float) -> Float, ) : diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/HorizontalAxis.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/HorizontalAxis.kt index 0bc3c93fa..20ab702cd 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/HorizontalAxis.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/HorizontalAxis.kt @@ -78,13 +78,11 @@ public open class HorizontalAxis( val textY = if (position.isBottom) tickMarkBottom else tickMarkTop val baseCanvasX = - bounds.getStart(isLtr) - horizontalScroll + + bounds.getStart(isLtr) - scroll + horizontalDimensions.startPadding * layoutDirectionMultiplier val firstVisibleX = fullXRange.start + - horizontalScroll / horizontalDimensions.xSpacing * - chartValues.xStep * - layoutDirectionMultiplier + scroll / horizontalDimensions.xSpacing * chartValues.xStep * layoutDirectionMultiplier val lastVisibleX = firstVisibleX + bounds.width() / horizontalDimensions.xSpacing * chartValues.xStep val visibleXRange = firstVisibleX..lastVisibleX diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CandlestickCartesianLayer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CandlestickCartesianLayer.kt index cc49869ba..86f2e95c4 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CandlestickCartesianLayer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CandlestickCartesianLayer.kt @@ -122,7 +122,7 @@ public open class CandlestickCartesianLayer( val drawingStart = bounds.getStart(isLtr) + (horizontalDimensions.startPadding - halfMaxCandleWidth * zoom) * - layoutDirectionMultiplier - horizontalScroll + layoutDirectionMultiplier - scroll var bodyCenterX: Float var candle: Candle diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/ColumnCartesianLayer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/ColumnCartesianLayer.kt index 542e2b9d1..3936cda3f 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/ColumnCartesianLayer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/ColumnCartesianLayer.kt @@ -122,7 +122,7 @@ public open class ColumnCartesianLayer( val mergeMode = mergeMode(model.extraStore) model.series.forEachIndexed { index, entryCollection -> - drawingStart = getDrawingStart(index, model.series.size, mergeMode) - horizontalScroll + drawingStart = getDrawingStart(index, model.series.size, mergeMode) - scroll entryCollection.forEachIn(chartValues.minX..chartValues.maxX) { entry, _ -> val columnInfo = drawingModel?.getOrNull(index)?.get(entry.x) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/LineCartesianLayer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/LineCartesianLayer.kt index e4c897253..cf288655e 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/LineCartesianLayer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/LineCartesianLayer.kt @@ -277,8 +277,7 @@ public open class LineCartesianLayer( val drawingStartAlignmentCorrection = layoutDirectionMultiplier * horizontalDimensions.startPadding - val drawingStart = - bounds.getStart(isLtr = isLtr) + drawingStartAlignmentCorrection - horizontalScroll + val drawingStart = bounds.getStart(isLtr = isLtr) + drawingStartAlignmentCorrection - scroll forEachPointInBounds( series = entries, diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/CartesianChartView.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/CartesianChartView.kt index 7e4cfb854..b02829a56 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/CartesianChartView.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/CartesianChartView.kt @@ -73,8 +73,10 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 density = context.density, isLtr = context.isLtr, scrollEnabled = false, - spToPx = context::spToPx, + zoomEnabled = false, + horizontalLayout = HorizontalLayout.Segmented, chartValues = ChartValues.Empty, + spToPx = context::spToPx, ) private val scaleGestureListener: ScaleGestureDetector.OnScaleGestureListener = @@ -108,7 +110,9 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 public var zoomHandler: ZoomHandler by invalidatingObservable( ZoomHandler.default(themeHandler.isChartZoomEnabled, scrollHandler.scrollEnabled) - ) + ) { _, newValue -> + measureContext.zoomEnabled = newValue.zoomEnabled + } private val motionEventHandler = MotionEventHandler( @@ -339,7 +343,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 markerTouchPoint = markerTouchPoint, horizontalDimensions = horizontalDimensions, chartBounds = chart.bounds, - horizontalScroll = scrollHandler.value, + scroll = scrollHandler.value, zoom = zoomHandler.value, ) diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/ScrollHandler.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/ScrollHandler.kt index b07749a06..4792b0648 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/ScrollHandler.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/ScrollHandler.kt @@ -36,7 +36,7 @@ import com.patrykandpatrick.vico.core.common.rangeWith * Houses information on a [CartesianChart]’s scroll value. Allows for scroll customization and * programmatic scrolling. * - * @property scrollEnabled whether scrolling is enabled. + * @property scrollEnabled whether scroll is enabled. * @property initialScroll represents the initial scroll value. * @property autoScroll represents the scroll value or delta for automatic scrolling. * @property autoScrollCondition defines when an automatic scroll should be performed. diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/ZoomHandler.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/ZoomHandler.kt index 1b1607807..7c3ef7c35 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/ZoomHandler.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/ZoomHandler.kt @@ -29,7 +29,7 @@ import com.patrykandpatrick.vico.core.common.Defaults /** * Houses information on a [CartesianChart]’s zoom factor. Allows for zoom customization. * - * @param zoomEnabled whether zooming is enabled. + * @param zoomEnabled whether zoom is enabled. * @param initialZoom represents the initial zoom factor. * @param minZoom represents the minimum zoom factor. * @param maxZoom represents the maximum zoom factor. From 6eda1da352e2bbadecf455ed18521b391c9fe2e1 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Fri, 28 Jun 2024 19:13:37 +0200 Subject: [PATCH 62/97] =?UTF-8?q?=E2=80=9CBug=E2=80=9D=20issue=20form:=20U?= =?UTF-8?q?pdate=20version=20drop-down?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 71e62a188..f0226c4fd 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -24,9 +24,8 @@ body: description: If you’re using an outdated version, please try updating Vico first. multiple: true options: - - 2.0.0-alpha.20 - - 1.15.0-alpha.1 - - 1.14.0 + - 2.0.0-alpha.21 + - 1.15.0 validations: required: true - type: input From 10eef592920f9c6a769b27e6e0e0bbd8a671f682 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Sat, 29 Jun 2024 11:35:07 +0200 Subject: [PATCH 63/97] `CartesianChartModelProducer`: Make `Transaction` constructor public, and deprecate `createTransaction` --- .../core/cartesian/data/CartesianChartModelProducer.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt index 1e961f443..b9573558e 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt @@ -155,6 +155,7 @@ public class CartesianChartModelProducer(dispatcher: CoroutineDispatcher = Dispa } /** Creates a [Transaction] instance. */ + @Deprecated("Use the `Transaction` constructor.", ReplaceWith("Transaction()")) public fun createTransaction(): Transaction = Transaction() /** @@ -162,20 +163,20 @@ public class CartesianChartModelProducer(dispatcher: CoroutineDispatcher = Dispa * For suspending behavior, use [runTransaction]. */ public fun tryRunTransaction(block: Transaction.() -> Unit): Boolean = - createTransaction().also(block).tryCommit() + Transaction().also(block).tryCommit() /** * Creates a [Transaction], runs [block], and calls [Transaction.commit], returning its output. */ public suspend fun runTransaction(block: Transaction.() -> Unit): Deferred = - createTransaction().also(block).commit() + Transaction().also(block).commit() /** * Handles data updates. An initially empty list of [CartesianLayerModel.Partial]s is created and * can be updated via the class’s functions. Each [CartesianLayerModel.Partial] corresponds to a * [CartesianLayer]. */ - public inner class Transaction internal constructor() { + public inner class Transaction { private val newPartials = mutableListOf() private val newExtraStore = MutableExtraStore() From 313f44f997378c81a913df446d71253f47f1cf85 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Sat, 29 Jun 2024 11:47:32 +0200 Subject: [PATCH 64/97] Update `CartesianChartModelProducer` to use `CoroutineScope.launch` for `UpdateReceiver.handleUpdate` --- .../cartesian/data/CartesianChartModelProducer.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt index b9573558e..da19000a4 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt @@ -25,10 +25,9 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.ensureActive +import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex @@ -54,9 +53,9 @@ public class CartesianChartModelProducer(dispatcher: CoroutineDispatcher = Dispa this.partials = immutablePartials this.extraStore = extraStore cachedModel = null - val deferredUpdates = updateReceivers.values.map { coroutineScope.async { it.handleUpdate() } } + val receiverJobs = updateReceivers.values.map { coroutineScope.launch { it.handleUpdate() } } coroutineScope.launch { - deferredUpdates.awaitAll() + receiverJobs.joinAll() mutex.unlock() } return true @@ -77,9 +76,9 @@ public class CartesianChartModelProducer(dispatcher: CoroutineDispatcher = Dispa this.partials = partials.toList() this.extraStore = extraStore cachedModel = null - val deferredUpdates = updateReceivers.values.map { coroutineScope.async { it.handleUpdate() } } + val receiverJobs = updateReceivers.values.map { coroutineScope.launch { it.handleUpdate() } } coroutineScope.launch { - deferredUpdates.awaitAll() + receiverJobs.joinAll() mutex.unlock() completableDeferred.complete(Unit) } From f599801664e9b66259f684503c577b24c2a1af96 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Sat, 29 Jun 2024 14:26:19 +0200 Subject: [PATCH 65/97] Make `Insets`-related updates --- .../vico/sample/showcase/Marker.kt | 18 ++- .../compose/cartesian/CartesianChartHost.kt | 8 +- .../vico/core/cartesian/CartesianChart.kt | 50 ++++-- .../vico/core/cartesian/ChartInsetter.kt | 34 ++-- .../vico/core/cartesian/HorizontalInsets.kt | 33 +++- .../vico/core/cartesian/Insets.kt | 149 ++++++------------ .../vico/core/cartesian/VirtualLayout.kt | 104 ------------ .../vico/core/cartesian/axis/AxisManager.kt | 46 +++--- .../core/cartesian/axis/HorizontalAxis.kt | 44 +++--- .../vico/core/cartesian/axis/VerticalAxis.kt | 34 ++-- .../cartesian/layer/BaseCartesianLayer.kt | 2 +- .../cartesian/layer/LineCartesianLayer.kt | 8 +- .../marker/DefaultCartesianMarker.kt | 8 +- .../views/cartesian/CartesianChartView.kt | 4 +- .../vico/views/common/BaseChartView.kt | 6 +- 15 files changed, 214 insertions(+), 334 deletions(-) delete mode 100644 vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/VirtualLayout.kt 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 84dad1482..a75dd370b 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 @@ -97,17 +97,25 @@ internal fun rememberMarker( }, guideline = guideline, ) { - override fun getInsets( + override fun updateInsets( context: CartesianMeasureContext, - outInsets: Insets, horizontalDimensions: HorizontalDimensions, + insets: Insets, ) { with(context) { - super.getInsets(context, outInsets, horizontalDimensions) val baseShadowInsetDp = CLIPPING_FREE_SHADOW_RADIUS_MULTIPLIER * LABEL_BACKGROUND_SHADOW_RADIUS_DP - outInsets.top += (baseShadowInsetDp - LABEL_BACKGROUND_SHADOW_DY_DP).pixels - outInsets.bottom += (baseShadowInsetDp + LABEL_BACKGROUND_SHADOW_DY_DP).pixels + var topInset = (baseShadowInsetDp - LABEL_BACKGROUND_SHADOW_DY_DP).pixels + var bottomInset = (baseShadowInsetDp + LABEL_BACKGROUND_SHADOW_DY_DP).pixels + when (labelPosition) { + LabelPosition.Top, + LabelPosition.AbovePoint -> + topInset += label.getHeight(context) + label.tickSizeDp.pixels + LabelPosition.Bottom -> + bottomInset += label.getHeight(context) + label.tickSizeDp.pixels + LabelPosition.AroundPoint -> {} + } + insets.ensureValuesAtLeast(top = topInset, bottom = bottomInset) } } } diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/CartesianChartHost.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/CartesianChartHost.kt index 723a85601..f389b157f 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/CartesianChartHost.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/CartesianChartHost.kt @@ -200,13 +200,13 @@ internal fun CartesianChartHostImpl( horizontalLayout: HorizontalLayout, chartValues: ChartValues, ) { - val bounds = remember { RectF() } + val canvasBounds = remember { RectF() } val markerTouchPoint = remember { mutableStateOf(null) } val measureContext = rememberCartesianMeasureContext( scrollState.scrollEnabled, zoomState.zoomEnabled, - bounds, + canvasBounds, horizontalLayout, with(LocalContext.current) { ::spToPx }, chartValues, @@ -251,10 +251,10 @@ internal fun CartesianChartHostImpl( }, ) ) { - bounds.set(left = 0, top = 0, right = size.width, bottom = size.height) + canvasBounds.set(left = 0, top = 0, right = size.width, bottom = size.height) horizontalDimensions.clear() - chart.prepare(measureContext, model, horizontalDimensions, bounds, marker) + chart.prepare(measureContext, model, horizontalDimensions, canvasBounds, marker) if (chart.bounds.isEmpty) return@Canvas diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianChart.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianChart.kt index bb3adc037..dfb4ad561 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianChart.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianChart.kt @@ -34,6 +34,7 @@ import com.patrykandpatrick.vico.core.common.BoundsAware import com.patrykandpatrick.vico.core.common.Legend import com.patrykandpatrick.vico.core.common.data.MutableExtraStore import com.patrykandpatrick.vico.core.common.inClip +import com.patrykandpatrick.vico.core.common.orZero import com.patrykandpatrick.vico.core.common.set import com.patrykandpatrick.vico.core.common.setAll import java.util.SortedMap @@ -52,9 +53,8 @@ public open class CartesianChart( ) : BoundsAware, ChartInsetter { private val decorations = mutableListOf() private val persistentMarkers = mutableMapOf() - private val tempInsets = Insets() + private val insets = Insets() private val axisManager = AxisManager() - private val virtualLayout = VirtualLayout(axisManager) private val _markerTargets = sortedMapOf>() private val drawingModelAndLayerConsumer = @@ -102,6 +102,7 @@ public open class CartesianChart( public val layers: List> = layers.toList() /** The [CartesianChart]’s [ChartInsetter]s (persistent [CartesianMarker]s). */ + @Deprecated("This is no longer used. Consumers aren’t expected to require this property.") public val chartInsetters: Collection = persistentMarkers.values /** Links _x_ values to [CartesianMarker.Target]s. */ @@ -139,10 +140,11 @@ public open class CartesianChart( context: CartesianMeasureContext, model: CartesianChartModel, horizontalDimensions: MutableHorizontalDimensions, - bounds: RectF, + canvasBounds: RectF, marker: CartesianMarker?, ) { _markerTargets.clear() + insets.clear() model.forEachWithLayer( horizontalDimensionUpdateModelAndLayerConsumer.apply { this.context = context @@ -153,7 +155,29 @@ public open class CartesianChart( topAxis?.updateHorizontalDimensions(context, horizontalDimensions) endAxis?.updateHorizontalDimensions(context, horizontalDimensions) bottomAxis?.updateHorizontalDimensions(context, horizontalDimensions) - virtualLayout.setBounds(context, bounds, this, legend, horizontalDimensions, marker) + val insetters = buildList { + add(this@CartesianChart) + addAll(axisManager.axisCache) + if (marker != null) add(marker) + addAll(persistentMarkers.values) + } + insetters.forEach { it.updateInsets(context, horizontalDimensions, insets) } + val legendHeight = legend?.getHeight(context, canvasBounds.width()).orZero + val freeHeight = canvasBounds.height() - insets.vertical - legendHeight + insetters.forEach { it.updateHorizontalInsets(context, freeHeight, insets) } + setBounds( + canvasBounds.left + insets.getLeft(context.isLtr), + canvasBounds.top + insets.top, + canvasBounds.right - insets.getRight(context.isLtr), + canvasBounds.bottom - insets.bottom - legendHeight, + ) + axisManager.setAxesBounds(context, canvasBounds, bounds, insets) + legend?.setBounds( + left = canvasBounds.left, + top = bounds.bottom + insets.bottom, + right = canvasBounds.right, + bottom = bounds.bottom + insets.bottom + legendHeight, + ) } /** Draws the [CartesianChart]. */ @@ -188,24 +212,20 @@ public open class CartesianChart( ) } - override fun getInsets( + override fun updateInsets( context: CartesianMeasureContext, - outInsets: Insets, horizontalDimensions: HorizontalDimensions, + insets: Insets, ) { - tempInsets.clear() - layers.forEach { it.getInsets(context, tempInsets, horizontalDimensions) } - outInsets.setValuesIfGreater(tempInsets) + layers.forEach { it.updateInsets(context, horizontalDimensions, insets) } } - override fun getHorizontalInsets( + override fun updateHorizontalInsets( context: CartesianMeasureContext, - availableHeight: Float, - outInsets: HorizontalInsets, + freeHeight: Float, + insets: HorizontalInsets, ) { - tempInsets.clear() - layers.forEach { it.getHorizontalInsets(context, availableHeight, tempInsets) } - outInsets.setValuesIfGreater(start = tempInsets.start, end = tempInsets.end) + layers.forEach { it.updateHorizontalInsets(context, freeHeight, insets) } } /** Prepares the [CartesianLayer]s for a difference animation. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/ChartInsetter.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/ChartInsetter.kt index 0630f0274..964f50ba4 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/ChartInsetter.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/ChartInsetter.kt @@ -25,33 +25,33 @@ import com.patrykandpatrick.vico.core.cartesian.marker.CartesianMarker */ 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 - * [CartesianChart] is the largest of the insets requested for the edge. + * Called during the measurement phase, before [updateHorizontalInsets]. Both horizontal and + * vertical insets can be requested from this function. The final inset for a given edge of the + * associated [CartesianChart] is the largest of the insets requested for the edge. * * @param context holds data used for the measuring of components. - * @param outInsets used to store the requested insets. * @param horizontalDimensions the [CartesianChart]’s [HorizontalDimensions]. + * @param insets used to store the requested insets. */ - public fun getInsets( + public fun updateInsets( context: CartesianMeasureContext, - outInsets: Insets, horizontalDimensions: HorizontalDimensions, - ): Unit = Unit + insets: Insets, + ) {} /** - * Called during the measurement phase, after [getInsets]. Only horizontal insets can be requested - * from this function. Unless the available height is of interest, [getInsets] can be used to set - * all insets. The final inset for a given edge of the associated [CartesianChart] is the largest - * of the insets requested for the edge. + * Called during the measurement phase, after [updateInsets]. Only horizontal insets can be + * requested from this function. Unless the available height is of interest, [updateInsets] can be + * used to set all insets. The final inset for a given edge of the associated [CartesianChart] is + * the largest of the insets requested for the edge. * * @param context holds data used for the measuring of components. - * @param availableHeight the available height. The vertical insets are considered here. - * @param outInsets used to store the requested insets. + * @param freeHeight the available height. The vertical insets are considered here. + * @param insets used to store the requested insets. */ - public fun getHorizontalInsets( + public fun updateHorizontalInsets( context: CartesianMeasureContext, - availableHeight: Float, - outInsets: HorizontalInsets, - ): Unit = Unit + freeHeight: Float, + insets: HorizontalInsets, + ) {} } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/HorizontalInsets.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/HorizontalInsets.kt index 7d8033214..169185f8c 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/HorizontalInsets.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/HorizontalInsets.kt @@ -23,12 +23,29 @@ package com.patrykandpatrick.vico.core.cartesian * @see Insets */ public interface HorizontalInsets { - /** Sets the start and end insets. */ - 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) + /** The start inset’s size (in pixels). */ + public val start: Float + + /** The end inset’s size (in pixels). */ + public val end: Float + + /** The sum of [start] and [end]. */ + public val horizontal: Float + get() = start + end + + /** Returns the left inset’s size (in pixels). */ + public fun getLeft(isLtr: Boolean): Float = if (isLtr) start else end + + /** Returns the right inset’s size (in pixels). */ + public fun getRight(isLtr: Boolean): Float = if (isLtr) end else start + + /** Ensures that the stored values are no smaller than the provided ones. */ + public fun ensureValuesAtLeast(start: Float = this.start, end: Float = this.end) + + /** Ensures that the stored values are no smaller than the provided ones. */ + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated("Use `ensureValuesAtLeast`.") + public fun setValuesIfGreater(start: Float, end: Float) { + ensureValuesAtLeast(start, end) + } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/Insets.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/Insets.kt index b0bc85091..2777401a3 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/Insets.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/Insets.kt @@ -16,127 +16,82 @@ package com.patrykandpatrick.vico.core.cartesian -import com.patrykandpatrick.vico.core.common.half -import kotlin.math.max - /** * Used to store the insets requested by [ChartInsetter]s. * - * @param start the start inset. - * @param top the top inset. - * @param end the end inset. - * @param bottom the bottom inset. * @see ChartInsetter */ -public class Insets( - public var start: Float = 0f, - public var top: Float = 0f, - public var end: Float = 0f, - public var bottom: Float = 0f, -) : HorizontalInsets { - /** The sum of the sizes of the start inset and the end inset. */ - public val horizontal: Float - get() = start + end +public class Insets : HorizontalInsets { + /** The start inset’s size (in pixels). */ + public override var start: Float = 0f + private set + + /** The top inset’s size (in pixels). */ + public var top: Float = 0f + private set + + /** The end inset’s size (in pixels). */ + public override var end: Float = 0f + private set - /** The sum of the sizes of the top inset and the bottom inset. */ + /** The bottom inset’s size (in pixels). */ + public var bottom: Float = 0f + private set + + /** The sum of [top] and [bottom]. */ public val vertical: Float get() = top + bottom - /** The largest of the four insets. */ + /** The largest of [start], [top], [end], and [bottom]. */ public val max: Float get() = maxOf(start, top, end, bottom) - /** - * Updates the size of each of the four insets to match the size of its corresponding inset from - * the provided [Insets] instance. - */ - public fun set(other: Insets): Insets = set(other.start, other.top, other.end, other.bottom) - - /** Sets a common size for all four insets. */ - public fun set(all: Float): Insets = set(all, all, all, all) - - /** Updates the size of each of the four insets individually. */ - public fun set(start: Float = 0f, top: Float = 0f, end: Float = 0f, bottom: Float = 0f): 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) { - this.start = start - this.end = end - } - - /** - * Returns the size of the left inset, taking into account the layout direction. - * - * @param isLtr whether the layout is left-to-right. - */ - public fun getLeft(isLtr: Boolean): Float = if (isLtr) start else end - - /** - * Returns the size of the right inset, taking into account the layout direction. - * - * @param isLtr whether the layout is left-to-right. - */ - public fun getRight(isLtr: Boolean): Float = if (isLtr) end else start - - /** - * 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 - } - - /** - * 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) { - this.start = max(start, this.start) - this.end = max(end, this.end) + override fun ensureValuesAtLeast(start: Float, end: Float) { + this.start = this.start.coerceAtLeast(start) + this.end = this.end.coerceAtLeast(end) } - /** - * For each of the four insets, updates the size of the inset to the size of the corresponding - * inset from the provided [Insets] instance if the size of the corresponding inset from the - * provided [Insets] instance is greater. - */ - public fun setValuesIfGreater(other: Insets) { - start = max(start, other.start) - top = max(top, other.top) - end = max(end, other.end) - bottom = max(bottom, other.bottom) + /** Ensures that the stored values are no smaller than the provided ones. */ + public fun ensureValuesAtLeast( + start: Float = this.start, + top: Float = this.top, + end: Float = this.end, + bottom: Float = this.bottom, + ) { + this.start = this.start.coerceAtLeast(start) + this.top = this.top.coerceAtLeast(top) + this.end = this.end.coerceAtLeast(end) + this.bottom = this.bottom.coerceAtLeast(bottom) } - /** - * For each of the four insets, updates the size of the inset to the corresponding provided value - * if the corresponding provided value is greater. - */ + /** Ensures that the stored values are no smaller than the provided ones. */ + @Deprecated( + "Use `ensureValuesAtLeast`.", + ReplaceWith("ensureValuesAtLeast(start, top, end, bottom)"), + ) public fun setAllIfGreater( start: Float = this.start, top: Float = this.top, end: Float = this.end, bottom: Float = this.bottom, ) { - this.start = max(start, this.start) - this.top = max(top, this.top) - this.end = max(end, this.end) - this.bottom = max(bottom, this.bottom) + ensureValuesAtLeast(start, top, end, bottom) + } + + /** Ensures that the stored values are no smaller than those in [other]. */ + @Deprecated( + "Use `ensureValuesAtLeast`.", + ReplaceWith("ensureValuesAtLeast(other.start, other.top, other.end, other.bottom)"), + ) + public fun setValuesIfGreater(other: Insets) { + ensureValuesAtLeast(other.start, other.top, other.end, other.bottom) } - /** Sets the size of each of the four insets to zero. */ + /** Clears the stored values. */ public fun clear() { - set(0f) + start = 0f + top = 0f + end = 0f + bottom = 0f } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/VirtualLayout.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/VirtualLayout.kt deleted file mode 100644 index d8ee427c5..000000000 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/VirtualLayout.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2024 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.core.cartesian - -import android.graphics.RectF -import com.patrykandpatrick.vico.core.cartesian.axis.AxisManager -import com.patrykandpatrick.vico.core.common.Legend -import com.patrykandpatrick.vico.core.common.orZero - -internal class VirtualLayout(private val axisManager: AxisManager) { - private val tempInsetters = ArrayList(TEMP_INSETTERS_INITIAL_SIZE) - - private val finalInsets: Insets = Insets() - - private val tempInsets: Insets = Insets() - - /** - * Measures and sets the bounds for the components of the chart. - * - * @param context holds data used for the measuring of components. - * @param contentBounds the bounds in which the chart should be drawn. - * @param chart the chart itself. - * @param legend the legend for the chart. - * @param horizontalDimensions the [HorizontalDimensions] of the chart. - * @param chartInsetter additional components that influence the chart layout, such as markers. - * @return the bounds applied to the chart. - */ - fun setBounds( - context: CartesianMeasureContext, - contentBounds: RectF, - chart: CartesianChart, - 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) - } - - 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/cartesian/axis/AxisManager.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/AxisManager.kt index 5df79c4e1..787ccc005 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/AxisManager.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/AxisManager.kt @@ -19,7 +19,6 @@ package com.patrykandpatrick.vico.core.cartesian.axis import android.graphics.RectF import com.patrykandpatrick.vico.core.cartesian.CartesianDrawContext import com.patrykandpatrick.vico.core.cartesian.CartesianMeasureContext -import com.patrykandpatrick.vico.core.cartesian.ChartInsetter import com.patrykandpatrick.vico.core.cartesian.Insets import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -32,42 +31,35 @@ internal class AxisManager { var endAxis: Axis? by cacheInList() var bottomAxis: Axis? by cacheInList() - fun addInsetters(destination: MutableList) { - startAxis?.let(destination::add) - topAxis?.let(destination::add) - endAxis?.let(destination::add) - bottomAxis?.let(destination::add) - } - fun setAxesBounds( measureContext: CartesianMeasureContext, - contentBounds: RectF, + canvasBounds: RectF, chartBounds: RectF, insets: Insets, ) { startAxis?.setStartAxisBounds( context = measureContext, - contentBounds = contentBounds, + canvasBounds = canvasBounds, chartBounds = chartBounds, insets = insets, ) topAxis?.setTopAxisBounds( context = measureContext, - contentBounds = contentBounds, + canvasBounds = canvasBounds, insets = insets, ) endAxis?.setEndAxisBounds( context = measureContext, - contentBounds = contentBounds, + canvasBounds = canvasBounds, chartBounds = chartBounds, insets = insets, ) bottomAxis?.setBottomAxisBounds( context = measureContext, - contentBounds = contentBounds, + canvasBounds = canvasBounds, chartBounds = chartBounds, insets = insets, ) @@ -77,15 +69,15 @@ internal class AxisManager { private fun Axis.setStartAxisBounds( context: CartesianMeasureContext, - contentBounds: RectF, + canvasBounds: RectF, chartBounds: RectF, insets: Insets, ) { with(context) { setBounds( - left = if (isLtr) contentBounds.left else contentBounds.right - insets.start, + left = if (isLtr) canvasBounds.left else canvasBounds.right - insets.start, top = chartBounds.top, - right = if (isLtr) contentBounds.left + insets.start else contentBounds.right, + right = if (isLtr) canvasBounds.left + insets.start else canvasBounds.right, bottom = chartBounds.bottom, ) } @@ -93,30 +85,30 @@ internal class AxisManager { private fun Axis.setTopAxisBounds( context: CartesianMeasureContext, - contentBounds: RectF, + canvasBounds: RectF, insets: Insets, ) { with(context) { setBounds( - left = contentBounds.left + if (isLtr) insets.start else insets.end, - top = contentBounds.top, - right = contentBounds.right - if (isLtr) insets.end else insets.start, - bottom = contentBounds.top + insets.top, + left = canvasBounds.left + if (isLtr) insets.start else insets.end, + top = canvasBounds.top, + right = canvasBounds.right - if (isLtr) insets.end else insets.start, + bottom = canvasBounds.top + insets.top, ) } } private fun Axis.setEndAxisBounds( context: CartesianMeasureContext, - contentBounds: RectF, + canvasBounds: RectF, chartBounds: RectF, insets: Insets, ) { with(context) { setBounds( - left = if (isLtr) contentBounds.right - insets.end else contentBounds.left, + left = if (isLtr) canvasBounds.right - insets.end else canvasBounds.left, top = chartBounds.top, - right = if (isLtr) contentBounds.right else contentBounds.left + insets.end, + right = if (isLtr) canvasBounds.right else canvasBounds.left + insets.end, bottom = chartBounds.bottom, ) } @@ -124,15 +116,15 @@ internal class AxisManager { private fun Axis.setBottomAxisBounds( context: CartesianMeasureContext, - contentBounds: RectF, + canvasBounds: RectF, chartBounds: RectF, insets: Insets, ) { with(context) { setBounds( - left = contentBounds.left + if (isLtr) insets.start else insets.end, + left = canvasBounds.left + if (isLtr) insets.start else insets.end, top = chartBounds.bottom, - right = contentBounds.right - if (isLtr) insets.end else insets.start, + right = canvasBounds.right - if (isLtr) insets.end else insets.start, bottom = chartBounds.bottom + insets.bottom, ) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/HorizontalAxis.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/HorizontalAxis.kt index 20ab702cd..867d80c0e 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/HorizontalAxis.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/HorizontalAxis.kt @@ -275,33 +275,31 @@ public open class HorizontalAxis( } } - override fun getInsets( + override fun updateInsets( context: CartesianMeasureContext, - outInsets: Insets, horizontalDimensions: HorizontalDimensions, + insets: Insets, ) { val maxLabelWidth = context.getMaxLabelWidth(horizontalDimensions, context.getFullXRange(horizontalDimensions)) - with(outInsets) { - start = - itemPlacer.getStartHorizontalAxisInset( - context, - horizontalDimensions, - context.tickThickness, - maxLabelWidth, - ) - end = - itemPlacer.getEndHorizontalAxisInset( - context, - horizontalDimensions, - context.tickThickness, - maxLabelWidth, - ) - top = - if (position.isTop) getDesiredHeight(context, horizontalDimensions, maxLabelWidth) else 0f - bottom = - if (position.isBottom) getDesiredHeight(context, horizontalDimensions, maxLabelWidth) - else 0f + val height = getHeight(context, horizontalDimensions, maxLabelWidth) + insets.ensureValuesAtLeast( + itemPlacer.getStartHorizontalAxisInset( + context, + horizontalDimensions, + context.tickThickness, + maxLabelWidth, + ), + itemPlacer.getEndHorizontalAxisInset( + context, + horizontalDimensions, + context.tickThickness, + maxLabelWidth, + ), + ) + when { + position.isTop -> insets.ensureValuesAtLeast(top = height) + position.isBottom -> insets.ensureValuesAtLeast(bottom = height) } } @@ -314,7 +312,7 @@ public open class HorizontalAxis( start..end } - protected open fun getDesiredHeight( + protected open fun getHeight( context: CartesianMeasureContext, horizontalDimensions: HorizontalDimensions, maxLabelWidth: Float, diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/VerticalAxis.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/VerticalAxis.kt index b7af7e990..29a2bf140 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/VerticalAxis.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/VerticalAxis.kt @@ -215,29 +215,27 @@ public open class VerticalAxis(override val po } } - override fun getHorizontalInsets( + override fun updateHorizontalInsets( context: CartesianMeasureContext, - availableHeight: Float, - outInsets: HorizontalInsets, - ): Unit = - with(context) { - val desiredWidth = getDesiredWidth(this, availableHeight) - - outInsets.set( - start = if (position.isStart) desiredWidth else 0f, - end = if (position.isEnd) desiredWidth else 0f, - ) + freeHeight: Float, + insets: HorizontalInsets, + ) { + val width = getWidth(context, freeHeight) + when { + position.isStart -> insets.ensureValuesAtLeast(start = width) + position.isEnd -> insets.ensureValuesAtLeast(end = width) } + } - override fun getInsets( + override fun updateInsets( context: CartesianMeasureContext, - outInsets: Insets, horizontalDimensions: HorizontalDimensions, + insets: Insets, ): Unit = with(context) { val maxLabelHeight = getMaxLabelHeight() val maxLineThickness = maxOf(axisThickness, tickThickness) - outInsets.set( + insets.ensureValuesAtLeast( top = itemPlacer.getTopVerticalAxisInset( context, @@ -255,11 +253,7 @@ public open class VerticalAxis(override val po ) } - /** - * Calculates the optimal width for this [VerticalAxis], accounting for the value of - * [sizeConstraint]. - */ - protected open fun getDesiredWidth(context: CartesianMeasureContext, height: Float): Float = + protected open fun getWidth(context: CartesianMeasureContext, freeHeight: Float): Float = with(context) { when (val constraint = sizeConstraint) { is SizeConstraint.Auto -> { @@ -277,7 +271,7 @@ public open class VerticalAxis(override val po val labelSpace = when (horizontalLabelPosition) { Outside -> { - val maxLabelWidth = getMaxLabelWidth(height).ceil + val maxLabelWidth = getMaxLabelWidth(freeHeight).ceil extraStore[maxLabelWidthKey] = maxLabelWidth maxLabelWidth + tickLength } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/BaseCartesianLayer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/BaseCartesianLayer.kt index da89a0c43..0018ae58f 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/BaseCartesianLayer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/BaseCartesianLayer.kt @@ -53,7 +53,7 @@ public abstract class BaseCartesianLayer : CartesianLay override fun draw(context: CartesianDrawContext, model: T) { with(context) { insets.clear() - getInsets(this, insets, horizontalDimensions) + updateInsets(this, horizontalDimensions, insets) canvas.inClip( left = bounds.left - insets.getLeft(isLtr), top = bounds.top - insets.top, diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/LineCartesianLayer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/LineCartesianLayer.kt index cf288655e..b8cb4f5df 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/LineCartesianLayer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/LineCartesianLayer.kt @@ -546,17 +546,17 @@ public open class LineCartesianLayer( ) } - override fun getInsets( + override fun updateInsets( context: CartesianMeasureContext, - outInsets: Insets, horizontalDimensions: HorizontalDimensions, + insets: Insets, ): Unit = with(context) { - outInsets.setVertical( + val verticalInset = lines .maxOf { if (it.point != null) max(it.thicknessDp, it.pointSizeDp) else it.thicknessDp } .pixels - ) + insets.ensureValuesAtLeast(top = verticalInset, bottom = verticalInset) } override fun prepareForTransformation( diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/marker/DefaultCartesianMarker.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/marker/DefaultCartesianMarker.kt index 133a16543..c6b6828ce 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/marker/DefaultCartesianMarker.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/marker/DefaultCartesianMarker.kt @@ -200,18 +200,18 @@ public open class DefaultCartesianMarker( .forEach { x -> guideline?.drawVertical(this, chartBounds.top, chartBounds.bottom, x) } } - override fun getInsets( + override fun updateInsets( context: CartesianMeasureContext, - outInsets: Insets, horizontalDimensions: HorizontalDimensions, + insets: Insets, ) { with(context) { when (labelPosition) { LabelPosition.Top, LabelPosition.AbovePoint -> - outInsets.top = label.getHeight(context) + label.tickSizeDp.pixels + insets.ensureValuesAtLeast(top = label.getHeight(context) + label.tickSizeDp.pixels) LabelPosition.Bottom -> - outInsets.bottom = label.getHeight(context) + label.tickSizeDp.pixels + insets.ensureValuesAtLeast(bottom = label.getHeight(context) + label.tickSizeDp.pixels) LabelPosition.AroundPoint -> Unit // Will be inside the chart } } diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/CartesianChartView.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/CartesianChartView.kt index b02829a56..828085c29 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/CartesianChartView.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/CartesianChartView.kt @@ -69,7 +69,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 override val measureContext: MutableCartesianMeasureContext = MutableCartesianMeasureContext( - canvasBounds = contentBounds, + canvasBounds = canvasBounds, density = context.density, isLtr = context.isLtr, scrollEnabled = false, @@ -322,7 +322,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 withChartAndModel { chart, model -> measureContext.reset() horizontalDimensions.clear() - chart.prepare(measureContext, model, horizontalDimensions, contentBounds, marker) + chart.prepare(measureContext, model, horizontalDimensions, canvasBounds, marker) if (chart.bounds.isEmpty) return@withChartAndModel diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/common/BaseChartView.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/common/BaseChartView.kt index f5f513dbf..791add694 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/common/BaseChartView.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/common/BaseChartView.kt @@ -50,11 +50,11 @@ public abstract class BaseChartView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : FrameLayout(context, attrs, defStyleAttr) { - protected val contentBounds: RectF = RectF() + protected val canvasBounds: RectF = RectF() protected open val measureContext: MutableMeasureContext = MutableMeasureContext( - canvasBounds = contentBounds, + canvasBounds = canvasBounds, density = context.density, isLtr = context.isLtr, spToPx = context::spToPx, @@ -151,7 +151,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY), ) - contentBounds.set( + canvasBounds.set( left = paddingLeft, top = paddingTop, right = width - paddingRight, From cbd91e60c6ec887d9df887f5116c19b6a4a56e4b Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:09:06 +0200 Subject: [PATCH 66/97] Rename `BoundsAware` to `Bounded` --- .../patrykandpatrick/vico/core/cartesian/CartesianChart.kt | 4 ++-- .../com/patrykandpatrick/vico/core/cartesian/axis/Axis.kt | 4 ++-- .../vico/core/cartesian/layer/BaseCartesianLayer.kt | 4 ++-- .../vico/core/cartesian/layer/CartesianLayer.kt | 4 ++-- .../vico/core/common/{BoundsAware.kt => Bounded.kt} | 5 ++++- .../java/com/patrykandpatrick/vico/core/common/Legend.kt | 2 +- 6 files changed, 13 insertions(+), 10 deletions(-) rename vico/core/src/main/java/com/patrykandpatrick/vico/core/common/{BoundsAware.kt => Bounded.kt} (90%) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianChart.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianChart.kt index dfb4ad561..8fe5f4fb7 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianChart.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/CartesianChart.kt @@ -30,7 +30,7 @@ import com.patrykandpatrick.vico.core.cartesian.layer.CartesianLayer import com.patrykandpatrick.vico.core.cartesian.layer.ColumnCartesianLayer import com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer import com.patrykandpatrick.vico.core.cartesian.marker.CartesianMarker -import com.patrykandpatrick.vico.core.common.BoundsAware +import com.patrykandpatrick.vico.core.common.Bounded import com.patrykandpatrick.vico.core.common.Legend import com.patrykandpatrick.vico.core.common.data.MutableExtraStore import com.patrykandpatrick.vico.core.common.inClip @@ -50,7 +50,7 @@ public open class CartesianChart( layers: List>, public var legend: Legend? = null, public var fadingEdges: FadingEdges? = null, -) : BoundsAware, ChartInsetter { +) : Bounded, ChartInsetter { private val decorations = mutableListOf() private val persistentMarkers = mutableMapOf() private val insets = Insets() diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/Axis.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/Axis.kt index c80388c2e..80563a8be 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/Axis.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/Axis.kt @@ -22,13 +22,13 @@ import com.patrykandpatrick.vico.core.cartesian.CartesianDrawContext import com.patrykandpatrick.vico.core.cartesian.CartesianMeasureContext import com.patrykandpatrick.vico.core.cartesian.ChartInsetter import com.patrykandpatrick.vico.core.cartesian.MutableHorizontalDimensions -import com.patrykandpatrick.vico.core.common.BoundsAware +import com.patrykandpatrick.vico.core.common.Bounded /** * Defines the minimal set of properties and functions required by other parts of the library to * draw an axis. */ -public interface Axis : BoundsAware, ChartInsetter { +public interface Axis : Bounded, ChartInsetter { /** Defines the position of the axis relative to the [CartesianChart]. */ public val position: Position diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/BaseCartesianLayer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/BaseCartesianLayer.kt index 0018ae58f..3696af7f7 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/BaseCartesianLayer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/BaseCartesianLayer.kt @@ -23,12 +23,12 @@ import com.patrykandpatrick.vico.core.cartesian.MutableHorizontalDimensions import com.patrykandpatrick.vico.core.cartesian.data.AxisValueOverrider import com.patrykandpatrick.vico.core.cartesian.data.CartesianLayerModel import com.patrykandpatrick.vico.core.cartesian.data.ChartValues -import com.patrykandpatrick.vico.core.common.BoundsAware +import com.patrykandpatrick.vico.core.common.Bounded import com.patrykandpatrick.vico.core.common.half import com.patrykandpatrick.vico.core.common.inClip /** A base [CartesianLayer] implementation. */ -public abstract class BaseCartesianLayer : CartesianLayer, BoundsAware { +public abstract class BaseCartesianLayer : CartesianLayer, Bounded { private val insets: Insets = Insets() override val bounds: RectF = RectF() diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CartesianLayer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CartesianLayer.kt index 707ffb389..1d00eca48 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CartesianLayer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/layer/CartesianLayer.kt @@ -25,14 +25,14 @@ import com.patrykandpatrick.vico.core.cartesian.data.CartesianLayerModel import com.patrykandpatrick.vico.core.cartesian.data.ChartValues import com.patrykandpatrick.vico.core.cartesian.data.MutableChartValues import com.patrykandpatrick.vico.core.cartesian.marker.CartesianMarker -import com.patrykandpatrick.vico.core.common.BoundsAware +import com.patrykandpatrick.vico.core.common.Bounded import com.patrykandpatrick.vico.core.common.data.MutableExtraStore /** * Visualizes data on a Cartesian plane. [CartesianLayer]s are combined and drawn by * [CartesianChart]s. */ -public interface CartesianLayer : BoundsAware, ChartInsetter { +public interface CartesianLayer : Bounded, ChartInsetter { /** Links _x_ values to [CartesianMarker.Target]s. */ public val markerTargets: Map> diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/BoundsAware.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Bounded.kt similarity index 90% rename from vico/core/src/main/java/com/patrykandpatrick/vico/core/common/BoundsAware.kt rename to vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Bounded.kt index 58b73063d..c3a53f8bf 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/BoundsAware.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Bounded.kt @@ -19,7 +19,7 @@ package com.patrykandpatrick.vico.core.common import android.graphics.RectF /** Defines an abstract component that has some physical bounds. */ -public interface BoundsAware { +public interface Bounded { /** The bounds of the abstract component. */ public val bounds: RectF @@ -33,3 +33,6 @@ public interface BoundsAware { this.bounds.set(bounds) } } + +@Deprecated("Use `Bounded`.", ReplaceWith("Bounded")) +public typealias BoundsAware = Bounded diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Legend.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Legend.kt index 39fdc9241..f4e56ea00 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Legend.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Legend.kt @@ -19,7 +19,7 @@ package com.patrykandpatrick.vico.core.common import android.graphics.RectF /** Defines the functions required by the library to draw a chart legend. */ -public interface Legend : BoundsAware { +public interface Legend : Bounded { /** Returns the height of the legend. */ public fun getHeight(context: M, availableWidth: Float): Float From 32f3c04cf76a243fcaeef0256dc6314cbb00d5f2 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:11:21 +0200 Subject: [PATCH 67/97] Add missing `@suppress` --- .../main/java/com/patrykandpatrick/vico/core/common/Constants.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Constants.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Constants.kt index 47f8d4a70..dafbe400b 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Constants.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/Constants.kt @@ -23,6 +23,7 @@ internal const val ERR_REPEATING_COLLECTION_EMPTY = internal const val ELLIPSIS = "…" +/** @suppress */ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public const val NEW_PRODUCER_ERROR_MESSAGE: String = "A new `CartesianChartModelProducer` was provided. Run data updates via `tryRunTransaction` or " + From bdd33b1b71c5b1830886cd5f92350b1c6a4a3873 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Sun, 30 Jun 2024 13:07:43 +0200 Subject: [PATCH 68/97] Improve `HorizontalAxis.updateHorizontalDimensions` --- .../compose/cartesian/CartesianChartHost.kt | 12 ++--- .../core/cartesian/axis/HorizontalAxis.kt | 45 +++++++++++-------- .../views/cartesian/CartesianChartView.kt | 3 +- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/CartesianChartHost.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/CartesianChartHost.kt index f389b157f..309774290 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/CartesianChartHost.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/cartesian/CartesianChartHost.kt @@ -204,12 +204,12 @@ internal fun CartesianChartHostImpl( val markerTouchPoint = remember { mutableStateOf(null) } val measureContext = rememberCartesianMeasureContext( - scrollState.scrollEnabled, - zoomState.zoomEnabled, - canvasBounds, - horizontalLayout, - with(LocalContext.current) { ::spToPx }, - chartValues, + scrollEnabled = scrollState.scrollEnabled, + zoomEnabled = scrollState.scrollEnabled && zoomState.zoomEnabled, + canvasBounds = canvasBounds, + horizontalLayout = horizontalLayout, + spToPx = with(LocalContext.current) { ::spToPx }, + chartValues = chartValues, ) val previousMarkerTargetHashCode = remember { ValueWrapper(null) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/HorizontalAxis.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/HorizontalAxis.kt index 867d80c0e..601c11604 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/HorizontalAxis.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/axis/HorizontalAxis.kt @@ -246,15 +246,20 @@ public open class HorizontalAxis( chartValues = chartValues, verticalAxisPosition = null, ) - label - .getWidth( - context = context, - text = text, - rotationDegrees = labelRotationDegrees, - pad = true, - ) - .half - .let { horizontalDimensions.ensureValuesAtLeast(unscalableStartPadding = it) } + var unscalableStartPadding = + label + .getWidth( + context = context, + text = text, + rotationDegrees = labelRotationDegrees, + pad = true, + ) + .half + if (!context.zoomEnabled) { + unscalableStartPadding -= + (firstLabelValue - chartValues.minX) * horizontalDimensions.xSpacing + } + horizontalDimensions.ensureValuesAtLeast(unscalableStartPadding = unscalableStartPadding) } if (lastLabelValue != null) { val text = @@ -263,15 +268,19 @@ public open class HorizontalAxis( chartValues = chartValues, verticalAxisPosition = null, ) - label - .getWidth( - context = context, - text = text, - rotationDegrees = labelRotationDegrees, - pad = true, - ) - .half - .let { horizontalDimensions.ensureValuesAtLeast(unscalableEndPadding = it) } + var unscalableEndPadding = + label + .getWidth( + context = context, + text = text, + rotationDegrees = labelRotationDegrees, + pad = true, + ) + .half + if (!context.zoomEnabled) { + unscalableEndPadding -= (chartValues.maxX - lastLabelValue) * horizontalDimensions.xSpacing + } + horizontalDimensions.ensureValuesAtLeast(unscalableEndPadding = unscalableEndPadding) } } diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/CartesianChartView.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/CartesianChartView.kt index 828085c29..8f7c89dd1 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/CartesianChartView.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/cartesian/CartesianChartView.kt @@ -104,6 +104,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 newValue.postInvalidate = ::postInvalidate newValue.postInvalidateOnAnimation = ::postInvalidateOnAnimation measureContext.scrollEnabled = newValue.scrollEnabled + measureContext.zoomEnabled = measureContext.zoomEnabled && newValue.scrollEnabled } /** Houses information on the [CartesianChart]’s zoom factor. Allows for zoom customization. */ @@ -111,7 +112,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 invalidatingObservable( ZoomHandler.default(themeHandler.isChartZoomEnabled, scrollHandler.scrollEnabled) ) { _, newValue -> - measureContext.zoomEnabled = newValue.zoomEnabled + measureContext.zoomEnabled = newValue.zoomEnabled && measureContext.scrollEnabled } private val motionEventHandler = From cb033c3ce253373cc5afd318c139a14434d8adae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 18:31:59 +0000 Subject: [PATCH 69/97] Update lifecycle to v2.8.3 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index af636ed21..e8f95278b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,7 +13,7 @@ jUnit = "4.13.2" jUnitExt = "1.1.5" jupiter = "5.10.3" kotlin = "2.0.0" -lifecycle = "2.8.2" +lifecycle = "2.8.3" material = "1.12.0" mockK = "1.13.11" paparazziGradlePlugin = "1.3.4" From 0a74edafa26d7a96494f65e054e8e3043862b287 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Tue, 2 Jul 2024 19:43:55 +0200 Subject: [PATCH 70/97] Make fixes and improvements to `ShapeComponent` --- .../core/common/component/ShapeComponent.kt | 84 ++++++++----------- 1 file changed, 37 insertions(+), 47 deletions(-) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/component/ShapeComponent.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/component/ShapeComponent.kt index 891e3abf9..4fe35e92e 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/component/ShapeComponent.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/component/ShapeComponent.kt @@ -24,7 +24,6 @@ import com.patrykandpatrick.vico.core.common.Dimensions import com.patrykandpatrick.vico.core.common.DrawContext import com.patrykandpatrick.vico.core.common.alpha import com.patrykandpatrick.vico.core.common.half -import com.patrykandpatrick.vico.core.common.round import com.patrykandpatrick.vico.core.common.shader.DynamicShader import com.patrykandpatrick.vico.core.common.shape.Shape import com.patrykandpatrick.vico.core.common.withOpacity @@ -48,8 +47,12 @@ public open class ShapeComponent( public val strokeWidthDp: Float = 0f, strokeColor: Int = Color.TRANSPARENT, ) : PaintComponent(), Component { - private val paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG) - private val strokePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG) + private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { this.color = color } + private val strokePaint = + Paint(Paint.ANTI_ALIAS_FLAG).apply { + this.color = strokeColor + style = Paint.Style.STROKE + } protected val path: Path = Path() @@ -61,12 +64,7 @@ public open class ShapeComponent( Delegates.observable(strokeColor) { _, _, value -> strokePaint.color = value } init { - paint.color = color - - with(strokePaint) { - this.color = strokeColor - style = Paint.Style.STROKE - } + require(strokeWidthDp >= 0) { "`strokeWidthDp` must be nonnegative." } } override fun draw( @@ -76,48 +74,40 @@ 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 - componentShadow.maybeUpdateShadowLayer( - context = this, - paint = paint, - backgroundColor = color, - opacity = opacity, - ) - + var adjustedLeft = left + margins.getLeftDp(isLtr).pixels + var adjustedTop = top + margins.topDp.pixels + var adjustedRight = right - margins.getRightDp(isLtr).pixels + var adjustedBottom = bottom - margins.bottomDp.pixels + if (adjustedLeft >= adjustedRight || adjustedTop >= adjustedBottom) return val strokeWidth = strokeWidthDp.pixels - strokePaint.strokeWidth = strokeWidth - - fun drawShape(paint: Paint, isStroke: Boolean) { - val strokeCompensation = if (isStroke) strokeWidth.half else 0f - - shape.drawShape( - context = context, - paint = paint, - path = path, - left = - minOf(left + margins.startDp.pixels + strokeWidth.half, centerX - strokeCompensation) - .round, - top = - minOf(top + margins.topDp.pixels + strokeWidth.half, centerY - strokeCompensation) - .round, - right = - maxOf(right - margins.endDp.pixels - strokeWidth.half, centerX + strokeCompensation) - .round, - bottom = - maxOf(bottom - margins.bottomDp.pixels - strokeWidth.half, centerY + strokeCompensation) - .round, - ) + if (strokeWidth != 0f) { + adjustedLeft += strokeWidth.half + adjustedTop += strokeWidth.half + adjustedRight -= strokeWidth.half + adjustedBottom -= strokeWidth.half + if (adjustedLeft > adjustedRight || adjustedTop > adjustedBottom) return } - - paint.withOpacity(opacity) { paint -> drawShape(paint = paint, isStroke = false) } - if (strokeWidth > 0f && strokeColor.alpha > 0) drawShape(paint = strokePaint, isStroke = true) + path.rewind() + applyShader(this, left, top, right, bottom) + componentShadow.maybeUpdateShadowLayer(this, paint, color, opacity) + paint.withOpacity(opacity) { paint -> + shape.drawShape(this, paint, path, adjustedLeft, adjustedTop, adjustedRight, adjustedBottom) + } + if (strokeWidth == 0f || strokeColor.alpha == 0) return + strokePaint.strokeWidth = strokeWidth + shape.drawShape( + this, + strokePaint, + path, + adjustedLeft, + adjustedTop, + adjustedRight, + adjustedBottom, + ) } + } protected fun applyShader( context: DrawContext, From a1d10c04dbdeb4a01230fd356fee93d7b17dee02 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Wed, 3 Jul 2024 16:31:33 +0200 Subject: [PATCH 71/97] Update `version_name` --- versions.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/versions.gradle b/versions.gradle index b358940d6..cf77542e7 100644 --- a/versions.gradle +++ b/versions.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2023 by Patryk Goworowski and Patrick Michalik. + * Copyright 2024 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,7 @@ ext { library = [ groupId : "com.patrykandpatrick.vico", - version_name : "2.0.0-alpha.21", + version_name : "2.0.0-alpha.22", version_code : 1, target_sdk : 34, min_sdk : 19, From 0da91835459132486eeb5090778ffbf3f9b1df21 Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Thu, 4 Jul 2024 11:34:45 +0200 Subject: [PATCH 72/97] Fix `CartesianChartModelProducer` concurrency issues Co-authored-by: Patryk Goworowski --- .../vico/sample/showcase/charts/Chart1.kt | 2 +- .../vico/sample/showcase/charts/Chart10.kt | 2 +- .../vico/sample/showcase/charts/Chart2.kt | 2 +- .../vico/sample/showcase/charts/Chart3.kt | 2 +- .../vico/sample/showcase/charts/Chart4.kt | 2 +- .../vico/sample/showcase/charts/Chart5.kt | 2 +- .../vico/sample/showcase/charts/Chart6.kt | 2 +- .../vico/sample/showcase/charts/Chart7.kt | 2 +- .../vico/sample/showcase/charts/Chart8.kt | 2 +- .../vico/sample/showcase/charts/Chart9.kt | 2 +- .../data/CartesianChartModelProducer.kt | 92 +++++++++++-------- 11 files changed, 64 insertions(+), 48 deletions(-) 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 25e998307..0a2311d00 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 @@ -46,7 +46,7 @@ internal fun Chart1(uiFramework: UIFramework, modifier: Modifier) { val modelProducer = remember { CartesianChartModelProducer() } LaunchedEffect(Unit) { withContext(Dispatchers.Default) { - modelProducer.tryRunTransaction { + modelProducer.runTransaction { /* Learn more: https://patrykandpatrick.com/vico/wiki/cartesian-charts/layers/line-layer#data. */ lineSeries { series(x, x.map { Random.nextFloat() * 15 }) } diff --git a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart10.kt b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart10.kt index b942d58fe..ee3b74998 100644 --- a/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart10.kt +++ b/sample/src/main/java/com/patrykandpatrick/vico/sample/showcase/charts/Chart10.kt @@ -46,7 +46,7 @@ internal fun Chart10(uiFramework: UIFramework, modifier: Modifier) { LaunchedEffect(key1 = Unit) { withContext(Dispatchers.Default) { while (isActive) { - modelProducer.tryRunTransaction { + modelProducer.runTransaction { /* Learn more: https://patrykandpatrick.com/vico/wiki/cartesian-charts/layers/candlestick-layer#data. */ add(RandomCartesianModelGenerator.getRandomCandlestickLayerModelPartial()) 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 2d0cb379d..fad8ecd08 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 @@ -66,7 +66,7 @@ internal fun Chart2(uiFramework: UIFramework, modifier: Modifier) { LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { - modelProducer.tryRunTransaction { + modelProducer.runTransaction { /* Learn more: https://patrykandpatrick.com/vico/wiki/cartesian-charts/layers/column-layer#data. */ columnSeries { series(List(47) { 2 + Random.nextFloat() * 18 }) } 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 0263e8724..672392aa5 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 @@ -65,7 +65,7 @@ internal fun Chart3(uiFramework: UIFramework, modifier: Modifier) { LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { - modelProducer.tryRunTransaction { + modelProducer.runTransaction { /* Learn more: https://patrykandpatrick.com/vico/wiki/cartesian-charts/layers/line-layer#data. */ lineSeries { series(List(Defaults.ENTRY_COUNT) { Random.nextFloat() * 20 }) } 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 167b8a9df..96ca011dc 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 @@ -57,7 +57,7 @@ internal fun Chart4(uiFramework: UIFramework, modifier: Modifier) { LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { - modelProducer.tryRunTransaction { + modelProducer.runTransaction { /* Learn more: https://patrykandpatrick.com/vico/wiki/cartesian-charts/layers/column-layer#data. */ columnSeries { 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 d37bedb72..be73cfbb2 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 @@ -52,7 +52,7 @@ internal fun Chart5(uiFramework: UIFramework, modifier: Modifier) { LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { - modelProducer.tryRunTransaction { + modelProducer.runTransaction { /* Learn more: https://patrykandpatrick.com/vico/wiki/cartesian-charts/layers/column-layer#data. */ columnSeries { 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 144c9a987..350b1699f 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 @@ -63,7 +63,7 @@ internal fun Chart6(uiFramework: UIFramework, modifier: Modifier) { LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { - modelProducer.tryRunTransaction { + modelProducer.runTransaction { /* Learn more: https://patrykandpatrick.com/vico/wiki/cartesian-charts/layers/column-layer#data. */ columnSeries { 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 f527d33aa..99a2f4b3c 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 @@ -67,7 +67,7 @@ internal fun Chart7(uiFramework: UIFramework, modifier: Modifier) { LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { - modelProducer.tryRunTransaction { + modelProducer.runTransaction { /* Learn more: https://patrykandpatrick.com/vico/wiki/cartesian-charts/layers/line-layer#data. */ lineSeries { 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 8e7d2f40f..39cf17b20 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 @@ -59,7 +59,7 @@ internal fun Chart8(uiFramework: UIFramework, modifier: Modifier) { LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { - modelProducer.tryRunTransaction { + modelProducer.runTransaction { /* Learn more: https://patrykandpatrick.com/vico/wiki/cartesian-charts/layers/column-layer#data. */ columnSeries { 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 08dd2ee93..ddc782bb4 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 @@ -71,7 +71,7 @@ internal fun Chart9(uiFramework: UIFramework, modifier: Modifier) { LaunchedEffect(Unit) { withContext(Dispatchers.Default) { while (isActive) { - modelProducer.tryRunTransaction { + modelProducer.runTransaction { /* Learn more: https://patrykandpatrick.com/vico/wiki/cartesian-charts/layers/line-layer#data. */ lineSeries { series(x = x, y = x.map { Random.nextFloat() * 30 - 10 }) } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt index da19000a4..5a82c5863 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt @@ -17,37 +17,43 @@ package com.patrykandpatrick.vico.core.cartesian.data import androidx.annotation.WorkerThread +import com.patrykandpatrick.vico.core.cartesian.CartesianChart import com.patrykandpatrick.vico.core.cartesian.layer.CartesianLayer import com.patrykandpatrick.vico.core.common.data.ExtraStore import com.patrykandpatrick.vico.core.common.data.MutableExtraStore -import kotlinx.coroutines.CompletableDeferred +import java.util.concurrent.ConcurrentHashMap import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.ensureActive import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock /** Creates [CartesianChartModel]s and handles difference animations. */ -public class CartesianChartModelProducer(dispatcher: CoroutineDispatcher = Dispatchers.Default) { +public class CartesianChartModelProducer private constructor(dispatcher: CoroutineDispatcher) { private var partials = emptyList() private var extraStore = MutableExtraStore() private var cachedModel: CartesianChartModel? = null - private val mutex = Mutex() + private val updateMutex = Mutex() + private val registrationMutex = Mutex() private val coroutineScope = CoroutineScope(dispatcher) - private val updateReceivers = mutableMapOf() + private val updateReceivers = ConcurrentHashMap() + + /** Creates a [CartesianChartModelProducer]. */ + public constructor() : this(Dispatchers.Default) private fun tryUpdate( partials: List, extraStore: MutableExtraStore, ): Boolean { - if (!mutex.tryLock()) return false + if (!updateMutex.tryLock()) return false val immutablePartials = partials.toList() if (immutablePartials == this.partials && extraStore == this.extraStore) { - mutex.unlock() + updateMutex.unlock() return true } this.partials = immutablePartials @@ -56,7 +62,7 @@ public class CartesianChartModelProducer(dispatcher: CoroutineDispatcher = Dispa val receiverJobs = updateReceivers.values.map { coroutineScope.launch { it.handleUpdate() } } coroutineScope.launch { receiverJobs.joinAll() - mutex.unlock() + updateMutex.unlock() } return true } @@ -64,25 +70,23 @@ public class CartesianChartModelProducer(dispatcher: CoroutineDispatcher = Dispa private suspend fun update( partials: List, extraStore: MutableExtraStore, - ): Deferred { - mutex.lock() - val completableDeferred = CompletableDeferred() + ) { + updateMutex.lock() + registrationMutex.lock() val immutablePartials = partials.toList() if (immutablePartials == this.partials && extraStore == this.extraStore) { - mutex.unlock() - completableDeferred.complete(Unit) - return completableDeferred + updateMutex.unlock() + registrationMutex.unlock() + return } this.partials = partials.toList() this.extraStore = extraStore cachedModel = null - val receiverJobs = updateReceivers.values.map { coroutineScope.launch { it.handleUpdate() } } - coroutineScope.launch { - receiverJobs.joinAll() - mutex.unlock() - completableDeferred.complete(Unit) + coroutineScope { + updateReceivers.values.forEach { launch { it.handleUpdate() } } + registrationMutex.unlock() } - return completableDeferred + updateMutex.unlock() } private fun getModel(extraStore: ExtraStore) = @@ -130,7 +134,8 @@ public class CartesianChartModelProducer(dispatcher: CoroutineDispatcher = Dispa updateChartValues: (CartesianChartModel?) -> ChartValues, onModelCreated: (CartesianChartModel?, ChartValues) -> Unit, ) { - UpdateReceiver( + val receiver = + UpdateReceiver( cancelAnimation, startAnimation, onModelCreated, @@ -139,10 +144,10 @@ public class CartesianChartModelProducer(dispatcher: CoroutineDispatcher = Dispa transform, updateChartValues, ) - .run { - updateReceivers[key] = this - handleUpdate() - } + registrationMutex.withLock { + updateReceivers[key] = receiver + receiver.handleUpdate() + } } /** Checks if an update listener with the given key is registered. */ @@ -161,14 +166,18 @@ public class CartesianChartModelProducer(dispatcher: CoroutineDispatcher = Dispa * Creates a [Transaction], runs [block], and calls [Transaction.tryCommit], returning its output. * For suspending behavior, use [runTransaction]. */ + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated( + "Use `runTransaction`. More information: " + + "https://patrykandpatrick.com/vico/releases/2.0.0-alpha.22." + ) public fun tryRunTransaction(block: Transaction.() -> Unit): Boolean = - Transaction().also(block).tryCommit() + @Suppress("DEPRECATION") Transaction().also(block).tryCommit() - /** - * Creates a [Transaction], runs [block], and calls [Transaction.commit], returning its output. - */ - public suspend fun runTransaction(block: Transaction.() -> Unit): Deferred = + /** Creates a [Transaction], runs [block], and calls [Transaction.commit]. */ + public suspend fun runTransaction(block: Transaction.() -> Unit) { Transaction().also(block).commit() + } /** * Handles data updates. An initially empty list of [CartesianLayerModel.Partial]s is created and @@ -206,14 +215,20 @@ public class CartesianChartModelProducer(dispatcher: CoroutineDispatcher = Dispa * rejected, which occurs when there’s already an update in progress, `false` is returned. For * suspending behavior, use [commit]. */ + @Deprecated( + "Use `commit`. More information: https://patrykandpatrick.com/vico/releases/2.0.0-alpha.22." + ) public fun tryCommit(): Boolean = tryUpdate(newPartials, newExtraStore) /** - * Runs a data update. Unlike [tryCommit], 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. + * Runs a data update, returning once the update is complete. If there’s already an update in + * progress, the current coroutine is first suspended until the ongoing update’s completion. An + * update is complete once a new [CartesianChartModel] has been generated, and the + * [CartesianChart] hosts have been notified. */ - public suspend fun commit(): Deferred = update(newPartials, newExtraStore) + public suspend fun commit() { + update(newPartials, newExtraStore) + } } private inner class UpdateReceiver( @@ -239,17 +254,18 @@ public class CartesianChartModelProducer(dispatcher: CoroutineDispatcher = Dispa * Creates a [CartesianChartModelProducer], running an initial [Transaction]. [dispatcher] is * the [CoroutineDispatcher] to be used for update handling. */ - @Suppress("DeprecatedCallableAddReplaceWith") @Deprecated( - "To create a `CartesianChartModelProducer`, use the constructor. To run an initial " + - "`Transaction`, use `tryRunTransaction` immediately after the constructor invocation." + "To create a `CartesianChartModelProducer`, use the constructor. Rather than passing a " + + "`CoroutineDispatcher`, use `runTransaction` with a `CoroutineContext` that includes the " + + "`CoroutineDispatcher`. Also use `runTransaction` to run an initial `Transaction`. " + + "More information: https://patrykandpatrick.com/vico/releases/2.0.0-alpha.22." ) public fun build( dispatcher: CoroutineDispatcher = Dispatchers.Default, transaction: (Transaction.() -> Unit)? = null, ): CartesianChartModelProducer = CartesianChartModelProducer(dispatcher).apply { - if (transaction != null) tryRunTransaction(transaction) + if (transaction != null) @Suppress("DEPRECATION") tryRunTransaction(transaction) } } } From e0b1516335d45129c9a49e1f258db89442c40cfc Mon Sep 17 00:00:00 2001 From: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> Date: Thu, 4 Jul 2024 13:11:21 +0200 Subject: [PATCH 73/97] Update `CartesianChartModelProducer.unregisterFromUpdates` documentation --- .../vico/core/cartesian/data/CartesianChartModelProducer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt index 5a82c5863..ce044c71b 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/cartesian/data/CartesianChartModelProducer.kt @@ -153,7 +153,7 @@ public class CartesianChartModelProducer private constructor(dispatcher: Corouti /** Checks if an update listener with the given key is registered. */ public fun isRegistered(key: Any): Boolean = updateReceivers.containsKey(key) - /** Unregisters the update listener associated with the given [key]. */ + /** Unregisters the update listener associated with the given key. */ public fun unregisterFromUpdates(key: Any) { updateReceivers.remove(key) } From 831308d7f3b4c20104b21a939c77eb6b7ee1807c Mon Sep 17 00:00:00 2001 From: Patryk Goworowski Date: Thu, 4 Jul 2024 17:00:44 +0200 Subject: [PATCH 74/97] Rename `Shape.drawShape` to `Shape.draw` and deprecate the old function Co-authored-by: Patrick Michalik <120058021+patrickmichalik@users.noreply.github.com> --- .../vico/sample/previews/ShapePreviews.kt | 2 +- .../vico/compose/common/shape/Shape.kt | 18 ++++- .../core/common/component/ShapeComponent.kt | 12 +--- .../vico/core/common/shape/CorneredShape.kt | 18 ++++- .../vico/core/common/shape/DashedShape.kt | 22 +++++- .../core/common/shape/MarkerCorneredShape.kt | 20 +++++- .../vico/core/common/shape/Shape.kt | 69 ++++++++++++++++++- .../vico/views/common/shape/ShapeDrawable.kt | 2 +- 8 files changed, 141 insertions(+), 22 deletions(-) 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 b83b18b01..8d97becd2 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 @@ -64,7 +64,7 @@ private fun PreviewShape(shape: Shape) { Text(text = "Canvas") Canvas(modifier = Modifier.fillMaxWidth().height(50.dp)) { - shape.drawShape( + shape.draw( context = drawContext(drawContext.canvas.nativeCanvas), paint = paint, path = path, diff --git a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/common/shape/Shape.kt b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/common/shape/Shape.kt index 2a9cb3938..4b697785b 100644 --- a/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/common/shape/Shape.kt +++ b/vico/compose/src/main/java/com/patrykandpatrick/vico/compose/common/shape/Shape.kt @@ -71,7 +71,7 @@ public fun androidx.compose.ui.graphics.Shape.toVicoShape(): Shape = private val radii by lazy { FloatArray(RADII_ARRAY_SIZE) } private val matrix: Matrix by lazy { Matrix() } - override fun drawShape( + override fun draw( context: DrawContext, paint: Paint, path: Path, @@ -104,6 +104,22 @@ public fun androidx.compose.ui.graphics.Shape.toVicoShape(): Shape = } context.canvas.drawPath(path, paint) } + + @Deprecated( + "Use `draw`.", + replaceWith = ReplaceWith("draw(context, paint, path, left, top, right, bottom)"), + ) + override fun drawShape( + context: DrawContext, + paint: Paint, + path: Path, + left: Float, + top: Float, + right: Float, + bottom: Float, + ) { + draw(context, paint, path, left, top, right, bottom) + } } /** Converts this [CorneredShape] to an instance of [androidx.compose.ui.graphics.Shape]. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/component/ShapeComponent.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/component/ShapeComponent.kt index 4fe35e92e..d4af94e3c 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/component/ShapeComponent.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/component/ShapeComponent.kt @@ -93,19 +93,11 @@ public open class ShapeComponent( applyShader(this, left, top, right, bottom) componentShadow.maybeUpdateShadowLayer(this, paint, color, opacity) paint.withOpacity(opacity) { paint -> - shape.drawShape(this, paint, path, adjustedLeft, adjustedTop, adjustedRight, adjustedBottom) + shape.draw(this, paint, path, adjustedLeft, adjustedTop, adjustedRight, adjustedBottom) } if (strokeWidth == 0f || strokeColor.alpha == 0) return strokePaint.strokeWidth = strokeWidth - shape.drawShape( - this, - strokePaint, - path, - adjustedLeft, - adjustedTop, - adjustedRight, - adjustedBottom, - ) + shape.draw(this, strokePaint, path, adjustedLeft, adjustedTop, adjustedRight, adjustedBottom) } } diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/CorneredShape.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/CorneredShape.kt index 754a8de31..09fb2806d 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/CorneredShape.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/CorneredShape.kt @@ -60,7 +60,7 @@ public open class CorneredShape( ) } - override fun drawShape( + override fun draw( context: DrawContext, paint: Paint, path: Path, @@ -73,6 +73,22 @@ public open class CorneredShape( context.canvas.drawPath(path, paint) } + @Deprecated( + "Use `draw`.", + replaceWith = ReplaceWith("draw(context, paint, path, left, top, right, bottom)"), + ) + override fun drawShape( + context: DrawContext, + paint: Paint, + path: Path, + left: Float, + top: Float, + right: Float, + bottom: Float, + ) { + draw(context, paint, path, left, top, right, bottom) + } + protected open fun createPath( context: DrawContext, path: Path, diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/DashedShape.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/DashedShape.kt index aeba88ec5..bccb59559 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/DashedShape.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/DashedShape.kt @@ -40,7 +40,7 @@ public class DashedShape( private var drawDashLength = dashLengthDp private var drawGapLength = gapLengthDp - override fun drawShape( + override fun draw( context: DrawContext, paint: Paint, path: Path, @@ -56,6 +56,22 @@ public class DashedShape( } } + @Deprecated( + "Use `draw`.", + replaceWith = ReplaceWith("draw(context, paint, path, left, top, right, bottom)"), + ) + override fun drawShape( + context: DrawContext, + paint: Paint, + path: Path, + left: Float, + top: Float, + right: Float, + bottom: Float, + ) { + draw(context, paint, path, left, top, right, bottom) + } + private fun drawHorizontalDashes( context: DrawContext, paint: Paint, @@ -73,7 +89,7 @@ public class DashedShape( drawnLength += if (index % 2 == 0) { path.reset() - shape.drawShape( + shape.draw( context = context, paint = paint, path = path, @@ -107,7 +123,7 @@ public class DashedShape( drawnLength += if (index % 2 == 0) { path.reset() - shape.drawShape( + shape.draw( context = context, paint = paint, path = path, diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/MarkerCorneredShape.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/MarkerCorneredShape.kt index addd109b6..f759ad2ba 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/MarkerCorneredShape.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/MarkerCorneredShape.kt @@ -57,7 +57,7 @@ public open class MarkerCorneredShape( tickSizeDp = tickSizeDp, ) - override fun drawShape( + override fun draw( context: DrawContext, paint: Paint, path: Path, @@ -110,10 +110,26 @@ public open class MarkerCorneredShape( path.close() context.canvas.drawPath(path, paint) } else { - super.drawShape(context, paint, path, left, top, right, bottom) + super.draw(context, paint, path, left, top, right, bottom) } } + @Deprecated( + "Use `draw`.", + replaceWith = ReplaceWith("draw(context, paint, path, left, top, right, bottom)"), + ) + override fun drawShape( + context: DrawContext, + paint: Paint, + path: Path, + left: Float, + top: Float, + right: Float, + bottom: Float, + ) { + draw(context, paint, path, left, top, right, bottom) + } + /** Specifies the position of a [MarkerCorneredShape]’s tick. */ public enum class TickPosition { /** Positions the tick at the top of the [MarkerCorneredShape]. */ diff --git a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/Shape.kt b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/Shape.kt index 3d5b8140d..3478b60f0 100644 --- a/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/Shape.kt +++ b/vico/core/src/main/java/com/patrykandpatrick/vico/core/common/shape/Shape.kt @@ -40,6 +40,37 @@ public interface Shape { * @param bottom the _y_ coordinate of the bottom edge of the bounds in which the shape should be * drawn. */ + @Suppress("DEPRECATION") + public fun draw( + context: DrawContext, + paint: Paint, + path: Path, + left: Float, + top: Float, + right: Float, + bottom: Float, + ) { + drawShape(context, paint, path, left, top, right, bottom) + } + + /** + * Draws the [Shape] on the canvas. + * + * @param context holds environment data. + * @param paint the [Paint] used to draw the shape. + * @param path the [Path] defining the shape. + * @param left the _x_ coordinate of the left edge of the bounds in which the shape should be + * drawn. + * @param top the _y_ coordinate of the top edge of the bounds in which the shape should be drawn. + * @param right the _x_ coordinate of the right edge of the bounds in which the shape should be + * drawn. + * @param bottom the _y_ coordinate of the bottom edge of the bounds in which the shape should be + * drawn. + */ + @Deprecated( + message = "Use `draw`.", + replaceWith = ReplaceWith("draw(context, paint, path, left, top, right, bottom)"), + ) public fun drawShape( context: DrawContext, paint: Paint, @@ -54,7 +85,7 @@ public interface Shape { /** A rectangle with sharp corners. */ public val Rectangle: Shape = object : Shape { - override fun drawShape( + override fun draw( context: DrawContext, paint: Paint, path: Path, @@ -70,6 +101,22 @@ public interface Shape { path.close() context.canvas.drawPath(path, paint) } + + @Deprecated( + "Use `draw`.", + replaceWith = ReplaceWith("draw(context, paint, path, left, top, right, bottom)"), + ) + override fun drawShape( + context: DrawContext, + paint: Paint, + path: Path, + left: Float, + top: Float, + right: Float, + bottom: Float, + ) { + draw(context, paint, path, left, top, right, bottom) + } } /** A shape whose each corner is fully rounded. */ @@ -187,7 +234,7 @@ public interface Shape { drawable.intrinsicWidth.coerceAtLeast(1) / drawable.intrinsicHeight.coerceAtLeast(1).toFloat() - override fun drawShape( + override fun draw( context: DrawContext, paint: Paint, path: Path, @@ -233,7 +280,7 @@ public interface Shape { otherShape ?: return if (bottom - otherComponentTop > 0 && right - otherComponentLeft > 0) { - otherShape.drawShape( + otherShape.draw( context = context, paint = paint, path = path, @@ -244,6 +291,22 @@ public interface Shape { ) } } + + @Deprecated( + "Use `draw`.", + replaceWith = ReplaceWith("draw(context, paint, path, left, top, right, bottom)"), + ) + override fun drawShape( + context: DrawContext, + paint: Paint, + path: Path, + left: Float, + top: Float, + right: Float, + bottom: Float, + ) { + draw(context, paint, path, left, top, right, bottom) + } } } } diff --git a/vico/views/src/main/java/com/patrykandpatrick/vico/views/common/shape/ShapeDrawable.kt b/vico/views/src/main/java/com/patrykandpatrick/vico/views/common/shape/ShapeDrawable.kt index 93a6f97e9..418793ca1 100644 --- a/vico/views/src/main/java/com/patrykandpatrick/vico/views/common/shape/ShapeDrawable.kt +++ b/vico/views/src/main/java/com/patrykandpatrick/vico/views/common/shape/ShapeDrawable.kt @@ -73,7 +73,7 @@ public class ShapeDrawable( } override fun draw(canvas: Canvas) { - shape.drawShape( + shape.draw( drawContext(canvas = canvas, density = density, isLtr = isLtr()), paint = paint, path = path, From 688aee4d0e43eb5412b7970f931b500e4deeac98 Mon Sep 17 00:00:00 2001 From: Patryk Goworowski Date: Thu, 4 Jul 2024 17:01:54 +0200 Subject: [PATCH 75/97] Update `Project.xml` and `compiler.xml` --- .idea/codeStyles/Project.xml | 4 +++- .idea/compiler.xml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 962b221c2..7d9505635 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -127,8 +127,10 @@