diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthMorph.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthMorph.kt index 8c61e9f..c8d099c 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthMorph.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthMorph.kt @@ -66,6 +66,25 @@ internal class MonthMorphScope( internal val LocalMonthMorph = compositionLocalOf { null } +/** + * True while the grid is mid-morph, for content that has to stop clipping to let + * the travelling pieces through. + * + * A mark's two homes are in *different rows*: the dot for the 20th sits a third + * of the way down the compact grid, its bar most of the way down the expanded + * one. Clipped to the row it is arriving at, a mark spends the first half of its + * journey outside those bounds and is simply not drawn — so it appears from + * nowhere, part-way through, already near its destination. Rendering in place + * rather than in an overlay is what subjects them to that clip, and is worth + * keeping; the clip is what has to yield, and only while pieces are in flight. + */ +@OptIn(ExperimentalSharedTransitionApi::class) +@Composable +internal fun morphInFlight(): Boolean { + val morph = LocalMonthMorph.current ?: return false + return morph.shared.isTransitionActive +} + /** * Tag content that is *the same thing* on both sides — a background pill, a day * number — so it animates between its two positions and sizes. diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthScreen.kt index 21fab47..faa39ce 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthScreen.kt @@ -1647,6 +1647,7 @@ private fun MonthWeekRow( val dark = isSystemInDarkTheme() val laneCount = (week.spans.maxOfOrNull { it.lane } ?: -1) + 1 val shownLanes = laneCount.coerceAtMost(MAX_EVENT_ROWS) + val morphing = morphInFlight() Row(modifier) { // Optional calendar-week gutter, sized so the seven day columns below @@ -1720,11 +1721,15 @@ private fun MonthWeekRow( // Breathing room between the day number (and today's circle) and the // first event row. Spacer(Modifier.height(DAY_NUMBER_GAP)) + // Clipped at rest so a bar can't spill into the row below, but not + // while a morph is in flight: a mark travels between two different + // rows, and clipping it to the one it is arriving at hides it for + // the whole first half of the journey. See [morphInFlight]. Box( modifier = Modifier .fillMaxWidth() .weight(1f) - .clipToBounds(), + .then(if (morphing) Modifier else Modifier.clipToBounds()), ) { // Spanning bars on their shared lanes. week.spans.filter { it.lane < shownLanes }.forEach { span ->