Skip to content

Commit b7d44a2

Browse files
committed
feat(graphs): add markers to graphs that appear on click
#118
1 parent 56ef71a commit b7d44a2

File tree

2 files changed

+98
-6
lines changed

2 files changed

+98
-6
lines changed

app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/TimeColumnChart.kt

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,34 @@
11
/*
22
* Copyright (c) 2025 Nishant Mishra
33
*
4-
* 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/>.
4+
* This file is part of Tomato - a minimalist pomodoro timer for Android.
5+
*
6+
* Tomato is free software: you can redistribute it and/or modify it under the terms of the GNU
7+
* General Public License as published by the Free Software Foundation, either version 3 of the
8+
* License, or (at your option) any later version.
9+
*
10+
* Tomato is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
11+
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
12+
* Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along with Tomato.
15+
* If not, see <https://www.gnu.org/licenses/>.
616
*/
717

818
package org.nsh07.pomodoro.ui.statsScreen
919

1020
import androidx.compose.animation.core.AnimationSpec
21+
import androidx.compose.foundation.layout.height
1122
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
23+
import androidx.compose.material3.MaterialTheme.colorScheme
1224
import androidx.compose.material3.MaterialTheme.motionScheme
25+
import androidx.compose.material3.MaterialTheme.typography
1326
import androidx.compose.material3.Surface
1427
import androidx.compose.runtime.Composable
1528
import androidx.compose.runtime.LaunchedEffect
1629
import androidx.compose.runtime.remember
1730
import androidx.compose.ui.Modifier
31+
import androidx.compose.ui.graphics.toArgb
1832
import androidx.compose.ui.tooling.preview.Preview
1933
import androidx.compose.ui.unit.Dp
2034
import androidx.compose.ui.unit.dp
@@ -37,10 +51,16 @@ import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer
3751
import com.patrykandpatrick.vico.core.cartesian.data.CartesianValueFormatter
3852
import com.patrykandpatrick.vico.core.cartesian.data.columnSeries
3953
import com.patrykandpatrick.vico.core.cartesian.layer.ColumnCartesianLayer
54+
import com.patrykandpatrick.vico.core.cartesian.marker.ColumnCartesianLayerMarkerTarget
55+
import com.patrykandpatrick.vico.core.cartesian.marker.DefaultCartesianMarker
4056
import com.patrykandpatrick.vico.core.common.Fill
57+
import com.patrykandpatrick.vico.core.common.Insets
58+
import com.patrykandpatrick.vico.core.common.component.ShapeComponent
59+
import com.patrykandpatrick.vico.core.common.component.TextComponent
4160
import com.patrykandpatrick.vico.core.common.shape.CorneredShape
4261
import org.nsh07.pomodoro.ui.theme.TomatoTheme
4362
import org.nsh07.pomodoro.utils.millisecondsToHours
63+
import org.nsh07.pomodoro.utils.millisecondsToHoursMinutes
4464
import org.nsh07.pomodoro.utils.millisecondsToMinutes
4565

4666
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@@ -58,6 +78,18 @@ fun TimeColumnChart(
5878
millisecondsToMinutes(value.toLong())
5979
}
6080
},
81+
markerValueFormatter: DefaultCartesianMarker.ValueFormatter = DefaultCartesianMarker.ValueFormatter { _, targets ->
82+
val first = targets.firstOrNull()
83+
val value = if (first is ColumnCartesianLayerMarkerTarget) {
84+
first.columns.sumOf { it.entry.y.toLong() }
85+
} else 0L
86+
87+
if (value >= 60 * 60 * 1000) {
88+
millisecondsToHoursMinutes(value)
89+
} else {
90+
millisecondsToMinutes(value)
91+
}
92+
},
6193
animationSpec: AnimationSpec<Float>? = motionScheme.slowEffectsSpec()
6294
) {
6395
ProvideVicoTheme(rememberM3VicoTheme()) {
@@ -88,6 +120,20 @@ fun TimeColumnChart(
88120
guideline = rememberLineComponent(Fill.Transparent),
89121
valueFormatter = xValueFormatter
90122
),
123+
marker = DefaultCartesianMarker(
124+
TextComponent(
125+
color = colorScheme.surface.toArgb(),
126+
background = ShapeComponent(
127+
fill = fill(colorScheme.onSurface),
128+
shape = CorneredShape.Pill
129+
),
130+
textSizeSp = typography.labelSmall.fontSize.value,
131+
lineHeightSp = typography.labelSmall.fontSize.value,
132+
padding = Insets(verticalDp = 4f, horizontalDp = 8f),
133+
margins = Insets(bottomDp = 2f)
134+
),
135+
valueFormatter = markerValueFormatter
136+
),
91137
fadingEdges = FadingEdges()
92138
),
93139
modelProducer = modelProducer,
@@ -97,7 +143,7 @@ fun TimeColumnChart(
97143
minZoom = Zoom.min(Zoom.Content, Zoom.fixed())
98144
),
99145
animationSpec = animationSpec,
100-
modifier = modifier,
146+
modifier = modifier.height(224.dp),
101147
)
102148
}
103149
}

