Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 47 additions & 41 deletions Reef/src/main/java/dev/pranav/reef/util/TimeColumnChart.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
* Copyright (c) 2025 Nishant Mishra
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/

package org.nsh07.pomodoro.ui.statsScreen
Expand Down Expand Up @@ -34,6 +35,7 @@ import com.patrykandpatrick.vico.compose.common.MarkerCornerBasedShape
import com.patrykandpatrick.vico.compose.common.ProvideVicoTheme
import com.patrykandpatrick.vico.compose.common.component.rememberLineComponent
import com.patrykandpatrick.vico.compose.m3.common.rememberM3VicoTheme
import kotlin.math.roundToInt

@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
Expand All @@ -51,46 +53,43 @@ internal fun TimeColumnChart(
) {
if (dataValues.isEmpty()) return

val radius = with(LocalDensity.current) {
(thickness / 2).toPx()
}

var chartSize by remember { mutableStateOf(IntSize.Zero) }
val density = LocalDensity.current

val primaryColor = MaterialTheme.colorScheme.primary

// Use a dedicated scrollState variable to track horizontal movement
val vicoScrollState = rememberVicoScrollState(initialScroll = Scroll.Absolute.End)

ProvideVicoTheme(rememberM3VicoTheme()) {
CartesianChartHost(
chart =
rememberCartesianChart(
rememberColumnCartesianLayer(
ColumnCartesianLayer.ColumnProvider.series(
dataValues.indices.map { _ ->
rememberLineComponent(
fill = Fill(primaryColor),
thickness = thickness,
shape = MarkerCornerBasedShape(RoundedCornerShape(16.dp))
)
}
),
columnCollectionSpacing = columnCollectionSpacing
),
startAxis = VerticalAxis.rememberStart(
valueFormatter = yValueFormatter
chart = rememberCartesianChart(
rememberColumnCartesianLayer(
ColumnCartesianLayer.ColumnProvider.series(
dataValues.indices.map { _ ->
rememberLineComponent(
fill = Fill(primaryColor),
thickness = thickness,
shape = MarkerCornerBasedShape(RoundedCornerShape(16.dp))
)
}
),
bottomAxis = HorizontalAxis.rememberBottom(
guideline = rememberLineComponent(Fill.Transparent),
valueFormatter = xValueFormatter
)
columnCollectionSpacing = columnCollectionSpacing
),
startAxis = VerticalAxis.rememberStart(
valueFormatter = yValueFormatter
),
bottomAxis = HorizontalAxis.rememberBottom(
guideline = rememberLineComponent(Fill.Transparent),
valueFormatter = xValueFormatter
)
),
modelProducer = modelProducer,
zoomState = rememberVicoZoomState(
zoomEnabled = false,
initialZoom = Zoom.fixed(),
minZoom = Zoom.min(Zoom.Content, Zoom.fixed())
),
scrollState = rememberVicoScrollState(initialScroll = Scroll.Absolute.End),
scrollState = vicoScrollState,
animationSpec = animationSpec,
modifier = modifier
.onSizeChanged { chartSize = it }
Expand All @@ -104,28 +103,35 @@ internal fun TimeColumnChart(
val endPadding = with(density) { 16.dp.toPx() }
val bottomAxisHeight = with(density) { 32.dp.toPx() }
val topPadding = with(density) { 8.dp.toPx() }
val availableWidth = chartWidth - startAxisWidth - endPadding

val availableHeight = chartHeight - bottomAxisHeight - topPadding

val columnWidth = with(density) { thickness.toPx() }
val spacing = with(density) { columnCollectionSpacing.toPx() }
val totalColumnWidth = columnWidth + spacing

val clickX = offset.x - startAxisWidth
// Correcting coordinate: Add current scroll offset to the tap position relative to the chart start
val scrollOffset = vicoScrollState.value
val relativeTapX = offset.x - startAxisWidth
val absoluteX = relativeTapX + scrollOffset
val clickY = offset.y - topPadding

if (clickX in 0.0f..availableWidth && clickY >= 0 && clickY <= availableHeight) {
val columnIndex = (clickX / totalColumnWidth).toInt()
if (columnIndex >= 0 && columnIndex < dataValues.size) {
val maxValue = dataValues.maxOrNull() ?: 1f
val barHeightRatio =
if (maxValue > 0) dataValues[columnIndex] / maxValue else 0f
val barHeight = availableHeight * barHeightRatio
val barTop = availableHeight - barHeight
// Ensure tap is within the horizontal bounds of the chart content
if (offset.x in startAxisWidth..(chartWidth - endPadding) &&
clickY in 0f..availableHeight) {

// Using integer division for more predictable column mapping
val columnIndex = (absoluteX / totalColumnWidth).toInt()
.coerceIn(0, dataValues.size - 1)

if (clickY in barTop..availableHeight) {
onColumnClick(columnIndex)
}
val maxValue = dataValues.maxOrNull() ?: 1f
val barHeightRatio = if (maxValue > 0) dataValues[columnIndex] / maxValue else 0f
val barHeight = availableHeight * barHeightRatio
val barTop = availableHeight - barHeight

// Trigger callback if the tap falls within the column's vertical area
if (clickY in barTop..availableHeight) {
onColumnClick(columnIndex)
}
}
}
Expand Down Expand Up @@ -186,4 +192,4 @@ internal fun TimeLineChart(
modifier = modifier
)
}
}
}
Loading