- Swiping between days in the split month pane now ticks, like pulling the pane open and closed already did. - Dropping a moved or resized event gives a confirm buzz. It only fires on a real drop; putting an event back where it was, cancelling, or undoing stays silent. - Fixes a #141 flicker: after a resize, the block dimmed briefly as the floating copy faded out. It now takes over at full opacity, as a block released in place already did. Deviation from the issue: the original ask (haptics when swiping the split view up and down) shipped in 2.22.0, so this covers the remaining gaps from the analysis. Month and week paging stays silent on purpose, and settings switches are left for a later floret-kit change. Closes #116 Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de> Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/401
This commit is contained in:
@@ -212,11 +212,21 @@ private fun rememberPickUpSurface(
|
||||
}
|
||||
}
|
||||
|
||||
/** A lift on pickup, then a tick every time the drop target snaps to a new slot. */
|
||||
/**
|
||||
* A lift on pickup, a tick every time the drop target snaps to a new slot, and a
|
||||
* confirm each time [drops] counts a real drop.
|
||||
*/
|
||||
@Composable
|
||||
fun DragSnapHaptics(slot: Any?) {
|
||||
fun DragSnapHaptics(slot: Any?, drops: Int) {
|
||||
val haptics = LocalHapticFeedback.current
|
||||
val previous = remember { arrayOfNulls<Any>(1) }
|
||||
val dropsSeen = remember { intArrayOf(drops) }
|
||||
LaunchedEffect(drops) {
|
||||
if (drops != dropsSeen[0]) {
|
||||
dropsSeen[0] = drops
|
||||
haptics.performHapticFeedback(HapticFeedbackType.Confirm)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(slot) {
|
||||
val had = previous[0]
|
||||
previous[0] = slot
|
||||
|
||||
@@ -21,6 +21,7 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -467,6 +468,10 @@ class TimelineDragController {
|
||||
var settling: TimelineDrag? by mutableStateOf(null)
|
||||
private set
|
||||
|
||||
/** How many real drops this controller has handed back. */
|
||||
var drops by mutableIntStateOf(0)
|
||||
private set
|
||||
|
||||
/**
|
||||
* Which block is lifted, and whether anything is. Separate snapshot state
|
||||
* from [drag], which changes every frame, so blocks that only need "am I the
|
||||
@@ -770,6 +775,7 @@ class TimelineDragController {
|
||||
liftedInstanceId = landed.event.instanceId
|
||||
settledOrigin = landed.event.eventId to landed.event.start
|
||||
settledAbandonedMark = abandonedTicks?.value ?: 0
|
||||
drops++
|
||||
return TimelineDrop(landed.event, landed.date, landed.startMin, clipOffset)
|
||||
}
|
||||
|
||||
@@ -826,6 +832,7 @@ class TimelineDragController {
|
||||
settledOrigin = landed.event.eventId to landed.event.start
|
||||
settledAbandonedMark = abandonedTicks?.value ?: 0
|
||||
settledEnd = drop.end
|
||||
drops++
|
||||
return drop
|
||||
}
|
||||
|
||||
@@ -1160,7 +1167,7 @@ fun TimelineDragOverlay(controller: TimelineDragController, modifier: Modifier =
|
||||
|
||||
// Both live here rather than beside the controller: they read [drag], which
|
||||
// changes every frame, and this composable is the one that is meant to.
|
||||
DragSnapHaptics(controller.drag?.slot)
|
||||
DragSnapHaptics(controller.drag?.slot, controller.drops)
|
||||
// Only once the finger has moved: a long press held near an edge must not
|
||||
// scroll the drop target out from under a finger that only means to select.
|
||||
LaunchedEffect(controller.isDragging && controller.pointerMoved) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -133,6 +134,10 @@ class MonthDragController {
|
||||
var settling: MonthChipDrag? by mutableStateOf(null)
|
||||
private set
|
||||
|
||||
/** How many real drops this controller has handed back. */
|
||||
var drops by mutableIntStateOf(0)
|
||||
private set
|
||||
|
||||
/**
|
||||
* Which chip is lifted, and whether anything is. Separate snapshot state from
|
||||
* [drag], which changes every frame, so the chips and rows that only need
|
||||
@@ -291,6 +296,7 @@ class MonthDragController {
|
||||
settledGhost = true
|
||||
undoable = settling
|
||||
settledAbandonedMark = abandonedTicks?.value ?: 0
|
||||
drops++
|
||||
return MonthChipDrop(landed.event, landed.grabDate, target)
|
||||
}
|
||||
|
||||
|
||||
@@ -596,7 +596,7 @@ private fun MonthDragOverlay(controller: MonthDragController) {
|
||||
val moveInFlight = moveInFlight()
|
||||
// Here rather than beside the controller: this reads [drag], which changes
|
||||
// every frame, and this composable is the one that is meant to.
|
||||
DragSnapHaptics(controller.drag?.targetDate)
|
||||
DragSnapHaptics(controller.drag?.targetDate, controller.drops)
|
||||
// Hold the landed chip until the write is done — including the time a
|
||||
// recurring drop's scope dialog is up — and then, unlike the timeline, carry
|
||||
// it to its seat: it was dropped at whatever height the finger was at, while
|
||||
@@ -1463,8 +1463,14 @@ private fun SplitMonthContent(
|
||||
showWeekNumbers = showWeekNumbers,
|
||||
expandDrag = expandDrag,
|
||||
onSelectDay = onSelectDay,
|
||||
onPaneSwipeNext = onPaneSwipeNext,
|
||||
onPaneSwipePrev = onPaneSwipePrev,
|
||||
onPaneSwipeNext = {
|
||||
onPaneSwipeNext()
|
||||
haptics.performHapticFeedback(HapticFeedbackType.SegmentTick)
|
||||
},
|
||||
onPaneSwipePrev = {
|
||||
onPaneSwipePrev()
|
||||
haptics.performHapticFeedback(HapticFeedbackType.SegmentTick)
|
||||
},
|
||||
onClearSelection = onClearSelection,
|
||||
onOpenDay = onOpenDay,
|
||||
onEventClick = onEventClick,
|
||||
|
||||
@@ -587,7 +587,8 @@ private fun WeekContent(
|
||||
dragController.select(resized)
|
||||
return@resize
|
||||
}
|
||||
dragController.select(resized, moved = true)
|
||||
// Still lifted under its copy, so it takes over without the ghost's fade-in dip.
|
||||
dragController.select(resized, moved = true, fromLift = true)
|
||||
move?.move(
|
||||
MoveRequest(
|
||||
eventId = drop.event.eventId,
|
||||
|
||||
Reference in New Issue
Block a user