feat(graphs): add markers to graphs that appear on click

#118
This commit is contained in:
Nishant Mishra
2025-11-15 18:26:42 +05:30
parent 56ef71aafa
commit b7d44a279c
2 changed files with 98 additions and 6 deletions

View File

@@ -1,20 +1,34 @@
/*
* 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/>.
* This file is part of Tomato - a minimalist pomodoro timer for Android.
*
* Tomato is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Tomato is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tomato.
* If not, see <https://www.gnu.org/licenses/>.
*/
package org.nsh07.pomodoro.ui.statsScreen
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.foundation.layout.height
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.motionScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@@ -37,10 +51,16 @@ import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer
import com.patrykandpatrick.vico.core.cartesian.data.CartesianValueFormatter
import com.patrykandpatrick.vico.core.cartesian.data.columnSeries
import com.patrykandpatrick.vico.core.cartesian.layer.ColumnCartesianLayer
import com.patrykandpatrick.vico.core.cartesian.marker.ColumnCartesianLayerMarkerTarget
import com.patrykandpatrick.vico.core.cartesian.marker.DefaultCartesianMarker
import com.patrykandpatrick.vico.core.common.Fill
import com.patrykandpatrick.vico.core.common.Insets
import com.patrykandpatrick.vico.core.common.component.ShapeComponent
import com.patrykandpatrick.vico.core.common.component.TextComponent
import com.patrykandpatrick.vico.core.common.shape.CorneredShape
import org.nsh07.pomodoro.ui.theme.TomatoTheme
import org.nsh07.pomodoro.utils.millisecondsToHours
import org.nsh07.pomodoro.utils.millisecondsToHoursMinutes
import org.nsh07.pomodoro.utils.millisecondsToMinutes
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@@ -58,6 +78,18 @@ fun TimeColumnChart(
millisecondsToMinutes(value.toLong())
}
},
markerValueFormatter: DefaultCartesianMarker.ValueFormatter = DefaultCartesianMarker.ValueFormatter { _, targets ->
val first = targets.firstOrNull()
val value = if (first is ColumnCartesianLayerMarkerTarget) {
first.columns.sumOf { it.entry.y.toLong() }
} else 0L
if (value >= 60 * 60 * 1000) {
millisecondsToHoursMinutes(value)
} else {
millisecondsToMinutes(value)
}
},
animationSpec: AnimationSpec<Float>? = motionScheme.slowEffectsSpec()
) {
ProvideVicoTheme(rememberM3VicoTheme()) {
@@ -88,6 +120,20 @@ fun TimeColumnChart(
guideline = rememberLineComponent(Fill.Transparent),
valueFormatter = xValueFormatter
),
marker = DefaultCartesianMarker(
TextComponent(
color = colorScheme.surface.toArgb(),
background = ShapeComponent(
fill = fill(colorScheme.onSurface),
shape = CorneredShape.Pill
),
textSizeSp = typography.labelSmall.fontSize.value,
lineHeightSp = typography.labelSmall.fontSize.value,
padding = Insets(verticalDp = 4f, horizontalDp = 8f),
margins = Insets(bottomDp = 2f)
),
valueFormatter = markerValueFormatter
),
fadingEdges = FadingEdges()
),
modelProducer = modelProducer,
@@ -97,7 +143,7 @@ fun TimeColumnChart(
minZoom = Zoom.min(Zoom.Content, Zoom.fixed())
),
animationSpec = animationSpec,
modifier = modifier,
modifier = modifier.height(224.dp),
)
}
}

View File

@@ -1,15 +1,28 @@
/*
* 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/>.
* This file is part of Tomato - a minimalist pomodoro timer for Android.
*
* Tomato is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Tomato is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tomato.
* If not, see <https://www.gnu.org/licenses/>.
*/
package org.nsh07.pomodoro.ui.statsScreen
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.foundation.layout.height
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.motionScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -41,10 +54,17 @@ import com.patrykandpatrick.vico.core.cartesian.data.CartesianValueFormatter
import com.patrykandpatrick.vico.core.cartesian.data.lineSeries
import com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer
import com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer.LineFill.Companion.single
import com.patrykandpatrick.vico.core.cartesian.marker.DefaultCartesianMarker
import com.patrykandpatrick.vico.core.cartesian.marker.LineCartesianLayerMarkerTarget
import com.patrykandpatrick.vico.core.common.Fill
import com.patrykandpatrick.vico.core.common.Insets
import com.patrykandpatrick.vico.core.common.component.ShapeComponent
import com.patrykandpatrick.vico.core.common.component.TextComponent
import com.patrykandpatrick.vico.core.common.shader.ShaderProvider
import com.patrykandpatrick.vico.core.common.shape.CorneredShape
import org.nsh07.pomodoro.ui.theme.TomatoTheme
import org.nsh07.pomodoro.utils.millisecondsToHours
import org.nsh07.pomodoro.utils.millisecondsToHoursMinutes
import org.nsh07.pomodoro.utils.millisecondsToMinutes
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@@ -62,6 +82,18 @@ fun TimeLineChart(
millisecondsToMinutes(value.toLong())
}
},
markerValueFormatter: DefaultCartesianMarker.ValueFormatter = DefaultCartesianMarker.ValueFormatter { _, targets ->
val first = targets.firstOrNull()
val value = if (first is LineCartesianLayerMarkerTarget) {
first.points.sumOf { it.entry.y.toLong() }
} else 0L
if (value >= 60 * 60 * 1000) {
millisecondsToHoursMinutes(value)
} else {
millisecondsToMinutes(value)
}
},
animationSpec: AnimationSpec<Float>? = motionScheme.slowEffectsSpec()
) {
ProvideVicoTheme(rememberM3VicoTheme()) {
@@ -102,6 +134,20 @@ fun TimeLineChart(
guideline = rememberLineComponent(Fill.Transparent),
valueFormatter = xValueFormatter
),
marker = DefaultCartesianMarker(
TextComponent(
color = colorScheme.surface.toArgb(),
background = ShapeComponent(
fill = fill(colorScheme.onSurface),
shape = CorneredShape.Pill
),
textSizeSp = typography.labelSmall.fontSize.value,
lineHeightSp = typography.labelSmall.fontSize.value,
padding = Insets(verticalDp = 4f, horizontalDp = 8f),
margins = Insets(bottomDp = 2f)
),
valueFormatter = markerValueFormatter
),
fadingEdges = FadingEdges()
),
modelProducer = modelProducer,
@@ -111,7 +157,7 @@ fun TimeLineChart(
minZoom = Zoom.min(Zoom.Content, Zoom.fixed())
),
animationSpec = animationSpec,
modifier = modifier,
modifier = modifier.height(224.dp),
)
}
}