Add subtitle support to slider preference and general cleanup (#2635)

This commit is contained in:
AntsyLich
2025-11-01 18:47:06 +06:00
committed by GitHub
parent aef3beb15f
commit f36c259c1f
10 changed files with 71 additions and 63 deletions

View File

@@ -255,7 +255,7 @@ private fun ColumnScope.DisplayPage(
value = columns,
valueRange = 0..10,
label = stringResource(MR.strings.pref_library_columns),
valueText = if (columns > 0) {
valueString = if (columns > 0) {
columns.toString()
} else {
stringResource(MR.strings.label_auto)

View File

@@ -15,10 +15,10 @@ sealed class Preference {
abstract val title: String
abstract val enabled: Boolean
sealed class PreferenceItem<T> : Preference() {
sealed class PreferenceItem<T, R> : Preference() {
abstract val subtitle: String?
abstract val icon: ImageVector?
abstract val onValueChanged: suspend (value: T) -> Boolean
abstract val onValueChanged: suspend (value: T) -> R
/**
* A basic [PreferenceItem] that only displays texts.
@@ -28,9 +28,9 @@ sealed class Preference {
override val subtitle: String? = null,
override val enabled: Boolean = true,
val onClick: (() -> Unit)? = null,
) : PreferenceItem<String>() {
) : PreferenceItem<String, Unit>() {
override val icon: ImageVector? = null
override val onValueChanged: suspend (value: String) -> Boolean = { true }
override val onValueChanged: suspend (value: String) -> Unit = {}
}
/**
@@ -42,7 +42,7 @@ sealed class Preference {
override val subtitle: String? = null,
override val enabled: Boolean = true,
override val onValueChanged: suspend (value: Boolean) -> Boolean = { true },
) : PreferenceItem<Boolean>() {
) : PreferenceItem<Boolean, Boolean>() {
override val icon: ImageVector? = null
}
@@ -52,12 +52,13 @@ sealed class Preference {
data class SliderPreference(
val value: Int,
override val title: String,
override val subtitle: String? = null,
val valueString: String? = null,
val valueRange: IntProgression = 0..1,
@IntRange(from = 0) val steps: Int = with(valueRange) { (last - first) - 1 },
override val subtitle: String? = null,
override val enabled: Boolean = true,
override val onValueChanged: suspend (value: Int) -> Boolean = { true },
) : PreferenceItem<Int>() {
override val onValueChanged: suspend (value: Int) -> Unit = {},
) : PreferenceItem<Int, Unit>() {
override val icon: ImageVector? = null
}
@@ -75,7 +76,7 @@ sealed class Preference {
override val icon: ImageVector? = null,
override val enabled: Boolean = true,
override val onValueChanged: suspend (value: T) -> Boolean = { true },
) : PreferenceItem<T>() {
) : PreferenceItem<T, Boolean>() {
internal fun internalSet(value: Any) = preference.set(value as T)
internal suspend fun internalOnValueChanged(value: Any) = onValueChanged(value as T)
@@ -96,8 +97,8 @@ sealed class Preference {
{ v, e -> subtitle?.format(e[v]) },
override val icon: ImageVector? = null,
override val enabled: Boolean = true,
override val onValueChanged: suspend (value: String) -> Boolean = { true },
) : PreferenceItem<String>()
override val onValueChanged: suspend (value: String) -> Unit = {},
) : PreferenceItem<String, Unit>()
/**
* A [PreferenceItem] that displays a list of entries as a dialog.
@@ -121,7 +122,7 @@ sealed class Preference {
override val icon: ImageVector? = null,
override val enabled: Boolean = true,
override val onValueChanged: suspend (value: Set<String>) -> Boolean = { true },
) : PreferenceItem<Set<String>>()
) : PreferenceItem<Set<String>, Boolean>()
/**
* A [PreferenceItem] that shows a EditText in the dialog.
@@ -132,7 +133,7 @@ sealed class Preference {
override val subtitle: String? = "%s",
override val enabled: Boolean = true,
override val onValueChanged: suspend (value: String) -> Boolean = { true },
) : PreferenceItem<String>() {
) : PreferenceItem<String, Boolean>() {
override val icon: ImageVector? = null
}
@@ -143,31 +144,31 @@ sealed class Preference {
val tracker: Tracker,
val login: () -> Unit,
val logout: () -> Unit,
) : PreferenceItem<String>() {
) : PreferenceItem<String, Unit>() {
override val title: String = ""
override val enabled: Boolean = true
override val subtitle: String? = null
override val icon: ImageVector? = null
override val onValueChanged: suspend (value: String) -> Boolean = { true }
override val onValueChanged: suspend (value: String) -> Unit = {}
}
data class InfoPreference(
override val title: String,
) : PreferenceItem<String>() {
) : PreferenceItem<String, Unit>() {
override val enabled: Boolean = true
override val subtitle: String? = null
override val icon: ImageVector? = null
override val onValueChanged: suspend (value: String) -> Boolean = { true }
override val onValueChanged: suspend (value: String) -> Unit = {}
}
data class CustomPreference(
override val title: String,
val content: @Composable () -> Unit,
) : PreferenceItem<Unit>() {
) : PreferenceItem<Unit, Unit>() {
override val enabled: Boolean = true
override val subtitle: String? = null
override val icon: ImageVector? = null
override val onValueChanged: suspend (value: Unit) -> Boolean = { true }
override val onValueChanged: suspend (value: Unit) -> Unit = {}
}
}
@@ -175,6 +176,6 @@ sealed class Preference {
override val title: String,
override val enabled: Boolean = true,
val preferenceItems: ImmutableList<PreferenceItem<out Any>>,
val preferenceItems: ImmutableList<PreferenceItem<out Any, out Any>>,
) : Preference()
}

View File

@@ -35,7 +35,7 @@ val LocalPreferenceMinHeight = compositionLocalOf(structuralEqualityPolicy()) {
@Composable
fun StatusWrapper(
item: Preference.PreferenceItem<*>,
item: Preference.PreferenceItem<*, *>,
highlightKey: String?,
content: @Composable () -> Unit,
) {
@@ -56,7 +56,7 @@ fun StatusWrapper(
@Composable
internal fun PreferenceItem(
item: Preference.PreferenceItem<*>,
item: Preference.PreferenceItem<*, *>,
highlightKey: String?,
) {
val scope = rememberCoroutineScope()
@@ -83,17 +83,18 @@ internal fun PreferenceItem(
}
is Preference.PreferenceItem.SliderPreference -> {
BaseSliderItem(
label = item.title,
value = item.value,
valueRange = item.valueRange,
valueText = item.subtitle.takeUnless { it.isNullOrEmpty() } ?: item.value.toString(),
steps = item.steps,
labelStyle = MaterialTheme.typography.titleLarge.copy(fontSize = TitleFontSize),
title = item.title,
subtitle = item.subtitle,
valueString = item.valueString.takeUnless { it.isNullOrEmpty() } ?: item.value.toString(),
onChange = {
scope.launch {
item.onValueChanged(it)
}
},
titleStyle = MaterialTheme.typography.titleLarge.copy(fontSize = TitleFontSize),
modifier = Modifier.padding(
horizontal = PrefsHorizontalPadding,
vertical = PrefsVerticalPadding,

View File

@@ -71,7 +71,7 @@ fun PreferenceScreen(
}
// Create Preference Item
is Preference.PreferenceItem<*> -> item {
is Preference.PreferenceItem<*, *> -> item {
PreferenceItem(
item = preference,
highlightKey = highlightKey,

View File

@@ -143,23 +143,17 @@ object SettingsReaderScreen : SearchableSettings {
value = flashMillis / ReaderPreferences.MILLI_CONVERSION,
valueRange = 1..15,
title = stringResource(MR.strings.pref_flash_duration),
subtitle = stringResource(MR.strings.pref_flash_duration_summary, flashMillis),
valueString = stringResource(MR.strings.pref_flash_duration_summary, flashMillis),
enabled = flashPageState,
onValueChanged = {
flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION)
true
},
onValueChanged = { flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION) },
),
Preference.PreferenceItem.SliderPreference(
value = flashInterval,
valueRange = 1..10,
title = stringResource(MR.strings.pref_flash_page_interval),
subtitle = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval),
valueString = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval),
enabled = flashPageState,
onValueChanged = {
flashIntervalPref.set(it)
true
},
onValueChanged = { flashIntervalPref.set(it) },
),
Preference.PreferenceItem.ListPreference(
preference = flashColorPref,
@@ -342,11 +336,8 @@ object SettingsReaderScreen : SearchableSettings {
it.WEBTOON_PADDING_MIN..it.WEBTOON_PADDING_MAX
},
title = stringResource(MR.strings.pref_webtoon_side_padding),
subtitle = numberFormat.format(webtoonSidePadding / 100f),
onValueChanged = {
webtoonSidePaddingPref.set(it)
true
},
valueString = numberFormat.format(webtoonSidePadding / 100f),
onValueChanged = { webtoonSidePaddingPref.set(it) },
),
Preference.PreferenceItem.ListPreference(
preference = readerPreferences.readerHideThreshold(),

View File

@@ -183,7 +183,7 @@ private fun SearchResult(
emptySequence()
}
}
is Preference.PreferenceItem<*> -> sequenceOf(null to p)
is Preference.PreferenceItem<*, *> -> sequenceOf(null to p)
}
}
// Don't show info preference

View File

@@ -99,7 +99,7 @@ class DebugInfoScreen : Screen() {
}
private fun getDeviceInfoGroup(): Preference.PreferenceGroup {
val items = persistentListOf<Preference.PreferenceItem<out Any>>().mutate {
val items = persistentListOf<Preference.PreferenceItem<out Any, out Any>>().mutate {
it.add(
Preference.PreferenceItem.TextPreference(
title = "Model",

View File

@@ -100,7 +100,7 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
value = flashMillis / ReaderPreferences.MILLI_CONVERSION,
valueRange = 1..15,
label = stringResource(MR.strings.pref_flash_duration),
valueText = stringResource(MR.strings.pref_flash_duration_summary, flashMillis),
valueString = stringResource(MR.strings.pref_flash_duration_summary, flashMillis),
onChange = { flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION) },
pillColor = MaterialTheme.colorScheme.surfaceContainerHighest,
)
@@ -108,7 +108,7 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
value = flashInterval,
valueRange = 1..10,
label = stringResource(MR.strings.pref_flash_page_interval),
valueText = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval),
valueString = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval),
onChange = {
flashIntervalPref.set(it)
},

View File

@@ -156,7 +156,7 @@ private fun ColumnScope.WebtoonViewerSettings(screenModel: ReaderSettingsScreenM
value = webtoonSidePadding,
valueRange = ReaderPreferences.let { it.WEBTOON_PADDING_MIN..it.WEBTOON_PADDING_MAX },
label = stringResource(MR.strings.pref_webtoon_side_padding),
valueText = numberFormat.format(webtoonSidePadding / 100f),
valueString = numberFormat.format(webtoonSidePadding / 100f),
onChange = {
screenModel.preferences.webtoonSidePadding().set(it)
},

View File

@@ -59,6 +59,7 @@ import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.theme.header
import tachiyomi.presentation.core.util.collectAsState
import tachiyomi.presentation.core.util.secondaryItemAlpha
object SettingsItemsPaddings {
val Horizontal = 24.dp
@@ -178,7 +179,7 @@ fun SliderItem(
label: String,
onChange: (Int) -> Unit,
steps: Int = with(valueRange) { (last - first) - 1 },
valueText: String = value.toString(),
valueString: String = value.toString(),
labelStyle: TextStyle = MaterialTheme.typography.bodyMedium,
pillColor: Color = MaterialTheme.colorScheme.surfaceContainerHigh,
) {
@@ -186,10 +187,10 @@ fun SliderItem(
value = value,
valueRange = valueRange,
steps = steps,
label = label,
valueText = valueText,
title = label,
valueString = valueString,
onChange = onChange,
labelStyle = labelStyle,
titleStyle = labelStyle,
pillColor = pillColor,
modifier = Modifier.padding(
horizontal = SettingsItemsPaddings.Horizontal,
@@ -202,12 +203,14 @@ fun SliderItem(
fun BaseSliderItem(
value: Int,
valueRange: IntProgression,
label: String,
title: String,
onChange: (Int) -> Unit,
modifier: Modifier = Modifier,
subtitle: String? = null,
steps: Int = with(valueRange) { (last - first) - 1 },
valueText: String = value.toString(),
labelStyle: TextStyle = MaterialTheme.typography.bodyMedium,
valueString: String = value.toString(),
titleStyle: TextStyle = MaterialTheme.typography.titleLarge,
subtitleStyle: TextStyle = MaterialTheme.typography.bodySmall,
pillColor: Color = MaterialTheme.colorScheme.surfaceContainerHigh,
) {
val haptic = LocalHapticFeedback.current
@@ -221,13 +224,21 @@ fun BaseSliderItem(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
) {
Text(
text = label,
style = labelStyle,
modifier = Modifier.weight(1f),
)
Column(modifier = Modifier.weight(1f)) {
Text(
text = title,
style = titleStyle,
)
if (subtitle != null) {
Text(
text = subtitle,
style = subtitleStyle,
modifier = Modifier.secondaryItemAlpha(),
)
}
}
Pill(
text = valueText,
text = valueString,
style = MaterialTheme.typography.bodyMedium,
color = pillColor,
)
@@ -251,12 +262,16 @@ fun SliderItemPreview() {
MaterialTheme(if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme()) {
var value by remember { mutableIntStateOf(0) }
Surface {
SliderItem(
BaseSliderItem(
value = value,
valueRange = 0..10,
label = "Item per row",
valueText = if (value == 0) "Auto" else value.toString(),
title = "Item per row",
valueString = if (value == 0) "Auto" else value.toString(),
onChange = { value = it },
modifier = Modifier.padding(
horizontal = SettingsItemsPaddings.Horizontal,
vertical = SettingsItemsPaddings.Vertical,
),
)
}
}