Compare commits

..

3 Commits

Author SHA1 Message Date
e1e32fa3bb fix(deps): update material3 (alpha) to v1.5.0-alpha25 2026-08-06 18:26:05 +00:00
Jean-Luc Makiola
66dbdc8913 2.18.1 — create at the tapped hour after a zoom (#155)
All checks were successful
Release — F-Droid repo + Gitea/Codeberg release + Play / detect (push) Successful in 9s
Release — F-Droid repo + Gitea/Codeberg release + Play / release (push) Successful in 14m28s
Release — F-Droid repo + Gitea/Codeberg release + Play / play (push) Successful in 1m4s
Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/155
2026-08-06 20:09:50 +02:00
Jean-Luc Makiolas Weblate Bot
6a125b7c73 Translations update from Weblate (#136)
All checks were successful
Release — F-Droid repo + Gitea/Codeberg release + Play / detect (push) Successful in 32s
Release — F-Droid repo + Gitea/Codeberg release + Play / release (push) Has been skipped
Release — F-Droid repo + Gitea/Codeberg release + Play / play (push) Has been skipped
Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/136
2026-08-06 19:40:09 +02:00
18 changed files with 91 additions and 6 deletions

View File

@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [2.18.1] — 2026-08-06
### Fixed
- Tapping an empty slot in week or day view creates the event at the hour you
tapped, after the timeline has been zoomed. Pinching the day taller or shorter
— or changing **Hour height** in Settings — left the tap still being measured
against the old spacing, so the new event landed at some other hour entirely
([#148]).
## [2.18.0] — 2026-07-31
### Added
@@ -1323,3 +1332,4 @@ automatically, with zero telemetry and no internet permission.
[#57]: https://codeberg.org/jlmakiola/calendula/issues/57
[#56]: https://codeberg.org/jlmakiola/calendula/issues/56
[#114]: https://codeberg.org/jlmakiola/calendula/issues/114
[#148]: https://codeberg.org/jlmakiola/calendula/issues/148

View File

@@ -28,8 +28,8 @@ android {
// which builds this version and then creates the matching vX.Y.Z tag +
// release itself (versionCode is pinned to MAJOR*10000 + MINOR*100 +
// PATCH from versionName, e.g. 2.7.2 -> 20702). See docs/RELEASING.md.
versionCode = 21800
versionName = "2.18.0"
versionCode = 21801
versionName = "2.18.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

View File

@@ -76,6 +76,18 @@ fun TimelineScale.hourHeight(viewportHeight: Dp): Dp = when (this) {
.coerceAtLeast(fillHourHeight(viewportHeight))
}
/**
* The minute of the day a tap [offsetY] px down a timeline column means, snapped
* to the hour it landed in.
*
* [hourPx] must be the hour height the column is drawing at *now* — a tap
* detector that captured it when it was installed maps taps to a pre-pinch grid
* (#148). A zero or negative height (a viewport measured at nothing) has no grid
* to read, so it answers midnight rather than dividing by it.
*/
fun tappedMinuteOfDay(offsetY: Float, hourPx: Float): Int =
if (hourPx <= 0f) 0 else (offsetY / hourPx).toInt().coerceIn(0, 23) * 60
/**
* Shortest an event block may render, as a fraction of an hour. Blocks keep a
* floor so a 15-minute event stays tappable, but the floor scales with the hour

View File

@@ -45,6 +45,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshotFlow
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@@ -96,6 +97,7 @@ import de.jeanlucmakiola.calendula.ui.common.rememberTimelinePinchZoom
import de.jeanlucmakiola.calendula.ui.common.formatHourLabel
import de.jeanlucmakiola.calendula.ui.common.formatMinuteOfDay
import de.jeanlucmakiola.calendula.ui.common.hourSeparatorLines
import de.jeanlucmakiola.calendula.ui.common.tappedMinuteOfDay
import de.jeanlucmakiola.calendula.ui.week.TimedBlock
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
@@ -577,6 +579,12 @@ private fun DayColumnCard(
val hourPx = with(LocalDensity.current) { hourHeight.toPx() }
val showHourLines = LocalShowHourLines.current
val hourLineColor = MaterialTheme.colorScheme.outlineVariant
// The tap detector outlives the composition that installed it — a pinch or a
// Settings change moves the hour height without restarting it — so it reads
// the height and the callback through state handles instead of capturing
// them, or taps land on the scale the column had before the zoom (#148).
val currentHourPx = rememberUpdatedState(hourPx)
val currentOnCreateAt = rememberUpdatedState(onCreateAt)
Card(
// Plain rectangular column — the soft corners come from the outer
// rounded scroll viewport, so inner rounding would look odd at the edges.
@@ -597,8 +605,10 @@ private fun DayColumnCard(
// only fires on the column background. Snaps to the tapped hour.
.pointerInput(date) {
detectTapGestures { offset ->
val hour = (offset.y / hourPx).toInt().coerceIn(0, 23)
onCreateAt(date, hour * 60)
currentOnCreateAt.value(
date,
tappedMinuteOfDay(offset.y, currentHourPx.value),
)
}
},
) {

View File

@@ -51,6 +51,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshotFlow
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@@ -107,6 +108,7 @@ import de.jeanlucmakiola.calendula.ui.common.rememberTimelinePinchZoom
import de.jeanlucmakiola.calendula.ui.common.formatHourLabel
import de.jeanlucmakiola.calendula.ui.common.formatMinuteOfDay
import de.jeanlucmakiola.calendula.ui.common.hourSeparatorLines
import de.jeanlucmakiola.calendula.ui.common.tappedMinuteOfDay
import de.jeanlucmakiola.calendula.ui.common.rememberCalendarSlideSpec
import de.jeanlucmakiola.calendula.ui.common.next
import de.jeanlucmakiola.floret.time.isoWeekNumber
@@ -722,6 +724,12 @@ private fun DayColumnCard(
val hourPx = with(LocalDensity.current) { hourHeight.toPx() }
val showHourLines = LocalShowHourLines.current
val hourLineColor = MaterialTheme.colorScheme.outlineVariant
// The tap detector outlives the composition that installed it — a pinch or a
// Settings change moves the hour height without restarting it — so it reads
// the height and the callback through state handles instead of capturing
// them, or taps land on the scale the column had before the zoom (#148).
val currentHourPx = rememberUpdatedState(hourPx)
val currentOnCreateAt = rememberUpdatedState(onCreateAt)
Card(
// Plain rectangular columns — the soft corners come from the outer
// rounded scroll viewport, so inner rounding would look odd at the edges.
@@ -741,8 +749,10 @@ private fun DayColumnCard(
// blocks are consumed by their own handler first. Snaps to hour.
.pointerInput(date) {
detectTapGestures { offset ->
val hour = (offset.y / hourPx).toInt().coerceIn(0, 23)
onCreateAt(date, hour * 60)
currentOnCreateAt.value(
date,
tappedMinuteOfDay(offset.y, currentHourPx.value),
)
}
},
) {

View File

@@ -491,4 +491,5 @@
<item quantity="many">Importando %d eventos</item>
<item quantity="other">Importando %d eventos</item>
</plurals>
<string name="settings_soften_colors">Armoniza los colores del calendario</string>
</resources>

View File

@@ -126,4 +126,24 @@ class TimelineScaleTest {
assertThat(parseTimelineScale(stored)).isEqualTo(TimelineScale.Regular)
}
}
@Test
fun `a tap creates at the hour it landed in, at whatever scale`() {
// Same point on the same column reads as a different hour once the
// timeline has been pinched — which is the whole of #148: the tap has to
// be measured against the height the column is drawing at now.
assertThat(tappedMinuteOfDay(offsetY = 500f, hourPx = 100f)).isEqualTo(5 * 60)
assertThat(tappedMinuteOfDay(offsetY = 500f, hourPx = 50f)).isEqualTo(10 * 60)
// Within an hour it snaps back to that hour's start.
assertThat(tappedMinuteOfDay(offsetY = 599f, hourPx = 100f)).isEqualTo(5 * 60)
}
@Test
fun `a tap outside the day stays inside it`() {
assertThat(tappedMinuteOfDay(offsetY = -20f, hourPx = 56f)).isEqualTo(0)
assertThat(tappedMinuteOfDay(offsetY = 99_999f, hourPx = 56f)).isEqualTo(23 * 60)
// A viewport measured at nothing has no grid to read — midnight, not a
// division by zero.
assertThat(tappedMinuteOfDay(offsetY = 500f, hourPx = 0f)).isEqualTo(0)
}
}

View File

@@ -0,0 +1,2 @@
إصلاحات
• عرض الأسبوع واليوم: النقر على مساحة فارغة يُنشئ الحدث في الساعة التي نقرت عليها، حتى بعد تكبير شريط الوقت أو تغيير ارتفاع الساعة.

View File

@@ -0,0 +1,2 @@
Behoben
• Wochen- und Tagesansicht: Ein Tippen auf einen freien Bereich legt den Termin jetzt zur angetippten Uhrzeit an auch nachdem die Zeitleiste gezoomt oder die Stundenhöhe geändert wurde.

View File

@@ -0,0 +1,2 @@
Fixed
• Week and day view: tapping an empty slot now creates the event at the hour you tapped, even after zooming the timeline or changing the hour height.

View File

@@ -0,0 +1,2 @@
Fixed
• Week and day view: tapping an empty slot now creates the event at the hour you tapped, even after zooming the timeline or changing the hour height.

View File

@@ -0,0 +1,2 @@
Corregido
• Vista de semana y día: al tocar un hueco libre, el evento se crea a la hora que has tocado, incluso después de ampliar la línea de tiempo o cambiar la altura de la hora.

View File

@@ -0,0 +1,2 @@
Corrigé
• Vues Semaine et Jour : appuyer sur un créneau vide crée désormais l'événement à l'heure touchée, même après avoir zoomé sur la grille ou modifié la hauteur d'une heure.

View File

@@ -0,0 +1,2 @@
Corretto
• Vista settimana e giorno: toccando uno spazio libero l'evento viene creato all'ora toccata, anche dopo aver ingrandito la linea temporale o cambiato l'altezza dell'ora.

View File

@@ -0,0 +1,2 @@
Poprawki
• Widok tygodnia i dnia: dotknięcie pustego miejsca tworzy teraz wydarzenie o dotkniętej godzinie, także po powiększeniu osi czasu lub zmianie wysokości godziny.

View File

@@ -0,0 +1,2 @@
Corrigido
• Vista de semana e de dia: tocar num espaço livre cria o evento à hora tocada, mesmo depois de ampliar a linha temporal ou alterar a altura da hora.

View File

@@ -0,0 +1,2 @@
Исправлено
• Вид недели и дня: нажатие на свободное место создаёт событие на выбранный час — в том числе после масштабирования шкалы времени или изменения высоты часа.

View File

@@ -0,0 +1,2 @@
修复
• 周视图和日视图:点按空白处会在所点的时间创建事件,即使在缩放时间轴或更改小时高度之后也是如此。