Merge main (v2.23.1) into release/2.24.0

This commit is contained in:
makiolaj committed 2026-11-06 11:43:06 +01:00
commit 03138b8740
21 files changed
+119 -20

No files matched your search

+24
View File
@@ -7,11 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [2.24.0] — 2026-11-06
### Added
- **Six new home-screen widgets.** Quick add (1×1) opens a new event on today with the same defaults as the app's + button ([#435]). Date (1×1) shows today's weekday and day number on a rounded shape and opens today ([#376]). Week strip (4×1) shows this week's days with a dot in each event's colour and today filled ([#436]). Upcoming (2×2) shows today's date over your next events, as many as fit ([#437]). Today lists today's events; once they're over it says "Nothing left today" and shows tomorrow's first one or two ([#438]). Countdown counts down to your next timed event, or to an event or series you pick, like a holiday in 12 days ([#439]).
- **A month and day widget.** A compact month grid sits beside the events of one day. Tap a day in the grid to see its events, and tap the list to open that day in the app ([#441]).
- **Each widget has its own settings.** Placing a widget, or long-pressing it and choosing Edit, opens a settings screen with a live preview. Pick a solid, tinted or transparent background, a light or dark theme or the app's, and which calendars it shows. Widgets that list events also have their time range, text size and how past events look. Settings → Widgets lists the widgets on your home screen so you can change them from there. These options used to be shared by all widgets; widgets you already placed keep what you had set ([#292]).
- **Turn off start times in the month view.** Settings → Views → Month → Start times hides the time in front of event titles, so narrow fonts keep more of the title. With it on, the time now only shows when the whole title, or at least its first 12 characters, still fits beside it ([#476]).
- **Drag all-day events.** In the day, 3-day and week views, long-press an event in the all-day strip and move it sideways to another day, or down into the grid to turn it into a timed event ([#296]).
### Changed
- **The agenda and month widgets have a new look.** The agenda puts each day in its own card, with a round date pill beside the first event and today's pill filled. The month widget has rounded day cells, weekday pills, shaded days outside the month and a + for a new event. The agenda's refresh button is gone, since widgets now update on their own ([#71], [#90]).
@@ -19,12 +22,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **The widget picker shows what each widget looks like.** On Android 15 and later the previews are the real widgets with sample events, in your language and colours; on older versions they are redrawn to match.
- **Widgets are listed by their name alone,** without "Calendula" in front, and each has a short description in the picker ([#71]).
- **Past events in widgets stay readable when dimmed.** Their background fades further and their text less ([#90]).
- **Events from midnight to midnight show as all-day events.** A timed event that starts and ends at midnight now sits in the all-day strip, shows as "All day" in the agenda and widgets, and its reminder names the day instead of 00:00 – 00:00. The event itself stays as it was saved ([#296]).
- **Cancelled and tentative events are marked.** Cancelled events are struck through and listed last, like declined invites, and tentative ones get the outline used for unanswered invites. Cancelled one-off events and series no longer disappear ([#388]).
- **One notification per event.** An event with several reminders now keeps a single notification, which each later reminder updates in place ([#463]).
- **Go further back and forward in time.** Every view and the date picker now reach from 1600 to 2399 ([#457]).
- **The split month agenda only lists days with events.** In other months it no longer starts with an empty 1st, and a month with nothing in it says so in one row ([#142]).
### Fixed
- **Widgets show your changes straight away.** After you added, edited or deleted an event, a widget could keep showing the old events until something else redrew it ([#420], [#229]).
- **Widgets follow the app's language.** After you picked a language in Settings, widgets kept the old one until something redrew them ([#90]).
- **The month widget handles busy months.** It could show "Can't show content", or only one or two events per day. Each day now shows as many events as its height fits, and the rest fold into +N ([#372]).
- **Short events late in the day are fully visible.** An event like 23:58–23:59 used to be cut down to a sliver at the bottom of the day, week and 3-day views. It now keeps its usual minimum height and sits flush against midnight ([#483]).
- **The month view no longer crashes after going before 1900** in the day or week view ([#457]).
- **Month pages keep updating.** After a small swipe that snapped back, a month page could stop showing new or edited events, and opening an event from the split view could show its old times ([#440], [#454]).
## [2.23.1] — 2026-10-28
### Changed
- **The split month view fits more events.** With a day selected, its date is no longer shown twice: the filter chip now stands in for the day's header. Tapping the chip opens the day, and its ✕ clears the filter. The day headers in the month agenda below the grid take less room too ([#142]).
### Fixed
- **Event dots in the split month grid stay in place on busy days.** A day with a "+N" next to its dots used to push them further down the cell ([#142]).
## [2.23.0] — 2026-10-21
@@ -1858,6 +1876,7 @@ automatically, with zero telemetry and no internet permission.
[#146]: https://codeberg.org/jlmakiola/calendula/issues/146
[#267]: https://codeberg.org/jlmakiola/calendula/issues/267
[#239]: https://codeberg.org/jlmakiola/calendula/issues/239
[#296]: https://codeberg.org/jlmakiola/calendula/issues/296
[#297]: https://codeberg.org/jlmakiola/calendula/issues/297
[#298]: https://codeberg.org/jlmakiola/calendula/issues/298
[#309]: https://codeberg.org/jlmakiola/calendula/issues/309
@@ -1907,6 +1926,7 @@ automatically, with zero telemetry and no internet permission.
[#306]: https://codeberg.org/jlmakiola/calendula/issues/306
[#178]: https://codeberg.org/jlmakiola/calendula/issues/178
[#116]: https://codeberg.org/jlmakiola/calendula/issues/116
[#388]: https://codeberg.org/jlmakiola/calendula/issues/388
[#413]: https://codeberg.org/jlmakiola/calendula/issues/413
[#71]: https://codeberg.org/jlmakiola/calendula/issues/71
[#90]: https://codeberg.org/jlmakiola/calendula/issues/90
@@ -1921,6 +1941,10 @@ automatically, with zero telemetry and no internet permission.
[#437]: https://codeberg.org/jlmakiola/calendula/issues/437
[#438]: https://codeberg.org/jlmakiola/calendula/issues/438
[#439]: https://codeberg.org/jlmakiola/calendula/issues/439
[#440]: https://codeberg.org/jlmakiola/calendula/issues/440
[#441]: https://codeberg.org/jlmakiola/calendula/issues/441
[#454]: https://codeberg.org/jlmakiola/calendula/issues/454
[#457]: https://codeberg.org/jlmakiola/calendula/issues/457
[#463]: https://codeberg.org/jlmakiola/calendula/issues/463
[#476]: https://codeberg.org/jlmakiola/calendula/issues/476
[#483]: https://codeberg.org/jlmakiola/calendula/issues/483
@@ -59,11 +59,16 @@ import java.util.Locale
// split style can list a day with exactly the same visual language instead of
// growing a parallel set of event rows.
/**
* A day's header; tapping it opens the Day view. [compact] trims the padding for
* the month view's split pane, where vertical space is short (#142).
*/
@Composable
internal fun AgendaDayHeader(
date: LocalDate,
today: LocalDate,
onOpenDay: (LocalDate) -> Unit,
compact: Boolean = false,
) {
Surface(
color = MaterialTheme.colorScheme.surface,
@@ -79,7 +84,12 @@ internal fun AgendaDayHeader(
} else {
MaterialTheme.colorScheme.onSurfaceVariant
},
modifier = Modifier.padding(start = 28.dp, end = 28.dp, top = 16.dp, bottom = 8.dp),
modifier = Modifier.padding(
start = 28.dp,
end = 28.dp,
top = if (compact) 8.dp else 16.dp,
bottom = if (compact) 4.dp else 8.dp,
),
)
}
}
@@ -220,10 +230,11 @@ internal fun LazyListScope.agendaDays(
dimmed: (EventInstance, LocalDate) -> Boolean,
onOpenDay: (LocalDate) -> Unit,
onEventClick: (EventInstance) -> Unit,
compact: Boolean = false,
) {
days.forEach { day ->
stickyHeader(key = "header-${day.date}") {
AgendaDayHeader(date = day.date, today = today, onOpenDay = onOpenDay)
AgendaDayHeader(date = day.date, today = today, onOpenDay = onOpenDay, compact = compact)
}
if (day.events.isEmpty()) {
item(key = "placeholder-${day.date}") {
@@ -251,6 +262,6 @@ internal fun LazyListScope.agendaDays(
)
}
}
item(key = "gap-${day.date}") { Spacer(Modifier.height(8.dp)) }
item(key = "gap-${day.date}") { Spacer(Modifier.height(if (compact) 4.dp else 8.dp)) }
}
}
@@ -63,6 +63,7 @@ import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
@@ -157,10 +158,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import de.jeanlucmakiola.calendula.R
import de.jeanlucmakiola.calendula.domain.EventInstance
import de.jeanlucmakiola.calendula.domain.hasEnded
import de.jeanlucmakiola.calendula.ui.agenda.AgendaDayHeader
import de.jeanlucmakiola.calendula.ui.agenda.agendaDayLabel
import de.jeanlucmakiola.calendula.ui.agenda.AgendaEmptyDayRow
import de.jeanlucmakiola.calendula.ui.agenda.AgendaEventRow
import de.jeanlucmakiola.calendula.ui.agenda.agendaDayLabel
import de.jeanlucmakiola.calendula.ui.common.formatCalendarTitle
import de.jeanlucmakiola.calendula.ui.common.AppBarSpacing
import de.jeanlucmakiola.calendula.ui.common.CalendarFailure
@@ -1446,24 +1446,13 @@ private fun SplitMonthCollapsed(
.then(paneModifier)
.then(rememberSplitPaneSwipe(onPaneSwipeNext, onPaneSwipePrev)),
) {
InputChip(
selected = true,
onClick = onClearSelection,
label = { Text(agendaDayLabel(selected, state.today)) },
trailingIcon = {
Icon(
Icons.Default.Close,
contentDescription = stringResource(R.string.month_split_clear_filter),
)
},
modifier = Modifier.padding(start = 16.dp),
)
SplitDayPane(
date = selected,
today = state.today,
events = state.instancesByDay[selected],
zone = state.zone,
onOpenDay = onOpenDay,
onClearSelection = onClearSelection,
onEventClick = onEventClick,
onCreateEvent = onCreateEvent,
modifier = Modifier.weight(1f).fillMaxWidth(),
@@ -1839,7 +1828,10 @@ private fun SplitDots(
if (events.isEmpty()) return
val soften = LocalSoftenColors.current
val dimCutoff = LocalDimCutoff.current
// Held to the dots' height so a "+N" doesn't grow the row and push the dots
// down; the label overflows it evenly instead (#142).
Row(
modifier = Modifier.height(SPLIT_DOT_SIZE),
horizontalArrangement = Arrangement.spacedBy(2.dp),
verticalAlignment = Alignment.CenterVertically,
) {
@@ -1874,6 +1866,7 @@ private fun SplitDots(
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier
.wrapContentHeight(unbounded = true)
.morphBounds(MonthMorphKey.Overflow(date))
.alpha(if (allEnded(hidden, date, dimCutoff)) EventDimAlpha else 1f),
)
@@ -1897,7 +1890,7 @@ private fun SplitMonthAgendaPane(
LazyColumn(state = listState, contentPadding = PaddingValues(bottom = FabClearance)) {
// A fixed first item, so a day added above the first listed one shows
// up rather than landing above a list that holds its scroll position.
item(key = "top") { Spacer(Modifier.height(8.dp)) }
item(key = "top") { Spacer(Modifier.height(4.dp)) }
if (days.isEmpty()) {
val start = agendaStartForMonth(state.month, state.today)
item(key = "empty") {
@@ -1918,6 +1911,7 @@ private fun SplitMonthAgendaPane(
dimmed = { event, day -> dimCutoff != null && event.isPastOn(day, dimCutoff) },
onOpenDay = onOpenDay,
onEventClick = onEventClick,
compact = true,
)
}
}
@@ -1925,7 +1919,8 @@ private fun SplitMonthAgendaPane(
/**
* The selected day's events, in the agenda's own row vocabulary so the two
* surfaces read as one app. The date header opens the full Day view, matching
* the Week and Agenda headers (#37).
* the Week and Agenda headers (#37). It is shown as the filter chip itself,
* whose ✕ clears the day filter back to the whole month (#142).
*/
@Composable
internal fun SplitDayPane(
@@ -1935,6 +1930,7 @@ internal fun SplitDayPane(
events: List<EventInstance>?,
zone: TimeZone,
onOpenDay: (LocalDate) -> Unit,
onClearSelection: () -> Unit,
onEventClick: (EventInstance) -> Unit,
onCreateEvent: (LocalDate) -> Unit,
modifier: Modifier = Modifier,
@@ -1946,7 +1942,23 @@ internal fun SplitDayPane(
val enter = itemEnter()
val fadeSpec = rememberCalendarFadeSpec()
Column(modifier = modifier) {
AgendaDayHeader(date = date, today = today, onOpenDay = onOpenDay)
InputChip(
selected = true,
onClick = { onOpenDay(date) },
label = {
Text(agendaDayLabel(date, today), style = MaterialTheme.typography.titleSmall)
},
trailingIcon = {
Icon(
Icons.Default.Close,
contentDescription = stringResource(R.string.month_split_clear_filter),
modifier = Modifier
.clip(CircleShape)
.clickable(onClick = onClearSelection),
)
},
modifier = Modifier.padding(start = 16.dp),
)
AnimatedContent(
targetState = date to events,
modifier = Modifier.weight(1f).fillMaxWidth(),
@@ -87,6 +87,7 @@ internal fun MonthStylePreview(
events = sample.month.instancesByDay[today].orEmpty(),
zone = zone,
onOpenDay = {},
onClearSelection = {},
onEventClick = {},
onCreateEvent = {},
modifier = Modifier.fillMaxWidth().height(SPLIT_PANE_HEIGHT),
@@ -0,0 +1,3 @@
إصلاحات
• يعرض عرض الشهر المقسّم أحداثًا أكثر: يظهر تاريخ اليوم المحدد مرة واحدة فقط في شريحة التصفية، وصارت رؤوس الأيام تشغل مساحة أقل.
• لم تعد نقاط الأحداث في الشبكة المقسّمة تنزل للأسفل في الأيام التي تعرض „+N“.
@@ -0,0 +1,3 @@
Выпраўлена
• Падзелены выгляд месяца змяшчае больш падзей: дата абранага дня паказваецца толькі раз, у чыпе фільтра, а загалоўкі дзён займаюць менш месца.
• Кропкі падзей у падзеленай сетцы больш не з'язджаюць уніз у днях з «+N».
@@ -0,0 +1,3 @@
Opraveno
• Rozdělený měsíční pohled pojme více událostí: vybraný den ukazuje datum jen jednou, v čipu filtru, a záhlaví dnů zabírají méně místa.
• Tečky událostí v rozdělené mřížce už ve dnech s „+N“ nesjíždějí dolů.
@@ -0,0 +1,3 @@
Behoben
• Die geteilte Monatsansicht zeigt mehr Termine: Ein gewählter Tag zeigt sein Datum nur noch einmal, im Filter-Chip, und die Tageskopfzeilen brauchen weniger Platz.
• Die Termin-Punkte im geteilten Raster rutschen an Tagen mit „+N“ nicht mehr nach unten.
@@ -0,0 +1,3 @@
Fixed
• The split month view fits more events: a selected day shows its date once, in the filter chip, and the day headers take less room.
• Event dots in the split grid no longer drop down on days with a "+N".
@@ -0,0 +1,3 @@
Corregido
• La vista mensual dividida muestra más eventos: el día seleccionado muestra su fecha una sola vez, en el chip del filtro, y las cabeceras de día ocupan menos espacio.
• Los puntos de eventos de la cuadrícula dividida ya no bajan en los días con «+N».
@@ -0,0 +1,3 @@
Corrigé
• La vue mensuelle partagée affiche plus d’événements : le jour sélectionné n’affiche plus sa date qu’une fois, dans la puce de filtre, et les en-têtes de jour prennent moins de place.
• Les points d’événements de la grille partagée ne descendent plus les jours affichant « +N ».
@@ -0,0 +1,3 @@
Javítva
• A megosztott havi nézet több eseményt mutat: a kiválasztott nap dátuma csak egyszer, a szűrőchipben jelenik meg, és a napfejlécek kevesebb helyet foglalnak.
• A megosztott rács eseménypöttyei már nem csúsznak lejjebb a „+N” jelzésű napokon.
@@ -0,0 +1,3 @@
Corretto
• La vista mensile divisa mostra più eventi: il giorno selezionato mostra la data una sola volta, nel chip del filtro, e le intestazioni dei giorni occupano meno spazio.
• I puntini degli eventi nella griglia divisa non scendono più nei giorni con «+N».
@@ -0,0 +1,3 @@
修正
• 分割月表示に表示できる予定が増えました。選択した日の日付はフィルターチップに一度だけ表示され、日付見出しも小さくなりました。
• 「+N」が表示される日に、分割グリッドの予定ドットが下にずれなくなりました。
@@ -0,0 +1,3 @@
Opgelost
• De gesplitste maandweergave toont meer afspraken: een gekozen dag toont zijn datum nog maar één keer, in de filterchip, en de dagkoppen nemen minder ruimte in.
• De afspraakstippen in het gesplitste raster zakken niet meer weg op dagen met „+N”.
@@ -0,0 +1,3 @@
Poprawki
• Podzielony widok miesiąca mieści więcej wydarzeń: wybrany dzień pokazuje datę tylko raz, w chipie filtra, a nagłówki dni zajmują mniej miejsca.
• Kropki wydarzeń w podzielonej siatce nie zjeżdżają już w dół w dniach z „+N”.
@@ -0,0 +1,3 @@
Corrigido
• A visualização mensal dividida mostra mais eventos: o dia selecionado mostra a data só uma vez, no chip do filtro, e os cabeçalhos dos dias ocupam menos espaço.
• Os pontos de eventos na grade dividida não descem mais nos dias com “+N”.
@@ -0,0 +1,3 @@
Corrigido
• A vista mensal dividida mostra mais eventos: o dia selecionado mostra a data só uma vez, no chip do filtro, e os cabeçalhos dos dias ocupam menos espaço.
• Os pontos de eventos na grelha dividida já não descem nos dias com «+N».
@@ -0,0 +1,3 @@
Исправлено
• Разделённый вид месяца вмещает больше событий: дата выбранного дня показывается один раз, в чипе фильтра, а заголовки дней занимают меньше места.
• Точки событий в разделённой сетке больше не съезжают вниз в днях с «+N».
@@ -0,0 +1,3 @@
Opravené
• Rozdelené mesačné zobrazenie pojme viac udalostí: vybraný deň ukazuje dátum len raz, v čipe filtra, a záhlavia dní zaberajú menej miesta.
• Bodky udalostí v rozdelenej mriežke už v dňoch s „+N“ neklesajú nadol.
@@ -0,0 +1,3 @@
修复
• 分割月视图可以显示更多日程:所选日期只在筛选标签中显示一次,日期标题也更紧凑。
• 显示“+N”的日子里,分割网格中的日程圆点不再下移。