Parcourir la source

Replace numberpicker with wheelpicker (#8501)

* Replace numberpicker with wheelpicker

* cleanups
Ivan Iskandar il y a 2 ans
Parent
commit
acc65529a0

+ 1 - 1
app/build.gradle.kts

@@ -173,6 +173,7 @@ dependencies {
     implementation(compose.foundation)
     implementation(compose.material3.core)
     implementation(compose.material3.adapter)
+    implementation(compose.material.core)
     implementation(compose.material.icons)
     implementation(compose.animation)
     implementation(compose.animation.graphics)
@@ -269,7 +270,6 @@ dependencies {
     implementation(libs.markwon)
     implementation(libs.aboutLibraries.compose)
     implementation(libs.cascade)
-    implementation(libs.numberpicker)
     implementation(libs.bundles.voyager)
     implementation(libs.wheelpicker)
 

+ 97 - 43
app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt

@@ -1,13 +1,15 @@
 package eu.kanade.presentation.more.settings.screen
 
-import android.content.Context
 import androidx.annotation.StringRes
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.layout.BoxWithConstraints
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.size
 import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.LocalTextStyle
 import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextButton
 import androidx.compose.runtime.Composable
@@ -21,15 +23,18 @@ import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clipToBounds
+import androidx.compose.ui.draw.alpha
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.pluralStringResource
 import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastMap
 import androidx.core.content.ContextCompat
 import cafe.adriel.voyager.navigator.currentOrThrow
 import com.bluelinelabs.conductor.Router
-import com.chargemap.compose.numberpicker.NumberPicker
+import com.commandiron.wheel_picker_compose.WheelPicker
 import eu.kanade.domain.category.interactor.GetCategories
 import eu.kanade.domain.category.interactor.ResetCategoryFlags
 import eu.kanade.domain.category.model.Category
@@ -78,7 +83,6 @@ class SettingsLibraryScreen : SearchableSettings {
 
     @Composable
     private fun getDisplayGroup(libraryPreferences: LibraryPreferences): Preference.PreferenceGroup {
-        val context = LocalContext.current
         val scope = rememberCoroutineScope()
         val portraitColumns by libraryPreferences.portraitColumns().stateIn(scope).collectAsState()
         val landscapeColumns by libraryPreferences.landscapeColumns().stateIn(scope).collectAsState()
@@ -102,8 +106,8 @@ class SettingsLibraryScreen : SearchableSettings {
             preferenceItems = listOf(
                 Preference.PreferenceItem.TextPreference(
                     title = stringResource(R.string.pref_library_columns),
-                    subtitle = "${stringResource(R.string.portrait)}: ${getColumnValue(context, portraitColumns)}, " +
-                        "${stringResource(R.string.landscape)}: ${getColumnValue(context, landscapeColumns)}",
+                    subtitle = "${stringResource(R.string.portrait)}: ${getColumnValue(portraitColumns)}, " +
+                        "${stringResource(R.string.landscape)}: ${getColumnValue(landscapeColumns)}",
                     onClick = { showDialog = true },
                 ),
             ),
@@ -273,7 +277,6 @@ class SettingsLibraryScreen : SearchableSettings {
         onDismissRequest: () -> Unit,
         onValueChanged: (portrait: Int, landscape: Int) -> Unit,
     ) {
-        val context = LocalContext.current
         var portraitValue by rememberSaveable { mutableStateOf(initialPortrait) }
         var landscapeValue by rememberSaveable { mutableStateOf(initialLandscape) }
 
@@ -281,48 +284,30 @@ class SettingsLibraryScreen : SearchableSettings {
             onDismissRequest = onDismissRequest,
             title = { Text(text = stringResource(R.string.pref_library_columns)) },
             text = {
-                Row {
-                    Column(
-                        modifier = Modifier.weight(1f),
-                        horizontalAlignment = Alignment.CenterHorizontally,
-                    ) {
+                Column {
+                    Row {
                         Text(
+                            modifier = Modifier.weight(1f),
                             text = stringResource(R.string.portrait),
+                            textAlign = TextAlign.Center,
+                            maxLines = 1,
                             style = MaterialTheme.typography.labelMedium,
                         )
-                        NumberPicker(
-                            modifier = Modifier
-                                .fillMaxWidth()
-                                .clipToBounds(),
-                            value = portraitValue,
-                            onValueChange = { portraitValue = it },
-                            range = 0..10,
-                            label = { getColumnValue(context, it) },
-                            dividersColor = MaterialTheme.colorScheme.primary,
-                            textStyle = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.onSurface),
-                        )
-                    }
-
-                    Column(
-                        modifier = Modifier.weight(1f),
-                        horizontalAlignment = Alignment.CenterHorizontally,
-                    ) {
                         Text(
+                            modifier = Modifier.weight(1f),
                             text = stringResource(R.string.landscape),
+                            textAlign = TextAlign.Center,
+                            maxLines = 1,
                             style = MaterialTheme.typography.labelMedium,
                         )
-                        NumberPicker(
-                            modifier = Modifier
-                                .fillMaxWidth()
-                                .clipToBounds(),
-                            value = landscapeValue,
-                            onValueChange = { landscapeValue = it },
-                            range = 0..10,
-                            label = { getColumnValue(context, it) },
-                            dividersColor = MaterialTheme.colorScheme.primary,
-                            textStyle = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.onSurface),
-                        )
                     }
+                    LibraryColumnsPicker(
+                        modifier = Modifier.fillMaxWidth(),
+                        portraitValue = portraitValue,
+                        onPortraitChange = { portraitValue = it },
+                        landscapeValue = landscapeValue,
+                        onLandscapeChange = { landscapeValue = it },
+                    )
                 }
             },
             dismissButton = {
@@ -338,9 +323,78 @@ class SettingsLibraryScreen : SearchableSettings {
         )
     }
 
-    private fun getColumnValue(context: Context, value: Int): String {
+    @Composable
+    private fun LibraryColumnsPicker(
+        modifier: Modifier = Modifier,
+        portraitValue: Int,
+        onPortraitChange: (Int) -> Unit,
+        landscapeValue: Int,
+        onLandscapeChange: (Int) -> Unit,
+    ) {
+        BoxWithConstraints(
+            modifier = modifier,
+            contentAlignment = Alignment.Center,
+        ) {
+            Surface(
+                modifier = Modifier.size(maxWidth, maxHeight / 3),
+                shape = MaterialTheme.shapes.large,
+                color = MaterialTheme.colorScheme.primary.copy(alpha = 0.2f),
+                border = BorderStroke(1.dp, MaterialTheme.colorScheme.primary),
+            ) {}
+
+            val size = DpSize(width = maxWidth / 2, height = 128.dp)
+            Row {
+                WheelPicker(
+                    size = size,
+                    count = 10,
+                    startIndex = portraitValue,
+                    onScrollFinished = {
+                        onPortraitChange(it)
+                        null
+                    },
+                ) { index, snappedIndex ->
+                    ColumnPickerLabel(index = index, snappedIndex = snappedIndex)
+                }
+                WheelPicker(
+                    size = size,
+                    count = 10,
+                    startIndex = landscapeValue,
+                    onScrollFinished = {
+                        onLandscapeChange(it)
+                        null
+                    },
+                ) { index, snappedIndex ->
+                    ColumnPickerLabel(index = index, snappedIndex = snappedIndex)
+                }
+            }
+        }
+    }
+
+    @Composable
+    private fun ColumnPickerLabel(
+        index: Int,
+        snappedIndex: Int,
+    ) {
+        Text(
+            modifier = Modifier.alpha(
+                when (snappedIndex) {
+                    index + 1 -> 0.2f
+                    index -> 1f
+                    index - 1 -> 0.2f
+                    else -> 0.2f
+                },
+            ),
+            text = getColumnValue(index),
+            style = MaterialTheme.typography.titleMedium,
+            maxLines = 1,
+        )
+    }
+
+    @Composable
+    @ReadOnlyComposable
+    private fun getColumnValue(value: Int): String {
         return if (value == 0) {
-            context.getString(R.string.label_default)
+            stringResource(R.string.label_default)
         } else {
             value.toString()
         }

+ 0 - 40
app/src/main/java/eu/kanade/tachiyomi/widget/MinMaxNumberPicker.kt

@@ -1,40 +0,0 @@
-package eu.kanade.tachiyomi.widget
-
-import android.content.Context
-import android.text.InputType
-import android.util.AttributeSet
-import android.widget.EditText
-import android.widget.NumberPicker
-import androidx.core.view.doOnLayout
-import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.util.view.findDescendant
-
-class MinMaxNumberPicker @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
-    NumberPicker(context, attrs) {
-
-    override fun setDisplayedValues(displayedValues: Array<out String>?) {
-        super.setDisplayedValues(displayedValues)
-
-        // Disable keyboard input when a value that can't be auto-filled with number exists
-        val notNumberValue = displayedValues?.find { it.getOrNull(0)?.digitToIntOrNull() == null }
-        if (notNumberValue != null) {
-            descendantFocusability = FOCUS_BLOCK_DESCENDANTS
-        }
-    }
-
-    init {
-        if (attrs != null) {
-            val ta = context.obtainStyledAttributes(attrs, R.styleable.MinMaxNumberPicker, 0, 0)
-            try {
-                minValue = ta.getInt(R.styleable.MinMaxNumberPicker_min, 0)
-                maxValue = ta.getInt(R.styleable.MinMaxNumberPicker_max, 0)
-            } finally {
-                ta.recycle()
-            }
-        }
-
-        doOnLayout {
-            findDescendant<EditText>()?.setRawInputType(InputType.TYPE_CLASS_NUMBER)
-        }
-    }
-}

+ 0 - 5
app/src/main/res/values/attrs.xml

@@ -1,11 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 
-    <declare-styleable name="MinMaxNumberPicker">
-        <attr name="min" format="integer"/>
-        <attr name="max" format="integer"/>
-    </declare-styleable>
-
     <declare-styleable name="MaterialSpinnerView">
         <attr name="title" format="reference|string"/>
         <attr name="android:entries"/>

+ 3 - 0
gradle/compose.versions.toml

@@ -16,6 +16,9 @@ material3-core = { module = "androidx.compose.material3:material3" }
 material3-adapter = "com.google.android.material:compose-theme-adapter-3:1.0.21"
 material-icons = { module = "androidx.compose.material:material-icons-extended" }
 
+# Here until M3's swipeable became public https://issuetracker.google.com/issues/234640556
+material-core = { module = "androidx.compose.material:material" }
+
 accompanist-webview = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanist" }
 accompanist-swiperefresh = { module = "com.google.accompanist:accompanist-swiperefresh", version.ref = "accompanist" }
 accompanist-flowlayout = { module = "com.google.accompanist:accompanist-flowlayout", version.ref = "accompanist" }

+ 0 - 1
gradle/libs.versions.toml

@@ -63,7 +63,6 @@ photoview = "com.github.chrisbanes:PhotoView:2.3.0"
 directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0"
 insetter = "dev.chrisbanes.insetter:insetter:0.6.1"
 cascade = "me.saket.cascade:cascade-compose:2.0.0-beta1"
-numberpicker = "com.chargemap.compose:numberpicker:1.0.3"
 wheelpicker = "com.github.commandiron:WheelPickerCompose:1.0.11"
 
 conductor-core = { module = "com.bluelinelabs:conductor", version.ref = "conductor_version" }