Эх сурвалжийг харах

Reimplement appbar color overlay on scroll (#7663)

only on updates and history screen for now, but the required changes on app bar
is there.

also fix missing incognito-downloaded mode indicator on history screen
Ivan Iskandar 2 жил өмнө
parent
commit
737cf9898d

+ 55 - 25
app/src/main/java/eu/kanade/presentation/components/AppBar.kt

@@ -2,11 +2,7 @@ package eu.kanade.presentation.components
 
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.RowScope
-import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.WindowInsetsSides
-import androidx.compose.foundation.layout.only
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.foundation.layout.statusBarsPadding
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.ArrowBack
 import androidx.compose.material.icons.filled.Close
@@ -18,6 +14,7 @@ import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.SmallTopAppBar
 import androidx.compose.material3.Text
 import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.TopAppBarScrollBehavior
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
@@ -51,19 +48,63 @@ fun AppBar(
     // Banners
     downloadedOnlyMode: Boolean = false,
     incognitoMode: Boolean = false,
+
+    scrollBehavior: TopAppBarScrollBehavior? = null,
 ) {
     val isActionMode by derivedStateOf { actionModeCounter > 0 }
-    val backgroundColor = if (isActionMode) {
-        TopAppBarDefaults.smallTopAppBarColors().containerColor(1f).value
-    } else {
-        MaterialTheme.colorScheme.surface
-    }
+    AppBar(
+        modifier = modifier,
+        titleContent = {
+            if (isActionMode) {
+                AppBarTitle(actionModeCounter.toString())
+            } else {
+                AppBarTitle(title, subtitle)
+            }
+        },
+        navigateUp = navigateUp,
+        navigationIcon = navigationIcon,
+        actions = {
+            if (isActionMode) {
+                actionModeActions()
+            } else {
+                actions()
+            }
+        },
+        isActionMode = isActionMode,
+        onCancelActionMode = onCancelActionMode,
+        downloadedOnlyMode = downloadedOnlyMode,
+        incognitoMode = incognitoMode,
+        scrollBehavior = scrollBehavior,
+    )
+}
+
+@Composable
+fun AppBar(
+    modifier: Modifier = Modifier,
+    // Title
+    titleContent: @Composable () -> Unit,
+    // Up button
+    navigateUp: (() -> Unit)? = null,
+    navigationIcon: ImageVector = Icons.Default.ArrowBack,
+    // Menu
+    actions: @Composable RowScope.() -> Unit = {},
+    // Action mode
+    isActionMode: Boolean,
+    onCancelActionMode: () -> Unit = {},
+    // Banners
+    downloadedOnlyMode: Boolean = false,
+    incognitoMode: Boolean = false,
+
+    scrollBehavior: TopAppBarScrollBehavior? = null,
+) {
+    val scrollFraction = if (isActionMode) 1f else scrollBehavior?.state?.overlappedFraction ?: 0f
+    val backgroundColor by TopAppBarDefaults.smallTopAppBarColors().containerColor(scrollFraction)
 
     Column(
         modifier = modifier.drawBehind { drawRect(backgroundColor) },
     ) {
         SmallTopAppBar(
-            modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top)),
+            modifier = Modifier.statusBarsPadding(),
             navigationIcon = {
                 if (isActionMode) {
                     IconButton(onClick = onCancelActionMode) {
@@ -83,25 +124,14 @@ fun AppBar(
                     }
                 }
             },
-            title = {
-                if (isActionMode) {
-                    AppBarTitle(actionModeCounter.toString())
-                } else {
-                    AppBarTitle(title, subtitle)
-                }
-            },
-            actions = {
-                if (isActionMode) {
-                    actionModeActions()
-                } else {
-                    actions()
-                }
-            },
+            title = titleContent,
+            actions = actions,
             // Background handled by parent
             colors = TopAppBarDefaults.smallTopAppBarColors(
                 containerColor = Color.Transparent,
                 scrolledContainerColor = Color.Transparent,
             ),
+            scrollBehavior = scrollBehavior,
         )
 
         if (downloadedOnlyMode) {

+ 22 - 3
app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt

@@ -1,10 +1,18 @@
 package eu.kanade.presentation.history
 
-import androidx.compose.foundation.layout.safeDrawingPadding
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.WindowInsetsSides
+import androidx.compose.foundation.layout.asPaddingValues
+import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.only
+import androidx.compose.foundation.layout.padding
 import androidx.compose.material3.Scaffold
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.rememberTopAppBarState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.platform.LocalContext
 import androidx.paging.LoadState
 import eu.kanade.domain.history.model.HistoryWithRelations
@@ -30,10 +38,21 @@ fun HistoryScreen(
     onClickResume: (HistoryWithRelations) -> Unit,
 ) {
     val context = LocalContext.current
+    val insetPaddingValue = WindowInsets.navigationBars
+        .only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom)
+        .asPaddingValues()
+    val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
     Scaffold(
-        modifier = Modifier.safeDrawingPadding(),
+        modifier = Modifier
+            .padding(insetPaddingValue)
+            .nestedScroll(scrollBehavior.nestedScrollConnection),
         topBar = {
-            HistoryToolbar(state = presenter)
+            HistoryToolbar(
+                state = presenter,
+                incognitoMode = presenter.isIncognitoMode,
+                downloadedOnlyMode = presenter.isDownloadOnly,
+                scrollBehavior = scrollBehavior,
+            )
         },
     ) { contentPadding ->
         val items = presenter.getLazyHistory()

+ 27 - 13
app/src/main/java/eu/kanade/presentation/history/components/HistoryToolbar.kt

@@ -9,8 +9,7 @@ import androidx.compose.material.icons.outlined.Search
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.SmallTopAppBar
-import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarScrollBehavior
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.remember
@@ -19,6 +18,7 @@ import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.res.stringResource
+import eu.kanade.presentation.components.AppBar
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.ui.recent.history.HistoryPresenter
 import eu.kanade.tachiyomi.ui.recent.history.HistoryState
@@ -27,17 +27,25 @@ import kotlinx.coroutines.delay
 @Composable
 fun HistoryToolbar(
     state: HistoryState,
+    scrollBehavior: TopAppBarScrollBehavior,
+    incognitoMode: Boolean,
+    downloadedOnlyMode: Boolean,
 ) {
     if (state.searchQuery == null) {
         HistoryRegularToolbar(
             onClickSearch = { state.searchQuery = "" },
             onClickDelete = { state.dialog = HistoryPresenter.Dialog.DeleteAll },
+            incognitoMode = incognitoMode,
+            downloadedOnlyMode = downloadedOnlyMode,
+            scrollBehavior = scrollBehavior,
         )
     } else {
         HistorySearchToolbar(
             searchQuery = state.searchQuery!!,
             onChangeSearchQuery = { state.searchQuery = it },
             onClickCloseSearch = { state.searchQuery = null },
+            incognitoMode = incognitoMode,
+            downloadedOnlyMode = downloadedOnlyMode,
         )
     }
 }
@@ -46,11 +54,12 @@ fun HistoryToolbar(
 fun HistoryRegularToolbar(
     onClickSearch: () -> Unit,
     onClickDelete: () -> Unit,
+    incognitoMode: Boolean,
+    downloadedOnlyMode: Boolean,
+    scrollBehavior: TopAppBarScrollBehavior,
 ) {
-    SmallTopAppBar(
-        title = {
-            Text(text = stringResource(id = R.string.history))
-        },
+    AppBar(
+        title = stringResource(id = R.string.history),
         actions = {
             IconButton(onClick = onClickSearch) {
                 Icon(Icons.Outlined.Search, contentDescription = "search")
@@ -59,6 +68,9 @@ fun HistoryRegularToolbar(
                 Icon(Icons.Outlined.DeleteSweep, contentDescription = "delete")
             }
         },
+        downloadedOnlyMode = downloadedOnlyMode,
+        incognitoMode = incognitoMode,
+        scrollBehavior = scrollBehavior,
     )
 }
 
@@ -67,15 +79,12 @@ fun HistorySearchToolbar(
     searchQuery: String,
     onChangeSearchQuery: (String) -> Unit,
     onClickCloseSearch: () -> Unit,
+    incognitoMode: Boolean,
+    downloadedOnlyMode: Boolean,
 ) {
     val focusRequester = remember { FocusRequester.Default }
-    SmallTopAppBar(
-        navigationIcon = {
-            IconButton(onClick = onClickCloseSearch) {
-                Icon(Icons.Outlined.ArrowBack, contentDescription = "delete")
-            }
-        },
-        title = {
+    AppBar(
+        titleContent = {
             BasicTextField(
                 value = searchQuery,
                 onValueChange = onChangeSearchQuery,
@@ -87,6 +96,11 @@ fun HistorySearchToolbar(
                 cursorBrush = SolidColor(MaterialTheme.colorScheme.onBackground),
             )
         },
+        navigationIcon = Icons.Outlined.ArrowBack,
+        navigateUp = onClickCloseSearch,
+        isActionMode = false,
+        downloadedOnlyMode = downloadedOnlyMode,
+        incognitoMode = incognitoMode,
     )
     LaunchedEffect(focusRequester) {
         // TODO: https://issuetracker.google.com/issues/204502668

+ 13 - 3
app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt

@@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.navigationBars
 import androidx.compose.foundation.layout.only
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.systemBars
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.material.icons.Icons
@@ -20,9 +19,13 @@ import androidx.compose.material.icons.filled.Refresh
 import androidx.compose.material.icons.filled.SelectAll
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.TopAppBarScrollBehavior
+import androidx.compose.material3.rememberTopAppBarState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.res.stringResource
@@ -57,7 +60,9 @@ fun UpdateScreen(
     onDownloadChapter: (List<UpdatesItem>, ChapterDownloadAction) -> Unit,
 ) {
     val updatesListState = rememberLazyListState()
-    val insetPaddingValue = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues()
+    val insetPaddingValue = WindowInsets.navigationBars
+        .only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom)
+        .asPaddingValues()
 
     val internalOnBackPressed = {
         if (presenter.selectionMode) {
@@ -76,9 +81,11 @@ fun UpdateScreen(
         }
     }
 
+    val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
     Scaffold(
         modifier = Modifier
-            .padding(insetPaddingValue),
+            .padding(insetPaddingValue)
+            .nestedScroll(scrollBehavior.nestedScrollConnection),
         topBar = {
             UpdatesAppBar(
                 incognitoMode = presenter.isIncognitoMode,
@@ -88,6 +95,7 @@ fun UpdateScreen(
                 onSelectAll = { presenter.toggleAllSelection(true) },
                 onInvertSelection = { presenter.invertSelection() },
                 onCancelActionMode = { presenter.toggleAllSelection(false) },
+                scrollBehavior = scrollBehavior,
             )
         },
         bottomBar = {
@@ -185,6 +193,7 @@ fun UpdatesAppBar(
     onSelectAll: () -> Unit,
     onInvertSelection: () -> Unit,
     onCancelActionMode: () -> Unit,
+    scrollBehavior: TopAppBarScrollBehavior,
 ) {
     AppBar(
         modifier = modifier,
@@ -215,6 +224,7 @@ fun UpdatesAppBar(
         },
         downloadedOnlyMode = downloadedOnlyMode,
         incognitoMode = incognitoMode,
+        scrollBehavior = scrollBehavior,
     )
 }
 

+ 6 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryPresenter.kt

@@ -22,6 +22,7 @@ import eu.kanade.domain.history.interactor.RemoveHistoryByMangaId
 import eu.kanade.domain.history.model.HistoryWithRelations
 import eu.kanade.presentation.history.HistoryUiModel
 import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
 import eu.kanade.tachiyomi.util.lang.launchIO
 import eu.kanade.tachiyomi.util.lang.launchUI
@@ -45,11 +46,16 @@ class HistoryPresenter(
     private val deleteHistoryTable: DeleteHistoryTable = Injekt.get(),
     private val removeHistoryById: RemoveHistoryById = Injekt.get(),
     private val removeHistoryByMangaId: RemoveHistoryByMangaId = Injekt.get(),
+    preferences: PreferencesHelper = Injekt.get(),
 ) : BasePresenter<HistoryController>(), HistoryState by state {
 
     private val _events: Channel<Event> = Channel(Int.MAX_VALUE)
     val events: Flow<Event> = _events.receiveAsFlow()
 
+    val isDownloadOnly: Boolean by preferences.downloadedOnly().asState()
+
+    val isIncognitoMode: Boolean by preferences.incognitoMode().asState()
+
     @Composable
     fun getLazyHistory(): LazyPagingItems<HistoryUiModel> {
         val scope = rememberCoroutineScope()