From 6cf789e747f09792f6bafef89d7909fe9787647b Mon Sep 17 00:00:00 2001 From: "Cuong M. Tran" Date: Mon, 20 May 2024 13:16:40 +0700 Subject: [PATCH] Revert back LazyList.isScroll... & fix menuAnchor deprecation caused by Bump compose version (#9682b8d9) --- .../presentation/browse/SourcesScreen.kt | 113 ++++++++---------- .../core/components/SettingsItems.kt | 3 +- .../presentation/core/util/LazyListState.kt | 59 +++++++++ 3 files changed, 113 insertions(+), 62 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt index cdb8837a4..ca9d93b87 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt @@ -34,14 +34,11 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester @@ -76,6 +73,7 @@ import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.theme.header import tachiyomi.presentation.core.util.clearFocusOnSoftKeyboardHide +import tachiyomi.presentation.core.util.isScrollingUp import tachiyomi.presentation.core.util.plus import tachiyomi.presentation.core.util.runOnEnterKeyPressed import tachiyomi.presentation.core.util.secondaryItemAlpha @@ -172,9 +170,9 @@ fun AnimatedFloatingSearchBox( placeholderText: String? = null, ) { AnimatedVisibility( - visible = listState.isScrollingUp().value, + visible = listState.isScrollingUp(), enter = expandVertically(), - exit = shrinkVertically() + exit = shrinkVertically(), ) { SourcesSearch( searchQuery = searchQuery, @@ -184,24 +182,6 @@ fun AnimatedFloatingSearchBox( } } -@Composable -fun LazyListState.isScrollingUp(): State { - return produceState(initialValue = true) { - var lastIndex = 0 - var lastScroll = Int.MAX_VALUE - snapshotFlow { - firstVisibleItemIndex to firstVisibleItemScrollOffset - }.collect { (currentIndex, currentScroll) -> - if (currentIndex != lastIndex || currentScroll != lastScroll) { - value = currentIndex < lastIndex || - (currentIndex == lastIndex && currentScroll < lastScroll) - lastIndex = currentIndex - lastScroll = currentScroll - } - } - } -} - @Composable fun SourcesSearch( searchQuery: String?, @@ -250,39 +230,47 @@ fun SourcesSearch( keyboardActions = KeyboardActions(onSearch = { searchAndClearFocus() }), singleLine = true, cursorBrush = SolidColor(MaterialTheme.colorScheme.onBackground), - decorationBox = { innerTextField -> TextFieldDefaults.DecorationBox( - value = searchQuery ?: "", - innerTextField = innerTextField, - enabled = true, - singleLine = true, - visualTransformation = VisualTransformation.None, - interactionSource = remember { MutableInteractionSource() }, - placeholder = { Text( - modifier = Modifier.secondaryItemAlpha(), - text = (placeholderText ?: stringResource(MR.strings.action_search_hint)), - maxLines = 1, - overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.bodyMedium, - ) }, - leadingIcon = { SearchBoxLeadingIcon( - isFocused || !searchQuery.isNullOrBlank(), - modifier = Modifier, - onClickCloseSearch, - ) }, - trailingIcon = { SearchBoxTrailingIcon( - searchQuery.isNullOrEmpty(), - modifier = Modifier, - onClickClearSearch, - ) }, - shape = RoundedCornerShape(24.dp), - colors = TextFieldDefaults.colors( - focusedIndicatorColor = Color.Transparent, - unfocusedIndicatorColor = Color.Transparent, - errorIndicatorColor = Color.Transparent, - cursorColor = MaterialTheme.colorScheme.onBackground, - ), - contentPadding = PaddingValues(12.dp), - ) } + decorationBox = { innerTextField -> + TextFieldDefaults.DecorationBox( + value = searchQuery ?: "", + innerTextField = innerTextField, + enabled = true, + singleLine = true, + visualTransformation = VisualTransformation.None, + interactionSource = remember { MutableInteractionSource() }, + placeholder = { + Text( + modifier = Modifier.secondaryItemAlpha(), + text = (placeholderText ?: stringResource(MR.strings.action_search_hint)), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.bodyMedium, + ) + }, + leadingIcon = { + SearchBoxLeadingIcon( + isFocused || !searchQuery.isNullOrBlank(), + modifier = Modifier, + onClickCloseSearch, + ) + }, + trailingIcon = { + SearchBoxTrailingIcon( + searchQuery.isNullOrEmpty(), + modifier = Modifier, + onClickClearSearch, + ) + }, + shape = RoundedCornerShape(24.dp), + colors = TextFieldDefaults.colors( + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + errorIndicatorColor = Color.Transparent, + cursorColor = MaterialTheme.colorScheme.onBackground, + ), + contentPadding = PaddingValues(12.dp), + ) + } ) } @@ -292,7 +280,7 @@ fun SearchBoxLeadingIcon( @Suppress("UNUSED_PARAMETER") modifier: Modifier = Modifier, onClickCloseSearch: () -> Unit = {}, ) { - if (isSearching) + if (isSearching) { IconButton( onClick = onClickCloseSearch, ) { @@ -301,11 +289,12 @@ fun SearchBoxLeadingIcon( contentDescription = "Close", ) } - else + } else { Icon( imageVector = Icons.Filled.Search, contentDescription = "Search", ) + } } @Composable @@ -314,7 +303,7 @@ fun SearchBoxTrailingIcon( @Suppress("UNUSED_PARAMETER") modifier: Modifier = Modifier, onClickClearSearch: () -> Unit = {}, ) { - if (!isEmpty) + if (!isEmpty) { IconButton( onClick = onClickClearSearch, ) { @@ -323,6 +312,7 @@ fun SearchBoxTrailingIcon( contentDescription = "Clear", ) } + } } // KMK <-- @@ -475,9 +465,10 @@ fun SourceOptionsDialog( // SY <-- // KMK --> if (onClickSettings != null && source.installedExtension !== null && - source.id !in listOf(LocalSource.ID, EH_SOURCE_ID, EXH_SOURCE_ID)) { + source.id !in listOf(LocalSource.ID, EH_SOURCE_ID, EXH_SOURCE_ID) + ) { Text( - text = stringResource(MR.strings.label_extension_info), + text = stringResource(MR.strings.label_extension_info), modifier = Modifier .clickable(onClick = onClickSettings) .fillMaxWidth() 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 3f671ff8f..910b45459 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 @@ -26,6 +26,7 @@ import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.RadioButton import androidx.compose.material3.Slider @@ -215,7 +216,7 @@ fun SelectItem( ) { OutlinedTextField( modifier = Modifier - .menuAnchor() + .menuAnchor(MenuAnchorType.PrimaryEditable) .fillMaxWidth() .padding( horizontal = SettingsItemsPaddings.Horizontal, diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/util/LazyListState.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/util/LazyListState.kt index cdabf1b49..5106934ef 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/util/LazyListState.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/util/LazyListState.kt @@ -3,7 +3,10 @@ package tachiyomi.presentation.core.util import androidx.compose.foundation.lazy.LazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue @Composable fun LazyListState.shouldExpandFAB(): Boolean { @@ -16,3 +19,59 @@ fun LazyListState.shouldExpandFAB(): Boolean { } .value } + +@Composable +fun LazyListState.isScrolledToStart(): Boolean { + return remember { + derivedStateOf { + val firstItem = layoutInfo.visibleItemsInfo.firstOrNull() + firstItem == null || firstItem.offset == layoutInfo.viewportStartOffset + } + }.value +} + +@Composable +fun LazyListState.isScrolledToEnd(): Boolean { + return remember { + derivedStateOf { + val lastItem = layoutInfo.visibleItemsInfo.lastOrNull() + lastItem == null || lastItem.size + lastItem.offset <= layoutInfo.viewportEndOffset + } + }.value +} + +@Composable +fun LazyListState.isScrollingUp(): Boolean { + var previousIndex by remember { mutableIntStateOf(firstVisibleItemIndex) } + var previousScrollOffset by remember { mutableIntStateOf(firstVisibleItemScrollOffset) } + return remember { + derivedStateOf { + if (previousIndex != firstVisibleItemIndex) { + previousIndex > firstVisibleItemIndex + } else { + previousScrollOffset >= firstVisibleItemScrollOffset + }.also { + previousIndex = firstVisibleItemIndex + previousScrollOffset = firstVisibleItemScrollOffset + } + } + }.value +} + +@Composable +fun LazyListState.isScrollingDown(): Boolean { + var previousIndex by remember { mutableIntStateOf(firstVisibleItemIndex) } + var previousScrollOffset by remember { mutableIntStateOf(firstVisibleItemScrollOffset) } + return remember { + derivedStateOf { + if (previousIndex != firstVisibleItemIndex) { + previousIndex < firstVisibleItemIndex + } else { + previousScrollOffset <= firstVisibleItemScrollOffset + }.also { + previousIndex = firstVisibleItemIndex + previousScrollOffset = firstVisibleItemScrollOffset + } + } + }.value +}