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 f41d654b..1a769156 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/CalendarHost.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/CalendarHost.kt @@ -391,6 +391,8 @@ fun CalendarHost( val reschedule: RescheduleViewModel = hiltViewModel() val movableCalendarIds by reschedule.movableCalendarIds.collectAsStateWithLifecycle() val eventActions: EventActionsViewModel = hiltViewModel() + val moveOutcome by reschedule.outcome.collectAsStateWithLifecycle() + val selectionFailure by eventActions.failure.collectAsStateWithLifecycle() val onEditEvent: (EventInstance) -> Unit = { event -> val key = longArrayOf( event.eventId, @@ -543,6 +545,11 @@ fun CalendarHost( importFormSource = ImportSource.Insert importForm = form }, + chipShown = moveOutcome != null || selectionFailure != null, + onDismissChip = { + reschedule.consumeOutcome() + eventActions.consumeFailure() + }, ) CalendarFab(chromeHost, todayInToolbar, fabSelection) }, @@ -600,12 +607,10 @@ fun CalendarHost( // Scope prompt + confirmation/undo snackbar for a dropped event, declared // right after the calendar views so later overlays cover it. - val actionFailure by eventActions.failure.collectAsStateWithLifecycle() EventMoveHost( reschedule, modifier = Modifier.fillMaxSize(), - raised = chromeHost.selectionBarShown && chromeHost.selectionBarExpanded, - actionFailure = eventActionFailureMessage(actionFailure), + actionFailure = eventActionFailureMessage(selectionFailure), onActionFailureShown = eventActions::consumeFailure, ) diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/CalendarChrome.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/CalendarChrome.kt index a3f41db0..41acd8fe 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/CalendarChrome.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/CalendarChrome.kt @@ -90,10 +90,6 @@ class ChromeHost { var selectionCompact by mutableStateOf(false) internal set - /** Whether the selection's bar is unfolded, rather than folded into its button. */ - var selectionBarExpanded by mutableStateOf(false) - internal set - /** Whether the selection's action bar is showing — it steps aside mid-gesture. */ val selectionBarShown: Boolean get() = selection != null && !selectionBusy diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/CalendarFabColumn.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/CalendarFabColumn.kt index ca255f7f..71a6e3be 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/CalendarFabColumn.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/CalendarFabColumn.kt @@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight import androidx.compose.material.icons.filled.Add @@ -90,8 +89,6 @@ fun CalendarFabColumn( ) { AnimatedVisibility( visible = selection?.expanded == true, - // Overflows the row evenly above and below, centred on the FAB. - modifier = Modifier.wrapContentHeight(unbounded = true), enter = expandHorizontally( animationSpec = MaterialTheme.motionScheme.fastSpatialSpec(), expandFrom = Alignment.End, diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventMoveHost.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventMoveHost.kt index 71c2bec7..0ab8a4a9 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventMoveHost.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventMoveHost.kt @@ -51,7 +51,6 @@ private val FAB_BAND = 88.dp fun EventMoveHost( viewModel: RescheduleViewModel, modifier: Modifier = Modifier, - raised: Boolean = false, actionFailure: String? = null, onActionFailureShown: () -> Unit = {}, ) { @@ -126,11 +125,7 @@ fun EventMoveHost( modifier = Modifier .align(Alignment.BottomStart) .navigationBarsPadding() - // Above the selection bar while it is up (#141). - .padding( - start = SnackChipMargin, - bottom = SnackChipMargin + if (raised) SelectionBarClearance else 0.dp, - ) + .padding(start = SnackChipMargin, bottom = SnackChipMargin) .height(SnackChipHeight), contentAlignment = Alignment.CenterStart, ) { diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventSelectionBar.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventSelectionBar.kt index 8568030d..7e0a18eb 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventSelectionBar.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventSelectionBar.kt @@ -1,23 +1,27 @@ package de.jeanlucmakiola.calendula.ui.common +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ContentCopy import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Share -import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.FloatingToolbarDefaults -import androidx.compose.material3.HorizontalFloatingToolbar import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import de.jeanlucmakiola.calendula.R /** The selected event's action bar (#141); Edit and Delete only where its calendar allows. */ -@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun EventSelectionBar( canEdit: Boolean, @@ -28,15 +32,23 @@ fun EventSelectionBar( onDelete: () -> Unit, modifier: Modifier = Modifier, ) { - HorizontalFloatingToolbar( - expanded = true, - colors = FloatingToolbarDefaults.vibrantFloatingToolbarColors(), - modifier = modifier, + // The FAB's height and colours, so the two read as one control. + Surface( + shape = CircleShape, + color = MaterialTheme.colorScheme.primaryContainer, + contentColor = MaterialTheme.colorScheme.onPrimaryContainer, + shadowElevation = 6.dp, + modifier = modifier.height(56.dp), ) { - if (canEdit) BarAction(Icons.Default.Edit, R.string.event_detail_edit, onEdit) - BarAction(Icons.Default.ContentCopy, R.string.event_detail_duplicate, onDuplicate) - BarAction(Icons.Default.Share, R.string.event_detail_share, onShare) - if (canDelete) BarAction(Icons.Default.Delete, R.string.event_detail_delete, onDelete) + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(horizontal = 4.dp), + ) { + if (canEdit) BarAction(Icons.Default.Edit, R.string.event_detail_edit, onEdit) + BarAction(Icons.Default.ContentCopy, R.string.event_detail_duplicate, onDuplicate) + BarAction(Icons.Default.Share, R.string.event_detail_share, onShare) + if (canDelete) BarAction(Icons.Default.Delete, R.string.event_detail_delete, onDelete) + } } } diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventSelectionHost.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventSelectionHost.kt index 2acb5095..f2353536 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventSelectionHost.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/EventSelectionHost.kt @@ -1,7 +1,6 @@ package de.jeanlucmakiola.calendula.ui.common import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -17,8 +16,8 @@ import de.jeanlucmakiola.calendula.domain.EventInstance import de.jeanlucmakiola.floret.components.SnackChipMargin import kotlinx.coroutines.launch -/** How far the bottom chips rise while the selection bar is open, to clear it. */ -val SelectionBarClearance = 64.dp + SnackChipMargin +/** The bottom band the selection bar and FAB cover, margin included. */ +val SelectionBarClearance = 56.dp + SnackChipMargin /** The FAB's role while an event is selected (#141): its action bar's toggle. Null otherwise. */ @Composable @@ -27,6 +26,8 @@ fun rememberFabSelection( viewModel: EventActionsViewModel, onEdit: (EventInstance) -> Unit, onDuplicate: (EventForm) -> Unit, + chipShown: Boolean, + onDismissChip: () -> Unit, ): FabSelection? { val calendars by viewModel.calendars.collectAsStateWithLifecycle() val context = LocalContext.current @@ -36,8 +37,8 @@ fun rememberFabSelection( // What the FAB has been told for this selection, over what the coverage says. var chosen by remember(host.selection?.event) { mutableStateOf(null) } - val expanded = chosen ?: !host.selectionCompact - SideEffect { host.selectionBarExpanded = expanded && host.selection != null } + // Folded while a chip is up, which then sits beside the FAB as usual. + val expanded = !chipShown && (chosen ?: !host.selectionCompact) val selection = host.selection ?: return null val event = selection.event @@ -45,7 +46,10 @@ fun rememberFabSelection( val canEdit = calendar?.canModifyContents == true return FabSelection( expanded = expanded, - onToggle = { chosen = !expanded }, + onToggle = { + if (chipShown) onDismissChip() + chosen = !expanded + }, bar = { EventSelectionBar( canEdit = canEdit,