TabbedScreen.kt 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. package eu.kanade.presentation.components
  2. import androidx.annotation.StringRes
  3. import androidx.compose.foundation.layout.Column
  4. import androidx.compose.foundation.layout.PaddingValues
  5. import androidx.compose.foundation.layout.calculateEndPadding
  6. import androidx.compose.foundation.layout.calculateStartPadding
  7. import androidx.compose.foundation.layout.fillMaxSize
  8. import androidx.compose.foundation.layout.padding
  9. import androidx.compose.material3.MaterialTheme
  10. import androidx.compose.material3.SnackbarHost
  11. import androidx.compose.material3.SnackbarHostState
  12. import androidx.compose.material3.Tab
  13. import androidx.compose.material3.TabRow
  14. import androidx.compose.runtime.Composable
  15. import androidx.compose.runtime.LaunchedEffect
  16. import androidx.compose.runtime.remember
  17. import androidx.compose.runtime.rememberCoroutineScope
  18. import androidx.compose.ui.Alignment
  19. import androidx.compose.ui.Modifier
  20. import androidx.compose.ui.platform.LocalLayoutDirection
  21. import androidx.compose.ui.res.stringResource
  22. import kotlinx.coroutines.launch
  23. import tachiyomi.presentation.core.components.HorizontalPager
  24. import tachiyomi.presentation.core.components.material.Scaffold
  25. import tachiyomi.presentation.core.components.material.TabIndicator
  26. import tachiyomi.presentation.core.components.material.TabText
  27. import tachiyomi.presentation.core.components.rememberPagerState
  28. @Composable
  29. fun TabbedScreen(
  30. @StringRes titleRes: Int,
  31. tabs: List<TabContent>,
  32. startIndex: Int? = null,
  33. searchQuery: String? = null,
  34. onChangeSearchQuery: (String?) -> Unit = {},
  35. ) {
  36. val scope = rememberCoroutineScope()
  37. val state = rememberPagerState()
  38. val snackbarHostState = remember { SnackbarHostState() }
  39. LaunchedEffect(startIndex) {
  40. if (startIndex != null) {
  41. state.scrollToPage(startIndex)
  42. }
  43. }
  44. Scaffold(
  45. topBar = {
  46. val tab = tabs[state.currentPage]
  47. val searchEnabled = tab.searchEnabled
  48. SearchToolbar(
  49. titleContent = { AppBarTitle(stringResource(titleRes)) },
  50. searchEnabled = searchEnabled,
  51. searchQuery = if (searchEnabled) searchQuery else null,
  52. onChangeSearchQuery = onChangeSearchQuery,
  53. actions = { AppBarActions(tab.actions) },
  54. )
  55. },
  56. snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
  57. ) { contentPadding ->
  58. Column(
  59. modifier = Modifier.padding(
  60. top = contentPadding.calculateTopPadding(),
  61. start = contentPadding.calculateStartPadding(LocalLayoutDirection.current),
  62. end = contentPadding.calculateEndPadding(LocalLayoutDirection.current),
  63. ),
  64. ) {
  65. TabRow(
  66. selectedTabIndex = state.currentPage,
  67. indicator = { TabIndicator(it[state.currentPage]) },
  68. ) {
  69. tabs.forEachIndexed { index, tab ->
  70. Tab(
  71. selected = state.currentPage == index,
  72. onClick = { scope.launch { state.animateScrollToPage(index) } },
  73. text = { TabText(text = stringResource(tab.titleRes), badgeCount = tab.badgeNumber) },
  74. unselectedContentColor = MaterialTheme.colorScheme.onSurface,
  75. )
  76. }
  77. }
  78. HorizontalPager(
  79. count = tabs.size,
  80. modifier = Modifier.fillMaxSize(),
  81. state = state,
  82. verticalAlignment = Alignment.Top,
  83. ) { page ->
  84. tabs[page].content(
  85. PaddingValues(bottom = contentPadding.calculateBottomPadding()),
  86. snackbarHostState,
  87. )
  88. }
  89. }
  90. }
  91. }
  92. data class TabContent(
  93. @StringRes val titleRes: Int,
  94. val badgeNumber: Int? = null,
  95. val searchEnabled: Boolean = false,
  96. val actions: List<AppBar.Action> = emptyList(),
  97. val content: @Composable (contentPadding: PaddingValues, snackbarHostState: SnackbarHostState) -> Unit,
  98. )