From 0a89ff9d0e8ccde91a9ac2a7376087e8296b0d07 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Fri, 3 Jul 2026 15:22:18 +0200 Subject: [PATCH] fix(edit): pin the picker title instead of a large collapsing header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full-screen selection pickers opened with the LargeTopAppBar expanded, so the title started below the bar and only settled into it after a scroll — a tall empty header above a short option list. Give CollapsingScaffold a largeTopBar flag and have FullScreenPicker use a pinned single-line TopAppBar, so every picker (calendar, theme, week start, language, reminder, agenda range) shows its title in the bar from the start. Settings and the calendar manager keep the large collapsing header. Co-Authored-By: Claude Opus 4.8 --- .../calendula/ui/common/GroupedList.kt | 63 +++++++++++++------ .../calendula/ui/common/Picker.kt | 12 +++- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/GroupedList.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/GroupedList.kt index c183aab..de2a542 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/GroupedList.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/GroupedList.kt @@ -28,6 +28,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable @@ -57,10 +58,16 @@ fun positionOf(index: Int, count: Int): Position = when { } /** - * The app's standard full-screen list scaffold: a collapsing [LargeTopAppBar] - * whose title shrinks into the bar (next to the back button) as the content - * scrolls. Content is a scrollable column that feeds the toolbar via nested - * scroll. Used by Settings and the calendar manager so they share one shell. + * The app's standard full-screen list scaffold. By default a collapsing + * [LargeTopAppBar] whose title shrinks into the bar (next to the back button) as + * the content scrolls — used by Settings and the calendar manager, where the + * large header sets the page. Content is a scrollable column that feeds the + * toolbar via nested scroll. + * + * Set [largeTopBar] to false for a pinned, single-line [TopAppBar] instead: the + * title sits in the bar from the start, with no expanded header to scroll past. + * Preferred for selection pickers, where the tall header is just wasted space + * above the options. */ @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -68,12 +75,16 @@ fun CollapsingScaffold( title: String, onBack: () -> Unit, modifier: Modifier = Modifier, + largeTopBar: Boolean = true, snackbarHost: @Composable () -> Unit = {}, actions: @Composable RowScope.() -> Unit = {}, content: @Composable ColumnScope.() -> Unit, ) { - val scrollBehavior = + val scrollBehavior = if (largeTopBar) { TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) + } else { + TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) + } Scaffold( modifier = modifier .predictiveBack(onBack = onBack) @@ -81,22 +92,34 @@ fun CollapsingScaffold( .background(MaterialTheme.colorScheme.surface) .nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { - LargeTopAppBar( - title = { Text(title) }, - navigationIcon = { - IconButton(onClick = onBack) { - Icon( - Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(R.string.settings_back), - ) - } - }, - actions = actions, - scrollBehavior = scrollBehavior, - colors = TopAppBarDefaults.topAppBarColors( - scrolledContainerColor = MaterialTheme.colorScheme.surface, - ), + val navigationIcon = @Composable { + IconButton(onClick = onBack) { + Icon( + Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = stringResource(R.string.settings_back), + ) + } + } + val colors = TopAppBarDefaults.topAppBarColors( + scrolledContainerColor = MaterialTheme.colorScheme.surface, ) + if (largeTopBar) { + LargeTopAppBar( + title = { Text(title) }, + navigationIcon = navigationIcon, + actions = actions, + scrollBehavior = scrollBehavior, + colors = colors, + ) + } else { + TopAppBar( + title = { Text(title) }, + navigationIcon = navigationIcon, + actions = actions, + scrollBehavior = scrollBehavior, + colors = colors, + ) + } }, snackbarHost = snackbarHost, ) { innerPadding -> diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/Picker.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/Picker.kt index ce6abae..66b24df 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/Picker.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/Picker.kt @@ -43,8 +43,9 @@ import de.jeanlucmakiola.calendula.ui.agenda.AgendaRange /** * Shared full-screen scaffold for selection pickers: a full-bleed [Dialog] that - * reuses the app's [CollapsingScaffold] (collapsing title + back button), so a - * picker is visually identical to a Settings sub-page and uses the full width. + * reuses the app's [CollapsingScaffold] (back button + full width), but with a + * pinned single-line title rather than the large collapsing header — a picker is + * a short list, so the tall header would only be empty space to scroll past. * [content] places the connected grouped rows; selecting one calls [onDismiss]. */ @Composable @@ -69,7 +70,12 @@ fun FullScreenPicker( (view.parent as? DialogWindowProvider)?.window ?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING) } - CollapsingScaffold(title = title, onBack = onDismiss, content = content) + CollapsingScaffold( + title = title, + onBack = onDismiss, + largeTopBar = false, + content = content, + ) } }