mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +01:00 
			
		
		
		
	Settings: M3 and two pane ui (#8211)
* Settings: M3 and two pane ui
* TrackingLoginDialog: Move close button
* Use small top bar
* Revert "Update voyager to v1.0.0-rc02"
This reverts commit 570fec6ea6.
https://github.com/adrielcafe/voyager/issues/62
			
			
This commit is contained in:
		| @@ -56,6 +56,7 @@ import androidx.compose.ui.unit.dp | ||||
|  * @sample androidx.compose.material3.samples.ScaffoldWithSimpleSnackbar | ||||
|  * | ||||
|  * Tachiyomi changes: | ||||
|  * * 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 | ||||
|  * | ||||
| @@ -80,6 +81,7 @@ import androidx.compose.ui.unit.dp | ||||
| @Composable | ||||
| fun Scaffold( | ||||
|     modifier: Modifier = Modifier, | ||||
|     topBarScrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()), | ||||
|     topBar: @Composable (TopAppBarScrollBehavior) -> Unit = {}, | ||||
|     bottomBar: @Composable () -> Unit = {}, | ||||
|     snackbarHost: @Composable () -> Unit = {}, | ||||
| @@ -89,21 +91,16 @@ fun Scaffold( | ||||
|     contentColor: Color = contentColorFor(containerColor), | ||||
|     content: @Composable (PaddingValues) -> Unit, | ||||
| ) { | ||||
|     /** | ||||
|      * Tachiyomi: Pass scroll behavior to topBar | ||||
|      */ | ||||
|     val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) | ||||
|  | ||||
|     androidx.compose.material3.Surface( | ||||
|         modifier = Modifier | ||||
|             .nestedScroll(scrollBehavior.nestedScrollConnection) | ||||
|             .nestedScroll(topBarScrollBehavior.nestedScrollConnection) | ||||
|             .then(modifier), | ||||
|         color = containerColor, | ||||
|         contentColor = contentColor, | ||||
|     ) { | ||||
|         ScaffoldLayout( | ||||
|             fabPosition = floatingActionButtonPosition, | ||||
|             topBar = { topBar(scrollBehavior) }, | ||||
|             topBar = { topBar(topBarScrollBehavior) }, | ||||
|             bottomBar = bottomBar, | ||||
|             content = content, | ||||
|             snackbar = snackbarHost, | ||||
|   | ||||
| @@ -0,0 +1,35 @@ | ||||
| package eu.kanade.presentation.components | ||||
|  | ||||
| import androidx.compose.foundation.layout.Box | ||||
| import androidx.compose.foundation.layout.BoxScope | ||||
| import androidx.compose.foundation.layout.BoxWithConstraints | ||||
| import androidx.compose.foundation.layout.fillMaxSize | ||||
| import androidx.compose.foundation.layout.width | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.unit.dp | ||||
|  | ||||
| @Composable | ||||
| fun TwoPanelBox( | ||||
|     modifier: Modifier = Modifier, | ||||
|     startContent: @Composable BoxScope.() -> Unit, | ||||
|     endContent: @Composable BoxScope.() -> Unit, | ||||
| ) { | ||||
|     BoxWithConstraints(modifier = modifier.fillMaxSize()) { | ||||
|         val firstWidth = (maxWidth / 2).coerceAtMost(450.dp) | ||||
|         val secondWidth = maxWidth - firstWidth | ||||
|         Box( | ||||
|             modifier = Modifier | ||||
|                 .align(Alignment.TopStart) | ||||
|                 .width(firstWidth), | ||||
|             content = startContent, | ||||
|         ) | ||||
|         Box( | ||||
|             modifier = Modifier | ||||
|                 .align(Alignment.TopEnd) | ||||
|                 .width(secondWidth), | ||||
|             content = endContent, | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| @@ -6,7 +6,6 @@ import androidx.compose.animation.core.animateFloatAsState | ||||
| import androidx.compose.animation.fadeIn | ||||
| import androidx.compose.animation.fadeOut | ||||
| import androidx.compose.foundation.layout.Box | ||||
| import androidx.compose.foundation.layout.BoxWithConstraints | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.PaddingValues | ||||
| import androidx.compose.foundation.layout.WindowInsets | ||||
| @@ -15,13 +14,11 @@ import androidx.compose.foundation.layout.asPaddingValues | ||||
| import androidx.compose.foundation.layout.calculateEndPadding | ||||
| import androidx.compose.foundation.layout.calculateStartPadding | ||||
| import androidx.compose.foundation.layout.fillMaxHeight | ||||
| import androidx.compose.foundation.layout.fillMaxSize | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.layout.navigationBars | ||||
| import androidx.compose.foundation.layout.only | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.layout.systemBars | ||||
| import androidx.compose.foundation.layout.width | ||||
| import androidx.compose.foundation.lazy.LazyListScope | ||||
| import androidx.compose.foundation.lazy.items | ||||
| import androidx.compose.foundation.lazy.rememberLazyListState | ||||
| @@ -47,7 +44,6 @@ import androidx.compose.ui.platform.LocalDensity | ||||
| import androidx.compose.ui.platform.LocalHapticFeedback | ||||
| import androidx.compose.ui.platform.LocalLayoutDirection | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.unit.dp | ||||
| import eu.kanade.domain.chapter.model.Chapter | ||||
| import eu.kanade.presentation.components.ChapterDownloadAction | ||||
| import eu.kanade.presentation.components.ExtendedFloatingActionButton | ||||
| @@ -55,6 +51,7 @@ import eu.kanade.presentation.components.LazyColumn | ||||
| import eu.kanade.presentation.components.MangaBottomActionMenu | ||||
| import eu.kanade.presentation.components.Scaffold | ||||
| import eu.kanade.presentation.components.SwipeRefresh | ||||
| import eu.kanade.presentation.components.TwoPanelBox | ||||
| import eu.kanade.presentation.components.VerticalFastScroller | ||||
| import eu.kanade.presentation.manga.components.ChapterHeader | ||||
| import eu.kanade.presentation.manga.components.ExpandableMangaDescription | ||||
| @@ -501,79 +498,74 @@ fun MangaScreenLargeImpl( | ||||
|                 } | ||||
|             }, | ||||
|         ) { contentPadding -> | ||||
|             BoxWithConstraints(modifier = Modifier.fillMaxSize()) { | ||||
|                 val firstWidth = (maxWidth / 2).coerceAtMost(450.dp) | ||||
|                 val secondWidth = maxWidth - firstWidth | ||||
|  | ||||
|                 Column( | ||||
|                     modifier = Modifier | ||||
|                         .align(Alignment.TopStart) | ||||
|                         .width(firstWidth) | ||||
|                         .verticalScroll(rememberScrollState()), | ||||
|                 ) { | ||||
|                     MangaInfoBox( | ||||
|                         windowWidthSizeClass = windowWidthSizeClass, | ||||
|                         appBarPadding = contentPadding.calculateTopPadding(), | ||||
|                         title = state.manga.title, | ||||
|                         author = state.manga.author, | ||||
|                         artist = state.manga.artist, | ||||
|                         sourceName = remember { state.source.getNameForMangaInfo() }, | ||||
|                         isStubSource = remember { state.source is SourceManager.StubSource }, | ||||
|                         coverDataProvider = { state.manga }, | ||||
|                         status = state.manga.status, | ||||
|                         onCoverClick = onCoverClicked, | ||||
|                         doSearch = onSearch, | ||||
|                     ) | ||||
|                     MangaActionRow( | ||||
|                         favorite = state.manga.favorite, | ||||
|                         trackingCount = state.trackingCount, | ||||
|                         onAddToLibraryClicked = onAddToLibraryClicked, | ||||
|                         onWebViewClicked = onWebViewClicked, | ||||
|                         onTrackingClicked = onTrackingClicked, | ||||
|                         onEditCategory = onEditCategoryClicked, | ||||
|                     ) | ||||
|                     ExpandableMangaDescription( | ||||
|                         defaultExpandState = true, | ||||
|                         description = state.manga.description, | ||||
|                         tagsProvider = { state.manga.genre }, | ||||
|                         onTagClicked = onTagClicked, | ||||
|                     ) | ||||
|                 } | ||||
|  | ||||
|                 VerticalFastScroller( | ||||
|                     listState = chapterListState, | ||||
|                     modifier = Modifier | ||||
|                         .align(Alignment.TopEnd) | ||||
|                         .width(secondWidth), | ||||
|                     topContentPadding = contentPadding.calculateTopPadding(), | ||||
|                 ) { | ||||
|                     LazyColumn( | ||||
|                         modifier = Modifier.fillMaxHeight(), | ||||
|                         state = chapterListState, | ||||
|                         contentPadding = PaddingValues( | ||||
|                             top = contentPadding.calculateTopPadding(), | ||||
|                             bottom = contentPadding.calculateBottomPadding(), | ||||
|                         ), | ||||
|             TwoPanelBox( | ||||
|                 startContent = { | ||||
|                     Column( | ||||
|                         modifier = Modifier | ||||
|                             .verticalScroll(rememberScrollState()), | ||||
|                     ) { | ||||
|                         item( | ||||
|                             key = MangaScreenItem.CHAPTER_HEADER, | ||||
|                             contentType = MangaScreenItem.CHAPTER_HEADER, | ||||
|                         ) { | ||||
|                             ChapterHeader( | ||||
|                                 chapterCount = chapters.size, | ||||
|                                 onClick = onFilterButtonClicked, | ||||
|                             ) | ||||
|                         } | ||||
|  | ||||
|                         sharedChapterItems( | ||||
|                             chapters = chapters, | ||||
|                             onChapterClicked = onChapterClicked, | ||||
|                             onDownloadChapter = onDownloadChapter, | ||||
|                             onChapterSelected = onChapterSelected, | ||||
|                         MangaInfoBox( | ||||
|                             windowWidthSizeClass = windowWidthSizeClass, | ||||
|                             appBarPadding = contentPadding.calculateTopPadding(), | ||||
|                             title = state.manga.title, | ||||
|                             author = state.manga.author, | ||||
|                             artist = state.manga.artist, | ||||
|                             sourceName = remember { state.source.getNameForMangaInfo() }, | ||||
|                             isStubSource = remember { state.source is SourceManager.StubSource }, | ||||
|                             coverDataProvider = { state.manga }, | ||||
|                             status = state.manga.status, | ||||
|                             onCoverClick = onCoverClicked, | ||||
|                             doSearch = onSearch, | ||||
|                         ) | ||||
|                         MangaActionRow( | ||||
|                             favorite = state.manga.favorite, | ||||
|                             trackingCount = state.trackingCount, | ||||
|                             onAddToLibraryClicked = onAddToLibraryClicked, | ||||
|                             onWebViewClicked = onWebViewClicked, | ||||
|                             onTrackingClicked = onTrackingClicked, | ||||
|                             onEditCategory = onEditCategoryClicked, | ||||
|                         ) | ||||
|                         ExpandableMangaDescription( | ||||
|                             defaultExpandState = true, | ||||
|                             description = state.manga.description, | ||||
|                             tagsProvider = { state.manga.genre }, | ||||
|                             onTagClicked = onTagClicked, | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|                 }, | ||||
|                 endContent = { | ||||
|                     VerticalFastScroller( | ||||
|                         listState = chapterListState, | ||||
|                         topContentPadding = contentPadding.calculateTopPadding(), | ||||
|                     ) { | ||||
|                         LazyColumn( | ||||
|                             modifier = Modifier.fillMaxHeight(), | ||||
|                             state = chapterListState, | ||||
|                             contentPadding = PaddingValues( | ||||
|                                 top = contentPadding.calculateTopPadding(), | ||||
|                                 bottom = contentPadding.calculateBottomPadding(), | ||||
|                             ), | ||||
|                         ) { | ||||
|                             item( | ||||
|                                 key = MangaScreenItem.CHAPTER_HEADER, | ||||
|                                 contentType = MangaScreenItem.CHAPTER_HEADER, | ||||
|                             ) { | ||||
|                                 ChapterHeader( | ||||
|                                     chapterCount = chapters.size, | ||||
|                                     onClick = onFilterButtonClicked, | ||||
|                                 ) | ||||
|                             } | ||||
|  | ||||
|                             sharedChapterItems( | ||||
|                                 chapters = chapters, | ||||
|                                 onChapterClicked = onChapterClicked, | ||||
|                                 onDownloadChapter = onDownloadChapter, | ||||
|                                 onChapterSelected = onChapterSelected, | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,25 +2,48 @@ package eu.kanade.presentation.more.settings | ||||
|  | ||||
| import androidx.annotation.StringRes | ||||
| import androidx.compose.foundation.layout.RowScope | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.filled.ArrowBack | ||||
| import androidx.compose.material3.Icon | ||||
| import androidx.compose.material3.IconButton | ||||
| import androidx.compose.material3.Text | ||||
| import androidx.compose.material3.TopAppBar | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import eu.kanade.presentation.components.AppBar | ||||
| import androidx.compose.ui.unit.dp | ||||
| import eu.kanade.presentation.components.Scaffold | ||||
| import eu.kanade.tachiyomi.R | ||||
|  | ||||
| @Composable | ||||
| fun PreferenceScaffold( | ||||
|     @StringRes titleRes: Int, | ||||
|     actions: @Composable RowScope.() -> Unit = {}, | ||||
|     onBackPressed: () -> Unit = {}, | ||||
|     onBackPressed: (() -> Unit)? = null, | ||||
|     itemsProvider: @Composable () -> List<Preference>, | ||||
| ) { | ||||
|     Scaffold( | ||||
|         topBar = { scrollBehavior -> | ||||
|             AppBar( | ||||
|                 title = stringResource(titleRes), | ||||
|                 navigateUp = onBackPressed, | ||||
|         topBar = { | ||||
|             TopAppBar( | ||||
|                 title = { | ||||
|                     Text( | ||||
|                         text = stringResource(id = titleRes), | ||||
|                         modifier = Modifier.padding(start = 8.dp), | ||||
|                     ) | ||||
|                 }, | ||||
|                 navigationIcon = { | ||||
|                     if (onBackPressed != null) { | ||||
|                         IconButton(onClick = onBackPressed) { | ||||
|                             Icon( | ||||
|                                 imageVector = Icons.Default.ArrowBack, | ||||
|                                 contentDescription = stringResource(R.string.abc_action_bar_up_description), | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|                 actions = actions, | ||||
|                 scrollBehavior = scrollBehavior, | ||||
|                 scrollBehavior = it, | ||||
|             ) | ||||
|         }, | ||||
|         content = { contentPadding -> | ||||
|   | ||||
| @@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.PaddingValues | ||||
| import androidx.compose.foundation.layout.Spacer | ||||
| import androidx.compose.foundation.layout.height | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.lazy.items | ||||
| import androidx.compose.foundation.lazy.rememberLazyListState | ||||
| import androidx.compose.runtime.Composable | ||||
| @@ -12,7 +11,6 @@ import androidx.compose.runtime.LaunchedEffect | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.util.fastForEachIndexed | ||||
| import eu.kanade.presentation.components.Divider | ||||
| import eu.kanade.presentation.components.ScrollbarLazyColumn | ||||
| import eu.kanade.presentation.more.settings.screen.SearchableSettings | ||||
| import eu.kanade.presentation.more.settings.widget.PreferenceGroupHeader | ||||
| @@ -55,9 +53,6 @@ fun PreferenceScreen( | ||||
|  | ||||
|                     item { | ||||
|                         Column { | ||||
|                             if (i != 0) { | ||||
|                                 Divider(modifier = Modifier.padding(bottom = 8.dp)) | ||||
|                             } | ||||
|                             PreferenceGroupHeader(title = preference.title) | ||||
|                         } | ||||
|                     } | ||||
| @@ -68,7 +63,9 @@ fun PreferenceScreen( | ||||
|                         ) | ||||
|                     } | ||||
|                     item { | ||||
|                         Spacer(modifier = Modifier.height(12.dp)) | ||||
|                         if (i < items.lastIndex) { | ||||
|                             Spacer(modifier = Modifier.height(12.dp)) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.RowScope | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.ReadOnlyComposable | ||||
| import cafe.adriel.voyager.core.screen.Screen | ||||
| import cafe.adriel.voyager.navigator.currentOrThrow | ||||
| import eu.kanade.presentation.more.settings.Preference | ||||
| import eu.kanade.presentation.more.settings.PreferenceScaffold | ||||
| import eu.kanade.presentation.util.LocalBackPress | ||||
| @@ -26,10 +25,10 @@ interface SearchableSettings : Screen { | ||||
|  | ||||
|     @Composable | ||||
|     override fun Content() { | ||||
|         val handleBack = LocalBackPress.currentOrThrow | ||||
|         val handleBack = LocalBackPress.current | ||||
|         PreferenceScaffold( | ||||
|             titleRes = getTitleRes(), | ||||
|             onBackPressed = handleBack::invoke, | ||||
|             onBackPressed = if (handleBack != null) handleBack::invoke else null, | ||||
|             actions = { AppBarAction() }, | ||||
|             itemsProvider = { getPreferences() }, | ||||
|         ) | ||||
|   | ||||
| @@ -1,7 +1,13 @@ | ||||
| package eu.kanade.presentation.more.settings.screen | ||||
|  | ||||
| import androidx.annotation.StringRes | ||||
| import androidx.compose.foundation.background | ||||
| import androidx.compose.foundation.isSystemInDarkTheme | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.lazy.items | ||||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.filled.ArrowBack | ||||
| import androidx.compose.material.icons.outlined.ChromeReaderMode | ||||
| import androidx.compose.material.icons.outlined.Code | ||||
| import androidx.compose.material.icons.outlined.CollectionsBookmark | ||||
| @@ -13,103 +19,210 @@ import androidx.compose.material.icons.outlined.Security | ||||
| import androidx.compose.material.icons.outlined.SettingsBackupRestore | ||||
| import androidx.compose.material.icons.outlined.Sync | ||||
| import androidx.compose.material.icons.outlined.Tune | ||||
| import androidx.compose.material3.Icon | ||||
| import androidx.compose.material3.IconButton | ||||
| import androidx.compose.material3.LocalContentColor | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.Text | ||||
| import androidx.compose.material3.TopAppBar | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.NonRestartableComposable | ||||
| import androidx.compose.runtime.ReadOnlyComposable | ||||
| import androidx.compose.runtime.CompositionLocalProvider | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.draw.clip | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import androidx.compose.ui.graphics.toArgb | ||||
| import androidx.compose.ui.graphics.vector.ImageVector | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.util.fastFirstOrNull | ||||
| import androidx.core.graphics.ColorUtils | ||||
| import cafe.adriel.voyager.core.screen.Screen | ||||
| import cafe.adriel.voyager.navigator.LocalNavigator | ||||
| import cafe.adriel.voyager.navigator.Navigator | ||||
| import cafe.adriel.voyager.navigator.currentOrThrow | ||||
| import eu.kanade.presentation.components.AppBar | ||||
| import eu.kanade.presentation.components.AppBarActions | ||||
| import eu.kanade.presentation.more.settings.Preference | ||||
| import eu.kanade.presentation.more.settings.PreferenceScaffold | ||||
| import eu.kanade.presentation.components.LazyColumn | ||||
| import eu.kanade.presentation.components.Scaffold | ||||
| import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget | ||||
| import eu.kanade.presentation.util.LocalBackPress | ||||
| import eu.kanade.tachiyomi.R | ||||
|  | ||||
| object SettingsMainScreen : SearchableSettings { | ||||
|  | ||||
|     @Composable | ||||
|     @ReadOnlyComposable | ||||
|     @StringRes | ||||
|     override fun getTitleRes() = R.string.label_settings | ||||
|  | ||||
|     @Composable | ||||
|     @NonRestartableComposable | ||||
|     override fun getPreferences(): List<Preference> { | ||||
|         val navigator = LocalNavigator.currentOrThrow | ||||
|         return listOf( | ||||
|             Preference.PreferenceItem.TextPreference( | ||||
|                 title = stringResource(R.string.pref_category_general), | ||||
|                 icon = Icons.Outlined.Tune, | ||||
|                 onClick = { navigator.push(SettingsGeneralScreen()) }, | ||||
|             ), | ||||
|             Preference.PreferenceItem.TextPreference( | ||||
|                 title = stringResource(R.string.pref_category_appearance), | ||||
|                 icon = Icons.Outlined.Palette, | ||||
|                 onClick = { navigator.push(SettingsAppearanceScreen()) }, | ||||
|             ), | ||||
|             Preference.PreferenceItem.TextPreference( | ||||
|                 title = stringResource(R.string.pref_category_library), | ||||
|                 icon = Icons.Outlined.CollectionsBookmark, | ||||
|                 onClick = { navigator.push(SettingsLibraryScreen()) }, | ||||
|             ), | ||||
|             Preference.PreferenceItem.TextPreference( | ||||
|                 title = stringResource(R.string.pref_category_reader), | ||||
|                 icon = Icons.Outlined.ChromeReaderMode, | ||||
|                 onClick = { navigator.push(SettingsReaderScreen()) }, | ||||
|             ), | ||||
|             Preference.PreferenceItem.TextPreference( | ||||
|                 title = stringResource(R.string.pref_category_downloads), | ||||
|                 icon = Icons.Outlined.GetApp, | ||||
|                 onClick = { navigator.push(SettingsDownloadScreen()) }, | ||||
|             ), | ||||
|             Preference.PreferenceItem.TextPreference( | ||||
|                 title = stringResource(R.string.pref_category_tracking), | ||||
|                 icon = Icons.Outlined.Sync, | ||||
|                 onClick = { navigator.push(SettingsTrackingScreen()) }, | ||||
|             ), | ||||
|             Preference.PreferenceItem.TextPreference( | ||||
|                 title = stringResource(R.string.browse), | ||||
|                 icon = Icons.Outlined.Explore, | ||||
|                 onClick = { navigator.push(SettingsBrowseScreen()) }, | ||||
|             ), | ||||
|             Preference.PreferenceItem.TextPreference( | ||||
|                 title = stringResource(R.string.label_backup), | ||||
|                 icon = Icons.Outlined.SettingsBackupRestore, | ||||
|                 onClick = { navigator.push(SettingsBackupScreen()) }, | ||||
|             ), | ||||
|             Preference.PreferenceItem.TextPreference( | ||||
|                 title = stringResource(R.string.pref_category_security), | ||||
|                 icon = Icons.Outlined.Security, | ||||
|                 onClick = { navigator.push(SettingsSecurityScreen()) }, | ||||
|             ), | ||||
|             Preference.PreferenceItem.TextPreference( | ||||
|                 title = stringResource(R.string.pref_category_advanced), | ||||
|                 icon = Icons.Outlined.Code, | ||||
|                 onClick = { navigator.push(SettingsAdvancedScreen()) }, | ||||
|             ), | ||||
|         ) | ||||
|     } | ||||
|  | ||||
| object SettingsMainScreen : Screen { | ||||
|     @Composable | ||||
|     override fun Content() { | ||||
|         Content(twoPane = false) | ||||
|     } | ||||
|  | ||||
|     @Composable | ||||
|     private fun getPalerSurface(): Color { | ||||
|         val surface = MaterialTheme.colorScheme.surface | ||||
|         val dark = isSystemInDarkTheme() | ||||
|         return remember(surface, dark) { | ||||
|             val arr = FloatArray(3) | ||||
|             ColorUtils.colorToHSL(surface.toArgb(), arr) | ||||
|             arr[2] = if (dark) { | ||||
|                 arr[2] - 0.05f | ||||
|             } else { | ||||
|                 arr[2] + 0.02f | ||||
|             }.coerceIn(0f, 1f) | ||||
|             Color.hsl(arr[0], arr[1], arr[2]) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Composable | ||||
|     fun Content(twoPane: Boolean) { | ||||
|         val navigator = LocalNavigator.currentOrThrow | ||||
|         val backPress = LocalBackPress.currentOrThrow | ||||
|         PreferenceScaffold( | ||||
|             titleRes = getTitleRes(), | ||||
|             actions = { | ||||
|                 AppBarActions( | ||||
|                     listOf( | ||||
|                         AppBar.Action( | ||||
|                             title = stringResource(R.string.action_search), | ||||
|                             icon = Icons.Outlined.Search, | ||||
|                             onClick = { navigator.push(SettingsSearchScreen()) }, | ||||
|                         ), | ||||
|                     ), | ||||
|                 ) | ||||
|         val containerColor = if (twoPane) getPalerSurface() else MaterialTheme.colorScheme.surface | ||||
|         Scaffold( | ||||
|             topBar = { scrollBehavior -> | ||||
|                 // https://issuetracker.google.com/issues/249688556 | ||||
|                 MaterialTheme( | ||||
|                     colorScheme = MaterialTheme.colorScheme.copy(surface = containerColor), | ||||
|                 ) { | ||||
|                     TopAppBar( | ||||
|                         title = { | ||||
|                             Text( | ||||
|                                 text = stringResource(R.string.label_settings), | ||||
|                                 modifier = Modifier.padding(start = 8.dp), | ||||
|                             ) | ||||
|                         }, | ||||
|                         navigationIcon = { | ||||
|                             IconButton(onClick = backPress::invoke) { | ||||
|                                 Icon( | ||||
|                                     imageVector = Icons.Default.ArrowBack, | ||||
|                                     contentDescription = stringResource(R.string.abc_action_bar_up_description), | ||||
|                                 ) | ||||
|                             } | ||||
|                         }, | ||||
|                         actions = { | ||||
|                             AppBarActions( | ||||
|                                 listOf( | ||||
|                                     AppBar.Action( | ||||
|                                         title = stringResource(R.string.action_search), | ||||
|                                         icon = Icons.Outlined.Search, | ||||
|                                         onClick = { navigator.navigate(SettingsSearchScreen(), twoPane) }, | ||||
|                                     ), | ||||
|                                 ), | ||||
|                             ) | ||||
|                         }, | ||||
|                         scrollBehavior = scrollBehavior, | ||||
|                     ) | ||||
|                 } | ||||
|             }, | ||||
|             containerColor = containerColor, | ||||
|             content = { contentPadding -> | ||||
|                 LazyColumn(contentPadding = contentPadding) { | ||||
|                     items( | ||||
|                         items = items, | ||||
|                         key = { it.hashCode() }, | ||||
|                     ) { item -> | ||||
|                         var modifier: Modifier = Modifier | ||||
|                         var contentColor = LocalContentColor.current | ||||
|                         if (twoPane) { | ||||
|                             val selected = navigator.items.fastFirstOrNull { it::class == item.screen::class } != null | ||||
|                             modifier = Modifier | ||||
|                                 .padding(horizontal = 8.dp) | ||||
|                                 .clip(RoundedCornerShape(24.dp)) | ||||
|                                 .then( | ||||
|                                     if (selected) { | ||||
|                                         Modifier.background(MaterialTheme.colorScheme.surfaceVariant) | ||||
|                                     } else { | ||||
|                                         Modifier | ||||
|                                     }, | ||||
|                                 ) | ||||
|                             if (selected) { | ||||
|                                 contentColor = MaterialTheme.colorScheme.onSurfaceVariant | ||||
|                             } | ||||
|                         } | ||||
|                         CompositionLocalProvider(LocalContentColor provides contentColor) { | ||||
|                             TextPreferenceWidget( | ||||
|                                 modifier = modifier, | ||||
|                                 title = stringResource(item.titleRes), | ||||
|                                 subtitle = stringResource(item.subtitleRes), | ||||
|                                 icon = item.icon, | ||||
|                                 onPreferenceClick = { navigator.navigate(item.screen, twoPane) }, | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             onBackPressed = backPress::invoke, | ||||
|             itemsProvider = { getPreferences() }, | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     private fun Navigator.navigate(screen: Screen, twoPane: Boolean) { | ||||
|         if (twoPane) replaceAll(screen) else push(screen) | ||||
|     } | ||||
| } | ||||
|  | ||||
| private data class Item( | ||||
|     @StringRes val titleRes: Int, | ||||
|     @StringRes val subtitleRes: Int, | ||||
|     val icon: ImageVector, | ||||
|     val screen: Screen, | ||||
| ) | ||||
|  | ||||
| private val items = listOf( | ||||
|     Item( | ||||
|         titleRes = R.string.pref_category_general, | ||||
|         subtitleRes = R.string.pref_general_summary, | ||||
|         icon = Icons.Outlined.Tune, | ||||
|         screen = SettingsGeneralScreen(), | ||||
|     ), | ||||
|     Item( | ||||
|         titleRes = R.string.pref_category_appearance, | ||||
|         subtitleRes = R.string.pref_appearance_summary, | ||||
|         icon = Icons.Outlined.Palette, | ||||
|         screen = SettingsAppearanceScreen(), | ||||
|     ), | ||||
|     Item( | ||||
|         titleRes = R.string.pref_category_library, | ||||
|         subtitleRes = R.string.pref_library_summary, | ||||
|         icon = Icons.Outlined.CollectionsBookmark, | ||||
|         screen = SettingsLibraryScreen(), | ||||
|     ), | ||||
|     Item( | ||||
|         titleRes = R.string.pref_category_reader, | ||||
|         subtitleRes = R.string.pref_reader_summary, | ||||
|         icon = Icons.Outlined.ChromeReaderMode, | ||||
|         screen = SettingsReaderScreen(), | ||||
|     ), | ||||
|     Item( | ||||
|         titleRes = R.string.pref_category_downloads, | ||||
|         subtitleRes = R.string.pref_downloads_summary, | ||||
|         icon = Icons.Outlined.GetApp, | ||||
|         screen = SettingsDownloadScreen(), | ||||
|     ), | ||||
|     Item( | ||||
|         titleRes = R.string.pref_category_tracking, | ||||
|         subtitleRes = R.string.pref_tracking_summary, | ||||
|         icon = Icons.Outlined.Sync, | ||||
|         screen = SettingsTrackingScreen(), | ||||
|     ), | ||||
|     Item( | ||||
|         titleRes = R.string.browse, | ||||
|         subtitleRes = R.string.pref_browse_summary, | ||||
|         icon = Icons.Outlined.Explore, | ||||
|         screen = SettingsBrowseScreen(), | ||||
|     ), | ||||
|     Item( | ||||
|         titleRes = R.string.label_backup, | ||||
|         subtitleRes = R.string.pref_backup_summary, | ||||
|         icon = Icons.Outlined.SettingsBackupRestore, | ||||
|         screen = SettingsBackupScreen(), | ||||
|     ), | ||||
|     Item( | ||||
|         titleRes = R.string.pref_category_security, | ||||
|         subtitleRes = R.string.pref_security_summary, | ||||
|         icon = Icons.Outlined.Security, | ||||
|         screen = SettingsSecurityScreen(), | ||||
|     ), | ||||
|     Item( | ||||
|         titleRes = R.string.pref_category_advanced, | ||||
|         subtitleRes = R.string.pref_advanced_summary, | ||||
|         icon = Icons.Outlined.Code, | ||||
|         screen = SettingsAdvancedScreen(), | ||||
|     ), | ||||
| ) | ||||
|   | ||||
| @@ -146,8 +146,7 @@ class SettingsSearchScreen : Screen { | ||||
|                 contentPadding = contentPadding, | ||||
|             ) { result -> | ||||
|                 SearchableSettings.highlightKey = result.highlightKey | ||||
|                 navigator.popUntil { it is SettingsMainScreen } | ||||
|                 navigator.push(result.route) | ||||
|                 navigator.replace(result.route) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -10,6 +10,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.filled.Close | ||||
| import androidx.compose.material.icons.filled.HelpOutline | ||||
| import androidx.compose.material.icons.filled.Visibility | ||||
| import androidx.compose.material.icons.filled.VisibilityOff | ||||
| @@ -22,7 +23,6 @@ import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.OutlinedButton | ||||
| import androidx.compose.material3.OutlinedTextField | ||||
| import androidx.compose.material3.Text | ||||
| import androidx.compose.material3.TextButton | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.ReadOnlyComposable | ||||
| import androidx.compose.runtime.getValue | ||||
| @@ -30,6 +30,7 @@ import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.runtime.rememberCoroutineScope | ||||
| import androidx.compose.runtime.setValue | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.platform.LocalContext | ||||
| import androidx.compose.ui.platform.LocalUriHandler | ||||
| @@ -189,7 +190,20 @@ class SettingsTrackingScreen : SearchableSettings { | ||||
|  | ||||
|         AlertDialog( | ||||
|             onDismissRequest = onDismissRequest, | ||||
|             title = { Text(text = stringResource(R.string.login_title, stringResource(service.nameRes()))) }, | ||||
|             title = { | ||||
|                 Row(verticalAlignment = Alignment.CenterVertically) { | ||||
|                     Text( | ||||
|                         text = stringResource(R.string.login_title, stringResource(service.nameRes())), | ||||
|                         modifier = Modifier.weight(1f), | ||||
|                     ) | ||||
|                     IconButton(onClick = onDismissRequest) { | ||||
|                         Icon( | ||||
|                             imageVector = Icons.Default.Close, | ||||
|                             contentDescription = stringResource(R.string.action_close), | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             text = { | ||||
|                 Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { | ||||
|                     OutlinedTextField( | ||||
| @@ -232,38 +246,30 @@ class SettingsTrackingScreen : SearchableSettings { | ||||
|                 } | ||||
|             }, | ||||
|             confirmButton = { | ||||
|                 Column { | ||||
|                     Button( | ||||
|                         modifier = Modifier.fillMaxWidth(), | ||||
|                         enabled = !processing, | ||||
|                         onClick = { | ||||
|                             if (username.text.isEmpty() || password.text.isEmpty()) { | ||||
|                                 inputError = true | ||||
|                                 return@Button | ||||
|                             } | ||||
|                             scope.launchIO { | ||||
|                                 inputError = false | ||||
|                                 processing = true | ||||
|                                 val result = checkLogin( | ||||
|                                     context = context, | ||||
|                                     service = service, | ||||
|                                     username = username.text, | ||||
|                                     password = password.text, | ||||
|                                 ) | ||||
|                                 if (result) onDismissRequest() | ||||
|                                 processing = false | ||||
|                             } | ||||
|                         }, | ||||
|                     ) { | ||||
|                         val id = if (processing) R.string.loading else R.string.login | ||||
|                         Text(text = stringResource(id)) | ||||
|                     } | ||||
|                     TextButton( | ||||
|                         modifier = Modifier.fillMaxWidth(), | ||||
|                         onClick = onDismissRequest, | ||||
|                     ) { | ||||
|                         Text(text = stringResource(android.R.string.cancel)) | ||||
|                     } | ||||
|                 Button( | ||||
|                     modifier = Modifier.fillMaxWidth(), | ||||
|                     enabled = !processing, | ||||
|                     onClick = { | ||||
|                         if (username.text.isEmpty() || password.text.isEmpty()) { | ||||
|                             inputError = true | ||||
|                             return@Button | ||||
|                         } | ||||
|                         scope.launchIO { | ||||
|                             inputError = false | ||||
|                             processing = true | ||||
|                             val result = checkLogin( | ||||
|                                 context = context, | ||||
|                                 service = service, | ||||
|                                 username = username.text, | ||||
|                                 password = password.text, | ||||
|                             ) | ||||
|                             if (result) onDismissRequest() | ||||
|                             processing = false | ||||
|                         } | ||||
|                     }, | ||||
|                 ) { | ||||
|                     val id = if (processing) R.string.loading else R.string.login | ||||
|                     Text(text = stringResource(id)) | ||||
|                 } | ||||
|             }, | ||||
|         ) | ||||
|   | ||||
| @@ -109,7 +109,7 @@ private fun AppThemesList( | ||||
|                     color = MaterialTheme.colorScheme.onSurface, | ||||
|                     textAlign = TextAlign.Center, | ||||
|                     maxLines = 2, | ||||
|                     style = MaterialTheme.typography.bodySmall, | ||||
|                     style = MaterialTheme.typography.bodyMedium, | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -31,6 +31,7 @@ import androidx.compose.ui.graphics.Color | ||||
| import androidx.compose.ui.graphics.vector.ImageVector | ||||
| import androidx.compose.ui.text.style.TextOverflow | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.unit.sp | ||||
| import eu.kanade.presentation.more.settings.LocalPreferenceHighlighted | ||||
| import eu.kanade.presentation.util.secondaryItemAlpha | ||||
| import kotlinx.coroutines.delay | ||||
| @@ -54,12 +55,12 @@ internal fun BasePreferenceWidget( | ||||
|                     modifier = Modifier | ||||
|                         .padding( | ||||
|                             start = HorizontalPadding, | ||||
|                             top = 4.dp, | ||||
|                             top = 0.dp, | ||||
|                             end = HorizontalPadding, | ||||
|                         ) | ||||
|                         .secondaryItemAlpha(), | ||||
|                     color = MaterialTheme.colorScheme.onSurface, | ||||
|                     style = MaterialTheme.typography.bodySmall, | ||||
|                     style = MaterialTheme.typography.bodyMedium, | ||||
|                     maxLines = 10, | ||||
|                 ) | ||||
|             } | ||||
|         } else { | ||||
| @@ -106,15 +107,13 @@ private fun BasePreferenceWidgetImpl( | ||||
|                     imageVector = icon, | ||||
|                     contentDescription = null, | ||||
|                     modifier = Modifier | ||||
|                         .padding(start = HorizontalPadding, end = 12.dp) | ||||
|                         .secondaryItemAlpha(), | ||||
|                     tint = MaterialTheme.colorScheme.onSurface, | ||||
|                         .padding(start = HorizontalPadding, end = 0.dp), | ||||
|                 ) | ||||
|             } | ||||
|             Column( | ||||
|                 modifier = Modifier | ||||
|                     .weight(1f) | ||||
|                     .padding(vertical = 14.dp), | ||||
|                     .padding(vertical = 16.dp), | ||||
|             ) { | ||||
|                 if (title.isNotBlank()) { | ||||
|                     Row( | ||||
| @@ -125,7 +124,8 @@ private fun BasePreferenceWidgetImpl( | ||||
|                             text = title, | ||||
|                             overflow = TextOverflow.Ellipsis, | ||||
|                             maxLines = 2, | ||||
|                             style = MaterialTheme.typography.bodyLarge, | ||||
|                             style = MaterialTheme.typography.titleLarge, | ||||
|                             fontSize = 20.sp, | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
| @@ -173,4 +173,4 @@ internal fun Modifier.highlightBackground(highlighted: Boolean): Modifier = comp | ||||
| } | ||||
|  | ||||
| internal val TrailingWidgetBuffer = 16.dp | ||||
| internal val HorizontalPadding = 16.dp | ||||
| internal val HorizontalPadding = 24.dp | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.lazy.rememberLazyListState | ||||
| import androidx.compose.foundation.selection.selectable | ||||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||||
| import androidx.compose.material3.AlertDialog | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.RadioButton | ||||
| @@ -16,6 +17,7 @@ import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.draw.clip | ||||
| import androidx.compose.ui.graphics.vector.ImageVector | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.unit.dp | ||||
| @@ -23,6 +25,7 @@ import eu.kanade.presentation.components.Divider | ||||
| import eu.kanade.presentation.components.ScrollbarLazyColumn | ||||
| import eu.kanade.presentation.util.isScrolledToEnd | ||||
| import eu.kanade.presentation.util.isScrolledToStart | ||||
| import eu.kanade.presentation.util.minimumTouchTargetSize | ||||
|  | ||||
| @Composable | ||||
| fun <T> ListPreferenceWidget( | ||||
| @@ -86,20 +89,22 @@ private fun DialogRow( | ||||
|     Row( | ||||
|         verticalAlignment = Alignment.CenterVertically, | ||||
|         modifier = Modifier | ||||
|             .fillMaxWidth() | ||||
|             .clip(RoundedCornerShape(8.dp)) | ||||
|             .selectable( | ||||
|                 selected = isSelected, | ||||
|                 onClick = { if (!isSelected) onSelected() }, | ||||
|             ), | ||||
|             ) | ||||
|             .fillMaxWidth() | ||||
|             .minimumTouchTargetSize(), | ||||
|     ) { | ||||
|         RadioButton( | ||||
|             selected = isSelected, | ||||
|             onClick = { if (!isSelected) onSelected() }, | ||||
|             onClick = null, | ||||
|         ) | ||||
|         Text( | ||||
|             text = label, | ||||
|             style = MaterialTheme.typography.bodyLarge.merge(), | ||||
|             modifier = Modifier.padding(start = 12.dp), | ||||
|             modifier = Modifier.padding(start = 24.dp), | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,11 @@ | ||||
| package eu.kanade.presentation.more.settings.widget | ||||
|  | ||||
| import androidx.compose.foundation.clickable | ||||
| import androidx.compose.foundation.layout.Row | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.lazy.LazyColumn | ||||
| import androidx.compose.foundation.selection.selectable | ||||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||||
| import androidx.compose.material3.AlertDialog | ||||
| import androidx.compose.material3.Checkbox | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| @@ -16,10 +17,12 @@ import androidx.compose.runtime.remember | ||||
| import androidx.compose.runtime.toMutableStateList | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.draw.clip | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.window.DialogProperties | ||||
| import eu.kanade.presentation.more.settings.Preference | ||||
| import eu.kanade.presentation.util.minimumTouchTargetSize | ||||
|  | ||||
| @Composable | ||||
| fun MultiSelectListPreferenceWidget( | ||||
| @@ -59,17 +62,22 @@ fun MultiSelectListPreferenceWidget( | ||||
|                             Row( | ||||
|                                 verticalAlignment = Alignment.CenterVertically, | ||||
|                                 modifier = Modifier | ||||
|                                     .fillMaxWidth() | ||||
|                                     .clickable { onSelectionChanged() }, | ||||
|                                     .clip(RoundedCornerShape(8.dp)) | ||||
|                                     .selectable( | ||||
|                                         selected = isSelected, | ||||
|                                         onClick = { onSelectionChanged() }, | ||||
|                                     ) | ||||
|                                     .minimumTouchTargetSize() | ||||
|                                     .fillMaxWidth(), | ||||
|                             ) { | ||||
|                                 Checkbox( | ||||
|                                     checked = isSelected, | ||||
|                                     onCheckedChange = { onSelectionChanged() }, | ||||
|                                     onCheckedChange = null, | ||||
|                                 ) | ||||
|                                 Text( | ||||
|                                     text = current.value, | ||||
|                                     style = MaterialTheme.typography.bodyMedium, | ||||
|                                     modifier = Modifier.padding(start = 12.dp), | ||||
|                                     modifier = Modifier.padding(start = 24.dp), | ||||
|                                 ) | ||||
|                             } | ||||
|                         } | ||||
|   | ||||
| @@ -21,7 +21,7 @@ fun PreferenceGroupHeader(title: String) { | ||||
|         Text( | ||||
|             text = title, | ||||
|             color = MaterialTheme.colorScheme.secondary, | ||||
|             modifier = Modifier.padding(horizontal = 16.dp), | ||||
|             modifier = Modifier.padding(horizontal = 24.dp), | ||||
|             style = MaterialTheme.typography.bodyMedium, | ||||
|         ) | ||||
|     } | ||||
|   | ||||
| @@ -8,18 +8,20 @@ import androidx.compose.material3.Surface | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.setValue | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.graphics.vector.ImageVector | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
|  | ||||
| @Composable | ||||
| fun TextPreferenceWidget( | ||||
|     modifier: Modifier = Modifier, | ||||
|     title: String, | ||||
|     subtitle: String? = null, | ||||
|     icon: ImageVector? = null, | ||||
|     onPreferenceClick: (() -> Unit)? = null, | ||||
| ) { | ||||
|     // TODO: Handle auth requirement here? | ||||
|     BasePreferenceWidget( | ||||
|         modifier = modifier, | ||||
|         title = title, | ||||
|         subtitle = subtitle, | ||||
|         icon = icon, | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import androidx.compose.ui.res.painterResource | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.unit.sp | ||||
| import eu.kanade.presentation.more.settings.LocalPreferenceHighlighted | ||||
|  | ||||
| @Composable | ||||
| @@ -39,7 +40,7 @@ fun TrackingPreferenceWidget( | ||||
|             modifier = modifier | ||||
|                 .clickable(enabled = onClick != null, onClick = { onClick?.invoke() }) | ||||
|                 .fillMaxWidth() | ||||
|                 .padding(horizontal = 16.dp, vertical = 8.dp), | ||||
|                 .padding(horizontal = HorizontalPadding, vertical = 8.dp), | ||||
|             verticalAlignment = Alignment.CenterVertically, | ||||
|         ) { | ||||
|             Box( | ||||
| @@ -60,7 +61,8 @@ fun TrackingPreferenceWidget( | ||||
|                     .weight(1f) | ||||
|                     .padding(horizontal = 16.dp), | ||||
|                 maxLines = 1, | ||||
|                 style = MaterialTheme.typography.titleMedium, | ||||
|                 style = MaterialTheme.typography.titleLarge, | ||||
|                 fontSize = 20.sp, | ||||
|             ) | ||||
|             if (checked) { | ||||
|                 Icon( | ||||
|   | ||||
| @@ -79,7 +79,7 @@ fun <T> TriStateListDialog( | ||||
|                             val state = selected[index] | ||||
|                             Row( | ||||
|                                 modifier = Modifier | ||||
|                                     .clip(RoundedCornerShape(25)) | ||||
|                                     .clip(RoundedCornerShape(8.dp)) | ||||
|                                     .clickable { | ||||
|                                         selected[index] = when (state) { | ||||
|                                             State.UNCHECKED -> State.CHECKED | ||||
|   | ||||
| @@ -1,18 +1,23 @@ | ||||
| package eu.kanade.tachiyomi.ui.setting | ||||
|  | ||||
| import android.os.Bundle | ||||
| import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.CompositionLocalProvider | ||||
| import androidx.core.os.bundleOf | ||||
| import cafe.adriel.voyager.core.stack.StackEvent | ||||
| import cafe.adriel.voyager.navigator.Navigator | ||||
| import cafe.adriel.voyager.transitions.ScreenTransition | ||||
| import eu.kanade.presentation.components.TwoPanelBox | ||||
| import eu.kanade.presentation.more.settings.screen.SettingsBackupScreen | ||||
| import eu.kanade.presentation.more.settings.screen.SettingsGeneralScreen | ||||
| import eu.kanade.presentation.more.settings.screen.SettingsMainScreen | ||||
| import eu.kanade.presentation.util.LocalBackPress | ||||
| import eu.kanade.presentation.util.LocalRouter | ||||
| import eu.kanade.presentation.util.calculateWindowWidthSizeClass | ||||
| import eu.kanade.tachiyomi.ui.base.controller.BasicFullComposeController | ||||
| import soup.compose.material.motion.animation.materialSharedAxisZ | ||||
| import soup.compose.material.motion.animation.materialSharedAxisX | ||||
| import soup.compose.material.motion.animation.rememberSlideDistance | ||||
|  | ||||
| class SettingsMainController : BasicFullComposeController { | ||||
|  | ||||
| @@ -25,20 +30,52 @@ class SettingsMainController : BasicFullComposeController { | ||||
|  | ||||
|     @Composable | ||||
|     override fun ComposeContent() { | ||||
|         Navigator( | ||||
|             screen = if (toBackupScreen) SettingsBackupScreen() else SettingsMainScreen, | ||||
|             content = { | ||||
|                 CompositionLocalProvider( | ||||
|                     LocalRouter provides router, | ||||
|                     LocalBackPress provides this::back, | ||||
|         CompositionLocalProvider(LocalRouter provides router) { | ||||
|             val widthSizeClass = calculateWindowWidthSizeClass() | ||||
|             if (widthSizeClass == WindowWidthSizeClass.Compact) { | ||||
|                 Navigator( | ||||
|                     screen = if (toBackupScreen) SettingsBackupScreen() else SettingsMainScreen, | ||||
|                     content = { | ||||
|                         CompositionLocalProvider(LocalBackPress provides this::back) { | ||||
|                             val slideDistance = rememberSlideDistance() | ||||
|                             ScreenTransition( | ||||
|                                 navigator = it, | ||||
|                                 transition = { | ||||
|                                     materialSharedAxisX( | ||||
|                                         forward = it.lastEvent != StackEvent.Pop, | ||||
|                                         slideDistance = slideDistance, | ||||
|                                     ) | ||||
|                                 }, | ||||
|                             ) | ||||
|                         } | ||||
|                     }, | ||||
|                 ) | ||||
|             } else { | ||||
|                 Navigator( | ||||
|                     screen = if (toBackupScreen) SettingsBackupScreen() else SettingsGeneralScreen(), | ||||
|                 ) { | ||||
|                     ScreenTransition( | ||||
|                         navigator = it, | ||||
|                         transition = { materialSharedAxisZ(forward = it.lastEvent != StackEvent.Pop) }, | ||||
|                     TwoPanelBox( | ||||
|                         startContent = { | ||||
|                             CompositionLocalProvider(LocalBackPress provides this@SettingsMainController::back) { | ||||
|                                 SettingsMainScreen.Content(twoPane = true) | ||||
|                             } | ||||
|                         }, | ||||
|                         endContent = { | ||||
|                             val slideDistance = rememberSlideDistance() | ||||
|                             ScreenTransition( | ||||
|                                 navigator = it, | ||||
|                                 transition = { | ||||
|                                     materialSharedAxisX( | ||||
|                                         forward = it.lastEvent != StackEvent.Pop, | ||||
|                                         slideDistance = slideDistance, | ||||
|                                     ) | ||||
|                                 }, | ||||
|                             ) | ||||
|                         }, | ||||
|                     ) | ||||
|                 } | ||||
|             }, | ||||
|         ) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private fun back() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user