Sfoglia il codice sorgente

fix: 优化外观设置滑条与背景图片交互

- SliderSettingItem 默认整数模式:滑动吸附整数、不再显示小数,
  输入框回填/输入同样按整数处理;新增 decimal 参数供 AI 温度、
  分割线宽度等真正需要 0.1 精度的场景使用
- 拖动滑条时标题下数值实时跟随,松手后才应用;字号滑条通过
  valueLabel 实时显示换算后的字体倍率
- 移除不透明度、背景虚化、屏幕色温滑条的密集刻度点(steps=99)
- 背景图片设置层级调整:合并为"背景图片"分组,下设白天/夜间
- 背景图片添加按钮直接拉起系统图片选择器,移除多余的操作抽屉

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5151561 3 mesi fa
parent
commit
cf7532a92b

+ 1 - 0
app/src/main/java/io/legado/app/ui/config/ai/AiModelEditScreen.kt

@@ -180,6 +180,7 @@ fun AiModelEditScreen(
                         valueRange = TranslationConstants.MIN_TEMPERATURE..TranslationConstants.MAX_TEMPERATURE,
                         valueRange = TranslationConstants.MIN_TEMPERATURE..TranslationConstants.MAX_TEMPERATURE,
                         steps = 19,
                         steps = 19,
                         description = state.temperature.toString(),
                         description = state.temperature.toString(),
+                        decimal = true,
                         onValueChange = { onIntent(AiModelEditIntent.UpdateTemperature(it)) }
                         onValueChange = { onIntent(AiModelEditIntent.UpdateTemperature(it)) }
                     )
                     )
                 }
                 }

+ 1 - 0
app/src/main/java/io/legado/app/ui/config/ai/summary/AiSummaryConfigScreen.kt

@@ -129,6 +129,7 @@ fun AiSummaryConfigScreen(
                         valueRange = TranslationConstants.MIN_TEMPERATURE..TranslationConstants.MAX_TEMPERATURE,
                         valueRange = TranslationConstants.MIN_TEMPERATURE..TranslationConstants.MAX_TEMPERATURE,
                         steps = 19,
                         steps = 19,
                         description = state.temperature.toString(),
                         description = state.temperature.toString(),
+                        decimal = true,
                         onValueChange = { onIntent(AiSummaryConfigIntent.UpdateTemperature(it)) }
                         onValueChange = { onIntent(AiSummaryConfigIntent.UpdateTemperature(it)) }
                     )
                     )
                     InputSettingItem(
                     InputSettingItem(

+ 1 - 17
app/src/main/java/io/legado/app/ui/config/themeConfig/BackgroundImageManageSheet.kt

@@ -14,11 +14,7 @@ import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.Close
 import androidx.compose.material.icons.filled.Close
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.layout.ContentScale
@@ -29,7 +25,6 @@ import io.legado.app.R
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.button.series.SmallTonalButton
 import io.legado.app.ui.widget.components.button.series.SmallTonalButton
 import io.legado.app.ui.widget.components.card.NormalCard
 import io.legado.app.ui.widget.components.card.NormalCard
-import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
 import io.legado.app.ui.widget.components.icon.AppIcon
 import io.legado.app.ui.widget.components.icon.AppIcon
 import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.launch
@@ -43,7 +38,6 @@ fun BackgroundImageManageSheet(
     viewModel: ThemeConfigViewModel = koinViewModel()
     viewModel: ThemeConfigViewModel = koinViewModel()
 ) {
 ) {
     val scope = rememberCoroutineScope()
     val scope = rememberCoroutineScope()
-    var showFilePicker by remember { mutableStateOf(false) }
 
 
     val selectImage =
     val selectImage =
         rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri ->
         rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri ->
@@ -76,7 +70,7 @@ fun BackgroundImageManageSheet(
 
 
             if (currentPath.isNullOrBlank()) {
             if (currentPath.isNullOrBlank()) {
                 NormalCard(
                 NormalCard(
-                    onClick = { showFilePicker = true },
+                    onClick = { selectImage.launch("image/*") },
                     cornerRadius = 12.dp,
                     cornerRadius = 12.dp,
                     containerColor = LegadoTheme.colorScheme.surfaceContainerHigh,
                     containerColor = LegadoTheme.colorScheme.surfaceContainerHigh,
                     modifier = Modifier
                     modifier = Modifier
@@ -127,14 +121,4 @@ fun BackgroundImageManageSheet(
             }
             }
         }
         }
     }
     }
-
-    FilePickerSheet(
-        show = showFilePicker,
-        onDismissRequest = { showFilePicker = false },
-        onSelectSysFile = {
-            selectImage.launch("image/*")
-            showFilePicker = false
-        },
-        allowExtensions = arrayOf("jpg", "jpeg", "png", "webp")
-    )
 }
 }

+ 6 - 16
app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt

@@ -70,7 +70,6 @@ import androidx.constraintlayout.compose.ConstraintLayout
 import androidx.core.net.toUri
 import androidx.core.net.toUri
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import io.legado.app.R
 import io.legado.app.R
-import io.legado.app.base.AppContextWrapper
 import io.legado.app.constant.EventBus
 import io.legado.app.constant.EventBus
 import io.legado.app.help.LauncherIconHelp
 import io.legado.app.help.LauncherIconHelp
 import io.legado.app.help.config.ThemeConfigStore
 import io.legado.app.help.config.ThemeConfigStore
@@ -347,12 +346,10 @@ fun ThemeConfigScreen(
                             context.toastOnUi(R.string.restart_to_apply)
                             context.toastOnUi(R.string.restart_to_apply)
                         }
                         }
                     )
                     )
