TabbedScreen.kt 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  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.foundation.pager.rememberPagerState
  10. import androidx.compose.material3.MaterialTheme
  11. import androidx.compose.material3.SnackbarHost
  12. import androidx.compose.material3.SnackbarHostState
  13. import androidx.compose.material3.Tab
  14. import androidx.compose.material3.TabRow
  15. import androidx.compose.runtime.Composable
  16. import androidx.compose.runtime.LaunchedEffect
  17. import androidx.compose.runtime.remember
  18. import androidx.compose.runtime.rememberCoroutineScope
  19. import androidx.compose.ui.Alignment
  20. import androidx.compose.ui.Modifier
  21. import androidx.compose.ui.platform.LocalLayoutDirection
  22. import androidx.compose.ui.res.stringResource
  23. import kotlinx.coroutines.launch
  24. import tachiyomi.presentation.core.components.HorizontalPager
  25. import tachiyomi.presentation.core.components.material.Scaffold
  26. import tachiyomi.presentation.core.components.material.TabIndicator
  27. import tachiyomi.presentation.core.components.material.TabText
  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 { tabs.size }
  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], state.currentPageOffsetFraction) },
  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. modifier = Modifier.fillMaxSize(),
  80. state = state,
  81. verticalAlignment = Alignment.Top,
  82. ) { page ->
  83. tabs[page].content(
  84. PaddingValues(bottom = contentPadding.calculateBottomPadding()),
  85. snackbarHostState,
  86. )
  87. }
  88. }
  89. }
  90. }
  91. data class TabContent(
  92. @StringRes val titleRes: Int,
  93. val badgeNumber: Int? = null,
  94. val searchEnabled: Boolean = false,
  95. val actions: List<AppBar.Action> = emptyList(),
  96. val content: @Composable (contentPadding: PaddingValues, snackbarHostState: SnackbarHostState) -> Unit,
  97. )