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

(cherry picked from commit f36c259c1faf2ee4a108fd98a5d27d93014ba34c)
Co-authored-by: Cuong-Tran <16017808+cuong-tran@users.noreply.github.com>
This commit is contained in:
AntsyLich 2025-11-01 18:47:06 +06:00 committed by Cuong-Tran
parent c9b3f8fda0
commit 4ab421e822
No known key found for this signature in database
GPG key ID: 94EFFE320FE7A47C
11 changed files with 76 additions and 71 deletions

View file

@ -346,7 +346,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

@ -16,13 +16,13 @@ sealed class Preference {
abstract val title: String
abstract val enabled: Boolean
sealed class PreferenceItem<T> : Preference() {
sealed class PreferenceItem<T, R> : Preference() {
// SY -->
abstract val subtitle: CharSequence?
// SY <--
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.
@ -32,9 +32,9 @@ sealed class Preference {
override val subtitle: CharSequence? = 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 = {}
}
/**
@ -46,7 +46,7 @@ sealed class Preference {
override val subtitle: CharSequence? = null,
override val enabled: Boolean = true,
override val onValueChanged: suspend (value: Boolean) -> Boolean = { true },
) : PreferenceItem<Boolean>() {
) : PreferenceItem<Boolean, Boolean>() {
override val icon: ImageVector? = null
}
@ -56,12 +56,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
}
@ -79,7 +80,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)
@ -100,8 +101,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.
@ -125,7 +126,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.
@ -136,7 +137,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
}
@ -147,12 +148,12 @@ 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 = {}
}
// AM (CONNECTIONS) -->
@ -165,30 +166,30 @@ sealed class Preference {
val login: () -> Unit,
val openSettings: () -> Unit,
override val subtitle: String? = null,
) : PreferenceItem<String>() {
) : PreferenceItem<String, Unit>() {
override val enabled: Boolean = true
override val icon: ImageVector? = null
override val onValueChanged: suspend (newValue: String) -> Boolean = { true }
override val onValueChanged: suspend (newValue: String) -> Unit = {}
}
// <-- AM (CONNECTIONS)
data class InfoPreference(
override val title: String,
) : PreferenceItem<String>() {
) : PreferenceItem<String, Unit>() {
override val subtitle: String? = null
override val enabled: Boolean = true
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 = {}
}
}
@ -196,6 +197,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

@ -36,7 +36,7 @@ val LocalPreferenceMinHeight = compositionLocalOf(structuralEqualityPolicy()) {
@Composable
fun StatusWrapper(
item: Preference.PreferenceItem<*>,
item: Preference.PreferenceItem<*, *>,
highlightKey: String?,
content: @Composable () -> Unit,
) {
@ -57,7 +57,7 @@ fun StatusWrapper(
@Composable
internal fun PreferenceItem(
item: Preference.PreferenceItem<*>,
item: Preference.PreferenceItem<*, *>,
highlightKey: String?,
) {
val scope = rememberCoroutineScope()
@ -84,17 +84,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

@ -311,7 +311,7 @@ object SettingsAppearanceScreen : SearchableSettings {
value = previewsRowCount,
valueRange = 0..10,
title = stringResource(SYMR.strings.pref_previews_row_count),
subtitle = if (previewsRowCount > 0) {
valueString = if (previewsRowCount > 0) {
pluralStringResource(
SYMR.plurals.row_count,
previewsRowCount,
@ -320,10 +320,7 @@ object SettingsAppearanceScreen : SearchableSettings {
} else {
stringResource(MR.strings.disabled)
},
onValueChanged = {
uiPreferences.previewsRowCount().set(it)
true
},
onValueChanged = { uiPreferences.previewsRowCount().set(it) },
),
),
)

View file

@ -188,23 +188,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,
@ -434,11 +428,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

@ -122,7 +122,7 @@ internal fun 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,
)
@ -130,7 +130,7 @@ internal fun 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

@ -248,7 +248,7 @@ private fun WebtoonViewerSettings(
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

@ -60,6 +60,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
@ -179,7 +180,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,
) {
@ -187,10 +188,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,
@ -203,12 +204,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
@ -222,13 +225,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,
)
@ -252,12 +263,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,
),
)
}
}