refactor more bottom sheets

This commit is contained in:
MM20
2026-01-20 23:42:15 +01:00
parent c7235df62b
commit c3479bc6bf
11 changed files with 1176 additions and 1096 deletions

View File

@@ -1,16 +1,22 @@
package de.mm20.launcher2.ui.common package de.mm20.launcher2.ui.common
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material3.DockedSearchBar
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -18,6 +24,8 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
@@ -32,81 +40,122 @@ import de.mm20.launcher2.ui.ktx.toPixels
@Composable @Composable
fun SearchablePicker( fun SearchablePicker(
expanded: Boolean,
value: SavableSearchable?, value: SavableSearchable?,
onValueChanged: (SavableSearchable?) -> Unit, onValueChanged: (SavableSearchable?) -> Unit,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
) { ) {
val viewModel: SearchablePickerVM = viewModel() DismissableBottomSheet(expanded = expanded, onDismissRequest = onDismissRequest) {
val viewModel: SearchablePickerVM = viewModel()
DismissableBottomSheet(onDismissRequest = onDismissRequest) { val colorSurface = MaterialTheme.colorScheme.surfaceContainerLow
Column(
LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize(),
.padding(it) contentPadding = PaddingValues(
) { start = 16.dp,
OutlinedTextField( end = 16.dp,
modifier = Modifier bottom = 16.dp + WindowInsets.navigationBars.asPaddingValues()
.fillMaxWidth() .calculateBottomPadding(),
.padding(bottom = 12.dp), top = 16.dp,
value = viewModel.searchQuery,
onValueChange = { viewModel.onSearchQueryChanged(it) },
leadingIcon = {
Icon(painterResource(R.drawable.search_24px), null)
},
placeholder = {
Text(stringResource(R.string.search_bar_placeholder))
},
singleLine = true,
) )
LazyColumn( ) {
modifier = Modifier stickyHeader {
.weight(1f) DockedSearchBar(
.fillMaxWidth(), modifier = Modifier
) { .fillMaxWidth()
items(viewModel.items) { .drawBehind {
val iconSize = 32.dp.toPixels() drawRect(
val icon by remember(it.key) { brush = Brush.verticalGradient(
viewModel.getIcon( 0.5f to colorSurface,
it, 0.5f to colorSurface.copy(alpha = 0f),
iconSize.toInt()
)
}.collectAsStateWithLifecycle(null)
val selected = it.key == value?.key
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
shape = MaterialTheme.shapes.small,
border = BorderStroke(1.dp, if (selected) MaterialTheme.colorScheme.secondary else MaterialTheme.colorScheme.outline),
color = if (selected) MaterialTheme.colorScheme.secondaryContainer else MaterialTheme.colorScheme.surface,
onClick = { onValueChanged(it) }) {
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
ShapedLauncherIcon(
icon = { icon },
size = 32.dp,
)
Text(
text = it.label,
modifier = Modifier.weight(1f).padding(horizontal = 16.dp),
style = MaterialTheme.typography.labelMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
if (selected) {
Icon(
painterResource(R.drawable.check_circle_24px_filled),
contentDescription = null,
tint = MaterialTheme.colorScheme.secondary,
) )
} )
}
.padding(bottom = 16.dp),
expanded = false,
onExpandedChange = {},
inputField = {
SearchBarDefaults.InputField(
leadingIcon = {
Icon(
painterResource(R.drawable.search_24px),
contentDescription = null
)
},
trailingIcon = {
if (viewModel.searchQuery.isNotEmpty()) {
IconButton(
onClick = {
viewModel.onSearchQueryChanged("")
}) {
Icon(painterResource(R.drawable.close_24px), null)
}
}
},
onSearch = {},
expanded = false,
onExpandedChange = {},
query = viewModel.searchQuery,
onQueryChange = {
viewModel.onSearchQueryChanged(it)
},
placeholder = {
Text(stringResource(R.string.search_bar_placeholder))
},
)
}
) {
}
}
items(viewModel.items) {
val iconSize = 32.dp.toPixels()
val icon by remember(it.key) {
viewModel.getIcon(
it,
iconSize.toInt()
)
}.collectAsStateWithLifecycle(null)
val selected = it.key == value?.key
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
shape = MaterialTheme.shapes.small,
border = BorderStroke(
1.dp,
if (selected) MaterialTheme.colorScheme.secondary else MaterialTheme.colorScheme.outline
),
color = if (selected) MaterialTheme.colorScheme.secondaryContainer else MaterialTheme.colorScheme.surface,
onClick = { onValueChanged(it) }) {
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
ShapedLauncherIcon(
icon = { icon },
size = 32.dp,
)
Text(
text = it.label,
modifier = Modifier
.weight(1f)
.padding(horizontal = 16.dp),
style = MaterialTheme.typography.labelMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
if (selected) {
Icon(
painterResource(R.drawable.check_circle_24px_filled),
contentDescription = null,
tint = MaterialTheme.colorScheme.secondary,
)
} }
} }
} }
} }
} }
} }
} }

View File

@@ -67,7 +67,7 @@ import kotlinx.coroutines.CancellationException
fun DismissableBottomSheet( fun DismissableBottomSheet(
expanded: Boolean = true, expanded: Boolean = true,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
content: @Composable (contentPadding: PaddingValues) -> Unit, content: @Composable (contentPadding: PaddingValues) -> Unit,
) { ) {
DismissableBottomSheet( DismissableBottomSheet(
state = expanded, state = expanded,

View File

@@ -272,19 +272,18 @@ fun CustomizeSearchableSheet(
} }
} }
} }
if (pickIcon) { DismissableBottomSheet(
DismissableBottomSheet( expanded = pickIcon,
onDismissRequest = { viewModel.closeIconPicker() }, onDismissRequest = { viewModel.closeIconPicker() },
) { ) {
IconPicker( IconPicker(
searchable = searchable, searchable = searchable,
onSelect = { onSelect = {
viewModel.pickIcon(it) viewModel.pickIcon(it)
viewModel.closeIconPicker() viewModel.closeIconPicker()
}, },
contentPadding = it, contentPadding = it,
) )
}
} }
} }
} }

View File

