From 24dabe7cb5f6853439bbe6d62cd42d0782912a04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s?= Date: Tue, 24 Feb 2026 20:59:28 +0100 Subject: [PATCH 01/11] Extract core module --- CLAUDE.md | 54 +++++++++++-- .../settings/sample/shared/SettingsScreen.kt | 1 - .../sample/shared/internal/SampleSection.kt | 2 +- settings.gradle.kts | 1 + ui-base/build.gradle.kts | 6 ++ .../ui/base/internal/SettingsTileDefaults.kt | 63 +-------------- .../ui/base/internal/SettingsTileScaffold.kt | 2 + ui-core/build.gradle.kts | 9 +++ ui-core/config/ktlint/baseline.xml | 3 + .../settings/ui/core}/CompositionLocals.kt | 3 +- .../settings/ui/core}/SettingsTextStyles.kt | 2 +- .../settings/ui/core}/SettingsTileColors.kt | 2 +- .../settings/ui/core/SettingsTileConstants.kt | 5 ++ .../ui/core/SettingsTileCoreDefaults.kt | 78 +++++++++++++++++++ .../ui/expressive/SettingsButtonGroup.kt | 4 +- .../compose/settings/ui/SettingsSegmented.kt | 4 +- .../compose/settings/ui/SettingsSlider.kt | 4 +- .../compose/settings/ui/SettingsCheckbox.kt | 6 +- .../compose/settings/ui/SettingsGroup.kt | 7 +- .../compose/settings/ui/SettingsMenuLink.kt | 6 +- .../settings/ui/SettingsRadioButton.kt | 6 +- .../compose/settings/ui/SettingsSwitch.kt | 6 +- .../settings/ui/SettingsTriStateCheckbox.kt | 6 +- 23 files changed, 181 insertions(+), 99 deletions(-) create mode 100644 ui-core/build.gradle.kts create mode 100644 ui-core/config/ktlint/baseline.xml rename {ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal => ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core}/CompositionLocals.kt (81%) rename {ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal => ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core}/SettingsTextStyles.kt (80%) rename {ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal => ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core}/SettingsTileColors.kt (95%) create mode 100644 ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileConstants.kt create mode 100644 ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt diff --git a/CLAUDE.md b/CLAUDE.md index 201b2a2d..60b5e917 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,40 +12,78 @@ Jetpack Compose. It targets Android, iOS, Desktop (JVM), JavaScript, and WebAsse ### Module Structure -The project is organized into three main library modules: +The project is organized into four main library modules: -1. **ui-base** - Foundation module containing shared internal components +1. **ui-core** - Pure Kotlin shared logic (no Material 3 dependencies) + - Location: `ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/` + - Provides: `SettingsTileColors`, `SettingsTextStyles`, `CompositionLocals` ( + `LocalSettingsGroupEnabled`, `LocalSettingsTileColors`, `LocalSettingsTextStyles`), + `SettingsTileConstants` + - Contains only pure data classes and composition locals - no UI rendering + - Used by both standard and expressive Material 3 implementations + +2. **ui-base** - Standard Material 3 foundation module - Location: `ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/` - - Provides: `SettingsTileScaffold`, `SettingsTileDefaults`, `SettingsTileColors`, - `CompositionLocals` + - Depends on: `ui-core`, standard Material 3 + - Provides: `SettingsTileScaffold` (wraps Material 3's `ListItem`), `SettingsTileDefaults` - Contains no public API - only internal utilities used by other modules -2. **ui-tiles** - Core settings components +3. **ui-tiles** - Core settings components (standard Material 3) - Location: `ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/` - Depends on: `ui-base` - Components: `SettingsMenuLink`, `SettingsCheckbox`, `SettingsRadioButton`, `SettingsSwitch`, `SettingsTriStateCheckbox`, `SettingsGroup` - Published as: `com.github.alorma.compose-settings:ui-tiles` -3. **ui-tiles-extended** - Advanced/extended settings components +4. **ui-tiles-extended** - Advanced/extended settings components (standard Material 3) - Location: `ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/` - Depends on: `ui-base` - Components: `SettingsSlider`, `SettingsSegmented` - Published as: `com.github.alorma.compose-settings:ui-tiles-extended` +5. **ui-tiles-expressive** - Expressive Material 3 components + - Location: `ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/` + - Depends on: `ui-base`, Material 3 Expressive + - Components: `SettingsButtonGroup` + - Published as: `com.github.alorma.compose-settings:ui-tiles-expressive` + ### Design Patterns All settings components follow a consistent pattern: -1. Built on top of `SettingsTileScaffold` (from `ui-base`) +1. Built on top of `SettingsTileScaffold` (from `ui-base` or `ui-base-expressive`) 2. Accept common parameters: `title`, `subtitle`, `icon`, `modifier`, `enabled`, `colors` 3. Use Material 3 components internally (ListItem, Checkbox, Switch, etc.) 4. Support `LocalSettingsGroupEnabled` for hierarchical enabled state -5. Leverage `SettingsTileColors` for consistent theming across all components +5. Leverage `SettingsTileColors` and `SettingsTextStyles` (from `ui-core`) for consistent theming + across all components The `SettingsTileScaffold` wraps Material 3's `ListItem` and provides consistent layout and color handling. +### Architecture Layers + +The library uses a layered architecture: + +``` +ui-core (pure Kotlin - colors, text styles, composition locals) + ↓ +ui-base (standard Material 3 - ListItem scaffold) + ↓ +ui-tiles, ui-tiles-extended (standard Material 3 components) + +ui-core (pure Kotlin - colors, text styles, composition locals) + ↓ +ui-base-expressive (Material 3 Expressive - SegmentedListItem scaffold) [PLANNED] + ↓ +ui-tiles-expressive (expressive Material 3 components) +``` + +This separation allows: +- **Code reuse**: Pure logic (colors, styles) shared between standard and expressive variants +- **Flexibility**: Easy to add support for new Material 3 variants +- **Type safety**: Strong separation between rendering layer and data layer + ### Platform-Specific Code Platform-specific implementations are in separate source sets: diff --git a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt index 3e32e997..d5d74dcd 100644 --- a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt +++ b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt @@ -37,7 +37,6 @@ import com.alorma.compose.settings.ui.SettingsSlider import com.alorma.compose.settings.ui.SettingsSwitch import com.alorma.compose.settings.ui.SettingsTriStateCheckbox import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults -import com.alorma.compose.settings.ui.base.internal.SettingsTextStyles import com.alorma.compose.settings.ui.expressive.SettingsButtonGroup @OptIn(ExperimentalMaterial3Api::class) diff --git a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/internal/SampleSection.kt b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/internal/SampleSection.kt index 5015cd8c..e48fb094 100644 --- a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/internal/SampleSection.kt +++ b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/internal/SampleSection.kt @@ -9,8 +9,8 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp import com.alorma.compose.settings.ui.SettingsGroup -import com.alorma.compose.settings.ui.base.internal.LocalSettingsTileColors import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults +import com.alorma.compose.settings.ui.core.LocalSettingsTileColors @Composable internal fun SampleSection( diff --git a/settings.gradle.kts b/settings.gradle.kts index 76545485..d1da4ab4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -25,6 +25,7 @@ include(":samples:webApp") include(":samples:iosApp") include(":samples:androidApp") +include(":ui-core") include(":ui-base") include(":ui-tiles") include(":ui-tiles-extended") diff --git a/ui-base/build.gradle.kts b/ui-base/build.gradle.kts index a95af297..a4f76c19 100644 --- a/ui-base/build.gradle.kts +++ b/ui-base/build.gradle.kts @@ -6,4 +6,10 @@ kotlin { androidLibrary { namespace = libs.versions.namespace.get() + ".ui.base" } + + sourceSets { + commonMain.dependencies { + api(projects.uiCore) + } + } } diff --git a/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileDefaults.kt b/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileDefaults.kt index 270f3ac0..39a285b7 100644 --- a/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileDefaults.kt +++ b/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileDefaults.kt @@ -1,71 +1,14 @@ package com.alorma.compose.settings.ui.base.internal import androidx.compose.material3.ListItemDefaults -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Dp +import com.alorma.compose.settings.ui.core.SettingsTileCoreDefaults -object SettingsTileDefaults { - val Elevation: Dp = ListItemDefaults.Elevation - const val DisabledAlpha: Float = 0.38f +object SettingsTileDefaults : SettingsTileCoreDefaults() { + override val Elevation: Dp = ListItemDefaults.Elevation @Composable fun shape(): Shape = ListItemDefaults.shape - - @Composable - fun textStyles( - groupTitleStyle: TextStyle = - LocalSettingsTextStyles.current?.groupTitleStyle - ?: MaterialTheme.typography.titleMedium, - titleStyle: TextStyle = - LocalSettingsTextStyles.current?.titleStyle - ?: MaterialTheme.typography.bodyLarge, - subtitleStyle: TextStyle = - LocalSettingsTextStyles.current?.subtitleStyle - ?: MaterialTheme.typography.bodyMedium, - ): SettingsTextStyles = - SettingsTextStyles( - groupTitleStyle = groupTitleStyle, - titleStyle = titleStyle, - subtitleStyle = subtitleStyle, - ) - - @Composable - fun colors( - containerColor: Color = - LocalSettingsTileColors.current?.containerColor - ?: MaterialTheme.colorScheme.surface, - titleColor: Color = - LocalSettingsTileColors.current?.titleColor - ?: MaterialTheme.colorScheme.primary, - groupTitleColor: Color = - LocalSettingsTileColors.current?.groupTitleColor - ?: MaterialTheme.colorScheme.onBackground, - iconColor: Color = LocalSettingsTileColors.current?.iconColor ?: MaterialTheme.colorScheme.onSurface, - subtitleColor: Color = LocalSettingsTileColors.current?.subtitleColor ?: MaterialTheme.colorScheme.onSurface, - actionColor: Color = - LocalSettingsTileColors.current?.actionColor - ?: MaterialTheme.colorScheme.primary, - disabledTitleColor: Color = titleColor.copy(alpha = DisabledAlpha), - disabledGroupTitleColor: Color = groupTitleColor.copy(alpha = DisabledAlpha), - disabledIconColor: Color = iconColor.copy(alpha = DisabledAlpha), - disabledSubtitleColor: Color = subtitleColor.copy(alpha = DisabledAlpha), - disabledActionColor: Color = actionColor.copy(alpha = DisabledAlpha), - ): SettingsTileColors = - SettingsTileColors( - containerColor = containerColor, - titleColor = titleColor, - groupTitleColor = groupTitleColor, - iconColor = iconColor, - subtitleColor = subtitleColor, - actionColor = actionColor, - disabledTitleColor = disabledTitleColor, - disabledGroupTitleColor = disabledGroupTitleColor, - disabledIconColor = disabledIconColor, - disabledSubtitleColor = disabledSubtitleColor, - disabledActionColor = disabledActionColor, - ) } diff --git a/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileScaffold.kt b/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileScaffold.kt index 8cc5fd9d..c68b014f 100644 --- a/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileScaffold.kt +++ b/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileScaffold.kt @@ -13,6 +13,8 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Dp +import com.alorma.compose.settings.ui.core.SettingsTileColors +import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable fun SettingsTileScaffold( diff --git a/ui-core/build.gradle.kts b/ui-core/build.gradle.kts new file mode 100644 index 00000000..904c9388 --- /dev/null +++ b/ui-core/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("compose.library") +} + +kotlin { + androidLibrary { + namespace = libs.versions.namespace.get() + ".ui.core" + } +} diff --git a/ui-core/config/ktlint/baseline.xml b/ui-core/config/ktlint/baseline.xml new file mode 100644 index 00000000..98142077 --- /dev/null +++ b/ui-core/config/ktlint/baseline.xml @@ -0,0 +1,3 @@ + + + diff --git a/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/CompositionLocals.kt b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/CompositionLocals.kt similarity index 81% rename from ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/CompositionLocals.kt rename to ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/CompositionLocals.kt index f98d3e5e..ba493a79 100644 --- a/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/CompositionLocals.kt +++ b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/CompositionLocals.kt @@ -1,8 +1,7 @@ -package com.alorma.compose.settings.ui.base.internal +package com.alorma.compose.settings.ui.core import androidx.compose.runtime.ProvidableCompositionLocal import androidx.compose.runtime.compositionLocalOf -import androidx.compose.ui.text.TextStyle val LocalSettingsGroupEnabled: ProvidableCompositionLocal = compositionLocalOf { true } diff --git a/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTextStyles.kt b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTextStyles.kt similarity index 80% rename from ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTextStyles.kt rename to ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTextStyles.kt index 95c7b5f3..da0a02ad 100644 --- a/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTextStyles.kt +++ b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTextStyles.kt @@ -1,4 +1,4 @@ -package com.alorma.compose.settings.ui.base.internal +package com.alorma.compose.settings.ui.core import androidx.compose.runtime.Immutable import androidx.compose.ui.text.TextStyle diff --git a/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileColors.kt b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileColors.kt similarity index 95% rename from ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileColors.kt rename to ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileColors.kt index fc02d80f..d090a2ed 100644 --- a/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileColors.kt +++ b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileColors.kt @@ -1,4 +1,4 @@ -package com.alorma.compose.settings.ui.base.internal +package com.alorma.compose.settings.ui.core import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable diff --git a/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileConstants.kt b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileConstants.kt new file mode 100644 index 00000000..e711dbfc --- /dev/null +++ b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileConstants.kt @@ -0,0 +1,5 @@ +package com.alorma.compose.settings.ui.core + +object SettingsTileConstants { + const val DisabledAlpha: Float = 0.38f +} diff --git a/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt new file mode 100644 index 00000000..abae8e88 --- /dev/null +++ b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt @@ -0,0 +1,78 @@ +package com.alorma.compose.settings.ui.core + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.Dp + +/** + * Shared defaults for creating SettingsTileColors and SettingsTextStyles. + * These are framework-agnostic and work with both standard and expressive Material 3. + */ +abstract class SettingsTileCoreDefaults { + + abstract val Elevation: Dp + + /** + * Creates a [SettingsTextStyles] with default values from MaterialTheme typography + * and respects [LocalSettingsTextStyles] overrides. + */ + @Composable + fun textStyles( + groupTitleStyle: TextStyle = + LocalSettingsTextStyles.current?.groupTitleStyle + ?: MaterialTheme.typography.titleMedium, + titleStyle: TextStyle = + LocalSettingsTextStyles.current?.titleStyle + ?: MaterialTheme.typography.bodyLarge, + subtitleStyle: TextStyle = + LocalSettingsTextStyles.current?.subtitleStyle + ?: MaterialTheme.typography.bodyMedium, + ): SettingsTextStyles = + SettingsTextStyles( + groupTitleStyle = groupTitleStyle, + titleStyle = titleStyle, + subtitleStyle = subtitleStyle, + ) + + /** + * Creates a [SettingsTileColors] with default values from MaterialTheme color scheme + * and respects [LocalSettingsTileColors] overrides. + */ + @Composable + fun colors( + containerColor: Color = + LocalSettingsTileColors.current?.containerColor + ?: MaterialTheme.colorScheme.surface, + titleColor: Color = + LocalSettingsTileColors.current?.titleColor + ?: MaterialTheme.colorScheme.primary, + groupTitleColor: Color = + LocalSettingsTileColors.current?.groupTitleColor + ?: MaterialTheme.colorScheme.onBackground, + iconColor: Color = LocalSettingsTileColors.current?.iconColor ?: MaterialTheme.colorScheme.onSurface, + subtitleColor: Color = LocalSettingsTileColors.current?.subtitleColor ?: MaterialTheme.colorScheme.onSurface, + actionColor: Color = + LocalSettingsTileColors.current?.actionColor + ?: MaterialTheme.colorScheme.primary, + disabledTitleColor: Color = titleColor.copy(alpha = SettingsTileConstants.DisabledAlpha), + disabledGroupTitleColor: Color = groupTitleColor.copy(alpha = SettingsTileConstants.DisabledAlpha), + disabledIconColor: Color = iconColor.copy(alpha = SettingsTileConstants.DisabledAlpha), + disabledSubtitleColor: Color = subtitleColor.copy(alpha = SettingsTileConstants.DisabledAlpha), + disabledActionColor: Color = actionColor.copy(alpha = SettingsTileConstants.DisabledAlpha), + ): SettingsTileColors = + SettingsTileColors( + containerColor = containerColor, + titleColor = titleColor, + groupTitleColor = groupTitleColor, + iconColor = iconColor, + subtitleColor = subtitleColor, + actionColor = actionColor, + disabledTitleColor = disabledTitleColor, + disabledGroupTitleColor = disabledGroupTitleColor, + disabledIconColor = disabledIconColor, + disabledSubtitleColor = disabledSubtitleColor, + disabledActionColor = disabledActionColor, + ) +} diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt index f6ddfaf2..595af1f2 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt @@ -15,8 +15,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.alorma.compose.settings.ui.base.internal.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.base.internal.SettingsTileColors +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTileColors import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold diff --git a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt index d6689e80..b16c47c2 100644 --- a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt +++ b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt @@ -16,8 +16,8 @@ import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.alorma.compose.settings.ui.base.internal.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.base.internal.SettingsTileColors +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTileColors import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold diff --git a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt index 69fd147d..7cda6958 100644 --- a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt +++ b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt @@ -11,8 +11,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.alorma.compose.settings.ui.base.internal.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.base.internal.SettingsTileColors +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTileColors import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt index af3fa5df..51d918c5 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt @@ -13,11 +13,11 @@ import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp -import com.alorma.compose.settings.ui.base.internal.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.base.internal.SettingsTileColors +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTileColors import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold -import com.alorma.compose.settings.ui.base.internal.SettingsTextStyles +import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable fun SettingsCheckbox( diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt index 170a0eac..9f5143ed 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt @@ -15,11 +15,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp -import com.alorma.compose.settings.ui.base.internal.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.base.internal.SettingsTextStyles -import com.alorma.compose.settings.ui.base.internal.SettingsTileColors +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTextStyles +import com.alorma.compose.settings.ui.core.SettingsTileColors import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults @Composable diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt index 013e5513..e12de26b 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt @@ -7,11 +7,11 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp -import com.alorma.compose.settings.ui.base.internal.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.base.internal.SettingsTileColors +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTileColors import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold -import com.alorma.compose.settings.ui.base.internal.SettingsTextStyles +import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable fun SettingsMenuLink( diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt index 4b57b760..fed5f628 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt @@ -12,11 +12,11 @@ import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp -import com.alorma.compose.settings.ui.base.internal.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.base.internal.SettingsTileColors +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTileColors import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold -import com.alorma.compose.settings.ui.base.internal.SettingsTextStyles +import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable fun SettingsRadioButton( diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt index 3be8ded1..3d40d4ff 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt @@ -13,11 +13,11 @@ import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp -import com.alorma.compose.settings.ui.base.internal.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.base.internal.SettingsTileColors +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTileColors import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold -import com.alorma.compose.settings.ui.base.internal.SettingsTextStyles +import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable fun SettingsSwitch( diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt index 7260c55b..bf8c4a6c 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt @@ -14,11 +14,11 @@ import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.unit.Dp -import com.alorma.compose.settings.ui.base.internal.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.base.internal.SettingsTileColors +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTileColors import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold -import com.alorma.compose.settings.ui.base.internal.SettingsTextStyles +import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable fun SettingsTriStateCheckbox( From 719cf38aa207c37c56ecdc5f7ca0c3599e4ff81d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s?= Date: Tue, 24 Feb 2026 21:13:00 +0100 Subject: [PATCH 02/11] Split, remove ui-base --- .github/ISSUE_TEMPLATE/feature_request.yml | 3 +- .github/workflows/main.yml | 10 +- .github/workflows/publish-and-release.yml | 10 +- .github/workflows/pull_request.yml | 10 +- ARCHITECTURE.md | 35 +++--- CLAUDE.md | 56 ++++----- CONTRIBUTING.md | 5 +- PUBLISHING.md | 6 +- .../kotlin/ComposeLibraryConventionPlugin.kt | 1 - build.gradle.kts | 2 +- samples/shared/build.gradle.kts | 2 +- .../settings/sample/shared/SettingsScreen.kt | 2 +- .../sample/shared/internal/SampleSection.kt | 2 +- settings.gradle.kts | 1 - ui-base/build.gradle.kts | 15 --- ui-base/config/ktlint/baseline.xml | 12 -- ui-core/build.gradle.kts | 6 + .../ui/core/SettingsTileCoreDefaults.kt | 1 + ui-tiles-expressive/build.gradle.kts | 2 +- .../ui/expressive/SettingsButtonGroup.kt | 2 - .../ui/expressive/SettingsTileDefaults.kt | 20 +++ .../ui/expressive}/SettingsTileScaffold.kt | 2 +- ui-tiles-extended/build.gradle.kts | 2 +- .../compose/settings/ui/SettingsSegmented.kt | 2 - .../compose/settings/ui/SettingsSlider.kt | 2 - ui-tiles/build.gradle.kts | 3 +- .../compose/settings/ui/SettingsCheckbox.kt | 2 - .../compose/settings/ui/SettingsGroup.kt | 1 - .../compose/settings/ui/SettingsMenuLink.kt | 2 - .../settings/ui/SettingsRadioButton.kt | 2 - .../compose/settings/ui/SettingsSwitch.kt | 2 - .../settings/ui}/SettingsTileDefaults.kt | 2 +- .../settings/ui/SettingsTileScaffold.kt | 114 ++++++++++++++++++ .../settings/ui/SettingsTriStateCheckbox.kt | 2 - 34 files changed, 216 insertions(+), 125 deletions(-) delete mode 100644 ui-base/build.gradle.kts delete mode 100644 ui-base/config/ktlint/baseline.xml create mode 100644 ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileDefaults.kt rename {ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal => ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive}/SettingsTileScaffold.kt (98%) rename {ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal => ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui}/SettingsTileDefaults.kt (88%) create mode 100644 ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index c0c50e57..1b98f0de 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -49,7 +49,8 @@ body: options: - ui-tiles (core components) - ui-tiles-extended (advanced components) - - ui-base (internal/shared) + - ui-tiles-expressive (expressive Material 3) + - ui-core (foundation/shared) - New module - Not sure validations: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8ee46741..33596aa2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,8 +34,8 @@ jobs: echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT echo "Current version: $CURRENT_VERSION" - build-lib-base: - name: Build base - Main + build-lib-core: + name: Build core - Main if: ${{ success() }} needs: [ get-version ] runs-on: macos-latest @@ -50,7 +50,7 @@ jobs: java-version: 17 - name: Build - run: ./gradlew :ui-base:build + run: ./gradlew :ui-core:build build-lib-ui: strategy: @@ -62,7 +62,7 @@ jobs: ] name: Build ${{ matrix.config.target }} - Main if: ${{ success() }} - needs: [ build-lib-base ] + needs: [ build-lib-core ] runs-on: macos-latest steps: - name: Check out code @@ -124,7 +124,7 @@ jobs: env: ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_PASSWORD }} - run: ./gradlew ui-base:signKotlinMultiplatformPublication ui-tiles:signKotlinMultiplatformPublication ui-tiles-extended:signKotlinMultiplatformPublication ui-tiles-expressive:signKotlinMultiplatformPublication + run: ./gradlew ui-core:signKotlinMultiplatformPublication ui-tiles:signKotlinMultiplatformPublication ui-tiles-extended:signKotlinMultiplatformPublication ui-tiles-expressive:signKotlinMultiplatformPublication fake-publish: name: Fake publish - Main diff --git a/.github/workflows/publish-and-release.yml b/.github/workflows/publish-and-release.yml index b0fefd18..97a89cb3 100644 --- a/.github/workflows/publish-and-release.yml +++ b/.github/workflows/publish-and-release.yml @@ -54,8 +54,8 @@ jobs: echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT echo "Current version to publish: $CURRENT_VERSION" - build-lib-base: - name: Build base - Release + build-lib-core: + name: Build core - Release needs: [ get-version ] runs-on: macos-latest steps: @@ -69,7 +69,7 @@ jobs: java-version: 17 - name: Build - run: ./gradlew :ui-base:build + run: ./gradlew :ui-core:build build-lib-ui: strategy: @@ -80,7 +80,7 @@ jobs: { target: tiles expressive, module: ui-tiles-expressive, task: build, continueOnError: false }, ] name: Build ${{ matrix.config.target }} - Release - needs: [ build-lib-base ] + needs: [ build-lib-core ] runs-on: macos-latest steps: - name: Check out code @@ -140,7 +140,7 @@ jobs: env: ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_PASSWORD }} - run: ./gradlew ui-base:signKotlinMultiplatformPublication ui-tiles:signKotlinMultiplatformPublication ui-tiles-extended:signKotlinMultiplatformPublication ui-tiles-expressive:signKotlinMultiplatformPublication + run: ./gradlew ui-core:signKotlinMultiplatformPublication ui-tiles:signKotlinMultiplatformPublication ui-tiles-extended:signKotlinMultiplatformPublication ui-tiles-expressive:signKotlinMultiplatformPublication fake-publish: name: Fake publish - Release diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 81a92aad..c7a58506 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -35,8 +35,8 @@ jobs: echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT echo "Current version: $CURRENT_VERSION" - build-lib-base: - name: Build base - PR + build-lib-core: + name: Build core - PR if: ${{ success() }} needs: [ get-version ] runs-on: macos-latest @@ -51,7 +51,7 @@ jobs: java-version: 17 - name: Build - run: ./gradlew :ui-base:build + run: ./gradlew :ui-core:build build-lib-ui: strategy: @@ -63,7 +63,7 @@ jobs: ] name: Build ${{ matrix.config.target }} - PR if: ${{ success() }} - needs: [ build-lib-base ] + needs: [ build-lib-core ] runs-on: macos-latest steps: - name: Check out code @@ -125,7 +125,7 @@ jobs: env: ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_PASSWORD }} - run: ./gradlew ui-base:signKotlinMultiplatformPublication ui-tiles:signKotlinMultiplatformPublication ui-tiles-extended:signKotlinMultiplatformPublication ui-tiles-expressive:signKotlinMultiplatformPublication + run: ./gradlew ui-core:signKotlinMultiplatformPublication ui-tiles:signKotlinMultiplatformPublication ui-tiles-extended:signKotlinMultiplatformPublication ui-tiles-expressive:signKotlinMultiplatformPublication fake-publish: name: Fake publish - PR diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 9ce8c7ea..9860cd26 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -28,26 +28,30 @@ Compose-Settings is a Kotlin Multiplatform library that provides pre-built setti ``` ui-tiles ──────────┐ - ├──> ui-base (internal) -ui-tiles-extended ─┘ + │ +ui-tiles-extended ─┼──> ui-core (foundation) + │ +ui-tiles-expressive┘ ``` ### Module Responsibilities -#### ui-base +#### ui-core -**Purpose**: Internal foundation module providing shared infrastructure +**Purpose**: Foundation module providing shared infrastructure -**Location**: `ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/` +**Location**: `ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/` **Key Components**: - `SettingsTileScaffold` - Base composable for all settings components -- `SettingsTileDefaults` - Default values for colors, shapes, text styles +- `SettingsTileCoreDefaults` - Shared default values for colors and text styles +- `SettingsTileDefaults` - Module-specific defaults (e.g., shapes) - `SettingsTileColors` - Color system for settings components - `SettingsTextStyles` - Typography system - `LocalSettingsGroupEnabled` - Composition local for hierarchical enabled state +- `SettingsTileConstants` - Shared constants -**Visibility**: All APIs are `internal` - not exposed to library consumers +**Visibility**: Most APIs are `internal` - not exposed to library consumers **Why separate module?** - Prevents duplication between `ui-tiles` and `ui-tiles-extended` @@ -356,19 +360,20 @@ All dependencies in `gradle/libs.versions.toml`: ## Design Decisions -### Why Internal ui-base Module? +### Why ui-core Foundation Module? -**Problem**: Both `ui-tiles` and `ui-tiles-extended` need shared components +**Problem**: All component modules (`ui-tiles`, `ui-tiles-extended`, `ui-tiles-expressive`) need shared components **Options considered**: -1. Duplicate code in both modules ❌ -2. Make one depend on the other ❌ (circular dependency potential) -3. Create internal shared module ✅ +1. Duplicate code in all modules ❌ +2. Make modules depend on each other ❌ (circular dependency potential) +3. Create shared foundation module ✅ -**Decision**: Internal `ui-base` module +**Decision**: `ui-core` foundation module - Prevents duplication -- Both modules depend on it -- Not exposed to consumers (implementation detail) +- All component modules depend on it +- Contains scaffolds, colors, styles, and shared utilities +- Published as a separate artifact for flexibility ### Why Composable Content Parameters? diff --git a/CLAUDE.md b/CLAUDE.md index 60b5e917..e6cabbd7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,36 +14,32 @@ Jetpack Compose. It targets Android, iOS, Desktop (JVM), JavaScript, and WebAsse The project is organized into four main library modules: -1. **ui-core** - Pure Kotlin shared logic (no Material 3 dependencies) +1. **ui-core** - Foundation module with shared logic and Material 3 scaffold - Location: `ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/` - - Provides: `SettingsTileColors`, `SettingsTextStyles`, `CompositionLocals` ( - `LocalSettingsGroupEnabled`, `LocalSettingsTileColors`, `LocalSettingsTextStyles`), - `SettingsTileConstants` - - Contains only pure data classes and composition locals - no UI rendering - - Used by both standard and expressive Material 3 implementations - -2. **ui-base** - Standard Material 3 foundation module - - Location: `ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/` - - Depends on: `ui-core`, standard Material 3 - - Provides: `SettingsTileScaffold` (wraps Material 3's `ListItem`), `SettingsTileDefaults` - - Contains no public API - only internal utilities used by other modules - -3. **ui-tiles** - Core settings components (standard Material 3) + - Provides: `SettingsTileColors`, `SettingsTextStyles`, `SettingsTileCoreDefaults`, + `CompositionLocals` (`LocalSettingsGroupEnabled`, `LocalSettingsTileColors`, + `LocalSettingsTextStyles`), `SettingsTileConstants`, `SettingsTileScaffold` (wraps Material + 3's `ListItem`) + - Contains internal utilities and data models used by all other modules + - Depends on: standard Material 3 + - Published as: `com.github.alorma.compose-settings:ui-core` + +2. **ui-tiles** - Core settings components (standard Material 3) - Location: `ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/` - - Depends on: `ui-base` + - Depends on: `ui-core` - Components: `SettingsMenuLink`, `SettingsCheckbox`, `SettingsRadioButton`, `SettingsSwitch`, `SettingsTriStateCheckbox`, `SettingsGroup` - Published as: `com.github.alorma.compose-settings:ui-tiles` -4. **ui-tiles-extended** - Advanced/extended settings components (standard Material 3) +3. **ui-tiles-extended** - Advanced/extended settings components (standard Material 3) - Location: `ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/` - - Depends on: `ui-base` + - Depends on: `ui-core` - Components: `SettingsSlider`, `SettingsSegmented` - Published as: `com.github.alorma.compose-settings:ui-tiles-extended` -5. **ui-tiles-expressive** - Expressive Material 3 components +4. **ui-tiles-expressive** - Expressive Material 3 components - Location: `ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/` - - Depends on: `ui-base`, Material 3 Expressive + - Depends on: `ui-core`, Material 3 Expressive - Components: `SettingsButtonGroup` - Published as: `com.github.alorma.compose-settings:ui-tiles-expressive` @@ -51,7 +47,7 @@ The project is organized into four main library modules: All settings components follow a consistent pattern: -1. Built on top of `SettingsTileScaffold` (from `ui-base` or `ui-base-expressive`) +1. Built on top of `SettingsTileScaffold` (from `ui-core`) 2. Accept common parameters: `title`, `subtitle`, `icon`, `modifier`, `enabled`, `colors` 3. Use Material 3 components internally (ListItem, Checkbox, Switch, etc.) 4. Support `LocalSettingsGroupEnabled` for hierarchical enabled state @@ -66,23 +62,15 @@ handling. The library uses a layered architecture: ``` -ui-core (pure Kotlin - colors, text styles, composition locals) +ui-core (foundation - colors, styles, scaffold, composition locals) ↓ -ui-base (standard Material 3 - ListItem scaffold) - ↓ -ui-tiles, ui-tiles-extended (standard Material 3 components) - -ui-core (pure Kotlin - colors, text styles, composition locals) - ↓ -ui-base-expressive (Material 3 Expressive - SegmentedListItem scaffold) [PLANNED] - ↓ -ui-tiles-expressive (expressive Material 3 components) +ui-tiles, ui-tiles-extended, ui-tiles-expressive (components) ``` This separation allows: -- **Code reuse**: Pure logic (colors, styles) shared between standard and expressive variants -- **Flexibility**: Easy to add support for new Material 3 variants -- **Type safety**: Strong separation between rendering layer and data layer +- **Code reuse**: Shared foundation (colors, styles, scaffold) used by all component modules +- **Flexibility**: Easy to add new component modules +- **Type safety**: Strong separation between foundation layer and component layer ### Platform-Specific Code @@ -204,7 +192,7 @@ The default hierarchy template is applied via `applyDefaultHierarchyTemplate()`. The project uses Gradle convention plugins located in `build-logic/convention/`: 1. **ComposeLibraryConventionPlugin** (`compose.library`) - - Applied to all library modules (ui-base, ui-tiles, ui-tiles-extended) + - Applied to all library modules (ui-core, ui-tiles, ui-tiles-extended, ui-tiles-expressive) - Configures Kotlin Multiplatform with all target platforms - Sets up Compose Multiplatform and Compose Compiler - Configures Android library settings diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 821f4699..33040133 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,9 +41,10 @@ By participating in this project, you agree to abide by our [Code of Conduct](CO ``` Compose-Settings/ -├── ui-base/ # Foundation module (internal components) +├── ui-core/ # Foundation module (shared components) ├── ui-tiles/ # Core settings components ├── ui-tiles-extended/ # Advanced settings components +├── ui-tiles-expressive/ # Expressive Material 3 components ├── samples/ # Sample applications │ ├── androidApp/ # Android demo │ ├── desktopApp/ # Desktop (JVM) demo @@ -151,7 +152,7 @@ When creating new settings components: - `ui-tiles-extended`: Advanced components requiring additional dependencies (slider, etc.) 2. **Use the scaffold pattern**: - - Build on `SettingsTileScaffold` from `ui-base` + - Build on `SettingsTileScaffold` from `ui-core` - This ensures consistent layout and theming 3. **Follow the API pattern**: diff --git a/PUBLISHING.md b/PUBLISHING.md index 5fb51211..09308ba1 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -16,7 +16,7 @@ The publishing process is fully automated through GitHub Actions and uses: ### Convention Plugin Setup -All library modules (`ui-base`, `ui-tiles`, `ui-tiles-extended`, `ui-tiles-expressive`) use the +All library modules (`ui-core`, `ui-tiles`, `ui-tiles-extended`, `ui-tiles-expressive`) use the `compose.library` convention plugin which automatically configures: **Location**: `build-logic/convention/src/main/kotlin/ComposeLibraryConventionPlugin.kt` @@ -246,9 +246,9 @@ env: Each library module publishes multiple artifacts for each platform: -### ui-base +### ui-core -- `com.alorma.compose.settings:ui-base:$version` +- `com.alorma.compose.settings:ui-core:$version` - Platform variants: `-android`, `-desktop`, `-iosarm64`, `-iossimulatorarm64`, `-iosx64`, `-js`, `-wasmjs` diff --git a/build-logic/convention/src/main/kotlin/ComposeLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/ComposeLibraryConventionPlugin.kt index cc27f9fb..720b2541 100644 --- a/build-logic/convention/src/main/kotlin/ComposeLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/ComposeLibraryConventionPlugin.kt @@ -87,7 +87,6 @@ class ComposeLibraryConventionPlugin : Plugin { dependencies { implementation(libs.findLibrary("compose-runtime").get()) implementation(libs.findLibrary("compose-foundation").get()) - api(libs.findLibrary("compose-material3").get()) } } } diff --git a/build.gradle.kts b/build.gradle.kts index 1a6ddfd5..0aaf0eae 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -45,7 +45,7 @@ rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlu dependencies { listOf( - projects.uiBase, + projects.uiCore, projects.uiTiles, projects.uiTilesExtended, projects.uiTilesExpressive, diff --git a/samples/shared/build.gradle.kts b/samples/shared/build.gradle.kts index c5bb3507..0ea5110e 100644 --- a/samples/shared/build.gradle.kts +++ b/samples/shared/build.gradle.kts @@ -38,7 +38,7 @@ kotlin { // Add the generated source directory to the source sets kotlin.srcDir(layout.buildDirectory.dir("generated/kotlin")) dependencies { - implementation(projects.uiBase) + implementation(projects.uiCore) implementation(projects.uiTiles) implementation(projects.uiTilesExtended) implementation(projects.uiTilesExpressive) diff --git a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt index d5d74dcd..4a4a10a4 100644 --- a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt +++ b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt @@ -36,7 +36,7 @@ import com.alorma.compose.settings.ui.SettingsSegmented import com.alorma.compose.settings.ui.SettingsSlider import com.alorma.compose.settings.ui.SettingsSwitch import com.alorma.compose.settings.ui.SettingsTriStateCheckbox -import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults +import com.alorma.compose.settings.ui.SettingsTileDefaults import com.alorma.compose.settings.ui.expressive.SettingsButtonGroup @OptIn(ExperimentalMaterial3Api::class) diff --git a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/internal/SampleSection.kt b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/internal/SampleSection.kt index e48fb094..a74ef58e 100644 --- a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/internal/SampleSection.kt +++ b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/internal/SampleSection.kt @@ -9,7 +9,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp import com.alorma.compose.settings.ui.SettingsGroup -import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults +import com.alorma.compose.settings.ui.SettingsTileDefaults import com.alorma.compose.settings.ui.core.LocalSettingsTileColors @Composable diff --git a/settings.gradle.kts b/settings.gradle.kts index d1da4ab4..03072d2e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -26,7 +26,6 @@ include(":samples:iosApp") include(":samples:androidApp") include(":ui-core") -include(":ui-base") include(":ui-tiles") include(":ui-tiles-extended") include(":ui-tiles-expressive") diff --git a/ui-base/build.gradle.kts b/ui-base/build.gradle.kts deleted file mode 100644 index a4f76c19..00000000 --- a/ui-base/build.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -plugins { - id("compose.library") -} - -kotlin { - androidLibrary { - namespace = libs.versions.namespace.get() + ".ui.base" - } - - sourceSets { - commonMain.dependencies { - api(projects.uiCore) - } - } -} diff --git a/ui-base/config/ktlint/baseline.xml b/ui-base/config/ktlint/baseline.xml deleted file mode 100644 index b1997c0f..00000000 --- a/ui-base/config/ktlint/baseline.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - diff --git a/ui-core/build.gradle.kts b/ui-core/build.gradle.kts index 904c9388..a681a8e9 100644 --- a/ui-core/build.gradle.kts +++ b/ui-core/build.gradle.kts @@ -6,4 +6,10 @@ kotlin { androidLibrary { namespace = libs.versions.namespace.get() + ".ui.core" } + + sourceSets { + commonMain.dependencies { + api(libs.compose.material3) + } + } } diff --git a/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt index abae8e88..c90ee41e 100644 --- a/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt +++ b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.unit.Dp abstract class SettingsTileCoreDefaults { abstract val Elevation: Dp + val DisabledAlpha: Float = 0.38f /** * Creates a [SettingsTextStyles] with default values from MaterialTheme typography diff --git a/ui-tiles-expressive/build.gradle.kts b/ui-tiles-expressive/build.gradle.kts index 517ac726..79e80842 100644 --- a/ui-tiles-expressive/build.gradle.kts +++ b/ui-tiles-expressive/build.gradle.kts @@ -9,7 +9,7 @@ kotlin { sourceSets { commonMain.dependencies { - api(projects.uiBase) + implementation(projects.uiCore) // Use material3-expressive instead of standard material3 api(libs.compose.material3.expressive) } diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt index 595af1f2..8f262d32 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt @@ -17,8 +17,6 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults -import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileDefaults.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileDefaults.kt new file mode 100644 index 00000000..1bdff721 --- /dev/null +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileDefaults.kt @@ -0,0 +1,20 @@ +package com.alorma.compose.settings.ui.expressive + +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ListItemDefaults +import androidx.compose.material3.ListItemShapes +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.unit.Dp +import com.alorma.compose.settings.ui.core.SettingsTileCoreDefaults + +object SettingsTileDefaults : SettingsTileCoreDefaults() { + override val Elevation: Dp = ListItemDefaults.Elevation + + @Composable + fun shape(): Shape = ListItemDefaults.shape + + @OptIn(ExperimentalMaterial3ExpressiveApi::class) + @Composable + fun segmentedShape(): ListItemShapes = ListItemDefaults.shapes() +} diff --git a/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileScaffold.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt similarity index 98% rename from ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileScaffold.kt rename to ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt index c68b014f..1a9f6d7e 100644 --- a/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileScaffold.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt @@ -1,4 +1,4 @@ -package com.alorma.compose.settings.ui.base.internal +package com.alorma.compose.settings.ui.expressive import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.ListItem diff --git a/ui-tiles-extended/build.gradle.kts b/ui-tiles-extended/build.gradle.kts index 084d021f..284c377d 100644 --- a/ui-tiles-extended/build.gradle.kts +++ b/ui-tiles-extended/build.gradle.kts @@ -9,7 +9,7 @@ kotlin { sourceSets { commonMain.dependencies { - api(projects.uiBase) + api(projects.uiTiles) } } } diff --git a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt index b16c47c2..9a8a4dfe 100644 --- a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt +++ b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt @@ -18,8 +18,6 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults -import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold @Composable @Suppress("LongParameterList") diff --git a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt index 7cda6958..f8a887b5 100644 --- a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt +++ b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt @@ -13,8 +13,6 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults -import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold @Composable @Suppress("LongParameterList") diff --git a/ui-tiles/build.gradle.kts b/ui-tiles/build.gradle.kts index fdf33649..2658933b 100644 --- a/ui-tiles/build.gradle.kts +++ b/ui-tiles/build.gradle.kts @@ -9,7 +9,8 @@ kotlin { sourceSets { commonMain.dependencies { - api(projects.uiBase) + api(projects.uiCore) + api(libs.compose.material3) } } } diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt index 51d918c5..747f2f5c 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt @@ -15,8 +15,6 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults -import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt index 9f5143ed..1cbed8ec 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt @@ -19,7 +19,6 @@ import androidx.compose.ui.unit.dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled import com.alorma.compose.settings.ui.core.SettingsTextStyles import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults @Composable fun SettingsGroup( diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt index e12de26b..ffa60553 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt @@ -9,8 +9,6 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults -import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt index fed5f628..76dc3e7a 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt @@ -14,8 +14,6 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults -import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt index 3d40d4ff..bc7209df 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt @@ -15,8 +15,6 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults -import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable diff --git a/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileDefaults.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileDefaults.kt similarity index 88% rename from ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileDefaults.kt rename to ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileDefaults.kt index 39a285b7..ad964eb9 100644 --- a/ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/SettingsTileDefaults.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileDefaults.kt @@ -1,4 +1,4 @@ -package com.alorma.compose.settings.ui.base.internal +package com.alorma.compose.settings.ui import androidx.compose.material3.ListItemDefaults import androidx.compose.runtime.Composable diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt new file mode 100644 index 00000000..f9a97d5d --- /dev/null +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt @@ -0,0 +1,114 @@ +package com.alorma.compose.settings.ui + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.ListItem +import androidx.compose.material3.ListItemColors +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.LocalTextStyle +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.Dp +import com.alorma.compose.settings.ui.core.SettingsTileColors +import com.alorma.compose.settings.ui.core.SettingsTextStyles + +@Composable +fun SettingsTileScaffold( + title: @Composable () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + subtitle: @Composable (() -> Unit)? = null, + icon: @Composable (() -> Unit)? = null, + colors: SettingsTileColors = SettingsTileDefaults.colors(), + textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + shape: Shape = SettingsTileDefaults.shape(), + tonalElevation: Dp = SettingsTileDefaults.Elevation, + shadowElevation: Dp = SettingsTileDefaults.Elevation, + action: @Composable (() -> Unit)? = null, +) { + val decoratedTitle: @Composable () -> Unit = { + ProvideContentColorAndTextStyle( + contentColor = colors.titleColor(enabled), + textStyle = textStyles.titleStyle, + ) { + title() + } + } + val decoratedSubtitle: @Composable (() -> Unit)? = + subtitle?.let { + { + ProvideContentColorAndTextStyle( + contentColor = colors.subtitleColor(enabled), + textStyle = textStyles.subtitleStyle, + ) { + subtitle() + } + } + } + val decoratedIcon: @Composable (() -> Unit)? = + icon?.let { + { + ProvideContentColor(colors.iconColor(enabled)) { + icon() + } + } + } + val decoratedAction: @Composable (() -> Unit)? = + action?.let { + { + ProvideContentColor(colors.actionColor(enabled)) { + action() + } + } + } + + ListItem( + modifier = Modifier.fillMaxWidth().clip(shape).then(modifier), + headlineContent = decoratedTitle, + supportingContent = decoratedSubtitle, + leadingContent = decoratedIcon, + trailingContent = decoratedAction, + colors = + ListItemColors( + containerColor = colors.containerColor, + headlineColor = colors.titleColor, + leadingIconColor = colors.iconColor, + overlineColor = colors.actionColor, + supportingTextColor = colors.subtitleColor, + trailingIconColor = colors.actionColor, + disabledHeadlineColor = colors.disabledTitleColor, + disabledLeadingIconColor = colors.disabledIconColor, + disabledTrailingIconColor = colors.disabledActionColor, + ), + tonalElevation = tonalElevation, + shadowElevation = shadowElevation, + ) +} + +@Composable +private fun ProvideContentColor( + contentColor: Color, + content: @Composable () -> Unit, +) { + CompositionLocalProvider(LocalContentColor provides contentColor) { + content() + } +} + +@Composable +private fun ProvideContentColorAndTextStyle( + contentColor: Color, + textStyle: TextStyle, + content: @Composable () -> Unit, +) { + CompositionLocalProvider( + LocalContentColor provides contentColor, + LocalTextStyle provides textStyle, + ) { + content() + } +} diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt index bf8c4a6c..f9fb10a9 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt @@ -16,8 +16,6 @@ import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.unit.Dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults -import com.alorma.compose.settings.ui.base.internal.SettingsTileScaffold import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable From 777a7f48b95c191c308da67f85f6769dc93b5aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s?= Date: Tue, 24 Feb 2026 21:21:24 +0100 Subject: [PATCH 03/11] Expressive scaffold --- .../ui/expressive/SettingsButtonGroup.kt | 14 ++++----- .../ui/expressive/SettingsTileDefaults.kt | 13 ++++++-- .../ui/expressive/SettingsTileScaffold.kt | 31 +++++++++++-------- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt index 8f262d32..5c786ac3 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt @@ -6,14 +6,14 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.ButtonGroupDefaults import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ListItemElevation +import androidx.compose.material3.ListItemShapes import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedToggleButton import androidx.compose.material3.Text import androidx.compose.material3.ToggleButtonDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled import com.alorma.compose.settings.ui.core.SettingsTileColors @@ -32,9 +32,8 @@ fun SettingsButtonGroup( colors: SettingsTileColors = SettingsTileDefaults.colors(), subtitle: @Composable (() -> Unit)? = null, icon: @Composable (() -> Unit)? = null, - shape: Shape = SettingsTileDefaults.shape(), - tonalElevation: Dp = SettingsTileDefaults.Elevation, - shadowElevation: Dp = SettingsTileDefaults.Elevation, + shapes: ListItemShapes = SettingsTileDefaults.shapes(), + elevation: ListItemElevation = SettingsTileDefaults.elevation(), ) { SettingsTileScaffold( modifier = modifier, @@ -66,8 +65,7 @@ fun SettingsButtonGroup( }, icon = icon, colors = colors, - shape = shape, - tonalElevation = tonalElevation, - shadowElevation = shadowElevation, + shapes = shapes, + elevation = elevation, ) } diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileDefaults.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileDefaults.kt index 1bdff721..3d85f032 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileDefaults.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileDefaults.kt @@ -2,19 +2,26 @@ package com.alorma.compose.settings.ui.expressive import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ListItemDefaults +import androidx.compose.material3.ListItemElevation import androidx.compose.material3.ListItemShapes import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.Dp import com.alorma.compose.settings.ui.core.SettingsTileCoreDefaults object SettingsTileDefaults : SettingsTileCoreDefaults() { override val Elevation: Dp = ListItemDefaults.Elevation + /** + * Returns the default shapes for expressive Material 3 segmented list items. + */ + @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable - fun shape(): Shape = ListItemDefaults.shape + fun shapes(): ListItemShapes = ListItemDefaults.shapes() + /** + * Returns the default elevation for expressive Material 3 segmented list items. + */ @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable - fun segmentedShape(): ListItemShapes = ListItemDefaults.shapes() + fun elevation(): ListItemElevation = ListItemDefaults.elevation() } diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt index 1a9f6d7e..e431ac22 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt @@ -1,21 +1,23 @@ package com.alorma.compose.settings.ui.expressive import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.material3.ListItem +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ListItemColors +import androidx.compose.material3.ListItemElevation +import androidx.compose.material3.ListItemShapes import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.SegmentedListItem import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Shape import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Dp import com.alorma.compose.settings.ui.core.SettingsTileColors import com.alorma.compose.settings.ui.core.SettingsTextStyles +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun SettingsTileScaffold( title: @Composable () -> Unit, @@ -25,9 +27,8 @@ fun SettingsTileScaffold( icon: @Composable (() -> Unit)? = null, colors: SettingsTileColors = SettingsTileDefaults.colors(), textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), - shape: Shape = SettingsTileDefaults.shape(), - tonalElevation: Dp = SettingsTileDefaults.Elevation, - shadowElevation: Dp = SettingsTileDefaults.Elevation, + shapes: ListItemShapes = SettingsTileDefaults.shapes(), + elevation: ListItemElevation = SettingsTileDefaults.elevation(), action: @Composable (() -> Unit)? = null, ) { val decoratedTitle: @Composable () -> Unit = { @@ -66,12 +67,15 @@ fun SettingsTileScaffold( } } - ListItem( - modifier = Modifier.fillMaxWidth().clip(shape).then(modifier), - headlineContent = decoratedTitle, - supportingContent = decoratedSubtitle, + SegmentedListItem( + selected = false, + onClick = {}, + shapes = shapes, + modifier = Modifier.fillMaxWidth().then(modifier), + enabled = enabled, leadingContent = decoratedIcon, trailingContent = decoratedAction, + supportingContent = decoratedSubtitle, colors = ListItemColors( containerColor = colors.containerColor, @@ -84,9 +88,10 @@ fun SettingsTileScaffold( disabledLeadingIconColor = colors.disabledIconColor, disabledTrailingIconColor = colors.disabledActionColor, ), - tonalElevation = tonalElevation, - shadowElevation = shadowElevation, - ) + elevation = elevation, + ) { + decoratedTitle() + } } @Composable From cf4f9fef08dbb606e09a41299b2af0e103c0730e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s?= Date: Tue, 24 Feb 2026 21:25:19 +0100 Subject: [PATCH 04/11] Split2 --- .../ui/expressive/SettingsCheckbox.kt | 71 +++++++++++++++++ .../settings/ui/expressive/SettingsGroup.kt | 72 +++++++++++++++++ .../ui/expressive/SettingsMenuLink.kt | 49 ++++++++++++ .../ui/expressive/SettingsRadioButton.kt | 68 ++++++++++++++++ .../settings/ui/expressive/SettingsSwitch.kt | 73 +++++++++++++++++ .../ui/expressive/SettingsTriStateCheckbox.kt | 79 +++++++++++++++++++ 6 files changed, 412 insertions(+) create mode 100644 ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt create mode 100644 ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt create mode 100644 ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt create mode 100644 ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt create mode 100644 ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt create mode 100644 ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt new file mode 100644 index 00000000..129093fd --- /dev/null +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt @@ -0,0 +1,71 @@ +package com.alorma.compose.settings.ui.expressive + +import androidx.compose.foundation.selection.toggleable +import androidx.compose.material3.Checkbox +import androidx.compose.material3.CheckboxColors +import androidx.compose.material3.CheckboxDefaults +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ListItemElevation +import androidx.compose.material3.ListItemShapes +import androidx.compose.material3.contentColorFor +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.SemanticsPropertyReceiver +import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.ui.semantics.semantics +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTileColors +import com.alorma.compose.settings.ui.core.SettingsTextStyles + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun SettingsCheckbox( + state: Boolean, + title: @Composable () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = LocalSettingsGroupEnabled.current, + icon: @Composable (() -> Unit)? = null, + subtitle: @Composable (() -> Unit)? = null, + colors: SettingsTileColors = SettingsTileDefaults.colors(), + checkboxColors: CheckboxColors = + CheckboxDefaults.colors( + checkedColor = colors.actionColor(enabled), + checkmarkColor = contentColorFor(colors.actionColor(enabled)), + disabledCheckedColor = colors.actionColor(enabled), + ), + textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + shapes: ListItemShapes = SettingsTileDefaults.shapes(), + elevation: ListItemElevation = SettingsTileDefaults.elevation(), + semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, + onCheckedChange: (Boolean) -> Unit, +) { + val update: (Boolean) -> Unit = { boolean -> onCheckedChange(boolean) } + SettingsTileScaffold( + modifier = + Modifier + .toggleable( + enabled = enabled, + value = state, + role = Role.Checkbox, + onValueChange = { update(!state) }, + ).semantics(properties = semanticProperties) + .then(modifier), + enabled = enabled, + title = title, + subtitle = subtitle, + icon = icon, + colors = colors, + textStyles = textStyles, + shapes = shapes, + elevation = elevation, + ) { + Checkbox( + modifier = Modifier.clearAndSetSemantics { }, + enabled = enabled, + checked = state, + onCheckedChange = update, + colors = checkboxColors, + ) + } +} diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt new file mode 100644 index 00000000..25e314ef --- /dev/null +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt @@ -0,0 +1,72 @@ +package com.alorma.compose.settings.ui.expressive + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.ProvideTextStyle +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.SemanticsPropertyReceiver +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.dp +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTextStyles +import com.alorma.compose.settings.ui.core.SettingsTileColors + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun SettingsGroup( + modifier: Modifier = Modifier, + enabled: Boolean = true, + contentPadding: PaddingValues = PaddingValues(0.dp), + verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(8.dp), + colors: SettingsTileColors = SettingsTileDefaults.colors(), + textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + title: @Composable (() -> Unit)? = null, + semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, + content: @Composable ColumnScope.() -> Unit, +) { + Column( + modifier = + Modifier + .fillMaxWidth() + .semantics(properties = semanticProperties) + .then(modifier) + .padding(contentPadding), + verticalArrangement = verticalArrangement, + ) { + if (title != null) { + CompositionLocalProvider(LocalContentColor provides colors.groupTitleColor(enabled)) { + ProvideTextStyle( + textStyles.groupTitleStyle, + ) { + SettingsGroupTitle(title) + } + } + } + CompositionLocalProvider(LocalSettingsGroupEnabled provides enabled) { + content() + } + } +} + +@Composable +internal fun SettingsGroupTitle(title: @Composable () -> Unit) { + Box( + modifier = + Modifier + .fillMaxWidth() + .padding(vertical = 8.dp, horizontal = 16.dp), + contentAlignment = Alignment.CenterStart, + ) { + title() + } +} diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt new file mode 100644 index 00000000..c99fcfcf --- /dev/null +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt @@ -0,0 +1,49 @@ +package com.alorma.compose.settings.ui.expressive + +import androidx.compose.foundation.clickable +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ListItemElevation +import androidx.compose.material3.ListItemShapes +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.SemanticsPropertyReceiver +import androidx.compose.ui.semantics.semantics +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTileColors +import com.alorma.compose.settings.ui.core.SettingsTextStyles + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun SettingsMenuLink( + title: @Composable () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = LocalSettingsGroupEnabled.current, + icon: (@Composable () -> Unit)? = null, + subtitle: (@Composable () -> Unit)? = null, + action: (@Composable () -> Unit)? = null, + colors: SettingsTileColors = SettingsTileDefaults.colors(), + textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + shapes: ListItemShapes = SettingsTileDefaults.shapes(), + elevation: ListItemElevation = SettingsTileDefaults.elevation(), + semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, + onClick: () -> Unit, +) { + SettingsTileScaffold( + modifier = + Modifier + .clickable( + enabled = enabled, + onClick = onClick, + ).semantics(properties = semanticProperties) + .then(modifier), + enabled = enabled, + title = title, + subtitle = subtitle, + icon = icon, + colors = colors, + textStyles = textStyles, + shapes = shapes, + elevation = elevation, + action = action, + ) +} diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt new file mode 100644 index 00000000..ab5b2631 --- /dev/null +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt @@ -0,0 +1,68 @@ +package com.alorma.compose.settings.ui.expressive + +import androidx.compose.foundation.selection.toggleable +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ListItemElevation +import androidx.compose.material3.ListItemShapes +import androidx.compose.material3.RadioButton +import androidx.compose.material3.RadioButtonColors +import androidx.compose.material3.RadioButtonDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.SemanticsPropertyReceiver +import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.ui.semantics.semantics +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTileColors +import com.alorma.compose.settings.ui.core.SettingsTextStyles + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun SettingsRadioButton( + state: Boolean, + title: @Composable () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = LocalSettingsGroupEnabled.current, + icon: @Composable (() -> Unit)? = null, + subtitle: @Composable (() -> Unit)? = null, + colors: SettingsTileColors = SettingsTileDefaults.colors(), + checkboxColors: RadioButtonColors = + RadioButtonDefaults.colors( + selectedColor = colors.actionColor(enabled), + disabledSelectedColor = colors.actionColor(enabled), + ), + textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + shapes: ListItemShapes = SettingsTileDefaults.shapes(), + elevation: ListItemElevation = SettingsTileDefaults.elevation(), + semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, + onClick: () -> Unit, +) { + SettingsTileScaffold( + modifier = + Modifier + .toggleable( + enabled = enabled, + value = state, + role = Role.RadioButton, + onValueChange = { onClick() }, + ).semantics(properties = semanticProperties) + .then(modifier), + enabled = enabled, + title = title, + subtitle = subtitle, + icon = icon, + colors = colors, + textStyles = textStyles, + shapes = shapes, + elevation = elevation, + ) { + RadioButton( + modifier = Modifier.clearAndSetSemantics { }, + enabled = enabled, + selected = state, + onClick = onClick, + colors = checkboxColors, + ) + } +} diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt new file mode 100644 index 00000000..3c4fd26f --- /dev/null +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt @@ -0,0 +1,73 @@ +package com.alorma.compose.settings.ui.expressive + +import androidx.compose.foundation.selection.toggleable +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ListItemElevation +import androidx.compose.material3.ListItemShapes +import androidx.compose.material3.Switch +import androidx.compose.material3.SwitchColors +import androidx.compose.material3.SwitchDefaults +import androidx.compose.material3.contentColorFor +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.SemanticsPropertyReceiver +import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.ui.semantics.semantics +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTileColors +import com.alorma.compose.settings.ui.core.SettingsTextStyles + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun SettingsSwitch( + state: Boolean, + title: @Composable () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = LocalSettingsGroupEnabled.current, + icon: @Composable (() -> Unit)? = null, + subtitle: @Composable (() -> Unit)? = null, + colors: SettingsTileColors = SettingsTileDefaults.colors(), + switchColors: SwitchColors = + SwitchDefaults.colors( + checkedTrackColor = colors.actionColor(enabled), + checkedThumbColor = contentColorFor(colors.actionColor(enabled)), + disabledCheckedTrackColor = colors.actionColor(enabled), + disabledCheckedThumbColor = contentColorFor(colors.actionColor(enabled)), + ), + textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + shapes: ListItemShapes = SettingsTileDefaults.shapes(), + elevation: ListItemElevation = SettingsTileDefaults.elevation(), + semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, + onCheckedChange: (Boolean) -> Unit, +) { + val update: (Boolean) -> Unit = { boolean -> onCheckedChange(boolean) } + + SettingsTileScaffold( + modifier = + Modifier + .toggleable( + enabled = enabled, + value = state, + role = Role.Switch, + onValueChange = { update(!state) }, + ).semantics(properties = semanticProperties) + .then(modifier), + enabled = enabled, + title = title, + subtitle = subtitle, + icon = icon, + colors = colors, + textStyles = textStyles, + shapes = shapes, + elevation = elevation, + ) { + Switch( + modifier = Modifier.clearAndSetSemantics { }, + enabled = enabled, + checked = state, + onCheckedChange = update, + colors = switchColors, + ) + } +} diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt new file mode 100644 index 00000000..4b2b88f6 --- /dev/null +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt @@ -0,0 +1,79 @@ +package com.alorma.compose.settings.ui.expressive + +import androidx.compose.foundation.selection.triStateToggleable +import androidx.compose.material3.CheckboxColors +import androidx.compose.material3.CheckboxDefaults +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ListItemElevation +import androidx.compose.material3.ListItemShapes +import androidx.compose.material3.TriStateCheckbox +import androidx.compose.material3.contentColorFor +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.SemanticsPropertyReceiver +import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.state.ToggleableState +import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled +import com.alorma.compose.settings.ui.core.SettingsTileColors +import com.alorma.compose.settings.ui.core.SettingsTextStyles + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun SettingsTriStateCheckbox( + state: Boolean?, + title: @Composable () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = LocalSettingsGroupEnabled.current, + icon: @Composable (() -> Unit)? = null, + subtitle: @Composable (() -> Unit)? = null, + colors: SettingsTileColors = SettingsTileDefaults.colors(), + checkboxColors: CheckboxColors = + CheckboxDefaults.colors( + checkedColor = colors.actionColor(enabled), + checkmarkColor = contentColorFor(colors.actionColor(enabled)), + disabledCheckedColor = colors.actionColor(enabled), + ), + textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + shapes: ListItemShapes = SettingsTileDefaults.shapes(), + elevation: ListItemElevation = SettingsTileDefaults.elevation(), + semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, + onCheckedChange: (Boolean) -> Unit = {}, +) { + val update: () -> Unit = { onCheckedChange(state?.not() ?: true) } + SettingsTileScaffold( + modifier = + Modifier + .triStateToggleable( + state = mapNullableBooleanToToggleableState(state), + enabled = enabled, + role = Role.Checkbox, + onClick = update, + ).semantics(properties = semanticProperties) + .then(modifier), + enabled = enabled, + title = title, + subtitle = subtitle, + icon = icon, + colors = colors, + textStyles = textStyles, + shapes = shapes, + elevation = elevation, + ) { + TriStateCheckbox( + modifier = Modifier.clearAndSetSemantics { }, + enabled = enabled, + state = mapNullableBooleanToToggleableState(state), + onClick = update, + colors = checkboxColors, + ) + } +} + +private fun mapNullableBooleanToToggleableState(state: Boolean?) = + when (state) { + true -> ToggleableState.On + false -> ToggleableState.Off + null -> ToggleableState.Indeterminate + } From 1e151cfd68c4db8a19f21472457851f7b5f99248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s?= Date: Tue, 24 Feb 2026 22:49:28 +0100 Subject: [PATCH 05/11] Update samples --- .../settings/sample/shared/SettingsScreen.kt | 532 ++---------------- .../sample/shared/SettingsScreenExpressive.kt | 252 +++++++++ .../sample/shared/SettingsScreenStandard.kt | 485 ++++++++++++++++ .../settings/ui/core/SettingsTextStyles.kt | 11 - .../settings/ui/core/SettingsTileColors.kt | 35 -- .../ui/core/SettingsTileCoreDefaults.kt | 66 +-- .../ui/expressive/SettingsButtonGroup.kt | 1 - .../ui/expressive/SettingsCheckbox.kt | 2 - .../settings/ui/expressive/SettingsGroup.kt | 2 - .../ui/expressive/SettingsMenuLink.kt | 2 - .../ui/expressive/SettingsRadioButton.kt | 2 - .../settings/ui/expressive/SettingsSwitch.kt | 15 +- .../ui/expressive/SettingsTileDefaults.kt | 10 +- .../ui/expressive/SettingsTileScaffold.kt | 88 +-- .../ui/expressive/SettingsTriStateCheckbox.kt | 2 - .../compose/settings/ui/SettingsSegmented.kt | 1 - .../compose/settings/ui/SettingsSlider.kt | 1 - .../compose/settings/ui/SettingsCheckbox.kt | 2 - .../compose/settings/ui/SettingsGroup.kt | 2 - .../compose/settings/ui/SettingsMenuLink.kt | 2 - .../settings/ui/SettingsRadioButton.kt | 2 - .../compose/settings/ui/SettingsSwitch.kt | 2 - .../settings/ui/SettingsTileDefaults.kt | 4 + .../settings/ui/SettingsTileScaffold.kt | 2 - .../settings/ui/SettingsTriStateCheckbox.kt | 2 - 25 files changed, 792 insertions(+), 733 deletions(-) create mode 100644 samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt create mode 100644 samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenStandard.kt delete mode 100644 ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTextStyles.kt delete mode 100644 ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileColors.kt diff --git a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt index 4a4a10a4..94f9354f 100644 --- a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt +++ b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt @@ -1,531 +1,69 @@ package com.alorma.compose.settings.sample.shared -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.CutCornerShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll import androidx.compose.material3.AssistChip import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.FilterChip import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.alorma.compose.settings.sample.shared.internal.MultiChoiceAlertDialog -import com.alorma.compose.settings.sample.shared.internal.SampleData -import com.alorma.compose.settings.sample.shared.internal.SampleSection -import com.alorma.compose.settings.sample.shared.internal.SingleChoiceAlertDialog -import com.alorma.compose.settings.ui.SettingsCheckbox -import com.alorma.compose.settings.ui.SettingsMenuLink -import com.alorma.compose.settings.ui.SettingsRadioButton -import com.alorma.compose.settings.ui.SettingsSegmented -import com.alorma.compose.settings.ui.SettingsSlider -import com.alorma.compose.settings.ui.SettingsSwitch -import com.alorma.compose.settings.ui.SettingsTriStateCheckbox -import com.alorma.compose.settings.ui.SettingsTileDefaults -import com.alorma.compose.settings.ui.expressive.SettingsButtonGroup + +enum class Material3Mode { + STANDARD, + EXPRESSIVE +} @OptIn(ExperimentalMaterial3Api::class) @Composable fun SettingsScreen(modifier: Modifier = Modifier) { + val materialMode = remember { mutableStateOf(Material3Mode.STANDARD) } + Scaffold( modifier = modifier, topBar = { TopAppBar( title = { Text(text = "Compose Settings") }, actions = { - AssistChip( - modifier = Modifier.padding(horizontal = 16.dp), - label = { Text(text = Version.LIB_VERSION) }, - onClick = {}, - ) + Row(modifier = Modifier.padding(horizontal = 16.dp)) { + FilterChip( + selected = materialMode.value == Material3Mode.STANDARD, + onClick = { materialMode.value = Material3Mode.STANDARD }, + label = { Text("M3") }, + ) + FilterChip( + selected = materialMode.value == Material3Mode.EXPRESSIVE, + onClick = { materialMode.value = Material3Mode.EXPRESSIVE }, + label = { Text("M3 Expressive") }, + modifier = Modifier.padding(start = 8.dp), + ) + AssistChip( + modifier = Modifier.padding(start = 8.dp), + label = { Text(text = Version.LIB_VERSION) }, + onClick = {}, + ) + } }, ) }, ) { padding -> - val scrollState = rememberScrollState() - Column( - modifier = - Modifier - .consumeWindowInsets(padding) - .verticalScroll(scrollState) - .padding(top = padding.calculateTopPadding()), + Box( + modifier = Modifier + .consumeWindowInsets(padding) + .padding(top = padding.calculateTopPadding()), ) { - SettingsSwitchSampleSection() - SettingsCheckboxSampleSection() - SettingsTriStateCheckboxSampleSection() - SettingsRadioButtonSampleSection() - SettingsMenuLinkSectionSample() - SettingsSliderSectionSample() - SettingsSelectorsSample() - SettingsShapeSampleSection() - SettingsTextStylesSampleSection() - SettingsGroupSectionSample() - SettingsGroupSpacingSample() - } - } -} - -@Composable -private fun SettingsSwitchSampleSection() { - SampleSection(title = "SettingsSwitch") { - val state = remember { mutableStateOf(false) } - SettingsSwitch( - state = state.value, - title = { Text(text = "Switch") }, - subtitle = { Text(text = "Switch subtitle") }, - onCheckedChange = { state.value = it }, - ) - } -} - -@Composable -private fun SettingsCheckboxSampleSection() { - SampleSection(title = "SettingsCheckbox") { - val state = remember { mutableStateOf(false) } - SettingsCheckbox( - state = state.value, - title = { Text(text = "Checkbox") }, - subtitle = { Text(text = "Checkbox subtitle") }, - onCheckedChange = { state.value = it }, - ) - } -} - -@Composable -private fun SettingsRadioButtonSampleSection() { - SampleSection(title = "SettingsRadioButton") { - val state = remember { mutableStateOf(null) } - - SampleData.items.forEach { sampleItem -> - SettingsRadioButton( - state = state.value == sampleItem.key, - title = { Text(text = sampleItem.title) }, - subtitle = { Text(text = sampleItem.description) }, - onClick = { state.value = sampleItem.key }, - ) - } - } -} - -@Composable -private fun SettingsTriStateCheckboxSampleSection() { - SampleSection(title = "SettingsTriStateCheckbox") { - val child1State = remember { mutableStateOf(false) } - val child2State = remember { mutableStateOf(true) } - val child3State = remember { mutableStateOf(false) } - - val allChildStates = - remember { - derivedStateOf { - listOf( - child1State.value, - child2State.value, - child3State.value, - ) - } + when (materialMode.value) { + Material3Mode.STANDARD -> StandardMaterial3Samples() + Material3Mode.EXPRESSIVE -> ExpressiveMaterial3Samples() } - - val areAllChildEnabled = allChildStates.value.all { it } - val areNoneChildEnabled = allChildStates.value.none { it } - - val areSomeChildEnabled = !areAllChildEnabled && !areNoneChildEnabled - - val triStateWithChildState = - if (areSomeChildEnabled) { - null - } else { - areAllChildEnabled || !areNoneChildEnabled - } - - SettingsTriStateCheckbox( - state = triStateWithChildState, - title = { Text(text = "TriStateCheckbox") }, - subtitle = { Text(text = "With child checkboxes") }, - onCheckedChange = { newState -> - child1State.value = newState - child2State.value = newState - child3State.value = newState - }, - ) - Column { - SettingsCheckbox( - modifier = Modifier.padding(start = 16.dp, end = 32.dp), - state = child1State.value, - title = { Text(text = "Child #1") }, - onCheckedChange = { child1State.value = it }, - ) - SettingsCheckbox( - modifier = Modifier.padding(start = 16.dp, end = 32.dp), - state = child2State.value, - title = { Text(text = "Child #2") }, - onCheckedChange = { child2State.value = it }, - ) - SettingsCheckbox( - modifier = Modifier.padding(start = 16.dp, end = 32.dp), - state = child3State.value, - title = { Text(text = "Child #3") }, - onCheckedChange = { child3State.value = it }, - ) - } - } -} - -@Composable -private fun SettingsSliderSectionSample() { - SampleSection(title = "SettingsSlider") { - DemoSlider() - } -} - -@Composable -private fun DemoSlider() { - val maxSteps = 6 - val state = remember { mutableStateOf(maxSteps / 2) } - - SettingsSlider( - title = { Text(text = "Slider") }, - value = state.value.toFloat(), - onValueChange = { state.value = it.toInt() }, - subtitle = { Text(text = "Selected value: ${state.value}") }, - valueRange = 0f..maxSteps.toFloat(), - steps = maxSteps - 1, - ) -} - -@Composable -private fun SettingsMenuLinkSectionSample() { - SampleSection(title = "SettingsMenuLink") { - val actionState = remember { mutableStateOf(false) } - - SettingsSwitch( - state = actionState.value, - title = { Text(text = "Show action") }, - onCheckedChange = { actionState.value = it }, - ) - - SettingsMenuLink( - title = { Text(text = "Menu") }, - onClick = { }, - ) - SettingsMenuLink( - title = { Text(text = "Menu") }, - subtitle = { Text(text = "With subtitle") }, - onClick = { }, - ) - } -} - -@Composable -private fun SettingsSelectorsSample() { - val items = SampleData.items - - SampleSection(title = "Selectors") { - val singleSelectionState = remember { mutableStateOf(null) } - - val showSingleChoiceDialog = remember { mutableStateOf(false) } - - SettingsMenuLink( - title = { Text(text = "Single choice") }, - subtitle = { - val item = items.find { it.key == singleSelectionState.value } - if (item == null) { - Text(text = "No item selected") - } else { - Text(text = "Item selected: ${item.title}") - } - }, - onClick = { showSingleChoiceDialog.value = true }, - ) - - if (showSingleChoiceDialog.value) { - SingleChoiceAlertDialog( - items = items, - selectedItemKey = singleSelectionState.value, - onItemSelected = { selectedItemKey -> - singleSelectionState.value = selectedItemKey - showSingleChoiceDialog.value = false - }, - ) - } - - val multipleSelectionState = remember { mutableStateOf(null) } - - val showMultiChoiceDialog = remember { mutableStateOf(false) } - - SettingsMenuLink( - title = { Text(text = "Multi choice") }, - subtitle = { - val selectedItems = items.filter { multipleSelectionState.value?.contains(it.key) ?: false } - if (selectedItems.isEmpty()) { - Text(text = "No item selected") - } else if (selectedItems.size == 1) { - Text(text = "Item selected: ${selectedItems.first().title}") - } else { - Text(text = "Items selected: ${selectedItems.joinToString(", ") { it.title }}") - } - }, - onClick = { showMultiChoiceDialog.value = true }, - ) - - if (showMultiChoiceDialog.value) { - MultiChoiceAlertDialog( - items = items, - selectedItemKeys = multipleSelectionState.value?.split("|").orEmpty(), - onItemsSelected = { selectedItemKey -> - if (selectedItemKey.isNotEmpty()) { - multipleSelectionState.value = selectedItemKey.joinToString("|") - } - showMultiChoiceDialog.value = false - }, - ) } - - val segmentedItems = listOf(1, 2, 3) - val segmentedState = remember { mutableStateOf(3) } - SettingsSegmented( - title = { Text(text = "Segmented") }, - items = segmentedItems, - itemTitleMap = { item -> "#$item" }, - selectedItem = segmentedState.value, - onItemSelected = { segmentedState.value = it }, - subtitle = { Text(text = "Selected value: ${segmentedState.value}") }, - ) - - val buttonGroupItems = listOf(1, 2, 3) - val buttonGroupState = remember { mutableStateOf(3) } - SettingsButtonGroup( - title = { Text(text = "Button group") }, - items = buttonGroupItems, - itemTitleMap = { item -> "#$item" }, - selectedItem = buttonGroupState.value, - onItemSelected = { buttonGroupState.value = it }, - subtitle = { Text(text = "Selected value: ${buttonGroupState.value}") }, - ) - } -} - -@Composable -private fun SettingsTextStylesSampleSection() { - SampleSection(title = "Custom Text Styles") { - val largeTextState = remember { mutableStateOf(false) } - SettingsSwitch( - state = largeTextState.value, - title = { Text(text = "Large title text") }, - subtitle = { Text(text = "Using custom title and subtitle styles") }, - textStyles = SettingsTileDefaults.textStyles( - titleStyle = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.Bold), - subtitleStyle = MaterialTheme.typography.bodyLarge, - ), - onCheckedChange = { largeTextState.value = it }, - ) - - val smallTextState = remember { mutableStateOf(false) } - SettingsCheckbox( - state = smallTextState.value, - title = { Text(text = "Small title text") }, - subtitle = { Text(text = "Using smaller typography") }, - textStyles = SettingsTileDefaults.textStyles( - titleStyle = MaterialTheme.typography.bodySmall, - subtitleStyle = MaterialTheme.typography.labelSmall, - ), - onCheckedChange = { smallTextState.value = it }, - ) - - val customFontState = remember { mutableStateOf(null) } - SettingsRadioButton( - state = customFontState.value == "custom", - title = { Text(text = "Custom font weight") }, - subtitle = { Text(text = "Title with bold and subtitle with light weight") }, - textStyles = SettingsTileDefaults.textStyles( - titleStyle = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.ExtraBold), - subtitleStyle = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Light), - ), - onClick = { customFontState.value = "custom" }, - ) - - SettingsMenuLink( - title = { Text(text = "Menu with custom styles") }, - subtitle = { Text(text = "Both title and subtitle customized") }, - textStyles = SettingsTileDefaults.textStyles( - titleStyle = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold), - subtitleStyle = MaterialTheme.typography.bodySmall.copy(letterSpacing = 0.5.sp), - ), - onClick = { }, - ) - } -} - -@Composable -private fun SettingsShapeSampleSection() { - SampleSection(title = "Custom Shapes") { - val roundedState = remember { mutableStateOf(false) } - SettingsSwitch( - state = roundedState.value, - title = { Text(text = "Rounded corners") }, - subtitle = { Text(text = "Using RoundedCornerShape(16.dp)") }, - shape = RoundedCornerShape(16.dp), - onCheckedChange = { roundedState.value = it }, - ) - - val cutCornerState = remember { mutableStateOf(false) } - SettingsCheckbox( - state = cutCornerState.value, - title = { Text(text = "Cut corners") }, - subtitle = { Text(text = "Using CutCornerShape(8.dp)") }, - shape = CutCornerShape(8.dp), - onCheckedChange = { cutCornerState.value = it }, - ) - - val circleState = remember { mutableStateOf(null) } - SettingsRadioButton( - state = circleState.value == "circle", - title = { Text(text = "Circle shape") }, - subtitle = { Text(text = "Using CircleShape") }, - shape = CircleShape, - onClick = { circleState.value = "circle" }, - ) - - SettingsMenuLink( - title = { Text(text = "Menu with rounded shape") }, - subtitle = { Text(text = "Using RoundedCornerShape(24.dp)") }, - shape = RoundedCornerShape(24.dp), - onClick = { }, - ) - - val sliderValue = remember { mutableStateOf(3f) } - SettingsSlider( - title = { Text(text = "Slider with shape") }, - value = sliderValue.value, - onValueChange = { sliderValue.value = it }, - subtitle = { Text(text = "Using RoundedCornerShape(12.dp)") }, - shape = RoundedCornerShape(12.dp), - valueRange = 0f..6f, - steps = 5, - ) - } -} - -@OptIn(ExperimentalMaterial3ExpressiveApi::class) -@Composable -private fun SettingsGroupSectionSample() { - val groupEnabled = remember { mutableStateOf(true) } - SampleSection( - title = "SettingsGroup", - enabled = groupEnabled.value, - ) { - SettingsSwitch( - state = groupEnabled.value, - title = { Text(text = "Group Enabled") }, - subtitle = { Text(text = "This Switch is always enabled") }, - enabled = true, - onCheckedChange = { groupEnabled.value = it }, - ) - - HorizontalDivider() - - SettingsMenuLink( - title = { Text(text = "Menu") }, - onClick = { }, - ) - - val switchState = remember { mutableStateOf(false) } - SettingsSwitch( - state = switchState.value, - title = { Text(text = "Switch") }, - subtitle = { Text(text = "Switch subtitle") }, - onCheckedChange = { switchState.value = it }, - ) - - val checkboxState = remember { mutableStateOf(false) } - SettingsCheckbox( - state = checkboxState.value, - title = { Text(text = "Checkbox") }, - subtitle = { Text(text = "Checkbox subtitle") }, - onCheckedChange = { checkboxState.value = it }, - ) - - val triSateCheckboxState = remember { mutableStateOf(null) } - SettingsTriStateCheckbox( - state = triSateCheckboxState.value, - title = { Text(text = "TriStateCheckbox") }, - subtitle = { Text(text = "With child checkboxes") }, - onCheckedChange = { newState -> triSateCheckboxState.value = newState }, - ) - - val state = remember { mutableStateOf(false) } - SettingsRadioButton( - state = state.value, - title = { Text(text = "RadioButton") }, - subtitle = { Text(text = "RadioButton subtitle") }, - onClick = { state.value = !state.value }, - ) - } -} - -@Composable -private fun SettingsGroupSpacingSample() { - SampleSection( - title = "SettingsGroup - Custom Spacing", - verticalArrangement = Arrangement.spacedBy(4.dp), - ) { - val switch1State = remember { mutableStateOf(false) } - SettingsSwitch( - state = switch1State.value, - title = { Text(text = "Compact spacing") }, - subtitle = { Text(text = "Using 4.dp spacing between items") }, - onCheckedChange = { switch1State.value = it }, - ) - - val switch2State = remember { mutableStateOf(true) } - SettingsSwitch( - state = switch2State.value, - title = { Text(text = "Another switch") }, - subtitle = { Text(text = "Notice the reduced spacing") }, - onCheckedChange = { switch2State.value = it }, - ) - - val checkboxState = remember { mutableStateOf(false) } - SettingsCheckbox( - state = checkboxState.value, - title = { Text(text = "Checkbox item") }, - subtitle = { Text(text = "All items have consistent 4.dp spacing") }, - onCheckedChange = { checkboxState.value = it }, - ) - } - - SampleSection( - title = "SettingsGroup - No Spacing", - verticalArrangement = Arrangement.Top, - ) { - val switch1State = remember { mutableStateOf(false) } - SettingsSwitch( - state = switch1State.value, - title = { Text(text = "No spacing") }, - subtitle = { Text(text = "Using Arrangement.Top for no spacing") }, - onCheckedChange = { switch1State.value = it }, - ) - - val switch2State = remember { mutableStateOf(true) } - SettingsSwitch( - state = switch2State.value, - title = { Text(text = "Tightly packed") }, - subtitle = { Text(text = "Items are directly adjacent") }, - onCheckedChange = { switch2State.value = it }, - ) } } diff --git a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt new file mode 100644 index 00000000..fba8ac71 --- /dev/null +++ b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt @@ -0,0 +1,252 @@ +package com.alorma.compose.settings.sample.shared + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.ListItemDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.alorma.compose.settings.sample.shared.internal.SampleData +import com.alorma.compose.settings.sample.shared.internal.SampleSection +import com.alorma.compose.settings.ui.expressive.SettingsButtonGroup +import com.alorma.compose.settings.ui.expressive.SettingsCheckbox +import com.alorma.compose.settings.ui.expressive.SettingsMenuLink +import com.alorma.compose.settings.ui.expressive.SettingsRadioButton +import com.alorma.compose.settings.ui.expressive.SettingsSwitch +import com.alorma.compose.settings.ui.expressive.SettingsTriStateCheckbox + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun ExpressiveMaterial3Samples() { + Column( + modifier = Modifier + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + val colors = ListItemDefaults.segmentedColors() + + SettingsSwitchSampleSectionExpressive() + SettingsCheckboxSampleSectionExpressive() + SettingsTriStateCheckboxSampleSectionExpressive() + SettingsRadioButtonSampleSectionExpressive() + SettingsMenuLinkSectionSampleExpressive() + SettingsButtonGroupSample() + SettingsGroupSectionSampleExpressive() + } +} + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +private fun SettingsSwitchSampleSectionExpressive() { + SampleSection(title = "SettingsSwitch (Expressive)") { + val state = remember { mutableStateOf(false) } + SettingsSwitch( + state = state.value, + title = { Text(text = "Switch") }, + subtitle = { Text(text = "Switch subtitle") }, + onCheckedChange = { state.value = it }, + ) + } +} + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +private fun SettingsCheckboxSampleSectionExpressive() { + SampleSection(title = "SettingsCheckbox (Expressive)") { + val state = remember { mutableStateOf(false) } + SettingsCheckbox( + state = state.value, + title = { Text(text = "Checkbox") }, + subtitle = { Text(text = "Checkbox subtitle") }, + onCheckedChange = { state.value = it }, + ) + } +} + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +private fun SettingsRadioButtonSampleSectionExpressive() { + SampleSection(title = "SettingsRadioButton (Expressive)") { + val state = remember { mutableStateOf(null) } + + SampleData.items.forEach { sampleItem -> + SettingsRadioButton( + state = state.value == sampleItem.key, + title = { Text(text = sampleItem.title) }, + subtitle = { Text(text = sampleItem.description) }, + onClick = { state.value = sampleItem.key }, + ) + } + } +} + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +private fun SettingsTriStateCheckboxSampleSectionExpressive() { + SampleSection(title = "SettingsTriStateCheckbox (Expressive)") { + val child1State = remember { mutableStateOf(false) } + val child2State = remember { mutableStateOf(true) } + val child3State = remember { mutableStateOf(false) } + + val allChildStates = + remember { + derivedStateOf { + listOf( + child1State.value, + child2State.value, + child3State.value, + ) + } + } + + val areAllChildEnabled = allChildStates.value.all { it } + val areNoneChildEnabled = allChildStates.value.none { it } + + val areSomeChildEnabled = !areAllChildEnabled && !areNoneChildEnabled + + val triStateWithChildState = + if (areSomeChildEnabled) { + null + } else { + areAllChildEnabled || !areNoneChildEnabled + } + + SettingsTriStateCheckbox( + state = triStateWithChildState, + title = { Text(text = "TriStateCheckbox") }, + subtitle = { Text(text = "With child checkboxes") }, + onCheckedChange = { newState -> + child1State.value = newState + child2State.value = newState + child3State.value = newState + }, + ) + Column { + SettingsCheckbox( + modifier = Modifier.padding(start = 16.dp, end = 32.dp), + state = child1State.value, + title = { Text(text = "Child #1") }, + onCheckedChange = { child1State.value = it }, + ) + SettingsCheckbox( + modifier = Modifier.padding(start = 16.dp, end = 32.dp), + state = child2State.value, + title = { Text(text = "Child #2") }, + onCheckedChange = { child2State.value = it }, + ) + SettingsCheckbox( + modifier = Modifier.padding(start = 16.dp, end = 32.dp), + state = child3State.value, + title = { Text(text = "Child #3") }, + onCheckedChange = { child3State.value = it }, + ) + } + } +} + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +private fun SettingsMenuLinkSectionSampleExpressive() { + SampleSection(title = "SettingsMenuLink (Expressive)") { + val actionState = remember { mutableStateOf(false) } + + SettingsSwitch( + state = actionState.value, + title = { Text(text = "Show action") }, + onCheckedChange = { actionState.value = it }, + ) + + SettingsMenuLink( + title = { Text(text = "Menu") }, + onClick = { }, + ) + SettingsMenuLink( + title = { Text(text = "Menu") }, + subtitle = { Text(text = "With subtitle") }, + onClick = { }, + ) + } +} + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +private fun SettingsButtonGroupSample() { + SampleSection(title = "SettingsButtonGroup (Expressive)") { + val buttonGroupItems = listOf(1, 2, 3) + val buttonGroupState = remember { mutableStateOf(3) } + SettingsButtonGroup( + title = { Text(text = "Button group") }, + items = buttonGroupItems, + itemTitleMap = { item -> "#$item" }, + selectedItem = buttonGroupState.value, + onItemSelected = { buttonGroupState.value = it }, + subtitle = { Text(text = "Selected value: ${buttonGroupState.value}") }, + ) + } +} + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +private fun SettingsGroupSectionSampleExpressive() { + val groupEnabled = remember { mutableStateOf(true) } + SampleSection( + title = "SettingsGroup (Expressive)", + enabled = groupEnabled.value, + ) { + SettingsSwitch( + state = groupEnabled.value, + title = { Text(text = "Group Enabled") }, + subtitle = { Text(text = "This Switch is always enabled") }, + enabled = true, + onCheckedChange = { groupEnabled.value = it }, + ) + + HorizontalDivider() + + SettingsMenuLink( + title = { Text(text = "Menu") }, + onClick = { }, + ) + + val switchState = remember { mutableStateOf(false) } + SettingsSwitch( + state = switchState.value, + title = { Text(text = "Switch") }, + subtitle = { Text(text = "Switch subtitle") }, + onCheckedChange = { switchState.value = it }, + ) + + val checkboxState = remember { mutableStateOf(false) } + SettingsCheckbox( + state = checkboxState.value, + title = { Text(text = "Checkbox") }, + subtitle = { Text(text = "Checkbox subtitle") }, + onCheckedChange = { checkboxState.value = it }, + ) + + val triSateCheckboxState = remember { mutableStateOf(null) } + SettingsTriStateCheckbox( + state = triSateCheckboxState.value, + title = { Text(text = "TriStateCheckbox") }, + subtitle = { Text(text = "With child checkboxes") }, + onCheckedChange = { newState -> triSateCheckboxState.value = newState }, + ) + + val state = remember { mutableStateOf(false) } + SettingsRadioButton( + state = state.value, + title = { Text(text = "RadioButton") }, + subtitle = { Text(text = "RadioButton subtitle") }, + onClick = { state.value = !state.value }, + ) + } +} diff --git a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenStandard.kt b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenStandard.kt new file mode 100644 index 00000000..795feb8c --- /dev/null +++ b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenStandard.kt @@ -0,0 +1,485 @@ +package com.alorma.compose.settings.sample.shared + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.CutCornerShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.alorma.compose.settings.sample.shared.internal.MultiChoiceAlertDialog +import com.alorma.compose.settings.sample.shared.internal.SampleData +import com.alorma.compose.settings.sample.shared.internal.SampleSection +import com.alorma.compose.settings.sample.shared.internal.SingleChoiceAlertDialog +import com.alorma.compose.settings.ui.SettingsCheckbox +import com.alorma.compose.settings.ui.SettingsMenuLink +import com.alorma.compose.settings.ui.SettingsRadioButton +import com.alorma.compose.settings.ui.SettingsSegmented +import com.alorma.compose.settings.ui.SettingsSlider +import com.alorma.compose.settings.ui.SettingsSwitch +import com.alorma.compose.settings.ui.SettingsTileDefaults +import com.alorma.compose.settings.ui.SettingsTriStateCheckbox + +@Composable +fun StandardMaterial3Samples() { + Column( + modifier = Modifier + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + SettingsSwitchSampleSection() + SettingsCheckboxSampleSection() + SettingsTriStateCheckboxSampleSection() + SettingsRadioButtonSampleSection() + SettingsMenuLinkSectionSample() + SettingsSliderSectionSample() + SettingsSelectorsSample() + SettingsShapeSampleSection() + SettingsTextStylesSampleSection() + SettingsGroupSectionSample() + SettingsGroupSpacingSample() + } +} + +@Composable +private fun SettingsSwitchSampleSection() { + SampleSection(title = "SettingsSwitch") { + val state = remember { mutableStateOf(false) } + SettingsSwitch( + state = state.value, + title = { Text(text = "Switch") }, + subtitle = { Text(text = "Switch subtitle") }, + onCheckedChange = { state.value = it }, + ) + } +} + +@Composable +private fun SettingsCheckboxSampleSection() { + SampleSection(title = "SettingsCheckbox") { + val state = remember { mutableStateOf(false) } + SettingsCheckbox( + state = state.value, + title = { Text(text = "Checkbox") }, + subtitle = { Text(text = "Checkbox subtitle") }, + onCheckedChange = { state.value = it }, + ) + } +} + +@Composable +private fun SettingsRadioButtonSampleSection() { + SampleSection(title = "SettingsRadioButton") { + val state = remember { mutableStateOf(null) } + + SampleData.items.forEach { sampleItem -> + SettingsRadioButton( + state = state.value == sampleItem.key, + title = { Text(text = sampleItem.title) }, + subtitle = { Text(text = sampleItem.description) }, + onClick = { state.value = sampleItem.key }, + ) + } + } +} + +@Composable +private fun SettingsTriStateCheckboxSampleSection() { + SampleSection(title = "SettingsTriStateCheckbox") { + val child1State = remember { mutableStateOf(false) } + val child2State = remember { mutableStateOf(true) } + val child3State = remember { mutableStateOf(false) } + + val allChildStates = + remember { + derivedStateOf { + listOf( + child1State.value, + child2State.value, + child3State.value, + ) + } + } + + val areAllChildEnabled = allChildStates.value.all { it } + val areNoneChildEnabled = allChildStates.value.none { it } + + val areSomeChildEnabled = !areAllChildEnabled && !areNoneChildEnabled + + val triStateWithChildState = + if (areSomeChildEnabled) { + null + } else { + areAllChildEnabled || !areNoneChildEnabled + } + + SettingsTriStateCheckbox( + state = triStateWithChildState, + title = { Text(text = "TriStateCheckbox") }, + subtitle = { Text(text = "With child checkboxes") }, + onCheckedChange = { newState -> + child1State.value = newState + child2State.value = newState + child3State.value = newState + }, + ) + Column { + SettingsCheckbox( + modifier = Modifier.padding(start = 16.dp, end = 32.dp), + state = child1State.value, + title = { Text(text = "Child #1") }, + onCheckedChange = { child1State.value = it }, + ) + SettingsCheckbox( + modifier = Modifier.padding(start = 16.dp, end = 32.dp), + state = child2State.value, + title = { Text(text = "Child #2") }, + onCheckedChange = { child2State.value = it }, + ) + SettingsCheckbox( + modifier = Modifier.padding(start = 16.dp, end = 32.dp), + state = child3State.value, + title = { Text(text = "Child #3") }, + onCheckedChange = { child3State.value = it }, + ) + } + } +} + +@Composable +private fun SettingsSliderSectionSample() { + SampleSection(title = "SettingsSlider") { + val maxSteps = 6 + val state = remember { mutableStateOf(maxSteps / 2) } + + SettingsSlider( + title = { Text(text = "Slider") }, + value = state.value.toFloat(), + onValueChange = { state.value = it.toInt() }, + subtitle = { Text(text = "Selected value: ${state.value}") }, + valueRange = 0f..maxSteps.toFloat(), + steps = maxSteps - 1, + ) + } +} + +@Composable +private fun SettingsMenuLinkSectionSample() { + SampleSection(title = "SettingsMenuLink") { + val actionState = remember { mutableStateOf(false) } + + SettingsSwitch( + state = actionState.value, + title = { Text(text = "Show action") }, + onCheckedChange = { actionState.value = it }, + ) + + SettingsMenuLink( + title = { Text(text = "Menu") }, + onClick = { }, + ) + SettingsMenuLink( + title = { Text(text = "Menu") }, + subtitle = { Text(text = "With subtitle") }, + onClick = { }, + ) + } +} + +@Composable +private fun SettingsSelectorsSample() { + val items = SampleData.items + + SampleSection(title = "Selectors") { + val singleSelectionState = remember { mutableStateOf(null) } + val showSingleChoiceDialog = remember { mutableStateOf(false) } + + SettingsMenuLink( + title = { Text(text = "Single choice") }, + subtitle = { + val item = items.find { it.key == singleSelectionState.value } + if (item == null) { + Text(text = "No item selected") + } else { + Text(text = "Item selected: ${item.title}") + } + }, + onClick = { showSingleChoiceDialog.value = true }, + ) + + if (showSingleChoiceDialog.value) { + SingleChoiceAlertDialog( + items = items, + selectedItemKey = singleSelectionState.value, + onItemSelected = { selectedItemKey -> + singleSelectionState.value = selectedItemKey + showSingleChoiceDialog.value = false + }, + ) + } + + val multipleSelectionState = remember { mutableStateOf(null) } + val showMultiChoiceDialog = remember { mutableStateOf(false) } + + SettingsMenuLink( + title = { Text(text = "Multi choice") }, + subtitle = { + val selectedItems = items.filter { multipleSelectionState.value?.contains(it.key) ?: false } + if (selectedItems.isEmpty()) { + Text(text = "No item selected") + } else if (selectedItems.size == 1) { + Text(text = "Item selected: ${selectedItems.first().title}") + } else { + Text(text = "Items selected: ${selectedItems.joinToString(", ") { it.title }}") + } + }, + onClick = { showMultiChoiceDialog.value = true }, + ) + + if (showMultiChoiceDialog.value) { + MultiChoiceAlertDialog( + items = items, + selectedItemKeys = multipleSelectionState.value?.split("|").orEmpty(), + onItemsSelected = { selectedItemKey -> + if (selectedItemKey.isNotEmpty()) { + multipleSelectionState.value = selectedItemKey.joinToString("|") + } + showMultiChoiceDialog.value = false + }, + ) + } + + val segmentedItems = listOf(1, 2, 3) + val segmentedState = remember { mutableStateOf(3) } + SettingsSegmented( + title = { Text(text = "Segmented") }, + items = segmentedItems, + itemTitleMap = { item -> "#$item" }, + selectedItem = segmentedState.value, + onItemSelected = { segmentedState.value = it }, + subtitle = { Text(text = "Selected value: ${segmentedState.value}") }, + ) + } +} + +@Composable +private fun SettingsTextStylesSampleSection() { + SampleSection(title = "Custom Text Styles") { + val largeTextState = remember { mutableStateOf(false) } + SettingsSwitch( + state = largeTextState.value, + title = { Text(text = "Large title text") }, + subtitle = { Text(text = "Using custom title and subtitle styles") }, + textStyles = SettingsTileDefaults.textStyles( + titleStyle = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.Bold), + subtitleStyle = MaterialTheme.typography.bodyLarge, + ), + onCheckedChange = { largeTextState.value = it }, + ) + + val smallTextState = remember { mutableStateOf(false) } + SettingsCheckbox( + state = smallTextState.value, + title = { Text(text = "Small title text") }, + subtitle = { Text(text = "Using smaller typography") }, + textStyles = SettingsTileDefaults.textStyles( + titleStyle = MaterialTheme.typography.bodySmall, + subtitleStyle = MaterialTheme.typography.labelSmall, + ), + onCheckedChange = { smallTextState.value = it }, + ) + + val customFontState = remember { mutableStateOf(null) } + SettingsRadioButton( + state = customFontState.value == "custom", + title = { Text(text = "Custom font weight") }, + subtitle = { Text(text = "Title with bold and subtitle with light weight") }, + textStyles = SettingsTileDefaults.textStyles( + titleStyle = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.ExtraBold), + subtitleStyle = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Light), + ), + onClick = { customFontState.value = "custom" }, + ) + + SettingsMenuLink( + title = { Text(text = "Menu with custom styles") }, + subtitle = { Text(text = "Both title and subtitle customized") }, + textStyles = SettingsTileDefaults.textStyles( + titleStyle = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold), + subtitleStyle = MaterialTheme.typography.bodySmall.copy(letterSpacing = 0.5.sp), + ), + onClick = { }, + ) + } +} + +@Composable +private fun SettingsShapeSampleSection() { + SampleSection(title = "Custom Shapes") { + val roundedState = remember { mutableStateOf(false) } + SettingsSwitch( + state = roundedState.value, + title = { Text(text = "Rounded corners") }, + subtitle = { Text(text = "Using RoundedCornerShape(16.dp)") }, + shape = RoundedCornerShape(16.dp), + onCheckedChange = { roundedState.value = it }, + ) + + val cutCornerState = remember { mutableStateOf(false) } + SettingsCheckbox( + state = cutCornerState.value, + title = { Text(text = "Cut corners") }, + subtitle = { Text(text = "Using CutCornerShape(8.dp)") }, + shape = CutCornerShape(8.dp), + onCheckedChange = { cutCornerState.value = it }, + ) + + val circleState = remember { mutableStateOf(null) } + SettingsRadioButton( + state = circleState.value == "circle", + title = { Text(text = "Circle shape") }, + subtitle = { Text(text = "Using CircleShape") }, + shape = CircleShape, + onClick = { circleState.value = "circle" }, + ) + + SettingsMenuLink( + title = { Text(text = "Menu with rounded shape") }, + subtitle = { Text(text = "Using RoundedCornerShape(24.dp)") }, + shape = RoundedCornerShape(24.dp), + onClick = { }, + ) + + val sliderValue = remember { mutableStateOf(3f) } + SettingsSlider( + title = { Text(text = "Slider with shape") }, + value = sliderValue.value, + onValueChange = { sliderValue.value = it }, + subtitle = { Text(text = "Using RoundedCornerShape(12.dp)") }, + shape = RoundedCornerShape(12.dp), + valueRange = 0f..6f, + steps = 5, + ) + } +} + +@Composable +private fun SettingsGroupSectionSample() { + val groupEnabled = remember { mutableStateOf(true) } + SampleSection( + title = "SettingsGroup", + enabled = groupEnabled.value, + ) { + SettingsSwitch( + state = groupEnabled.value, + title = { Text(text = "Group Enabled") }, + subtitle = { Text(text = "This Switch is always enabled") }, + enabled = true, + onCheckedChange = { groupEnabled.value = it }, + ) + + HorizontalDivider() + + SettingsMenuLink( + title = { Text(text = "Menu") }, + onClick = { }, + ) + + val switchState = remember { mutableStateOf(false) } + SettingsSwitch( + state = switchState.value, + title = { Text(text = "Switch") }, + subtitle = { Text(text = "Switch subtitle") }, + onCheckedChange = { switchState.value = it }, + ) + + val checkboxState = remember { mutableStateOf(false) } + SettingsCheckbox( + state = checkboxState.value, + title = { Text(text = "Checkbox") }, + subtitle = { Text(text = "Checkbox subtitle") }, + onCheckedChange = { checkboxState.value = it }, + ) + + val triSateCheckboxState = remember { mutableStateOf(null) } + SettingsTriStateCheckbox( + state = triSateCheckboxState.value, + title = { Text(text = "TriStateCheckbox") }, + subtitle = { Text(text = "With child checkboxes") }, + onCheckedChange = { newState -> triSateCheckboxState.value = newState }, + ) + + val state = remember { mutableStateOf(false) } + SettingsRadioButton( + state = state.value, + title = { Text(text = "RadioButton") }, + subtitle = { Text(text = "RadioButton subtitle") }, + onClick = { state.value = !state.value }, + ) + } +} + +@Composable +private fun SettingsGroupSpacingSample() { + SampleSection( + title = "SettingsGroup - Custom Spacing", + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + val switch1State = remember { mutableStateOf(false) } + SettingsSwitch( + state = switch1State.value, + title = { Text(text = "Compact spacing") }, + subtitle = { Text(text = "Using 4.dp spacing between items") }, + onCheckedChange = { switch1State.value = it }, + ) + + val switch2State = remember { mutableStateOf(true) } + SettingsSwitch( + state = switch2State.value, + title = { Text(text = "Another switch") }, + subtitle = { Text(text = "Notice the reduced spacing") }, + onCheckedChange = { switch2State.value = it }, + ) + + val checkboxState = remember { mutableStateOf(false) } + SettingsCheckbox( + state = checkboxState.value, + title = { Text(text = "Checkbox item") }, + subtitle = { Text(text = "All items have consistent 4.dp spacing") }, + onCheckedChange = { checkboxState.value = it }, + ) + } + + SampleSection( + title = "SettingsGroup - No Spacing", + verticalArrangement = Arrangement.Top, + ) { + val switch1State = remember { mutableStateOf(false) } + SettingsSwitch( + state = switch1State.value, + title = { Text(text = "No spacing") }, + subtitle = { Text(text = "Using Arrangement.Top for no spacing") }, + onCheckedChange = { switch1State.value = it }, + ) + + val switch2State = remember { mutableStateOf(true) } + SettingsSwitch( + state = switch2State.value, + title = { Text(text = "Tightly packed") }, + subtitle = { Text(text = "Items are directly adjacent") }, + onCheckedChange = { switch2State.value = it }, + ) + } +} diff --git a/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTextStyles.kt b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTextStyles.kt deleted file mode 100644 index da0a02ad..00000000 --- a/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTextStyles.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.alorma.compose.settings.ui.core - -import androidx.compose.runtime.Immutable -import androidx.compose.ui.text.TextStyle - -@Immutable -class SettingsTextStyles( - val groupTitleStyle: TextStyle, - val titleStyle: TextStyle, - val subtitleStyle: TextStyle, -) diff --git a/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileColors.kt b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileColors.kt deleted file mode 100644 index d090a2ed..00000000 --- a/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileColors.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.alorma.compose.settings.ui.core - -import androidx.compose.runtime.Immutable -import androidx.compose.runtime.Stable -import androidx.compose.ui.graphics.Color - -@Immutable -class SettingsTileColors( - val containerColor: Color, - val titleColor: Color, - val groupTitleColor: Color, - val iconColor: Color, - val subtitleColor: Color, - val actionColor: Color, - val disabledTitleColor: Color, - val disabledGroupTitleColor: Color, - val disabledIconColor: Color, - val disabledSubtitleColor: Color, - val disabledActionColor: Color, -) { - @Stable - fun groupTitleColor(enabled: Boolean): Color = if (enabled) groupTitleColor else disabledGroupTitleColor - - @Stable - fun titleColor(enabled: Boolean): Color = if (enabled) titleColor else disabledTitleColor - - @Stable - fun iconColor(enabled: Boolean): Color = if (enabled) iconColor else disabledIconColor - - @Stable - fun subtitleColor(enabled: Boolean): Color = if (enabled) subtitleColor else disabledSubtitleColor - - @Stable - fun actionColor(enabled: Boolean): Color = if (enabled) actionColor else disabledActionColor -} diff --git a/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt index c90ee41e..17524b72 100644 --- a/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt +++ b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt @@ -1,9 +1,8 @@ package com.alorma.compose.settings.ui.core -import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ListItemColors +import androidx.compose.material3.ListItemDefaults import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Dp /** @@ -15,65 +14,6 @@ abstract class SettingsTileCoreDefaults { abstract val Elevation: Dp val DisabledAlpha: Float = 0.38f - /** - * Creates a [SettingsTextStyles] with default values from MaterialTheme typography - * and respects [LocalSettingsTextStyles] overrides. - */ @Composable - fun textStyles( - groupTitleStyle: TextStyle = - LocalSettingsTextStyles.current?.groupTitleStyle - ?: MaterialTheme.typography.titleMedium, - titleStyle: TextStyle = - LocalSettingsTextStyles.current?.titleStyle - ?: MaterialTheme.typography.bodyLarge, - subtitleStyle: TextStyle = - LocalSettingsTextStyles.current?.subtitleStyle - ?: MaterialTheme.typography.bodyMedium, - ): SettingsTextStyles = - SettingsTextStyles( - groupTitleStyle = groupTitleStyle, - titleStyle = titleStyle, - subtitleStyle = subtitleStyle, - ) - - /** - * Creates a [SettingsTileColors] with default values from MaterialTheme color scheme - * and respects [LocalSettingsTileColors] overrides. - */ - @Composable - fun colors( - containerColor: Color = - LocalSettingsTileColors.current?.containerColor - ?: MaterialTheme.colorScheme.surface, - titleColor: Color = - LocalSettingsTileColors.current?.titleColor - ?: MaterialTheme.colorScheme.primary, - groupTitleColor: Color = - LocalSettingsTileColors.current?.groupTitleColor - ?: MaterialTheme.colorScheme.onBackground, - iconColor: Color = LocalSettingsTileColors.current?.iconColor ?: MaterialTheme.colorScheme.onSurface, - subtitleColor: Color = LocalSettingsTileColors.current?.subtitleColor ?: MaterialTheme.colorScheme.onSurface, - actionColor: Color = - LocalSettingsTileColors.current?.actionColor - ?: MaterialTheme.colorScheme.primary, - disabledTitleColor: Color = titleColor.copy(alpha = SettingsTileConstants.DisabledAlpha), - disabledGroupTitleColor: Color = groupTitleColor.copy(alpha = SettingsTileConstants.DisabledAlpha), - disabledIconColor: Color = iconColor.copy(alpha = SettingsTileConstants.DisabledAlpha), - disabledSubtitleColor: Color = subtitleColor.copy(alpha = SettingsTileConstants.DisabledAlpha), - disabledActionColor: Color = actionColor.copy(alpha = SettingsTileConstants.DisabledAlpha), - ): SettingsTileColors = - SettingsTileColors( - containerColor = containerColor, - titleColor = titleColor, - groupTitleColor = groupTitleColor, - iconColor = iconColor, - subtitleColor = subtitleColor, - actionColor = actionColor, - disabledTitleColor = disabledTitleColor, - disabledGroupTitleColor = disabledGroupTitleColor, - disabledIconColor = disabledIconColor, - disabledSubtitleColor = disabledSubtitleColor, - disabledActionColor = disabledActionColor, - ) + abstract fun colors(): ListItemColors } diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt index 5c786ac3..4099556b 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt @@ -16,7 +16,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTileColors @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt index 129093fd..1717e054 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt @@ -15,8 +15,6 @@ import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.core.SettingsTextStyles @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt index 25e314ef..5a7f821c 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt @@ -18,8 +18,6 @@ import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTextStyles -import com.alorma.compose.settings.ui.core.SettingsTileColors @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt index c99fcfcf..46985330 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt @@ -9,8 +9,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.semantics import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.core.SettingsTextStyles @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt index ab5b2631..7d14b765 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt @@ -14,8 +14,6 @@ import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.core.SettingsTextStyles @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt index 3c4fd26f..f623c5f5 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt @@ -2,6 +2,7 @@ package com.alorma.compose.settings.ui.expressive import androidx.compose.foundation.selection.toggleable import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ListItemColors import androidx.compose.material3.ListItemElevation import androidx.compose.material3.ListItemShapes import androidx.compose.material3.Switch @@ -15,8 +16,6 @@ import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.core.SettingsTextStyles @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable @@ -27,15 +26,8 @@ fun SettingsSwitch( enabled: Boolean = LocalSettingsGroupEnabled.current, icon: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null, - colors: SettingsTileColors = SettingsTileDefaults.colors(), - switchColors: SwitchColors = - SwitchDefaults.colors( - checkedTrackColor = colors.actionColor(enabled), - checkedThumbColor = contentColorFor(colors.actionColor(enabled)), - disabledCheckedTrackColor = colors.actionColor(enabled), - disabledCheckedThumbColor = contentColorFor(colors.actionColor(enabled)), - ), - textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + colors: ListItemColors = SettingsTileDefaults.colors(), + switchColors: SwitchColors = SwitchDefaults.colors(), shapes: ListItemShapes = SettingsTileDefaults.shapes(), elevation: ListItemElevation = SettingsTileDefaults.elevation(), semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, @@ -58,7 +50,6 @@ fun SettingsSwitch( subtitle = subtitle, icon = icon, colors = colors, - textStyles = textStyles, shapes = shapes, elevation = elevation, ) { diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileDefaults.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileDefaults.kt index 3d85f032..123c526e 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileDefaults.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileDefaults.kt @@ -1,6 +1,7 @@ package com.alorma.compose.settings.ui.expressive import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ListItemColors import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.ListItemElevation import androidx.compose.material3.ListItemShapes @@ -11,17 +12,14 @@ import com.alorma.compose.settings.ui.core.SettingsTileCoreDefaults object SettingsTileDefaults : SettingsTileCoreDefaults() { override val Elevation: Dp = ListItemDefaults.Elevation - /** - * Returns the default shapes for expressive Material 3 segmented list items. - */ @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun shapes(): ListItemShapes = ListItemDefaults.shapes() - /** - * Returns the default elevation for expressive Material 3 segmented list items. - */ @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun elevation(): ListItemElevation = ListItemDefaults.elevation() + + @Composable + override fun colors(): ListItemColors = ListItemDefaults.colors() } diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt index e431ac22..eca63bee 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt @@ -5,17 +5,9 @@ import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ListItemColors import androidx.compose.material3.ListItemElevation import androidx.compose.material3.ListItemShapes -import androidx.compose.material3.LocalContentColor -import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.SegmentedListItem import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.unit.Dp -import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.core.SettingsTextStyles @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable @@ -25,47 +17,14 @@ fun SettingsTileScaffold( enabled: Boolean = true, subtitle: @Composable (() -> Unit)? = null, icon: @Composable (() -> Unit)? = null, - colors: SettingsTileColors = SettingsTileDefaults.colors(), - textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + colors: ListItemColors = SettingsTileDefaults.colors(), shapes: ListItemShapes = SettingsTileDefaults.shapes(), elevation: ListItemElevation = SettingsTileDefaults.elevation(), action: @Composable (() -> Unit)? = null, ) { - val decoratedTitle: @Composable () -> Unit = { - ProvideContentColorAndTextStyle( - contentColor = colors.titleColor(enabled), - textStyle = textStyles.titleStyle, - ) { - title() - } - } - val decoratedSubtitle: @Composable (() -> Unit)? = - subtitle?.let { - { - ProvideContentColorAndTextStyle( - contentColor = colors.subtitleColor(enabled), - textStyle = textStyles.subtitleStyle, - ) { - subtitle() - } - } - } - val decoratedIcon: @Composable (() -> Unit)? = - icon?.let { - { - ProvideContentColor(colors.iconColor(enabled)) { - icon() - } - } - } - val decoratedAction: @Composable (() -> Unit)? = - action?.let { - { - ProvideContentColor(colors.actionColor(enabled)) { - action() - } - } - } + val decoratedSubtitle: @Composable (() -> Unit)? = subtitle?.let { { subtitle() } } + val decoratedIcon: @Composable (() -> Unit)? = icon?.let { { icon() } } + val decoratedAction: @Composable (() -> Unit)? = action?.let { { action() } } SegmentedListItem( selected = false, @@ -76,44 +35,9 @@ fun SettingsTileScaffold( leadingContent = decoratedIcon, trailingContent = decoratedAction, supportingContent = decoratedSubtitle, - colors = - ListItemColors( - containerColor = colors.containerColor, - headlineColor = colors.titleColor, - leadingIconColor = colors.iconColor, - overlineColor = colors.actionColor, - supportingTextColor = colors.subtitleColor, - trailingIconColor = colors.actionColor, - disabledHeadlineColor = colors.disabledTitleColor, - disabledLeadingIconColor = colors.disabledIconColor, - disabledTrailingIconColor = colors.disabledActionColor, - ), + colors = colors, elevation = elevation, ) { - decoratedTitle() - } -} - -@Composable -private fun ProvideContentColor( - contentColor: Color, - content: @Composable () -> Unit, -) { - CompositionLocalProvider(LocalContentColor provides contentColor) { - content() - } -} - -@Composable -private fun ProvideContentColorAndTextStyle( - contentColor: Color, - textStyle: TextStyle, - content: @Composable () -> Unit, -) { - CompositionLocalProvider( - LocalContentColor provides contentColor, - LocalTextStyle provides textStyle, - ) { - content() + title() } } diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt index 4b2b88f6..59c27e3e 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt @@ -16,8 +16,6 @@ import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics import androidx.compose.ui.state.ToggleableState import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.core.SettingsTextStyles @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable diff --git a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt index 9a8a4dfe..75d1af40 100644 --- a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt +++ b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt @@ -17,7 +17,6 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTileColors @Composable @Suppress("LongParameterList") diff --git a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt index f8a887b5..f2fe3a8b 100644 --- a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt +++ b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTileColors @Composable @Suppress("LongParameterList") diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt index 747f2f5c..563cb7e5 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt @@ -14,8 +14,6 @@ import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable fun SettingsCheckbox( diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt index 1cbed8ec..83059655 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt @@ -17,8 +17,6 @@ import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTextStyles -import com.alorma.compose.settings.ui.core.SettingsTileColors @Composable fun SettingsGroup( diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt index ffa60553..94e1c2ba 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt @@ -8,8 +8,6 @@ import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable fun SettingsMenuLink( diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt index 76dc3e7a..dcd69df6 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt @@ -13,8 +13,6 @@ import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable fun SettingsRadioButton( diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt index bc7209df..8d66b510 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt @@ -14,8 +14,6 @@ import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable fun SettingsSwitch( diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileDefaults.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileDefaults.kt index ad964eb9..93b36851 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileDefaults.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileDefaults.kt @@ -1,5 +1,6 @@ package com.alorma.compose.settings.ui +import androidx.compose.material3.ListItemColors import androidx.compose.material3.ListItemDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Shape @@ -11,4 +12,7 @@ object SettingsTileDefaults : SettingsTileCoreDefaults() { @Composable fun shape(): Shape = ListItemDefaults.shape + + @Composable + override fun colors(): ListItemColors = ListItemDefaults.colors() } diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt index f9a97d5d..b434e49c 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt @@ -13,8 +13,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Dp -import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable fun SettingsTileScaffold( diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt index f9fb10a9..83f1746d 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt @@ -15,8 +15,6 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.unit.Dp import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled -import com.alorma.compose.settings.ui.core.SettingsTileColors -import com.alorma.compose.settings.ui.core.SettingsTextStyles @Composable fun SettingsTriStateCheckbox( From bc8426758fac6a786bc747294afdbe93a09488e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s?= Date: Tue, 24 Feb 2026 22:56:26 +0100 Subject: [PATCH 06/11] Update samples --- ARCHITECTURE.md | 2 +- CONTRIBUTING.md | 2 +- .../sample/shared/SettingsScreenExpressive.kt | 75 ++++++-- .../sample/shared/SettingsScreenStandard.kt | 165 ++++++++++-------- .../settings/ui/core/CompositionLocals.kt | 5 +- .../ui/expressive/SettingsButtonGroup.kt | 2 +- .../ui/expressive/SettingsCheckbox.kt | 2 +- .../settings/ui/expressive/SettingsGroup.kt | 2 +- .../ui/expressive/SettingsMenuLink.kt | 2 +- .../ui/expressive/SettingsRadioButton.kt | 2 +- .../ui/expressive/SettingsTriStateCheckbox.kt | 2 +- .../compose/settings/ui/SettingsSegmented.kt | 2 +- .../compose/settings/ui/SettingsSlider.kt | 2 +- .../compose/settings/ui/SettingsCheckbox.kt | 13 +- .../compose/settings/ui/SettingsGroup.kt | 2 +- .../compose/settings/ui/SettingsMenuLink.kt | 2 +- .../settings/ui/SettingsRadioButton.kt | 2 +- .../compose/settings/ui/SettingsSwitch.kt | 2 +- .../settings/ui/SettingsTileScaffold.kt | 2 +- .../settings/ui/SettingsTriStateCheckbox.kt | 2 +- 20 files changed, 171 insertions(+), 119 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 9860cd26..6eac8d71 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -249,7 +249,7 @@ fun CustomSettingsComponent( title: @Composable () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), ) { val groupEnabled = LocalSettingsGroupEnabled.current val actualEnabled = enabled && groupEnabled diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 33040133..bbc2dfe1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -164,7 +164,7 @@ When creating new settings components: subtitle: @Composable (() -> Unit)? = null, icon: @Composable (() -> Unit)? = null, enabled: Boolean = true, - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), // Component-specific parameters ) { // Implementation using SettingsTileScaffold diff --git a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt index fba8ac71..1c4226e7 100644 --- a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt +++ b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.ListItemColors import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -34,23 +35,40 @@ fun ExpressiveMaterial3Samples() { ) { val colors = ListItemDefaults.segmentedColors() - SettingsSwitchSampleSectionExpressive() - SettingsCheckboxSampleSectionExpressive() - SettingsTriStateCheckboxSampleSectionExpressive() - SettingsRadioButtonSampleSectionExpressive() - SettingsMenuLinkSectionSampleExpressive() - SettingsButtonGroupSample() - SettingsGroupSectionSampleExpressive() + SettingsSwitchSampleSectionExpressive( + colors = colors, + ) + SettingsCheckboxSampleSectionExpressive( + colors = colors, + ) + SettingsTriStateCheckboxSampleSectionExpressive( + colors = colors, + ) + SettingsRadioButtonSampleSectionExpressive( + colors = colors, + ) + SettingsMenuLinkSectionSampleExpressive( + colors = colors, + ) + SettingsButtonGroupSample( + colors = colors, + ) + SettingsGroupSectionSampleExpressive( + colors = colors, + ) } } @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -private fun SettingsSwitchSampleSectionExpressive() { +private fun SettingsSwitchSampleSectionExpressive( + colors: ListItemColors, +) { SampleSection(title = "SettingsSwitch (Expressive)") { val state = remember { mutableStateOf(false) } SettingsSwitch( state = state.value, + colors = colors, title = { Text(text = "Switch") }, subtitle = { Text(text = "Switch subtitle") }, onCheckedChange = { state.value = it }, @@ -60,11 +78,14 @@ private fun SettingsSwitchSampleSectionExpressive() { @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -private fun SettingsCheckboxSampleSectionExpressive() { +private fun SettingsCheckboxSampleSectionExpressive( + colors: ListItemColors, +) { SampleSection(title = "SettingsCheckbox (Expressive)") { val state = remember { mutableStateOf(false) } SettingsCheckbox( state = state.value, + colors = colors, title = { Text(text = "Checkbox") }, subtitle = { Text(text = "Checkbox subtitle") }, onCheckedChange = { state.value = it }, @@ -74,13 +95,16 @@ private fun SettingsCheckboxSampleSectionExpressive() { @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -private fun SettingsRadioButtonSampleSectionExpressive() { +private fun SettingsRadioButtonSampleSectionExpressive( + colors: ListItemColors, +) { SampleSection(title = "SettingsRadioButton (Expressive)") { val state = remember { mutableStateOf(null) } SampleData.items.forEach { sampleItem -> SettingsRadioButton( state = state.value == sampleItem.key, + colors = colors, title = { Text(text = sampleItem.title) }, subtitle = { Text(text = sampleItem.description) }, onClick = { state.value = sampleItem.key }, @@ -91,7 +115,9 @@ private fun SettingsRadioButtonSampleSectionExpressive() { @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -private fun SettingsTriStateCheckboxSampleSectionExpressive() { +private fun SettingsTriStateCheckboxSampleSectionExpressive( + colors: ListItemColors, +) { SampleSection(title = "SettingsTriStateCheckbox (Expressive)") { val child1State = remember { mutableStateOf(false) } val child2State = remember { mutableStateOf(true) } @@ -122,6 +148,7 @@ private fun SettingsTriStateCheckboxSampleSectionExpressive() { SettingsTriStateCheckbox( state = triStateWithChildState, + colors = colors, title = { Text(text = "TriStateCheckbox") }, subtitle = { Text(text = "With child checkboxes") }, onCheckedChange = { newState -> @@ -134,18 +161,21 @@ private fun SettingsTriStateCheckboxSampleSectionExpressive() { SettingsCheckbox( modifier = Modifier.padding(start = 16.dp, end = 32.dp), state = child1State.value, + colors = colors, title = { Text(text = "Child #1") }, onCheckedChange = { child1State.value = it }, ) SettingsCheckbox( modifier = Modifier.padding(start = 16.dp, end = 32.dp), state = child2State.value, + colors = colors, title = { Text(text = "Child #2") }, onCheckedChange = { child2State.value = it }, ) SettingsCheckbox( modifier = Modifier.padding(start = 16.dp, end = 32.dp), state = child3State.value, + colors = colors, title = { Text(text = "Child #3") }, onCheckedChange = { child3State.value = it }, ) @@ -155,21 +185,27 @@ private fun SettingsTriStateCheckboxSampleSectionExpressive() { @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -private fun SettingsMenuLinkSectionSampleExpressive() { +private fun SettingsMenuLinkSectionSampleExpressive( + colors: ListItemColors, +) { SampleSection(title = "SettingsMenuLink (Expressive)") { val actionState = remember { mutableStateOf(false) } SettingsSwitch( state = actionState.value, + colors = colors, title = { Text(text = "Show action") }, onCheckedChange = { actionState.value = it }, ) SettingsMenuLink( + colors = colors, title = { Text(text = "Menu") }, onClick = { }, ) + SettingsMenuLink( + colors = colors, title = { Text(text = "Menu") }, subtitle = { Text(text = "With subtitle") }, onClick = { }, @@ -179,11 +215,14 @@ private fun SettingsMenuLinkSectionSampleExpressive() { @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -private fun SettingsButtonGroupSample() { +private fun SettingsButtonGroupSample( + colors: ListItemColors, +) { SampleSection(title = "SettingsButtonGroup (Expressive)") { val buttonGroupItems = listOf(1, 2, 3) val buttonGroupState = remember { mutableStateOf(3) } SettingsButtonGroup( + colors = colors, title = { Text(text = "Button group") }, items = buttonGroupItems, itemTitleMap = { item -> "#$item" }, @@ -196,7 +235,9 @@ private fun SettingsButtonGroupSample() { @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -private fun SettingsGroupSectionSampleExpressive() { +private fun SettingsGroupSectionSampleExpressive( + colors: ListItemColors, +) { val groupEnabled = remember { mutableStateOf(true) } SampleSection( title = "SettingsGroup (Expressive)", @@ -204,6 +245,7 @@ private fun SettingsGroupSectionSampleExpressive() { ) { SettingsSwitch( state = groupEnabled.value, + colors = colors, title = { Text(text = "Group Enabled") }, subtitle = { Text(text = "This Switch is always enabled") }, enabled = true, @@ -213,6 +255,7 @@ private fun SettingsGroupSectionSampleExpressive() { HorizontalDivider() SettingsMenuLink( + colors = colors, title = { Text(text = "Menu") }, onClick = { }, ) @@ -220,6 +263,7 @@ private fun SettingsGroupSectionSampleExpressive() { val switchState = remember { mutableStateOf(false) } SettingsSwitch( state = switchState.value, + colors = colors, title = { Text(text = "Switch") }, subtitle = { Text(text = "Switch subtitle") }, onCheckedChange = { switchState.value = it }, @@ -228,6 +272,7 @@ private fun SettingsGroupSectionSampleExpressive() { val checkboxState = remember { mutableStateOf(false) } SettingsCheckbox( state = checkboxState.value, + colors = colors, title = { Text(text = "Checkbox") }, subtitle = { Text(text = "Checkbox subtitle") }, onCheckedChange = { checkboxState.value = it }, @@ -236,6 +281,7 @@ private fun SettingsGroupSectionSampleExpressive() { val triSateCheckboxState = remember { mutableStateOf(null) } SettingsTriStateCheckbox( state = triSateCheckboxState.value, + colors = colors, title = { Text(text = "TriStateCheckbox") }, subtitle = { Text(text = "With child checkboxes") }, onCheckedChange = { newState -> triSateCheckboxState.value = newState }, @@ -244,6 +290,7 @@ private fun SettingsGroupSectionSampleExpressive() { val state = remember { mutableStateOf(false) } SettingsRadioButton( state = state.value, + colors = colors, title = { Text(text = "RadioButton") }, subtitle = { Text(text = "RadioButton subtitle") }, onClick = { state.value = !state.value }, diff --git a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenStandard.kt b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenStandard.kt index 795feb8c..da563a7a 100644 --- a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenStandard.kt +++ b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenStandard.kt @@ -9,16 +9,15 @@ import androidx.compose.foundation.shape.CutCornerShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ListItemColors +import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import com.alorma.compose.settings.sample.shared.internal.MultiChoiceAlertDialog import com.alorma.compose.settings.sample.shared.internal.SampleData import com.alorma.compose.settings.sample.shared.internal.SampleSection @@ -29,7 +28,6 @@ import com.alorma.compose.settings.ui.SettingsRadioButton import com.alorma.compose.settings.ui.SettingsSegmented import com.alorma.compose.settings.ui.SettingsSlider import com.alorma.compose.settings.ui.SettingsSwitch -import com.alorma.compose.settings.ui.SettingsTileDefaults import com.alorma.compose.settings.ui.SettingsTriStateCheckbox @Composable @@ -39,26 +37,51 @@ fun StandardMaterial3Samples() { .verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(8.dp), ) { - SettingsSwitchSampleSection() - SettingsCheckboxSampleSection() - SettingsTriStateCheckboxSampleSection() - SettingsRadioButtonSampleSection() - SettingsMenuLinkSectionSample() - SettingsSliderSectionSample() - SettingsSelectorsSample() - SettingsShapeSampleSection() - SettingsTextStylesSampleSection() - SettingsGroupSectionSample() - SettingsGroupSpacingSample() + val colors = ListItemDefaults.colors() + + SettingsSwitchSampleSection( + colors = colors, + ) + SettingsCheckboxSampleSection( + colors = colors, + ) + SettingsTriStateCheckboxSampleSection( + colors = colors, + ) + SettingsRadioButtonSampleSection( + colors = colors, + ) + SettingsMenuLinkSectionSample( + colors = colors, + ) + SettingsSliderSectionSample( + colors = colors, + ) + SettingsSelectorsSample( + colors = colors, + ) + SettingsShapeSampleSection( + colors = colors, + ) + SettingsGroupSectionSample( + colors = colors, + ) + SettingsGroupSpacingSample( + colors = colors, + ) } } @Composable -private fun SettingsSwitchSampleSection() { +private fun SettingsSwitchSampleSection( + colors: ListItemColors, +) { SampleSection(title = "SettingsSwitch") { val state = remember { mutableStateOf(false) } + SettingsSwitch( state = state.value, + colors = colors, title = { Text(text = "Switch") }, subtitle = { Text(text = "Switch subtitle") }, onCheckedChange = { state.value = it }, @@ -67,11 +90,14 @@ private fun SettingsSwitchSampleSection() { } @Composable -private fun SettingsCheckboxSampleSection() { +private fun SettingsCheckboxSampleSection( + colors: ListItemColors, +) { SampleSection(title = "SettingsCheckbox") { val state = remember { mutableStateOf(false) } SettingsCheckbox( state = state.value, + colors = colors, title = { Text(text = "Checkbox") }, subtitle = { Text(text = "Checkbox subtitle") }, onCheckedChange = { state.value = it }, @@ -80,13 +106,16 @@ private fun SettingsCheckboxSampleSection() { } @Composable -private fun SettingsRadioButtonSampleSection() { +private fun SettingsRadioButtonSampleSection( + colors: ListItemColors, +) { SampleSection(title = "SettingsRadioButton") { val state = remember { mutableStateOf(null) } SampleData.items.forEach { sampleItem -> SettingsRadioButton( state = state.value == sampleItem.key, + colors = colors, title = { Text(text = sampleItem.title) }, subtitle = { Text(text = sampleItem.description) }, onClick = { state.value = sampleItem.key }, @@ -96,7 +125,9 @@ private fun SettingsRadioButtonSampleSection() { } @Composable -private fun SettingsTriStateCheckboxSampleSection() { +private fun SettingsTriStateCheckboxSampleSection( + colors: ListItemColors, +) { SampleSection(title = "SettingsTriStateCheckbox") { val child1State = remember { mutableStateOf(false) } val child2State = remember { mutableStateOf(true) } @@ -127,6 +158,7 @@ private fun SettingsTriStateCheckboxSampleSection() { SettingsTriStateCheckbox( state = triStateWithChildState, + colors = colors, title = { Text(text = "TriStateCheckbox") }, subtitle = { Text(text = "With child checkboxes") }, onCheckedChange = { newState -> @@ -139,18 +171,21 @@ private fun SettingsTriStateCheckboxSampleSection() { SettingsCheckbox( modifier = Modifier.padding(start = 16.dp, end = 32.dp), state = child1State.value, + colors = colors, title = { Text(text = "Child #1") }, onCheckedChange = { child1State.value = it }, ) SettingsCheckbox( modifier = Modifier.padding(start = 16.dp, end = 32.dp), state = child2State.value, + colors = colors, title = { Text(text = "Child #2") }, onCheckedChange = { child2State.value = it }, ) SettingsCheckbox( modifier = Modifier.padding(start = 16.dp, end = 32.dp), state = child3State.value, + colors = colors, title = { Text(text = "Child #3") }, onCheckedChange = { child3State.value = it }, ) @@ -159,7 +194,9 @@ private fun SettingsTriStateCheckboxSampleSection() { } @Composable -private fun SettingsSliderSectionSample() { +private fun SettingsSliderSectionSample( + colors: ListItemColors, +) { SampleSection(title = "SettingsSlider") { val maxSteps = 6 val state = remember { mutableStateOf(maxSteps / 2) } @@ -176,12 +213,15 @@ private fun SettingsSliderSectionSample() { } @Composable -private fun SettingsMenuLinkSectionSample() { +private fun SettingsMenuLinkSectionSample( + colors: ListItemColors, +) { SampleSection(title = "SettingsMenuLink") { val actionState = remember { mutableStateOf(false) } SettingsSwitch( state = actionState.value, + colors = colors, title = { Text(text = "Show action") }, onCheckedChange = { actionState.value = it }, ) @@ -199,7 +239,9 @@ private fun SettingsMenuLinkSectionSample() { } @Composable -private fun SettingsSelectorsSample() { +private fun SettingsSelectorsSample( + colors: ListItemColors, +) { val items = SampleData.items SampleSection(title = "Selectors") { @@ -275,62 +317,14 @@ private fun SettingsSelectorsSample() { } @Composable -private fun SettingsTextStylesSampleSection() { - SampleSection(title = "Custom Text Styles") { - val largeTextState = remember { mutableStateOf(false) } - SettingsSwitch( - state = largeTextState.value, - title = { Text(text = "Large title text") }, - subtitle = { Text(text = "Using custom title and subtitle styles") }, - textStyles = SettingsTileDefaults.textStyles( - titleStyle = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.Bold), - subtitleStyle = MaterialTheme.typography.bodyLarge, - ), - onCheckedChange = { largeTextState.value = it }, - ) - - val smallTextState = remember { mutableStateOf(false) } - SettingsCheckbox( - state = smallTextState.value, - title = { Text(text = "Small title text") }, - subtitle = { Text(text = "Using smaller typography") }, - textStyles = SettingsTileDefaults.textStyles( - titleStyle = MaterialTheme.typography.bodySmall, - subtitleStyle = MaterialTheme.typography.labelSmall, - ), - onCheckedChange = { smallTextState.value = it }, - ) - - val customFontState = remember { mutableStateOf(null) } - SettingsRadioButton( - state = customFontState.value == "custom", - title = { Text(text = "Custom font weight") }, - subtitle = { Text(text = "Title with bold and subtitle with light weight") }, - textStyles = SettingsTileDefaults.textStyles( - titleStyle = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.ExtraBold), - subtitleStyle = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Light), - ), - onClick = { customFontState.value = "custom" }, - ) - - SettingsMenuLink( - title = { Text(text = "Menu with custom styles") }, - subtitle = { Text(text = "Both title and subtitle customized") }, - textStyles = SettingsTileDefaults.textStyles( - titleStyle = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold), - subtitleStyle = MaterialTheme.typography.bodySmall.copy(letterSpacing = 0.5.sp), - ), - onClick = { }, - ) - } -} - -@Composable -private fun SettingsShapeSampleSection() { +private fun SettingsShapeSampleSection( + colors: ListItemColors, +) { SampleSection(title = "Custom Shapes") { val roundedState = remember { mutableStateOf(false) } SettingsSwitch( state = roundedState.value, + colors = colors, title = { Text(text = "Rounded corners") }, subtitle = { Text(text = "Using RoundedCornerShape(16.dp)") }, shape = RoundedCornerShape(16.dp), @@ -340,6 +334,7 @@ private fun SettingsShapeSampleSection() { val cutCornerState = remember { mutableStateOf(false) } SettingsCheckbox( state = cutCornerState.value, + colors = colors, title = { Text(text = "Cut corners") }, subtitle = { Text(text = "Using CutCornerShape(8.dp)") }, shape = CutCornerShape(8.dp), @@ -349,6 +344,7 @@ private fun SettingsShapeSampleSection() { val circleState = remember { mutableStateOf(null) } SettingsRadioButton( state = circleState.value == "circle", + colors = colors, title = { Text(text = "Circle shape") }, subtitle = { Text(text = "Using CircleShape") }, shape = CircleShape, @@ -356,6 +352,7 @@ private fun SettingsShapeSampleSection() { ) SettingsMenuLink( + colors = colors, title = { Text(text = "Menu with rounded shape") }, subtitle = { Text(text = "Using RoundedCornerShape(24.dp)") }, shape = RoundedCornerShape(24.dp), @@ -365,6 +362,7 @@ private fun SettingsShapeSampleSection() { val sliderValue = remember { mutableStateOf(3f) } SettingsSlider( title = { Text(text = "Slider with shape") }, + colors = colors, value = sliderValue.value, onValueChange = { sliderValue.value = it }, subtitle = { Text(text = "Using RoundedCornerShape(12.dp)") }, @@ -376,7 +374,9 @@ private fun SettingsShapeSampleSection() { } @Composable -private fun SettingsGroupSectionSample() { +private fun SettingsGroupSectionSample( + colors: ListItemColors, +) { val groupEnabled = remember { mutableStateOf(true) } SampleSection( title = "SettingsGroup", @@ -384,6 +384,7 @@ private fun SettingsGroupSectionSample() { ) { SettingsSwitch( state = groupEnabled.value, + colors = colors, title = { Text(text = "Group Enabled") }, subtitle = { Text(text = "This Switch is always enabled") }, enabled = true, @@ -393,6 +394,7 @@ private fun SettingsGroupSectionSample() { HorizontalDivider() SettingsMenuLink( + colors = colors, title = { Text(text = "Menu") }, onClick = { }, ) @@ -400,6 +402,7 @@ private fun SettingsGroupSectionSample() { val switchState = remember { mutableStateOf(false) } SettingsSwitch( state = switchState.value, + colors = colors, title = { Text(text = "Switch") }, subtitle = { Text(text = "Switch subtitle") }, onCheckedChange = { switchState.value = it }, @@ -408,6 +411,7 @@ private fun SettingsGroupSectionSample() { val checkboxState = remember { mutableStateOf(false) } SettingsCheckbox( state = checkboxState.value, + colors = colors, title = { Text(text = "Checkbox") }, subtitle = { Text(text = "Checkbox subtitle") }, onCheckedChange = { checkboxState.value = it }, @@ -416,6 +420,7 @@ private fun SettingsGroupSectionSample() { val triSateCheckboxState = remember { mutableStateOf(null) } SettingsTriStateCheckbox( state = triSateCheckboxState.value, + colors = colors, title = { Text(text = "TriStateCheckbox") }, subtitle = { Text(text = "With child checkboxes") }, onCheckedChange = { newState -> triSateCheckboxState.value = newState }, @@ -424,6 +429,7 @@ private fun SettingsGroupSectionSample() { val state = remember { mutableStateOf(false) } SettingsRadioButton( state = state.value, + colors = colors, title = { Text(text = "RadioButton") }, subtitle = { Text(text = "RadioButton subtitle") }, onClick = { state.value = !state.value }, @@ -432,7 +438,9 @@ private fun SettingsGroupSectionSample() { } @Composable -private fun SettingsGroupSpacingSample() { +private fun SettingsGroupSpacingSample( + colors: ListItemColors, +) { SampleSection( title = "SettingsGroup - Custom Spacing", verticalArrangement = Arrangement.spacedBy(4.dp), @@ -440,6 +448,7 @@ private fun SettingsGroupSpacingSample() { val switch1State = remember { mutableStateOf(false) } SettingsSwitch( state = switch1State.value, + colors = colors, title = { Text(text = "Compact spacing") }, subtitle = { Text(text = "Using 4.dp spacing between items") }, onCheckedChange = { switch1State.value = it }, @@ -448,6 +457,7 @@ private fun SettingsGroupSpacingSample() { val switch2State = remember { mutableStateOf(true) } SettingsSwitch( state = switch2State.value, + colors = colors, title = { Text(text = "Another switch") }, subtitle = { Text(text = "Notice the reduced spacing") }, onCheckedChange = { switch2State.value = it }, @@ -456,6 +466,7 @@ private fun SettingsGroupSpacingSample() { val checkboxState = remember { mutableStateOf(false) } SettingsCheckbox( state = checkboxState.value, + colors = colors, title = { Text(text = "Checkbox item") }, subtitle = { Text(text = "All items have consistent 4.dp spacing") }, onCheckedChange = { checkboxState.value = it }, @@ -469,6 +480,7 @@ private fun SettingsGroupSpacingSample() { val switch1State = remember { mutableStateOf(false) } SettingsSwitch( state = switch1State.value, + colors = colors, title = { Text(text = "No spacing") }, subtitle = { Text(text = "Using Arrangement.Top for no spacing") }, onCheckedChange = { switch1State.value = it }, @@ -477,6 +489,7 @@ private fun SettingsGroupSpacingSample() { val switch2State = remember { mutableStateOf(true) } SettingsSwitch( state = switch2State.value, + colors = colors, title = { Text(text = "Tightly packed") }, subtitle = { Text(text = "Items are directly adjacent") }, onCheckedChange = { switch2State.value = it }, diff --git a/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/CompositionLocals.kt b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/CompositionLocals.kt index ba493a79..8fd9d29c 100644 --- a/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/CompositionLocals.kt +++ b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/CompositionLocals.kt @@ -1,10 +1,9 @@ package com.alorma.compose.settings.ui.core +import androidx.compose.material3.ListItemColors import androidx.compose.runtime.ProvidableCompositionLocal import androidx.compose.runtime.compositionLocalOf val LocalSettingsGroupEnabled: ProvidableCompositionLocal = compositionLocalOf { true } -val LocalSettingsTileColors: ProvidableCompositionLocal = compositionLocalOf { null } - -val LocalSettingsTextStyles: ProvidableCompositionLocal = compositionLocalOf { null } +val LocalSettingsTileColors: ProvidableCompositionLocal = compositionLocalOf { null } diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt index 4099556b..18905db8 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt @@ -28,7 +28,7 @@ fun SettingsButtonGroup( itemTitleMap: (T) -> CharSequence, modifier: Modifier = Modifier, enabled: Boolean = LocalSettingsGroupEnabled.current, - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), subtitle: @Composable (() -> Unit)? = null, icon: @Composable (() -> Unit)? = null, shapes: ListItemShapes = SettingsTileDefaults.shapes(), diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt index 1717e054..8afd205f 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt @@ -25,7 +25,7 @@ fun SettingsCheckbox( enabled: Boolean = LocalSettingsGroupEnabled.current, icon: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null, - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), checkboxColors: CheckboxColors = CheckboxDefaults.colors( checkedColor = colors.actionColor(enabled), diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt index 5a7f821c..22e45885 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt @@ -26,7 +26,7 @@ fun SettingsGroup( enabled: Boolean = true, contentPadding: PaddingValues = PaddingValues(0.dp), verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(8.dp), - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), title: @Composable (() -> Unit)? = null, semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt index 46985330..8984c7c7 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt @@ -19,7 +19,7 @@ fun SettingsMenuLink( icon: (@Composable () -> Unit)? = null, subtitle: (@Composable () -> Unit)? = null, action: (@Composable () -> Unit)? = null, - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), shapes: ListItemShapes = SettingsTileDefaults.shapes(), elevation: ListItemElevation = SettingsTileDefaults.elevation(), diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt index 7d14b765..970b9b85 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt @@ -24,7 +24,7 @@ fun SettingsRadioButton( enabled: Boolean = LocalSettingsGroupEnabled.current, icon: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null, - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), checkboxColors: RadioButtonColors = RadioButtonDefaults.colors( selectedColor = colors.actionColor(enabled), diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt index 59c27e3e..f6d0a020 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt @@ -26,7 +26,7 @@ fun SettingsTriStateCheckbox( enabled: Boolean = LocalSettingsGroupEnabled.current, icon: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null, - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), checkboxColors: CheckboxColors = CheckboxDefaults.colors( checkedColor = colors.actionColor(enabled), diff --git a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt index 75d1af40..cf1e2c8c 100644 --- a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt +++ b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt @@ -28,7 +28,7 @@ fun SettingsSegmented( itemTitleMap: (T) -> CharSequence, modifier: Modifier = Modifier, enabled: Boolean = LocalSettingsGroupEnabled.current, - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), buttonSpace: Dp = SegmentedButtonDefaults.BorderWidth, buttonShape: @Composable (Int) -> Shape = { index -> SegmentedButtonDefaults.itemShape( diff --git a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt index f2fe3a8b..2a36c925 100644 --- a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt +++ b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt @@ -26,7 +26,7 @@ fun SettingsSlider( valueRange: ClosedFloatingPointRange = 0f..1f, steps: Int = 0, onValueChangeFinished: (() -> Unit)? = null, - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), sliderColors: SliderColors = SliderDefaults.colors( thumbColor = colors.actionColor(enabled), diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt index 563cb7e5..56dcc776 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt @@ -4,7 +4,7 @@ import androidx.compose.foundation.selection.toggleable import androidx.compose.material3.Checkbox import androidx.compose.material3.CheckboxColors import androidx.compose.material3.CheckboxDefaults -import androidx.compose.material3.contentColorFor +import androidx.compose.material3.ListItemColors import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape @@ -23,14 +23,8 @@ fun SettingsCheckbox( enabled: Boolean = LocalSettingsGroupEnabled.current, icon: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null, - colors: SettingsTileColors = SettingsTileDefaults.colors(), - checkboxColors: CheckboxColors = - CheckboxDefaults.colors( - checkedColor = colors.actionColor(enabled), - checkmarkColor = contentColorFor(colors.actionColor(enabled)), - disabledCheckedColor = colors.actionColor(enabled), - ), - textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + colors: ListItemColors = SettingsTileDefaults.colors(), + checkboxColors: CheckboxColors = CheckboxDefaults.colors(), shape: Shape = SettingsTileDefaults.shape(), tonalElevation: Dp = SettingsTileDefaults.Elevation, shadowElevation: Dp = SettingsTileDefaults.Elevation, @@ -53,7 +47,6 @@ fun SettingsCheckbox( subtitle = subtitle, icon = icon, colors = colors, - textStyles = textStyles, shape = shape, tonalElevation = tonalElevation, shadowElevation = shadowElevation, diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt index 83059655..d569c243 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt @@ -24,7 +24,7 @@ fun SettingsGroup( enabled: Boolean = true, contentPadding: PaddingValues = PaddingValues(0.dp), verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(8.dp), - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), title: @Composable (() -> Unit)? = null, semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt index 94e1c2ba..cf2ae997 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt @@ -17,7 +17,7 @@ fun SettingsMenuLink( icon: (@Composable () -> Unit)? = null, subtitle: (@Composable () -> Unit)? = null, action: (@Composable () -> Unit)? = null, - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), shape: Shape = SettingsTileDefaults.shape(), tonalElevation: Dp = SettingsTileDefaults.Elevation, diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt index dcd69df6..6dbac85f 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt @@ -22,7 +22,7 @@ fun SettingsRadioButton( enabled: Boolean = LocalSettingsGroupEnabled.current, icon: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null, - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), checkboxColors: RadioButtonColors = RadioButtonDefaults.colors( selectedColor = colors.actionColor(enabled), diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt index 8d66b510..218ec9b6 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt @@ -23,7 +23,7 @@ fun SettingsSwitch( enabled: Boolean = LocalSettingsGroupEnabled.current, icon: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null, - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), switchColors: SwitchColors = SwitchDefaults.colors( checkedTrackColor = colors.actionColor(enabled), diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt index b434e49c..fffbc871 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt @@ -21,7 +21,7 @@ fun SettingsTileScaffold( enabled: Boolean = true, subtitle: @Composable (() -> Unit)? = null, icon: @Composable (() -> Unit)? = null, - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), shape: Shape = SettingsTileDefaults.shape(), tonalElevation: Dp = SettingsTileDefaults.Elevation, diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt index 83f1746d..c7e7e2d9 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt @@ -24,7 +24,7 @@ fun SettingsTriStateCheckbox( enabled: Boolean = LocalSettingsGroupEnabled.current, icon: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null, - colors: SettingsTileColors = SettingsTileDefaults.colors(), + colors: ListItemColors = SettingsTileDefaults.colors(), checkboxColors: CheckboxColors = CheckboxDefaults.colors( checkedColor = colors.actionColor(enabled), From cc21d6f326c812e1afcdf7ebedfba871c97ad08a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s?= Date: Tue, 24 Feb 2026 23:34:59 +0100 Subject: [PATCH 07/11] Update with selected state --- .../sample/shared/SettingsScreenExpressive.kt | 165 +++++++----------- .../sample/shared/internal/SampleSection.kt | 13 +- .../ui/expressive/SettingsButtonGroup.kt | 5 +- .../ui/expressive/SettingsCheckbox.kt | 60 +++---- .../settings/ui/expressive/SettingsGroup.kt | 12 +- .../ui/expressive/SettingsMenuLink.kt | 9 +- .../ui/expressive/SettingsRadioButton.kt | 58 +++--- .../settings/ui/expressive/SettingsSwitch.kt | 49 +++--- .../ui/expressive/SettingsTileScaffold.kt | 16 +- .../ui/expressive/SettingsTriStateCheckbox.kt | 80 ++++----- .../compose/settings/ui/SettingsSegmented.kt | 31 +--- .../compose/settings/ui/SettingsSlider.kt | 9 +- .../compose/settings/ui/SettingsCheckbox.kt | 1 - .../compose/settings/ui/SettingsGroup.kt | 14 +- .../compose/settings/ui/SettingsMenuLink.kt | 17 +- .../settings/ui/SettingsRadioButton.kt | 10 +- .../compose/settings/ui/SettingsSwitch.kt | 13 +- .../settings/ui/SettingsTileScaffold.kt | 88 +--------- .../settings/ui/SettingsTriStateCheckbox.kt | 11 +- 19 files changed, 217 insertions(+), 444 deletions(-) diff --git a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt index 1c4226e7..335fe339 100644 --- a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt +++ b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt @@ -2,19 +2,19 @@ package com.alorma.compose.settings.sample.shared import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.ListItemColors import androidx.compose.material3.ListItemDefaults +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.unit.dp import com.alorma.compose.settings.sample.shared.internal.SampleData import com.alorma.compose.settings.sample.shared.internal.SampleSection @@ -33,8 +33,13 @@ fun ExpressiveMaterial3Samples() { .verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(8.dp), ) { - val colors = ListItemDefaults.segmentedColors() + val colors = ListItemDefaults.segmentedColors( + containerColor = MaterialTheme.colorScheme.surfaceContainer, + ) + SettingsMenuLinkSectionSampleExpressive( + colors = colors, + ) SettingsSwitchSampleSectionExpressive( colors = colors, ) @@ -47,15 +52,9 @@ fun ExpressiveMaterial3Samples() { SettingsRadioButtonSampleSectionExpressive( colors = colors, ) - SettingsMenuLinkSectionSampleExpressive( - colors = colors, - ) SettingsButtonGroupSample( colors = colors, ) - SettingsGroupSectionSampleExpressive( - colors = colors, - ) } } @@ -64,7 +63,10 @@ fun ExpressiveMaterial3Samples() { private fun SettingsSwitchSampleSectionExpressive( colors: ListItemColors, ) { - SampleSection(title = "SettingsSwitch (Expressive)") { + SampleSection( + title = "SettingsSwitch (Expressive)", + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + ) { val state = remember { mutableStateOf(false) } SettingsSwitch( state = state.value, @@ -81,7 +83,10 @@ private fun SettingsSwitchSampleSectionExpressive( private fun SettingsCheckboxSampleSectionExpressive( colors: ListItemColors, ) { - SampleSection(title = "SettingsCheckbox (Expressive)") { + SampleSection( + title = "SettingsCheckbox (Expressive)", + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + ) { val state = remember { mutableStateOf(false) } SettingsCheckbox( state = state.value, @@ -98,7 +103,10 @@ private fun SettingsCheckboxSampleSectionExpressive( private fun SettingsRadioButtonSampleSectionExpressive( colors: ListItemColors, ) { - SampleSection(title = "SettingsRadioButton (Expressive)") { + SampleSection( + title = "SettingsRadioButton (Expressive)", + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + ) { val state = remember { mutableStateOf(null) } SampleData.items.forEach { sampleItem -> @@ -118,7 +126,10 @@ private fun SettingsRadioButtonSampleSectionExpressive( private fun SettingsTriStateCheckboxSampleSectionExpressive( colors: ListItemColors, ) { - SampleSection(title = "SettingsTriStateCheckbox (Expressive)") { + SampleSection( + title = "SettingsTriStateCheckbox (Expressive)", + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + ) { val child1State = remember { mutableStateOf(false) } val child2State = remember { mutableStateOf(true) } val child3State = remember { mutableStateOf(false) } @@ -137,14 +148,13 @@ private fun SettingsTriStateCheckboxSampleSectionExpressive( val areAllChildEnabled = allChildStates.value.all { it } val areNoneChildEnabled = allChildStates.value.none { it } - val areSomeChildEnabled = !areAllChildEnabled && !areNoneChildEnabled - - val triStateWithChildState = - if (areSomeChildEnabled) { - null - } else { - areAllChildEnabled || !areNoneChildEnabled - } + val triStateWithChildState = if (areAllChildEnabled) { + ToggleableState.On + } else if (areNoneChildEnabled) { + ToggleableState.Off + } else { + ToggleableState.Indeterminate + } SettingsTriStateCheckbox( state = triStateWithChildState, @@ -152,28 +162,27 @@ private fun SettingsTriStateCheckboxSampleSectionExpressive( title = { Text(text = "TriStateCheckbox") }, subtitle = { Text(text = "With child checkboxes") }, onCheckedChange = { newState -> - child1State.value = newState - child2State.value = newState - child3State.value = newState + child1State.value = newState == ToggleableState.On + child2State.value = newState == ToggleableState.On + child3State.value = newState == ToggleableState.On }, ) - Column { + Column( + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + ) { SettingsCheckbox( - modifier = Modifier.padding(start = 16.dp, end = 32.dp), state = child1State.value, colors = colors, title = { Text(text = "Child #1") }, onCheckedChange = { child1State.value = it }, ) SettingsCheckbox( - modifier = Modifier.padding(start = 16.dp, end = 32.dp), state = child2State.value, colors = colors, title = { Text(text = "Child #2") }, onCheckedChange = { child2State.value = it }, ) SettingsCheckbox( - modifier = Modifier.padding(start = 16.dp, end = 32.dp), state = child3State.value, colors = colors, title = { Text(text = "Child #3") }, @@ -188,25 +197,37 @@ private fun SettingsTriStateCheckboxSampleSectionExpressive( private fun SettingsMenuLinkSectionSampleExpressive( colors: ListItemColors, ) { - SampleSection(title = "SettingsMenuLink (Expressive)") { - val actionState = remember { mutableStateOf(false) } - - SettingsSwitch( - state = actionState.value, + SampleSection( + title = "SettingsMenuLink (Expressive)", + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + ) { + SettingsMenuLink( colors = colors, - title = { Text(text = "Show action") }, - onCheckedChange = { actionState.value = it }, + shapes = ListItemDefaults.segmentedShapes( + index = 0, + count = 3, + ), + title = { Text(text = "Menu 1") }, + onClick = { }, ) SettingsMenuLink( colors = colors, - title = { Text(text = "Menu") }, + shapes = ListItemDefaults.segmentedShapes( + index = 1, + count = 3, + ), + title = { Text(text = "Menu 2") }, onClick = { }, ) SettingsMenuLink( colors = colors, - title = { Text(text = "Menu") }, + shapes = ListItemDefaults.segmentedShapes( + index = 2, + count = 3, + ), + title = { Text(text = "Menu 3") }, subtitle = { Text(text = "With subtitle") }, onClick = { }, ) @@ -218,11 +239,15 @@ private fun SettingsMenuLinkSectionSampleExpressive( private fun SettingsButtonGroupSample( colors: ListItemColors, ) { - SampleSection(title = "SettingsButtonGroup (Expressive)") { + SampleSection( + title = "SettingsButtonGroup (Expressive)", + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + ) { val buttonGroupItems = listOf(1, 2, 3) val buttonGroupState = remember { mutableStateOf(3) } SettingsButtonGroup( colors = colors, + shapes = ListItemDefaults.shapes(), title = { Text(text = "Button group") }, items = buttonGroupItems, itemTitleMap = { item -> "#$item" }, @@ -233,67 +258,3 @@ private fun SettingsButtonGroupSample( } } -@OptIn(ExperimentalMaterial3ExpressiveApi::class) -@Composable -private fun SettingsGroupSectionSampleExpressive( - colors: ListItemColors, -) { - val groupEnabled = remember { mutableStateOf(true) } - SampleSection( - title = "SettingsGroup (Expressive)", - enabled = groupEnabled.value, - ) { - SettingsSwitch( - state = groupEnabled.value, - colors = colors, - title = { Text(text = "Group Enabled") }, - subtitle = { Text(text = "This Switch is always enabled") }, - enabled = true, - onCheckedChange = { groupEnabled.value = it }, - ) - - HorizontalDivider() - - SettingsMenuLink( - colors = colors, - title = { Text(text = "Menu") }, - onClick = { }, - ) - - val switchState = remember { mutableStateOf(false) } - SettingsSwitch( - state = switchState.value, - colors = colors, - title = { Text(text = "Switch") }, - subtitle = { Text(text = "Switch subtitle") }, - onCheckedChange = { switchState.value = it }, - ) - - val checkboxState = remember { mutableStateOf(false) } - SettingsCheckbox( - state = checkboxState.value, - colors = colors, - title = { Text(text = "Checkbox") }, - subtitle = { Text(text = "Checkbox subtitle") }, - onCheckedChange = { checkboxState.value = it }, - ) - - val triSateCheckboxState = remember { mutableStateOf(null) } - SettingsTriStateCheckbox( - state = triSateCheckboxState.value, - colors = colors, - title = { Text(text = "TriStateCheckbox") }, - subtitle = { Text(text = "With child checkboxes") }, - onCheckedChange = { newState -> triSateCheckboxState.value = newState }, - ) - - val state = remember { mutableStateOf(false) } - SettingsRadioButton( - state = state.value, - colors = colors, - title = { Text(text = "RadioButton") }, - subtitle = { Text(text = "RadioButton subtitle") }, - onClick = { state.value = !state.value }, - ) - } -} diff --git a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/internal/SampleSection.kt b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/internal/SampleSection.kt index a74ef58e..2b57b6f0 100644 --- a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/internal/SampleSection.kt +++ b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/internal/SampleSection.kt @@ -3,32 +3,25 @@ package com.alorma.compose.settings.sample.shared.internal import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp import com.alorma.compose.settings.ui.SettingsGroup -import com.alorma.compose.settings.ui.SettingsTileDefaults -import com.alorma.compose.settings.ui.core.LocalSettingsTileColors @Composable internal fun SampleSection( title: String, enabled: Boolean = true, + paddingValues: PaddingValues = PaddingValues(16.dp), verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(12.dp), content: @Composable ColumnScope.() -> Unit, ) { SettingsGroup( - contentPadding = PaddingValues(16.dp), + contentPadding = paddingValues, verticalArrangement = verticalArrangement, enabled = enabled, title = { Text(text = title) }, ) { - Card( - colors = CardDefaults.cardColors(containerColor = (LocalSettingsTileColors.current ?: SettingsTileDefaults.colors()).containerColor), - ) { - content() - } + content() } } diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt index 18905db8..73d6d03b 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsButtonGroup.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.ButtonGroupDefaults import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ListItemColors import androidx.compose.material3.ListItemElevation import androidx.compose.material3.ListItemShapes import androidx.compose.material3.MaterialTheme @@ -38,7 +39,7 @@ fun SettingsButtonGroup( modifier = modifier, enabled = enabled, title = title, - subtitle = { + supportingContent = { Column( verticalArrangement = Arrangement.spacedBy(4.dp), ) { @@ -62,7 +63,7 @@ fun SettingsButtonGroup( } } }, - icon = icon, + leadingContent = icon, colors = colors, shapes = shapes, elevation = elevation, diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt index 8afd205f..409201bb 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt @@ -1,16 +1,16 @@ package com.alorma.compose.settings.ui.expressive -import androidx.compose.foundation.selection.toggleable +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.Checkbox import androidx.compose.material3.CheckboxColors import androidx.compose.material3.CheckboxDefaults import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ListItemColors import androidx.compose.material3.ListItemElevation import androidx.compose.material3.ListItemShapes -import androidx.compose.material3.contentColorFor +import androidx.compose.material3.SegmentedListItem import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics @@ -26,44 +26,36 @@ fun SettingsCheckbox( icon: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null, colors: ListItemColors = SettingsTileDefaults.colors(), - checkboxColors: CheckboxColors = - CheckboxDefaults.colors( - checkedColor = colors.actionColor(enabled), - checkmarkColor = contentColorFor(colors.actionColor(enabled)), - disabledCheckedColor = colors.actionColor(enabled), - ), - textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + checkboxColors: CheckboxColors = CheckboxDefaults.colors(), shapes: ListItemShapes = SettingsTileDefaults.shapes(), elevation: ListItemElevation = SettingsTileDefaults.elevation(), semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, onCheckedChange: (Boolean) -> Unit, ) { val update: (Boolean) -> Unit = { boolean -> onCheckedChange(boolean) } - SettingsTileScaffold( - modifier = - Modifier - .toggleable( - enabled = enabled, - value = state, - role = Role.Checkbox, - onValueChange = { update(!state) }, - ).semantics(properties = semanticProperties) - .then(modifier), + + SegmentedListItem( + modifier = Modifier + .fillMaxWidth() + .semantics(properties = semanticProperties) + .then(modifier), + checked = true, + onCheckedChange = { update(!state) }, + shapes = shapes, enabled = enabled, - title = title, - subtitle = subtitle, - icon = icon, + content = title, + leadingContent = icon, + supportingContent = subtitle, colors = colors, - textStyles = textStyles, - shapes = shapes, elevation = elevation, - ) { - Checkbox( - modifier = Modifier.clearAndSetSemantics { }, - enabled = enabled, - checked = state, - onCheckedChange = update, - colors = checkboxColors, - ) - } + trailingContent = { + Checkbox( + modifier = Modifier.clearAndSetSemantics { }, + enabled = enabled, + checked = state, + onCheckedChange = update, + colors = checkboxColors, + ) + }, + ) } diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt index 22e45885..4a90ca87 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsGroup.kt @@ -8,8 +8,6 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.LocalContentColor -import androidx.compose.material3.ProvideTextStyle import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment @@ -26,8 +24,6 @@ fun SettingsGroup( enabled: Boolean = true, contentPadding: PaddingValues = PaddingValues(0.dp), verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(8.dp), - colors: ListItemColors = SettingsTileDefaults.colors(), - textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), title: @Composable (() -> Unit)? = null, semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, content: @Composable ColumnScope.() -> Unit, @@ -42,13 +38,7 @@ fun SettingsGroup( verticalArrangement = verticalArrangement, ) { if (title != null) { - CompositionLocalProvider(LocalContentColor provides colors.groupTitleColor(enabled)) { - ProvideTextStyle( - textStyles.groupTitleStyle, - ) { - SettingsGroupTitle(title) - } - } + SettingsGroupTitle(title) } CompositionLocalProvider(LocalSettingsGroupEnabled provides enabled) { content() diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt index 8984c7c7..7dbf9027 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsMenuLink.kt @@ -2,6 +2,7 @@ package com.alorma.compose.settings.ui.expressive import androidx.compose.foundation.clickable import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ListItemColors import androidx.compose.material3.ListItemElevation import androidx.compose.material3.ListItemShapes import androidx.compose.runtime.Composable @@ -20,7 +21,6 @@ fun SettingsMenuLink( subtitle: (@Composable () -> Unit)? = null, action: (@Composable () -> Unit)? = null, colors: ListItemColors = SettingsTileDefaults.colors(), - textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), shapes: ListItemShapes = SettingsTileDefaults.shapes(), elevation: ListItemElevation = SettingsTileDefaults.elevation(), semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, @@ -36,12 +36,11 @@ fun SettingsMenuLink( .then(modifier), enabled = enabled, title = title, - subtitle = subtitle, - icon = icon, + supportingContent = subtitle, + leadingContent = icon, colors = colors, - textStyles = textStyles, shapes = shapes, elevation = elevation, - action = action, + trailingContent = action, ) } diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt index 970b9b85..37116cd0 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt @@ -1,15 +1,16 @@ package com.alorma.compose.settings.ui.expressive -import androidx.compose.foundation.selection.toggleable +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ListItemColors import androidx.compose.material3.ListItemElevation import androidx.compose.material3.ListItemShapes import androidx.compose.material3.RadioButton import androidx.compose.material3.RadioButtonColors import androidx.compose.material3.RadioButtonDefaults +import androidx.compose.material3.SegmentedListItem import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics @@ -25,42 +26,35 @@ fun SettingsRadioButton( icon: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null, colors: ListItemColors = SettingsTileDefaults.colors(), - checkboxColors: RadioButtonColors = - RadioButtonDefaults.colors( - selectedColor = colors.actionColor(enabled), - disabledSelectedColor = colors.actionColor(enabled), - ), - textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + checkboxColors: RadioButtonColors = RadioButtonDefaults.colors(), shapes: ListItemShapes = SettingsTileDefaults.shapes(), elevation: ListItemElevation = SettingsTileDefaults.elevation(), semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, onClick: () -> Unit, ) { - SettingsTileScaffold( - modifier = - Modifier - .toggleable( - enabled = enabled, - value = state, - role = Role.RadioButton, - onValueChange = { onClick() }, - ).semantics(properties = semanticProperties) - .then(modifier), + + SegmentedListItem( + modifier = Modifier + .fillMaxWidth() + .semantics(properties = semanticProperties) + .then(modifier), + checked = true, + onCheckedChange = { onClick() }, + shapes = shapes, enabled = enabled, - title = title, - subtitle = subtitle, - icon = icon, + content = title, + leadingContent = icon, + supportingContent = subtitle, colors = colors, - textStyles = textStyles, - shapes = shapes, elevation = elevation, - ) { - RadioButton( - modifier = Modifier.clearAndSetSemantics { }, - enabled = enabled, - selected = state, - onClick = onClick, - colors = checkboxColors, - ) - } + trailingContent = { + RadioButton( + modifier = Modifier.clearAndSetSemantics { }, + enabled = enabled, + selected = state, + onClick = onClick, + colors = checkboxColors, + ) + }, + ) } diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt index f623c5f5..ccf72da5 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt @@ -1,17 +1,16 @@ package com.alorma.compose.settings.ui.expressive -import androidx.compose.foundation.selection.toggleable +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ListItemColors import androidx.compose.material3.ListItemElevation import androidx.compose.material3.ListItemShapes +import androidx.compose.material3.SegmentedListItem import androidx.compose.material3.Switch import androidx.compose.material3.SwitchColors import androidx.compose.material3.SwitchDefaults -import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics @@ -35,30 +34,28 @@ fun SettingsSwitch( ) { val update: (Boolean) -> Unit = { boolean -> onCheckedChange(boolean) } - SettingsTileScaffold( - modifier = - Modifier - .toggleable( - enabled = enabled, - value = state, - role = Role.Switch, - onValueChange = { update(!state) }, - ).semantics(properties = semanticProperties) - .then(modifier), + SegmentedListItem( + modifier = Modifier + .fillMaxWidth() + .semantics(properties = semanticProperties) + .then(modifier), + checked = true, + onCheckedChange = { update(!state) }, + shapes = shapes, enabled = enabled, - title = title, - subtitle = subtitle, - icon = icon, + content = title, + leadingContent = icon, + supportingContent = subtitle, colors = colors, - shapes = shapes, elevation = elevation, - ) { - Switch( - modifier = Modifier.clearAndSetSemantics { }, - enabled = enabled, - checked = state, - onCheckedChange = update, - colors = switchColors, - ) - } + trailingContent = { + Switch( + modifier = Modifier.clearAndSetSemantics { }, + enabled = enabled, + checked = state, + onCheckedChange = update, + colors = switchColors, + ) + }, + ) } diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt index eca63bee..ff607f25 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTileScaffold.kt @@ -15,26 +15,22 @@ fun SettingsTileScaffold( title: @Composable () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, - subtitle: @Composable (() -> Unit)? = null, - icon: @Composable (() -> Unit)? = null, + supportingContent: @Composable (() -> Unit)? = null, + leadingContent: @Composable (() -> Unit)? = null, colors: ListItemColors = SettingsTileDefaults.colors(), shapes: ListItemShapes = SettingsTileDefaults.shapes(), elevation: ListItemElevation = SettingsTileDefaults.elevation(), - action: @Composable (() -> Unit)? = null, + trailingContent: @Composable (() -> Unit)? = null, ) { - val decoratedSubtitle: @Composable (() -> Unit)? = subtitle?.let { { subtitle() } } - val decoratedIcon: @Composable (() -> Unit)? = icon?.let { { icon() } } - val decoratedAction: @Composable (() -> Unit)? = action?.let { { action() } } - SegmentedListItem( selected = false, onClick = {}, shapes = shapes, modifier = Modifier.fillMaxWidth().then(modifier), enabled = enabled, - leadingContent = decoratedIcon, - trailingContent = decoratedAction, - supportingContent = decoratedSubtitle, + leadingContent = leadingContent, + trailingContent = trailingContent, + supportingContent = supportingContent, colors = colors, elevation = elevation, ) { diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt index f6d0a020..fc08949d 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt @@ -1,16 +1,16 @@ package com.alorma.compose.settings.ui.expressive -import androidx.compose.foundation.selection.triStateToggleable +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.CheckboxColors import androidx.compose.material3.CheckboxDefaults import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ListItemColors import androidx.compose.material3.ListItemElevation import androidx.compose.material3.ListItemShapes +import androidx.compose.material3.SegmentedListItem import androidx.compose.material3.TriStateCheckbox -import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.SemanticsPropertyReceiver import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.semantics @@ -20,58 +20,50 @@ import com.alorma.compose.settings.ui.core.LocalSettingsGroupEnabled @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun SettingsTriStateCheckbox( - state: Boolean?, + state: ToggleableState, title: @Composable () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = LocalSettingsGroupEnabled.current, icon: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null, colors: ListItemColors = SettingsTileDefaults.colors(), - checkboxColors: CheckboxColors = - CheckboxDefaults.colors( - checkedColor = colors.actionColor(enabled), - checkmarkColor = contentColorFor(colors.actionColor(enabled)), - disabledCheckedColor = colors.actionColor(enabled), - ), - textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + checkboxColors: CheckboxColors = CheckboxDefaults.colors(), shapes: ListItemShapes = SettingsTileDefaults.shapes(), elevation: ListItemElevation = SettingsTileDefaults.elevation(), semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, - onCheckedChange: (Boolean) -> Unit = {}, + onCheckedChange: (ToggleableState) -> Unit = {}, ) { - val update: () -> Unit = { onCheckedChange(state?.not() ?: true) } - SettingsTileScaffold( - modifier = - Modifier - .triStateToggleable( - state = mapNullableBooleanToToggleableState(state), - enabled = enabled, - role = Role.Checkbox, - onClick = update, - ).semantics(properties = semanticProperties) - .then(modifier), + val update: () -> Unit = { + val newState = when (state) { + ToggleableState.On -> ToggleableState.Off + ToggleableState.Off -> ToggleableState.On + ToggleableState.Indeterminate -> ToggleableState.On + } + onCheckedChange(newState) + } + + SegmentedListItem( + modifier = Modifier + .fillMaxWidth() + .semantics(properties = semanticProperties) + .then(modifier), + checked = true, + onCheckedChange = { update() }, + shapes = shapes, enabled = enabled, - title = title, - subtitle = subtitle, - icon = icon, + content = title, + leadingContent = icon, + supportingContent = subtitle, colors = colors, - textStyles = textStyles, - shapes = shapes, elevation = elevation, - ) { - TriStateCheckbox( - modifier = Modifier.clearAndSetSemantics { }, - enabled = enabled, - state = mapNullableBooleanToToggleableState(state), - onClick = update, - colors = checkboxColors, - ) - } + trailingContent = { + TriStateCheckbox( + modifier = Modifier.clearAndSetSemantics { }, + enabled = enabled, + state = state, + onClick = update, + colors = checkboxColors, + ) + }, + ) } - -private fun mapNullableBooleanToToggleableState(state: Boolean?) = - when (state) { - true -> ToggleableState.On - false -> ToggleableState.Off - null -> ToggleableState.Indeterminate - } diff --git a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt index cf1e2c8c..a3308ca6 100644 --- a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt +++ b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSegmented.kt @@ -3,7 +3,7 @@ package com.alorma.compose.settings.ui import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ListItemColors import androidx.compose.material3.SegmentedButton import androidx.compose.material3.SegmentedButtonColors import androidx.compose.material3.SegmentedButtonDefaults @@ -12,7 +12,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -36,32 +35,7 @@ fun SettingsSegmented( count = items.size, ) }, - buttonColors: SegmentedButtonColors = - SegmentedButtonDefaults.colors( - activeContainerColor = - colors - .actionColor(enabled) - .copy(alpha = 0.4f) - .compositeOver(MaterialTheme.colorScheme.surfaceContainerLowest), - inactiveContainerColor = colors.containerColor, - activeContentColor = colors.subtitleColor(enabled), - inactiveContentColor = colors.subtitleColor(enabled), - activeBorderColor = colors.subtitleColor, - inactiveBorderColor = colors.subtitleColor, - disabledActiveContainerColor = - colors - .actionColor(enabled) - .copy(alpha = 0.12f) - .compositeOver(MaterialTheme.colorScheme.surfaceContainerLowest), - disabledInactiveContainerColor = - MaterialTheme.colorScheme.surface - .copy(alpha = SettingsTileDefaults.DisabledAlpha) - .compositeOver(colors.containerColor), - disabledActiveContentColor = colors.subtitleColor(enabled), - disabledInactiveContentColor = colors.subtitleColor(enabled), - disabledActiveBorderColor = colors.subtitleColor(enabled), - disabledInactiveBorderColor = colors.subtitleColor(enabled), - ), + buttonColors: SegmentedButtonColors = SegmentedButtonDefaults.colors(), buttonIcon: @Composable (Boolean) -> Unit = { selected -> SegmentedButtonDefaults.Icon(selected) }, subtitle: @Composable (() -> Unit)? = null, icon: @Composable (() -> Unit)? = null, @@ -71,7 +45,6 @@ fun SettingsSegmented( ) { SettingsTileScaffold( modifier = modifier, - enabled = enabled, title = title, subtitle = { Column( diff --git a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt index 2a36c925..054c7289 100644 --- a/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt +++ b/ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSlider.kt @@ -3,6 +3,7 @@ package com.alorma.compose.settings.ui import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ListItemColors import androidx.compose.material3.Slider import androidx.compose.material3.SliderColors import androidx.compose.material3.SliderDefaults @@ -27,19 +28,13 @@ fun SettingsSlider( steps: Int = 0, onValueChangeFinished: (() -> Unit)? = null, colors: ListItemColors = SettingsTileDefaults.colors(), - sliderColors: SliderColors = - SliderDefaults.colors( - thumbColor = colors.actionColor(enabled), - activeTrackColor = colors.actionColor(enabled), - inactiveTrackColor = colors.actionColor(enabled).copy(alpha = 0.12f), - ), + sliderColors: SliderColors = SliderDefaults.colors(), shape: Shape = SettingsTileDefaults.shape(), tonalElevation: Dp = SettingsTileDefaults.Elevation, shadowElevation: Dp = SettingsTileDefaults.Elevation, ) { SettingsTileScaffold( modifier = modifier, - enabled = enabled, title = title, subtitle = { Column( diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt index 56dcc776..2359ae8b 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsCheckbox.kt @@ -42,7 +42,6 @@ fun SettingsCheckbox( onValueChange = { update(!state) }, ).semantics(properties = semanticProperties) .then(modifier), - enabled = enabled, title = title, subtitle = subtitle, icon = icon, diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt index d569c243..5698bc95 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsGroup.kt @@ -7,8 +7,8 @@ import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ListItemColors import androidx.compose.material3.LocalContentColor -import androidx.compose.material3.ProvideTextStyle import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment @@ -25,14 +25,12 @@ fun SettingsGroup( contentPadding: PaddingValues = PaddingValues(0.dp), verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(8.dp), colors: ListItemColors = SettingsTileDefaults.colors(), - textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), title: @Composable (() -> Unit)? = null, semanticProperties: (SemanticsPropertyReceiver.() -> Unit) = {}, content: @Composable ColumnScope.() -> Unit, ) { Column( - modifier = - Modifier + modifier = Modifier .fillMaxWidth() .semantics(properties = semanticProperties) .then(modifier) @@ -40,12 +38,8 @@ fun SettingsGroup( verticalArrangement = verticalArrangement, ) { if (title != null) { - CompositionLocalProvider(LocalContentColor provides colors.groupTitleColor(enabled)) { - ProvideTextStyle( - textStyles.groupTitleStyle, - ) { - SettingsGroupTitle(title) - } + CompositionLocalProvider(LocalContentColor provides colors.headlineColor) { + SettingsGroupTitle(title) } } CompositionLocalProvider(LocalSettingsGroupEnabled provides enabled) { diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt index cf2ae997..79f35bb3 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsMenuLink.kt @@ -1,6 +1,7 @@ package com.alorma.compose.settings.ui import androidx.compose.foundation.clickable +import androidx.compose.material3.ListItemColors import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape @@ -18,7 +19,6 @@ fun SettingsMenuLink( subtitle: (@Composable () -> Unit)? = null, action: (@Composable () -> Unit)? = null, colors: ListItemColors = SettingsTileDefaults.colors(), - textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), shape: Shape = SettingsTileDefaults.shape(), tonalElevation: Dp = SettingsTileDefaults.Elevation, shadowElevation: Dp = SettingsTileDefaults.Elevation, @@ -26,19 +26,16 @@ fun SettingsMenuLink( onClick: () -> Unit, ) { SettingsTileScaffold( - modifier = - Modifier - .clickable( - enabled = enabled, - onClick = onClick, - ).semantics(properties = semanticProperties) - .then(modifier), - enabled = enabled, + modifier = Modifier + .clickable( + enabled = enabled, + onClick = onClick, + ).semantics(properties = semanticProperties) + .then(modifier), title = title, subtitle = subtitle, icon = icon, colors = colors, - textStyles = textStyles, shape = shape, tonalElevation = tonalElevation, shadowElevation = shadowElevation, diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt index 6dbac85f..11389866 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsRadioButton.kt @@ -1,6 +1,7 @@ package com.alorma.compose.settings.ui import androidx.compose.foundation.selection.toggleable +import androidx.compose.material3.ListItemColors import androidx.compose.material3.RadioButton import androidx.compose.material3.RadioButtonColors import androidx.compose.material3.RadioButtonDefaults @@ -23,12 +24,7 @@ fun SettingsRadioButton( icon: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null, colors: ListItemColors = SettingsTileDefaults.colors(), - checkboxColors: RadioButtonColors = - RadioButtonDefaults.colors( - selectedColor = colors.actionColor(enabled), - disabledSelectedColor = colors.actionColor(enabled), - ), - textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + checkboxColors: RadioButtonColors = RadioButtonDefaults.colors(), shape: Shape = SettingsTileDefaults.shape(), tonalElevation: Dp = SettingsTileDefaults.Elevation, shadowElevation: Dp = SettingsTileDefaults.Elevation, @@ -45,12 +41,10 @@ fun SettingsRadioButton( onValueChange = { onClick() }, ).semantics(properties = semanticProperties) .then(modifier), - enabled = enabled, title = title, subtitle = subtitle, icon = icon, colors = colors, - textStyles = textStyles, shape = shape, tonalElevation = tonalElevation, shadowElevation = shadowElevation, diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt index 218ec9b6..0fa39cf9 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsSwitch.kt @@ -1,10 +1,10 @@ package com.alorma.compose.settings.ui import androidx.compose.foundation.selection.toggleable +import androidx.compose.material3.ListItemColors import androidx.compose.material3.Switch import androidx.compose.material3.SwitchColors import androidx.compose.material3.SwitchDefaults -import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape @@ -24,14 +24,7 @@ fun SettingsSwitch( icon: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null, colors: ListItemColors = SettingsTileDefaults.colors(), - switchColors: SwitchColors = - SwitchDefaults.colors( - checkedTrackColor = colors.actionColor(enabled), - checkedThumbColor = contentColorFor(colors.actionColor(enabled)), - disabledCheckedTrackColor = colors.actionColor(enabled), - disabledCheckedThumbColor = contentColorFor(colors.actionColor(enabled)), - ), - textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + switchColors: SwitchColors = SwitchDefaults.colors(), shape: Shape = SettingsTileDefaults.shape(), tonalElevation: Dp = SettingsTileDefaults.Elevation, shadowElevation: Dp = SettingsTileDefaults.Elevation, @@ -50,12 +43,10 @@ fun SettingsSwitch( onValueChange = { update(!state) }, ).semantics(properties = semanticProperties) .then(modifier), - enabled = enabled, title = title, subtitle = subtitle, icon = icon, colors = colors, - textStyles = textStyles, shape = shape, tonalElevation = tonalElevation, shadowElevation = shadowElevation, diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt index fffbc871..9694f59f 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTileScaffold.kt @@ -3,110 +3,32 @@ package com.alorma.compose.settings.ui import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.ListItem import androidx.compose.material3.ListItemColors -import androidx.compose.material3.LocalContentColor -import androidx.compose.material3.LocalTextStyle import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Dp @Composable fun SettingsTileScaffold( title: @Composable () -> Unit, modifier: Modifier = Modifier, - enabled: Boolean = true, subtitle: @Composable (() -> Unit)? = null, icon: @Composable (() -> Unit)? = null, colors: ListItemColors = SettingsTileDefaults.colors(), - textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), shape: Shape = SettingsTileDefaults.shape(), tonalElevation: Dp = SettingsTileDefaults.Elevation, shadowElevation: Dp = SettingsTileDefaults.Elevation, action: @Composable (() -> Unit)? = null, ) { - val decoratedTitle: @Composable () -> Unit = { - ProvideContentColorAndTextStyle( - contentColor = colors.titleColor(enabled), - textStyle = textStyles.titleStyle, - ) { - title() - } - } - val decoratedSubtitle: @Composable (() -> Unit)? = - subtitle?.let { - { - ProvideContentColorAndTextStyle( - contentColor = colors.subtitleColor(enabled), - textStyle = textStyles.subtitleStyle, - ) { - subtitle() - } - } - } - val decoratedIcon: @Composable (() -> Unit)? = - icon?.let { - { - ProvideContentColor(colors.iconColor(enabled)) { - icon() - } - } - } - val decoratedAction: @Composable (() -> Unit)? = - action?.let { - { - ProvideContentColor(colors.actionColor(enabled)) { - action() - } - } - } - ListItem( modifier = Modifier.fillMaxWidth().clip(shape).then(modifier), - headlineContent = decoratedTitle, - supportingContent = decoratedSubtitle, - leadingContent = decoratedIcon, - trailingContent = decoratedAction, - colors = - ListItemColors( - containerColor = colors.containerColor, - headlineColor = colors.titleColor, - leadingIconColor = colors.iconColor, - overlineColor = colors.actionColor, - supportingTextColor = colors.subtitleColor, - trailingIconColor = colors.actionColor, - disabledHeadlineColor = colors.disabledTitleColor, - disabledLeadingIconColor = colors.disabledIconColor, - disabledTrailingIconColor = colors.disabledActionColor, - ), + headlineContent = title, + supportingContent = subtitle, + leadingContent = icon, + trailingContent = action, + colors = colors, tonalElevation = tonalElevation, shadowElevation = shadowElevation, ) } - -@Composable -private fun ProvideContentColor( - contentColor: Color, - content: @Composable () -> Unit, -) { - CompositionLocalProvider(LocalContentColor provides contentColor) { - content() - } -} - -@Composable -private fun ProvideContentColorAndTextStyle( - contentColor: Color, - textStyle: TextStyle, - content: @Composable () -> Unit, -) { - CompositionLocalProvider( - LocalContentColor provides contentColor, - LocalTextStyle provides textStyle, - ) { - content() - } -} diff --git a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt index c7e7e2d9..b06aa31e 100644 --- a/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt +++ b/ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/SettingsTriStateCheckbox.kt @@ -3,6 +3,7 @@ package com.alorma.compose.settings.ui import androidx.compose.foundation.selection.triStateToggleable import androidx.compose.material3.CheckboxColors import androidx.compose.material3.CheckboxDefaults +import androidx.compose.material3.ListItemColors import androidx.compose.material3.TriStateCheckbox import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable @@ -25,13 +26,7 @@ fun SettingsTriStateCheckbox( icon: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null, colors: ListItemColors = SettingsTileDefaults.colors(), - checkboxColors: CheckboxColors = - CheckboxDefaults.colors( - checkedColor = colors.actionColor(enabled), - checkmarkColor = contentColorFor(colors.actionColor(enabled)), - disabledCheckedColor = colors.actionColor(enabled), - ), - textStyles: SettingsTextStyles = SettingsTileDefaults.textStyles(), + checkboxColors: CheckboxColors = CheckboxDefaults.colors(), shape: Shape = SettingsTileDefaults.shape(), tonalElevation: Dp = SettingsTileDefaults.Elevation, shadowElevation: Dp = SettingsTileDefaults.Elevation, @@ -49,12 +44,10 @@ fun SettingsTriStateCheckbox( onClick = update, ).semantics(properties = semanticProperties) .then(modifier), - enabled = enabled, title = title, subtitle = subtitle, icon = icon, colors = colors, - textStyles = textStyles, shape = shape, tonalElevation = tonalElevation, shadowElevation = shadowElevation, From 76362057f0b62f282fdbfba0af9296d3c3966965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s?= Date: Wed, 4 Mar 2026 18:39:33 +0100 Subject: [PATCH 08/11] ALign dependencies --- .../src/main/kotlin/ComposeSampleConventionPlugin.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build-logic/convention/src/main/kotlin/ComposeSampleConventionPlugin.kt b/build-logic/convention/src/main/kotlin/ComposeSampleConventionPlugin.kt index e8ebd850..9d7bd22f 100644 --- a/build-logic/convention/src/main/kotlin/ComposeSampleConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/ComposeSampleConventionPlugin.kt @@ -72,7 +72,7 @@ class ComposeSampleConventionPlugin : Plugin { val androidMain by getting { dependencies { - implementation(compose.dependencies.uiTooling) + implementation(libs.findLibrary("compose-ui-tooling").get()) } } @@ -101,7 +101,6 @@ class ComposeSampleConventionPlugin : Plugin { val desktopMain by getting { dependencies { api(compose.dependencies.desktop.currentOs) - implementation(compose.dependencies.desktop.common) } } } From fc38d491a5b6466c9e4541bec5df22ef302db951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s?= Date: Wed, 4 Mar 2026 18:39:59 +0100 Subject: [PATCH 09/11] Fix deprecated androidLibrary --- .../src/main/kotlin/ComposeLibraryConventionPlugin.kt | 2 +- ui-core/build.gradle.kts | 2 +- ui-tiles-expressive/build.gradle.kts | 2 +- ui-tiles-extended/build.gradle.kts | 2 +- ui-tiles/build.gradle.kts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build-logic/convention/src/main/kotlin/ComposeLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/ComposeLibraryConventionPlugin.kt index 720b2541..7a8adf92 100644 --- a/build-logic/convention/src/main/kotlin/ComposeLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/ComposeLibraryConventionPlugin.kt @@ -109,7 +109,7 @@ class ComposeLibraryConventionPlugin : Plugin { |Each library module must set a unique namespace in its build.gradle.kts: | | kotlin { - | androidLibrary { + | android { | namespace = libs.versions.namespace.get() + ".your.unique.suffix" | } | } diff --git a/ui-core/build.gradle.kts b/ui-core/build.gradle.kts index a681a8e9..29ed85b9 100644 --- a/ui-core/build.gradle.kts +++ b/ui-core/build.gradle.kts @@ -3,7 +3,7 @@ plugins { } kotlin { - androidLibrary { + android { namespace = libs.versions.namespace.get() + ".ui.core" } diff --git a/ui-tiles-expressive/build.gradle.kts b/ui-tiles-expressive/build.gradle.kts index 79e80842..dda12eb4 100644 --- a/ui-tiles-expressive/build.gradle.kts +++ b/ui-tiles-expressive/build.gradle.kts @@ -3,7 +3,7 @@ plugins { } kotlin { - androidLibrary { + android { namespace = libs.versions.namespace.get() + ".ui.expressive" } diff --git a/ui-tiles-extended/build.gradle.kts b/ui-tiles-extended/build.gradle.kts index 284c377d..4d195910 100644 --- a/ui-tiles-extended/build.gradle.kts +++ b/ui-tiles-extended/build.gradle.kts @@ -3,7 +3,7 @@ plugins { } kotlin { - androidLibrary { + android { namespace = libs.versions.namespace.get() + ".ui.extended" } diff --git a/ui-tiles/build.gradle.kts b/ui-tiles/build.gradle.kts index 2658933b..c3458586 100644 --- a/ui-tiles/build.gradle.kts +++ b/ui-tiles/build.gradle.kts @@ -3,7 +3,7 @@ plugins { } kotlin { - androidLibrary { + android { namespace = libs.versions.namespace.get() + ".ui.tiles" } From 1871525cbd3ce7d4bd28768549e3de08e08f786e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s?= Date: Wed, 4 Mar 2026 19:08:27 +0100 Subject: [PATCH 10/11] Update shapes samples, and fix checked state --- .../settings/sample/shared/SettingsScreen.kt | 42 ++++--- .../sample/shared/SettingsScreenExpressive.kt | 108 ++++++++++-------- .../sample/shared/SettingsScreenStandard.kt | 3 +- .../ui/core/SettingsTileCoreDefaults.kt | 1 - .../ui/expressive/SettingsCheckbox.kt | 2 +- .../ui/expressive/SettingsRadioButton.kt | 2 +- .../settings/ui/expressive/SettingsSwitch.kt | 2 +- .../ui/expressive/SettingsTriStateCheckbox.kt | 6 +- 8 files changed, 98 insertions(+), 68 deletions(-) diff --git a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt index 94f9354f..90085e5c 100644 --- a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt +++ b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreen.kt @@ -1,14 +1,14 @@ package com.alorma.compose.settings.sample.shared -import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState import androidx.compose.material3.AssistChip import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FilterChip +import androidx.compose.material3.PrimaryTabRow import androidx.compose.material3.Scaffold +import androidx.compose.material3.Tab import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable @@ -25,8 +25,6 @@ enum class Material3Mode { @OptIn(ExperimentalMaterial3Api::class) @Composable fun SettingsScreen(modifier: Modifier = Modifier) { - val materialMode = remember { mutableStateOf(Material3Mode.STANDARD) } - Scaffold( modifier = modifier, topBar = { @@ -34,17 +32,6 @@ fun SettingsScreen(modifier: Modifier = Modifier) { title = { Text(text = "Compose Settings") }, actions = { Row(modifier = Modifier.padding(horizontal = 16.dp)) { - FilterChip( - selected = materialMode.value == Material3Mode.STANDARD, - onClick = { materialMode.value = Material3Mode.STANDARD }, - label = { Text("M3") }, - ) - FilterChip( - selected = materialMode.value == Material3Mode.EXPRESSIVE, - onClick = { materialMode.value = Material3Mode.EXPRESSIVE }, - label = { Text("M3 Expressive") }, - modifier = Modifier.padding(start = 8.dp), - ) AssistChip( modifier = Modifier.padding(start = 8.dp), label = { Text(text = Version.LIB_VERSION) }, @@ -55,11 +42,32 @@ fun SettingsScreen(modifier: Modifier = Modifier) { ) }, ) { padding -> - Box( + Column( modifier = Modifier .consumeWindowInsets(padding) .padding(top = padding.calculateTopPadding()), ) { + val materialMode = remember { + mutableStateOf(Material3Mode.EXPRESSIVE) + } + + PrimaryTabRow( + selectedTabIndex = Material3Mode.entries.indexOf(materialMode.value), + divider = {}, + tabs = { + Tab( + selected = materialMode.value == Material3Mode.STANDARD, + text = { Text(text = "Standard") }, + onClick = { materialMode.value = Material3Mode.STANDARD }, + ) + Tab( + selected = materialMode.value == Material3Mode.EXPRESSIVE, + text = { Text(text = "Expressive") }, + onClick = { materialMode.value = Material3Mode.EXPRESSIVE }, + ) + }, + ) + when (materialMode.value) { Material3Mode.STANDARD -> StandardMaterial3Samples() Material3Mode.EXPRESSIVE -> ExpressiveMaterial3Samples() diff --git a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt index 335fe339..6db2382e 100644 --- a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt +++ b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenExpressive.kt @@ -2,11 +2,13 @@ package com.alorma.compose.settings.sample.shared import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ListItemColors import androidx.compose.material3.ListItemDefaults +import androidx.compose.material3.ListItemShapes import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -30,7 +32,8 @@ import com.alorma.compose.settings.ui.expressive.SettingsTriStateCheckbox fun ExpressiveMaterial3Samples() { Column( modifier = Modifier - .verticalScroll(rememberScrollState()), + .verticalScroll(rememberScrollState()) + .padding(bottom = 90.dp), verticalArrangement = Arrangement.spacedBy(8.dp), ) { val colors = ListItemDefaults.segmentedColors( @@ -58,6 +61,39 @@ fun ExpressiveMaterial3Samples() { } } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +private fun SettingsMenuLinkSectionSampleExpressive( + colors: ListItemColors, +) { + SampleSection( + title = "SettingsMenuLink (Expressive)", + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + ) { + SettingsMenuLink( + colors = colors, + shapes = ListItemDefaults.segmentedShapes(0, 3,), + title = { Text(text = "Menu 1") }, + onClick = { }, + ) + + SettingsMenuLink( + colors = colors, + shapes = ListItemDefaults.segmentedShapes(1, 3,), + title = { Text(text = "Menu 2") }, + onClick = { }, + ) + + SettingsMenuLink( + colors = colors, + shapes = ListItemDefaults.segmentedShapes(2, 3,), + title = { Text(text = "Menu 3") }, + subtitle = { Text(text = "With subtitle") }, + onClick = { }, + ) + } +} + @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable private fun SettingsSwitchSampleSectionExpressive( @@ -71,10 +107,20 @@ private fun SettingsSwitchSampleSectionExpressive( SettingsSwitch( state = state.value, colors = colors, + shapes = ListItemDefaults.segmentedShapes(0, 3), title = { Text(text = "Switch") }, subtitle = { Text(text = "Switch subtitle") }, onCheckedChange = { state.value = it }, ) + + SettingsSwitch( + state = !state.value, + colors = colors, + shapes = ListItemDefaults.segmentedShapes(1, 3), + title = { Text(text = "Switch") }, + subtitle = { Text(text = "Switch subtitle") }, + onCheckedChange = { state.value = !it }, + ) } } @@ -91,10 +137,19 @@ private fun SettingsCheckboxSampleSectionExpressive( SettingsCheckbox( state = state.value, colors = colors, + shapes = ListItemDefaults.segmentedShapes(0, 2), title = { Text(text = "Checkbox") }, subtitle = { Text(text = "Checkbox subtitle") }, onCheckedChange = { state.value = it }, ) + SettingsCheckbox( + state = !state.value, + colors = colors, + shapes = ListItemDefaults.segmentedShapes(1, 2), + title = { Text(text = "Checkbox") }, + subtitle = { Text(text = "Checkbox subtitle") }, + onCheckedChange = { state.value = !it }, + ) } } @@ -109,10 +164,11 @@ private fun SettingsRadioButtonSampleSectionExpressive( ) { val state = remember { mutableStateOf(null) } - SampleData.items.forEach { sampleItem -> + SampleData.items.forEachIndexed { index, sampleItem -> SettingsRadioButton( state = state.value == sampleItem.key, colors = colors, + shapes = ListItemDefaults.segmentedShapes(index, SampleData.items.size), title = { Text(text = sampleItem.title) }, subtitle = { Text(text = sampleItem.description) }, onClick = { state.value = sampleItem.key }, @@ -159,6 +215,7 @@ private fun SettingsTriStateCheckboxSampleSectionExpressive( SettingsTriStateCheckbox( state = triStateWithChildState, colors = colors, + shapes = ListItemDefaults.segmentedShapes(0, 4), title = { Text(text = "TriStateCheckbox") }, subtitle = { Text(text = "With child checkboxes") }, onCheckedChange = { newState -> @@ -173,18 +230,21 @@ private fun SettingsTriStateCheckboxSampleSectionExpressive( SettingsCheckbox( state = child1State.value, colors = colors, + shapes = ListItemDefaults.segmentedShapes(1, 4), title = { Text(text = "Child #1") }, onCheckedChange = { child1State.value = it }, ) SettingsCheckbox( state = child2State.value, colors = colors, + shapes = ListItemDefaults.segmentedShapes(2, 4), title = { Text(text = "Child #2") }, onCheckedChange = { child2State.value = it }, ) SettingsCheckbox( state = child3State.value, colors = colors, + shapes = ListItemDefaults.segmentedShapes(3, 4), title = { Text(text = "Child #3") }, onCheckedChange = { child3State.value = it }, ) @@ -192,48 +252,6 @@ private fun SettingsTriStateCheckboxSampleSectionExpressive( } } -@OptIn(ExperimentalMaterial3ExpressiveApi::class) -@Composable -private fun SettingsMenuLinkSectionSampleExpressive( - colors: ListItemColors, -) { - SampleSection( - title = "SettingsMenuLink (Expressive)", - verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), - ) { - SettingsMenuLink( - colors = colors, - shapes = ListItemDefaults.segmentedShapes( - index = 0, - count = 3, - ), - title = { Text(text = "Menu 1") }, - onClick = { }, - ) - - SettingsMenuLink( - colors = colors, - shapes = ListItemDefaults.segmentedShapes( - index = 1, - count = 3, - ), - title = { Text(text = "Menu 2") }, - onClick = { }, - ) - - SettingsMenuLink( - colors = colors, - shapes = ListItemDefaults.segmentedShapes( - index = 2, - count = 3, - ), - title = { Text(text = "Menu 3") }, - subtitle = { Text(text = "With subtitle") }, - onClick = { }, - ) - } -} - @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable private fun SettingsButtonGroupSample( @@ -247,7 +265,7 @@ private fun SettingsButtonGroupSample( val buttonGroupState = remember { mutableStateOf(3) } SettingsButtonGroup( colors = colors, - shapes = ListItemDefaults.shapes(), + shapes = ListItemDefaults.segmentedShapes(0, 1), title = { Text(text = "Button group") }, items = buttonGroupItems, itemTitleMap = { item -> "#$item" }, diff --git a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenStandard.kt b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenStandard.kt index da563a7a..2a28f8f8 100644 --- a/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenStandard.kt +++ b/samples/shared/src/commonMain/kotlin/com/alorma/compose/settings/sample/shared/SettingsScreenStandard.kt @@ -34,7 +34,8 @@ import com.alorma.compose.settings.ui.SettingsTriStateCheckbox fun StandardMaterial3Samples() { Column( modifier = Modifier - .verticalScroll(rememberScrollState()), + .verticalScroll(rememberScrollState()) + .padding(bottom = 90.dp), verticalArrangement = Arrangement.spacedBy(8.dp), ) { val colors = ListItemDefaults.colors() diff --git a/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt index 17524b72..6a5bfe57 100644 --- a/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt +++ b/ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/SettingsTileCoreDefaults.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.unit.Dp abstract class SettingsTileCoreDefaults { abstract val Elevation: Dp - val DisabledAlpha: Float = 0.38f @Composable abstract fun colors(): ListItemColors diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt index 409201bb..99cc1bdb 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsCheckbox.kt @@ -39,7 +39,7 @@ fun SettingsCheckbox( .fillMaxWidth() .semantics(properties = semanticProperties) .then(modifier), - checked = true, + checked = state, onCheckedChange = { update(!state) }, shapes = shapes, enabled = enabled, diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt index 37116cd0..93231b89 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsRadioButton.kt @@ -38,7 +38,7 @@ fun SettingsRadioButton( .fillMaxWidth() .semantics(properties = semanticProperties) .then(modifier), - checked = true, + checked = state, onCheckedChange = { onClick() }, shapes = shapes, enabled = enabled, diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt index ccf72da5..99b3dfbe 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsSwitch.kt @@ -39,7 +39,7 @@ fun SettingsSwitch( .fillMaxWidth() .semantics(properties = semanticProperties) .then(modifier), - checked = true, + checked = state, onCheckedChange = { update(!state) }, shapes = shapes, enabled = enabled, diff --git a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt index fc08949d..9143011c 100644 --- a/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt +++ b/ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/SettingsTriStateCheckbox.kt @@ -47,7 +47,11 @@ fun SettingsTriStateCheckbox( .fillMaxWidth() .semantics(properties = semanticProperties) .then(modifier), - checked = true, + checked = when(state) { + ToggleableState.On -> true + ToggleableState.Indeterminate -> true + ToggleableState.Off -> false + }, onCheckedChange = { update() }, shapes = shapes, enabled = enabled, From 833740af77e705755f05dcdf908ac8ab0aa480ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s?= Date: Wed, 4 Mar 2026 19:25:02 +0100 Subject: [PATCH 11/11] Update docs --- ARCHITECTURE.md | 31 +++++++ CLAUDE.md | 29 ++++--- README.md | 209 +++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 247 insertions(+), 22 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 6eac8d71..f410b193 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -82,6 +82,7 @@ ui-tiles-expressive┘ **Components**: - `SettingsSlider` - Numeric value selection with slider +- `SettingsSegmented` - Single-choice selection using Material 3 `SingleChoiceSegmentedButtonRow` **Artifact**: `com.github.alorma.compose-settings:ui-tiles-extended` @@ -90,6 +91,36 @@ ui-tiles-expressive┘ - Advanced components may have different Material 3 requirements - Users can opt-in to extended features as needed +#### ui-tiles-expressive + +**Purpose**: Expressive Material 3 variants of all standard tiles, plus expressive-only components + +**Location**: `ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/` + +**Components**: +- `SettingsMenuLink` - Expressive variant built on `SegmentedListItem` +- `SettingsCheckbox` - Expressive variant built on `SegmentedListItem` +- `SettingsTriStateCheckbox` - Expressive variant built on `SegmentedListItem` +- `SettingsRadioButton` - Expressive variant built on `SegmentedListItem` +- `SettingsSwitch` - Expressive variant built on `SegmentedListItem` +- `SettingsGroup` - Expressive variant (same API as M3 version) +- `SettingsButtonGroup` - Button group using `OutlinedToggleButton` (expressive-only) + +All components expose a `shapes: ListItemShapes` parameter (instead of `shape: Shape`) enabling +the connected/grouped visual treatment via `ListItemDefaults.segmentedShapes(index, count)`. + +**Requires**: `@OptIn(ExperimentalMaterial3ExpressiveApi::class)` + +**Artifact**: `com.github.alorma.compose-settings:ui-tiles-expressive` + +**Why mirror the standard tiles?** +- Consumers can build a full settings screen using only the expressive module, without + also depending on `ui-tiles` +- The `shapes` parameter allows precise control of corner rounding so groups of items + connect visually as a single segmented unit +- `SegmentedListItem` provides the interactive checked state and segmented appearance + out of the box, which standard `ListItem` does not + ## Design Patterns ### Scaffold Pattern diff --git a/CLAUDE.md b/CLAUDE.md index e6cabbd7..1851f842 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -37,25 +37,36 @@ The project is organized into four main library modules: - Components: `SettingsSlider`, `SettingsSegmented` - Published as: `com.github.alorma.compose-settings:ui-tiles-extended` -4. **ui-tiles-expressive** - Expressive Material 3 components +4. **ui-tiles-expressive** - Expressive Material 3 components (full tile set + expressive-only) - Location: `ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/` - Depends on: `ui-core`, Material 3 Expressive - - Components: `SettingsButtonGroup` + - Components: `SettingsMenuLink`, `SettingsCheckbox`, `SettingsTriStateCheckbox`, + `SettingsRadioButton`, `SettingsSwitch`, `SettingsGroup`, `SettingsButtonGroup` + - All components (except `SettingsGroup` and `SettingsButtonGroup`) are built on + `SegmentedListItem` and accept a `shapes: ListItemShapes` parameter instead of `shape: Shape` + - Requires `@OptIn(ExperimentalMaterial3ExpressiveApi::class)` - Published as: `com.github.alorma.compose-settings:ui-tiles-expressive` ### Design Patterns -All settings components follow a consistent pattern: +Settings components follow one of two patterns depending on the module: +**Standard M3 pattern** (`ui-tiles`, `ui-tiles-extended`): 1. Built on top of `SettingsTileScaffold` (from `ui-core`) -2. Accept common parameters: `title`, `subtitle`, `icon`, `modifier`, `enabled`, `colors` -3. Use Material 3 components internally (ListItem, Checkbox, Switch, etc.) -4. Support `LocalSettingsGroupEnabled` for hierarchical enabled state -5. Leverage `SettingsTileColors` and `SettingsTextStyles` (from `ui-core`) for consistent theming - across all components +2. Accept common parameters: `title`, `subtitle`, `icon`, `modifier`, `enabled`, `colors`, `shape` +3. Use Material 3 `ListItem` internally + +**Expressive M3 pattern** (`ui-tiles-expressive`): +1. Built directly on Material 3 Expressive `SegmentedListItem` +2. Accept `shapes: ListItemShapes` instead of `shape: Shape`, enabling segmented list styling +3. Use `ListItemDefaults.segmentedShapes(index, count)` to connect items into groups + +Both patterns: +- Support `LocalSettingsGroupEnabled` for hierarchical enabled state +- Leverage `SettingsTileColors` (from `ui-core`) for consistent theming The `SettingsTileScaffold` wraps Material 3's `ListItem` and provides consistent layout and color -handling. +handling (standard modules only). ### Architecture Layers diff --git a/README.md b/README.md index 6a1039d2..dad668be 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,10 @@ This library provides a set of **Settings** like composable items to help android *Jetpack Compose* developers build complex settings screens without all the boilerplate. -**Ui tiles** +Two flavors are available: standard **Material 3** components and **Material 3 Expressive** components. +The expressive variants are built on `SegmentedListItem` and support segmented list styling. + +**Ui tiles** (`ui-tiles`) — Material 3 | Component | Screenshot | |-------------------------------------------------------|----------------------------------------------------------------------------------| @@ -36,22 +39,40 @@ developers build complex settings screens without all the boilerplate. | [SettingsSwitch](#SettingsSwitch) | switch.png | | [SettingsGroup](#SettingsGroup) | group.png | -**Ui tiles expanded** +**Ui tiles extended** (`ui-tiles-extended`) — Material 3 + +| Component | Screenshot | +|---------------------------------------------|----------------------------------------------------------------| +| [SettingsSlider](#SettingsSlider) | slider.png | +| [SettingsSegmented](#SettingsSegmented) | | -| Component | Screenshot | -|-----------------------------------|----------------------------------------------------------------| -| [SettingsSlider](#SettingsSlider) | slider.png | +**Ui tiles expressive** (`ui-tiles-expressive`) — Material 3 Expressive -**Ui tiles expressive** +> Requires Material 3 Expressive API (`@OptIn(ExperimentalMaterial3ExpressiveApi::class)`). +> All components support segmented list styling via the `shapes` parameter. -| Component | Screenshot | -|---------------------------------------------|----------------------------------------------------------------------------| -| [SettingsButtonGroup](#SettingsButtonGroup) | button-group.png | +| Component | Screenshot | +|-------------------------------------------------------------------|----------------------------------------------------------------------------| +| [SettingsMenuLink (Expressive)](#SettingsMenuLink-Expressive) | menu.png | +| [SettingsCheckbox (Expressive)](#SettingsCheckbox-Expressive) | checkbox.png | +| [SettingsTriStateCheckbox (Expressive)](#SettingsTriStateCheckbox-Expressive) | | +| [SettingsRadioButton (Expressive)](#SettingsRadioButton-Expressive) | radiobutton.png | +| [SettingsSwitch (Expressive)](#SettingsSwitch-Expressive) | switch.png | +| [SettingsGroup (Expressive)](#SettingsGroup-Expressive) | | +| [SettingsButtonGroup](#SettingsButtonGroup) | button-group.png | ## Install +Pick the module(s) you need: + +| Module | Contents | +|--------|----------| +| `ui-tiles` | Standard M3: MenuLink, Checkbox, TriStateCheckbox, RadioButton, Switch, Group | +| `ui-tiles-extended` | Standard M3: Slider, Segmented | +| `ui-tiles-expressive` | Expressive M3: all of the above + ButtonGroup, with segmented list support | + ``` -##// groovy +// groovy implementation 'com.github.alorma.compose-settings:ui-tiles:$version' implementation 'com.github.alorma.compose-settings:ui-tiles-extended:$version' implementation 'com.github.alorma.compose-settings:ui-tiles-expressive:$version' @@ -127,6 +148,8 @@ This allows you to: ### Components +#### Material 3 (`ui-tiles`, `ui-tiles-extended`) + ##### SettingsMenuLink: ```kotlin @@ -225,9 +248,25 @@ SettingsSlider( slider.png +##### SettingsSegmented: + +```kotlin +SettingsSegmented( + title = { Text(text = "Setting title") }, + items = listOf(1, 2, 3), + selectedItem = 2, + itemTitleMap = { item -> "#$item" }, + onItemSelected = { selectedItem -> }, + modifier = Modifier, + enabled = false / true, + subtitle = { Text(text = "Setting subtitle") }, + icon = { Icon(...) }, +) +``` + ##### SettingsGroup -> Updates on `enabled` will be reflected on it's internal components unless you change their +> Updates on `enabled` will be reflected on its internal components unless you change their `enabled` state manually. ```kotlin @@ -268,10 +307,152 @@ SettingsGroup( group.png +--- + +#### Material 3 Expressive (`ui-tiles-expressive`) + +> These components require `@OptIn(ExperimentalMaterial3ExpressiveApi::class)`. +> +> They are built on `SegmentedListItem` and expose a `shapes: ListItemShapes` parameter, +> which enables the segmented list visual style (rounded groups of items with connected borders). + +**Segmented list example:** + +```kotlin +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +val colors = ListItemDefaults.segmentedColors( + containerColor = MaterialTheme.colorScheme.surfaceContainer, +) + +SettingsSwitch( + state = switchState, + title = { Text("Wi-Fi") }, + colors = colors, + shapes = ListItemDefaults.segmentedShapes(index = 0, count = 3), + onCheckedChange = { switchState = it }, +) +SettingsSwitch( + state = switchState2, + title = { Text("Bluetooth") }, + colors = colors, + shapes = ListItemDefaults.segmentedShapes(index = 1, count = 3), + onCheckedChange = { switchState2 = it }, +) +SettingsMenuLink( + title = { Text("Airplane mode") }, + colors = colors, + shapes = ListItemDefaults.segmentedShapes(index = 2, count = 3), + onClick = { }, +) +``` + +> Use `ListItemDefaults.segmentedShapes(index, count)` to give each item the correct corner +> rounding based on its position in the group. Pair with +> `Arrangement.spacedBy(ListItemDefaults.SegmentedGap)` between items. + +##### SettingsMenuLink (Expressive): + +```kotlin +SettingsMenuLink( + title = { Text(text = "Setting title") }, + subtitle = { Text(text = "Setting subtitle") }, + modifier = Modifier, + enabled = false / true, + icon = { Icon(...) }, + action = { IconButton() }, + colors = colors, + shapes = ListItemDefaults.segmentedShapes(index, count), + onClick = { ... }, +) +``` + +##### SettingsCheckbox (Expressive): + +```kotlin +SettingsCheckbox( + state = false / true, + title = { Text(text = "Setting title") }, + subtitle = { Text(text = "Setting subtitle") }, + modifier = Modifier, + enabled = false / true, + icon = { Icon(...) }, + colors = colors, + shapes = ListItemDefaults.segmentedShapes(index, count), + onCheckedChange = { newState: Boolean -> }, +) +``` + +##### SettingsTriStateCheckbox (Expressive): + +```kotlin +SettingsTriStateCheckbox( + state = ToggleableState.On / ToggleableState.Off / ToggleableState.Indeterminate, + title = { Text(text = "Setting title") }, + subtitle = { Text(text = "Setting subtitle") }, + modifier = Modifier, + enabled = false / true, + icon = { Icon(...) }, + colors = colors, + shapes = ListItemDefaults.segmentedShapes(index, count), + onCheckedChange = { newState: ToggleableState -> }, +) +``` + +##### SettingsRadioButton (Expressive): + +```kotlin +SettingsRadioButton( + state = false / true, + title = { Text(text = "Setting title") }, + subtitle = { Text(text = "Setting subtitle") }, + modifier = Modifier, + enabled = false / true, + icon = { Icon(...) }, + colors = colors, + shapes = ListItemDefaults.segmentedShapes(index, count), + onClick = { }, +) +``` + +##### SettingsSwitch (Expressive): + +```kotlin +SettingsSwitch( + state = false / true, + title = { Text(text = "Setting title") }, + subtitle = { Text(text = "Setting subtitle") }, + modifier = Modifier, + enabled = false / true, + icon = { Icon(...) }, + colors = colors, + shapes = ListItemDefaults.segmentedShapes(index, count), + onCheckedChange = { newState: Boolean -> }, +) +``` + +##### SettingsGroup (Expressive): + +> Works exactly like the M3 version. Pair with `Arrangement.spacedBy(ListItemDefaults.SegmentedGap)` +> and `ListItemDefaults.segmentedShapes` on children to achieve a connected segmented look. + +```kotlin +SettingsGroup( + modifier = Modifier, + enabled = false / true, + title = { Text(text = "SettingsGroup") }, + contentPadding = PaddingValues(16.dp), + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), +) { + SettingsMenuLink(...) + SettingsCheckbox(...) + SettingsSwitch(...) + ... +} +``` + ##### SettingsButtonGroup -> Requires Material 3 Expressive components (uses `OutlinedToggleButton` from Material 3 Expressive -> API). +> Exclusive to the expressive module. Uses `OutlinedToggleButton` from Material 3 Expressive. ```kotlin SettingsButtonGroup( @@ -284,6 +465,8 @@ SettingsButtonGroup( enabled = false / true, subtitle = { Text(text = "Setting subtitle") }, icon = { Icon(...) }, + colors = colors, + shapes = ListItemDefaults.segmentedShapes(index, count), ) ```