1
0
mirror of https://github.com/AllanWang/Frost-for-Facebook.git synced 2024-11-08 12:02:33 +01:00

Create initial settings item

This commit is contained in:
Allan Wang 2023-06-22 15:24:41 -07:00
parent bc07e6ec9d
commit 09bb021e66
No known key found for this signature in database
GPG Key ID: C93E3F9C679D7A56
3 changed files with 236 additions and 0 deletions

View File

@ -0,0 +1,156 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.pitchedapps.frost.compose.settings
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.LayoutScopeMarker
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Person
import androidx.compose.material3.Checkbox as MaterialCheckbox
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.MutableState
import androidx.compose.runtime.Stable
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.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pitchedapps.frost.ext.optionalCompose
import com.pitchedapps.frost.ext.thenIf
/** Basic building block for settings */
@Composable
fun SettingsListItem(
modifier: Modifier = Modifier,
icon: ImageVector? = null,
title: String,
description: String? = null,
content: (@Composable SettingsContentScope.() -> SettingsContent)? = null
) {
val settingsContent = content?.invoke(SettingsContentScopeImpl)
ListItem(
modifier =
modifier.thenIf(settingsContent != null) {
Modifier.clickable(
onClick = settingsContent!!::onClick,
)
},
leadingContent =
icon.optionalCompose {
Icon(
modifier = Modifier.size(24.dp),
imageVector = it,
contentDescription = null,
)
},
headlineContent = { Text(text = title) },
supportingContent = description.optionalCompose { Text(text = it) },
trailingContent =
settingsContent.takeIf { it !is SettingsContentClickOnly }.optionalCompose { it.compose() },
)
}
@Preview
@Composable
private fun SettingsListItemPreview() {
val state = remember { mutableStateOf(false) }
MaterialTheme {
SettingsListItem(
icon = Icons.Outlined.Person,
title = "Test Title",
description = "Test Description",
) {
checkbox(state.asSettingState())
}
}
}
private object SettingsContentScopeImpl : SettingsContentScope
@LayoutScopeMarker
interface SettingsContentScope {
@Composable
fun <T, R> T.rememberSetting(getter: T.() -> R, setter: T.(R) -> Unit): SettingState<R> {
return remember(this) {
object : SettingState<R> {
override var value: R
get() = getter()
set(value) {
setter(value)
}
}
}
}
@Composable
fun <T> MutableState<T>.asSettingState(): SettingState<T> {
return remember(this) {
object : SettingState<T> {
override var value: T by this@asSettingState
}
}
}
}
interface SettingsContent {
fun onClick()
@Composable fun compose()
}
private class SettingsContentClickOnly(private val action: () -> Unit) : SettingsContent {
override fun onClick() {
action()
}
@Composable final override fun compose() = Unit
}
@Stable
interface SettingState<T> {
var value: T
}
fun SettingsContentScope.click(action: () -> Unit): SettingsContent =
SettingsContentClickOnly(action)
fun SettingsContentScope.checkbox(state: SettingState<Boolean>): SettingsContent =
object : SettingsContent {
override fun onClick() {
state.value = !state.value
}
@Composable
override fun compose() {
MaterialCheckbox(
checked = state.value,
onCheckedChange = { state.value = it },
)
}
}

View File

@ -0,0 +1,70 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
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)
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) },
),
)
}
},
)
}
MaterialTheme { LazyColumn { items(composables) { compose -> compose() } } }
}

View File

@ -34,3 +34,13 @@ fun Offset.toIntOffset() = IntOffset(x.roundToInt(), y.roundToInt())
fun IntSize.toDpSize(density: Density): DpSize { fun IntSize.toDpSize(density: Density): DpSize {
return with(density) { DpSize(width.toDp(), height.toDp()) } return with(density) { DpSize(width.toDp(), height.toDp()) }
} }
/**
* Helper for functions that take in nullable compose lambdas.
*
* If the input is null, return null. Otherwise, return the provided composable lambda.
*/
fun <T> T?.optionalCompose(action: @Composable (T) -> Unit): (@Composable () -> Unit)? {
if (this == null) return null
return { action(this) }
}