@@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentHeight
@@ -123,19 +124,18 @@ fun NotesWidget(
} }
}, },
) )
if (showConflictResolveSheet) { NoteWidgetConflictResolveSheet(
NoteWidgetConflictResolveSheet( expanded = showConflictResolveSheet,
localContent = widget.config.storedText, localContent = widget.config.storedText,
fileContent = text.text, fileContent = text.text,
onResolve = { onResolve = {
viewModel.resolveFileContentConflict(context, it) viewModel.resolveFileContentConflict(context, it)
showConflictResolveSheet = false showConflictResolveSheet = false
}, },
onDismissRequest = { onDismissRequest = {
showConflictResolveSheet = false showConflictResolveSheet = false
} }
) )
}
return return
} }
Column { Column {
@@ -374,44 +374,44 @@ fun NotesWidget(
} }
} }
if (readWriteErrorSheetText != null) { NoteReadWriteErrorSheet(
NoteReadWriteErrorSheet( message = readWriteErrorSheetText,
message = readWriteErrorSheetText!!, onDismiss = { readWriteErrorSheetText = null },
onDismiss = { readWriteErrorSheetText = null }, onRelink = {
onRelink = { linkFileLauncher.launch(
linkFileLauncher.launch( context.getString(
context.getString( R.string.notes_widget_export_filename,
R.string.notes_widget_export_filename, ZonedDateTime.now().format(
ZonedDateTime.now().format( DateTimeFormatter.ISO_INSTANT
DateTimeFormatter.ISO_INSTANT
)
) )
) )
}, )
onUnlink = { },
viewModel.unlinkFile(context) onUnlink = {
} viewModel.unlinkFile(context)
) }
} )
} }
@Composable @Composable
fun NoteWidgetConflictResolveSheet( fun NoteWidgetConflictResolveSheet(
expanded: Boolean,
localContent: String, localContent: String,
fileContent: String, fileContent: String,
onResolve: (LinkedFileConflictStrategy) -> Unit, onResolve: (LinkedFileConflictStrategy) -> Unit,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
) { ) {
var selectedStrategy by remember { mutableStateOf<LinkedFileConflictStrategy?>(null) }
DismissableBottomSheet( DismissableBottomSheet(
expanded = expanded,
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
) { ) {
var selectedStrategy by remember { mutableStateOf<LinkedFileConflictStrategy?>(null) }
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(it), .padding(16.dp)
.navigationBarsPadding(),
) { ) {
Text( Text(
@@ -538,19 +538,24 @@ fun SelectableNoteContent(
@Composable @Composable
fun NoteReadWriteErrorSheet( fun NoteReadWriteErrorSheet(
message: String, message: String?,
onDismiss: () -> Unit, onDismiss: () -> Unit,
onRelink: () -> Unit, onRelink: () -> Unit,
onUnlink: () -> Unit, onUnlink: () -> Unit,
) { ) {
DismissableBottomSheet( DismissableBottomSheet(
state = message,
expanded = { it != null },
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
) { ) { message ->
message ?: return@DismissableBottomSheet
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(it) .padding(16.dp)
.navigationBarsPadding()
) { ) {
Text( Text(
text = message, text = message,

View File

@@ -8,6 +8,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
@@ -51,7 +52,8 @@ fun CorePaletteColorPreference(
var showDialog by remember { mutableStateOf(false) } var showDialog by remember { mutableStateOf(false) }
Row( Row(
modifier = modifier.fillMaxWidth() modifier = modifier
.fillMaxWidth()
.clip(MaterialTheme.shapes.extraSmall) .clip(MaterialTheme.shapes.extraSmall)
.clickable( .clickable(
onClick = { showDialog = true }, onClick = { showDialog = true },
@@ -62,7 +64,9 @@ fun CorePaletteColorPreference(
) { ) {
ColorSwatch( ColorSwatch(
color = Color(value ?: defaultValue), color = Color(value ?: defaultValue),
modifier = Modifier.padding(end = 20.dp).size(48.dp), modifier = Modifier
.padding(end = 20.dp)
.size(48.dp),
) )
Text( Text(
@@ -74,78 +78,79 @@ fun CorePaletteColorPreference(
) )
} }
if (showDialog) { var currentValue by remember { mutableStateOf(value) }
var currentValue by remember { mutableStateOf(value) } DismissableBottomSheet(
DismissableBottomSheet(onDismissRequest = { expanded = showDialog,
onDismissRequest = {
onValueChange(currentValue) onValueChange(currentValue)
showDialog = false showDialog = false
}) { }) {
Column( Column(
modifier = Modifier modifier = Modifier
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(it) .padding(16.dp)
) { .navigationBarsPadding()
SwitchPreference( ) {
icon = R.drawable.rule_settings_24px, SwitchPreference(
title = stringResource(R.string.theme_color_scheme_system_default), icon = R.drawable.rule_settings_24px,
value = currentValue == null, title = stringResource(R.string.theme_color_scheme_system_default),
onValueChanged = { value = currentValue == null,
currentValue = if (it) null else defaultValue onValueChanged = {
}, currentValue = if (it) null else defaultValue
containerColor = Color.Transparent, },
containerColor = Color.Transparent,
)
AnimatedVisibility(
currentValue != null,
enter = expandVertically(
expandFrom = Alignment.Top,
),
exit = shrinkVertically(
shrinkTowards = Alignment.Top,
) )
AnimatedVisibility( ) {
currentValue != null, Column {
enter = expandVertically( HorizontalDivider(
expandFrom = Alignment.Top, modifier = Modifier.padding(bottom = 24.dp)
),
exit = shrinkVertically(
shrinkTowards = Alignment.Top,
) )
) { val colorPickerState = rememberHctColorPickerState(
Column { initialColor = Color(value ?: defaultValue),
onColorChanged = {
currentValue = it.toArgb()
}
)
HctColorPicker(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.CenterHorizontally),
state = colorPickerState,
)
if (autoGenerate != null) {
HorizontalDivider( HorizontalDivider(
modifier = Modifier.padding(bottom = 24.dp) modifier = Modifier.padding(top = 16.dp)
) )
val colorPickerState = rememberHctColorPickerState(
initialColor = Color(value ?: defaultValue), TextButton(
onColorChanged = {
currentValue = it.toArgb()
}
)
HctColorPicker(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .padding(top = 8.dp)
.align(Alignment.CenterHorizontally), .align(Alignment.End),
state = colorPickerState, contentPadding = ButtonDefaults.TextButtonWithIconContentPadding,
) onClick = {
val autoGenerated = autoGenerate()
if (autoGenerate != null) { currentValue = autoGenerated
HorizontalDivider( if (autoGenerated != null) {
modifier = Modifier.padding(top = 16.dp) colorPickerState.setColor(Color(autoGenerated))
)
TextButton(
modifier = Modifier
.padding(top = 8.dp)
.align(Alignment.End),
contentPadding = ButtonDefaults.TextButtonWithIconContentPadding,
onClick = {
val autoGenerated = autoGenerate()
currentValue = autoGenerated
if (autoGenerated != null) {
colorPickerState.setColor(Color(autoGenerated))
}
} }
) {
Icon(
painterResource(R.drawable.wand_stars_20px), null,
modifier = Modifier
.padding(ButtonDefaults.IconSpacing)
.size(ButtonDefaults.IconSize)
)
Text(stringResource(R.string.theme_color_scheme_autogenerate))
} }
) {
Icon(
painterResource(R.drawable.wand_stars_20px), null,
modifier = Modifier
.padding(ButtonDefaults.IconSpacing)
.size(ButtonDefaults.IconSize)
)
Text(stringResource(R.string.theme_color_scheme_autogenerate))
} }
} }
} }

View File

@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
@@ -74,7 +75,8 @@ fun ThemeColorPreference(
var showDialog by remember { mutableStateOf(false) } var showDialog by remember { mutableStateOf(false) }
Row( Row(
modifier = modifier.fillMaxWidth() modifier = modifier
.fillMaxWidth()
.clip(MaterialTheme.shapes.extraSmall) .clip(MaterialTheme.shapes.extraSmall)
.clickable( .clickable(
onClick = { showDialog = true }, onClick = { showDialog = true },
@@ -85,7 +87,9 @@ fun ThemeColorPreference(
) { ) {
ColorSwatch( ColorSwatch(
color = Color((value ?: defaultValue).get(corePalette)), color = Color((value ?: defaultValue).get(corePalette)),
modifier = Modifier.padding(end = 20.dp).size(48.dp), modifier = Modifier
.padding(end = 20.dp)
.size(48.dp),
) )
Text( Text(
@@ -97,337 +101,338 @@ fun ThemeColorPreference(
) )
} }
if (showDialog) { var currentValue by remember { mutableStateOf(value) }
var currentValue by remember { mutableStateOf(value) }
val actualValue = currentValue ?: defaultValue DismissableBottomSheet(
DismissableBottomSheet(onDismissRequest = { expanded = showDialog,
onDismissRequest = {
onValueChange(currentValue) onValueChange(currentValue)
showDialog = false showDialog = false
}) { }) {
Column( val actualValue = currentValue ?: defaultValue
modifier = Modifier Column(
.fillMaxWidth() modifier = Modifier
.verticalScroll(rememberScrollState()) .fillMaxWidth()
.padding(it), .verticalScroll(rememberScrollState())
.padding(16.dp)
.navigationBarsPadding(),
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween)
) { ) {
Row( ToggleButton(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.weight(1f),
horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween) checked = actualValue is ColorRef,
onCheckedChange = {
if (actualValue is ColorRef) return@ToggleButton
currentValue = defaultValue
},
shapes = ButtonGroupDefaults.connectedLeadingButtonShapes()
) { ) {
ToggleButton( Icon(
modifier = Modifier.weight(1f), painterResource(
checked = actualValue is ColorRef, if (actualValue is ColorRef) R.drawable.check_20px else R.drawable.palette_20px
onCheckedChange = { ),
if (actualValue is ColorRef) return@ToggleButton null,
currentValue = defaultValue modifier = Modifier
}, .padding(end = ToggleButtonDefaults.IconSpacing)
shapes = ButtonGroupDefaults.connectedLeadingButtonShapes() .size(ToggleButtonDefaults.IconSize)
) { )
Icon( Text(stringResource(R.string.theme_color_scheme_palette_color))
painterResource(
if (actualValue is ColorRef) R.drawable.check_20px else R.drawable.palette_20px
),
null,
modifier = Modifier
.padding(end = ToggleButtonDefaults.IconSpacing)
.size(ToggleButtonDefaults.IconSize)
)
Text(stringResource(R.string.theme_color_scheme_palette_color))
}
ToggleButton(
modifier = Modifier.weight(1f),
checked = actualValue is StaticColor,
onCheckedChange = {
currentValue = StaticColor(actualValue.get(corePalette))
},
shapes = ButtonGroupDefaults.connectedTrailingButtonShapes()
) {
Icon(
painterResource(
if (actualValue is StaticColor) R.drawable.check_20px else R.drawable.colorize_20px
),
null,
modifier = Modifier
.padding(end = ToggleButtonDefaults.IconSpacing)
.size(ToggleButtonDefaults.IconSize)
)
Text(stringResource(R.string.theme_color_scheme_custom_color))
}
} }
AnimatedContent( ToggleButton(
actualValue, modifier = Modifier.weight(1f),
label = "AnimatedContent", checked = actualValue is StaticColor,
contentKey = { it is StaticColor } onCheckedChange = {
) { themeColor -> currentValue = StaticColor(actualValue.get(corePalette))
Column { },
if (themeColor is StaticColor) { shapes = ButtonGroupDefaults.connectedTrailingButtonShapes()
val colorPickerState = rememberHctColorPickerState( ) {
initialColor = Color(themeColor.color), Icon(
onColorChanged = { painterResource(
currentValue = StaticColor(it.toArgb()) if (actualValue is StaticColor) R.drawable.check_20px else R.drawable.colorize_20px
} ),
) null,
HctColorPicker( modifier = Modifier
modifier = Modifier .padding(end = ToggleButtonDefaults.IconSpacing)
.fillMaxWidth() .size(ToggleButtonDefaults.IconSize)
.padding(top = 24.dp) )
.align(Alignment.CenterHorizontally), Text(stringResource(R.string.theme_color_scheme_custom_color))
state = colorPickerState }
) }
} else if (themeColor is ColorRef) { AnimatedContent(
val hct = Hct.fromInt(corePalette.get(themeColor.color)) actualValue,
val hue = hct.hue.toFloat() label = "AnimatedContent",
val chroma = hct.chroma.toFloat() contentKey = { it is StaticColor }
var tone by remember(value == null) { mutableStateOf(themeColor.tone.toFloat()) } ) { themeColor ->
Row( Column {
modifier = Modifier.padding(top = 24.dp, bottom = 8.dp) if (themeColor is StaticColor) {
) { val colorPickerState = rememberHctColorPickerState(
ColorSwatch( initialColor = Color(themeColor.color),
color = Color( onColorChanged = {
corePalette currentValue = StaticColor(it.toArgb())
.get(CorePaletteColor.Primary)
.atTone(tone.toInt())
),
modifier = Modifier
.padding(8.dp)
.size(64.dp)
.clickable {
currentValue = ColorRef(
CorePaletteColor.Primary,
tone.roundToInt()
)
},
selected = themeColor.color == CorePaletteColor.Primary,
)
Spacer(modifier = Modifier.weight(1f))
ColorSwatch(
color = Color(
corePalette
.get(CorePaletteColor.Secondary)
.atTone(tone.toInt())
),
modifier = Modifier
.padding(8.dp)
.size(64.dp)
.clickable {
currentValue = ColorRef(
CorePaletteColor.Secondary,
tone.roundToInt()
)
},
selected = themeColor.color == CorePaletteColor.Secondary,
)
Spacer(modifier = Modifier.weight(1f))
ColorSwatch(
color = Color(
corePalette
.get(CorePaletteColor.Tertiary)
.atTone(tone.toInt())
),
modifier = Modifier
.padding(8.dp)
.size(64.dp)
.clickable {
currentValue = ColorRef(
CorePaletteColor.Tertiary,
tone.roundToInt()
)
},
selected = themeColor.color == CorePaletteColor.Tertiary,
)
} }
Row( )
modifier = Modifier.padding(bottom = 16.dp) HctColorPicker(
) { modifier = Modifier
ColorSwatch( .fillMaxWidth()
color = Color( .padding(top = 24.dp)
corePalette .align(Alignment.CenterHorizontally),
.get(CorePaletteColor.Neutral) state = colorPickerState
.atTone(tone.toInt()) )
), } else if (themeColor is ColorRef) {
modifier = Modifier val hct = Hct.fromInt(corePalette.get(themeColor.color))
.padding(8.dp) val hue = hct.hue.toFloat()
.size(64.dp) val chroma = hct.chroma.toFloat()
.clickable { var tone by remember(value == null) { mutableStateOf(themeColor.tone.toFloat()) }
currentValue = ColorRef( Row(
CorePaletteColor.Neutral, modifier = Modifier.padding(top = 24.dp, bottom = 8.dp)
tone.roundToInt() ) {
) ColorSwatch(
}, color = Color(
selected = themeColor.color == CorePaletteColor.Neutral, corePalette
) .get(CorePaletteColor.Primary)
Spacer(modifier = Modifier.weight(1f)) .atTone(tone.toInt())
ColorSwatch( ),
color = Color(
corePalette
.get(CorePaletteColor.NeutralVariant)
.atTone(tone.toInt())
),
modifier = Modifier
.padding(8.dp)
.size(64.dp)
.clickable {
currentValue = ColorRef(
CorePaletteColor.NeutralVariant,
tone.roundToInt()
)
},
selected = themeColor.color == CorePaletteColor.NeutralVariant,
)
Spacer(modifier = Modifier.weight(1f))
ColorSwatch(
color = Color(
corePalette
.get(CorePaletteColor.Error)
.atTone(tone.toInt())
),
modifier = Modifier
.padding(8.dp)
.size(64.dp)
.clickable {
currentValue = ColorRef(
CorePaletteColor.Error,
tone.roundToInt()
)
},
selected = themeColor.color == CorePaletteColor.Error,
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .padding(8.dp)
.padding(top = 16.dp) .size(64.dp)
) { .clickable {
Text( currentValue = ColorRef(
text = "T", CorePaletteColor.Primary,
modifier = Modifier.width(32.dp), tone.roundToInt()
style = MaterialTheme.typography.labelMedium, )
textAlign = TextAlign.Center
)
Slider(
modifier = Modifier.weight(1f),
value = tone,
valueRange = 0f..100f,
onValueChange = {
tone = it
currentValue = themeColor.copy(tone = it.roundToInt())
}, },
track = { selected = themeColor.color == CorePaletteColor.Primary,
Canvas( )
modifier = Modifier Spacer(modifier = Modifier.weight(1f))
.fillMaxWidth() ColorSwatch(
.height(20.dp) color = Color(
) { corePalette
drawRoundRect( .get(CorePaletteColor.Secondary)
brush = Brush.horizontalGradient( .atTone(tone.toInt())
colors = listOf( ),
Color.hct( modifier = Modifier
hue, .padding(8.dp)
chroma, .size(64.dp)
0f .clickable {
), currentValue = ColorRef(
Color.hct( CorePaletteColor.Secondary,
hue, tone.roundToInt()
chroma, )
10f },
), selected = themeColor.color == CorePaletteColor.Secondary,
Color.hct( )
hue, Spacer(modifier = Modifier.weight(1f))
chroma, ColorSwatch(
20f color = Color(
), corePalette
Color.hct( .get(CorePaletteColor.Tertiary)
hue, .atTone(tone.toInt())
chroma, ),
30f modifier = Modifier
), .padding(8.dp)
Color.hct( .size(64.dp)
hue, .clickable {
chroma, currentValue = ColorRef(
40f CorePaletteColor.Tertiary,
), tone.roundToInt()
Color.hct( )
hue, },
chroma, selected = themeColor.color == CorePaletteColor.Tertiary,
50f )
), }
Color.hct( Row(
hue, modifier = Modifier.padding(bottom = 16.dp)
chroma, ) {
60f ColorSwatch(
), color = Color(
Color.hct( corePalette
hue, .get(CorePaletteColor.Neutral)
chroma, .atTone(tone.toInt())
70f ),
), modifier = Modifier
Color.hct( .padding(8.dp)
hue, .size(64.dp)
chroma, .clickable {
80f currentValue = ColorRef(
), CorePaletteColor.Neutral,
Color.hct( tone.roundToInt()
hue, )
chroma, },
90f selected = themeColor.color == CorePaletteColor.Neutral,
), )
Color.hct( Spacer(modifier = Modifier.weight(1f))
hue, ColorSwatch(
chroma, color = Color(
100f corePalette
), .get(CorePaletteColor.NeutralVariant)
) .atTone(tone.toInt())
), ),
style = Fill, modifier = Modifier
cornerRadius = CornerRadius( .padding(8.dp)
10.dp.toPx(), .size(64.dp)
10.dp.toPx() .clickable {
currentValue = ColorRef(
CorePaletteColor.NeutralVariant,
tone.roundToInt()
)
},
selected = themeColor.color == CorePaletteColor.NeutralVariant,
)
Spacer(modifier = Modifier.weight(1f))
ColorSwatch(
color = Color(
corePalette
.get(CorePaletteColor.Error)
.atTone(tone.toInt())
),
modifier = Modifier
.padding(8.dp)
.size(64.dp)
.clickable {
currentValue = ColorRef(
CorePaletteColor.Error,
tone.roundToInt()
)
},
selected = themeColor.color == CorePaletteColor.Error,
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp)
) {
Text(
text = "T",
modifier = Modifier.width(32.dp),
style = MaterialTheme.typography.labelMedium,
textAlign = TextAlign.Center
)
Slider(
modifier = Modifier.weight(1f),
value = tone,
valueRange = 0f..100f,
onValueChange = {
tone = it
currentValue = themeColor.copy(tone = it.roundToInt())
},
track = {
Canvas(
modifier = Modifier
.fillMaxWidth()
.height(20.dp)
) {
drawRoundRect(
brush = Brush.horizontalGradient(
colors = listOf(
Color.hct(
hue,
chroma,
0f
),
Color.hct(
hue,
chroma,
10f
),
Color.hct(
hue,
chroma,
20f
),
Color.hct(
hue,
chroma,
30f
),
Color.hct(
hue,
chroma,
40f
),
Color.hct(
hue,
chroma,
50f
),
Color.hct(
hue,
chroma,
60f
),
Color.hct(
hue,
chroma,
70f
),
Color.hct(
hue,
chroma,
80f
),
Color.hct(
hue,
chroma,
90f
),
Color.hct(
hue,
chroma,
100f
),
) )
),
style = Fill,
cornerRadius = CornerRadius(
10.dp.toPx(),
10.dp.toPx()
) )
}
},
thumb = {
Box(
modifier = Modifier
.padding(vertical = 2.dp, horizontal = 8.dp)
.size(16.dp)
.shadow(1.dp, CircleShape)
.clip(CircleShape)
.background(Color.White)
) )
} }
) },
Text( thumb = {
text = tone.roundToInt().toString(), Box(
modifier = Modifier.width(32.dp), modifier = Modifier
style = MaterialTheme.typography.labelMedium, .padding(vertical = 2.dp, horizontal = 8.dp)
textAlign = TextAlign.Center, .size(16.dp)
) .shadow(1.dp, CircleShape)
} .clip(CircleShape)
} .background(Color.White)
HorizontalDivider( )
modifier = Modifier.padding(top = 16.dp) }
) )
Text(
TextButton( text = tone.roundToInt().toString(),
modifier = Modifier modifier = Modifier.width(32.dp),
.padding(top = 8.dp) style = MaterialTheme.typography.labelMedium,
.align(Alignment.End), textAlign = TextAlign.Center,
contentPadding = ButtonDefaults.TextButtonWithIconContentPadding,
onClick = { currentValue = null }
) {
Icon(
painterResource(R.drawable.restart_alt_20px),
null,
modifier = Modifier
.padding(ButtonDefaults.IconSpacing)
.size(ButtonDefaults.IconSize)
) )
Text(stringResource(R.string.preference_restore_default))
} }
} }
HorizontalDivider(
modifier = Modifier.padding(top = 16.dp)
)
TextButton(
modifier = Modifier
.padding(top = 8.dp)
.align(Alignment.End),
contentPadding = ButtonDefaults.TextButtonWithIconContentPadding,
onClick = { currentValue = null }
) {
Icon(
painterResource(R.drawable.restart_alt_20px),
null,
modifier = Modifier
.padding(ButtonDefaults.IconSpacing)
.size(ButtonDefaults.IconSize)
)
Text(stringResource(R.string.preference_restore_default))
}
} }
} }
} }

View File

@@ -285,8 +285,9 @@ fun GesturePreference(
} }
} }
if (value is GestureAction.Launch && (showAppPicker || app == null)) { if (value is GestureAction.Launch) {
SearchablePicker( SearchablePicker(
expanded = (showAppPicker || app == null),
onDismissRequest = { onDismissRequest = {
showAppPicker = false showAppPicker = false
if (app == null) onValueChanged(GestureAction.NoAction) if (app == null) onValueChanged(GestureAction.NoAction)

View File

@@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
@@ -298,142 +299,141 @@ fun SearchBarStylePreference(
) { ) {
var showDialog by remember { mutableStateOf(false) } var showDialog by remember { mutableStateOf(false) }
Preference(title = title, summary = summary, onClick = { showDialog = true }) Preference(title = title, summary = summary, onClick = { showDialog = true })
if (showDialog && value != null) {
val styles = remember { DismissableBottomSheet(
SearchBarStyle.entries expanded = showDialog,
onDismissRequest = {
showDialog = false
} }
) {
val styles = SearchBarStyle.entries
val darkColors = val darkColors =
LocalPreferDarkContentOverWallpaper.current && colors == SearchBarColors.Auto || colors == SearchBarColors.Dark LocalPreferDarkContentOverWallpaper.current && colors == SearchBarColors.Auto || colors == SearchBarColors.Dark
DismissableBottomSheet( Box(
onDismissRequest = { modifier = Modifier
showDialog = false .verticalScroll(rememberScrollState())
} .padding(16.dp)
.navigationBarsPadding(),
) { ) {
Box( Column(
modifier = Modifier verticalArrangement = Arrangement.spacedBy(16.dp),
.verticalScroll(rememberScrollState())
.padding(it)
) { ) {
Column( for (style in styles) {
verticalArrangement = Arrangement.spacedBy(16.dp), Column {
) { Row(
for (style in styles) { modifier = Modifier
Column { .padding(top = 8.dp),
Row( verticalAlignment = Alignment.CenterVertically,
modifier = Modifier ) {
.padding(top = 8.dp), Text(
verticalAlignment = Alignment.CenterVertically, text = stringResource(
) { when (style) {
Text( SearchBarStyle.Transparent -> R.string.preference_search_bar_style_transparent
text = stringResource( SearchBarStyle.Solid -> R.string.preference_search_bar_style_solid
when (style) { SearchBarStyle.Hidden -> R.string.preference_search_bar_style_hidden
SearchBarStyle.Transparent -> R.string.preference_search_bar_style_transparent
SearchBarStyle.Solid -> R.string.preference_search_bar_style_solid
SearchBarStyle.Hidden -> R.string.preference_search_bar_style_hidden
}
),
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.secondary,
modifier = Modifier.weight(1f),
)
IconButton(
onClick = {
onStyleChanged(style)
} }
) { ),
Icon( style = MaterialTheme.typography.titleSmall,
painterResource( color = MaterialTheme.colorScheme.secondary,
if (style == value) R.drawable.check_circle_24px_filled modifier = Modifier.weight(1f),
else R.drawable.circle_24px )
), IconButton(
contentDescription = null, onClick = {
tint = if (style == value) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface, onStyleChanged(style)
)
} }
) {
Icon(
painterResource(
if (style == value) R.drawable.check_circle_24px_filled
else R.drawable.circle_24px
),
contentDescription = null,
tint = if (style == value) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
)
} }
}
Box(
modifier = Modifier
.border(
if (style == value) 4.dp else 2.dp,
if (style == value) MaterialTheme.colorScheme.primary else Color.Transparent,
MaterialTheme.shapes.medium,
)
.clip(MaterialTheme.shapes.medium)
.background(
when {
style != SearchBarStyle.Transparent -> MaterialTheme.colorScheme.inverseSurface
LocalDarkTheme.current != darkColors -> MaterialTheme.colorScheme.surfaceContainer
else -> MaterialTheme.colorScheme.inverseSurface
}
)
.height(IntrinsicSize.Min)
) {
SearchBar(
modifier = Modifier
.padding(16.dp),
level = SearchBarLevel.Resting,
style = style,
value = "",
onValueChange = {},
readOnly = true,
darkColors = darkColors,
)
Box( Box(
modifier = Modifier modifier = Modifier
.border( .fillMaxSize()
if (style == value) 4.dp else 2.dp, .clickable {
if (style == value) MaterialTheme.colorScheme.primary else Color.Transparent, onStyleChanged(style)
MaterialTheme.shapes.medium, }
) )
.clip(MaterialTheme.shapes.medium) }
.background( if (style == SearchBarStyle.Transparent) {
when { Row(
style != SearchBarStyle.Transparent -> MaterialTheme.colorScheme.inverseSurface modifier = Modifier
LocalDarkTheme.current != darkColors -> MaterialTheme.colorScheme.surfaceContainer .fillMaxWidth()
else -> MaterialTheme.colorScheme.inverseSurface .padding(top = 16.dp),
} horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween)
)
.height(IntrinsicSize.Min)
) { ) {
SearchBar( ToggleButton(
modifier = Modifier modifier = Modifier.weight(1f),
.padding(16.dp), checked = colors == SearchBarColors.Auto,
level = SearchBarLevel.Resting, onCheckedChange = {
style = style, onColorsChanged(SearchBarColors.Auto)
value = "", },
onValueChange = {}, shapes = ButtonGroupDefaults.connectedLeadingButtonShapes(),
readOnly = true,
darkColors = darkColors,
)
Box(
modifier = Modifier
.fillMaxSize()
.clickable {
onStyleChanged(style)
}
)
}
if (style == SearchBarStyle.Transparent) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween)
) { ) {
ToggleButton( Icon(
modifier = Modifier.weight(1f), painterResource(R.drawable.auto_awesome_20dp),
checked = colors == SearchBarColors.Auto, contentDescription = null,
onCheckedChange = { )
onColorsChanged(SearchBarColors.Auto) }
}, ToggleButton(
shapes = ButtonGroupDefaults.connectedLeadingButtonShapes(), modifier = Modifier.weight(1f),
) { checked = colors == SearchBarColors.Light,
Icon( onCheckedChange = {
painterResource(R.drawable.auto_awesome_20dp), onColorsChanged(SearchBarColors.Light)
contentDescription = null, },
) shapes = ButtonGroupDefaults.connectedMiddleButtonShapes(),
} ) {
ToggleButton( Icon(
modifier = Modifier.weight(1f), painterResource(R.drawable.light_mode_24px),
checked = colors == SearchBarColors.Light, contentDescription = null,
onCheckedChange = { )
onColorsChanged(SearchBarColors.Light) }
}, ToggleButton(
shapes = ButtonGroupDefaults.connectedMiddleButtonShapes(), modifier = Modifier.weight(1f),
) { checked = colors == SearchBarColors.Dark,
Icon( onCheckedChange = {
painterResource(R.drawable.light_mode_24px), onColorsChanged(SearchBarColors.Dark)
contentDescription = null, },
) shapes = ButtonGroupDefaults.connectedTrailingButtonShapes(),
} ) {
ToggleButton( Icon(
modifier = Modifier.weight(1f), painterResource(R.drawable.dark_mode_24px),
checked = colors == SearchBarColors.Dark, contentDescription = null,
onCheckedChange = { )
onColorsChanged(SearchBarColors.Dark)
},
shapes = ButtonGroupDefaults.connectedTrailingButtonShapes(),
) {
Icon(
painterResource(R.drawable.dark_mode_24px),
contentDescription = null,
)
}
} }
} }
} }

View File

@@ -3,6 +3,7 @@ package de.mm20.launcher2.ui.settings.search
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
@@ -397,27 +398,29 @@ fun SearchSettingsScreen() {
} }
} }
if (showFilterEditor) { DismissableBottomSheet(
expanded = showFilterEditor,
onDismissRequest = { showFilterEditor = false }) {
val filters by viewModel.searchFilters.collectAsStateWithLifecycle() val filters by viewModel.searchFilters.collectAsStateWithLifecycle()
DismissableBottomSheet(onDismissRequest = { showFilterEditor = false }) { Column(
Column( modifier = Modifier
modifier = Modifier.padding(it) .padding(16.dp)
) { .navigationBarsPadding()
AnimatedVisibility(filters.allowNetwork) { ) {
SmallMessage( AnimatedVisibility(filters.allowNetwork) {
modifier = Modifier.padding(bottom = 16.dp), SmallMessage(
icon = R.drawable.warning_24px, modifier = Modifier.padding(bottom = 16.dp),
text = stringResource(R.string.filter_settings_network_warning) icon = R.drawable.warning_24px,
) text = stringResource(R.string.filter_settings_network_warning)
}
SearchFilters(
filters = filters,
onFiltersChange = {
viewModel.setSearchFilters(it)
},
settings = true,
) )
} }
SearchFilters(
filters = filters,
onFiltersChange = {
viewModel.setSearchFilters(it)
},
settings = true,
)
} }
} }
} }

View File

@@ -77,7 +77,7 @@ import de.mm20.launcher2.themes.shapes.Shape as ThemeShape
@Serializable @Serializable
data class ShapeSchemeSettingsRoute( data class ShapeSchemeSettingsRoute(
@Serializable(with = UUIDSerializer::class) val id: UUID @Serializable(with = UUIDSerializer::class) val id: UUID
): NavKey ) : NavKey
@Composable @Composable
fun ShapeSchemeSettingsScreen(themeId: UUID) { fun ShapeSchemeSettingsScreen(themeId: UUID) {
@@ -463,20 +463,38 @@ fun ShapePreference(
) )
} }
if (showDialog) { val maxRadius = (24 * factor).toInt()
val maxRadius = (24 * factor).toInt()
val baseTopStart = ((baseShape.radii?.get(0) ?: 12) * factor).toInt() val baseTopStart = ((baseShape.radii?.get(0) ?: 12) * factor).toInt()
val baseTopEnd = ((baseShape.radii?.get(1) ?: 12) * factor).toInt() val baseTopEnd = ((baseShape.radii?.get(1) ?: 12) * factor).toInt()
val baseBottomEnd = ((baseShape.radii?.get(2) ?: 12) * factor).toInt() val baseBottomEnd = ((baseShape.radii?.get(2) ?: 12) * factor).toInt()
val baseBottomStart = ((baseShape.radii?.get(3) ?: 12) * factor).toInt() val baseBottomStart = ((baseShape.radii?.get(3) ?: 12) * factor).toInt()
var currentCornerStyle by remember(shape) { mutableStateOf(shape?.corners) } var currentCornerStyle by remember(shape) { mutableStateOf(shape?.corners) }
var currentTopStart by remember(shape) { mutableStateOf(shape?.radii?.get(0)) } var currentTopStart by remember(shape) { mutableStateOf(shape?.radii?.get(0)) }
var currentTopEnd by remember(shape) { mutableStateOf(shape?.radii?.get(1)) } var currentTopEnd by remember(shape) { mutableStateOf(shape?.radii?.get(1)) }
var currentBottomEnd by remember(shape) { mutableStateOf(shape?.radii?.get(2)) } var currentBottomEnd by remember(shape) { mutableStateOf(shape?.radii?.get(2)) }
var currentBottomStart by remember(shape) { mutableStateOf(shape?.radii?.get(3)) } var currentBottomStart by remember(shape) { mutableStateOf(shape?.radii?.get(3)) }
DismissableBottomSheet(
expanded = showDialog,
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
)
)
}) {
val actualCornerStyle = currentCornerStyle ?: baseShape.corners ?: CornerStyle.Rounded val actualCornerStyle = currentCornerStyle ?: baseShape.corners ?: CornerStyle.Rounded
val actualTopStart = currentTopStart ?: baseTopStart val actualTopStart = currentTopStart ?: baseTopStart
@@ -484,241 +502,222 @@ fun ShapePreference(
val actualBottomEnd = currentBottomEnd ?: baseBottomEnd val actualBottomEnd = currentBottomEnd ?: baseBottomEnd
val actualBottomStart = currentBottomStart ?: baseBottomStart val actualBottomStart = currentBottomStart ?: baseBottomStart
DismissableBottomSheet( Column(
onDismissRequest = { modifier = Modifier
showDialog = false .verticalScroll(rememberScrollState())
onValueChange( .padding(it)
ThemeShape( .padding(bottom = 8.dp),
corners = currentCornerStyle, verticalArrangement = Arrangement.spacedBy(16.dp)
radii = if (currentTopStart != null || currentTopEnd != null || currentBottomEnd != null || currentBottomStart != null) { ) {
intArrayOf( val previewShape = if ((currentCornerStyle ?: baseShape.corners
currentTopStart ?: baseTopStart, ?: CornerStyle.Rounded) == CornerStyle.Rounded
currentTopEnd ?: baseTopEnd,
currentBottomEnd ?: baseBottomEnd,
currentBottomStart ?: baseBottomStart,
)
} else null
)
)
}) {
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(it)
.padding(bottom = 8.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
val previewShape = if ((currentCornerStyle ?: baseShape.corners RoundedCornerShape(
?: CornerStyle.Rounded) == CornerStyle.Rounded topStart = (currentTopStart ?: baseTopStart).dp,
) { topEnd = (currentTopEnd ?: baseTopEnd).dp,
RoundedCornerShape( bottomEnd = (currentBottomEnd ?: baseBottomEnd).dp,
topStart = (currentTopStart ?: baseTopStart).dp, bottomStart = (currentBottomStart ?: baseBottomStart).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
) )
} else { )
CutCornerShape(
topStart = (currentTopStart ?: baseTopStart).dp, Row(
topEnd = (currentTopEnd ?: baseTopEnd).dp, modifier = Modifier
bottomEnd = (currentBottomEnd ?: baseBottomEnd).dp, .fillMaxWidth(),
bottomStart = (currentBottomStart ?: baseBottomStart).dp horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween),
) {
ToggleButton(
modifier = Modifier.weight(1f),
checked = actualCornerStyle == CornerStyle.Rounded,
onCheckedChange = {
currentCornerStyle = CornerStyle.Rounded
},
shapes = ButtonGroupDefaults.connectedLeadingButtonShapes(),
) {
Icon(
painterResource(
if (actualCornerStyle == CornerStyle.Rounded) R.drawable.check_20px else R.drawable.rounded_corner_alt_24px
),
contentDescription = null,
modifier = Modifier
.padding(end = ToggleButtonDefaults.IconSpacing)
.size(ToggleButtonDefaults.IconSize)
) )
Text(stringResource(R.string.preference_cards_shape_rounded))
} }
Box( ToggleButton(
modifier = Modifier.weight(1f),
checked = actualCornerStyle == CornerStyle.Cut,
onCheckedChange = {
currentCornerStyle = CornerStyle.Cut
},
shapes = ButtonGroupDefaults.connectedTrailingButtonShapes(),
) {
Icon(
painterResource(
if (actualCornerStyle == CornerStyle.Cut) R.drawable.check_20px else R.drawable.cut_corner_20px
),
contentDescription = null,
modifier = Modifier
.padding(end = ToggleButtonDefaults.IconSpacing)
.size(ToggleButtonDefaults.IconSize)
)
Text(stringResource(R.string.preference_cards_shape_cut))
}
}
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
painterResource(R.drawable.rounded_corner_24px),
null,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .padding(end = 8.dp)
.height(max(96, maxRadius * 2).dp) .rotate(-90f)
.background(MaterialTheme.colorScheme.surfaceContainer, previewShape) )
.border( Slider(
2.dp, modifier = Modifier
MaterialTheme.colorScheme.primary, .weight(1f),
previewShape 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(
painterResource(R.drawable.rounded_corner_24px),
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(
painterResource(R.drawable.rounded_corner_24px),
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(
painterResource(R.drawable.rounded_corner_24px),
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,
) )
Row( }
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(
painterResource(R.drawable.restart_alt_20px), null,
modifier = Modifier modifier = Modifier
.fillMaxWidth(), .padding(ButtonDefaults.IconSpacing)
horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween), .size(ButtonDefaults.IconSize)
) {
ToggleButton(
modifier = Modifier.weight(1f),
checked = actualCornerStyle == CornerStyle.Rounded,
onCheckedChange = {
currentCornerStyle = CornerStyle.Rounded
},
shapes = ButtonGroupDefaults.connectedLeadingButtonShapes(),
) {
Icon(
painterResource(
if (actualCornerStyle == CornerStyle.Rounded) R.drawable.check_20px else R.drawable.rounded_corner_alt_24px
),
contentDescription = null,
modifier = Modifier
.padding(end = ToggleButtonDefaults.IconSpacing)
.size(ToggleButtonDefaults.IconSize)
)
Text(stringResource(R.string.preference_cards_shape_rounded))
}
ToggleButton(
modifier = Modifier.weight(1f),
checked = actualCornerStyle == CornerStyle.Cut,
onCheckedChange = {
currentCornerStyle = CornerStyle.Cut
},
shapes = ButtonGroupDefaults.connectedTrailingButtonShapes(),
) {
Icon(
painterResource(
if (actualCornerStyle == CornerStyle.Cut) R.drawable.check_20px else R.drawable.cut_corner_20px
),
contentDescription = null,
modifier = Modifier
.padding(end = ToggleButtonDefaults.IconSpacing)
.size(ToggleButtonDefaults.IconSize)
)
Text(stringResource(R.string.preference_cards_shape_cut))
}
}
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
painterResource(R.drawable.rounded_corner_24px),
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(
painterResource(R.drawable.rounded_corner_24px),
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(
painterResource(R.drawable.rounded_corner_24px),
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(
painterResource(R.drawable.rounded_corner_24px),
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)
) )
Text(stringResource(R.string.preference_restore_default))
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(
painterResource(R.drawable.restart_alt_20px), null,
modifier = Modifier
.padding(ButtonDefaults.IconSpacing)
.size(ButtonDefaults.IconSize)
)
Text(stringResource(R.string.preference_restore_default))
}
} }
} }
} }

View File

@@ -8,14 +8,20 @@ import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
@@ -758,47 +764,54 @@ private fun FontPreference(
onClick = { showDialog = true }, onClick = { showDialog = true },
) )
if (showDialog) { DismissableBottomSheet(
expanded = showDialog,
onDismissRequest = { showDialog = false },
) {
val fonts = remember { fontManager.getInstalledFonts() } val fonts = remember { fontManager.getInstalledFonts() }
DismissableBottomSheet( LazyColumn(
onDismissRequest = { showDialog = false }, contentPadding = PaddingValues(
start = 16.dp,
top = 16.dp,
end = 16.dp,
bottom = 16.dp + WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding(),
),
verticalArrangement = Arrangement.spacedBy(12.dp)
) { ) {
LazyColumn(contentPadding = it, verticalArrangement = Arrangement.spacedBy(12.dp)) { if (fonts.builtIn.isNotEmpty()) {
if (fonts.builtIn.isNotEmpty()) { item {
item { FontPickerCategory(
FontPickerCategory( preview.ExtraShort,
preview.ExtraShort, null,
null, fonts.builtIn,
fonts.builtIn, onFontClick = {
onFontClick = { onValueChange(it)
onValueChange(it) showDialog = false
showDialog = false })
})
}
} }
if (fonts.deviceDefault.isNotEmpty()) { }
item { if (fonts.deviceDefault.isNotEmpty()) {
FontPickerCategory( item {
preview.ExtraShort, FontPickerCategory(
stringResource(R.string.font_category_device_default), preview.ExtraShort,
fonts.deviceDefault, stringResource(R.string.font_category_device_default),
onFontClick = { fonts.deviceDefault,
onValueChange(it) onFontClick = {
showDialog = false onValueChange(it)
}) showDialog = false
} })
} }
if (fonts.generic.isNotEmpty()) { }
item { if (fonts.generic.isNotEmpty()) {
FontPickerCategory( item {
preview.ExtraShort, FontPickerCategory(
stringResource(R.string.font_category_generic), preview.ExtraShort,
fonts.generic, stringResource(R.string.font_category_generic),
onFontClick = { fonts.generic,
onValueChange(it) onFontClick = {
showDialog = false onValueChange(it)
}) showDialog = false
} })
} }
} }
} }
@@ -951,209 +964,210 @@ private fun TextStylePreference(
}, },
onClick = { showDialog = true }, onClick = { showDialog = true },
) )
if (showDialog) { var fontFamily by remember(value) {
var fontFamily by remember(value) { mutableStateOf(value?.fontFamily)
mutableStateOf(value?.fontFamily) }
} var fontSize by remember(value) {
var fontSize by remember(value) { mutableStateOf(value?.fontSize)
mutableStateOf(value?.fontSize) }
} var lineHeight by remember(value) {
var lineHeight by remember(value) { mutableStateOf(value?.lineHeight)
mutableStateOf(value?.lineHeight) }
} var weight by remember(value) {
var weight by remember(value) { mutableStateOf((value?.fontWeight as? ThemeFontWeight.Absolute)?.weight)
mutableStateOf((value?.fontWeight as? ThemeFontWeight.Absolute)?.weight) }
} var letterSpacing by remember(value) {
var letterSpacing by remember(value) { mutableStateOf(value?.letterSpacing)
mutableStateOf(value?.letterSpacing) }
}
DismissableBottomSheet( DismissableBottomSheet(
onDismissRequest = { expanded = showDialog,
onValueChange( onDismissRequest = {
ThemeTextStyle( onValueChange(
fontFamily = fontFamily, ThemeTextStyle(
fontSize = fontSize, fontFamily = fontFamily,
lineHeight = lineHeight, fontSize = fontSize,
fontWeight = weight?.let { ThemeFontWeight.Absolute(it) }, lineHeight = lineHeight,
letterSpacing = letterSpacing fontWeight = weight?.let { ThemeFontWeight.Absolute(it) },
) letterSpacing = letterSpacing
) )
showDialog = false )
}, showDialog = false
) { },
) {
val actualFontFamily = fontFamily val actualFontFamily = fontFamily
?: parentValue?.fontFamily ?: parentValue?.fontFamily
?: defaultValue?.fontFamily ?: defaultValue?.fontFamily
?: defaultValueParent?.fontFamily!! ?: defaultValueParent?.fontFamily!!
val actualFontSize = fontSize val actualFontSize = fontSize
?: parentValue?.fontSize ?: parentValue?.fontSize
?: defaultValue?.fontSize ?: defaultValue?.fontSize
?: defaultValueParent?.fontSize!! ?: defaultValueParent?.fontSize!!
val actualLineHeight = lineHeight val actualLineHeight = lineHeight
?: parentValue?.lineHeight ?: parentValue?.lineHeight
?: defaultValue?.lineHeight ?: defaultValue?.lineHeight
?: defaultValueParent?.lineHeight!! ?: defaultValueParent?.lineHeight!!
val actualWeight = when { val actualWeight = when {
weight != null -> FontWeight(weight!!) weight != null -> FontWeight(weight!!)
parentValue?.fontWeight != null -> FontWeight(parentValue.fontWeight!!.weight) parentValue?.fontWeight != null -> FontWeight(parentValue.fontWeight!!.weight)
defaultValue?.fontWeight is ThemeFontWeight.Absolute -> FontWeight((defaultValue.fontWeight as ThemeFontWeight.Absolute).weight) defaultValue?.fontWeight is ThemeFontWeight.Absolute -> FontWeight((defaultValue.fontWeight as ThemeFontWeight.Absolute).weight)
defaultValue?.fontWeight is ThemeFontWeight.Relative && defaultValue?.fontWeight is ThemeFontWeight.Relative &&
defaultValueParent?.fontWeight is ThemeFontWeight.Absolute -> { defaultValueParent?.fontWeight is ThemeFontWeight.Absolute -> {
FontWeight( FontWeight(
(defaultValueParent.fontWeight as ThemeFontWeight.Absolute).weight + (defaultValueParent.fontWeight as ThemeFontWeight.Absolute).weight +
(defaultValue.fontWeight as ThemeFontWeight.Relative).relativeWeight (defaultValue.fontWeight as ThemeFontWeight.Relative).relativeWeight
) )
}
else -> FontWeight.Normal
} }
val actualLetterSpacing = letterSpacing
?: parentValue?.letterSpacing
?: defaultValue?.letterSpacing
?: defaultValueParent?.letterSpacing!!
Column( else -> FontWeight.Normal
modifier = Modifier.padding(it), }
val actualLetterSpacing = letterSpacing
?: parentValue?.letterSpacing
?: defaultValue?.letterSpacing
?: defaultValueParent?.letterSpacing!!
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(16.dp)
.navigationBarsPadding(),
) {
Box(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 200.dp),
contentAlignment = Alignment.Center,
) { ) {
Box( Text(
modifier = Modifier modifier = Modifier.fillMaxWidth(),
.fillMaxWidth() text = previewTexts.TwoLines,
.heightIn(min = 200.dp), textAlign = TextAlign.Center,
contentAlignment = Alignment.Center, color = MaterialTheme.colorScheme.primary,
) { fontFamily = fontFamilyOf(context, fonts[actualFontFamily]),
Text( fontSize = actualFontSize.sp,
modifier = Modifier.fillMaxWidth(), lineHeight = actualLineHeight.em,
text = previewTexts.TwoLines, fontWeight = actualWeight,
textAlign = TextAlign.Center, letterSpacing = actualLetterSpacing.em,
color = MaterialTheme.colorScheme.primary, )
fontFamily = fontFamilyOf(context, fonts[actualFontFamily]), }
fontSize = actualFontSize.sp, Row(
lineHeight = actualLineHeight.em, modifier = Modifier
fontWeight = actualWeight, .padding(vertical = 16.dp)
letterSpacing = actualLetterSpacing.em, .fillMaxWidth()
) .horizontalScroll(rememberScrollState()),
} horizontalArrangement = Arrangement.spacedBy(16.dp),
Row( ) {
modifier = Modifier for ((name, font) in fonts) {
.padding(vertical = 16.dp) val f = remember(font) { fontFamilyOf(context, font) }
.fillMaxWidth() Column(
.horizontalScroll(rememberScrollState()), modifier = Modifier
horizontalArrangement = Arrangement.spacedBy(16.dp), .width(56.dp),
) { horizontalAlignment = Alignment.CenterHorizontally,
for ((name, font) in fonts) { verticalArrangement = Arrangement.spacedBy(8.dp),
val f = remember(font) { fontFamilyOf(context, font) } ) {
Column(
modifier = Modifier
.width(56.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
OutlinedIconToggleButton( OutlinedIconToggleButton(
modifier = Modifier.size(56.dp), modifier = Modifier.size(56.dp),
checked = name == actualFontFamily, checked = name == actualFontFamily,
onCheckedChange = { onCheckedChange = {
if (it) fontFamily = name if (it) fontFamily = name
}, },
) { ) {
Text(
text = previewTexts.ExtraShort,
fontFamily = f,
style = MaterialTheme.typography.headlineSmall,
textAlign = TextAlign.Center,
)
}
Text( Text(
text = name.capitalize(LocaleList.current), text = previewTexts.ExtraShort,
fontFamily = f, fontFamily = f,
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.headlineSmall,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.width(56.dp),
maxLines = 1,
overflow = TextOverflow.MiddleEllipsis,
) )
} }
Text(
text = name.capitalize(LocaleList.current),
fontFamily = f,
style = MaterialTheme.typography.bodySmall,
textAlign = TextAlign.Center,
modifier = Modifier.width(56.dp),
maxLines = 1,
overflow = TextOverflow.MiddleEllipsis,
)
} }
} }
SliderRow(
icon = R.drawable.format_bold_24px,
min = 100f,
max = 900f,
step = 100f,
value = actualWeight.weight.toFloat(),
onValueChange = { weight = it.toInt() },
formatValue = {
it.roundToInt().toString()
}
)
SliderRow(
icon = R.drawable.format_size_24px,
min = floor((defaultValue?.fontSize ?: defaultValueParent?.fontSize)!! / 2f),
max = ceil((defaultValue?.fontSize ?: defaultValueParent?.fontSize)!! * 2f),
value = actualFontSize.toFloat(),
onValueChange = {
fontSize = it.roundToInt()
},
formatValue = {
it.roundToInt().toString()
}
)
SliderRow(
icon = R.drawable.format_line_spacing_24px,
min = 0.5f,
max = 2f,
step = 0.05f,
value = actualLineHeight,
onValueChange = { lineHeight = it },
formatValue = {
(it * 100).roundToInt().toString() + "%"
}
)
SliderRow(
icon = R.drawable.format_letter_spacing_24px,
min = -0.25f,
max = 1f,
step = 0.01f,
value = actualLetterSpacing,
onValueChange = { letterSpacing = (it * 100f).roundToInt() / 100f },
formatValue = {
(it * 100).roundToInt().toString() + "%"
}
)
HorizontalDivider(
modifier = Modifier.padding(top = 16.dp)
)
TextButton(
modifier = Modifier
.padding(top = 8.dp)
.align(Alignment.End),
contentPadding = ButtonDefaults.TextButtonWithIconContentPadding,
onClick = {
fontFamily = null
fontSize = null
lineHeight = null
weight = null
letterSpacing = null
onValueChange(null)
}
) {
Icon(
painterResource(R.drawable.restart_alt_20px), null,
modifier = Modifier
.padding(ButtonDefaults.IconSpacing)
.size(ButtonDefaults.IconSize)
)
Text(stringResource(R.string.preference_restore_default))
}
} }
}
SliderRow(
icon = R.drawable.format_bold_24px,
min = 100f,
max = 900f,
step = 100f,
value = actualWeight.weight.toFloat(),
onValueChange = { weight = it.toInt() },
formatValue = {
it.roundToInt().toString()
}
)
SliderRow(
icon = R.drawable.format_size_24px,
min = floor((defaultValue?.fontSize ?: defaultValueParent?.fontSize)!! / 2f),
max = ceil((defaultValue?.fontSize ?: defaultValueParent?.fontSize)!! * 2f),
value = actualFontSize.toFloat(),
onValueChange = {
fontSize = it.roundToInt()
},
formatValue = {
it.roundToInt().toString()
}
)
SliderRow(
icon = R.drawable.format_line_spacing_24px,
min = 0.5f,
max = 2f,
step = 0.05f,
value = actualLineHeight,
onValueChange = { lineHeight = it },
formatValue = {
(it * 100).roundToInt().toString() + "%"
}
)
SliderRow(
icon = R.drawable.format_letter_spacing_24px,
min = -0.25f,
max = 1f,
step = 0.01f,
value = actualLetterSpacing,
onValueChange = { letterSpacing = (it * 100f).roundToInt() / 100f },
formatValue = {
(it * 100).roundToInt().toString() + "%"
}
)
HorizontalDivider(
modifier = Modifier.padding(top = 16.dp)
)
TextButton(
modifier = Modifier
.padding(top = 8.dp)
.align(Alignment.End),
contentPadding = ButtonDefaults.TextButtonWithIconContentPadding,
onClick = {
fontFamily = null
fontSize = null
lineHeight = null
weight = null
letterSpacing = null
onValueChange(null)
}
) {
Icon(
painterResource(R.drawable.restart_alt_20px), null,
modifier = Modifier
.padding(ButtonDefaults.IconSpacing)
.size(ButtonDefaults.IconSize)
)
Text(stringResource(R.string.preference_restore_default))
}
}
} }
} }