diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsContent.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsContent.kt
deleted file mode 100644
index 8924ac59e..000000000
--- a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsContent.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2023 Allan Wang
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.pitchedapps.frost.compose.settings
-
-import androidx.compose.material3.Checkbox
-import androidx.compose.material3.Switch
-import androidx.compose.runtime.Composable
-
-fun SettingsContentScope.checkbox(state: SettingState): SettingsContent =
- object : SettingsContent {
-
- override fun onClick() {
- state.value = !state.value
- }
-
- @Composable
- override fun compose() {
- Checkbox(
- enabled = state.enabled,
- checked = state.value,
- onCheckedChange = { state.value = it },
- )
- }
- }
-
-fun SettingsContentScope.switch(
- state: SettingState,
-): SettingsContent =
- object : SettingsContent {
-
- override fun onClick() {
- state.value = !state.value
- }
-
- @Composable
- override fun compose() {
- Switch(
- enabled = state.enabled,
- checked = state.value,
- onCheckedChange = { state.value = it },
- )
- }
- }
diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsContentScope.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsContentScope.kt
deleted file mode 100644
index 8e4b92b06..000000000
--- a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsContentScope.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2023 Allan Wang
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.pitchedapps.frost.compose.settings
-
-import androidx.compose.foundation.layout.LayoutScopeMarker
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-
-@LayoutScopeMarker
-interface SettingsContentScope {
- @Composable
- fun T.rememberSetting(
- enabler: T.() -> Boolean = { true },
- getter: T.() -> R,
- setter: T.(R) -> Unit
- ): SettingState {
- return remember(this) {
- object : SettingState {
- override val enabled: Boolean
- get() = enabler()
-
- override var value: R
- get() = getter()
- set(value) {
- setter(value)
- }
- }
- }
- }
-
- @Composable
- fun MutableState.asSettingState(): SettingState {
- return remember(this) {
- object : SettingState {
- override val enabled: Boolean = true
- override var value: T by this@asSettingState
- }
- }
- }
-}
-
-interface SettingsContent {
-
- fun onClick()
-
- @Composable fun compose()
-}
diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsDsl.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsDsl.kt
new file mode 100644
index 000000000..eb8889f36
--- /dev/null
+++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsDsl.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2023 Allan Wang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.pitchedapps.frost.compose.settings
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.vector.ImageVector
+
+@DslMarker annotation class SettingsDslMarker
+
+@SettingsDslMarker
+interface SettingsDsl {
+ /** Entry point to avoid cluttering the global namespace. */
+ companion object : SettingsDsl
+}
+
+/** Dsl for creating individual entries in a list */
+@SettingsDslMarker
+interface SettingsListDsl {
+
+ /**
+ * Sub list with group title
+ *
+ * TODO support collapsed and/or shown?
+ */
+ // fun group(title: String, enabled: Boolean = true, action: SettingsListDsl.() -> Unit)
+
+ /** Generic item without content */
+ fun item(
+ title: String,
+ enabled: Boolean = true,
+ icon: ImageVector? = null,
+ description: String? = null,
+ onClick: (() -> Unit)? = null
+ )
+
+ /** Long, non clickable content */
+ fun description(text: String, icon: ImageVector? = null)
+
+ fun checkbox(
+ title: String,
+ enabled: Boolean = true,
+ icon: ImageVector? = null,
+ description: String? = null,
+ checked: Boolean,
+ onCheckedChanged: (Boolean) -> Unit,
+ )
+
+ fun switch(
+ title: String,
+ enabled: Boolean = true,
+ icon: ImageVector? = null,
+ description: String? = null,
+ checked: Boolean,
+ onCheckedChanged: (Boolean) -> Unit,
+ )
+
+ fun custom(
+ title: String,
+ enabled: Boolean = true,
+ icon: ImageVector? = null,
+ description: String? = null,
+ onClick: (() -> Unit)? = null,
+ content: @Composable () -> Unit,
+ )
+}
diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsListDsl.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsListDsl.kt
new file mode 100644
index 000000000..62a54a279
--- /dev/null
+++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsListDsl.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2023 Allan Wang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.pitchedapps.frost.compose.settings
+
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+
+@Composable
+fun SettingsListDsl(modifier: Modifier = Modifier, content: SettingsListDsl.() -> Unit) {
+ val items = SettingsDsl.settingsListDsl(content)
+
+ LazyColumn(modifier = modifier) { items(items) { compose -> compose() } }
+}
+
+@Preview
+@Composable
+fun SettingsListDslPreview() {
+
+ data class Model(
+ val check1: Boolean = false,
+ val switch1: Boolean = false,
+ val switch2: Boolean = false,
+ )
+
+ var state by remember { mutableStateOf(Model()) }
+
+ MaterialTheme {
+ SettingsListDsl {
+ checkbox(
+ title = "Check 1",
+ checked = state.check1,
+ onCheckedChanged = { state = state.copy(check1 = it) },
+ )
+ checkbox(
+ title = "Check 1",
+ description = "Linked again",
+ checked = state.check1,
+ onCheckedChanged = { state = state.copy(check1 = it) },
+ )
+ switch(
+ title = "Switch 1",
+ checked = state.switch1,
+ onCheckedChanged = { state = state.copy(switch1 = it) },
+ )
+ switch(
+ title = "Switch 2",
+ enabled = state.switch1,
+ description = "Enabled by switch 1",
+ checked = state.switch2,
+ onCheckedChanged = { state = state.copy(switch2 = it) },
+ )
+ }
+ }
+}
diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsListItem.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsListItem.kt
index 7ef81e271..8fa729669 100644
--- a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsListItem.kt
+++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsListItem.kt
@@ -18,16 +18,22 @@ package com.pitchedapps.frost.compose.settings
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.size
+import androidx.compose.material.ContentAlpha
+import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Person
+import androidx.compose.material3.Checkbox
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -38,60 +44,54 @@ import com.pitchedapps.frost.ext.thenIf
@Composable
fun SettingsListItem(
modifier: Modifier = Modifier,
- icon: ImageVector? = null,
title: String,
+ enabled: Boolean = true,
+ icon: ImageVector? = null,
description: String? = null,
- content: (@Composable SettingsContentScope.() -> SettingsContent)? = null
+ onClick: (() -> Unit)? = null,
+ content: (@Composable () -> Unit)? = null
) {
- val settingsContent = content?.invoke(SettingsContentScopeImpl)
-
+ val alpha = if (enabled) LocalContentAlpha.current else ContentAlpha.disabled
ListItem(
modifier =
- modifier.thenIf(settingsContent != null) {
- Modifier.clickable(
- onClick = settingsContent!!::onClick,
- )
+ modifier.thenIf(onClick != null) {
+ Modifier.clickable(enabled = enabled) { onClick?.invoke() }
},
leadingContent =
icon.optionalCompose {
Icon(
- modifier = Modifier.size(24.dp),
+ modifier = Modifier.size(24.dp).alpha(alpha),
imageVector = it,
contentDescription = null,
)
},
- headlineContent = { Text(text = title) },
- supportingContent = description.optionalCompose { Text(text = it) },
- trailingContent =
- settingsContent.takeIf { it !is SettingsContentClickOnly }.optionalCompose { it.compose() },
+ headlineContent = { Text(modifier = Modifier.alpha(alpha), text = title) },
+ supportingContent =
+ description.optionalCompose {
+ Text(
+ modifier = Modifier.alpha(alpha),
+ text = it,
+ )
+ },
+ trailingContent = content,
)
}
@Preview
@Composable
private fun SettingsListItemPreview() {
- val state = remember { mutableStateOf(false) }
+ var state by remember { mutableStateOf(false) }
+
MaterialTheme {
SettingsListItem(
icon = Icons.Outlined.Person,
title = "Test Title",
description = "Test Description",
) {
- checkbox(state.asSettingState())
+ Checkbox(
+ checked = state,
+ onCheckedChange = { state = it },
+ )
}
}
}
-
-private object SettingsContentScopeImpl : SettingsContentScope
-
-private class SettingsContentClickOnly(private val action: () -> Unit) : SettingsContent {
-
- override fun onClick() {
- action()
- }
-
- @Composable override fun compose() = Unit
-}
-
-fun SettingsContentScope.click(action: () -> Unit): SettingsContent =
- SettingsContentClickOnly(action)
diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsListItemDsl.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsListItemDsl.kt
new file mode 100644
index 000000000..69378f01b
--- /dev/null
+++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsListItemDsl.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2023 Allan Wang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.pitchedapps.frost.compose.settings
+
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.Switch
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.vector.ImageVector
+
+fun SettingsDsl.settingsListDsl(content: SettingsListDsl.() -> Unit): List<@Composable () -> Unit> {
+ val data = SettingsListDslData()
+ data.content()
+ return data.items
+}
+
+private class SettingsListDslData : SettingsListDsl {
+ val items: MutableList<@Composable () -> Unit> = mutableListOf()
+
+ private fun addCompose(content: @Composable () -> Unit) {
+ items.add(content)
+ }
+
+ override fun item(
+ title: String,
+ enabled: Boolean,
+ icon: ImageVector?,
+ description: String?,
+ onClick: (() -> Unit)?
+ ) {
+ addCompose {}
+ }
+
+ override fun description(text: String, icon: ImageVector?) {}
+
+ override fun checkbox(
+ title: String,
+ enabled: Boolean,
+ icon: ImageVector?,
+ description: String?,
+ checked: Boolean,
+ onCheckedChanged: (Boolean) -> Unit
+ ) {
+ custom(
+ icon = icon,
+ title = title,
+ enabled = enabled,
+ description = description,
+ onClick = { onCheckedChanged(!checked) },
+ ) {
+ Checkbox(
+ enabled = enabled,
+ checked = checked,
+ onCheckedChange = onCheckedChanged,
+ )
+ }
+ }
+
+ override fun switch(
+ title: String,
+ enabled: Boolean,
+ icon: ImageVector?,
+ description: String?,
+ checked: Boolean,
+ onCheckedChanged: (Boolean) -> Unit
+ ) {
+ custom(
+ icon = icon,
+ title = title,
+ enabled = enabled,
+ description = description,
+ onClick = { onCheckedChanged(!checked) },
+ ) {
+ Switch(
+ enabled = enabled,
+ checked = checked,
+ onCheckedChange = onCheckedChanged,
+ )
+ }
+ }
+
+ override fun custom(
+ title: String,
+ enabled: Boolean,
+ icon: ImageVector?,
+ description: String?,
+ onClick: (() -> Unit)?,
+ content: @Composable () -> Unit
+ ) {
+ addCompose {
+ SettingsListItem(
+ icon = icon,
+ title = title,
+ enabled = enabled,
+ description = description,
+ onClick = onClick,
+ content = content,
+ )
+ }
+ }
+}
diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsScreen.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsScreen.kt
deleted file mode 100644
index 58655254d..000000000
--- a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/settings/SettingsScreen.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2023 Allan Wang
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.pitchedapps.frost.compose.settings
-
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.items
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.tooling.preview.Preview
-
-@Composable fun SettingsScreen() {}
-
-@Preview
-@Composable
-fun SettingsScreenPreview() {
-
- data class Model(
- val check1: Boolean = false,
- val switch1: Boolean = false,
- val switch2: Boolean = false,
- )
-
- var state by remember { mutableStateOf(Model()) }
-
- val composables: List<@Composable () -> Unit> = remember {
- listOf(
- {
- SettingsListItem(
- title = "Check 1",
- ) {
- checkbox(
- state.rememberSetting(
- getter = { state.check1 },
- setter = { state = state.copy(check1 = it) },
- ),
- )
- }
- },
- {
- SettingsListItem(
- title = "Check 1",
- description = "Linked again",
- ) {
- checkbox(
- state.rememberSetting(
- getter = { state.check1 },
- setter = { state = state.copy(check1 = it) },
- ),
- )
- }
- },
- {
- SettingsListItem(
- title = "Switch 1",
- ) {
- switch(
- state.rememberSetting(
- getter = { state.switch1 },
- setter = { state = state.copy(switch1 = it) },
- ),
- )
- }
- },
- {
- SettingsListItem(
- title = "Switch 2",
- description = "Enabled by switch 1",
- ) {
- switch(
- state.rememberSetting(
- enabler = { state.switch1 },
- getter = { state.switch2 },
- setter = { state = state.copy(switch2 = it) },
- ),
- )
- }
- },
- )
- }
-
- MaterialTheme { LazyColumn { items(composables) { compose -> compose() } } }
-}