mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +01:00 
			
		
		
		
	Merge branch 'master' into sync-part-final
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							| @@ -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' | ||||
|   | ||||
| @@ -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, | ||||
|                     ), | ||||
|                 ) | ||||
|   | ||||
| @@ -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, | ||||
|                                     ), | ||||
|                                 ) | ||||
|   | ||||
| @@ -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 | ||||
|                         }, | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
| @@ -58,7 +58,7 @@ fun GlobalSearchToolbar( | ||||
|             ) | ||||
|             if (progress in 1..<total) { | ||||
|                 LinearProgressIndicator( | ||||
|                     progress = progress / total.toFloat(), | ||||
|                     progress = { progress / total.toFloat() }, | ||||
|                     modifier = Modifier | ||||
|                         .align(Alignment.BottomStart) | ||||
|                         .fillMaxWidth(), | ||||
|   | ||||
| @@ -19,7 +19,7 @@ fun CategoryFloatingActionButton( | ||||
| ) { | ||||
|     ExtendedFloatingActionButton( | ||||
|         text = { Text(text = stringResource(R.string.action_add)) }, | ||||
|         icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = "") }, | ||||
|         icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = null) }, | ||||
|         onClick = onCreate, | ||||
|         expanded = lazyListState.isScrollingUp() || lazyListState.isScrolledToEnd(), | ||||
|     ) | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Spacer | ||||
| 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.Label | ||||
| import androidx.compose.material.icons.outlined.ArrowDropDown | ||||
| import androidx.compose.material.icons.outlined.ArrowDropUp | ||||
| import androidx.compose.material.icons.outlined.Delete | ||||
| @@ -49,7 +50,7 @@ fun CategoryListItem( | ||||
|                 ), | ||||
|             verticalAlignment = Alignment.CenterVertically, | ||||
|         ) { | ||||
|             Icon(imageVector = Icons.Outlined.Label, contentDescription = "") | ||||
|             Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = "") | ||||
|             Text( | ||||
|                 text = category.name, | ||||
|                 modifier = Modifier | ||||
|   | ||||
| @@ -8,10 +8,8 @@ import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.text.BasicTextField | ||||
| import androidx.compose.foundation.text.KeyboardActions | ||||
| import androidx.compose.foundation.text.KeyboardOptions | ||||
| import androidx.compose.material.TextFieldDefaults | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.outlined.ArrowBack | ||||
| import androidx.compose.material.icons.outlined.ArrowForward | ||||
| import androidx.compose.material.icons.automirrored.outlined.ArrowBack | ||||
| import androidx.compose.material.icons.outlined.Close | ||||
| import androidx.compose.material.icons.outlined.MoreVert | ||||
| import androidx.compose.material.icons.outlined.Search | ||||
| @@ -20,11 +18,15 @@ import androidx.compose.material3.Icon | ||||
| import androidx.compose.material3.IconButton | ||||
| import androidx.compose.material3.LocalContentColor | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.PlainTooltipBox | ||||
| import androidx.compose.material3.PlainTooltip | ||||
| import androidx.compose.material3.Text | ||||
| import androidx.compose.material3.TextFieldDefaults | ||||
| import androidx.compose.material3.TooltipBox | ||||
| import androidx.compose.material3.TooltipDefaults | ||||
| import androidx.compose.material3.TopAppBar | ||||
| import androidx.compose.material3.TopAppBarDefaults | ||||
| import androidx.compose.material3.TopAppBarScrollBehavior | ||||
| import androidx.compose.material3.rememberTooltipState | ||||
| import androidx.compose.material3.surfaceColorAtElevation | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.derivedStateOf | ||||
| @@ -40,14 +42,12 @@ import androidx.compose.ui.graphics.Color | ||||
| import androidx.compose.ui.graphics.SolidColor | ||||
| import androidx.compose.ui.graphics.vector.ImageVector | ||||
| import androidx.compose.ui.platform.LocalFocusManager | ||||
| import androidx.compose.ui.platform.LocalLayoutDirection | ||||
| import androidx.compose.ui.platform.LocalSoftwareKeyboardController | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.text.font.FontWeight | ||||
| import androidx.compose.ui.text.input.ImeAction | ||||
| import androidx.compose.ui.text.input.VisualTransformation | ||||
| import androidx.compose.ui.text.style.TextOverflow | ||||
| import androidx.compose.ui.unit.LayoutDirection | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.unit.sp | ||||
| import eu.kanade.tachiyomi.R | ||||
| @@ -189,13 +189,18 @@ fun AppBarActions( | ||||
|     var showMenu by remember { mutableStateOf(false) } | ||||
|  | ||||
|     actions.filterIsInstance<AppBar.Action>().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<AppBar.OverflowAction>() | ||||
|     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), | ||||
|   | ||||
| @@ -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, | ||||
|             ) | ||||
|         }, | ||||
|   | ||||
| @@ -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 = {}, | ||||
|                     ), | ||||
|                 ), | ||||
|   | ||||
| @@ -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 -> | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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), | ||||
|             ) | ||||
|         } | ||||
|   | ||||
| @@ -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 = {}, | ||||
|   | ||||
| @@ -148,7 +148,7 @@ private fun DownloadingIndicator( | ||||
|                 MaterialTheme.colorScheme.background | ||||
|             } | ||||
|             CircularProgressIndicator( | ||||
|                 progress = animatedProgress, | ||||
|                 progress = { animatedProgress }, | ||||
|                 modifier = IndicatorModifier, | ||||
|                 color = strokeColor, | ||||
|                 strokeWidth = IndicatorSize / 2, | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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) }, | ||||
|                 ) | ||||
|             } | ||||
|   | ||||
| @@ -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) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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), | ||||
|             ) | ||||
|         } | ||||
|   | ||||
| @@ -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, | ||||
|                             ) | ||||
|   | ||||
| @@ -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), | ||||
|   | ||||
| @@ -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, | ||||
|                     ) | ||||
|                 } | ||||
|   | ||||
| @@ -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") | ||||
|                 }, | ||||
|   | ||||
| @@ -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), | ||||
|                                     ) | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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") }, | ||||
|                             ), | ||||
|                         ), | ||||
|   | ||||
| @@ -39,7 +39,7 @@ class ReaderProgressIndicator @JvmOverloads constructor( | ||||
|     @Composable | ||||
|     override fun Content() { | ||||
|         TachiyomiTheme { | ||||
|             CombinedCircularProgressIndicator(progress = progress) | ||||
|             CombinedCircularProgressIndicator(progress = { progress }) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
| @@ -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" } | ||||
|   | ||||
| @@ -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] | ||||
|   | ||||
| @@ -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 }) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -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<FabPlacement?> { 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 } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user