+                    val fontScaleSummary = stringResource(R.string.font_scale_summary)
                     SliderSettingItem(
                     SliderSettingItem(
                         title = stringResource(R.string.font_scale),
                         title = stringResource(R.string.font_scale),
-                        description = stringResource(
-                            R.string.font_scale_summary,
-                            AppContextWrapper.getFontScale(context)
-                        ),
+                        valueLabel = { fontScaleSummary.format(it / 10f) },
                         value = fontScaleValue.floatValue,
                         value = fontScaleValue.floatValue,
                         defaultValue = 10f,
                         defaultValue = 10f,
                         valueRange = 8f..16f,
                         valueRange = 8f..16f,
@@ -499,7 +496,6 @@ fun ThemeConfigScreen(
                                 value = colorTemperature.toFloat(),
                                 value = colorTemperature.toFloat(),
                                 defaultValue = 50f,
                                 defaultValue = 50f,
                                 valueRange = 0f..100f,
                                 valueRange = 0f..100f,
-                                steps = 99,
                                 onValueChange = {
                                 onValueChange = {
                                     colorTemperature = it.toInt()
                                     colorTemperature = it.toInt()
                                     ThemeConfig.colorTemperature = it.toInt()
                                     ThemeConfig.colorTemperature = it.toInt()
@@ -647,7 +643,6 @@ fun ThemeConfigScreen(
                                 value = ThemeConfig.topBarOpacity.toFloat(),
                                 value = ThemeConfig.topBarOpacity.toFloat(),
                                 defaultValue = 100f,
                                 defaultValue = 100f,
                                 valueRange = 0f..100f,
                                 valueRange = 0f..100f,
-                                steps = 99,
                                 onValueChange = { ThemeConfig.topBarOpacity = it.toInt() }
                                 onValueChange = { ThemeConfig.topBarOpacity = it.toInt() }
                             )
                             )
                             SliderSettingItem(
                             SliderSettingItem(
@@ -659,7 +654,6 @@ fun ThemeConfigScreen(
                                 value = ThemeConfig.bottomBarOpacity.toFloat(),
                                 value = ThemeConfig.bottomBarOpacity.toFloat(),
                                 defaultValue = 100f,
                                 defaultValue = 100f,
                                 valueRange = 0f..100f,
                                 valueRange = 0f..100f,
-                                steps = 99,
                                 onValueChange = { ThemeConfig.bottomBarOpacity = it.toInt() }
                                 onValueChange = { ThemeConfig.bottomBarOpacity = it.toInt() }
                             )
                             )
                         }
                         }
@@ -674,16 +668,15 @@ fun ThemeConfigScreen(
                             value = ThemeConfig.containerOpacity.toFloat(),
                             value = ThemeConfig.containerOpacity.toFloat(),
                             defaultValue = 100f,
                             defaultValue = 100f,
                             valueRange = 0f..100f,
                             valueRange = 0f..100f,
-                            steps = 99,
                             onValueChange = { ThemeConfig.containerOpacity = it.toInt() }
                             onValueChange = { ThemeConfig.containerOpacity = it.toInt() }
                         )
                         )
                     }
                     }
                 }
                 }
 
 
