Compare commits
2 Commits
aee26a28f3
...
566caf4305
| Author | SHA1 | Date | |
|---|---|---|---|
| 566caf4305 | |||
| d9e4e877cc |
@@ -3,6 +3,7 @@ package de.jeanlucmakiola.floret.components
|
|||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.imePadding
|
import androidx.compose.foundation.layout.imePadding
|
||||||
@@ -25,6 +26,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import de.jeanlucmakiola.floret.identity.predictiveBack
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The family's collapsing scaffold for settings hubs, sub-screens and the
|
* The family's collapsing scaffold for settings hubs, sub-screens and the
|
||||||
@@ -32,6 +34,11 @@ import androidx.compose.ui.unit.dp
|
|||||||
* button, and a vertically scrolling content column. [content] lays out the
|
* button, and a vertically scrolling content column. [content] lays out the
|
||||||
* connected grouped rows. The back content description uses the kit's `back`
|
* connected grouped rows. The back content description uses the kit's `back`
|
||||||
* string, which an app can override with its own (localized) `back`.
|
* string, which an app can override with its own (localized) `back`.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -39,12 +46,16 @@ fun CollapsingScaffold(
|
|||||||
title: String,
|
title: String,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
predictiveBack: Boolean = false,
|
||||||
|
actions: @Composable RowScope.() -> Unit = {},
|
||||||
|
snackbarHost: @Composable () -> Unit = {},
|
||||||
content: @Composable ColumnScope.() -> Unit,
|
content: @Composable ColumnScope.() -> Unit,
|
||||||
) {
|
) {
|
||||||
val scrollBehavior =
|
val scrollBehavior =
|
||||||
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState())
|
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState())
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
.then(if (predictiveBack) Modifier.predictiveBack(onBack = onBack) else Modifier)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(MaterialTheme.colorScheme.surface)
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
@@ -59,12 +70,14 @@ fun CollapsingScaffold(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
actions = actions,
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
colors = TopAppBarDefaults.largeTopAppBarColors(
|
colors = TopAppBarDefaults.largeTopAppBarColors(
|
||||||
scrolledContainerColor = MaterialTheme.colorScheme.surface,
|
scrolledContainerColor = MaterialTheme.colorScheme.surface,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
snackbarHost = snackbarHost,
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
@@ -82,6 +82,12 @@ fun GroupedSurface(
|
|||||||
* One row in a grouped list: an M3 [ListItem] over a [GroupedSurface] (the
|
* One row in a grouped list: an M3 [ListItem] over a [GroupedSurface] (the
|
||||||
* tonal, position-shaped, press-morphing family container). Insets 16dp like
|
* tonal, position-shaped, press-morphing family container). Insets 16dp like
|
||||||
* the lists overview.
|
* the lists overview.
|
||||||
|
*
|
||||||
|
* [selected] highlights the row in the secondary container. [dimmed] fades the
|
||||||
|
* headline and summary to the M3 disabled emphasis while leaving the [trailing]
|
||||||
|
* control at full opacity — for rows that are present but switched off.
|
||||||
|
* [container] overrides the row's tonal colour (e.g. a category tint); ignored
|
||||||
|
* when [selected].
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -91,6 +97,8 @@ fun GroupedRow(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
summary: String? = null,
|
summary: String? = null,
|
||||||
selected: Boolean = false,
|
selected: Boolean = false,
|
||||||
|
dimmed: Boolean = false,
|
||||||
|
container: Color? = null,
|
||||||
minHeight: Dp = 72.dp,
|
minHeight: Dp = 72.dp,
|
||||||
leading: @Composable (() -> Unit)? = null,
|
leading: @Composable (() -> Unit)? = null,
|
||||||
trailing: @Composable (() -> Unit)? = null,
|
trailing: @Composable (() -> Unit)? = null,
|
||||||
@@ -104,13 +112,23 @@ fun GroupedRow(
|
|||||||
supportingColor = MaterialTheme.colorScheme.onSecondaryContainer,
|
supportingColor = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||||
trailingIconColor = MaterialTheme.colorScheme.onSecondaryContainer,
|
trailingIconColor = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||||
)
|
)
|
||||||
|
} else if (dimmed) {
|
||||||
|
// M3 disabled emphasis (0.38α) on the text/leading; the trailing control
|
||||||
|
// stays full-opacity so a toggle reads as live even on a faded row.
|
||||||
|
val muted = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
|
||||||
|
ListItemDefaults.colors(
|
||||||
|
containerColor = Color.Transparent,
|
||||||
|
headlineColor = muted,
|
||||||
|
leadingIconColor = muted,
|
||||||
|
supportingColor = muted,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
ListItemDefaults.colors(containerColor = Color.Transparent)
|
ListItemDefaults.colors(containerColor = Color.Transparent)
|
||||||
}
|
}
|
||||||
val containerColor = if (selected) {
|
val containerColor = when {
|
||||||
MaterialTheme.colorScheme.secondaryContainer
|
selected -> MaterialTheme.colorScheme.secondaryContainer
|
||||||
} else {
|
container != null -> container
|
||||||
MaterialTheme.colorScheme.surfaceContainerHigh
|
else -> MaterialTheme.colorScheme.surfaceContainerHigh
|
||||||
}
|
}
|
||||||
GroupedSurface(
|
GroupedSurface(
|
||||||
position = position,
|
position = position,
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
package de.jeanlucmakiola.floret.components
|
package de.jeanlucmakiola.floret.components
|
||||||
|
|
||||||
|
import android.view.WindowManager
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.Check
|
import androidx.compose.material.icons.rounded.Check
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.SideEffect
|
||||||
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import androidx.compose.ui.window.DialogWindowProvider
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Full-screen scaffold for selection pickers: a full-bleed [Dialog] that reuses
|
* Full-screen scaffold for selection pickers: a full-bleed [Dialog] that reuses
|
||||||
@@ -19,6 +23,7 @@ import androidx.compose.ui.window.DialogProperties
|
|||||||
fun FullScreenPicker(
|
fun FullScreenPicker(
|
||||||
title: String,
|
title: String,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
|
predictiveBack: Boolean = false,
|
||||||
content: @Composable ColumnScope.() -> Unit,
|
content: @Composable ColumnScope.() -> Unit,
|
||||||
) {
|
) {
|
||||||
Dialog(
|
Dialog(
|
||||||
@@ -28,7 +33,21 @@ fun FullScreenPicker(
|
|||||||
decorFitsSystemWindows = false,
|
decorFitsSystemWindows = false,
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
CollapsingScaffold(title = title, onBack = onDismiss, content = content)
|
// The dialog window pans by default when the keyboard opens, which —
|
||||||
|
// combined with the content's own imePadding — leaves a fixed black gap
|
||||||
|
// above the keyboard. Switch it to ADJUST_NOTHING so the window stays
|
||||||
|
// full-screen and imePadding alone lifts the focused field.
|
||||||
|
val view = LocalView.current
|
||||||
|
SideEffect {
|
||||||
|
(view.parent as? DialogWindowProvider)?.window
|
||||||
|
?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
|
||||||
|
}
|
||||||
|
CollapsingScaffold(
|
||||||
|
title = title,
|
||||||
|
onBack = onDismiss,
|
||||||
|
predictiveBack = predictiveBack,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,8 +65,11 @@ fun <T> OptionPicker(
|
|||||||
onSelect: (T) -> Unit,
|
onSelect: (T) -> Unit,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
leading: (@Composable (T) -> Unit)? = null,
|
leading: (@Composable (T) -> Unit)? = null,
|
||||||
|
header: (@Composable ColumnScope.() -> Unit)? = null,
|
||||||
|
predictiveBack: Boolean = false,
|
||||||
) {
|
) {
|
||||||
FullScreenPicker(title = title, onDismiss = onDismiss) {
|
FullScreenPicker(title = title, onDismiss = onDismiss, predictiveBack = predictiveBack) {
|
||||||
|
header?.invoke(this)
|
||||||
options.forEachIndexed { index, option ->
|
options.forEachIndexed { index, option ->
|
||||||
val isSelected = option == selected
|
val isSelected = option == selected
|
||||||
GroupedRow(
|
GroupedRow(
|
||||||
|
|||||||
Reference in New Issue
Block a user