(feat) shape schemes
This commit is contained in:
@@ -40,7 +40,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.themes.Theme
|
||||
import de.mm20.launcher2.themes.Colors
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||
import de.mm20.launcher2.ui.component.LargeMessage
|
||||
@@ -63,7 +63,7 @@ fun ImportThemeSheet(
|
||||
viewModel.readTheme(context, uri)
|
||||
}
|
||||
|
||||
val theme by viewModel.theme
|
||||
val theme by viewModel.colors
|
||||
val error by viewModel.error
|
||||
var apply by viewModel.apply
|
||||
|
||||
@@ -132,13 +132,13 @@ fun ImportThemeSheet(
|
||||
|
||||
@Composable
|
||||
fun ThemePreview(
|
||||
theme: Theme,
|
||||
colors: Colors,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val darkMode = LocalDarkTheme.current
|
||||
var darkTheme by remember { mutableStateOf(darkMode) }
|
||||
|
||||
val colorScheme = if (darkTheme) darkColorSchemeOf(theme) else lightColorSchemeOf(theme)
|
||||
val colorScheme = if (darkTheme) darkColorSchemeOf(colors) else lightColorSchemeOf(colors)
|
||||
|
||||
Column(modifier = modifier) {
|
||||
Row(
|
||||
@@ -146,7 +146,7 @@ fun ThemePreview(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = theme.name,
|
||||
text = colors.name,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier = Modifier.weight(1f),
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
|
||||
@@ -5,11 +5,11 @@ import android.net.Uri
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.preferences.ThemeDescriptor
|
||||
import de.mm20.launcher2.preferences.ColorsDescriptor
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.themes.Theme
|
||||
import de.mm20.launcher2.themes.Colors
|
||||
import de.mm20.launcher2.themes.ThemeRepository
|
||||
import de.mm20.launcher2.themes.fromJson
|
||||
import de.mm20.launcher2.themes.fromLegacyJson
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
@@ -20,12 +20,12 @@ class ImportThemeSheetVM : ViewModel(), KoinComponent {
|
||||
private val themeRepository: ThemeRepository by inject()
|
||||
private val uiSettings: UiSettings by inject()
|
||||
|
||||
val theme = mutableStateOf<Theme?>(null)
|
||||
val colors = mutableStateOf<Colors?>(null)
|
||||
val error = mutableStateOf<Boolean>(false)
|
||||
val apply = mutableStateOf<Boolean>(false)
|
||||
|
||||
fun import() {
|
||||
val theme = theme.value
|
||||
val theme = colors.value
|
||||
val apply = apply.value
|
||||
if (theme != null) {
|
||||
viewModelScope.launch {
|
||||
@@ -37,30 +37,30 @@ class ImportThemeSheetVM : ViewModel(), KoinComponent {
|
||||
|
||||
fun readTheme(context: Context, uri: Uri) {
|
||||
error.value = false
|
||||
theme.value = null
|
||||
colors.value = null
|
||||
apply.value = true
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val inputStream =
|
||||
context.contentResolver.openInputStream(uri) ?: return@launch
|
||||
val theme = inputStream.use {
|
||||
val colors = inputStream.use {
|
||||
val json = it.readBytes().toString(Charsets.UTF_8)
|
||||
try {
|
||||
Theme.fromJson(json)
|
||||
Colors.fromLegacyJson(json)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
this@ImportThemeSheetVM.theme.value = theme
|
||||
if (theme == null) {
|
||||
this@ImportThemeSheetVM.colors.value = colors
|
||||
if (colors == null) {
|
||||
error.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun importTheme(theme: Theme, apply: Boolean) {
|
||||
themeRepository.createTheme(theme)
|
||||
private fun importTheme(colors: Colors, apply: Boolean) {
|
||||
themeRepository.createColors(colors)
|
||||
if (apply) {
|
||||
uiSettings.setTheme(ThemeDescriptor.Custom(theme.id.toString()))
|
||||
uiSettings.setColors(ColorsDescriptor.Custom(colors.id.toString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ fun Modifier.verticalFadingEdges(
|
||||
if(!enabled) return this
|
||||
if (top == 0.dp && bottom == 0.dp) return this
|
||||
|
||||
return this then drawWithContent {
|
||||
return drawWithContent {
|
||||
|
||||
val topColors = if (top > 0.dp) createColors(
|
||||
1f - amount,
|
||||
|
||||
@@ -27,7 +27,7 @@ fun Modifier.verticalScrims(
|
||||
if (!enabled) return this
|
||||
if (top == 0.dp && bottom == 0.dp) return this
|
||||
|
||||
return this then drawWithCache {
|
||||
return drawWithCache {
|
||||
onDrawWithContent {
|
||||
val topColors = if (top > 0.dp) createColors(
|
||||
1f - amount,
|
||||
|
||||
@@ -42,8 +42,8 @@ import de.mm20.launcher2.ui.settings.buildinfo.BuildInfoSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.calendarsearch.CalendarProviderSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.calendarsearch.CalendarSearchSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.cards.CardsSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.colorscheme.ThemeSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.colorscheme.ThemesSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.colorscheme.ColorSchemeSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.colorscheme.ColorSchemesSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.contacts.ContactsSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.crashreporter.CrashReportScreen
|
||||
import de.mm20.launcher2.ui.settings.crashreporter.CrashReporterScreen
|
||||
@@ -69,6 +69,8 @@ import de.mm20.launcher2.ui.settings.plugins.PluginSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.plugins.PluginsSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.search.SearchSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.searchactions.SearchActionsSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.shapes.ShapeSchemeSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.shapes.ShapeSchemesSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.tags.TagsSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.tasks.TasksIntegrationSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.unitconverter.UnitConverterHelpSettingsScreen
|
||||
@@ -160,11 +162,11 @@ class SettingsActivity : BaseActivity() {
|
||||
composable("settings/icons") {
|
||||
IconsSettingsScreen()
|
||||
}
|
||||
composable("settings/appearance/themes") {
|
||||
ThemesSettingsScreen()
|
||||
composable("settings/appearance/colors") {
|
||||
ColorSchemesSettingsScreen()
|
||||
}
|
||||
composable(
|
||||
"settings/appearance/themes/{id}",
|
||||
"settings/appearance/colors/{id}",
|
||||
arguments = listOf(navArgument("id") {
|
||||
nullable = false
|
||||
})
|
||||
@@ -172,7 +174,21 @@ class SettingsActivity : BaseActivity() {
|
||||
val id = it.arguments?.getString("id")?.let {
|
||||
UUID.fromString(it)
|
||||
} ?: return@composable
|
||||
ThemeSettingsScreen(id)
|
||||
ColorSchemeSettingsScreen(id)
|
||||
}
|
||||
composable("settings/appearance/shapes") {
|
||||
ShapeSchemesSettingsScreen()
|
||||
}
|
||||
composable(
|
||||
"settings/appearance/shapes/{id}",
|
||||
arguments = listOf(navArgument("id") {
|
||||
nullable = false
|
||||
})
|
||||
) {
|
||||
val id = it.arguments?.getString("id")?.let {
|
||||
UUID.fromString(it)
|
||||
} ?: return@composable
|
||||
ShapeSchemeSettingsScreen(id)
|
||||
}
|
||||
composable("settings/appearance/cards") {
|
||||
CardsSettingsScreen()
|
||||
|
||||
+22
-3
@@ -1,5 +1,10 @@
|
||||
package de.mm20.launcher2.ui.settings.appearance
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.CropSquare
|
||||
import androidx.compose.material.icons.rounded.Palette
|
||||
import androidx.compose.material.icons.rounded.TextFields
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@@ -45,12 +50,17 @@ fun AppearanceSettingsScreen() {
|
||||
viewModel.setColorScheme(newValue)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
PreferenceCategory {
|
||||
Preference(
|
||||
title = stringResource(id = R.string.preference_screen_colors),
|
||||
summary = themeName,
|
||||
onClick = {
|
||||
navController?.navigate("settings/appearance/themes")
|
||||
}
|
||||
navController?.navigate("settings/appearance/colors")
|
||||
},
|
||||
icon = Icons.Rounded.Palette,
|
||||
)
|
||||
val font by viewModel.font.collectAsState()
|
||||
ListPreference(
|
||||
@@ -68,7 +78,16 @@ fun AppearanceSettingsScreen() {
|
||||
getTypography(context, it.value)
|
||||
}
|
||||
Text(it.first, style = typography.titleMedium)
|
||||
}
|
||||
},
|
||||
icon = Icons.Rounded.TextFields,
|
||||
)
|
||||
Preference(
|
||||
title = stringResource(id = R.string.preference_screen_shapes),
|
||||
summary = themeName,
|
||||
onClick = {
|
||||
navController?.navigate("settings/appearance/shapes")
|
||||
},
|
||||
icon = Icons.Rounded.CropSquare,
|
||||
)
|
||||
|
||||
Preference(
|
||||
|
||||
+2
-3
@@ -2,7 +2,6 @@ package de.mm20.launcher2.ui.settings.appearance
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.icons.IconService
|
||||
import de.mm20.launcher2.preferences.ColorScheme
|
||||
import de.mm20.launcher2.preferences.Font
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
@@ -26,8 +25,8 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
uiSettings.setColorScheme(colorScheme)
|
||||
}
|
||||
|
||||
val themeName = uiSettings.theme.flatMapLatest {
|
||||
themeRepository.getThemeOrDefault(it)
|
||||
val themeName = uiSettings.colors.flatMapLatest {
|
||||
themeRepository.getColorsOrDefault(it)
|
||||
}.map {
|
||||
it.name
|
||||
}
|
||||
|
||||
@@ -47,28 +47,6 @@ fun CardsSettingsScreen() {
|
||||
}
|
||||
item {
|
||||
PreferenceCategory {
|
||||
ListPreference(
|
||||
icon = Icons.Rounded.Rectangle,
|
||||
title = stringResource(R.string.preference_cards_shape),
|
||||
items = listOf(
|
||||
stringResource(R.string.preference_cards_shape_rounded) to SurfaceShape.Rounded,
|
||||
stringResource(R.string.preference_cards_shape_cut) to SurfaceShape.Cut,
|
||||
),
|
||||
value = cardStyle.shape,
|
||||
onValueChanged = {
|
||||
viewModel.setShape(it)
|
||||
})
|
||||
SliderPreference(
|
||||
title = stringResource(R.string.preference_cards_corner_radius),
|
||||
icon = Icons.Rounded.RoundedCorner,
|
||||
value = cardStyle.cornerRadius,
|
||||
min = 0,
|
||||
max = 24,
|
||||
step = 1,
|
||||
onValueChanged = {
|
||||
viewModel.setRadius(it)
|
||||
}
|
||||
)
|
||||
SliderPreference(
|
||||
title = stringResource(R.string.preference_cards_opacity),
|
||||
icon = Icons.Rounded.Opacity,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package de.mm20.launcher2.ui.settings.cards
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import de.mm20.launcher2.preferences.SurfaceShape
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
@@ -15,15 +14,8 @@ class CardsSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
uiSettings.setCardOpacity(opacity)
|
||||
}
|
||||
|
||||
fun setRadius(radius: Int) {
|
||||
uiSettings.setCardRadius(radius)
|
||||
}
|
||||
|
||||
fun setBorderWidth(borderWidth: Int) {
|
||||
uiSettings.setCardBorderWidth(borderWidth)
|
||||
}
|
||||
|
||||
fun setShape(shape: SurfaceShape) {
|
||||
uiSettings.setCardShape(shape)
|
||||
}
|
||||
}
|
||||
+3
-15
@@ -1,16 +1,11 @@
|
||||
package de.mm20.launcher2.ui.settings.colorscheme
|
||||
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.FlowRowScope
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.DarkMode
|
||||
@@ -31,7 +26,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun ThemePreferenceCategory(
|
||||
fun ColorSchemePreferenceCategory(
|
||||
title: String,
|
||||
previewColorScheme: ColorScheme,
|
||||
darkMode: Boolean,
|
||||
@@ -95,15 +90,8 @@ fun ThemePreferenceCategory(
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.horizontalScroll(rememberScrollState())
|
||||
.padding(16.dp)
|
||||
) {
|
||||
colorPreferences()
|
||||
}
|
||||
HorizontalDivider()
|
||||
colorPreferences()
|
||||
HorizontalDivider(modifier = Modifier.padding(top = 8.dp))
|
||||
}
|
||||
|
||||
}
|
||||
+128
-160
@@ -1,20 +1,16 @@
|
||||
package de.mm20.launcher2.ui.settings.colorscheme
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material.icons.rounded.Edit
|
||||
import androidx.compose.material.icons.rounded.Lock
|
||||
import androidx.compose.material.icons.rounded.OpenInNew
|
||||
@@ -60,6 +56,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.badges.Badge
|
||||
import de.mm20.launcher2.icons.ColorLayer
|
||||
import de.mm20.launcher2.icons.StaticLauncherIcon
|
||||
import de.mm20.launcher2.icons.TintedIconLayer
|
||||
@@ -78,8 +75,8 @@ import palettes.CorePalette
|
||||
import java.util.UUID
|
||||
|
||||
@Composable
|
||||
fun ThemeSettingsScreen(themeId: UUID) {
|
||||
val viewModel: ThemesSettingsScreenVM = viewModel()
|
||||
fun ColorSchemeSettingsScreen(themeId: UUID) {
|
||||
val viewModel: ColorSchemesSettingsScreenVM = viewModel()
|
||||
|
||||
val context = LocalContext.current
|
||||
val dark = LocalDarkTheme.current
|
||||
@@ -107,7 +104,13 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
var name by remember(theme) { mutableStateOf(theme?.name ?: "") }
|
||||
AlertDialog(
|
||||
onDismissRequest = { editName = false },
|
||||
text = { OutlinedTextField(value = name, onValueChange = { name = it }, singleLine = true) },
|
||||
text = {
|
||||
OutlinedTextField(
|
||||
value = name,
|
||||
onValueChange = { name = it },
|
||||
singleLine = true
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
@@ -146,135 +149,129 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
stringResource(R.string.preference_custom_colors_corepalette),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
modifier = Modifier.padding(bottom = 16.dp, start = 16.dp, end = 16.dp),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.horizontalScroll(rememberScrollState())
|
||||
.padding(16.dp)
|
||||
) {
|
||||
CorePaletteColorPreference(
|
||||
title = "Primary",
|
||||
value = theme?.corePalette?.primary,
|
||||
onValueChange = {
|
||||
viewModel.updateTheme(
|
||||
theme!!.copy(
|
||||
corePalette = theme!!.corePalette.copy(
|
||||
primary = it
|
||||
)
|
||||
CorePaletteColorPreference(
|
||||
title = "Primary",
|
||||
value = theme?.corePalette?.primary,
|
||||
onValueChange = {
|
||||
viewModel.updateTheme(
|
||||
theme!!.copy(
|
||||
corePalette = theme!!.corePalette.copy(
|
||||
primary = it
|
||||
)
|
||||
)
|
||||
},
|
||||
defaultValue = systemPalette.primary,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
CorePaletteColorPreference(
|
||||
title = "Secondary",
|
||||
value = theme?.corePalette?.secondary,
|
||||
onValueChange = {
|
||||
viewModel.updateTheme(
|
||||
theme!!.copy(
|
||||
corePalette = theme!!.corePalette.copy(
|
||||
secondary = it
|
||||
)
|
||||
)
|
||||
},
|
||||
defaultValue = systemPalette.primary,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
CorePaletteColorPreference(
|
||||
title = "Secondary",
|
||||
value = theme?.corePalette?.secondary,
|
||||
onValueChange = {
|
||||
viewModel.updateTheme(
|
||||
theme!!.copy(
|
||||
corePalette = theme!!.corePalette.copy(
|
||||
secondary = it
|
||||
)
|
||||
)
|
||||
},
|
||||
defaultValue = systemPalette.secondary,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
autoGenerate = {
|
||||
theme!!.corePalette.primary?.let {
|
||||
CorePalette.of(it).a2.keyColor.toInt()
|
||||
}
|
||||
},
|
||||
)
|
||||
CorePaletteColorPreference(
|
||||
title = "Tertiary",
|
||||
value = theme?.corePalette?.tertiary,
|
||||
onValueChange = {
|
||||
viewModel.updateTheme(
|
||||
theme!!.copy(
|
||||
corePalette = theme!!.corePalette.copy(
|
||||
tertiary = it
|
||||
)
|
||||
)
|
||||
},
|
||||
defaultValue = systemPalette.secondary,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
autoGenerate = {
|
||||
theme!!.corePalette.primary?.let {
|
||||
CorePalette.of(it).a2.keyColor.toInt()
|
||||
}
|
||||
},
|
||||
)
|
||||
CorePaletteColorPreference(
|
||||
title = "Tertiary",
|
||||
value = theme?.corePalette?.tertiary,
|
||||
onValueChange = {
|
||||
viewModel.updateTheme(
|
||||
theme!!.copy(
|
||||
corePalette = theme!!.corePalette.copy(
|
||||
tertiary = it
|
||||
)
|
||||
)
|
||||
},
|
||||
defaultValue = systemPalette.tertiary,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
autoGenerate = {
|
||||
theme!!.corePalette.primary?.let {
|
||||
CorePalette.of(it).a3.keyColor.toInt()
|
||||
}
|
||||
},
|
||||
)
|
||||
CorePaletteColorPreference(
|
||||
title = "Neutral",
|
||||
value = theme?.corePalette?.neutral,
|
||||
onValueChange = {
|
||||
viewModel.updateTheme(
|
||||
theme!!.copy(
|
||||
corePalette = theme!!.corePalette.copy(
|
||||
neutral = it
|
||||
)
|
||||
)
|
||||
},
|
||||
defaultValue = systemPalette.tertiary,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
autoGenerate = {
|
||||
theme!!.corePalette.primary?.let {
|
||||
CorePalette.of(it).a3.keyColor.toInt()
|
||||
}
|
||||
},
|
||||
)
|
||||
CorePaletteColorPreference(
|
||||
title = "Neutral",
|
||||
value = theme?.corePalette?.neutral,
|
||||
onValueChange = {
|
||||
viewModel.updateTheme(
|
||||
theme!!.copy(
|
||||
corePalette = theme!!.corePalette.copy(
|
||||
neutral = it
|
||||
)
|
||||
)
|
||||
},
|
||||
defaultValue = systemPalette.neutral,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
autoGenerate = {
|
||||
theme!!.corePalette.primary?.let {
|
||||
CorePalette.of(it).n1.keyColor.toInt()
|
||||
}
|
||||
},
|
||||
)
|
||||
CorePaletteColorPreference(
|
||||
title = "Neutral Variant",
|
||||
value = theme?.corePalette?.neutralVariant,
|
||||
onValueChange = {
|
||||
viewModel.updateTheme(
|
||||
theme!!.copy(
|
||||
corePalette = theme!!.corePalette.copy(
|
||||
neutralVariant = it
|
||||
)
|
||||
)
|
||||
},
|
||||
defaultValue = systemPalette.neutral,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
autoGenerate = {
|
||||
theme!!.corePalette.primary?.let {
|
||||
CorePalette.of(it).n1.keyColor.toInt()
|
||||
}
|
||||
},
|
||||
)
|
||||
CorePaletteColorPreference(
|
||||
title = "Neutral Variant",
|
||||
value = theme?.corePalette?.neutralVariant,
|
||||
onValueChange = {
|
||||
viewModel.updateTheme(
|
||||
theme!!.copy(
|
||||
corePalette = theme!!.corePalette.copy(
|
||||
neutralVariant = it
|
||||
)
|
||||
)
|
||||
},
|
||||
defaultValue = systemPalette.neutralVariant,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
autoGenerate = {
|
||||
theme!!.corePalette.primary?.let {
|
||||
CorePalette.of(it).n2.keyColor.toInt()
|
||||
}
|
||||
},
|
||||
)
|
||||
CorePaletteColorPreference(
|
||||
title = "Error",
|
||||
value = theme?.corePalette?.error,
|
||||
onValueChange = {
|
||||
viewModel.updateTheme(
|
||||
theme!!.copy(
|
||||
corePalette = theme!!.corePalette.copy(
|
||||
error = it
|
||||
)
|
||||
)
|
||||
},
|
||||
defaultValue = systemPalette.neutralVariant,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
autoGenerate = {
|
||||
theme!!.corePalette.primary?.let {
|
||||
CorePalette.of(it).n2.keyColor.toInt()
|
||||
}
|
||||
},
|
||||
)
|
||||
CorePaletteColorPreference(
|
||||
title = "Error",
|
||||
value = theme?.corePalette?.error,
|
||||
onValueChange = {
|
||||
viewModel.updateTheme(
|
||||
theme!!.copy(
|
||||
corePalette = theme!!.corePalette.copy(
|
||||
error = it
|
||||
)
|
||||
)
|
||||
},
|
||||
defaultValue = systemPalette.error,
|
||||
autoGenerate = {
|
||||
theme!!.corePalette.primary?.let {
|
||||
CorePalette.of(it).error.keyColor.toInt()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
HorizontalDivider()
|
||||
)
|
||||
},
|
||||
defaultValue = systemPalette.error,
|
||||
autoGenerate = {
|
||||
theme!!.corePalette.primary?.let {
|
||||
CorePalette.of(it).error.keyColor.toInt()
|
||||
}
|
||||
},
|
||||
)
|
||||
HorizontalDivider(modifier = Modifier.padding(top = 8.dp))
|
||||
}
|
||||
}
|
||||
item {
|
||||
ThemePreferenceCategory(
|
||||
ColorSchemePreferenceCategory(
|
||||
title = "Primary colors",
|
||||
previewColorScheme = previewColorScheme,
|
||||
darkMode = previewDarkTheme,
|
||||
@@ -302,7 +299,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.primary,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "On Primary",
|
||||
@@ -326,7 +322,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.onPrimary,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "Primary Container",
|
||||
@@ -350,7 +345,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.primaryContainer,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "On Primary Container",
|
||||
@@ -374,7 +368,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.onPrimaryContainer,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
|
||||
},
|
||||
@@ -419,7 +412,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
}
|
||||
}
|
||||
item {
|
||||
ThemePreferenceCategory(
|
||||
ColorSchemePreferenceCategory(
|
||||
title = "Secondary colors",
|
||||
previewColorScheme = previewColorScheme,
|
||||
darkMode = previewDarkTheme,
|
||||
@@ -447,7 +440,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.secondary,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "On Secondary",
|
||||
@@ -471,7 +463,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.onSecondary,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "Secondary Container",
|
||||
@@ -495,7 +486,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.secondaryContainer,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "On Secondary Container",
|
||||
@@ -519,7 +509,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.onSecondaryContainer,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
},
|
||||
) {
|
||||
@@ -557,7 +546,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
}
|
||||
}
|
||||
item {
|
||||
ThemePreferenceCategory(
|
||||
ColorSchemePreferenceCategory(
|
||||
title = "Tertiary colors",
|
||||
previewColorScheme = previewColorScheme,
|
||||
darkMode = previewDarkTheme,
|
||||
@@ -585,7 +574,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.tertiary,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "On Tertiary",
|
||||
@@ -609,7 +597,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.onTertiary,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "Tertiary Container",
|
||||
@@ -633,7 +620,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.tertiaryContainer,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "On Tertiary Container",
|
||||
@@ -657,14 +643,17 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.onTertiaryContainer,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
},
|
||||
) {
|
||||
ShapedLauncherIcon(
|
||||
badge = { Badge() },
|
||||
size = 48.dp,
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
ThemePreferenceCategory(
|
||||
ColorSchemePreferenceCategory(
|
||||
title = "Surface colors",
|
||||
previewColorScheme = previewColorScheme,
|
||||
darkMode = previewDarkTheme,
|
||||
@@ -716,7 +705,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.surface,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "Surface Bright",
|
||||
@@ -740,7 +728,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.surfaceBright,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "Surface Tint",
|
||||
@@ -764,7 +751,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.surfaceTint,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
},
|
||||
) {
|
||||
@@ -824,7 +810,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
}
|
||||
}
|
||||
item {
|
||||
ThemePreferenceCategory(
|
||||
ColorSchemePreferenceCategory(
|
||||
title = "Surface container colors",
|
||||
previewColorScheme = previewColorScheme,
|
||||
darkMode = previewDarkTheme,
|
||||
@@ -852,7 +838,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.surfaceContainerLowest,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "Surface Container Low",
|
||||
@@ -876,7 +861,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.surfaceContainerLow,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "Surface Container",
|
||||
@@ -900,7 +884,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.surfaceContainer,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "Surface Container High",
|
||||
@@ -924,7 +907,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.surfaceContainerHigh,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "Surface Container Highest",
|
||||
@@ -948,7 +930,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.surfaceContainerHighest,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "Surface Variant",
|
||||
@@ -972,7 +953,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.surfaceVariant,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
},
|
||||
) {
|
||||
@@ -994,7 +974,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
}
|
||||
}
|
||||
item {
|
||||
ThemePreferenceCategory(
|
||||
ColorSchemePreferenceCategory(
|
||||
title = "Content colors",
|
||||
previewColorScheme = previewColorScheme,
|
||||
darkMode = previewDarkTheme,
|
||||
@@ -1022,7 +1002,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.onSurface,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
|
||||
|
||||
@@ -1048,7 +1027,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
|
||||
ThemeColorPreference(
|
||||
@@ -1073,7 +1051,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
},
|
||||
) {
|
||||
@@ -1126,7 +1103,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
}
|
||||
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
color = MaterialTheme.colorScheme.surfaceContainer,
|
||||
shape = MaterialTheme.shapes.extraSmall,
|
||||
tonalElevation = 3.dp,
|
||||
shadowElevation = 3.dp,
|
||||
@@ -1148,7 +1125,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
}
|
||||
}
|
||||
item {
|
||||
ThemePreferenceCategory(
|
||||
ColorSchemePreferenceCategory(
|
||||
title = "Outline colors",
|
||||
previewColorScheme = previewColorScheme,
|
||||
darkMode = previewDarkTheme,
|
||||
@@ -1176,7 +1153,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.outline,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "Outline Variant",
|
||||
@@ -1200,7 +1176,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.outlineVariant,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
},
|
||||
) {
|
||||
@@ -1240,7 +1215,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
}
|
||||
}
|
||||
item {
|
||||
ThemePreferenceCategory(
|
||||
ColorSchemePreferenceCategory(
|
||||
title = "Error colors",
|
||||
previewColorScheme = previewColorScheme,
|
||||
darkMode = previewDarkTheme,
|
||||
@@ -1268,7 +1243,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.error,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "On Error",
|
||||
@@ -1292,7 +1266,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.onError,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "Error Container",
|
||||
@@ -1316,7 +1289,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.errorContainer,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "On Error Container",
|
||||
@@ -1340,7 +1312,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.onErrorContainer,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
},
|
||||
) {
|
||||
@@ -1354,7 +1325,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
}
|
||||
}
|
||||
item {
|
||||
ThemePreferenceCategory(
|
||||
ColorSchemePreferenceCategory(
|
||||
title = "Inverse colors",
|
||||
previewColorScheme = previewColorScheme,
|
||||
darkMode = previewDarkTheme,
|
||||
@@ -1382,7 +1353,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.inverseSurface,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "Inverse Surface",
|
||||
@@ -1406,7 +1376,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.inverseOnSurface,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
ThemeColorPreference(
|
||||
title = "Inverse Primary",
|
||||
@@ -1430,7 +1399,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
|
||||
)
|
||||
},
|
||||
defaultValue = selectedDefaultScheme.inversePrimary,
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
)
|
||||
},
|
||||
) {
|
||||
+16
-16
@@ -45,7 +45,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.themes.Theme
|
||||
import de.mm20.launcher2.themes.Colors
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.common.ImportThemeSheet
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
@@ -57,15 +57,15 @@ import de.mm20.launcher2.ui.theme.colorscheme.darkColorSchemeOf
|
||||
import de.mm20.launcher2.ui.theme.colorscheme.lightColorSchemeOf
|
||||
|
||||
@Composable
|
||||
fun ThemesSettingsScreen() {
|
||||
val viewModel: ThemesSettingsScreenVM = viewModel()
|
||||
fun ColorSchemesSettingsScreen() {
|
||||
val viewModel: ColorSchemesSettingsScreenVM = viewModel()
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
|
||||
val selectedTheme by viewModel.selectedTheme.collectAsStateWithLifecycle(null)
|
||||
val themes by viewModel.themes.collectAsStateWithLifecycle(emptyList())
|
||||
val selectedTheme by viewModel.selectedColors.collectAsStateWithLifecycle(null)
|
||||
val themes by viewModel.colors.collectAsStateWithLifecycle(emptyList())
|
||||
|
||||
var deleteTheme by remember { mutableStateOf<Theme?>(null) }
|
||||
var deleteColors by remember { mutableStateOf<Colors?>(null) }
|
||||
|
||||
var importThemeUri by remember { mutableStateOf<Uri?>(null) }
|
||||
|
||||
@@ -114,7 +114,7 @@ fun ThemesSettingsScreen() {
|
||||
},
|
||||
text = { Text(stringResource(R.string.edit)) },
|
||||
onClick = {
|
||||
navController?.navigate("settings/appearance/themes/${theme.id}")
|
||||
navController?.navigate("settings/appearance/colors/${theme.id}")
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
@@ -146,7 +146,7 @@ fun ThemesSettingsScreen() {
|
||||
},
|
||||
text = { Text(stringResource(R.string.menu_delete)) },
|
||||
onClick = {
|
||||
deleteTheme = theme
|
||||
deleteColors = theme
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
@@ -162,22 +162,22 @@ fun ThemesSettingsScreen() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deleteTheme != null) {
|
||||
if (deleteColors != null) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { deleteTheme = null },
|
||||
onDismissRequest = { deleteColors = null },
|
||||
text = {
|
||||
Text(
|
||||
stringResource(
|
||||
R.string.confirmation_delete_color_scheme,
|
||||
deleteTheme!!.name
|
||||
deleteColors!!.name
|
||||
)
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.delete(deleteTheme!!)
|
||||
deleteTheme = null
|
||||
viewModel.delete(deleteColors!!)
|
||||
deleteColors = null
|
||||
}
|
||||
) {
|
||||
Text(stringResource(android.R.string.ok))
|
||||
@@ -185,7 +185,7 @@ fun ThemesSettingsScreen() {
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = { deleteTheme = null }
|
||||
onClick = { deleteColors = null }
|
||||
) {
|
||||
Text(stringResource(android.R.string.cancel))
|
||||
}
|
||||
@@ -199,9 +199,9 @@ fun ThemesSettingsScreen() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ColorSchemePreview(theme: Theme) {
|
||||
fun ColorSchemePreview(colors: Colors) {
|
||||
val dark = LocalDarkTheme.current
|
||||
val scheme = if (dark) darkColorSchemeOf(theme) else lightColorSchemeOf(theme)
|
||||
val scheme = if (dark) darkColorSchemeOf(colors) else lightColorSchemeOf(colors)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(28.dp)
|
||||
+27
-27
@@ -6,13 +6,13 @@ import androidx.core.content.FileProvider
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.ktx.tryStartActivity
|
||||
import de.mm20.launcher2.preferences.ThemeDescriptor
|
||||
import de.mm20.launcher2.preferences.ColorsDescriptor
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.themes.BlackAndWhiteThemeId
|
||||
import de.mm20.launcher2.themes.DefaultThemeId
|
||||
import de.mm20.launcher2.themes.Theme
|
||||
import de.mm20.launcher2.themes.Colors
|
||||
import de.mm20.launcher2.themes.ThemeRepository
|
||||
import de.mm20.launcher2.themes.toJson
|
||||
import de.mm20.launcher2.themes.toLegacyJson
|
||||
import de.mm20.launcher2.ui.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -24,49 +24,49 @@ import org.koin.core.component.inject
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
|
||||
class ThemesSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
class ColorSchemesSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
|
||||
private val themeRepository: ThemeRepository by inject()
|
||||
private val uiSettings: UiSettings by inject()
|
||||
|
||||
val selectedTheme = uiSettings.theme.map {
|
||||
val selectedColors = uiSettings.colors.map {
|
||||
when(it) {
|
||||
ThemeDescriptor.Default -> DefaultThemeId
|
||||
ThemeDescriptor.BlackAndWhite -> BlackAndWhiteThemeId
|
||||
is ThemeDescriptor.Custom -> UUID.fromString(it.id)
|
||||
ColorsDescriptor.Default -> DefaultThemeId
|
||||
ColorsDescriptor.BlackAndWhite -> BlackAndWhiteThemeId
|
||||
is ColorsDescriptor.Custom -> UUID.fromString(it.id)
|
||||
}
|
||||
}
|
||||
val themes: Flow<List<Theme>> = themeRepository.getThemes()
|
||||
val colors: Flow<List<Colors>> = themeRepository.getAllColors()
|
||||
|
||||
fun getTheme(id: UUID): Flow<Theme?> {
|
||||
return themeRepository.getTheme(id)
|
||||
fun getTheme(id: UUID): Flow<Colors?> {
|
||||
return themeRepository.getColors(id)
|
||||
}
|
||||
|
||||
fun updateTheme(theme: Theme) {
|
||||
themeRepository.updateTheme(theme)
|
||||
fun updateTheme(colors: Colors) {
|
||||
themeRepository.updateColors(colors)
|
||||
}
|
||||
|
||||
fun selectTheme(theme: Theme) {
|
||||
uiSettings.setTheme(when(theme.id) {
|
||||
DefaultThemeId -> ThemeDescriptor.Default
|
||||
BlackAndWhiteThemeId -> ThemeDescriptor.BlackAndWhite
|
||||
else -> ThemeDescriptor.Custom(theme.id.toString())
|
||||
fun selectTheme(colors: Colors) {
|
||||
uiSettings.setColors(when(colors.id) {
|
||||
DefaultThemeId -> ColorsDescriptor.Default
|
||||
BlackAndWhiteThemeId -> ColorsDescriptor.BlackAndWhite
|
||||
else -> ColorsDescriptor.Custom(colors.id.toString())
|
||||
})
|
||||
}
|
||||
|
||||
fun duplicate(theme: Theme) {
|
||||
themeRepository.createTheme(theme.copy(id = UUID.randomUUID()))
|
||||
fun duplicate(colors: Colors) {
|
||||
themeRepository.createColors(colors.copy(id = UUID.randomUUID()))
|
||||
}
|
||||
|
||||
fun delete(theme: Theme) {
|
||||
themeRepository.deleteTheme(theme)
|
||||
fun delete(colors: Colors) {
|
||||
themeRepository.deleteColors(colors)
|
||||
}
|
||||
|
||||
fun exportTheme(context: Context, theme: Theme) {
|
||||
fun exportTheme(context: Context, colors: Colors) {
|
||||
viewModelScope.launch {
|
||||
val file = withContext(Dispatchers.IO) {
|
||||
val file = File(context.cacheDir, "${theme.name}.kvtheme")
|
||||
file.writeText(theme.toJson())
|
||||
val file = File(context.cacheDir, "${colors.name}.kvtheme")
|
||||
file.writeText(colors.toLegacyJson())
|
||||
file
|
||||
}
|
||||
context.tryStartActivity(Intent().apply {
|
||||
@@ -82,8 +82,8 @@ class ThemesSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
}
|
||||
|
||||
fun createNew(context: Context) {
|
||||
themeRepository.createTheme(
|
||||
Theme(
|
||||
themeRepository.createColors(
|
||||
Colors(
|
||||
id = UUID.randomUUID(),
|
||||
name = context.getString(R.string.new_color_scheme_name)
|
||||
)
|
||||
+23
-10
@@ -3,8 +3,10 @@ package de.mm20.launcher2.ui.settings.colorscheme
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
@@ -16,6 +18,7 @@ import androidx.compose.material.icons.rounded.SettingsSuggest
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -28,7 +31,11 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.mm20.launcher2.themes.get
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||
import de.mm20.launcher2.ui.component.Tooltip
|
||||
@@ -47,19 +54,25 @@ fun CorePaletteColorPreference(
|
||||
) {
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
|
||||
Tooltip(
|
||||
tooltipText = title
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth()
|
||||
.clickable(
|
||||
onClick = { showDialog = true },
|
||||
)
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
ColorSwatch(
|
||||
color = Color(value ?: defaultValue),
|
||||
modifier = modifier
|
||||
.size(48.dp)
|
||||
.combinedClickable(
|
||||
onClick = { showDialog = true },
|
||||
onLongClick = {
|
||||
onValueChange(null)
|
||||
}
|
||||
),
|
||||
modifier = Modifier.padding(end = 20.dp).size(48.dp),
|
||||
)
|
||||
|
||||
Text(
|
||||
title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+21
-8
@@ -4,6 +4,7 @@ import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -11,6 +12,7 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
@@ -45,7 +47,9 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.drawscope.Fill
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.mm20.launcher2.themes.ColorRef
|
||||
import de.mm20.launcher2.themes.CorePaletteColor
|
||||
@@ -66,7 +70,7 @@ import de.mm20.launcher2.themes.Color as ThemeColor
|
||||
@Composable
|
||||
fun ThemeColorPreference(
|
||||
title: String,
|
||||
value: de.mm20.launcher2.themes.Color?,
|
||||
value: ThemeColor?,
|
||||
corePalette: FullCorePalette,
|
||||
onValueChange: (ThemeColor?) -> Unit,
|
||||
defaultValue: ThemeColor,
|
||||
@@ -74,16 +78,25 @@ fun ThemeColorPreference(
|
||||
) {
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
|
||||
Tooltip(
|
||||
tooltipText = title
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth()
|
||||
.clickable(
|
||||
onClick = { showDialog = true },
|
||||
)
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
ColorSwatch(
|
||||
color = Color((value ?: defaultValue).get(corePalette)),
|
||||
modifier = modifier
|
||||
.size(48.dp)
|
||||
.clickable(
|
||||
onClick = { showDialog = true },
|
||||
),
|
||||
modifier = Modifier.padding(end = 20.dp).size(48.dp),
|
||||
)
|
||||
|
||||
Text(
|
||||
title,
|
||||
style = MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace),
|
||||
textAlign = TextAlign.Center,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+733
@@ -0,0 +1,733 @@
|
||||
package de.mm20.launcher2.ui.settings.shapes
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CutCornerShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Edit
|
||||
import androidx.compose.material.icons.rounded.OpenInNew
|
||||
import androidx.compose.material.icons.rounded.RestartAlt
|
||||
import androidx.compose.material.icons.rounded.RoundedCorner
|
||||
import androidx.compose.material.icons.rounded.Search
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.BottomSheetDefaults
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.SegmentedButton
|
||||
import androidx.compose.material3.SegmentedButtonDefaults
|
||||
import androidx.compose.material3.Shapes
|
||||
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
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.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.min
|
||||
import androidx.compose.ui.unit.times
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.icons.CutCorner
|
||||
import de.mm20.launcher2.icons.RoundedCornerAlt
|
||||
import de.mm20.launcher2.themes.CornerStyle
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||
import de.mm20.launcher2.ui.ktx.withCorners
|
||||
import de.mm20.launcher2.ui.theme.shapes.shapesOf
|
||||
import java.util.UUID
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import de.mm20.launcher2.themes.Shape as ThemeShape
|
||||
|
||||
@Composable
|
||||
fun ShapeSchemeSettingsScreen(themeId: UUID) {
|
||||
val viewModel: ShapeSchemesSettingsScreenVM = viewModel()
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
val theme by remember(
|
||||
viewModel,
|
||||
themeId
|
||||
) { viewModel.getShapes(themeId) }.collectAsStateWithLifecycle(null)
|
||||
val previewShapes = theme?.let { shapesOf(it) }
|
||||
|
||||
|
||||
var editName by remember { mutableStateOf(false) }
|
||||
|
||||
if (editName) {
|
||||
var name by remember(theme) { mutableStateOf(theme?.name ?: "") }
|
||||
AlertDialog(
|
||||
onDismissRequest = { editName = false },
|
||||
text = {
|
||||
OutlinedTextField(
|
||||
value = name,
|
||||
onValueChange = { name = it },
|
||||
singleLine = true
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.updateShapes(theme!!.copy(name = name))
|
||||
editName = false
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.save))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
PreferenceScreen(
|
||||
title = {
|
||||
Text(
|
||||
theme?.name ?: "",
|
||||
modifier = Modifier.clickable {
|
||||
editName = true
|
||||
},
|
||||
)
|
||||
},
|
||||
helpUrl = "https://kvaesitso.mm20.de/docs/user-guide/customization/color-schemes",
|
||||
) {
|
||||
if (theme == null || previewShapes == null) return@PreferenceScreen
|
||||
val baseShape = theme!!.baseShape
|
||||
|
||||
item {
|
||||
PreferenceCategory {
|
||||
ShapePreference(
|
||||
title = stringResource(R.string.preference_shapes_base),
|
||||
shape = baseShape,
|
||||
baseShape = baseShape,
|
||||
factor = 1f,
|
||||
onValueChange = {
|
||||
viewModel.updateShapes(
|
||||
theme!!.copy(
|
||||
baseShape = it ?: ThemeShape(
|
||||
corners = CornerStyle.Rounded,
|
||||
radii = intArrayOf(12, 12, 12, 12)
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
titleTextStyle = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
PreferenceCategory {
|
||||
ShapePreview(
|
||||
previewShapes = previewShapes,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.width(200.dp)
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(32.dp)
|
||||
.background(
|
||||
MaterialTheme.colorScheme.surface,
|
||||
MaterialTheme.shapes.extraSmall.withCorners(
|
||||
topStart = false,
|
||||
topEnd = false
|
||||
)
|
||||
)
|
||||
)
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 2.dp)
|
||||
.height(32.dp)
|
||||
.background(
|
||||
MaterialTheme.colorScheme.surface,
|
||||
MaterialTheme.shapes.extraSmall.withCorners(
|
||||
bottomStart = false,
|
||||
bottomEnd = false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surfaceContainer,
|
||||
shape = MaterialTheme.shapes.extraSmall,
|
||||
tonalElevation = 3.dp,
|
||||
shadowElevation = 3.dp,
|
||||
modifier = Modifier.wrapContentWidth()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 8.dp)
|
||||
.width(IntrinsicSize.Max),
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.OpenInNew, null)
|
||||
},
|
||||
text = { Text("Menu") },
|
||||
onClick = { })
|
||||
}
|
||||
}
|
||||
}
|
||||
ShapePreference(
|
||||
title = "Extra small",
|
||||
shape = theme!!.extraSmall,
|
||||
baseShape = baseShape,
|
||||
factor = 1f / 3f,
|
||||
onValueChange = {
|
||||
viewModel.updateShapes(
|
||||
theme!!.copy(extraSmall = it)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
PreferenceCategory {
|
||||
ShapePreview(
|
||||
previewShapes = previewShapes,
|
||||
) {
|
||||
FilterChip(
|
||||
onClick = {},
|
||||
label = {
|
||||
Text("Chip")
|
||||
},
|
||||
selected = false,
|
||||
)
|
||||
}
|
||||
ShapePreference(
|
||||
title = "Small",
|
||||
shape = theme!!.small,
|
||||
baseShape = baseShape,
|
||||
factor = 2f / 3f,
|
||||
onValueChange = {
|
||||
viewModel.updateShapes(
|
||||
theme!!.copy(small = it)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
PreferenceCategory {
|
||||
ShapePreview(
|
||||
previewShapes = previewShapes,
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.width(200.dp)
|
||||
.height(48.dp)
|
||||
.background(
|
||||
MaterialTheme.colorScheme.surface,
|
||||
MaterialTheme.shapes.medium
|
||||
)
|
||||
) {
|
||||
Icon(
|
||||
Icons.Rounded.Search,
|
||||
null,
|
||||
modifier = Modifier.padding(12.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
ShapePreference(
|
||||
title = "Medium",
|
||||
shape = theme!!.medium,
|
||||
baseShape = baseShape,
|
||||
factor = 1f,
|
||||
onValueChange = {
|
||||
viewModel.updateShapes(
|
||||
theme!!.copy(medium = it)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
PreferenceCategory {
|
||||
ShapePreview(
|
||||
previewShapes = previewShapes,
|
||||
) {
|
||||
FloatingActionButton(onClick = {}) {
|
||||
Icon(Icons.Rounded.Edit, null)
|
||||
}
|
||||
}
|
||||
ShapePreference(
|
||||
title = "Large",
|
||||
shape = theme!!.large,
|
||||
baseShape = baseShape,
|
||||
factor = 4f / 3f,
|
||||
onValueChange = {
|
||||
viewModel.updateShapes(
|
||||
theme!!.copy(large = it)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
PreferenceCategory {
|
||||
ShapePreference(
|
||||
title = "Large increased",
|
||||
shape = theme!!.largeIncreased,
|
||||
baseShape = baseShape,
|
||||
factor = 5f / 3f,
|
||||
onValueChange = {
|
||||
viewModel.updateShapes(
|
||||
theme!!.copy(largeIncreased = it)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
PreferenceCategory {
|
||||
ShapePreview(
|
||||
previewShapes = previewShapes,
|
||||
) {
|
||||
Surface(
|
||||
shape = BottomSheetDefaults.ExpandedShape,
|
||||
color = BottomSheetDefaults.ContainerColor,
|
||||
shadowElevation = BottomSheetDefaults.Elevation,
|
||||
tonalElevation = BottomSheetDefaults.Elevation,
|
||||
modifier = Modifier
|
||||
.width(250.dp)
|
||||
.height(144.dp),
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.TopCenter,
|
||||
) {
|
||||
BottomSheetDefaults.DragHandle()
|
||||
}
|
||||
}
|
||||
}
|
||||
ShapePreference(
|
||||
title = "Extra large",
|
||||
shape = theme!!.extraLarge,
|
||||
baseShape = baseShape,
|
||||
factor = 7f / 3f,
|
||||
onValueChange = {
|
||||
viewModel.updateShapes(
|
||||
theme!!.copy(extraLarge = it)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
PreferenceCategory {
|
||||
ShapePreference(
|
||||
title = "Extra large increased",
|
||||
shape = theme!!.extraLargeIncreased,
|
||||
baseShape = baseShape,
|
||||
factor = 8f / 3f,
|
||||
onValueChange = {
|
||||
viewModel.updateShapes(
|
||||
theme!!.copy(extraLargeIncreased = it)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
PreferenceCategory {
|
||||
ShapePreference(
|
||||
title = "Extra extra large",
|
||||
shape = theme!!.extraExtraLarge,
|
||||
baseShape = baseShape,
|
||||
factor = 12f / 3f,
|
||||
onValueChange = {
|
||||
viewModel.updateShapes(
|
||||
theme!!.copy(extraExtraLarge = it)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ShapePreference(
|
||||
title: String,
|
||||
shape: ThemeShape?,
|
||||
baseShape: ThemeShape,
|
||||
factor: Float = 1f,
|
||||
onValueChange: (ThemeShape?) -> Unit,
|
||||
titleTextStyle: TextStyle = MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace)
|
||||
) {
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
|
||||
val f = min(1f, factor)
|
||||
val topStart =
|
||||
(shape?.radii?.get(0)?.div(factor) ?: baseShape.radii?.get(0)?.toFloat() ?: 12f) * f
|
||||
val topEnd =
|
||||
(shape?.radii?.get(1)?.div(factor) ?: baseShape.radii?.get(1)?.toFloat() ?: 12f) * f
|
||||
val bottomEnd =
|
||||
(shape?.radii?.get(2)?.div(factor) ?: baseShape.radii?.get(2)?.toFloat() ?: 12f) * f
|
||||
val bottomStart =
|
||||
(shape?.radii?.get(3)?.div(factor) ?: baseShape.radii?.get(3)?.toFloat() ?: 12f) * f
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 8.dp)
|
||||
.clickable(
|
||||
onClick = { showDialog = true },
|
||||
)
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(end = 20.dp)
|
||||
.size(48.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(min(48.dp, factor * 48.dp))
|
||||
.border(
|
||||
2.dp,
|
||||
MaterialTheme.colorScheme.primary,
|
||||
if ((shape?.corners ?: baseShape.corners
|
||||
?: CornerStyle.Rounded) == CornerStyle.Cut
|
||||
) {
|
||||
CutCornerShape(
|
||||
topStart = topStart.dp,
|
||||
topEnd = topEnd.dp,
|
||||
bottomEnd = bottomEnd.dp,
|
||||
bottomStart = bottomStart.dp
|
||||
)
|
||||
} else {
|
||||
RoundedCornerShape(
|
||||
topStart = topStart.dp,
|
||||
topEnd = topEnd.dp,
|
||||
bottomEnd = bottomEnd.dp,
|
||||
bottomStart = bottomStart.dp
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Text(
|
||||
title,
|
||||
style = titleTextStyle,
|
||||
textAlign = TextAlign.Center,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
|
||||
if (showDialog) {
|
||||
val maxRadius = (24 * factor).toInt()
|
||||
|
||||
val baseTopStart = ((baseShape.radii?.get(0) ?: 12) * factor).toInt()
|
||||
val baseTopEnd = ((baseShape.radii?.get(1) ?: 12) * factor).toInt()
|
||||
val baseBottomEnd = ((baseShape.radii?.get(2) ?: 12) * factor).toInt()
|
||||
val baseBottomStart = ((baseShape.radii?.get(3) ?: 12) * factor).toInt()
|
||||
|
||||
|
||||
var currentCornerStyle by remember(shape) { mutableStateOf(shape?.corners) }
|
||||
var currentTopStart by remember(shape) { mutableStateOf(shape?.radii?.get(0)) }
|
||||
var currentTopEnd by remember(shape) { mutableStateOf(shape?.radii?.get(1)) }
|
||||
var currentBottomEnd by remember(shape) { mutableStateOf(shape?.radii?.get(2)) }
|
||||
var currentBottomStart by remember(shape) { mutableStateOf(shape?.radii?.get(3)) }
|
||||
|
||||
val actualCornerStyle = currentCornerStyle ?: baseShape.corners ?: CornerStyle.Rounded
|
||||
val actualTopStart = currentTopStart ?: baseTopStart
|
||||
val actualTopEnd = currentTopEnd ?: baseTopEnd
|
||||
val actualBottomEnd = currentBottomEnd ?: baseBottomEnd
|
||||
val actualBottomStart = currentBottomStart ?: baseBottomStart
|
||||
|
||||
BottomSheetDialog(
|
||||
onDismissRequest = {
|
||||
showDialog = false
|
||||
onValueChange(
|
||||
ThemeShape(
|
||||
corners = currentCornerStyle,
|
||||
radii = if (currentTopStart != null || currentTopEnd != null || currentBottomEnd != null || currentBottomStart != null) {
|
||||
intArrayOf(
|
||||
currentTopStart ?: baseTopStart,
|
||||
currentTopEnd ?: baseTopEnd,
|
||||
currentBottomEnd ?: baseBottomEnd,
|
||||
currentBottomStart ?: baseBottomStart,
|
||||
)
|
||||
} else null
|
||||
)
|
||||
)
|
||||
}) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(it),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
val previewShape = if ((currentCornerStyle ?: baseShape.corners
|
||||
?: CornerStyle.Rounded) == CornerStyle.Rounded
|
||||
) {
|
||||
RoundedCornerShape(
|
||||
topStart = (currentTopStart ?: baseTopStart).dp,
|
||||
topEnd = (currentTopEnd ?: baseTopEnd).dp,
|
||||
bottomEnd = (currentBottomEnd ?: baseBottomEnd).dp,
|
||||
bottomStart = (currentBottomStart ?: baseBottomStart).dp
|
||||
|
||||
)
|
||||
} else {
|
||||
CutCornerShape(
|
||||
topStart = (currentTopStart ?: baseTopStart).dp,
|
||||
topEnd = (currentTopEnd ?: baseTopEnd).dp,
|
||||
bottomEnd = (currentBottomEnd ?: baseBottomEnd).dp,
|
||||
bottomStart = (currentBottomStart ?: baseBottomStart).dp
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(max(96, maxRadius * 2).dp)
|
||||
.background(MaterialTheme.colorScheme.surfaceContainer, previewShape)
|
||||
.border(
|
||||
2.dp,
|
||||
MaterialTheme.colorScheme.primary,
|
||||
previewShape
|
||||
)
|
||||
)
|
||||
|
||||
SingleChoiceSegmentedButtonRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
SegmentedButton(
|
||||
selected = actualCornerStyle == CornerStyle.Rounded,
|
||||
onClick = {
|
||||
currentCornerStyle = CornerStyle.Rounded
|
||||
},
|
||||
shape = SegmentedButtonDefaults.itemShape(0, 2),
|
||||
icon = {
|
||||
SegmentedButtonDefaults.Icon(
|
||||
active = actualCornerStyle == CornerStyle.Rounded,
|
||||
) {
|
||||
Icon(Icons.Rounded.RoundedCornerAlt, null)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.preference_cards_shape_rounded))
|
||||
}
|
||||
SegmentedButton(
|
||||
selected = actualCornerStyle == CornerStyle.Cut,
|
||||
onClick = {
|
||||
currentCornerStyle = CornerStyle.Cut
|
||||
},
|
||||
shape = SegmentedButtonDefaults.itemShape(1, 2),
|
||||
icon = {
|
||||
SegmentedButtonDefaults.Icon(
|
||||
active = actualCornerStyle == CornerStyle.Cut,
|
||||
) {
|
||||
Icon(Icons.Rounded.CutCorner, null)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.preference_cards_shape_cut))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
Icons.Rounded.RoundedCorner,
|
||||
null,
|
||||
modifier = Modifier
|
||||
.padding(end = 8.dp)
|
||||
.rotate(-90f)
|
||||
)
|
||||
Slider(
|
||||
modifier = Modifier
|
||||
.weight(1f),
|
||||
value = actualTopStart.toFloat(),
|
||||
onValueChange = {
|
||||
currentTopStart = it.toInt()
|
||||
},
|
||||
valueRange = 0f..maxRadius.toFloat(),
|
||||
steps = maxRadius + 1
|
||||
)
|
||||
Text(
|
||||
text = actualTopStart.toString(),
|
||||
modifier = Modifier.width(32.dp),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
Icons.Rounded.RoundedCorner,
|
||||
null,
|
||||
modifier = Modifier.padding(end = 8.dp)
|
||||
)
|
||||
Slider(
|
||||
modifier = Modifier
|
||||
.weight(1f),
|
||||
value = actualTopEnd.toFloat(),
|
||||
onValueChange = {
|
||||
currentTopEnd = it.toInt()
|
||||
},
|
||||
valueRange = 0f..maxRadius.toFloat(),
|
||||
steps = maxRadius + 1,
|
||||
)
|
||||
Text(
|
||||
text = actualTopEnd.toString(),
|
||||
modifier = Modifier.width(32.dp),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
Icons.Rounded.RoundedCorner,
|
||||
null,
|
||||
modifier = Modifier
|
||||
.padding(end = 8.dp)
|
||||
.rotate(90f)
|
||||
)
|
||||
Slider(
|
||||
modifier = Modifier
|
||||
.weight(1f),
|
||||
value = actualBottomEnd.toFloat(),
|
||||
onValueChange = {
|
||||
currentBottomEnd = it.toInt()
|
||||
},
|
||||
valueRange = 0f..maxRadius.toFloat(),
|
||||
steps = maxRadius + 1,
|
||||
)
|
||||
Text(
|
||||
text = actualBottomEnd.toString(),
|
||||
modifier = Modifier.width(32.dp),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
Icons.Rounded.RoundedCorner,
|
||||
null,
|
||||
modifier = Modifier
|
||||
.padding(end = 8.dp)
|
||||
.rotate(180f)
|
||||
)
|
||||
Slider(
|
||||
modifier = Modifier
|
||||
.weight(1f),
|
||||
value = actualBottomStart.toFloat(),
|
||||
onValueChange = {
|
||||
currentBottomStart = it.toInt()
|
||||
},
|
||||
valueRange = 0f..maxRadius.toFloat(),
|
||||
steps = maxRadius + 1,
|
||||
)
|
||||
Text(
|
||||
text = actualBottomStart.toString(),
|
||||
modifier = Modifier.width(32.dp),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
)
|
||||
|
||||
TextButton(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp)
|
||||
.align(Alignment.End),
|
||||
contentPadding = ButtonDefaults.TextButtonWithIconContentPadding,
|
||||
onClick = {
|
||||
currentTopStart = null
|
||||
currentTopEnd = null
|
||||
currentBottomEnd = null
|
||||
currentBottomStart = null
|
||||
currentCornerStyle = null
|
||||
onValueChange(null)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Rounded.RestartAlt, null,
|
||||
modifier = Modifier
|
||||
.padding(ButtonDefaults.IconSpacing)
|
||||
.size(ButtonDefaults.IconSize)
|
||||
)
|
||||
Text(stringResource(R.string.preference_restore_default))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShapePreview(
|
||||
previewShapes: Shapes,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
MaterialTheme(
|
||||
shapes = previewShapes
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceContainer
|
||||
)
|
||||
) {
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.horizontalScroll(rememberScrollState())
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
package de.mm20.launcher2.ui.settings.shapes
|
||||
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CutCornerShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material.icons.rounded.ContentCopy
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material.icons.rounded.Edit
|
||||
import androidx.compose.material.icons.rounded.MoreVert
|
||||
import androidx.compose.material.icons.rounded.RadioButtonChecked
|
||||
import androidx.compose.material.icons.rounded.RadioButtonUnchecked
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
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.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.themes.CornerStyle
|
||||
import de.mm20.launcher2.themes.Shapes
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||
import de.mm20.launcher2.ui.locals.LocalNavController
|
||||
import de.mm20.launcher2.ui.theme.shapes.shapesOf
|
||||
|
||||
@Composable
|
||||
fun ShapeSchemesSettingsScreen() {
|
||||
val viewModel: ShapeSchemesSettingsScreenVM = viewModel()
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
|
||||
val selectedTheme by viewModel.selectedShapes.collectAsStateWithLifecycle(null)
|
||||
val themes by viewModel.shapes.collectAsStateWithLifecycle(emptyList())
|
||||
|
||||
var deleteShapes by remember { mutableStateOf<Shapes?>(null) }
|
||||
|
||||
PreferenceScreen(
|
||||
title = stringResource(R.string.preference_screen_shapes),
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(onClick = { viewModel.createNew(context) }) {
|
||||
Icon(Icons.Rounded.Add, null)
|
||||
}
|
||||
}
|
||||
) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
for (theme in themes) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
Preference(
|
||||
icon = if (theme.id == selectedTheme) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
|
||||
title = theme.name,
|
||||
controls = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
ShapesPreview(theme)
|
||||
IconButton(
|
||||
modifier = Modifier.padding(start = 12.dp),
|
||||
onClick = { showMenu = true }) {
|
||||
Icon(Icons.Rounded.MoreVert, null)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }
|
||||
) {
|
||||
if (!theme.builtIn) {
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.Edit, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.edit)) },
|
||||
onClick = {
|
||||
navController?.navigate("settings/appearance/shapes/${theme.id}")
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.ContentCopy, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.duplicate)) },
|
||||
onClick = {
|
||||
viewModel.duplicate(theme)
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
if (!theme.builtIn) {
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.Delete, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.menu_delete)) },
|
||||
onClick = {
|
||||
deleteShapes = theme
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
viewModel.selectShapes(theme)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deleteShapes != null) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { deleteShapes = null },
|
||||
text = {
|
||||
Text(
|
||||
stringResource(
|
||||
R.string.confirmation_delete_shapes_scheme,
|
||||
deleteShapes!!.name
|
||||
)
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.delete(deleteShapes!!)
|
||||
deleteShapes = null
|
||||
}
|
||||
) {
|
||||
Text(stringResource(android.R.string.ok))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = { deleteShapes = null }
|
||||
) {
|
||||
Text(stringResource(android.R.string.cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShapesPreview(theme: Shapes) {
|
||||
val shape = theme.medium
|
||||
val baseShape = theme.baseShape
|
||||
|
||||
val topStart =
|
||||
(shape?.radii?.get(0)?.toFloat() ?: baseShape.radii?.get(0)?.toFloat() ?: 8f) / 3f * 2f
|
||||
val topEnd =
|
||||
(shape?.radii?.get(1)?.toFloat() ?: baseShape.radii?.get(1)?.toFloat() ?: 8f) / 3f * 2f
|
||||
val bottomEnd =
|
||||
(shape?.radii?.get(2)?.toFloat() ?: baseShape.radii?.get(2)?.toFloat() ?: 8f) / 3f * 2f
|
||||
val bottomStart =
|
||||
(shape?.radii?.get(3)?.toFloat() ?: baseShape.radii?.get(3)?.toFloat() ?: 8f) / 3f * 2f
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.border(
|
||||
2.dp,
|
||||
MaterialTheme.colorScheme.primary,
|
||||
if ((theme.medium?.corners ?: theme.baseShape.corners
|
||||
?: CornerStyle.Rounded) == CornerStyle.Cut
|
||||
) {
|
||||
CutCornerShape(
|
||||
topStart = topStart.dp,
|
||||
topEnd = topEnd.dp,
|
||||
bottomEnd = bottomEnd.dp,
|
||||
bottomStart = bottomStart.dp
|
||||
)
|
||||
} else {
|
||||
RoundedCornerShape(
|
||||
topStart = topStart.dp,
|
||||
topEnd = topEnd.dp,
|
||||
bottomEnd = bottomEnd.dp,
|
||||
bottomStart = bottomStart.dp
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package de.mm20.launcher2.ui.settings.shapes
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import de.mm20.launcher2.preferences.ShapesDescriptor
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.themes.CutShapesId
|
||||
import de.mm20.launcher2.themes.DefaultThemeId
|
||||
import de.mm20.launcher2.themes.ExtraRoundShapesId
|
||||
import de.mm20.launcher2.themes.RectShapesId
|
||||
import de.mm20.launcher2.themes.Shapes
|
||||
import de.mm20.launcher2.themes.ThemeRepository
|
||||
import de.mm20.launcher2.ui.R
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import java.util.UUID
|
||||
import kotlin.getValue
|
||||
|
||||
class ShapeSchemesSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
|
||||
private val themeRepository: ThemeRepository by inject()
|
||||
private val uiSettings: UiSettings by inject()
|
||||
|
||||
val selectedShapes = uiSettings.shapes.map {
|
||||
when(it) {
|
||||
ShapesDescriptor.Default -> DefaultThemeId
|
||||
ShapesDescriptor.Cut -> CutShapesId
|
||||
ShapesDescriptor.ExtraRound -> ExtraRoundShapesId
|
||||
ShapesDescriptor.Rect -> RectShapesId
|
||||
is ShapesDescriptor.Custom -> UUID.fromString(it.id)
|
||||
}
|
||||
}
|
||||
val shapes: Flow<List<Shapes>> = themeRepository.getAllShapes()
|
||||
|
||||
fun getShapes(id: UUID): Flow<Shapes?> {
|
||||
return themeRepository.getShapes(id)
|
||||
}
|
||||
|
||||
fun updateShapes(shapes: Shapes) {
|
||||
themeRepository.updateShapes(shapes)
|
||||
}
|
||||
|
||||
fun selectShapes(shapes: Shapes) {
|
||||
uiSettings.setShapes(when(shapes.id) {
|
||||
DefaultThemeId -> ShapesDescriptor.Default
|
||||
else -> ShapesDescriptor.Custom(shapes.id.toString())
|
||||
})
|
||||
}
|
||||
|
||||
fun duplicate(shapes: Shapes) {
|
||||
themeRepository.createShapes(shapes.copy(id = UUID.randomUUID()))
|
||||
}
|
||||
|
||||
fun delete(shapes: Shapes) {
|
||||
themeRepository.deleteShapes(shapes)
|
||||
}
|
||||
|
||||
fun createNew(context: Context) {
|
||||
themeRepository.createShapes(
|
||||
Shapes(
|
||||
id = UUID.randomUUID(),
|
||||
name = context.getString(R.string.new_shapes_name)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.themes.ThemeRepository
|
||||
import de.mm20.launcher2.ui.locals.LocalDarkTheme
|
||||
import de.mm20.launcher2.ui.theme.colorscheme.*
|
||||
import de.mm20.launcher2.ui.theme.shapes.shapesOf
|
||||
import de.mm20.launcher2.ui.theme.typography.DefaultTypography
|
||||
import de.mm20.launcher2.ui.theme.typography.getDeviceDefaultTypography
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
@@ -33,11 +34,17 @@ fun LauncherTheme(
|
||||
val uiSettings: UiSettings = koinInject()
|
||||
val themeRepository: ThemeRepository = koinInject()
|
||||
|
||||
val theme by remember {
|
||||
uiSettings.theme.flatMapLatest {
|
||||
themeRepository.getThemeOrDefault(it)
|
||||
val themeColors by remember {
|
||||
uiSettings.colors.flatMapLatest {
|
||||
themeRepository.getColorsOrDefault(it)
|
||||
}
|
||||
}.collectAsState(themeRepository.getDefaultTheme())
|
||||
}.collectAsState(null)
|
||||
|
||||
val themeShapes by remember {
|
||||
uiSettings.shapes.flatMapLatest {
|
||||
themeRepository.getShapesOrDefault(it)
|
||||
}
|
||||
}.collectAsState(null)
|
||||
|
||||
val colorSchemePref by remember { uiSettings.colorScheme }.collectAsState(
|
||||
ColorSchemePref.System
|
||||
@@ -45,27 +52,20 @@ fun LauncherTheme(
|
||||
val darkTheme =
|
||||
colorSchemePref == ColorSchemePref.Dark || colorSchemePref == ColorSchemePref.System && isSystemInDarkTheme()
|
||||
|
||||
val cornerRadius by remember {
|
||||
uiSettings.cardStyle.map {
|
||||
it.cornerRadius.dp
|
||||
}
|
||||
}.collectAsState(8.dp)
|
||||
|
||||
val baseShape by remember {
|
||||
uiSettings.cardStyle.map {
|
||||
when (it.shape) {
|
||||
SurfaceShape.Cut -> CutCornerShape(0f)
|
||||
else -> RoundedCornerShape(0f)
|
||||
}
|
||||
}
|
||||
}.collectAsState(RoundedCornerShape(0f))
|
||||
if (themeColors == null || themeShapes == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val colorScheme = if (darkTheme) {
|
||||
darkColorSchemeOf(theme)
|
||||
darkColorSchemeOf(themeColors!!)
|
||||
} else {
|
||||
lightColorSchemeOf(theme)
|
||||
lightColorSchemeOf(themeColors!!)
|
||||
}
|
||||
|
||||
val shapes = shapesOf(themeShapes!!)
|
||||
|
||||
|
||||
|
||||
val font by remember { uiSettings.font }.collectAsState(
|
||||
Font.Outfit
|
||||
)
|
||||
@@ -80,13 +80,7 @@ fun LauncherTheme(
|
||||
MaterialExpressiveTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = typography,
|
||||
shapes = Shapes(
|
||||
extraSmall = baseShape.copy(CornerSize(cornerRadius / 3f)),
|
||||
small = baseShape.copy(CornerSize(cornerRadius / 3f * 2f)),
|
||||
medium = baseShape.copy(CornerSize(cornerRadius)),
|
||||
large = baseShape.copy(CornerSize((cornerRadius / 3f * 4f).coerceAtMost(16.dp))),
|
||||
extraLarge = baseShape.copy(CornerSize((cornerRadius / 3f * 7f).coerceAtMost(28.dp))),
|
||||
),
|
||||
shapes = shapes,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package de.mm20.launcher2.ui.theme.colorscheme
|
||||
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
|
||||
val LightBlackAndWhiteColorScheme = lightColorScheme(
|
||||
primary = Color.Black,
|
||||
onPrimary = Color.White,
|
||||
primaryContainer = Color.White,
|
||||
onPrimaryContainer = Color.Black,
|
||||
inversePrimary = Color.White,
|
||||
secondary = Color.Black,
|
||||
onSecondary = Color.White,
|
||||
secondaryContainer = Color.White,
|
||||
onSecondaryContainer = Color.Black,
|
||||
tertiary = Color.Black,
|
||||
onTertiary = Color.White,
|
||||
tertiaryContainer = Color.White,
|
||||
onTertiaryContainer = Color.Black,
|
||||
background = Color.White,
|
||||
onBackground = Color.Black,
|
||||
surface = Color.White,
|
||||
onSurface = Color.Black,
|
||||
surfaceVariant = Color.White,
|
||||
onSurfaceVariant = Color.Black,
|
||||
inverseSurface = Color.Black,
|
||||
inverseOnSurface = Color.White,
|
||||
error = Color(0xFFC10000),
|
||||
onError = Color(0xFFFFFFFF),
|
||||
errorContainer = Color(0xFFFFDAD3),
|
||||
onErrorContainer = Color(0xFF410000),
|
||||
outline = Color.Black,
|
||||
surfaceTint = Color.White,
|
||||
outlineVariant = Color.Black,
|
||||
scrim = Color.Black,
|
||||
surfaceDim = Color.White,
|
||||
surfaceBright = Color.White,
|
||||
surfaceContainer = Color.White,
|
||||
surfaceContainerHigh = Color.White,
|
||||
surfaceContainerHighest = Color.White,
|
||||
surfaceContainerLow = Color.White,
|
||||
surfaceContainerLowest = Color.White,
|
||||
)
|
||||
val DarkBlackAndWhiteColorScheme = darkColorScheme(
|
||||
primary = Color.White,
|
||||
onPrimary = Color.Black,
|
||||
primaryContainer = Color.Black,
|
||||
onPrimaryContainer = Color.White,
|
||||
inversePrimary = Color.Black,
|
||||
secondary = Color.White,
|
||||
onSecondary = Color.Black,
|
||||
secondaryContainer = Color.Black,
|
||||
onSecondaryContainer = Color.White,
|
||||
tertiary = Color.White,
|
||||
onTertiary = Color.Black,
|
||||
tertiaryContainer = Color.Black,
|
||||
onTertiaryContainer = Color.White,
|
||||
background = Color.Black,
|
||||
onBackground = Color.White,
|
||||
surface = Color.Black,
|
||||
onSurface = Color.White,
|
||||
surfaceVariant = Color.Black,
|
||||
onSurfaceVariant = Color.White,
|
||||
inverseSurface = Color.White,
|
||||
inverseOnSurface = Color.Black,
|
||||
error = Color(0xffffb4a6),
|
||||
onError = Color(0xff690000),
|
||||
errorContainer = Color(0xff940000),
|
||||
onErrorContainer = Color(0xffffb4a6),
|
||||
outline = Color.White,
|
||||
surfaceTint = Color.White,
|
||||
outlineVariant = Color.White,
|
||||
scrim = Color.White,
|
||||
surfaceDim = Color.Black,
|
||||
surfaceBright = Color.Black,
|
||||
surfaceContainer = Color.Black,
|
||||
surfaceContainerHigh = Color.Black,
|
||||
surfaceContainerHighest = Color.Black,
|
||||
surfaceContainerLow = Color.Black,
|
||||
surfaceContainerLowest = Color.Black,
|
||||
)
|
||||
+5
-5
@@ -16,20 +16,20 @@ import de.mm20.launcher2.themes.DefaultDarkColorScheme
|
||||
import de.mm20.launcher2.themes.DefaultLightColorScheme
|
||||
import de.mm20.launcher2.themes.FullColorScheme
|
||||
import de.mm20.launcher2.themes.PartialCorePalette
|
||||
import de.mm20.launcher2.themes.Theme
|
||||
import de.mm20.launcher2.themes.Colors as ThemeColors
|
||||
import de.mm20.launcher2.themes.get
|
||||
import de.mm20.launcher2.themes.merge
|
||||
import de.mm20.launcher2.ui.locals.LocalWallpaperColors
|
||||
import org.koin.compose.koinInject
|
||||
|
||||
@Composable
|
||||
fun lightColorSchemeOf(theme: Theme): ColorScheme {
|
||||
return colorSchemeOf(theme.lightColorScheme.merge(DefaultLightColorScheme), theme.corePalette)
|
||||
fun lightColorSchemeOf(colors: ThemeColors): ColorScheme {
|
||||
return colorSchemeOf(colors.lightColorScheme.merge(DefaultLightColorScheme), colors.corePalette)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun darkColorSchemeOf(theme: Theme): ColorScheme {
|
||||
return colorSchemeOf(theme.darkColorScheme.merge(DefaultDarkColorScheme), theme.corePalette)
|
||||
fun darkColorSchemeOf(colors: ThemeColors): ColorScheme {
|
||||
return colorSchemeOf(colors.darkColorScheme.merge(DefaultDarkColorScheme), colors.corePalette)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -1,67 +0,0 @@
|
||||
package de.mm20.launcher2.ui.theme.colorscheme
|
||||
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val LightDefaultColorScheme = lightColorScheme(
|
||||
primary = Color(0xFF3D608A),
|
||||
onPrimary = Color(0xFFFFFFFF),
|
||||
primaryContainer = Color(0xFFD2E4FF),
|
||||
onPrimaryContainer = Color(0xFF001C37),
|
||||
inversePrimary = Color(0xFFA5C9F8),
|
||||
secondary = Color(0xFF535F70),
|
||||
onSecondary = Color(0xFFFFFFFF),
|
||||
secondaryContainer = Color(0xFFD7E3F8),
|
||||
onSecondaryContainer = Color(0xFF101C2B),
|
||||
tertiary = Color(0xFF6B5778),
|
||||
onTertiary = Color(0xFFFFFFFF),
|
||||
tertiaryContainer = Color(0xFFF3DAFF),
|
||||
onTertiaryContainer = Color(0xFF251431),
|
||||
surface = Color(0xFFFAF9FC),
|
||||
onSurface = Color(0xFF1A1C1E),
|
||||
onSurfaceVariant = Color(0xFF43474E),
|
||||
inverseSurface = Color(0xFF2F3033),
|
||||
inverseOnSurface = Color(0xFFF1F0F3),
|
||||
error = Color(0xFFBA1A1A),
|
||||
onError = Color(0xFFFFFFFF),
|
||||
errorContainer = Color(0xFFFFDAD5),
|
||||
onErrorContainer = Color(0xFF410002),
|
||||
outline = Color(0xFF73777F),
|
||||
outlineVariant = Color(0xFFC3C6CF),
|
||||
scrim = Color(0xFF000000),
|
||||
background = Color(0xFFFDFCFF),
|
||||
onBackground = Color(0xFF1A1C1E),
|
||||
surfaceVariant = Color(0xFFDFE2EB),
|
||||
)
|
||||
|
||||
val DarkDefaultColorScheme = darkColorScheme(
|
||||
primary = Color(0xFFA5C9F8),
|
||||
onPrimary = Color(0xFF023258),
|
||||
primaryContainer = Color(0xFF224970),
|
||||
onPrimaryContainer = Color(0xFFD2E4FF),
|
||||
inversePrimary = Color(0xFF3D608A),
|
||||
secondary = Color(0xFFBBC7DB),
|
||||
onSecondary = Color(0xFF253141),
|
||||
secondaryContainer = Color(0xFF3C4858),
|
||||
onSecondaryContainer = Color(0xFFD7E3F8),
|
||||
tertiary = Color(0xFFD6BEE4),
|
||||
onTertiary = Color(0xFF3B2947),
|
||||
tertiaryContainer = Color(0xFF523F5F),
|
||||
onTertiaryContainer = Color(0xFFF3DAFF),
|
||||
surface = Color(0xFF1A1C1E),
|
||||
onSurface = Color(0xFFE3E2E5),
|
||||
onSurfaceVariant = Color(0xFFC3C6CF),
|
||||
inverseSurface = Color(0xFFE3E2E5),
|
||||
inverseOnSurface = Color(0xFF2F3033),
|
||||
error = Color(0xFFFFB4AB),
|
||||
onError = Color(0xFF690004),
|
||||
errorContainer = Color(0xFF930009),
|
||||
onErrorContainer = Color(0xFFFFB4AB),
|
||||
outline = Color(0xFF8D9199),
|
||||
outlineVariant = Color(0xFF43474E),
|
||||
scrim = Color(0xFF000000),
|
||||
background = Color(0xFF1A1C1E),
|
||||
onBackground = Color(0xFFE3E2E5),
|
||||
surfaceVariant = Color(0xFF43474E),
|
||||
)
|
||||
@@ -1,67 +0,0 @@
|
||||
package de.mm20.launcher2.ui.theme.colorscheme
|
||||
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val LightEasterEggColorScheme = lightColorScheme(
|
||||
primary = Color(0xFFB40180),
|
||||
onPrimary = Color(0xFFFFFFFF),
|
||||
primaryContainer = Color(0xFFFFD8E9),
|
||||
onPrimaryContainer = Color(0xFF3C0029),
|
||||
inversePrimary = Color(0xFFFFAFD7),
|
||||
secondary = Color(0xFF725763),
|
||||
onSecondary = Color(0xFFFFFFFF),
|
||||
secondaryContainer = Color(0xFFFDD9E7),
|
||||
onSecondaryContainer = Color(0xFF29151F),
|
||||
tertiary = Color(0xFF7F543B),
|
||||
onTertiary = Color(0xFFFFFFFF),
|
||||
tertiaryContainer = Color(0xFFFFDBC9),
|
||||
onTertiaryContainer = Color(0xFF311302),
|
||||
surface = Color(0xFFFCF8FC),
|
||||
onSurface = Color(0xFF1C1B1E),
|
||||
onSurfaceVariant = Color(0xFF4F4448),
|
||||
inverseSurface = Color(0xFF313033),
|
||||
inverseOnSurface = Color(0xFFF3EFF3),
|
||||
error = Color(0xFFBA1A1A),
|
||||
onError = Color(0xFFFFFFFF),
|
||||
errorContainer = Color(0xFFFFDAD5),
|
||||
onErrorContainer = Color(0xFF410002),
|
||||
outline = Color(0xFF817379),
|
||||
outlineVariant = Color(0xFFD3C2C8),
|
||||
scrim = Color(0xFF000000),
|
||||
background = Color(0xFFFFFBFF),
|
||||
onBackground = Color(0xFF1C1B1E),
|
||||
surfaceVariant = Color(0xFFF0DEE4),
|
||||
)
|
||||
|
||||
val DarkEasterEggColorScheme = darkColorScheme(
|
||||
primary = Color(0xFFFFAFD7),
|
||||
onPrimary = Color(0xFF620044),
|
||||
primaryContainer = Color(0xFF8A0061),
|
||||
onPrimaryContainer = Color(0xFFFFD8E9),
|
||||
inversePrimary = Color(0xFFB40180),
|
||||
secondary = Color(0xFFE0BDCB),
|
||||
onSecondary = Color(0xFF402A35),
|
||||
secondaryContainer = Color(0xFF59404B),
|
||||
onSecondaryContainer = Color(0xFFFDD9E7),
|
||||
tertiary = Color(0xFFF3BA9B),
|
||||
onTertiary = Color(0xFF4A2812),
|
||||
tertiaryContainer = Color(0xFF643D26),
|
||||
onTertiaryContainer = Color(0xFFFFDBC9),
|
||||
surface = Color(0xFF1C1B1E),
|
||||
onSurface = Color(0xFFE5E1E5),
|
||||
onSurfaceVariant = Color(0xFFD3C2C8),
|
||||
inverseSurface = Color(0xFFE5E1E5),
|
||||
inverseOnSurface = Color(0xFF313033),
|
||||
error = Color(0xFFFFB4AB),
|
||||
onError = Color(0xFF690004),
|
||||
errorContainer = Color(0xFF930009),
|
||||
onErrorContainer = Color(0xFFFFB4AB),
|
||||
outline = Color(0xFF9C8D92),
|
||||
outlineVariant = Color(0xFF4F4448),
|
||||
scrim = Color(0xFF000000),
|
||||
background = Color(0xFF1C1B1E),
|
||||
onBackground = Color(0xFFE5E1E5),
|
||||
surfaceVariant = Color(0xFF4F4448),
|
||||
)
|
||||
@@ -1,56 +0,0 @@
|
||||
package de.mm20.launcher2.ui.theme.colorscheme
|
||||
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import de.mm20.launcher2.ui.theme.WallpaperColors
|
||||
import palettes.TonalPalette
|
||||
import scheme.Scheme
|
||||
|
||||
fun MaterialYouCompatScheme(wallpaperColors: WallpaperColors, darkTheme: Boolean): ColorScheme {
|
||||
val scheme = if (darkTheme) {
|
||||
Scheme.dark(wallpaperColors.primary.toArgb())
|
||||
} else {
|
||||
Scheme.light(wallpaperColors.primary.toArgb())
|
||||
}
|
||||
return ColorScheme(
|
||||
primary = Color(scheme.primary),
|
||||
onPrimary = Color(scheme.onPrimary),
|
||||
primaryContainer = Color(scheme.primaryContainer),
|
||||
onPrimaryContainer = Color(scheme.onPrimaryContainer),
|
||||
secondary = Color(scheme.secondary),
|
||||
onSecondary = Color(scheme.onSecondary),
|
||||
secondaryContainer = Color(scheme.secondaryContainer),
|
||||
onSecondaryContainer = Color(scheme.onSecondaryContainer),
|
||||
tertiary = Color(scheme.tertiary),
|
||||
onTertiary = Color(scheme.onTertiary),
|
||||
tertiaryContainer = Color(scheme.tertiaryContainer),
|
||||
onTertiaryContainer = Color(scheme.onTertiaryContainer),
|
||||
background = Color(scheme.background),
|
||||
onBackground = Color(scheme.onBackground),
|
||||
surface = Color(scheme.surface),
|
||||
onSurface = Color(scheme.onSurface),
|
||||
surfaceVariant = Color(scheme.surfaceVariant),
|
||||
onSurfaceVariant = Color(scheme.onSurfaceVariant),
|
||||
outline = Color(scheme.outline),
|
||||
inverseSurface = Color(scheme.inverseSurface),
|
||||
inverseOnSurface = Color(scheme.inverseOnSurface),
|
||||
inversePrimary = Color(scheme.inversePrimary),
|
||||
surfaceTint = Color(scheme.primary),
|
||||
error = Color(scheme.error),
|
||||
onError = Color(scheme.onError),
|
||||
errorContainer = Color(scheme.errorContainer),
|
||||
onErrorContainer = Color(scheme.onErrorContainer),
|
||||
scrim = Color(scheme.scrim),
|
||||
outlineVariant = Color(scheme.outlineVariant),
|
||||
surfaceBright = Color(scheme.surfaceBright),
|
||||
surfaceContainer = Color(scheme.surfaceContainer),
|
||||
surfaceContainerHigh = Color(scheme.surfaceContainerHigh),
|
||||
surfaceContainerHighest = Color(scheme.surfaceContainerHighest),
|
||||
surfaceContainerLow = Color(scheme.surfaceContainerLow),
|
||||
surfaceContainerLowest = Color(scheme.surfaceContainerLowest),
|
||||
surfaceDim = Color(scheme.surfaceDim),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package de.mm20.launcher2.ui.theme.shapes
|
||||
|
||||
import androidx.compose.foundation.shape.CornerBasedShape
|
||||
import androidx.compose.foundation.shape.CornerSize
|
||||
import androidx.compose.foundation.shape.CutCornerShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Shapes
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.mm20.launcher2.themes.CornerStyle
|
||||
import de.mm20.launcher2.themes.Shape as ThemeShape
|
||||
import de.mm20.launcher2.themes.Shapes as ThemeShapes
|
||||
|
||||
@Composable
|
||||
fun shapesOf(shapes: ThemeShapes): Shapes {
|
||||
return remember(shapes) {
|
||||
Shapes(
|
||||
extraSmall = fromShape(shapes.extraSmall, shapes.baseShape, 1f / 3f),
|
||||
small = fromShape(shapes.small, shapes.baseShape, 2f / 3f),
|
||||
medium = fromShape(shapes.medium, shapes.baseShape, 1f),
|
||||
large = fromShape(shapes.large, shapes.baseShape, 4f / 3f),
|
||||
largeIncreased = fromShape(shapes.largeIncreased, shapes.baseShape, 5f / 3f),
|
||||
extraLarge = fromShape(shapes.extraLarge, shapes.baseShape, 7f / 3f),
|
||||
extraLargeIncreased = fromShape(shapes.extraLargeIncreased, shapes.baseShape, 8f / 3f),
|
||||
extraExtraLarge = fromShape(shapes.extraExtraLarge, shapes.baseShape, 12f / 3f),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fromShape(shape: ThemeShape?, baseShape: ThemeShape, factor: Float): CornerBasedShape {
|
||||
val topStart = getCornerRadius(shape, baseShape, factor, 0)
|
||||
val topEnd = getCornerRadius(shape, baseShape, factor, 1)
|
||||
val bottomEnd = getCornerRadius(shape, baseShape, factor, 2)
|
||||
val bottomStart = getCornerRadius(shape, baseShape, factor, 3)
|
||||
|
||||
return if ((shape?.corners ?: baseShape.corners) == CornerStyle.Cut) {
|
||||
CutCornerShape(
|
||||
topStart = topStart,
|
||||
topEnd = topEnd,
|
||||
bottomEnd = bottomEnd,
|
||||
bottomStart = bottomStart
|
||||
)
|
||||
} else {
|
||||
RoundedCornerShape(
|
||||
topStart = topStart,
|
||||
topEnd = topEnd,
|
||||
bottomEnd = bottomEnd,
|
||||
bottomStart = bottomStart
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCornerRadius(
|
||||
shape: ThemeShape?,
|
||||
baseShape: ThemeShape,
|
||||
factor: Float,
|
||||
index: Int
|
||||
): CornerSize {
|
||||
return CornerSize(
|
||||
(shape?.radii?.get(index)?.toFloat() ?: ((baseShape.radii?.get(index)?.toFloat()
|
||||
?: 12f) * factor)).dp
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user