-                SplicedColumnGroup(title = stringResource(R.string.day)) {
+                SplicedColumnGroup(title = stringResource(R.string.background_image)) {
                     val hasLightBg = !ThemeConfig.bgImageLight.isNullOrBlank()
                     val hasLightBg = !ThemeConfig.bgImageLight.isNullOrBlank()
                     ClickableSettingItem(
                     ClickableSettingItem(
-                        title = stringResource(R.string.background_image),
+                        title = stringResource(R.string.day),
                         description = if (hasLightBg) stringResource(R.string.click_to_delete) else stringResource(
                         description = if (hasLightBg) stringResource(R.string.click_to_delete) else stringResource(
                             R.string.select_image
                             R.string.select_image
                         ),
                         ),
@@ -696,18 +689,15 @@ fun ThemeConfigScreen(
                             value = ThemeConfig.bgImageBlurring.toFloat(),
                             value = ThemeConfig.bgImageBlurring.toFloat(),
                             defaultValue = 0f,
                             defaultValue = 0f,
                             valueRange = 0f..100f,
                             valueRange = 0f..100f,
-                            steps = 99,
                             onValueChange = {
                             onValueChange = {
                                 ThemeConfig.bgImageBlurring = it.toInt()
                                 ThemeConfig.bgImageBlurring = it.toInt()
                             }
                             }
                         )
                         )
                     }
                     }
-                }
 
 
-                SplicedColumnGroup(title = stringResource(R.string.night)) {
                     val hasDarkBg = !ThemeConfig.bgImageDark.isNullOrBlank()
                     val hasDarkBg = !ThemeConfig.bgImageDark.isNullOrBlank()
                     ClickableSettingItem(
                     ClickableSettingItem(
-                        title = stringResource(R.string.background_image),
+                        title = stringResource(R.string.night),
                         description = if (hasDarkBg) stringResource(R.string.click_to_delete) else stringResource(
                         description = if (hasDarkBg) stringResource(R.string.click_to_delete) else stringResource(
                             R.string.select_image
                             R.string.select_image
                         ),
                         ),
@@ -720,7 +710,6 @@ fun ThemeConfigScreen(
                             value = ThemeConfig.bgImageNBlurring.toFloat(),
                             value = ThemeConfig.bgImageNBlurring.toFloat(),
                             defaultValue = 0f,
                             defaultValue = 0f,
                             valueRange = 0f..100f,
                             valueRange = 0f..100f,
-                            steps = 99,
                             onValueChange = {
                             onValueChange = {
                                 ThemeConfig.bgImageNBlurring = it.toInt()
                                 ThemeConfig.bgImageNBlurring = it.toInt()
                             }
                             }
@@ -745,6 +734,7 @@ fun ThemeConfigScreen(
                             defaultValue = 1f,
                             defaultValue = 1f,
                             valueRange = 0f..5f,
                             valueRange = 0f..5f,
                             steps = 49,
                             steps = 49,
+                            decimal = true,
                             onValueChange = { ThemeConfig.itemDividerWidth = it }
                             onValueChange = { ThemeConfig.itemDividerWidth = it }
                         )
                         )
                         SliderSettingItem(
                         SliderSettingItem(

+ 27 - 20
app/src/main/java/io/legado/app/ui/widget/components/settingItem/SliderSettingItem.kt

@@ -53,6 +53,8 @@ fun SliderSettingItem(
     valueRange: ClosedFloatingPointRange<Float>,
     valueRange: ClosedFloatingPointRange<Float>,
     steps: Int = 0,
     steps: Int = 0,
     description: String? = null,
     description: String? = null,
+    valueLabel: ((Float) -> String)? = null,
+    decimal: Boolean = false,
     onValueChange: (Float) -> Unit
     onValueChange: (Float) -> Unit
 ) {
 ) {
 
 
@@ -61,16 +63,30 @@ fun SliderSettingItem(
     var sliderValue by remember(value) { mutableFloatStateOf(value) }
     var sliderValue by remember(value) { mutableFloatStateOf(value) }
     val textFieldState = rememberTextFieldState()
     val textFieldState = rememberTextFieldState()
 
 
+    // 默认整数模式:滑动吸附到整数;decimal = true 时保留 0.1 精度
+    fun snap(v: Float): Float =
+        if (decimal) (v * 10).roundToInt() / 10f else v.roundToInt().toFloat()
+
+    fun format(v: Float): String =
+        if (v % 1f == 0f) v.toInt().toString() else v.toString()
+
     LaunchedEffect(value) {
     LaunchedEffect(value) {
         sliderValue = value
         sliderValue = value
     }
     }
 
 
-    val sliderAccessibilityValue = description ?: sliderValue.toString()
+    // 拖动过程中让标题下的数值实时跟随滑块,松手后才真正应用
+    val displayDescription = when {
+        valueLabel != null -> valueLabel(sliderValue)
+        sliderValue != value -> format(sliderValue)
+        else -> description
+    }
+
+    val sliderAccessibilityValue = displayDescription ?: sliderValue.toString()
 
 
     LaunchedEffect(isInputMode) {
     LaunchedEffect(isInputMode) {
         if (isInputMode) {
         if (isInputMode) {
             textFieldState.edit {
             textFieldState.edit {
-                replace(0, length, value.toString())
+                replace(0, length, format(value))
             }
             }
         }
         }
     }
     }
@@ -78,8 +94,7 @@ fun SliderSettingItem(
     fun commitValue() {
     fun commitValue() {
         if (isInputMode) {
         if (isInputMode) {
             textFieldState.text.toString().toFloatOrNull()?.let { num ->
             textFieldState.text.toString().toFloatOrNull()?.let { num ->
-                val rounded = (num * 10).roundToInt() / 10f
-                onValueChange(rounded.coerceIn(valueRange))
+                onValueChange(snap(num).coerceIn(valueRange))
             }
             }
         } else if (sliderValue != value) {
         } else if (sliderValue != value) {
             onValueChange(sliderValue)
             onValueChange(sliderValue)
@@ -96,7 +111,7 @@ fun SliderSettingItem(
         ) {
         ) {
             BasicComponent(
             BasicComponent(
                 title = title,
                 title = title,
-                summary = description,
+                summary = displayDescription,
                 onClick = {
                 onClick = {
                     if (expanded) {
                     if (expanded) {
                         commitValue()
                         commitValue()
@@ -131,9 +146,9 @@ fun SliderSettingItem(
                             MiuixSlider(
                             MiuixSlider(
                                 value = sliderValue,
                                 value = sliderValue,
                                 onValueChange = {
                                 onValueChange = {
-                                    sliderValue = (it * 10).roundToInt() / 10f
+                                    sliderValue = snap(it)
                                     textFieldState.edit {
                                     textFieldState.edit {
-                                        replace(0, length, sliderValue.toString())
+                                        replace(0, length, format(sliderValue))
                                     }
                                     }
                                 },
                                 },
                                 onValueChangeFinished = {
                                 onValueChangeFinished = {
@@ -157,11 +172,7 @@ fun SliderSettingItem(
                         onConfirm = {
                         onConfirm = {
                             onValueChange(defaultValue)
                             onValueChange(defaultValue)
                             textFieldState.edit {
                             textFieldState.edit {
-                                replace(
-                                    0,
-                                    length,
-                                    defaultValue.toInt().toString()
-                                )
+                                replace(0, length, format(defaultValue))
                             }
                             }
                         },
                         },
                         dismissText = if (isInputMode) {
                         dismissText = if (isInputMode) {
@@ -178,7 +189,7 @@ fun SliderSettingItem(
     } else {
     } else {
         SettingItem(
         SettingItem(
             title = title,
             title = title,
-            option = description,
+            option = displayDescription,
             expanded = expanded,
             expanded = expanded,
             onExpandChange = {
             onExpandChange = {
                 if (expanded) {
                 if (expanded) {
@@ -220,9 +231,9 @@ fun SliderSettingItem(
                             Slider(
                             Slider(
                                 value = sliderValue,
                                 value = sliderValue,
                                 onValueChange = {
                                 onValueChange = {
-                                    sliderValue = (it * 10).roundToInt() / 10f
+                                    sliderValue = snap(it)
                                     textFieldState.edit {
                                     textFieldState.edit {
-                                        replace(0, length, sliderValue.toString())
+                                        replace(0, length, format(sliderValue))
                                     }
                                     }
                                 },
                                 },
                                 onValueChangeFinished = {
                                 onValueChangeFinished = {
@@ -247,11 +258,7 @@ fun SliderSettingItem(
                     onConfirm = {
                     onConfirm = {
                         onValueChange(defaultValue)
                         onValueChange(defaultValue)
                         textFieldState.edit {
                         textFieldState.edit {
-                            replace(
-                                0,
-                                length,
-                                defaultValue.toInt().toString()
-                            )
+                            replace(0, length, format(defaultValue))
                         }
                         }
                     },
                     },
                     dismissText = if (isInputMode) {
                     dismissText = if (isInputMode) {