Keep the move action and default off while unknown (#173)
Switching drag off gated the whole move scope, which also removed the TalkBack move action — a menu item can't cause an accidental drag, so it stays; only the gesture is gated now, via EventMoveScope.dragEnabled. The host flow also started at true, re-arming the gesture for the first frames of a cold start for exactly the user who turned it off.
This commit is contained in:
@@ -312,12 +312,13 @@ fun CalendarHost(
|
|||||||
heldEditKey = key
|
heldEditKey = key
|
||||||
editKey = key
|
editKey = key
|
||||||
}
|
}
|
||||||
// Off by preference (#173) means no scope at all: every block then registers
|
// Off by preference (#173) switches off the gesture, not the scope: blocks
|
||||||
// no drag gesture, and the edit form stays the way to reschedule.
|
// register no drag, while the edit form and its TalkBack action stay put.
|
||||||
val dragToReschedule = viewModel.dragToReschedule.collectAsStateWithLifecycle().value
|
val dragToReschedule = viewModel.dragToReschedule.collectAsStateWithLifecycle().value
|
||||||
val moveScope = remember(movableCalendarIds, reschedule) {
|
val moveScope = remember(movableCalendarIds, reschedule, dragToReschedule) {
|
||||||
EventMoveScope(
|
EventMoveScope(
|
||||||
movableCalendarIds = movableCalendarIds,
|
movableCalendarIds = movableCalendarIds,
|
||||||
|
dragEnabled = dragToReschedule,
|
||||||
move = reschedule::move,
|
move = reschedule::move,
|
||||||
inFlight = reschedule.inFlight,
|
inFlight = reschedule.inFlight,
|
||||||
undoStarted = reschedule.undoStarted,
|
undoStarted = reschedule.undoStarted,
|
||||||
@@ -342,7 +343,7 @@ fun CalendarHost(
|
|||||||
// navigation, so it fades through rather than sliding — paging *within* a
|
// navigation, so it fades through rather than sliding — paging *within* a
|
||||||
// view keeps the directional slide. AnimatedContent keyed on the view type.
|
// view keeps the directional slide. AnimatedContent keyed on the view type.
|
||||||
val viewSwitch = fadeThrough()
|
val viewSwitch = fadeThrough()
|
||||||
CompositionLocalProvider(LocalEventMove provides moveScope.takeIf { dragToReschedule }) {
|
CompositionLocalProvider(LocalEventMove provides moveScope) {
|
||||||
AnimatedContent(
|
AnimatedContent(
|
||||||
targetState = view,
|
targetState = view,
|
||||||
transitionSpec = { viewSwitch },
|
transitionSpec = { viewSwitch },
|
||||||
|
|||||||
@@ -54,11 +54,17 @@ class CalendarHostViewModel @Inject constructor(
|
|||||||
initialValue = false,
|
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<Boolean> = prefs.dragToReschedule
|
val dragToReschedule: StateFlow<Boolean> = prefs.dragToReschedule
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope = viewModelScope,
|
scope = viewModelScope,
|
||||||
started = SharingStarted.WhileSubscribed(5_000L),
|
started = SharingStarted.WhileSubscribed(5_000L),
|
||||||
initialValue = true,
|
initialValue = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ class EventMoveScope(
|
|||||||
* the repository writes whatever it is handed — so this gate is load-bearing.
|
* the repository writes whatever it is handed — so this gate is load-bearing.
|
||||||
*/
|
*/
|
||||||
val movableCalendarIds: Set<Long>,
|
val movableCalendarIds: Set<Long>,
|
||||||
|
/**
|
||||||
|
* 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. */
|
/** False when the drop was refused outright, so nothing will be written. */
|
||||||
val move: (MoveRequest) -> Boolean,
|
val move: (MoveRequest) -> Boolean,
|
||||||
/**
|
/**
|
||||||
@@ -91,6 +97,17 @@ fun ghostAlpha(lifted: Boolean): Float = animateFloatAsState(
|
|||||||
label = "ghost-alpha",
|
label = "ghost-alpha",
|
||||||
).value
|
).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
|
* A TalkBack action that opens [event] in the edit form, so rescheduling isn't
|
||||||
* pointer-only. Null when this event can't be moved.
|
* pointer-only. Null when this event can't be moved.
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ import de.jeanlucmakiola.calendula.ui.common.TimelineDragController
|
|||||||
import de.jeanlucmakiola.calendula.ui.common.TimelineDragOverlay
|
import de.jeanlucmakiola.calendula.ui.common.TimelineDragOverlay
|
||||||
import de.jeanlucmakiola.calendula.ui.common.TimelineDrop
|
import de.jeanlucmakiola.calendula.ui.common.TimelineDrop
|
||||||
import de.jeanlucmakiola.calendula.ui.common.beginsOn
|
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.eventMoveAction
|
||||||
import de.jeanlucmakiola.calendula.ui.common.rememberEventDragSource
|
import de.jeanlucmakiola.calendula.ui.common.rememberEventDragSource
|
||||||
import de.jeanlucmakiola.calendula.ui.common.rememberTimelineDragController
|
import de.jeanlucmakiola.calendula.ui.common.rememberTimelineDragController
|
||||||
@@ -737,7 +738,7 @@ private fun EventBlock(
|
|||||||
val moveAction = eventMoveAction(block.event)
|
val moveAction = eventMoveAction(block.event)
|
||||||
// A block clipped at the top continues from the previous day: its top edge
|
// 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.
|
// 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(
|
val dragModifier = rememberEventDragSource(
|
||||||
enabled = draggable,
|
enabled = draggable,
|
||||||
key = block.event.instanceId,
|
key = block.event.instanceId,
|
||||||
|
|||||||
@@ -2172,7 +2172,7 @@ private fun monthChipDragModifier(
|
|||||||
rowHeightPx: Float,
|
rowHeightPx: Float,
|
||||||
isRtl: Boolean,
|
isRtl: Boolean,
|
||||||
): Modifier = rememberDragSurface(
|
): Modifier = rememberDragSurface(
|
||||||
enabled = moveScope != null && controller != null,
|
enabled = moveScope?.dragEnabled == true && controller != null,
|
||||||
key = week.days.first(),
|
key = week.days.first(),
|
||||||
onPickUp = { local, pointerInRoot, nodeInRoot, size ->
|
onPickUp = { local, pointerInRoot, nodeInRoot, size ->
|
||||||
val bandTop = band[0]?.takeIf { it.isAttached }?.positionInRoot()?.y
|
val bandTop = band[0]?.takeIf { it.isAttached }?.positionInRoot()?.y
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ import de.jeanlucmakiola.calendula.ui.common.TimelineDragController
|
|||||||
import de.jeanlucmakiola.calendula.ui.common.TimelineDragOverlay
|
import de.jeanlucmakiola.calendula.ui.common.TimelineDragOverlay
|
||||||
import de.jeanlucmakiola.calendula.ui.common.TimelineDrop
|
import de.jeanlucmakiola.calendula.ui.common.TimelineDrop
|
||||||
import de.jeanlucmakiola.calendula.ui.common.beginsOn
|
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.eventMoveAction
|
||||||
import de.jeanlucmakiola.calendula.ui.common.rememberEventDragSource
|
import de.jeanlucmakiola.calendula.ui.common.rememberEventDragSource
|
||||||
import de.jeanlucmakiola.calendula.ui.common.rememberTimelineDragController
|
import de.jeanlucmakiola.calendula.ui.common.rememberTimelineDragController
|
||||||
@@ -905,7 +906,7 @@ private fun EventBlock(
|
|||||||
val moveAction = eventMoveAction(block.event)
|
val moveAction = eventMoveAction(block.event)
|
||||||
// A block clipped at the top continues from the previous day: its top edge
|
// 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.
|
// 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(
|
val dragModifier = rememberEventDragSource(
|
||||||
enabled = draggable,
|
enabled = draggable,
|
||||||
key = block.event.instanceId,
|
key = block.event.instanceId,
|
||||||
|
|||||||
Reference in New Issue
Block a user