22 * Copyright (c) 2025 Nishant Mishra
33 *
44 * You should have received a copy of the GNU General Public License
5- * along with this program. If not, see <https://www.gnu.org/licenses/>.
5+ * along with this program.
6+ * If not, see <https://www.gnu.org/licenses/>.
67 */
78
89package org.nsh07.pomodoro.ui.statsScreen
@@ -34,6 +35,7 @@ import com.patrykandpatrick.vico.compose.common.MarkerCornerBasedShape
3435import com.patrykandpatrick.vico.compose.common.ProvideVicoTheme
3536import com.patrykandpatrick.vico.compose.common.component.rememberLineComponent
3637import com.patrykandpatrick.vico.compose.m3.common.rememberM3VicoTheme
38+ import kotlin.math.roundToInt
3739
3840@OptIn(ExperimentalMaterial3ExpressiveApi ::class )
3941@Composable
@@ -51,46 +53,43 @@ internal fun TimeColumnChart(
5153) {
5254 if (dataValues.isEmpty()) return
5355
54- val radius = with (LocalDensity .current) {
55- (thickness / 2 ).toPx()
56- }
57-
5856 var chartSize by remember { mutableStateOf(IntSize .Zero ) }
5957 val density = LocalDensity .current
60-
6158 val primaryColor = MaterialTheme .colorScheme.primary
6259
60+ // Use a dedicated scrollState variable to track horizontal movement
61+ val vicoScrollState = rememberVicoScrollState(initialScroll = Scroll .Absolute .End )
62+
6363 ProvideVicoTheme (rememberM3VicoTheme()) {
6464 CartesianChartHost (
65- chart =
66- rememberCartesianChart(
67- rememberColumnCartesianLayer(
68- ColumnCartesianLayer .ColumnProvider .series(
69- dataValues.indices.map { _ ->
70- rememberLineComponent(
71- fill = Fill (primaryColor),
72- thickness = thickness,
73- shape = MarkerCornerBasedShape (RoundedCornerShape (16 .dp))
74- )
75- }
76- ),
77- columnCollectionSpacing = columnCollectionSpacing
78- ),
79- startAxis = VerticalAxis .rememberStart(
80- valueFormatter = yValueFormatter
65+ chart = rememberCartesianChart(
66+ rememberColumnCartesianLayer(
67+ ColumnCartesianLayer .ColumnProvider .series(
68+ dataValues.indices.map { _ ->
69+ rememberLineComponent(
70+ fill = Fill (primaryColor),
71+ thickness = thickness,
72+ shape = MarkerCornerBasedShape (RoundedCornerShape (16 .dp))
73+ )
74+ }
8175 ),
82- bottomAxis = HorizontalAxis .rememberBottom(
83- guideline = rememberLineComponent(Fill .Transparent ),
84- valueFormatter = xValueFormatter
85- )
76+ columnCollectionSpacing = columnCollectionSpacing
8677 ),
78+ startAxis = VerticalAxis .rememberStart(
79+ valueFormatter = yValueFormatter
80+ ),
81+ bottomAxis = HorizontalAxis .rememberBottom(
82+ guideline = rememberLineComponent(Fill .Transparent ),
83+ valueFormatter = xValueFormatter
84+ )
85+ ),
8786 modelProducer = modelProducer,
8887 zoomState = rememberVicoZoomState(
8988 zoomEnabled = false ,
9089 initialZoom = Zoom .fixed(),
9190 minZoom = Zoom .min(Zoom .Content , Zoom .fixed())
9291 ),
93- scrollState = rememberVicoScrollState(initialScroll = Scroll . Absolute . End ) ,
92+ scrollState = vicoScrollState ,
9493 animationSpec = animationSpec,
9594 modifier = modifier
9695 .onSizeChanged { chartSize = it }
@@ -104,28 +103,35 @@ internal fun TimeColumnChart(
104103 val endPadding = with (density) { 16 .dp.toPx() }
105104 val bottomAxisHeight = with (density) { 32 .dp.toPx() }
106105 val topPadding = with (density) { 8 .dp.toPx() }
107- val availableWidth = chartWidth - startAxisWidth - endPadding
106+
108107 val availableHeight = chartHeight - bottomAxisHeight - topPadding
109108
110109 val columnWidth = with (density) { thickness.toPx() }
111110 val spacing = with (density) { columnCollectionSpacing.toPx() }
112111 val totalColumnWidth = columnWidth + spacing
113112
114- val clickX = offset.x - startAxisWidth
113+ // Correcting coordinate: Add current scroll offset to the tap position relative to the chart start
114+ val scrollOffset = vicoScrollState.value
115+ val relativeTapX = offset.x - startAxisWidth
116+ val absoluteX = relativeTapX + scrollOffset
115117 val clickY = offset.y - topPadding
116118
117- if (clickX in 0.0f .. availableWidth && clickY >= 0 && clickY <= availableHeight) {
118- val columnIndex = (clickX / totalColumnWidth).toInt()
119- if (columnIndex >= 0 && columnIndex < dataValues.size) {
120- val maxValue = dataValues.maxOrNull() ? : 1f
121- val barHeightRatio =
122- if (maxValue > 0 ) dataValues[columnIndex] / maxValue else 0f
123- val barHeight = availableHeight * barHeightRatio
124- val barTop = availableHeight - barHeight
119+ // Ensure tap is within the horizontal bounds of the chart content
120+ if (offset.x in startAxisWidth.. (chartWidth - endPadding) &&
121+ clickY in 0f .. availableHeight) {
122+
123+ // Using integer division for more predictable column mapping
124+ val columnIndex = (absoluteX / totalColumnWidth).toInt()
125+ .coerceIn(0 , dataValues.size - 1 )
125126
126- if (clickY in barTop.. availableHeight) {
127- onColumnClick(columnIndex)
128- }
127+ val maxValue = dataValues.maxOrNull() ? : 1f
128+ val barHeightRatio = if (maxValue > 0 ) dataValues[columnIndex] / maxValue else 0f
129+ val barHeight = availableHeight * barHeightRatio
130+ val barTop = availableHeight - barHeight
131+
132+ // Trigger callback if the tap falls within the column's vertical area
133+ if (clickY in barTop.. availableHeight) {
134+ onColumnClick(columnIndex)
129135 }
130136 }
131137 }
@@ -186,4 +192,4 @@ internal fun TimeLineChart(
186192 modifier = modifier
187193 )
188194 }
189- }
195+ }
0 commit comments