app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/TimeLineChart.kt

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
11
/*
22
* Copyright (c) 2025 Nishant Mishra
33
*
4-
* 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/>.
4+
* This file is part of Tomato - a minimalist pomodoro timer for Android.
5+
*
6+
* Tomato is free software: you can redistribute it and/or modify it under the terms of the GNU
7+
* General Public License as published by the Free Software Foundation, either version 3 of the
8+
* License, or (at your option) any later version.
9+
*
10+
* Tomato is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
11+
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
12+
* Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along with Tomato.
15+
* If not, see <https://www.gnu.org/licenses/>.
616
*/
717

818
package org.nsh07.pomodoro.ui.statsScreen
919

1020
import androidx.compose.animation.core.AnimationSpec
21+
import androidx.compose.foundation.layout.height
1122
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
23+
import androidx.compose.material3.MaterialTheme.colorScheme
1224
import androidx.compose.material3.MaterialTheme.motionScheme
25+
import androidx.compose.material3.MaterialTheme.typography
1326
import androidx.compose.material3.Surface
1427
import androidx.compose.runtime.Composable
1528
import androidx.compose.runtime.LaunchedEffect
@@ -41,10 +54,17 @@ import com.patrykandpatrick.vico.core.cartesian.data.CartesianValueFormatter
4154
import com.patrykandpatrick.vico.core.cartesian.data.lineSeries
4255
import com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer
4356
import com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer.LineFill.Companion.single
57+
import com.patrykandpatrick.vico.core.cartesian.marker.DefaultCartesianMarker
58+
import com.patrykandpatrick.vico.core.cartesian.marker.LineCartesianLayerMarkerTarget
4459
import com.patrykandpatrick.vico.core.common.Fill
60+
import com.patrykandpatrick.vico.core.common.Insets
61+
import com.patrykandpatrick.vico.core.common.component.ShapeComponent
62+
import com.patrykandpatrick.vico.core.common.component.TextComponent
4563
import com.patrykandpatrick.vico.core.common.shader.ShaderProvider
64+
import com.patrykandpatrick.vico.core.common.shape.CorneredShape
4665
import org.nsh07.pomodoro.ui.theme.TomatoTheme
4766
import org.nsh07.pomodoro.utils.millisecondsToHours
67+
import org.nsh07.pomodoro.utils.millisecondsToHoursMinutes
4868
import org.nsh07.pomodoro.utils.millisecondsToMinutes
4969

5070
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@@ -62,6 +82,18 @@ fun TimeLineChart(
6282
millisecondsToMinutes(value.toLong())
6383
}
6484
},
85+
markerValueFormatter: DefaultCartesianMarker.ValueFormatter = DefaultCartesianMarker.ValueFormatter { _, targets ->
86+
val first = targets.firstOrNull()
87+
val value = if (first is LineCartesianLayerMarkerTarget) {
88+
first.points.sumOf { it.entry.y.toLong() }
89+
} else 0L
90+
91+
if (value >= 60 * 60 * 1000) {
92+
millisecondsToHoursMinutes(value)
93+
} else {
94+
millisecondsToMinutes(value)
95+
}
96+
},
6597
animationSpec: AnimationSpec<Float>? = motionScheme.slowEffectsSpec()
6698
) {
6799
ProvideVicoTheme(rememberM3VicoTheme()) {
@@ -102,6 +134,20 @@ fun TimeLineChart(
102134
guideline = rememberLineComponent(Fill.Transparent),
103135
valueFormatter = xValueFormatter
104136
),
137+
marker = DefaultCartesianMarker(
138+
TextComponent(
139+
color = colorScheme.surface.toArgb(),
140+
background = ShapeComponent(
141+
fill = fill(colorScheme.onSurface),
142+
shape = CorneredShape.Pill
143+
),
144+
textSizeSp = typography.labelSmall.fontSize.value,
145+
lineHeightSp = typography.labelSmall.fontSize.value,
146+
padding = Insets(verticalDp = 4f, horizontalDp = 8f),
147+
margins = Insets(bottomDp = 2f)
148+
),
149+
valueFormatter = markerValueFormatter
150+
),
105151
fadingEdges = FadingEdges()
106152
),
107153
modelProducer = modelProducer,
@@ -111,7 +157,7 @@ fun TimeLineChart(
111157
minZoom = Zoom.min(Zoom.Content, Zoom.fixed())
112158
),
113159
animationSpec = animationSpec,
114-
modifier = modifier,
160+
modifier = modifier.height(224.dp),
115161
)
116162
}
117163
}

0 commit comments

Comments
 (0)