Converge sibling branches into main (0.7.0) (#11)

Brings the branches the sibling apps actually ship from back onto main, so floret-kit stops forking per project.

Merged in:
- `vouch/settings` (what vouch pins), which already contains `feat/clockula` (what clockula pins) and `chore/deps-2026-10`: core-prefs, core-di, CollapsingScaffold FAB/no-back slots, the glance module, LocaleManager on API 33+, and the dependency bump
- `feat/glance-module`: the three glance commits clockula's branch didn't have yet (progress ring/tile/check button, expressive shapes + header pill, WidgetDoneState)

Both merged without conflicts. Version ends up at 0.7.0 (untagged).

Heads-up for consumers: the deps bump moves the toolchain to Gradle 9.8.0 / AGP 9.4.1 / Kotlin 2.4.20. Since floret-kit is a composite build, a consumer must be on Gradle >= 9.6 to pin anything from here on. Calendula builds, tests and lints against this branch with just that toolchain bump, no source changes.

Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/floret-kit/pulls/11
This commit is contained in:
Jean-Luc Makiola
2026-10-07 09:36:15 +02:00
co-authored by makiolaj
parent 318aeab6bd
commit 8b33635a85
55 changed files with 2517 additions and 80 deletions
@@ -13,6 +13,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FabPosition
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LargeTopAppBar
@@ -44,10 +45,17 @@ import de.jeanlucmakiola.floret.identity.predictiveBack
*
* Optional extras, all defaulting to the bare scaffold so existing callers are
* unchanged: [actions] adds trailing app-bar items; [snackbarHost] hosts
* transient messages; [predictiveBack] (off by default) wires the gesture
* preview to [onBack] for full-screen surfaces that want it, with [visible]
* passed through to [Modifier.predictiveBack] for a surface that is shown and
* hidden in place.
* transient messages; [floatingActionButton] (with
* [floatingActionButtonPosition]) places a FAB the underlying [Scaffold]
* positions and insets — for a list destination whose primary action is "add";
* [predictiveBack] (off by default) wires the gesture preview to [onBack] for
* full-screen surfaces that want it, with [visible] passed through to
* [Modifier.predictiveBack] for a surface that is shown and hidden in place.
*
* [onBack] is optional. Pass null for a **top-level destination** — a tab in an
* app's navigation bar, say — which has nothing to go back to: no navigation
* icon is drawn, and [predictiveBack] wires nothing, so the gesture falls
* through to whoever owns the back stack above.
*
* Set [scrollable] to false when [content] brings its own scrolling container.
* The default column scrolls as a whole, which composes every row up front —
@@ -63,7 +71,7 @@ import de.jeanlucmakiola.floret.identity.predictiveBack
@Composable
fun CollapsingScaffold(
title: String,
onBack: () -> Unit,
onBack: (() -> Unit)? = null,
modifier: Modifier = Modifier,
largeTopBar: Boolean = true,
predictiveBack: Boolean = false,
@@ -71,6 +79,8 @@ fun CollapsingScaffold(
scrollable: Boolean = true,
actions: @Composable RowScope.() -> Unit = {},
snackbarHost: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
content: @Composable ColumnScope.() -> Unit,
) {
val scrollBehavior = if (largeTopBar) {
@@ -80,17 +90,27 @@ fun CollapsingScaffold(
}
Scaffold(
modifier = modifier
.then(if (predictiveBack) Modifier.predictiveBack(onBack = onBack, visible = visible) else Modifier)
.then(
if (predictiveBack && onBack != null) {
Modifier.predictiveBack(onBack = onBack, visible = visible)
} else {
Modifier
},
)
.fillMaxSize()
.background(MaterialTheme.colorScheme.surface)
.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
val navigationIcon = @Composable {
IconButton(onClick = onBack) {
Icon(
Icons.AutoMirrored.Rounded.ArrowBack,
contentDescription = stringResource(R.string.back),
)
// A top-level destination has nothing to go back to, so a null
// [onBack] draws no arrow at all rather than an arrow that lies.
if (onBack != null) {
IconButton(onClick = onBack) {
Icon(
Icons.AutoMirrored.Rounded.ArrowBack,
contentDescription = stringResource(R.string.back),
)
}
}
}
if (largeTopBar) {
@@ -116,6 +136,8 @@ fun CollapsingScaffold(
}
},
snackbarHost = snackbarHost,
floatingActionButton = floatingActionButton,
floatingActionButtonPosition = floatingActionButtonPosition,
) { innerPadding ->
Column(
modifier = Modifier
@@ -123,7 +123,7 @@ fun LanguagePickerRow(
) {
val context = LocalContext.current
val supported = remember { AppLanguage.supportedTags(context, localesConfig) }
var current by remember { mutableStateOf(AppLanguage.currentTag(supported)) }
var current by remember { mutableStateOf(AppLanguage.currentTag(context, supported)) }
var showDialog by remember { mutableStateOf(false) }
// null = follow the system; the rest are BCP-47 tags from locales_config.xml.
val options = remember(supported) { listOf<String?>(null) + supported }
@@ -145,7 +145,7 @@ fun LanguagePickerRow(
label = { label(it) },
onSelect = {
current = it
AppLanguage.apply(it)
AppLanguage.apply(context, it)
},
onDismiss = { showDialog = false },
)