diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/CalendarHost.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/CalendarHost.kt index 0276be3..fa32b89 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/CalendarHost.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/CalendarHost.kt @@ -312,12 +312,13 @@ fun CalendarHost( heldEditKey = key editKey = key } - // Off by preference (#173) means no scope at all: every block then registers - // no drag gesture, and the edit form stays the way to reschedule. + // Off by preference (#173) switches off the gesture, not the scope: blocks + // register no drag, while the edit form and its TalkBack action stay put. val dragToReschedule = viewModel.dragToReschedule.collectAsStateWithLifecycle().value - val moveScope = remember(movableCalendarIds, reschedule) { + val moveScope = remember(movableCalendarIds, reschedule, dragToReschedule) { EventMoveScope( movableCalendarIds = movableCalendarIds, + dragEnabled = dragToReschedule, move = reschedule::move, inFlight = reschedule.inFlight, undoStarted = reschedule.undoStarted, @@ -342,7 +343,7 @@ fun CalendarHost( // navigation, so it fades through rather than sliding — paging *within* a // view keeps the directional slide. AnimatedContent keyed on the view type. val viewSwitch = fadeThrough() - CompositionLocalProvider(LocalEventMove provides moveScope.takeIf { dragToReschedule }) { + CompositionLocalProvider(LocalEventMove provides moveScope) { AnimatedContent( targetState = view, transitionSpec = { viewSwitch }, diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/CalendarHostViewModel.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/CalendarHostViewModel.kt index a50f1ac..70cabf6 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/CalendarHostViewModel.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/CalendarHostViewModel.kt @@ -54,11 +54,17 @@ class CalendarHostViewModel @Inject constructor( initialValue = false, ) - /** Whether events may be dragged to another slot to reschedule them (#68, #173). */ + /** + * Whether events may be dragged to another slot to reschedule them (#68, #173). + * Off until the stored value arrives, even though the preference itself defaults + * on: the wrong guess costs an enabled user a gesture for the few frames a cold + * start takes to read DataStore, but re-arms the one thing a disabled user + * switched off to be rid of. + */ val dragToReschedule: StateFlow = prefs.dragToReschedule .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000L), - initialValue = true, + initialValue = false, ) } diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventMoveScope.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventMoveScope.kt index 6fb0d8a..c338e40 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventMoveScope.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventMoveScope.kt @@ -29,6 +29,12 @@ class EventMoveScope( * the repository writes whatever it is handed — so this gate is load-bearing. */ val movableCalendarIds: Set, + /** + * Whether the *gesture* is on (#173). Off gates the drag alone: [edit] and the + * TalkBack action below stay, since invoking a menu item can't cause the + * accidental drags the setting exists to stop. + */ + val dragEnabled: Boolean, /** False when the drop was refused outright, so nothing will be written. */ val move: (MoveRequest) -> Boolean, /** @@ -91,6 +97,17 @@ fun ghostAlpha(lifted: Boolean): Float = animateFloatAsState( label = "ghost-alpha", ).value +/** + * Whether [event] can be picked up and dragged — its calendar allows moving and + * the gesture is switched on. Not the same question as [eventMoveAction], which + * survives the setting. + */ +@Composable +fun eventDragAllowed(event: EventInstance): Boolean { + val move = LocalEventMove.current ?: return false + return move.dragEnabled && move.allows(event) +} + /** * A TalkBack action that opens [event] in the edit form, so rescheduling isn't * pointer-only. Null when this event can't be moved. diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/day/DayScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/day/DayScreen.kt index 05c2ef5..6b6d4fe 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/day/DayScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/day/DayScreen.kt @@ -92,6 +92,7 @@ import de.jeanlucmakiola.calendula.ui.common.TimelineDragController import de.jeanlucmakiola.calendula.ui.common.TimelineDragOverlay import de.jeanlucmakiola.calendula.ui.common.TimelineDrop import de.jeanlucmakiola.calendula.ui.common.beginsOn +import de.jeanlucmakiola.calendula.ui.common.eventDragAllowed import de.jeanlucmakiola.calendula.ui.common.eventMoveAction import de.jeanlucmakiola.calendula.ui.common.rememberEventDragSource import de.jeanlucmakiola.calendula.ui.common.rememberTimelineDragController @@ -737,7 +738,7 @@ private fun EventBlock( val moveAction = eventMoveAction(block.event) // A block clipped at the top continues from the previous day: its top edge // is midnight, not the event's start, so dragging it would invent a time. - val draggable = moveAction != null && block.beginsOn(date, zone) + val draggable = eventDragAllowed(block.event) && block.beginsOn(date, zone) val dragModifier = rememberEventDragSource( enabled = draggable, key = block.event.instanceId, 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 14c7628..ffcccf6 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 @@ -2172,7 +2172,7 @@ private fun monthChipDragModifier( rowHeightPx: Float, isRtl: Boolean, ): Modifier = rememberDragSurface( - enabled = moveScope != null && controller != null, + enabled = moveScope?.dragEnabled == true && controller != null, key = week.days.first(), onPickUp = { local, pointerInRoot, nodeInRoot, size -> val bandTop = band[0]?.takeIf { it.isAttached }?.positionInRoot()?.y diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/week/WeekScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/week/WeekScreen.kt index b4debbe..869c3a2 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/week/WeekScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/week/WeekScreen.kt @@ -100,6 +100,7 @@ import de.jeanlucmakiola.calendula.ui.common.TimelineDragController import de.jeanlucmakiola.calendula.ui.common.TimelineDragOverlay import de.jeanlucmakiola.calendula.ui.common.TimelineDrop import de.jeanlucmakiola.calendula.ui.common.beginsOn +import de.jeanlucmakiola.calendula.ui.common.eventDragAllowed import de.jeanlucmakiola.calendula.ui.common.eventMoveAction import de.jeanlucmakiola.calendula.ui.common.rememberEventDragSource import de.jeanlucmakiola.calendula.ui.common.rememberTimelineDragController @@ -905,7 +906,7 @@ private fun EventBlock( val moveAction = eventMoveAction(block.event) // A block clipped at the top continues from the previous day: its top edge // is midnight, not the event's start, so dragging it would invent a time. - val draggable = moveAction != null && block.beginsOn(date, zone) + val draggable = eventDragAllowed(block.event) && block.beginsOn(date, zone) val dragModifier = rememberEventDragSource( enabled = draggable, key = block.event.instanceId,