From 4f06c1cc09d15245b26b8a862738cb6a859fedcc Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 9 Mar 2025 12:28:24 +0600 Subject: [PATCH] Make more sliders discrete and ensure they don't look out of place (#1840) Also cleanup the underlying code --- CHANGELOG.md | 3 +- .../library/LibrarySettingsDialog.kt | 4 +- .../presentation/more/settings/Preference.kt | 6 +- .../more/settings/PreferenceItem.kt | 20 +++++-- .../settings/screen/SettingsReaderScreen.kt | 13 ++--- .../reader/settings/ColorFilterPage.kt | 31 +++++----- .../reader/settings/GeneralSettingsPage.kt | 8 +-- .../reader/settings/ReadingModePage.kt | 5 +- .../core/components/SettingsItems.kt | 58 +++++++++++++------ .../core/components/material/Slider.kt | 6 +- 10 files changed, 89 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb22a491e..019d0b0fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co ## [Unreleased] ### Added - Add option to always decode long strip images with SSIV - - Change option label ([@AntsyLich](https://github.com/AntsyLich)) ([#1835](https://github.com/mihonapp/mihon/pull/1835)) + - Change option label ([@AntsyLich](https://github.com/AntsyLich)) ([#1835](https://github.com/mihonapp/mihon/pull/1835)) - Added option to enable incognito per extension ([@sdaqo](https://github.com/sdaqo), [@AntsyLich](https://github.com/AntsyLich)) ([#157](https://github.com/mihonapp/mihon/pull/157)) - Add button to favorite manga from history screen ([@Animeboynz](https://github.com/Animeboynz)) ([#1733](https://github.com/mihonapp/mihon/pull/1733)) - Add Monochrome theme (made with e-ink displays in mind) ([@MajorTanya](https://github.com/MajorTanya)) ([#1752](https://github.com/mihonapp/mihon/pull/1752)) @@ -25,6 +25,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co - Display staff information on Anilist tracker search results ([@NarwhalHorns](https://github.com/NarwhalHorns)) ([#1810](https://github.com/mihonapp/mihon/pull/1810)) ### Changed +- Sliders UI - Apply "Downloaded only" filter to all entries regardless of favourite status ([@NGB-Was-Taken](https://github.com/NGB-Was-Taken)) ([#1603](https://github.com/mihonapp/mihon/pull/1603)) - Ignore hidden files/folders for Local Source chapter list ([@BrutuZ](https://github.com/BrutuZ)) ([#1763](https://github.com/mihonapp/mihon/pull/1763)) - Migrate to newer Bangumi API ([@MajorTanya](https://github.com/MajorTanya)) ([#1748](https://github.com/mihonapp/mihon/pull/1748)) diff --git a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt index 724e5d66a..36d810003 100644 --- a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt @@ -252,9 +252,9 @@ private fun ColumnScope.DisplayPage( val columns by columnPreference.collectAsState() SliderItem( - label = stringResource(MR.strings.pref_library_columns), - max = 10, value = columns, + valueRange = 0..10, + label = stringResource(MR.strings.pref_library_columns), valueText = if (columns > 0) { columns.toString() } else { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt b/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt index 82e3fd9d0..d2f809d5d 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt @@ -1,5 +1,6 @@ package eu.kanade.presentation.more.settings +import androidx.annotation.IntRange import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.graphics.vector.ImageVector @@ -50,10 +51,9 @@ sealed class Preference { */ data class SliderPreference( val value: Int, - val max: Int, - val min: Int = 0, - val steps: Int = 0, override val title: String, + 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 }, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt index 9f97d3a83..74515e98e 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt @@ -5,6 +5,7 @@ import androidx.compose.animation.expandVertically import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -13,16 +14,20 @@ import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.structuralEqualityPolicy +import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget import eu.kanade.presentation.more.settings.widget.InfoWidget import eu.kanade.presentation.more.settings.widget.ListPreferenceWidget import eu.kanade.presentation.more.settings.widget.MultiSelectListPreferenceWidget +import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding +import eu.kanade.presentation.more.settings.widget.PrefsVerticalPadding import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget +import eu.kanade.presentation.more.settings.widget.TitleFontSize import eu.kanade.presentation.more.settings.widget.TrackingPreferenceWidget import kotlinx.coroutines.launch -import tachiyomi.presentation.core.components.SliderItem +import tachiyomi.presentation.core.components.BaseSliderItem import tachiyomi.presentation.core.util.collectAsState val LocalPreferenceHighlighted = compositionLocalOf(structuralEqualityPolicy()) { false } @@ -77,19 +82,22 @@ internal fun PreferenceItem( ) } is Preference.PreferenceItem.SliderPreference -> { - SliderItem( + BaseSliderItem( label = item.title, - min = item.min, - max = item.max, - steps = item.steps, 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), onChange = { scope.launch { item.onValueChanged(it) } }, - labelStyle = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding( + horizontal = PrefsHorizontalPadding, + vertical = PrefsVerticalPadding, + ), ) } is Preference.PreferenceItem.ListPreference<*> -> { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt index 3ba1e0a3b..903397bca 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt @@ -141,9 +141,7 @@ object SettingsReaderScreen : SearchableSettings { ), Preference.PreferenceItem.SliderPreference( value = flashMillis / ReaderPreferences.MILLI_CONVERSION, - max = 15, - min = 1, - steps = 13, + valueRange = 1..15, title = stringResource(MR.strings.pref_flash_duration), subtitle = stringResource(MR.strings.pref_flash_duration_summary, flashMillis), enabled = flashPageState, @@ -154,9 +152,7 @@ object SettingsReaderScreen : SearchableSettings { ), Preference.PreferenceItem.SliderPreference( value = flashInterval, - max = 10, - min = 1, - steps = 8, + valueRange = 1..10, title = stringResource(MR.strings.pref_flash_page_interval), subtitle = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval), enabled = flashPageState, @@ -342,8 +338,9 @@ object SettingsReaderScreen : SearchableSettings { ), Preference.PreferenceItem.SliderPreference( value = webtoonSidePadding, - max = ReaderPreferences.WEBTOON_PADDING_MAX, - min = ReaderPreferences.WEBTOON_PADDING_MIN, + valueRange = ReaderPreferences.let { + it.WEBTOON_PADDING_MIN..it.WEBTOON_PADDING_MAX + }, title = stringResource(MR.strings.pref_webtoon_side_padding), subtitle = numberFormat.format(webtoonSidePadding / 100f), onValueChanged = { diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt index 211a9e853..d0017aa41 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt @@ -37,11 +37,10 @@ internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel) if (customBrightness) { val customBrightnessValue by screenModel.preferences.customBrightnessValue().collectAsState() SliderItem( - label = stringResource(MR.strings.pref_custom_brightness), - min = -75, - max = 100, value = customBrightnessValue, - valueText = customBrightnessValue.toString(), + valueRange = -75..100, + steps = 0, + label = stringResource(MR.strings.pref_custom_brightness), onChange = { screenModel.preferences.customBrightnessValue().set(it) }, pillColor = MaterialTheme.colorScheme.surfaceContainerHighest, ) @@ -55,10 +54,10 @@ internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel) if (colorFilter) { val colorFilterValue by screenModel.preferences.colorFilterValue().collectAsState() SliderItem( - label = stringResource(MR.strings.color_filter_r_value), - max = 255, value = colorFilterValue.red, - valueText = colorFilterValue.red.toString(), + valueRange = 0..255, + steps = 0, + label = stringResource(MR.strings.color_filter_r_value), onChange = { newRValue -> screenModel.preferences.colorFilterValue().getAndSet { getColorValue(it, newRValue, RED_MASK, 16) @@ -67,10 +66,10 @@ internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel) pillColor = MaterialTheme.colorScheme.surfaceContainerHighest, ) SliderItem( - label = stringResource(MR.strings.color_filter_g_value), - max = 255, value = colorFilterValue.green, - valueText = colorFilterValue.green.toString(), + valueRange = 0..255, + steps = 0, + label = stringResource(MR.strings.color_filter_g_value), onChange = { newGValue -> screenModel.preferences.colorFilterValue().getAndSet { getColorValue(it, newGValue, GREEN_MASK, 8) @@ -79,10 +78,10 @@ internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel) pillColor = MaterialTheme.colorScheme.surfaceContainerHighest, ) SliderItem( - label = stringResource(MR.strings.color_filter_b_value), - max = 255, value = colorFilterValue.blue, - valueText = colorFilterValue.blue.toString(), + valueRange = 0..255, + steps = 0, + label = stringResource(MR.strings.color_filter_b_value), onChange = { newBValue -> screenModel.preferences.colorFilterValue().getAndSet { getColorValue(it, newBValue, BLUE_MASK, 0) @@ -91,10 +90,10 @@ internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel) pillColor = MaterialTheme.colorScheme.surfaceContainerHighest, ) SliderItem( - label = stringResource(MR.strings.color_filter_a_value), - max = 255, value = colorFilterValue.alpha, - valueText = colorFilterValue.alpha.toString(), + valueRange = 0..255, + steps = 0, + label = stringResource(MR.strings.color_filter_a_value), onChange = { newAValue -> screenModel.preferences.colorFilterValue().getAndSet { getColorValue(it, newAValue, ALPHA_MASK, 24) diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt index 8c2bdde97..5eb9c3345 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt @@ -98,24 +98,20 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) { if (flashPageState) { SliderItem( value = flashMillis / ReaderPreferences.MILLI_CONVERSION, + valueRange = 1..15, label = stringResource(MR.strings.pref_flash_duration), valueText = stringResource(MR.strings.pref_flash_duration_summary, flashMillis), onChange = { flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION) }, - min = 1, - max = 15, - steps = 13, pillColor = MaterialTheme.colorScheme.surfaceContainerHighest, ) SliderItem( value = flashInterval, + valueRange = 1..10, label = stringResource(MR.strings.pref_flash_page_interval), valueText = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval), onChange = { flashIntervalPref.set(it) }, - min = 1, - max = 10, - steps = 8, pillColor = MaterialTheme.colorScheme.surfaceContainerHighest, ) SettingsChipRow(MR.strings.pref_flash_with) { diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt index b89c806c4..ca5b3a693 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt @@ -153,10 +153,9 @@ private fun ColumnScope.WebtoonViewerSettings(screenModel: ReaderSettingsScreenM val webtoonSidePadding by screenModel.preferences.webtoonSidePadding().collectAsState() SliderItem( - label = stringResource(MR.strings.pref_webtoon_side_padding), - min = ReaderPreferences.WEBTOON_PADDING_MIN, - max = ReaderPreferences.WEBTOON_PADDING_MAX, 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), onChange = { screenModel.preferences.webtoonSidePadding().set(it) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt index 6f5e2e3b8..9e7d99559 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt @@ -36,6 +36,7 @@ import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -172,25 +173,49 @@ fun RadioItem(label: String, selected: Boolean, onClick: () -> Unit) { @Composable fun SliderItem( - label: String, value: Int, - valueText: String, + valueRange: IntProgression, + label: String, onChange: (Int) -> Unit, - max: Int, - min: Int = 0, - steps: Int = 0, + steps: Int = with(valueRange) { (last - first) - 1 }, + valueText: String = value.toString(), + labelStyle: TextStyle = MaterialTheme.typography.bodyMedium, + pillColor: Color = MaterialTheme.colorScheme.surfaceContainerHigh, +) { + BaseSliderItem( + value = value, + valueRange = valueRange, + steps = steps, + label = label, + valueText = valueText, + onChange = onChange, + labelStyle = labelStyle, + pillColor = pillColor, + modifier = Modifier.padding( + horizontal = SettingsItemsPaddings.Horizontal, + vertical = SettingsItemsPaddings.Vertical, + ), + ) +} + +@Composable +fun BaseSliderItem( + value: Int, + valueRange: IntProgression, + label: String, + onChange: (Int) -> Unit, + modifier: Modifier = Modifier, + steps: Int = with(valueRange) { (last - first) - 1 }, + valueText: String = value.toString(), labelStyle: TextStyle = MaterialTheme.typography.bodyMedium, pillColor: Color = MaterialTheme.colorScheme.surfaceContainerHigh, ) { val haptic = LocalHapticFeedback.current - Column( modifier = Modifier .fillMaxWidth() - .padding( - horizontal = SettingsItemsPaddings.Horizontal, - vertical = SettingsItemsPaddings.Vertical, - ), + .then(modifier), + verticalArrangement = Arrangement.spacedBy(2.dp), ) { Row( verticalAlignment = Alignment.CenterVertically, @@ -214,7 +239,7 @@ fun SliderItem( onChange(it) haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove) }, - valueRange = min..max, + valueRange = valueRange, steps = steps, ) } @@ -224,15 +249,14 @@ fun SliderItem( @PreviewLightDark fun SliderItemPreview() { MaterialTheme(if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme()) { + var value by remember { mutableIntStateOf(0) } Surface { SliderItem( + value = value, + valueRange = 0..10, label = "Item per row", - valueText = "Auto", - value = 0, - onChange = {}, - min = 0, - max = 10, - steps = 8, + valueText = if (value == 0) "Auto" else value.toString(), + onChange = { value = it }, ) } } diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Slider.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Slider.kt index e590c3327..66f2124a2 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Slider.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Slider.kt @@ -17,8 +17,8 @@ fun Slider( onValueChange: (Int) -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, - valueRange: ClosedRange = 0..1, - @IntRange(from = 0) steps: Int = 0, + valueRange: IntProgression = 0..1, + @IntRange(from = 0) steps: Int = with(valueRange) { (last - first) - 1 }, onValueChangeFinished: (() -> Unit)? = null, colors: SliderColors = SliderDefaults.colors(), interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, @@ -38,7 +38,7 @@ fun Slider( onValueChange = { onValueChange(it.roundToInt()) }, modifier = modifier, enabled = enabled, - valueRange = with(valueRange) { start.toFloat()..endInclusive.toFloat() }, + valueRange = with(valueRange) { first.toFloat()..last.toFloat() }, steps = steps, onValueChangeFinished = onValueChangeFinished, colors = colors,