Feedback from #141: when resizing with the left hand, the finger covers both the event's own time and the hour-column badge. While a selected event is moved or resized, the time under its title in the top bar now follows the gesture. It shows what letting go would save, using the same calculation as the drop (real duration for a move, the untouched edge kept for a resize, DST-safe), and stays on the saved times until the event leaves its starting slot. It only recalculates when the snapped slot changes. Closes #141 Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de> Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/403
This commit is contained in:
@@ -77,6 +77,8 @@ class EventSelection(
|
||||
val event: EventInstance,
|
||||
val dismiss: () -> Unit,
|
||||
val create: (() -> Unit)? = null,
|
||||
/** The event at the times an ongoing move or resize would give it, else null. */
|
||||
val preview: () -> EventInstance? = { null },
|
||||
)
|
||||
|
||||
/** The screen on show's [ScreenChrome] and [ScreenActions], for the host to draw. */
|
||||
@@ -126,10 +128,12 @@ fun PublishSelection(
|
||||
compact: Boolean,
|
||||
onDismiss: () -> Unit,
|
||||
onCreate: (() -> Unit)? = null,
|
||||
preview: () -> EventInstance? = { null },
|
||||
) {
|
||||
val slot = LocalChromeSlot.current ?: return
|
||||
val currentDismiss by rememberUpdatedState(onDismiss)
|
||||
val currentCreate by rememberUpdatedState(onCreate)
|
||||
val currentPreview by rememberUpdatedState(preview)
|
||||
val drafted = onCreate != null
|
||||
val selection = remember(event, drafted) {
|
||||
event?.let {
|
||||
@@ -137,6 +141,7 @@ fun PublishSelection(
|
||||
event = it,
|
||||
dismiss = { currentDismiss() },
|
||||
create = if (drafted) ({ currentCreate?.invoke() }) else null,
|
||||
preview = { currentPreview() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
@@ -30,10 +32,13 @@ import kotlin.time.toJavaInstant
|
||||
@Composable
|
||||
fun SelectionTopBar(selection: EventSelection) {
|
||||
val event = selection.event
|
||||
// Follows a move or resize, so the times stay readable under the finger.
|
||||
val preview by remember(selection) { derivedStateOf { selection.preview() } }
|
||||
val shown = preview ?: event
|
||||
val use24Hour = LocalUse24HourFormat.current
|
||||
val locale = currentLocale()
|
||||
val whenText = remember(event.start, event.end, use24Hour, locale) {
|
||||
formatSelectionWhen(event, use24Hour, locale)
|
||||
val whenText = remember(shown.start, shown.end, use24Hour, locale) {
|
||||
formatSelectionWhen(shown, use24Hour, locale)
|
||||
}
|
||||
TopAppBar(
|
||||
navigationIcon = {
|
||||
|
||||
@@ -19,6 +19,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
@@ -815,6 +816,25 @@ class TimelineDragController {
|
||||
originSlot = drag?.slot
|
||||
}
|
||||
|
||||
/** The slot the gesture would land on, changing once per snap rather than per frame. */
|
||||
private val landingSlot by derivedStateOf { drag?.slot }
|
||||
|
||||
/**
|
||||
* The dragged event at the times letting go now would give it, worked out as
|
||||
* [finish] and [finishResize] do. Null while it is still on its own slot (#141).
|
||||
*/
|
||||
fun preview(zone: TimeZone): EventInstance? {
|
||||
val (date, startMin, spanMin) = landingSlot?.takeIf { it != originSlot } ?: return null
|
||||
val event = source?.event ?: return null
|
||||
val edge = resizeEdge
|
||||
if (edge != null) {
|
||||
val drop = resizedDrop(event, edge, date, startMin, spanMin, zone)
|
||||
return event.copy(start = drop.start, end = drop.end)
|
||||
}
|
||||
val start = TimelineDrop(event, date, startMin, clipOffsetMin).startInstant(zone)
|
||||
return event.copy(start = start, end = start + dragDuration)
|
||||
}
|
||||
|
||||
/**
|
||||
* End a resize, handing back the event's new span — null when it never
|
||||
* resolved or came back to the length it started at. Settles as [finish] does.
|
||||
|
||||
@@ -614,6 +614,7 @@ private fun WeekContent(
|
||||
compact = dragController.selectionCovered,
|
||||
onDismiss = dragController::deselect,
|
||||
onCreate = selection?.event?.takeIf { it.isDraft }?.let { draft -> { createDraft(draft) } },
|
||||
preview = { dragController.preview(zone) },
|
||||
)
|
||||
BackHandler(enabled = selection != null) { dragController.deselect() }
|
||||
LaunchedEffect(pagerState, dragController) {
|
||||
|
||||
Reference in New Issue
Block a user