Replace numberpicker with wheelpicker (#8501)

* Replace numberpicker with wheelpicker

* cleanups
This commit is contained in:
Ivan Iskandar 2022-11-12 03:02:45 +07:00 committed by GitHub
parent 3061f198e9
commit acc65529a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 101 additions and 90 deletions

View File

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

View File

@ -1,13 +1,15 @@
package eu.kanade.presentation.more.settings.screen package eu.kanade.presentation.more.settings.screen
import android.content.Context
import androidx.annotation.StringRes 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.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -21,15 +23,18 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue 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.draw.clipToBounds import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource 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.compose.ui.util.fastMap
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import com.bluelinelabs.conductor.Router 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.GetCategories
import eu.kanade.domain.category.interactor.ResetCategoryFlags import eu.kanade.domain.category.interactor.ResetCategoryFlags
import eu.kanade.domain.category.model.Category import eu.kanade.domain.category.model.Category
@ -78,7 +83,6 @@ class SettingsLibraryScreen : SearchableSettings {
@Composable @Composable
private fun getDisplayGroup(libraryPreferences: LibraryPreferences): Preference.PreferenceGroup { private fun getDisplayGroup(libraryPreferences: LibraryPreferences): Preference.PreferenceGroup {
val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val portraitColumns by libraryPreferences.portraitColumns().stateIn(scope).collectAsState() val portraitColumns by libraryPreferences.portraitColumns().stateIn(scope).collectAsState()
val landscapeColumns by libraryPreferences.landscapeColumns().stateIn(scope).collectAsState() val landscapeColumns by libraryPreferences.landscapeColumns().stateIn(scope).collectAsState()
@ -102,8 +106,8 @@ class SettingsLibraryScreen : SearchableSettings {
preferenceItems = listOf( preferenceItems = listOf(
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = stringResource(R.string.pref_library_columns), title = stringResource(R.string.pref_library_columns),
subtitle = "${stringResource(R.string.portrait)}: ${getColumnValue(context, portraitColumns)}, " + subtitle = "${stringResource(R.string.portrait)}: ${getColumnValue(portraitColumns)}, " +
"${stringResource(R.string.landscape)}: ${getColumnValue(context, landscapeColumns)}", "${stringResource(R.string.landscape)}: ${getColumnValue(landscapeColumns)}",
onClick = { showDialog = true }, onClick = { showDialog = true },
), ),
), ),
@ -273,7 +277,6 @@ class SettingsLibraryScreen : SearchableSettings {
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
onValueChanged: (portrait: Int, landscape: Int) -> Unit, onValueChanged: (portrait: Int, landscape: Int) -> Unit,
) { ) {
val context = LocalContext.current
var portraitValue by rememberSaveable { mutableStateOf(initialPortrait) } var portraitValue by rememberSaveable { mutableStateOf(initialPortrait) }
var landscapeValue by rememberSaveable { mutableStateOf(initialLandscape) } var landscapeValue by rememberSaveable { mutableStateOf(initialLandscape) }
@ -281,48 +284,30 @@ class SettingsLibraryScreen : SearchableSettings {
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
title = { Text(text = stringResource(R.string.pref_library_columns)) }, title = { Text(text = stringResource(R.string.pref_library_columns)) },
text = { text = {
Row { Column {
Column( Row {
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text( Text(
modifier = Modifier.weight(1f),
text = stringResource(R.string.portrait), text = stringResource(R.string.portrait),
textAlign = TextAlign.Center,
maxLines = 1,
style = MaterialTheme.typography.labelMedium, 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( Text(
modifier = Modifier.weight(1f),
text = stringResource(R.string.landscape), text = stringResource(R.string.landscape),
textAlign = TextAlign.Center,
maxLines = 1,
style = MaterialTheme.typography.labelMedium, 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 = { 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) { return if (value == 0) {
context.getString(R.string.label_default) stringResource(R.string.label_default)
} else { } else {
value.toString() value.toString()
} }

View File

@ -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)
}
}
}

View File

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

View File

@ -16,6 +16,9 @@ material3-core = { module = "androidx.compose.material3:material3" }
material3-adapter = "com.google.android.material:compose-theme-adapter-3:1.0.21" material3-adapter = "com.google.android.material:compose-theme-adapter-3:1.0.21"
material-icons = { module = "androidx.compose.material:material-icons-extended" } 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-webview = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanist" }
accompanist-swiperefresh = { module = "com.google.accompanist:accompanist-swiperefresh", 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" } accompanist-flowlayout = { module = "com.google.accompanist:accompanist-flowlayout", version.ref = "accompanist" }

View File

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