Crossfade a block's time label (#68)
The block keeps its identity across a move and slides to the new slot, so its label was the one thing left changing in a single frame.
This commit is contained in:
@@ -1,16 +1,50 @@
|
|||||||
package de.jeanlucmakiola.calendula.ui.common
|
package de.jeanlucmakiola.calendula.ui.common
|
||||||
|
|
||||||
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.animation.core.FiniteAnimationSpec
|
import androidx.compose.animation.core.FiniteAnimationSpec
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
import androidx.compose.animation.core.snap
|
import androidx.compose.animation.core.snap
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import de.jeanlucmakiola.floret.identity.rememberReduceMotion
|
import de.jeanlucmakiola.floret.identity.rememberReduceMotion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A timed block's own time label, crossfaded rather than replaced. The block
|
||||||
|
* keeps its identity across a move and slides to the new slot; the label is the
|
||||||
|
* one thing on it that would otherwise change in a single frame.
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
@Composable
|
||||||
|
fun BlockTimeLabel(label: String, color: Color, modifier: Modifier = Modifier) {
|
||||||
|
val spec: FiniteAnimationSpec<Float> = if (rememberReduceMotion()) {
|
||||||
|
snap()
|
||||||
|
} else {
|
||||||
|
MaterialTheme.motionScheme.fastEffectsSpec()
|
||||||
|
}
|
||||||
|
Crossfade(
|
||||||
|
targetState = label,
|
||||||
|
animationSpec = spec,
|
||||||
|
label = "block-time",
|
||||||
|
modifier = modifier,
|
||||||
|
) { text ->
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
color = color,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Where a timed block sits in its column, after tweening. */
|
/** Where a timed block sits in its column, after tweening. */
|
||||||
@Immutable
|
@Immutable
|
||||||
data class BlockPlacement(val x: Dp, val y: Dp, val width: Dp, val height: Dp)
|
data class BlockPlacement(val x: Dp, val y: Dp, val width: Dp, val height: Dp)
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ import de.jeanlucmakiola.calendula.ui.common.TodayAction
|
|||||||
import de.jeanlucmakiola.calendula.ui.common.CalendarFailure
|
import de.jeanlucmakiola.calendula.ui.common.CalendarFailure
|
||||||
import de.jeanlucmakiola.calendula.ui.common.CalendarView
|
import de.jeanlucmakiola.calendula.ui.common.CalendarView
|
||||||
import de.jeanlucmakiola.calendula.ui.common.IMPLEMENTED_VIEWS
|
import de.jeanlucmakiola.calendula.ui.common.IMPLEMENTED_VIEWS
|
||||||
|
import de.jeanlucmakiola.calendula.ui.common.BlockTimeLabel
|
||||||
import de.jeanlucmakiola.calendula.ui.common.animatedBlockPlacement
|
import de.jeanlucmakiola.calendula.ui.common.animatedBlockPlacement
|
||||||
import de.jeanlucmakiola.calendula.ui.common.ghostAlpha
|
import de.jeanlucmakiola.calendula.ui.common.ghostAlpha
|
||||||
import de.jeanlucmakiola.calendula.ui.common.LocalEventMove
|
import de.jeanlucmakiola.calendula.ui.common.LocalEventMove
|
||||||
@@ -762,11 +763,8 @@ private fun EventBlock(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (showTime) {
|
if (showTime) {
|
||||||
Text(
|
BlockTimeLabel(
|
||||||
text = timeLabel,
|
label = timeLabel,
|
||||||
style = MaterialTheme.typography.labelSmall,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
color = eventInk(fill, alpha = SECONDARY_INK_ALPHA),
|
color = eventInk(fill, alpha = SECONDARY_INK_ALPHA),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ import de.jeanlucmakiola.calendula.ui.common.CalendarFailure
|
|||||||
import de.jeanlucmakiola.calendula.ui.common.CalendarView
|
import de.jeanlucmakiola.calendula.ui.common.CalendarView
|
||||||
import de.jeanlucmakiola.calendula.ui.common.IMPLEMENTED_VIEWS
|
import de.jeanlucmakiola.calendula.ui.common.IMPLEMENTED_VIEWS
|
||||||
import de.jeanlucmakiola.calendula.ui.common.EventDimAlpha
|
import de.jeanlucmakiola.calendula.ui.common.EventDimAlpha
|
||||||
|
import de.jeanlucmakiola.calendula.ui.common.BlockTimeLabel
|
||||||
import de.jeanlucmakiola.calendula.ui.common.animatedBlockPlacement
|
import de.jeanlucmakiola.calendula.ui.common.animatedBlockPlacement
|
||||||
import de.jeanlucmakiola.calendula.ui.common.ghostAlpha
|
import de.jeanlucmakiola.calendula.ui.common.ghostAlpha
|
||||||
import de.jeanlucmakiola.calendula.ui.common.LocalEventMove
|
import de.jeanlucmakiola.calendula.ui.common.LocalEventMove
|
||||||
@@ -931,11 +932,8 @@ private fun EventBlock(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (showTime) {
|
if (showTime) {
|
||||||
Text(
|
BlockTimeLabel(
|
||||||
text = timeLabel,
|
label = timeLabel,
|
||||||
style = MaterialTheme.typography.labelSmall,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
color = eventInk(fill, alpha = SECONDARY_INK_ALPHA),
|
color = eventInk(fill, alpha = SECONDARY_INK_ALPHA),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user