mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	Rework on the wheel picker (#8559)
* Rework the wheel picker doesn't need for the animation to stop to change the value * fix --------- Co-authored-by: arkon <arkon@users.noreply.github.com>
This commit is contained in:
		@@ -0,0 +1,286 @@
 | 
			
		||||
package eu.kanade.presentation.components
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.BorderStroke
 | 
			
		||||
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
import androidx.compose.foundation.layout.PaddingValues
 | 
			
		||||
import androidx.compose.foundation.layout.Row
 | 
			
		||||
import androidx.compose.foundation.layout.height
 | 
			
		||||
import androidx.compose.foundation.layout.size
 | 
			
		||||
import androidx.compose.foundation.layout.width
 | 
			
		||||
import androidx.compose.foundation.lazy.LazyItemScope
 | 
			
		||||
import androidx.compose.foundation.lazy.LazyListItemInfo
 | 
			
		||||
import androidx.compose.foundation.lazy.LazyListState
 | 
			
		||||
import androidx.compose.foundation.lazy.rememberLazyListState
 | 
			
		||||
import androidx.compose.foundation.shape.RoundedCornerShape
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.Text
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.LaunchedEffect
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.runtime.setValue
 | 
			
		||||
import androidx.compose.runtime.snapshotFlow
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.draw.alpha
 | 
			
		||||
import androidx.compose.ui.unit.DpSize
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import eu.kanade.presentation.util.padding
 | 
			
		||||
import kotlinx.coroutines.flow.collectLatest
 | 
			
		||||
import kotlinx.coroutines.flow.distinctUntilChanged
 | 
			
		||||
import kotlinx.coroutines.flow.map
 | 
			
		||||
import java.text.DateFormatSymbols
 | 
			
		||||
import java.time.LocalDate
 | 
			
		||||
import kotlin.math.absoluteValue
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun WheelPicker(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    startIndex: Int = 0,
 | 
			
		||||
    count: Int,
 | 
			
		||||
    size: DpSize = DpSize(128.dp, 128.dp),
 | 
			
		||||
    onSelectionChanged: (index: Int) -> Unit = {},
 | 
			
		||||
    backgroundContent: (@Composable (size: DpSize) -> Unit)? = {
 | 
			
		||||
        WheelPickerDefaults.Background(size = it)
 | 
			
		||||
    },
 | 
			
		||||
    itemContent: @Composable LazyItemScope.(index: Int) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    val lazyListState = rememberLazyListState(startIndex)
 | 
			
		||||
 | 
			
		||||
    LaunchedEffect(lazyListState, onSelectionChanged) {
 | 
			
		||||
        snapshotFlow { lazyListState.firstVisibleItemScrollOffset }
 | 
			
		||||
            .map { calculateSnappedItemIndex(lazyListState) }
 | 
			
		||||
            .distinctUntilChanged()
 | 
			
		||||
            .collectLatest {
 | 
			
		||||
                onSelectionChanged(it)
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Box(
 | 
			
		||||
        modifier = modifier,
 | 
			
		||||
        contentAlignment = Alignment.Center,
 | 
			
		||||
    ) {
 | 
			
		||||
        backgroundContent?.invoke(size)
 | 
			
		||||
 | 
			
		||||
        LazyColumn(
 | 
			
		||||
            modifier = Modifier
 | 
			
		||||
                .height(size.height)
 | 
			
		||||
                .width(size.width),
 | 
			
		||||
            state = lazyListState,
 | 
			
		||||
            contentPadding = PaddingValues(vertical = size.height / RowCount * ((RowCount - 1) / 2)),
 | 
			
		||||
            flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState),
 | 
			
		||||
        ) {
 | 
			
		||||
            items(count) { index ->
 | 
			
		||||
                Box(
 | 
			
		||||
                    modifier = Modifier
 | 
			
		||||
                        .height(size.height / RowCount)
 | 
			
		||||
                        .width(size.width)
 | 
			
		||||
                        .alpha(
 | 
			
		||||
                            calculateAnimatedAlpha(
 | 
			
		||||
                                lazyListState = lazyListState,
 | 
			
		||||
                                index = index,
 | 
			
		||||
                            ),
 | 
			
		||||
                        ),
 | 
			
		||||
                    contentAlignment = Alignment.Center,
 | 
			
		||||
                ) {
 | 
			
		||||
                    itemContent(index)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun WheelTextPicker(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    startIndex: Int = 0,
 | 
			
		||||
    texts: List<String>,
 | 
			
		||||
    size: DpSize = DpSize(128.dp, 128.dp),
 | 
			
		||||
    onSelectionChanged: (index: Int) -> Unit = {},
 | 
			
		||||
    backgroundContent: (@Composable (size: DpSize) -> Unit)? = {
 | 
			
		||||
        WheelPickerDefaults.Background(size = it)
 | 
			
		||||
    },
 | 
			
		||||
) {
 | 
			
		||||
    WheelPicker(
 | 
			
		||||
        modifier = modifier,
 | 
			
		||||
        startIndex = startIndex,
 | 
			
		||||
        count = remember(texts) { texts.size },
 | 
			
		||||
        size = size,
 | 
			
		||||
        onSelectionChanged = onSelectionChanged,
 | 
			
		||||
        backgroundContent = backgroundContent,
 | 
			
		||||
    ) {
 | 
			
		||||
        WheelPickerDefaults.Item(text = texts[it])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun WheelDatePicker(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    startDate: LocalDate = LocalDate.now(),
 | 
			
		||||
    minDate: LocalDate? = null,
 | 
			
		||||
    maxDate: LocalDate? = null,
 | 
			
		||||
    size: DpSize = DpSize(256.dp, 128.dp),
 | 
			
		||||
    backgroundContent: (@Composable (size: DpSize) -> Unit)? = {
 | 
			
		||||
        WheelPickerDefaults.Background(size = it)
 | 
			
		||||
    },
 | 
			
		||||
    onSelectionChanged: (date: LocalDate) -> Unit = {},
 | 
			
		||||
) {
 | 
			
		||||
    var internalSelection by remember { mutableStateOf(startDate) }
 | 
			
		||||
    val internalOnSelectionChange: (LocalDate) -> Unit = {
 | 
			
		||||
        internalSelection = it
 | 
			
		||||
        onSelectionChanged(internalSelection)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Box(modifier = modifier, contentAlignment = Alignment.Center) {
 | 
			
		||||
        backgroundContent?.invoke(size)
 | 
			
		||||
        Row {
 | 
			
		||||
            val singularPickerSize = DpSize(
 | 
			
		||||
                width = size.width / 3,
 | 
			
		||||
                height = size.height,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            // Day
 | 
			
		||||
            val dayOfMonths = remember(internalSelection, minDate, maxDate) {
 | 
			
		||||
                if (minDate == null && maxDate == null) {
 | 
			
		||||
                    1..internalSelection.lengthOfMonth()
 | 
			
		||||
                } else {
 | 
			
		||||
                    val minDay = if (minDate?.month == internalSelection.month &&
 | 
			
		||||
                        minDate?.year == internalSelection.year
 | 
			
		||||
                    ) {
 | 
			
		||||
                        minDate.dayOfMonth
 | 
			
		||||
                    } else {
 | 
			
		||||
                        1
 | 
			
		||||
                    }
 | 
			
		||||
                    val maxDay = if (maxDate?.month == internalSelection.month &&
 | 
			
		||||
                        maxDate?.year == internalSelection.year
 | 
			
		||||
                    ) {
 | 
			
		||||
                        maxDate.dayOfMonth
 | 
			
		||||
                    } else {
 | 
			
		||||
                        31
 | 
			
		||||
                    }
 | 
			
		||||
                    minDay..maxDay.coerceAtMost(internalSelection.lengthOfMonth())
 | 
			
		||||
                }.toList()
 | 
			
		||||
            }
 | 
			
		||||
            WheelTextPicker(
 | 
			
		||||
                size = singularPickerSize,
 | 
			
		||||
                texts = dayOfMonths.map { it.toString() },
 | 
			
		||||
                backgroundContent = null,
 | 
			
		||||
                startIndex = dayOfMonths.indexOfFirst { it == startDate.dayOfMonth }.coerceAtLeast(0),
 | 
			
		||||
                onSelectionChanged = { index ->
 | 
			
		||||
                    val newDayOfMonth = dayOfMonths[index]
 | 
			
		||||
                    internalOnSelectionChange(internalSelection.withDayOfMonth(newDayOfMonth))
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            // Month
 | 
			
		||||
            val months = remember(internalSelection, minDate, maxDate) {
 | 
			
		||||
                val monthRange = if (minDate == null && maxDate == null) {
 | 
			
		||||
                    1..12
 | 
			
		||||
                } else {
 | 
			
		||||
                    val minMonth = if (minDate?.year == internalSelection.year) {
 | 
			
		||||
                        minDate.monthValue
 | 
			
		||||
                    } else {
 | 
			
		||||
                        1
 | 
			
		||||
                    }
 | 
			
		||||
                    val maxMonth = if (maxDate?.year == internalSelection.year) {
 | 
			
		||||
                        maxDate.monthValue
 | 
			
		||||
                    } else {
 | 
			
		||||
                        12
 | 
			
		||||
                    }
 | 
			
		||||
                    minMonth..maxMonth
 | 
			
		||||
                }
 | 
			
		||||
                val dateFormatSymbols = DateFormatSymbols()
 | 
			
		||||
                monthRange.map { it to dateFormatSymbols.months[it - 1] }
 | 
			
		||||
            }
 | 
			
		||||
            WheelTextPicker(
 | 
			
		||||
                size = singularPickerSize,
 | 
			
		||||
                texts = months.map { it.second },
 | 
			
		||||
                backgroundContent = null,
 | 
			
		||||
                startIndex = months.indexOfFirst { it.first == startDate.monthValue }.coerceAtLeast(0),
 | 
			
		||||
                onSelectionChanged = { index ->
 | 
			
		||||
                    val newMonth = months[index].first
 | 
			
		||||
                    internalOnSelectionChange(internalSelection.withMonth(newMonth))
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            // Year
 | 
			
		||||
            val years = remember(minDate, maxDate) {
 | 
			
		||||
                val minYear = minDate?.year?.coerceAtLeast(1900) ?: 1900
 | 
			
		||||
                val maxYear = maxDate?.year?.coerceAtMost(2100) ?: 2100
 | 
			
		||||
                val yearRange = minYear..maxYear
 | 
			
		||||
                yearRange.toList()
 | 
			
		||||
            }
 | 
			
		||||
            WheelTextPicker(
 | 
			
		||||
                size = singularPickerSize,
 | 
			
		||||
                texts = years.map { it.toString() },
 | 
			
		||||
                backgroundContent = null,
 | 
			
		||||
                startIndex = years.indexOfFirst { it == startDate.year }.coerceAtLeast(0),
 | 
			
		||||
                onSelectionChanged = { index ->
 | 
			
		||||
                    val newYear = years[index]
 | 
			
		||||
                    internalOnSelectionChange(internalSelection.withYear(newYear))
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private fun LazyListState.snapOffsetForItem(itemInfo: LazyListItemInfo): Int {
 | 
			
		||||
    val startScrollOffset = 0
 | 
			
		||||
    val endScrollOffset = layoutInfo.let { it.viewportEndOffset - it.afterContentPadding }
 | 
			
		||||
    return startScrollOffset + (endScrollOffset - startScrollOffset - itemInfo.size) / 2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private fun LazyListState.distanceToSnapForIndex(index: Int): Int {
 | 
			
		||||
    val itemInfo = layoutInfo.visibleItemsInfo.firstOrNull { it.index == index }
 | 
			
		||||
    if (itemInfo != null) {
 | 
			
		||||
        return itemInfo.offset - snapOffsetForItem(itemInfo)
 | 
			
		||||
    }
 | 
			
		||||
    return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private fun calculateAnimatedAlpha(
 | 
			
		||||
    lazyListState: LazyListState,
 | 
			
		||||
    index: Int,
 | 
			
		||||
): Float {
 | 
			
		||||
    val distanceToIndexSnap = lazyListState.distanceToSnapForIndex(index).absoluteValue
 | 
			
		||||
    val viewPortHeight = lazyListState.layoutInfo.viewportSize.height.toFloat()
 | 
			
		||||
    val singleViewPortHeight = viewPortHeight / RowCount
 | 
			
		||||
    return if (distanceToIndexSnap in 0..singleViewPortHeight.toInt()) {
 | 
			
		||||
        1.2f - (distanceToIndexSnap / singleViewPortHeight)
 | 
			
		||||
    } else {
 | 
			
		||||
        0.2f
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private fun calculateSnappedItemIndex(lazyListState: LazyListState): Int {
 | 
			
		||||
    return lazyListState.layoutInfo.visibleItemsInfo
 | 
			
		||||
        .maxBy { calculateAnimatedAlpha(lazyListState, it.index) }
 | 
			
		||||
        .index
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
object WheelPickerDefaults {
 | 
			
		||||
    @Composable
 | 
			
		||||
    fun Background(size: DpSize) {
 | 
			
		||||
        androidx.compose.material3.Surface(
 | 
			
		||||
            modifier = Modifier
 | 
			
		||||
                .size(size.width, size.height / RowCount),
 | 
			
		||||
            shape = RoundedCornerShape(MaterialTheme.padding.medium),
 | 
			
		||||
            color = MaterialTheme.colorScheme.primary.copy(alpha = 0.2f),
 | 
			
		||||
            border = BorderStroke(1.dp, MaterialTheme.colorScheme.primary),
 | 
			
		||||
            content = {},
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Composable
 | 
			
		||||
    fun Item(text: String) {
 | 
			
		||||
        Text(
 | 
			
		||||
            text = text,
 | 
			
		||||
            style = MaterialTheme.typography.titleMedium,
 | 
			
		||||
            maxLines = 1,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private const val RowCount = 3
 | 
			
		||||
@@ -29,11 +29,11 @@ import androidx.compose.ui.draw.clip
 | 
			
		||||
import androidx.compose.ui.res.stringResource
 | 
			
		||||
import androidx.compose.ui.text.style.TextAlign
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import com.commandiron.wheel_picker_compose.WheelDatePicker
 | 
			
		||||
import com.commandiron.wheel_picker_compose.WheelTextPicker
 | 
			
		||||
import eu.kanade.presentation.components.AlertDialogContent
 | 
			
		||||
import eu.kanade.presentation.components.Divider
 | 
			
		||||
import eu.kanade.presentation.components.ScrollbarLazyColumn
 | 
			
		||||
import eu.kanade.presentation.components.WheelDatePicker
 | 
			
		||||
import eu.kanade.presentation.components.WheelTextPicker
 | 
			
		||||
import eu.kanade.presentation.util.isScrolledToEnd
 | 
			
		||||
import eu.kanade.presentation.util.isScrolledToStart
 | 
			
		||||
import eu.kanade.presentation.util.minimumTouchTargetSize
 | 
			
		||||
@@ -103,12 +103,9 @@ fun TrackChapterSelector(
 | 
			
		||||
        content = {
 | 
			
		||||
            WheelTextPicker(
 | 
			
		||||
                modifier = Modifier.align(Alignment.Center),
 | 
			
		||||
                texts = range.map { "$it" },
 | 
			
		||||
                onScrollFinished = {
 | 
			
		||||
                    onSelectionChange(it)
 | 
			
		||||
                    null
 | 
			
		||||
                },
 | 
			
		||||
                startIndex = selection,
 | 
			
		||||
                texts = range.map { "$it" },
 | 
			
		||||
                onSelectionChanged = { onSelectionChange(it) },
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        onConfirm = onConfirm,
 | 
			
		||||
@@ -129,12 +126,9 @@ fun TrackScoreSelector(
 | 
			
		||||
        content = {
 | 
			
		||||
            WheelTextPicker(
 | 
			
		||||
                modifier = Modifier.align(Alignment.Center),
 | 
			
		||||
                texts = selections,
 | 
			
		||||
                onScrollFinished = {
 | 
			
		||||
                    onSelectionChange(selections[it])
 | 
			
		||||
                    null
 | 
			
		||||
                },
 | 
			
		||||
                startIndex = selections.indexOf(selection).coerceAtLeast(0),
 | 
			
		||||
                texts = selections,
 | 
			
		||||
                onSelectionChanged = { onSelectionChange(selections[it]) },
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        onConfirm = onConfirm,
 | 
			
		||||
@@ -145,6 +139,8 @@ fun TrackScoreSelector(
 | 
			
		||||
@Composable
 | 
			
		||||
fun TrackDateSelector(
 | 
			
		||||
    title: String,
 | 
			
		||||
    minDate: LocalDate?,
 | 
			
		||||
    maxDate: LocalDate?,
 | 
			
		||||
    selection: LocalDate,
 | 
			
		||||
    onSelectionChange: (LocalDate) -> Unit,
 | 
			
		||||
    onConfirm: () -> Unit,
 | 
			
		||||
@@ -170,7 +166,9 @@ fun TrackDateSelector(
 | 
			
		||||
                )
 | 
			
		||||
                WheelDatePicker(
 | 
			
		||||
                    startDate = selection,
 | 
			
		||||
                    onScrollFinished = {
 | 
			
		||||
                    minDate = minDate,
 | 
			
		||||
                    maxDate = maxDate,
 | 
			
		||||
                    onSelectionChanged = {
 | 
			
		||||
                        internalSelection = it
 | 
			
		||||
                        onSelectionChange(it)
 | 
			
		||||
                    },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,12 @@
 | 
			
		||||
package eu.kanade.presentation.more.settings.screen
 | 
			
		||||
 | 
			
		||||
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.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.Surface
 | 
			
		||||
import androidx.compose.material3.Text
 | 
			
		||||
import androidx.compose.material3.TextButton
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
@@ -23,7 +20,6 @@ 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.alpha
 | 
			
		||||
import androidx.compose.ui.platform.LocalContext
 | 
			
		||||
import androidx.compose.ui.res.pluralStringResource
 | 
			
		||||
import androidx.compose.ui.res.stringResource
 | 
			
		||||
@@ -35,10 +31,11 @@ import androidx.core.content.ContextCompat
 | 
			
		||||
import cafe.adriel.voyager.navigator.LocalNavigator
 | 
			
		||||
import cafe.adriel.voyager.navigator.Navigator
 | 
			
		||||
import cafe.adriel.voyager.navigator.currentOrThrow
 | 
			
		||||
import com.commandiron.wheel_picker_compose.WheelPicker
 | 
			
		||||
import eu.kanade.domain.category.interactor.ResetCategoryFlags
 | 
			
		||||
import eu.kanade.domain.library.service.LibraryPreferences
 | 
			
		||||
import eu.kanade.presentation.category.visualName
 | 
			
		||||
import eu.kanade.presentation.components.WheelPicker
 | 
			
		||||
import eu.kanade.presentation.components.WheelPickerDefaults
 | 
			
		||||
import eu.kanade.presentation.more.settings.Preference
 | 
			
		||||
import eu.kanade.presentation.more.settings.widget.TriStateListDialog
 | 
			
		||||
import eu.kanade.presentation.util.collectAsState
 | 
			
		||||
@@ -337,12 +334,7 @@ object SettingsLibraryScreen : SearchableSettings {
 | 
			
		||||
            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),
 | 
			
		||||
            ) {}
 | 
			
		||||
            WheelPickerDefaults.Background(size = DpSize(maxWidth, maxHeight))
 | 
			
		||||
 | 
			
		||||
            val size = DpSize(width = maxWidth / 2, height = 128.dp)
 | 
			
		||||
            Row {
 | 
			
		||||
@@ -350,48 +342,24 @@ object SettingsLibraryScreen : SearchableSettings {
 | 
			
		||||
                    size = size,
 | 
			
		||||
                    count = 11,
 | 
			
		||||
                    startIndex = portraitValue,
 | 
			
		||||
                    onScrollFinished = {
 | 
			
		||||
                        onPortraitChange(it)
 | 
			
		||||
                        null
 | 
			
		||||
                    },
 | 
			
		||||
                ) { index, snappedIndex ->
 | 
			
		||||
                    ColumnPickerLabel(index = index, snappedIndex = snappedIndex)
 | 
			
		||||
                    onSelectionChanged = onPortraitChange,
 | 
			
		||||
                    backgroundContent = null,
 | 
			
		||||
                ) { index ->
 | 
			
		||||
                    WheelPickerDefaults.Item(text = getColumnValue(value = index))
 | 
			
		||||
                }
 | 
			
		||||
                WheelPicker(
 | 
			
		||||
                    size = size,
 | 
			
		||||
                    count = 11,
 | 
			
		||||
                    startIndex = landscapeValue,
 | 
			
		||||
                    onScrollFinished = {
 | 
			
		||||
                        onLandscapeChange(it)
 | 
			
		||||
                        null
 | 
			
		||||
                    },
 | 
			
		||||
                ) { index, snappedIndex ->
 | 
			
		||||
                    ColumnPickerLabel(index = index, snappedIndex = snappedIndex)
 | 
			
		||||
                    onSelectionChanged = onLandscapeChange,
 | 
			
		||||
                    backgroundContent = null,
 | 
			
		||||
                ) { index ->
 | 
			
		||||
                    WheelPickerDefaults.Item(text = getColumnValue(value = index))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -445,6 +445,19 @@ private data class TrackDateSelectorScreen(
 | 
			
		||||
            } else {
 | 
			
		||||
                stringResource(R.string.track_finished_reading_date)
 | 
			
		||||
            },
 | 
			
		||||
            minDate = if (!start && track.started_reading_date > 0) {
 | 
			
		||||
                // Disallow end date to be set earlier than start date
 | 
			
		||||
                Instant.ofEpochMilli(track.started_reading_date).atZone(ZoneId.systemDefault()).toLocalDate()
 | 
			
		||||
            } else {
 | 
			
		||||
                null
 | 
			
		||||
            },
 | 
			
		||||
            maxDate = if (start && track.finished_reading_date > 0) {
 | 
			
		||||
                // Disallow start date to be set later than finish date
 | 
			
		||||
                Instant.ofEpochMilli(track.finished_reading_date).atZone(ZoneId.systemDefault()).toLocalDate()
 | 
			
		||||
            } else {
 | 
			
		||||
                // Disallow future dates
 | 
			
		||||
                LocalDate.now()
 | 
			
		||||
            },
 | 
			
		||||
            selection = state.selection,
 | 
			
		||||
            onSelectionChange = sm::setSelection,
 | 
			
		||||
            onConfirm = { sm.setDate(); navigator.pop() },
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user