Trim comments to the project's style (#141)
This commit is contained in:
@@ -52,11 +52,7 @@ fun EventForm.shiftedByDays(days: Int, deviceZone: TimeZone): EventForm {
|
|||||||
return copy(start = newStart, end = (newStart.toInstant(zone) + span).toLocalDateTime(zone))
|
return copy(start = newStart, end = (newStart.toInstant(zone) + span).toLocalDateTime(zone))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** The form with new start and end, each in the form's own zone — a resize. All-day is untouched. */
|
||||||
* The form with its start and end set independently — a timeline resize, which
|
|
||||||
* drags one edge and leaves the other where it stands. Each edge is resolved in
|
|
||||||
* the form's own zone, as [shiftedTo] does. All-day events are returned untouched.
|
|
||||||
*/
|
|
||||||
fun EventForm.resizedTo(newStart: Instant, newEnd: Instant, deviceZone: TimeZone): EventForm {
|
fun EventForm.resizedTo(newStart: Instant, newEnd: Instant, deviceZone: TimeZone): EventForm {
|
||||||
if (isAllDay) return this
|
if (isAllDay) return this
|
||||||
val zone = resolvedZone(deviceZone)
|
val zone = resolvedZone(deviceZone)
|
||||||
|
|||||||
@@ -539,8 +539,7 @@ fun CalendarHost(
|
|||||||
viewModel = eventActions,
|
viewModel = eventActions,
|
||||||
onEdit = onEditEvent,
|
onEdit = onEditEvent,
|
||||||
onDuplicate = { form ->
|
onDuplicate = { form ->
|
||||||
// As a duplicate from the detail screen: a fresh event,
|
// A fresh event, like a duplicate from the detail screen (#52).
|
||||||
// through the prefilled-create overlay (#52).
|
|
||||||
importFormSource = ImportSource.Insert
|
importFormSource = ImportSource.Insert
|
||||||
importForm = form
|
importForm = form
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -82,8 +82,7 @@ fun CalendarFabColumn(
|
|||||||
// Held past the selection clearing, so the bar has something to fold away.
|
// Held past the selection clearing, so the bar has something to fold away.
|
||||||
val heldBar = remember { mutableStateOf<(@Composable () -> Unit)?>(null) }
|
val heldBar = remember { mutableStateOf<(@Composable () -> Unit)?>(null) }
|
||||||
selection?.let { heldBar.value = it.bar }
|
selection?.let { heldBar.value = it.bar }
|
||||||
// Held at the FAB's own height whatever the bar does: the bar is taller,
|
// Held at the FAB's height so the taller bar can't shift the FAB.
|
||||||
// and a row grown to fit it moved the FAB up as the bar unfolded.
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.height(FAB_SIZE),
|
modifier = Modifier.height(FAB_SIZE),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@@ -121,11 +120,7 @@ fun CalendarFabColumn(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** "+", or with an event selected a chevron that turns to open and fold the bar. */
|
||||||
* The FAB's glyph: "+", or with an event selected a chevron pointing where the
|
|
||||||
* bar unfolds — turned half round once it has, to fold it back. The "+" and the
|
|
||||||
* chevron spin past each other rather than fading.
|
|
||||||
*/
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun FabGlyph(selecting: Boolean, expanded: Boolean) {
|
private fun FabGlyph(selecting: Boolean, expanded: Boolean) {
|
||||||
val spatial = MaterialTheme.motionScheme.fastSpatialSpec<Float>()
|
val spatial = MaterialTheme.motionScheme.fastSpatialSpec<Float>()
|
||||||
|
|||||||
@@ -35,11 +35,7 @@ data class DeletePrompt(val event: EventInstance, val isRecurring: Boolean)
|
|||||||
/** How an action on a selected event ended, when it has something to say. */
|
/** How an action on a selected event ended, when it has something to say. */
|
||||||
enum class EventActionFailure { LoadFailed, ShareFailed, DeleteFailed, WriteDenied, Gone }
|
enum class EventActionFailure { LoadFailed, ShareFailed, DeleteFailed, WriteDenied, Gone }
|
||||||
|
|
||||||
/**
|
/** Duplicate, share and delete for a selected event (#141), loading it on demand. */
|
||||||
* The event actions a selected block offers outside the detail screen (#141):
|
|
||||||
* duplicate, share and delete, each loading the event on demand. Hosted at
|
|
||||||
* `CalendarHost` beside [RescheduleViewModel].
|
|
||||||
*/
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class EventActionsViewModel @Inject constructor(
|
class EventActionsViewModel @Inject constructor(
|
||||||
private val repository: CalendarRepository,
|
private val repository: CalendarRepository,
|
||||||
|
|||||||
@@ -116,8 +116,7 @@ private fun rememberPickUpSurface(
|
|||||||
val currentDrop by rememberUpdatedState(onDrop)
|
val currentDrop by rememberUpdatedState(onDrop)
|
||||||
val currentCancel by rememberUpdatedState(onCancel)
|
val currentCancel by rememberUpdatedState(onCancel)
|
||||||
val currentRelease by rememberUpdatedState(onRelease)
|
val currentRelease by rememberUpdatedState(onRelease)
|
||||||
// Read per gesture rather than keyed on: the pickup itself can change it,
|
// Not a key: the pickup changes it, and a restart would cancel the drag.
|
||||||
// and a restart would cancel the drag it just began.
|
|
||||||
val currentImmediate by rememberUpdatedState(immediate)
|
val currentImmediate by rememberUpdatedState(immediate)
|
||||||
|
|
||||||
return Modifier
|
return Modifier
|
||||||
@@ -128,9 +127,7 @@ private fun rememberPickUpSurface(
|
|||||||
val down = awaitFirstDown(requireUnconsumed = false)
|
val down = awaitFirstDown(requireUnconsumed = false)
|
||||||
val origin = down.position
|
val origin = down.position
|
||||||
val immediate = currentImmediate
|
val immediate = currentImmediate
|
||||||
// Null on the timeout — held still — true once an [immediate]
|
// Null: held still. True: an immediate pickup past slop. False: abandoned.
|
||||||
// block has been moved past slop, false when the press is
|
|
||||||
// abandoned.
|
|
||||||
val outcome = withTimeoutOrNull(holdMillis) {
|
val outcome = withTimeoutOrNull(holdMillis) {
|
||||||
var result: Boolean? = null
|
var result: Boolean? = null
|
||||||
while (result == null) {
|
while (result == null) {
|
||||||
|
|||||||
@@ -80,8 +80,7 @@ fun EventMoveHost(
|
|||||||
val resizedLabel = moved?.endMillis?.let { end ->
|
val resizedLabel = moved?.endMillis?.let { end ->
|
||||||
"$movedLabel–${formatTime(end, use24Hour, locale)}"
|
"$movedLabel–${formatTime(end, use24Hour, locale)}"
|
||||||
}
|
}
|
||||||
// One chip for every message down here: a failed selection action (#141)
|
// A failed selection action (#141) shares the chip rather than stacking on it.
|
||||||
// shows when no move has anything to say, rather than stacking over it.
|
|
||||||
val message = when (outcome) {
|
val message = when (outcome) {
|
||||||
null -> actionFailure
|
null -> actionFailure
|
||||||
is MoveOutcome.Moved -> if (resizedLabel != null) {
|
is MoveOutcome.Moved -> if (resizedLabel != null) {
|
||||||
|
|||||||
@@ -16,12 +16,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import de.jeanlucmakiola.calendula.R
|
import de.jeanlucmakiola.calendula.R
|
||||||
|
|
||||||
/**
|
/** The selected event's action bar (#141); Edit and Delete only where its calendar allows. */
|
||||||
* The action bar a selected event unrolls from the FAB (#141), in the FAB's own
|
|
||||||
* colours so it stands clear of the calendar behind it. Edit and Delete are
|
|
||||||
* offered only where the event's calendar allows them; a read-only event can
|
|
||||||
* still be duplicated into a writable calendar, or shared.
|
|
||||||
*/
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun EventSelectionBar(
|
fun EventSelectionBar(
|
||||||
|
|||||||
@@ -20,11 +20,7 @@ import kotlinx.coroutines.launch
|
|||||||
/** How far the bottom chips rise while the selection bar is open, to clear it. */
|
/** How far the bottom chips rise while the selection bar is open, to clear it. */
|
||||||
val SelectionBarClearance = 64.dp + SnackChipMargin
|
val SelectionBarClearance = 64.dp + SnackChipMargin
|
||||||
|
|
||||||
/**
|
/** The FAB's role while an event is selected (#141): its action bar's toggle. Null otherwise. */
|
||||||
* What the FAB becomes while an event is selected (#141): the toggle for its
|
|
||||||
* action bar, folded while it would cover the selected event's bottom handle.
|
|
||||||
* Null with nothing selected.
|
|
||||||
*/
|
|
||||||
@Composable
|
@Composable
|
||||||
fun rememberFabSelection(
|
fun rememberFabSelection(
|
||||||
host: ChromeHost,
|
host: ChromeHost,
|
||||||
|
|||||||
@@ -29,13 +29,8 @@ private val HANDLE_DOT = 16.dp
|
|||||||
private val HANDLE_RING = 2.dp
|
private val HANDLE_RING = 2.dp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A grip on one edge of a selected event (#141). It takes the gesture from the
|
* A grip on one edge of a selected event (#141), dragged without a hold. No
|
||||||
* first movement, with no hold — the event is already selected — and hands the
|
* semantics: TalkBack resizes through the event's edit action.
|
||||||
* finger on in root coordinates. A tap is swallowed rather than falling through
|
|
||||||
* to the slot under it. While not [enabled] it takes no new touch.
|
|
||||||
*
|
|
||||||
* No semantics of its own: TalkBack reaches the same change through the event's
|
|
||||||
* edit action.
|
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun ResizeHandle(
|
fun ResizeHandle(
|
||||||
@@ -52,8 +47,7 @@ fun ResizeHandle(
|
|||||||
val currentMove by rememberUpdatedState(onMove)
|
val currentMove by rememberUpdatedState(onMove)
|
||||||
val currentEnd by rememberUpdatedState(onEnd)
|
val currentEnd by rememberUpdatedState(onEnd)
|
||||||
val currentCancel by rememberUpdatedState(onCancel)
|
val currentCancel by rememberUpdatedState(onCancel)
|
||||||
// Read per gesture: a hidden handle takes no new touch, but turning it off
|
// Not a key: turning it off mid-resize must not cancel that resize.
|
||||||
// mid-resize must not cancel the one it is running.
|
|
||||||
val currentEnabled by rememberUpdatedState(enabled)
|
val currentEnabled by rememberUpdatedState(enabled)
|
||||||
Box(
|
Box(
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
|
|||||||
@@ -275,10 +275,8 @@ enum class ResizeEdge { Top, Bottom }
|
|||||||
data class TimelineResizeDrop(val event: EventInstance, val start: Instant, val end: Instant)
|
data class TimelineResizeDrop(val event: EventInstance, val start: Instant, val end: Instant)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The span after dragging [edge] to [edgeMin] in day [edgeDay], all in minutes
|
* The span after dragging [edge] to [edgeMin] in day [edgeDay], in minutes from the
|
||||||
* from the start day's midnight.
|
* start day's midnight. Snapped, and at least one snap step long.
|
||||||
* The edge snaps to the grid, and never comes closer than one snap step to the
|
|
||||||
* other end — an event can't be resized away to nothing, or turned inside out.
|
|
||||||
*/
|
*/
|
||||||
internal fun resizedSpan(
|
internal fun resizedSpan(
|
||||||
edge: ResizeEdge,
|
edge: ResizeEdge,
|
||||||
@@ -287,9 +285,7 @@ internal fun resizedSpan(
|
|||||||
edgeMin: Float,
|
edgeMin: Float,
|
||||||
edgeDay: Int? = null,
|
edgeDay: Int? = null,
|
||||||
): Pair<Int, Int> {
|
): Pair<Int, Int> {
|
||||||
// Dragged into a day past the other end — the start into a day after the
|
// An edge dragged into a day past the other end changes nothing.
|
||||||
// event ends, the end into one before it starts: no change, rather than an
|
|
||||||
// event collapsed against its other end.
|
|
||||||
if (edgeDay != null) {
|
if (edgeDay != null) {
|
||||||
val endDay = (endMin - 1).floorDiv(MINUTES_PER_DAY).coerceAtLeast(0)
|
val endDay = (endMin - 1).floorDiv(MINUTES_PER_DAY).coerceAtLeast(0)
|
||||||
val wrongWay = when (edge) {
|
val wrongWay = when (edge) {
|
||||||
@@ -305,11 +301,7 @@ internal fun resizedSpan(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** [minutes] snapped, kept at [held] until three quarters of a step away. */
|
||||||
* [minutes] snapped to the grid, kept at [held] until the finger is three
|
|
||||||
* quarters of a step away from it: plain rounding flips between two slots
|
|
||||||
* whenever the finger rests near the line between them.
|
|
||||||
*/
|
|
||||||
internal fun stickySnap(minutes: Float, held: Int?): Int {
|
internal fun stickySnap(minutes: Float, held: Int?): Int {
|
||||||
if (held != null && kotlin.math.abs(minutes - held) < DRAG_SNAP_MINUTES * 0.75f) return held
|
if (held != null && kotlin.math.abs(minutes - held) < DRAG_SNAP_MINUTES * 0.75f) return held
|
||||||
return (minutes / DRAG_SNAP_MINUTES).roundToInt() * DRAG_SNAP_MINUTES
|
return (minutes / DRAG_SNAP_MINUTES).roundToInt() * DRAG_SNAP_MINUTES
|
||||||
@@ -318,12 +310,7 @@ internal fun stickySnap(minutes: Float, held: Int?): Int {
|
|||||||
/** How far past its column's side the finger has to go before a resize changes day. */
|
/** How far past its column's side the finger has to go before a resize changes day. */
|
||||||
private const val COLUMN_HYSTERESIS = 0.25f
|
private const val COLUMN_HYSTERESIS = 0.25f
|
||||||
|
|
||||||
/**
|
/** The column under [position], kept at [held] until [COLUMN_HYSTERESIS] into another. */
|
||||||
* The column under a finger at [position] columns from the grid's start, kept at
|
|
||||||
* [held] until the finger is a [COLUMN_HYSTERESIS] into the next. A resize drags
|
|
||||||
* up and down: the finger drifts sideways as it goes, and in a narrow week column
|
|
||||||
* every drift over a border moved the edge by a whole day and back.
|
|
||||||
*/
|
|
||||||
internal fun stickyColumn(position: Float, held: Int?, lastIndex: Int): Int {
|
internal fun stickyColumn(position: Float, held: Int?, lastIndex: Int): Int {
|
||||||
val under = position.toInt().coerceIn(0, lastIndex)
|
val under = position.toInt().coerceIn(0, lastIndex)
|
||||||
if (held == null) return under
|
if (held == null) return under
|
||||||
@@ -433,13 +420,8 @@ class TimelineDragController {
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether [block] is the selected event. Matched on the series and start too,
|
* Whether [block] is the selected event: by instance, by series and start, and
|
||||||
* since the provider regenerates `Instances` rows and a re-read can bring the
|
* just after a move by title and times, since a moved occurrence gets new ids.
|
||||||
* same occurrence back under a new instance id — and, just after a move, on
|
|
||||||
* what the event says, since moving one occurrence of a series writes it out
|
|
||||||
* as an exception row with an id of its own. Only then: two copies of one
|
|
||||||
* event would otherwise both match, and take the selection back and forth.
|
|
||||||
* A blank title matches nothing that way.
|
|
||||||
*/
|
*/
|
||||||
fun isSelected(block: TimedBlock): Boolean {
|
fun isSelected(block: TimedBlock): Boolean {
|
||||||
val current = selection ?: return false
|
val current = selection ?: return false
|
||||||
@@ -457,11 +439,7 @@ class TimelineDragController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Point the selection at [block]'s event, so the bar acts on the row now there. */
|
||||||
* Take over [block]'s event as the selection once the grid shows it, so the
|
|
||||||
* bar acts on the row that is there now — an exception written by a move has
|
|
||||||
* its own id, and an abandoned move left the original times standing.
|
|
||||||
*/
|
|
||||||
fun adoptSelection(block: TimedBlock) {
|
fun adoptSelection(block: TimedBlock) {
|
||||||
val current = selection ?: return
|
val current = selection ?: return
|
||||||
if (current.event != block.event || current.moved) {
|
if (current.event != block.event || current.moved) {
|
||||||
@@ -539,8 +517,7 @@ class TimelineDragController {
|
|||||||
pointerInRoot: Offset,
|
pointerInRoot: Offset,
|
||||||
blockInRoot: Offset,
|
blockInRoot: Offset,
|
||||||
) {
|
) {
|
||||||
// Picking up another event ends the selection; the selected one keeps
|
// The selected event keeps its selection through a drag; any other ends it.
|
||||||
// it through the drag, and the screen moves it along with the drop.
|
|
||||||
if (!isSelected(block)) deselect()
|
if (!isSelected(block)) deselect()
|
||||||
source = block
|
source = block
|
||||||
this.clipOffsetMin = clipOffsetMin
|
this.clipOffsetMin = clipOffsetMin
|
||||||
@@ -658,8 +635,7 @@ class TimelineDragController {
|
|||||||
resizeEdge = edge
|
resizeEdge = edge
|
||||||
resizeDay = start.date
|
resizeDay = start.date
|
||||||
resizeStartMin = start.time.toSecondOfDay() / 60
|
resizeStartMin = start.time.toSecondOfDay() / 60
|
||||||
// Wall clock at both ends, as the drop resolves them: across a DST
|
// Wall clock at both ends, as the drop resolves them (DST-safe).
|
||||||
// change the event's length in minutes isn't its span on the clock.
|
|
||||||
resizeEndMin = maxOf(
|
resizeEndMin = maxOf(
|
||||||
resizeStartMin,
|
resizeStartMin,
|
||||||
(end.date.toEpochDays() - start.date.toEpochDays()).toInt() * MINUTES_PER_DAY +
|
(end.date.toEpochDays() - start.date.toEpochDays()).toInt() * MINUTES_PER_DAY +
|
||||||
@@ -767,11 +743,7 @@ class TimelineDragController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Note whether the action bar would cover the selected block's bottom handle (#141). */
|
||||||
* Note where the selected block's bottom edge, [extraPx] of handle included,
|
|
||||||
* sits against the action bar [clearancePx] tall over the bottom of the
|
|
||||||
* viewport. Under it, the bar draws itself compact out of the way (#141).
|
|
||||||
*/
|
|
||||||
fun noteSelectedBottom(block: LayoutCoordinates, extraPx: Float, clearancePx: Float) {
|
fun noteSelectedBottom(block: LayoutCoordinates, extraPx: Float, clearancePx: Float) {
|
||||||
val viewport = geometry.viewport?.takeIf { it.isAttached } ?: return
|
val viewport = geometry.viewport?.takeIf { it.isAttached } ?: return
|
||||||
if (!block.isAttached) return
|
if (!block.isAttached) return
|
||||||
@@ -938,12 +910,7 @@ fun TimedBlock.continuesAfter(day: LocalDate, zone: TimeZone): Boolean =
|
|||||||
fun TimelineDrop.startInstant(zone: TimeZone): Instant =
|
fun TimelineDrop.startInstant(zone: TimeZone): Instant =
|
||||||
wallClockInstant(date, startMin - clipOffsetMin, zone)
|
wallClockInstant(date, startMin - clipOffsetMin, zone)
|
||||||
|
|
||||||
/**
|
/** [minutes] past [date]'s midnight, either way past it, as wall clock in [zone] (DST-safe). */
|
||||||
* [minutes] past [date]'s midnight as an instant, read as wall clock in [zone].
|
|
||||||
* Resolved as a day plus a minute of it rather than as an offset from an
|
|
||||||
* instant, which would shift by an hour across a DST boundary; a block can be
|
|
||||||
* dragged above its midnight and past the next one, so either end may overflow.
|
|
||||||
*/
|
|
||||||
fun wallClockInstant(date: LocalDate, minutes: Int, zone: TimeZone): Instant {
|
fun wallClockInstant(date: LocalDate, minutes: Int, zone: TimeZone): Instant {
|
||||||
val day = date.plus(minutes.floorDiv(MINUTES_PER_DAY), DateTimeUnit.DAY)
|
val day = date.plus(minutes.floorDiv(MINUTES_PER_DAY), DateTimeUnit.DAY)
|
||||||
val minute = minutes.mod(MINUTES_PER_DAY)
|
val minute = minutes.mod(MINUTES_PER_DAY)
|
||||||
@@ -1207,11 +1174,7 @@ private fun DragCopy(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** How far a selected block is lifted, 0 to 1 (#141); [snap] takes it over at once. */
|
||||||
* How far a selected block is lifted, between 0 and 1 (#141). [snap] takes the
|
|
||||||
* lift over at once — from the floating copy it replaces — rather than raising
|
|
||||||
* the block a second time.
|
|
||||||
*/
|
|
||||||
@Composable
|
@Composable
|
||||||
fun selectionLift(selected: Boolean, snap: Boolean = false): Float {
|
fun selectionLift(selected: Boolean, snap: Boolean = false): Float {
|
||||||
val reduceMotion = rememberReduceMotion()
|
val reduceMotion = rememberReduceMotion()
|
||||||
|
|||||||
@@ -563,8 +563,7 @@ private fun WeekContent(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A long-pressed event stays selected (#141) until something else is touched:
|
// Selection (#141) ends on back, a page or view change, or any tap.
|
||||||
// back, another page, a view switch, or any tap.
|
|
||||||
val selection = dragController.selection
|
val selection = dragController.selection
|
||||||
PublishSelection(
|
PublishSelection(
|
||||||
event = selection?.event,
|
event = selection?.event,
|
||||||
@@ -1562,9 +1561,7 @@ private fun EventBlock(
|
|||||||
val clipOffset = remember(block, date, zone) { block.clipOffsetMinutes(date, zone) }
|
val clipOffset = remember(block, date, zone) { block.clipOffsetMinutes(date, zone) }
|
||||||
val shape = remember(cuts) { timedBlockShape(cuts.top, cuts.bottom) }
|
val shape = remember(cuts) { timedBlockShape(cuts.top, cuts.bottom) }
|
||||||
val topInsetPx = with(density) { topInset.toPx() }
|
val topInsetPx = with(density) { topInset.toPx() }
|
||||||
// A long press lifts the block (#141): moved, it drags; let go where it was
|
// Long press (#141): move to drag, let go to select; selected blocks drag at once.
|
||||||
// picked up, it stays selected. A block that can't move is selected on the
|
|
||||||
// spot. Once selected, it drags without another hold.
|
|
||||||
val dragModifier = rememberEventDragSource(
|
val dragModifier = rememberEventDragSource(
|
||||||
enabled = selectable,
|
enabled = selectable,
|
||||||
key = block.event.instanceId,
|
key = block.event.instanceId,
|
||||||
@@ -1594,25 +1591,20 @@ private fun EventBlock(
|
|||||||
onRelease = { dragController.select(block.event, fromLift = draggable) },
|
onRelease = { dragController.select(block.event, fromLift = draggable) },
|
||||||
)
|
)
|
||||||
val lifted = draggable && dragController.ghosts(block)
|
val lifted = draggable && dragController.ghosts(block)
|
||||||
// Let go of without moving, the block takes the floating copy's place as it
|
// Released in place, the block takes over the copy's lift without a dip.
|
||||||
// stands: whole and lifted at once, not faded back up and raised again.
|
|
||||||
val takesOverLift = selected && dragController.selection?.fromLift == true
|
val takesOverLift = selected && dragController.selection?.fromLift == true
|
||||||
val ghostFade = ghostAlpha(lifted)
|
val ghostFade = ghostAlpha(lifted)
|
||||||
val ghost = if (takesOverLift && !lifted) 1f else ghostFade
|
val ghost = if (takesOverLift && !lifted) 1f else ghostFade
|
||||||
val raise = selectionLift(selected, snap = takesOverLift)
|
val raise = selectionLift(selected, snap = takesOverLift)
|
||||||
// Where the selected event's bottom handle sits against the action bar, which
|
// Tells the action bar to fold when it would cover the bottom handle.
|
||||||
// compacts itself rather than cover it. Read off the piece that ends the event.
|
|
||||||
val revealExtraPx = with(density) { (ResizeHandleTarget / 2).toPx() }
|
val revealExtraPx = with(density) { (ResizeHandleTarget / 2).toPx() }
|
||||||
val barClearancePx = with(density) { SelectionBarClearance.toPx() }
|
val barClearancePx = with(density) { SelectionBarClearance.toPx() }
|
||||||
val tracksBar = selected && !cuts.bottom
|
val tracksBar = selected && !cuts.bottom
|
||||||
// Not while the block is waiting on its own write: until then it still
|
// Not while its own write is pending: it still shows the old times.
|
||||||
// shows the times from before it.
|
|
||||||
LaunchedEffect(selected, block.event, lifted) {
|
LaunchedEffect(selected, block.event, lifted) {
|
||||||
if (selected && !lifted) dragController.adoptSelection(block)
|
if (selected && !lifted) dragController.adoptSelection(block)
|
||||||
}
|
}
|
||||||
// Handles on the selected event's own ends — not on a cut at midnight. They
|
// Kept composed while hidden: removing them would cancel their own resize.
|
||||||
// stay composed for the whole selection and only hide while something is in
|
|
||||||
// flight: removing them would cancel the resize they started.
|
|
||||||
DisposableEffect(selected) {
|
DisposableEffect(selected) {
|
||||||
if (selected) dragController.noteSelectedBlock(true)
|
if (selected) dragController.noteSelectedBlock(true)
|
||||||
onDispose { if (selected) dragController.noteSelectedBlock(false) }
|
onDispose { if (selected) dragController.noteSelectedBlock(false) }
|
||||||
|
|||||||
@@ -680,8 +680,7 @@ class RescheduleViewModelTest {
|
|||||||
)
|
)
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
|
|
||||||
// The start changed day but not by a whole number of days, so the
|
// Not a whole-day shift, so the series anchor can't follow.
|
||||||
// series anchor can't follow it.
|
|
||||||
assertThat(vm.scopePrompt.value)
|
assertThat(vm.scopePrompt.value)
|
||||||
.isEqualTo(MoveScopePrompt(occurrenceOnly = true, resize = true))
|
.isEqualTo(MoveScopePrompt(occurrenceOnly = true, resize = true))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user