diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index a86486b927..869c1140a1 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -12,7 +12,7 @@ jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v4 + - uses: dessant/lock-threads@v5 with: github-token: ${{ github.token }} issue-inactive-days: '2' diff --git a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt index 6d01f85b8b..d778390819 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt @@ -4,7 +4,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.HelpOutline +import androidx.compose.material.icons.automirrored.outlined.HelpOutline import androidx.compose.material.icons.outlined.Public import androidx.compose.material.icons.outlined.Refresh import androidx.compose.material3.SnackbarDuration @@ -80,7 +80,7 @@ fun BrowseSourceContent( persistentListOf( EmptyScreenAction( stringResId = R.string.local_source_help_guide, - icon = Icons.Outlined.HelpOutline, + icon = Icons.AutoMirrored.Outlined.HelpOutline, onClick = onLocalSourceHelpClick, ), ) @@ -98,7 +98,7 @@ fun BrowseSourceContent( ), EmptyScreenAction( stringResId = R.string.label_help, - icon = Icons.Outlined.HelpOutline, + icon = Icons.AutoMirrored.Outlined.HelpOutline, onClick = onHelpClick, ), ) diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt index b319f9fdc6..715e93b52f 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt @@ -16,7 +16,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.HelpOutline +import androidx.compose.material.icons.automirrored.outlined.HelpOutline import androidx.compose.material.icons.outlined.History import androidx.compose.material.icons.outlined.Settings import androidx.compose.material3.AlertDialog @@ -92,7 +92,7 @@ fun ExtensionDetailsScreen( add( AppBar.Action( title = stringResource(R.string.action_faq_and_guides), - icon = Icons.Outlined.HelpOutline, + icon = Icons.AutoMirrored.Outlined.HelpOutline, onClick = onClickReadme, ), ) diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt index 4ee222e22a..02e0e1befe 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt @@ -1,7 +1,7 @@ package eu.kanade.presentation.browse.components import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ViewList +import androidx.compose.material.icons.automirrored.filled.ViewList import androidx.compose.material.icons.filled.ViewModule import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarScrollBehavior @@ -57,7 +57,7 @@ fun BrowseSourceToolbar( AppBar.Action( title = stringResource(R.string.action_display_mode), icon = if (displayMode == LibraryDisplayMode.List) { - Icons.Filled.ViewList + Icons.AutoMirrored.Filled.ViewList } else { Icons.Filled.ViewModule }, diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt b/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt index 1270c011bf..4661c304ca 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.ArrowForward import androidx.compose.material.icons.outlined.ArrowForward import androidx.compose.material.icons.outlined.Error import androidx.compose.material3.CircularProgressIndicator @@ -54,7 +55,7 @@ fun GlobalSearchResultItem( Text(text = subtitle) } IconButton(onClick = onClick) { - Icon(imageVector = Icons.Outlined.ArrowForward, contentDescription = null) + Icon(imageVector = Icons.AutoMirrored.Outlined.ArrowForward, contentDescription = null) } } content() diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchToolbar.kt b/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchToolbar.kt index 6f108abba9..965894af8c 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchToolbar.kt @@ -58,7 +58,7 @@ fun GlobalSearchToolbar( ) if (progress in 1..().map { - PlainTooltipBox( - tooltip = { Text(it.title) }, + TooltipBox( + positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + tooltip = { + PlainTooltip { + Text(it.title) + } + }, + state = rememberTooltipState(), ) { IconButton( onClick = it.onClick, enabled = it.enabled, - modifier = Modifier.tooltipTrigger(), ) { Icon( imageVector = it.icon, @@ -208,12 +213,17 @@ fun AppBarActions( val overflowActions = actions.filterIsInstance() if (overflowActions.isNotEmpty()) { - PlainTooltipBox( - tooltip = { Text(stringResource(R.string.abc_action_menu_overflow_description)) }, + TooltipBox( + positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + tooltip = { + PlainTooltip { + Text(stringResource(R.string.abc_action_menu_overflow_description)) + } + }, + state = rememberTooltipState(), ) { IconButton( onClick = { showMenu = !showMenu }, - modifier = Modifier.tooltipTrigger(), ) { Icon( Icons.Outlined.MoreVert, @@ -327,12 +337,17 @@ fun SearchToolbar( if (!searchEnabled) { // Don't show search action } else if (searchQuery == null) { - PlainTooltipBox( - tooltip = { Text(stringResource(R.string.action_search)) }, + TooltipBox( + positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + tooltip = { + PlainTooltip { + Text(stringResource(R.string.action_search)) + } + }, + state = rememberTooltipState(), ) { IconButton( onClick = onClick, - modifier = Modifier.tooltipTrigger(), ) { Icon( Icons.Outlined.Search, @@ -341,15 +356,20 @@ fun SearchToolbar( } } } else if (searchQuery.isNotEmpty()) { - PlainTooltipBox( - tooltip = { Text(stringResource(R.string.action_reset)) }, + TooltipBox( + positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + tooltip = { + PlainTooltip { + Text(stringResource(R.string.action_reset)) + } + }, + state = rememberTooltipState(), ) { IconButton( onClick = { onClick() focusRequester.requestFocus() }, - modifier = Modifier.tooltipTrigger(), ) { Icon( Icons.Outlined.Close, @@ -370,11 +390,7 @@ fun SearchToolbar( @Composable fun UpIcon(navigationIcon: ImageVector? = null) { val icon = navigationIcon - ?: if (LocalLayoutDirection.current == LayoutDirection.Ltr) { - Icons.Outlined.ArrowBack - } else { - Icons.Outlined.ArrowForward - } + ?: Icons.AutoMirrored.Outlined.ArrowBack Icon( imageVector = icon, contentDescription = stringResource(R.string.abc_action_bar_up_description), diff --git a/app/src/main/java/eu/kanade/presentation/components/DropdownMenu.kt b/app/src/main/java/eu/kanade/presentation/components/DropdownMenu.kt index 14cd82117a..ba6cdd341e 100644 --- a/app/src/main/java/eu/kanade/presentation/components/DropdownMenu.kt +++ b/app/src/main/java/eu/kanade/presentation/components/DropdownMenu.kt @@ -3,8 +3,7 @@ package eu.kanade.presentation.components import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.sizeIn import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.ArrowLeft -import androidx.compose.material.icons.outlined.ArrowRight +import androidx.compose.material.icons.automirrored.outlined.ArrowRight import androidx.compose.material.icons.outlined.RadioButtonChecked import androidx.compose.material.icons.outlined.RadioButtonUnchecked import androidx.compose.material3.DropdownMenuItem @@ -16,10 +15,8 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.DpOffset -import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.compose.ui.window.PopupProperties import eu.kanade.tachiyomi.R @@ -77,14 +74,13 @@ fun NestedMenuItem( ) { var nestedExpanded by remember { mutableStateOf(false) } val closeMenu = { nestedExpanded = false } - val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr DropdownMenuItem( text = text, onClick = { nestedExpanded = true }, trailingIcon = { Icon( - imageVector = if (isLtr) Icons.Outlined.ArrowRight else Icons.Outlined.ArrowLeft, + imageVector = Icons.AutoMirrored.Outlined.ArrowRight, contentDescription = null, ) }, diff --git a/app/src/main/java/eu/kanade/presentation/components/EmptyScreen.kt b/app/src/main/java/eu/kanade/presentation/components/EmptyScreen.kt index cc2a56ec71..47efe2abaf 100644 --- a/app/src/main/java/eu/kanade/presentation/components/EmptyScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/components/EmptyScreen.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.components import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.HelpOutline import androidx.compose.material.icons.outlined.HelpOutline import androidx.compose.material.icons.outlined.Refresh import androidx.compose.material3.Surface @@ -39,7 +40,7 @@ private fun WithActionPreview() { ), EmptyScreenAction( stringResId = R.string.getting_started_guide, - icon = Icons.Outlined.HelpOutline, + icon = Icons.AutoMirrored.Outlined.HelpOutline, onClick = {}, ), ), diff --git a/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt b/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt index 20e3640c92..88dc527035 100644 --- a/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt @@ -14,8 +14,8 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.PrimaryTabRow import androidx.compose.material3.Tab -import androidx.compose.material3.TabRow import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -30,7 +30,6 @@ import androidx.compose.ui.util.fastForEachIndexed import eu.kanade.tachiyomi.R import kotlinx.coroutines.launch import tachiyomi.presentation.core.components.HorizontalPager -import tachiyomi.presentation.core.components.material.TabIndicator import tachiyomi.presentation.core.components.material.TabText object TabbedDialogPaddings { @@ -55,10 +54,9 @@ fun TabbedDialog( Column { Row { - TabRow( + PrimaryTabRow( modifier = Modifier.weight(1f), selectedTabIndex = pagerState.currentPage, - indicator = { TabIndicator(it[pagerState.currentPage], pagerState.currentPageOffsetFraction) }, divider = {}, ) { tabTitles.fastForEachIndexed { index, tab -> diff --git a/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt b/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt index 422b1f7dc6..72329aca6b 100644 --- a/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt @@ -9,10 +9,10 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.PrimaryTabRow import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Tab -import androidx.compose.material3.TabRow import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember @@ -24,7 +24,6 @@ import androidx.compose.ui.res.stringResource import kotlinx.coroutines.launch import tachiyomi.presentation.core.components.HorizontalPager import tachiyomi.presentation.core.components.material.Scaffold -import tachiyomi.presentation.core.components.material.TabIndicator import tachiyomi.presentation.core.components.material.TabText @Composable @@ -67,9 +66,8 @@ fun TabbedScreen( end = contentPadding.calculateEndPadding(LocalLayoutDirection.current), ), ) { - TabRow( + PrimaryTabRow( selectedTabIndex = state.currentPage, - indicator = { TabIndicator(it[state.currentPage], state.currentPageOffsetFraction) }, ) { tabs.forEachIndexed { index, tab -> Tab( diff --git a/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt b/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt index 5f83c764f4..6cffebe8bc 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt @@ -33,11 +33,13 @@ import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shadow +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import eu.kanade.presentation.manga.components.MangaCover +import eu.kanade.tachiyomi.R import tachiyomi.presentation.core.components.BadgeGroup import tachiyomi.presentation.core.util.selectedBackground @@ -376,7 +378,7 @@ private fun ContinueReadingButton( ) { Icon( imageVector = Icons.Filled.PlayArrow, - contentDescription = "", + contentDescription = stringResource(R.string.action_resume), modifier = Modifier.size(16.dp), ) } diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryTabs.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryTabs.kt index 6f68c78def..3200d437de 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryTabs.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryTabs.kt @@ -4,13 +4,12 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.pager.PagerState import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.ScrollableTabRow +import androidx.compose.material3.PrimaryScrollableTabRow import androidx.compose.material3.Tab import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp import eu.kanade.presentation.category.visualName import tachiyomi.domain.category.model.Category -import tachiyomi.presentation.core.components.material.TabIndicator import tachiyomi.presentation.core.components.material.TabText @Composable @@ -21,10 +20,9 @@ internal fun LibraryTabs( onTabItemClick: (Int) -> Unit, ) { Column { - ScrollableTabRow( + PrimaryScrollableTabRow( selectedTabIndex = pagerState.currentPage, edgePadding = 0.dp, - indicator = { TabIndicator(it[pagerState.currentPage], pagerState.currentPageOffsetFraction) }, // TODO: use default when width is fixed upstream // https://issuetracker.google.com/issues/242879624 divider = {}, diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt index 1e89317409..ce9fb64bf6 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt @@ -148,7 +148,7 @@ private fun DownloadingIndicator( MaterialTheme.colorScheme.background } CircularProgressIndicator( - progress = animatedProgress, + progress = { animatedProgress }, modifier = IndicatorModifier, color = strokeColor, strokeWidth = IndicatorSize / 2, diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt index fa98e176b6..6f31d5a1f1 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt @@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.shape.ZeroCornerSize import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.Label import androidx.compose.material.icons.outlined.BookmarkAdd import androidx.compose.material.icons.outlined.BookmarkRemove import androidx.compose.material.icons.outlined.Delete @@ -258,7 +259,7 @@ fun LibraryBottomActionMenu( ) { Button( title = stringResource(R.string.action_move_category), - icon = Icons.Outlined.Label, + icon = Icons.AutoMirrored.Outlined.Label, toConfirm = confirm[0], onLongClick = { onLongClickItem(0) }, onClick = onChangeCategoryClicked, diff --git a/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt b/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt index f7dba13981..56d5770664 100644 --- a/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt @@ -9,6 +9,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.HelpOutline +import androidx.compose.material.icons.automirrored.outlined.Label import androidx.compose.material.icons.outlined.CloudOff import androidx.compose.material.icons.outlined.GetApp import androidx.compose.material.icons.outlined.HelpOutline @@ -130,7 +132,7 @@ fun MoreScreen( item { TextPreferenceWidget( title = stringResource(R.string.categories), - icon = Icons.Outlined.Label, + icon = Icons.AutoMirrored.Outlined.Label, onPreferenceClick = onClickCategories, ) } @@ -168,7 +170,7 @@ fun MoreScreen( item { TextPreferenceWidget( title = stringResource(R.string.label_help), - icon = Icons.Outlined.HelpOutline, + icon = Icons.AutoMirrored.Outlined.HelpOutline, onPreferenceClick = { uriHandler.openUri(Constants.URL_HELP) }, ) } diff --git a/app/src/main/java/eu/kanade/presentation/more/NewUpdateScreen.kt b/app/src/main/java/eu/kanade/presentation/more/NewUpdateScreen.kt index 0cd661e850..887583c91f 100644 --- a/app/src/main/java/eu/kanade/presentation/more/NewUpdateScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/NewUpdateScreen.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.OpenInNew import androidx.compose.material.icons.filled.OpenInNew import androidx.compose.material.icons.outlined.NewReleases import androidx.compose.material3.Icon @@ -60,7 +61,7 @@ fun NewUpdateScreen( ) { Text(text = stringResource(R.string.update_check_open)) Spacer(modifier = Modifier.width(MaterialTheme.padding.tiny)) - Icon(imageVector = Icons.Default.OpenInNew, contentDescription = null) + Icon(imageVector = Icons.AutoMirrored.Outlined.OpenInNew, contentDescription = null) } } } diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt index 000914c826..de9981a67f 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.ChromeReaderMode import androidx.compose.material.icons.outlined.ChromeReaderMode import androidx.compose.material.icons.outlined.Code import androidx.compose.material.icons.outlined.CollectionsBookmark @@ -186,7 +187,7 @@ object SettingsMainScreen : Screen() { Item( titleRes = R.string.pref_category_reader, subtitleRes = R.string.pref_reader_summary, - icon = Icons.Outlined.ChromeReaderMode, + icon = Icons.AutoMirrored.Outlined.ChromeReaderMode, screen = SettingsReaderScreen, ), Item( diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt index 655d79cc20..eebcaa22c8 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.HelpOutline import androidx.compose.material.icons.filled.Visibility import androidx.compose.material.icons.filled.VisibilityOff import androidx.compose.material.icons.outlined.Close @@ -72,7 +73,7 @@ object SettingsTrackingScreen : SearchableSettings { val uriHandler = LocalUriHandler.current IconButton(onClick = { uriHandler.openUri("https://tachiyomi.org/docs/guides/tracking") }) { Icon( - imageVector = Icons.Outlined.HelpOutline, + imageVector = Icons.AutoMirrored.Outlined.HelpOutline, contentDescription = stringResource(R.string.tracking_guide), ) } diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackerSearch.kt b/app/src/main/java/eu/kanade/presentation/track/TrackerSearch.kt index 63f2f3917d..d42766799c 100644 --- a/app/src/main/java/eu/kanade/presentation/track/TrackerSearch.kt +++ b/app/src/main/java/eu/kanade/presentation/track/TrackerSearch.kt @@ -28,6 +28,7 @@ import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.ArrowBack import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.Close @@ -97,7 +98,7 @@ fun TrackerSearch( navigationIcon = { IconButton(onClick = onDismissRequest) { Icon( - imageVector = Icons.Default.ArrowBack, + imageVector = Icons.AutoMirrored.Outlined.ArrowBack, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant, ) diff --git a/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt b/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt index ce64a1fdcc..34ab4dbafa 100644 --- a/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt +++ b/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt @@ -11,6 +11,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.ArrowBack +import androidx.compose.material.icons.automirrored.outlined.ArrowForward import androidx.compose.material.icons.outlined.ArrowBack import androidx.compose.material.icons.outlined.ArrowForward import androidx.compose.material.icons.outlined.Close @@ -125,7 +127,7 @@ fun WebViewScreenContent( listOf( AppBar.Action( title = stringResource(R.string.action_webview_back), - icon = Icons.Outlined.ArrowBack, + icon = Icons.AutoMirrored.Outlined.ArrowBack, onClick = { if (navigator.canGoBack) { navigator.navigateBack() @@ -135,7 +137,7 @@ fun WebViewScreenContent( ), AppBar.Action( title = stringResource(R.string.action_webview_forward), - icon = Icons.Outlined.ArrowForward, + icon = Icons.AutoMirrored.Outlined.ArrowForward, onClick = { if (navigator.canGoForward) { navigator.navigateForward() @@ -188,7 +190,7 @@ fun WebViewScreenContent( .align(Alignment.BottomCenter), ) is LoadingState.Loading -> LinearProgressIndicator( - progress = (loadingState as? LoadingState.Loading)?.progress ?: 1f, + progress = { (loadingState as? LoadingState.Loading)?.progress ?: 1f }, modifier = Modifier .fillMaxWidth() .align(Alignment.BottomCenter), diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt index 5c1ac86439..1a7b039fa2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt @@ -67,7 +67,7 @@ data class SourceSearchScreen( AnimatedVisibility(visible = state.filters.isNotEmpty()) { ExtendedFloatingActionButton( text = { Text(text = stringResource(R.string.action_filter)) }, - icon = { Icon(Icons.Outlined.FilterList, contentDescription = "") }, + icon = { Icon(Icons.Outlined.FilterList, contentDescription = null) }, onClick = screenModel::openFilterSheet, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt index fe53ccfad4..8794c90789 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.ui.browse.migration.sources import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.HelpOutline import androidx.compose.material.icons.outlined.HelpOutline import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -29,7 +30,7 @@ fun Screen.migrateSourceTab(): TabContent { actions = listOf( AppBar.Action( title = stringResource(R.string.migration_help_guide), - icon = Icons.Outlined.HelpOutline, + icon = Icons.AutoMirrored.Outlined.HelpOutline, onClick = { uriHandler.openUri("https://tachiyomi.org/docs/guides/source-migration") }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt index 4f6e5d7a1a..99c4545372 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt @@ -144,7 +144,7 @@ data class BrowseSourceScreen( leadingIcon = { Icon( imageVector = Icons.Outlined.Favorite, - contentDescription = "", + contentDescription = null, modifier = Modifier .size(FilterChipDefaults.IconSize), ) @@ -163,7 +163,7 @@ data class BrowseSourceScreen( leadingIcon = { Icon( imageVector = Icons.Outlined.NewReleases, - contentDescription = "", + contentDescription = null, modifier = Modifier .size(FilterChipDefaults.IconSize), ) @@ -180,7 +180,7 @@ data class BrowseSourceScreen( leadingIcon = { Icon( imageVector = Icons.Outlined.FilterList, - contentDescription = "", + contentDescription = null, modifier = Modifier .size(FilterChipDefaults.IconSize), ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt index dcbb4536fc..0fc7f35762 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.Sort import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.outlined.Pause import androidx.compose.material.icons.outlined.Sort @@ -185,7 +186,7 @@ object DownloadQueueScreen : Screen() { listOf( AppBar.Action( title = stringResource(R.string.action_sort), - icon = Icons.Outlined.Sort, + icon = Icons.AutoMirrored.Outlined.Sort, onClick = { sortExpanded = true }, ), AppBar.OverflowAction( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt index 12e8f49747..d6f0afc4df 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt @@ -6,6 +6,7 @@ import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter import androidx.compose.animation.graphics.vector.AnimatedImageVector import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.HelpOutline import androidx.compose.material.icons.outlined.HelpOutline import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState @@ -158,7 +159,7 @@ object LibraryTab : Tab { actions = persistentListOf( EmptyScreenAction( stringResId = R.string.getting_started_guide, - icon = Icons.Outlined.HelpOutline, + icon = Icons.AutoMirrored.Outlined.HelpOutline, onClick = { handler.openUri("https://tachiyomi.org/docs/guides/getting-started") }, ), ), diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderProgressIndicator.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderProgressIndicator.kt index 2bf9cf95fa..e91b375ab9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderProgressIndicator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderProgressIndicator.kt @@ -39,7 +39,7 @@ class ReaderProgressIndicator @JvmOverloads constructor( @Composable override fun Content() { TachiyomiTheme { - CombinedCircularProgressIndicator(progress = progress) + CombinedCircularProgressIndicator(progress = { progress }) } } diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 2fd9332a48..8d586064ed 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,5 +1,5 @@ [versions] -agp_version = "8.1.2" +agp_version = "8.1.3" lifecycle_version = "2.6.2" paging_version = "3.2.1" @@ -25,7 +25,7 @@ workmanager = "androidx.work:work-runtime-ktx:2.8.1" paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging_version" } paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" } -benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.0" +benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.1" test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha01" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha01" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0-alpha05" diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index fbac9ac7f1..8f47b2db2c 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,10 +1,10 @@ [versions] compiler = "1.5.4" -compose-bom = "2023.09.00-alpha02" -accompanist = "0.33.1-alpha" +compose-bom = "2023.12.00-alpha01" +accompanist = "0.33.2-alpha" [libraries] -activity = "androidx.activity:activity-compose:1.8.0" +activity = "androidx.activity:activity-compose:1.8.1" bom = { group = "dev.chrisbanes.compose", name = "compose-bom", version.ref = "compose-bom" } foundation = { module = "androidx.compose.foundation:foundation" } animation = { module = "androidx.compose.animation:animation" } diff --git a/gradle/kotlinx.versions.toml b/gradle/kotlinx.versions.toml index 5212c2fd01..09a293d648 100644 --- a/gradle/kotlinx.versions.toml +++ b/gradle/kotlinx.versions.toml @@ -1,6 +1,6 @@ [versions] kotlin_version = "1.9.20" -serialization_version = "1.6.0" +serialization_version = "1.6.1" xml_serialization_version = "0.86.2" [libraries] diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/CircularProgressIndicator.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/CircularProgressIndicator.kt index bda305890f..b123e26e5b 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/CircularProgressIndicator.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/CircularProgressIndicator.kt @@ -37,16 +37,15 @@ import androidx.compose.ui.tooling.preview.Preview * By always rotating we give the feedback to the user that the application isn't 'stuck'. */ @Composable -fun CombinedCircularProgressIndicator(progress: Float) { - val animatedProgress by animateFloatAsState( - targetValue = progress, - animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec, - label = "progress", - ) +fun CombinedCircularProgressIndicator( + progress: () -> Float, + modifier: Modifier = Modifier, +) { AnimatedContent( - targetState = progress == 0f, + targetState = progress() == 0f, transitionSpec = { fadeIn() togetherWith fadeOut() }, label = "progressState", + modifier = modifier, ) { indeterminate -> if (indeterminate) { // Indeterminate @@ -63,8 +62,13 @@ fun CombinedCircularProgressIndicator(progress: Float) { ), label = "rotation", ) + val animatedProgress by animateFloatAsState( + targetValue = progress(), + animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec, + label = "progress", + ) CircularProgressIndicator( - progress = animatedProgress, + progress = { animatedProgress }, modifier = Modifier.rotate(rotation), ) } @@ -101,7 +105,7 @@ private fun CombinedCircularProgressIndicatorPreview() { .fillMaxSize() .padding(it), ) { - CombinedCircularProgressIndicator(progress = progress) + CombinedCircularProgressIndicator(progress = { progress }) } } } diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Scaffold.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Scaffold.kt index cd14e2c0ed..fd3cc1f74d 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Scaffold.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Scaffold.kt @@ -14,36 +14,39 @@ * limitations under the License. */ +@file:Suppress("KDocUnresolvedReference") + package tachiyomi.presentation.core.components.material -import androidx.compose.foundation.layout.MutableWindowInsets import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues -import androidx.compose.foundation.layout.calculateEndPadding -import androidx.compose.foundation.layout.calculateStartPadding -import androidx.compose.foundation.layout.exclude -import androidx.compose.foundation.layout.onConsumedWindowInsetsChanged +import androidx.compose.foundation.layout.windowInsetsBottomHeight +import androidx.compose.foundation.layout.windowInsetsEndWidth +import androidx.compose.foundation.layout.windowInsetsStartWidth +import androidx.compose.foundation.layout.windowInsetsTopHeight import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FabPosition import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ScaffoldDefaults import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.contentColorFor -import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.Immutable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.layout.SubcomposeLayout +import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.max +import androidx.compose.ui.unit.offset import androidx.compose.ui.util.fastForEach import androidx.compose.ui.util.fastMap import androidx.compose.ui.util.fastMaxBy @@ -70,8 +73,6 @@ import kotlin.math.max * * Pass scroll behavior to top bar by default * * Remove height constraint for expanded app bar * * Also take account of fab height when providing inner padding - * * Fixes for fab and snackbar horizontal placements when [contentWindowInsets] is used - * * Handle consumed window insets * * Add startBar slot for Navigation Rail * * @param modifier the [Modifier] to be applied to this scaffold @@ -99,9 +100,7 @@ import kotlin.math.max @Composable fun Scaffold( modifier: Modifier = Modifier, - topBarScrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.pinnedScrollBehavior( - rememberTopAppBarState(), - ), + topBarScrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(), topBar: @Composable (TopAppBarScrollBehavior) -> Unit = {}, bottomBar: @Composable () -> Unit = {}, startBar: @Composable () -> Unit = {}, @@ -113,16 +112,9 @@ fun Scaffold( contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets, content: @Composable (PaddingValues) -> Unit, ) { - // Tachiyomi: Handle consumed window insets - val remainingWindowInsets = remember { MutableWindowInsets() } androidx.compose.material3.Surface( modifier = Modifier .nestedScroll(topBarScrollBehavior.nestedScrollConnection) - .onConsumedWindowInsetsChanged { - remainingWindowInsets.insets = contentWindowInsets.exclude( - it, - ) - } .then(modifier), color = containerColor, contentColor = contentColor, @@ -134,7 +126,7 @@ fun Scaffold( bottomBar = bottomBar, content = content, snackbar = snackbarHost, - contentWindowInsets = remainingWindowInsets, + contentWindowInsets = contentWindowInsets, fab = floatingActionButton, ) } @@ -152,7 +144,6 @@ fun Scaffold( * @param bottomBar the content to place at the bottom of the [Scaffold], on top of the * [content], typically a [NavigationBar]. */ -@OptIn(ExperimentalMaterial3Api::class) @Composable private fun ScaffoldLayout( fabPosition: FabPosition, @@ -164,7 +155,47 @@ private fun ScaffoldLayout( contentWindowInsets: WindowInsets, bottomBar: @Composable () -> Unit, ) { - SubcomposeLayout { constraints -> + // Create the backing values for the content padding + // These values will be updated during measurement, but before measuring and placing + // the body content + var topContentPadding by remember { mutableStateOf(0.dp) } + var startContentPadding by remember { mutableStateOf(0.dp) } + var endContentPadding by remember { mutableStateOf(0.dp) } + var bottomContentPadding by remember { mutableStateOf(0.dp) } + + val contentPadding = remember { + object : PaddingValues { + override fun calculateLeftPadding(layoutDirection: LayoutDirection): Dp = + when (layoutDirection) { + LayoutDirection.Ltr -> startContentPadding + LayoutDirection.Rtl -> endContentPadding + } + + override fun calculateTopPadding(): Dp = topContentPadding + + override fun calculateRightPadding(layoutDirection: LayoutDirection): Dp = + when (layoutDirection) { + LayoutDirection.Ltr -> endContentPadding + LayoutDirection.Rtl -> startContentPadding + } + + override fun calculateBottomPadding(): Dp = bottomContentPadding + } + } + Layout( + contents = listOf( + { Spacer(Modifier.windowInsetsTopHeight(contentWindowInsets)) }, + { Spacer(Modifier.windowInsetsBottomHeight(contentWindowInsets)) }, + { Spacer(Modifier.windowInsetsStartWidth(contentWindowInsets)) }, + { Spacer(Modifier.windowInsetsEndWidth(contentWindowInsets)) }, + startBar, + topBar, + snackbar, + fab, + bottomBar, + { content(contentPadding) }, + ), + ) { measurables, constraints -> val layoutWidth = constraints.maxWidth val layoutHeight = constraints.maxHeight @@ -175,119 +206,117 @@ private fun ScaffoldLayout( */ val topBarConstraints = looseConstraints.copy(maxHeight = Constraints.Infinity) - layout(layoutWidth, layoutHeight) { - val leftInset = contentWindowInsets.getLeft(this@SubcomposeLayout, layoutDirection) - val rightInset = contentWindowInsets.getRight(this@SubcomposeLayout, layoutDirection) - val bottomInset = contentWindowInsets.getBottom(this@SubcomposeLayout) + val topInsetsPlaceables = measurables[0].single() + .measure(looseConstraints) + val bottomInsetsPlaceables = measurables[1].single() + .measure(looseConstraints) + val startInsetsPlaceables = measurables[2].single() + .measure(looseConstraints) + val endInsetsPlaceables = measurables[3].single() + .measure(looseConstraints) - // Tachiyomi: Add startBar slot for Navigation Rail - val startBarPlaceables = subcompose(ScaffoldLayoutContent.StartBar, startBar).fastMap { - it.measure(looseConstraints) - } - val startBarWidth = startBarPlaceables.fastMaxBy { it.width }?.width ?: 0 + val startInsetsWidth = startInsetsPlaceables.width + val endInsetsWidth = endInsetsPlaceables.width - // Tachiyomi: layoutWidth after horizontal insets - val insetLayoutWidth = layoutWidth - leftInset - rightInset - startBarWidth + val topInsetsHeight = topInsetsPlaceables.height + val bottomInsetsHeight = bottomInsetsPlaceables.height - val topBarPlaceables = subcompose(ScaffoldLayoutContent.TopBar, topBar).fastMap { - it.measure(topBarConstraints) - } + // Tachiyomi: Add startBar slot for Navigation Rail + val startBarPlaceables = measurables[4] + .fastMap { it.measure(looseConstraints) } - val topBarHeight = topBarPlaceables.fastMaxBy { it.height }?.height ?: 0 + val startBarWidth = startBarPlaceables.fastMaxBy { it.width }?.width ?: 0 - val snackbarPlaceables = subcompose(ScaffoldLayoutContent.Snackbar, snackbar).fastMap { - it.measure(looseConstraints) - } + val topBarPlaceables = measurables[5] + .fastMap { it.measure(topBarConstraints) } - val snackbarHeight = snackbarPlaceables.fastMaxBy { it.height }?.height ?: 0 - val snackbarWidth = snackbarPlaceables.fastMaxBy { it.width }?.width ?: 0 + val topBarHeight = topBarPlaceables.fastMaxBy { it.height }?.height ?: 0 - // Tachiyomi: Calculate insets for snackbar placement offset - val snackbarLeft = if (snackbarPlaceables.isNotEmpty()) { - (insetLayoutWidth - snackbarWidth) / 2 + leftInset - } else { - 0 - } + val bottomPlaceablesConstraints = looseConstraints.offset( + -startInsetsWidth - endInsetsWidth, + -bottomInsetsHeight, + ) - val fabPlaceables = - subcompose(ScaffoldLayoutContent.Fab, fab).fastMap { measurable -> - measurable.measure(looseConstraints) - } + val snackbarPlaceables = measurables[6] + .fastMap { it.measure(bottomPlaceablesConstraints) } - val fabWidth = fabPlaceables.fastMaxBy { it.width }?.width ?: 0 - val fabHeight = fabPlaceables.fastMaxBy { it.height }?.height ?: 0 + val snackbarHeight = snackbarPlaceables.fastMaxBy { it.height }?.height ?: 0 + val snackbarWidth = snackbarPlaceables.fastMaxBy { it.width }?.width ?: 0 - val fabPlacement = if (fabPlaceables.isNotEmpty() && fabWidth != 0 && fabHeight != 0) { - // FAB distance from the left of the layout, taking into account LTR / RTL - // Tachiyomi: Calculate insets for fab placement offset - val fabLeftOffset = if (fabPosition == FabPosition.End) { + val fabPlaceables = measurables[7] + .fastMap { it.measure(bottomPlaceablesConstraints) } + + val fabWidth = fabPlaceables.fastMaxBy { it.width }?.width ?: 0 + val fabHeight = fabPlaceables.fastMaxBy { it.height }?.height ?: 0 + + val fabPlacement = if (fabWidth > 0 && fabHeight > 0) { + // FAB distance from the left of the layout, taking into account LTR / RTL + val fabLeftOffset = when (fabPosition) { + FabPosition.Start -> { if (layoutDirection == LayoutDirection.Ltr) { - layoutWidth - FabSpacing.roundToPx() - fabWidth - rightInset + FabSpacing.roundToPx() } else { - FabSpacing.roundToPx() + leftInset + layoutWidth - FabSpacing.roundToPx() - fabWidth } - } else { - leftInset + ((insetLayoutWidth - fabWidth) / 2) } - - FabPlacement( - left = fabLeftOffset, - width = fabWidth, - height = fabHeight, - ) - } else { - null - } - - val bottomBarPlaceables = subcompose(ScaffoldLayoutContent.BottomBar) { - CompositionLocalProvider( - LocalFabPlacement provides fabPlacement, - content = bottomBar, - ) - }.fastMap { it.measure(looseConstraints) } - - val bottomBarHeight = bottomBarPlaceables - .fastMaxBy { it.height } - ?.height - ?.takeIf { it != 0 } - val fabOffsetFromBottom = fabPlacement?.let { - max(bottomBarHeight ?: 0, bottomInset) + it.height + FabSpacing.roundToPx() - } - - val snackbarOffsetFromBottom = if (snackbarHeight != 0) { - snackbarHeight + (fabOffsetFromBottom ?: max(bottomBarHeight ?: 0, bottomInset)) - } else { - 0 - } - - val bodyContentPlaceables = subcompose(ScaffoldLayoutContent.MainContent) { - val insets = contentWindowInsets.asPaddingValues(this@SubcomposeLayout) - val fabOffsetDp = fabOffsetFromBottom?.toDp() ?: 0.dp - val bottomBarHeightPx = bottomBarHeight ?: 0 - val innerPadding = PaddingValues( - top = - if (topBarPlaceables.isEmpty()) { - insets.calculateTopPadding() + FabPosition.End, FabPosition.EndOverlay -> { + if (layoutDirection == LayoutDirection.Ltr) { + layoutWidth - FabSpacing.roundToPx() - fabWidth } else { - topBarHeight.toDp() - }, - bottom = // Tachiyomi: Also take account of fab height when providing inner padding - if (bottomBarPlaceables.isEmpty() || bottomBarHeightPx == 0) { - max(insets.calculateBottomPadding(), fabOffsetDp) - } else { - max(bottomBarHeightPx.toDp(), fabOffsetDp) - }, - start = max( - insets.calculateStartPadding((this@SubcomposeLayout).layoutDirection), - startBarWidth.toDp(), - ), - end = insets.calculateEndPadding((this@SubcomposeLayout).layoutDirection), - ) - content(innerPadding) - }.fastMap { it.measure(looseConstraints) } + FabSpacing.roundToPx() + } + } + else -> (layoutWidth - fabWidth) / 2 + } + FabPlacement( + left = fabLeftOffset, + width = fabWidth, + height = fabHeight, + ) + } else { + null + } + + val bottomBarPlaceables = measurables[8] + .fastMap { it.measure(looseConstraints) } + + val bottomBarHeight = bottomBarPlaceables.fastMaxBy { it.height }?.height ?: 0 + + val fabOffsetFromBottom = fabPlacement?.let { + if (fabPosition == FabPosition.EndOverlay) { + it.height + FabSpacing.roundToPx() + bottomInsetsHeight + } else { + // Total height is the bottom bar height + the FAB height + the padding + // between the FAB and bottom bar + max(bottomBarHeight, bottomInsetsHeight) + it.height + FabSpacing.roundToPx() + } + } + val snackbarOffsetFromBottom = if (snackbarHeight != 0) { + snackbarHeight + max( + fabOffsetFromBottom ?: 0, + max( + bottomBarHeight, + bottomInsetsHeight, + ), + ) + } else { + 0 + } + + // Update the backing value for the content padding of the body content + // We do this before measuring or placing the body content + topContentPadding = max(topBarHeight, topInsetsHeight).toDp() + bottomContentPadding = max(fabOffsetFromBottom ?: 0, max(bottomBarHeight, bottomInsetsHeight)).toDp() + startContentPadding = max(startBarWidth, startInsetsWidth).toDp() + endContentPadding = endInsetsWidth.toDp() + + val bodyContentPlaceables = measurables[9] + .fastMap { it.measure(looseConstraints) } + + layout(layoutWidth, layoutHeight) { + // Inset spacers are just for convenient measurement logic, no need to place them // Placing to control drawing order to match default elevation of each placeable - bodyContentPlaceables.fastForEach { it.place(0, 0) } @@ -299,50 +328,27 @@ private fun ScaffoldLayout( } snackbarPlaceables.fastForEach { it.place( - snackbarLeft, + (layoutWidth - snackbarWidth) / 2 + when (layoutDirection) { + LayoutDirection.Ltr -> startInsetsWidth + LayoutDirection.Rtl -> endInsetsWidth + }, layoutHeight - snackbarOffsetFromBottom, ) } // The bottom bar is always at the bottom of the layout bottomBarPlaceables.fastForEach { - it.place(0, layoutHeight - (bottomBarHeight ?: 0)) + it.place(0, layoutHeight - bottomBarHeight) } // Explicitly not using placeRelative here as `leftOffset` already accounts for RTL - fabPlaceables.fastForEach { - it.place(fabPlacement?.left ?: 0, layoutHeight - (fabOffsetFromBottom ?: 0)) + fabPlacement?.let { placement -> + fabPlaceables.fastForEach { + it.place(placement.left, layoutHeight - fabOffsetFromBottom!!) + } } } } } -/** - * The possible positions for a [FloatingActionButton] attached to a [Scaffold]. - */ -@ExperimentalMaterial3Api -@JvmInline -value class FabPosition internal constructor(@Suppress("unused") private val value: Int) { - companion object { - /** - * Position FAB at the bottom of the screen in the center, above the [NavigationBar] (if it - * exists) - */ - val Center = FabPosition(0) - - /** - * Position FAB at the bottom of the screen at the end, above the [NavigationBar] (if it - * exists) - */ - val End = FabPosition(1) - } - - override fun toString(): String { - return when (this) { - Center -> "FabPosition.Center" - else -> "FabPosition.End" - } - } -} - /** * Placement information for a [FloatingActionButton] inside a [Scaffold]. * @@ -358,12 +364,5 @@ internal class FabPlacement( val height: Int, ) -/** - * CompositionLocal containing a [FabPlacement] that is used to calculate the FAB bottom offset. - */ -internal val LocalFabPlacement = staticCompositionLocalOf { null } - // FAB spacing above the bottom bar / bottom of the Scaffold private val FabSpacing = 16.dp - -private enum class ScaffoldLayoutContent { TopBar, MainContent, Snackbar, Fab, BottomBar, StartBar } diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt index 23d65d07f2..30f6766d88 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt @@ -1,63 +1,15 @@ package tachiyomi.presentation.core.components.material -import androidx.compose.animation.core.Spring -import androidx.compose.animation.core.animateDpAsState -import androidx.compose.animation.core.spring import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentSize -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.TabPosition -import androidx.compose.material3.TabRowDefaults.SecondaryIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.composed -import androidx.compose.ui.draw.clip import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import tachiyomi.presentation.core.components.Pill -private fun Modifier.tabIndicatorOffset( - currentTabPosition: TabPosition, - currentPageOffsetFraction: Float, -) = fillMaxWidth() - .wrapContentSize(Alignment.BottomStart) - .composed { - val currentTabWidth by animateDpAsState( - targetValue = currentTabPosition.width, - animationSpec = spring(stiffness = Spring.StiffnessMediumLow), - label = "currentTabWidth", - ) - val offset by animateDpAsState( - targetValue = currentTabPosition.left + (currentTabWidth * currentPageOffsetFraction), - animationSpec = spring(stiffness = Spring.StiffnessMediumLow), - label = "offset", - ) - Modifier - .offset { IntOffset(x = offset.roundToPx(), y = 0) } - .width(currentTabWidth) - } - -@Composable -fun TabIndicator(currentTabPosition: TabPosition, currentPageOffsetFraction: Float) { - SecondaryIndicator( - modifier = Modifier - .tabIndicatorOffset(currentTabPosition, currentPageOffsetFraction) - .padding(horizontal = 8.dp) - .clip(RoundedCornerShape(topStart = 3.dp, topEnd = 3.dp)), - ) -} - @Composable fun TabText(text: String, badgeCount: Int? = null) { val pillAlpha = if (isSystemInDarkTheme()) 0.12f else 0.08f