Open timelines on the visible hours from other views (#322)
This commit is contained in:
@@ -66,6 +66,7 @@ import de.jeanlucmakiola.calendula.ui.common.ViewFocusSaver
|
|||||||
import de.jeanlucmakiola.calendula.ui.common.LocalViewMorph
|
import de.jeanlucmakiola.calendula.ui.common.LocalViewMorph
|
||||||
import de.jeanlucmakiola.calendula.ui.common.RescheduleViewModel
|
import de.jeanlucmakiola.calendula.ui.common.RescheduleViewModel
|
||||||
import de.jeanlucmakiola.calendula.ui.common.TimelineScrollMemory
|
import de.jeanlucmakiola.calendula.ui.common.TimelineScrollMemory
|
||||||
|
import de.jeanlucmakiola.calendula.ui.common.LocalTimelineZoom
|
||||||
import de.jeanlucmakiola.calendula.ui.common.ViewMorphScope
|
import de.jeanlucmakiola.calendula.ui.common.ViewMorphScope
|
||||||
import de.jeanlucmakiola.calendula.ui.common.rememberCalendarFadeSpec
|
import de.jeanlucmakiola.calendula.ui.common.rememberCalendarFadeSpec
|
||||||
import de.jeanlucmakiola.calendula.ui.common.rememberViewMorphBoundsTransform
|
import de.jeanlucmakiola.calendula.ui.common.rememberViewMorphBoundsTransform
|
||||||
@@ -446,6 +447,14 @@ fun CalendarHost(
|
|||||||
val topBarBottom = TopAppBarDefaults.windowInsets.getTop(density) +
|
val topBarBottom = TopAppBarDefaults.windowInsets.getTop(density) +
|
||||||
with(density) { TopAppBarDefaults.TopAppBarExpandedHeight.toPx() }
|
with(density) { TopAppBarDefaults.TopAppBarExpandedHeight.toPx() }
|
||||||
val timelineScroll = remember { TimelineScrollMemory() }
|
val timelineScroll = remember { TimelineScrollMemory() }
|
||||||
|
// A timeline reached from another screen opens on the visible hours
|
||||||
|
// (#322); only a switch between timeline views carries the scroll over.
|
||||||
|
val timelineZoom = LocalTimelineZoom.current
|
||||||
|
LaunchedEffect(view.screen) {
|
||||||
|
if (view.screen != ViewScreen.Timeline && timelineZoom.hours.enabled) {
|
||||||
|
timelineScroll.midnightY = null
|
||||||
|
}
|
||||||
|
}
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalEventMove provides moveScope,
|
LocalEventMove provides moveScope,
|
||||||
LocalTimelineScroll provides timelineScroll,
|
LocalTimelineScroll provides timelineScroll,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import androidx.compose.ui.platform.LocalDensity
|
|||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,14 +82,20 @@ fun rememberTimelineScrollState(timelineTop: Dp): TimelineScroll {
|
|||||||
snapshotFlow { scrollState.maxValue }.first { it > 0 }
|
snapshotFlow { scrollState.maxValue }.first { it > 0 }
|
||||||
scrollState.scrollTo(scrollState.maxValue / 2)
|
scrollState.scrollTo(scrollState.maxValue / 2)
|
||||||
} else {
|
} else {
|
||||||
val (target, hourHeight) = snapshotFlow {
|
// Fitted to the scroll viewport itself: the layout resolves scales
|
||||||
val viewport = scrollState.viewportSize
|
// against the viewport plus the all-day strip, which would push the
|
||||||
|
// end hour below the screen.
|
||||||
|
val viewport = snapshotFlow { scrollState.viewportSize }.first { it > 0 }
|
||||||
val hourHeight = zoom.scale.hourHeight(with(density) { viewport.toDp() })
|
val hourHeight = zoom.scale.hourHeight(with(density) { viewport.toDp() })
|
||||||
val hourPx = with(density) { hourHeight.toPx() }
|
|
||||||
startHourScroll(scrollState.maxValue, viewport, startHour, hourPx)?.let { it to hourHeight }
|
|
||||||
}.filterNotNull().first()
|
|
||||||
scrollState.scrollTo(target)
|
|
||||||
zoom.holdFit(hourHeight)
|
zoom.holdFit(hourHeight)
|
||||||
|
val hourPx = with(density) { hourHeight.toPx() }
|
||||||
|
val target = withTimeoutOrNull(FIT_LAYOUT_TIMEOUT_MILLIS) {
|
||||||
|
snapshotFlow {
|
||||||
|
startHourScroll(scrollState.maxValue, scrollState.viewportSize, startHour, hourPx)
|
||||||
|
}.filterNotNull().first()
|
||||||
|
} ?: (startHour * (scrollState.maxValue + scrollState.viewportSize) / 24)
|
||||||
|
.coerceIn(0, scrollState.maxValue.coerceAtLeast(0))
|
||||||
|
scrollState.scrollTo(target)
|
||||||
}
|
}
|
||||||
scroll.noonPending = false
|
scroll.noonPending = false
|
||||||
}
|
}
|
||||||
@@ -99,3 +106,6 @@ fun rememberTimelineScrollState(timelineTop: Dp): TimelineScroll {
|
|||||||
}
|
}
|
||||||
return scroll
|
return scroll
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** How long an opening timeline waits to be laid out at its fitted hours before scrolling anyway. */
|
||||||
|
private const val FIT_LAYOUT_TIMEOUT_MILLIS = 500L
|
||||||
|
|||||||
Reference in New Issue
Block a user