mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-21 20:47:03 -05:00
Add scrollbar indicator to LazyColumn (#7164)
This commit is contained in:
parent
aa2370b381
commit
3b2362c784
14 changed files with 337 additions and 20 deletions
|
@ -147,6 +147,7 @@ dependencies {
|
||||||
implementation(compose.material.icons)
|
implementation(compose.material.icons)
|
||||||
implementation(compose.animation)
|
implementation(compose.animation)
|
||||||
implementation(compose.ui.tooling)
|
implementation(compose.ui.tooling)
|
||||||
|
implementation(compose.ui.util)
|
||||||
implementation(compose.accompanist.webview)
|
implementation(compose.accompanist.webview)
|
||||||
implementation(compose.accompanist.swiperefresh)
|
implementation(compose.accompanist.swiperefresh)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Settings
|
import androidx.compose.material.icons.outlined.Settings
|
||||||
|
@ -52,6 +51,7 @@ import eu.kanade.presentation.components.DIVIDER_ALPHA
|
||||||
import eu.kanade.presentation.components.Divider
|
import eu.kanade.presentation.components.Divider
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.PreferenceRow
|
import eu.kanade.presentation.components.PreferenceRow
|
||||||
|
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||||
import eu.kanade.presentation.util.horizontalPadding
|
import eu.kanade.presentation.util.horizontalPadding
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
|
@ -80,7 +80,7 @@ fun ExtensionDetailsScreen(
|
||||||
|
|
||||||
var showNsfwWarning by remember { mutableStateOf(false) }
|
var showNsfwWarning by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
LazyColumn(
|
ScrollbarLazyColumn(
|
||||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||||
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Close
|
import androidx.compose.material.icons.filled.Close
|
||||||
|
@ -41,6 +40,7 @@ import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||||
import eu.kanade.presentation.browse.components.BaseBrowseItem
|
import eu.kanade.presentation.browse.components.BaseBrowseItem
|
||||||
import eu.kanade.presentation.browse.components.ExtensionIcon
|
import eu.kanade.presentation.browse.components.ExtensionIcon
|
||||||
|
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||||
import eu.kanade.presentation.components.SwipeRefreshIndicator
|
import eu.kanade.presentation.components.SwipeRefreshIndicator
|
||||||
import eu.kanade.presentation.theme.header
|
import eu.kanade.presentation.theme.header
|
||||||
import eu.kanade.presentation.util.horizontalPadding
|
import eu.kanade.presentation.util.horizontalPadding
|
||||||
|
@ -113,7 +113,7 @@ fun ExtensionContent(
|
||||||
) {
|
) {
|
||||||
var trustState by remember { mutableStateOf<Extension.Untrusted?>(null) }
|
var trustState by remember { mutableStateOf<Extension.Untrusted?>(null) }
|
||||||
|
|
||||||
LazyColumn(
|
ScrollbarLazyColumn(
|
||||||
contentPadding = WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
contentPadding = WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
||||||
) {
|
) {
|
||||||
items(
|
items(
|
||||||
|
|
|
@ -3,7 +3,6 @@ package eu.kanade.presentation.browse
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
@ -15,6 +14,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import eu.kanade.domain.manga.model.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
|
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||||
import eu.kanade.presentation.manga.components.BaseMangaListItem
|
import eu.kanade.presentation.manga.components.BaseMangaListItem
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaState
|
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaState
|
||||||
|
@ -54,7 +54,7 @@ fun MigrateMangaContent(
|
||||||
EmptyScreen(textResource = R.string.empty_screen)
|
EmptyScreen(textResource = R.string.empty_screen)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
LazyColumn(
|
ScrollbarLazyColumn(
|
||||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||||
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
@ -21,6 +20,7 @@ import eu.kanade.presentation.browse.components.BaseSourceItem
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.ItemBadges
|
import eu.kanade.presentation.components.ItemBadges
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
|
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||||
import eu.kanade.presentation.theme.header
|
import eu.kanade.presentation.theme.header
|
||||||
import eu.kanade.presentation.util.horizontalPadding
|
import eu.kanade.presentation.util.horizontalPadding
|
||||||
import eu.kanade.presentation.util.plus
|
import eu.kanade.presentation.util.plus
|
||||||
|
@ -62,7 +62,7 @@ fun MigrateSourceList(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyColumn(
|
ScrollbarLazyColumn(
|
||||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||||
contentPadding = WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
contentPadding = WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -3,7 +3,6 @@ package eu.kanade.presentation.browse
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
|
@ -20,6 +19,7 @@ import eu.kanade.presentation.browse.components.BaseSourceItem
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
import eu.kanade.presentation.components.PreferenceRow
|
import eu.kanade.presentation.components.PreferenceRow
|
||||||
|
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.FilterUiModel
|
import eu.kanade.tachiyomi.ui.browse.source.FilterUiModel
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.SourceFilterState
|
import eu.kanade.tachiyomi.ui.browse.source.SourceFilterState
|
||||||
|
@ -60,7 +60,7 @@ fun SourcesFilterContent(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyColumn(
|
ScrollbarLazyColumn(
|
||||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||||
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.PushPin
|
import androidx.compose.material.icons.filled.PushPin
|
||||||
|
@ -36,6 +35,7 @@ import eu.kanade.domain.source.model.Source
|
||||||
import eu.kanade.presentation.browse.components.BaseSourceItem
|
import eu.kanade.presentation.browse.components.BaseSourceItem
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
|
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||||
import eu.kanade.presentation.theme.header
|
import eu.kanade.presentation.theme.header
|
||||||
import eu.kanade.presentation.util.horizontalPadding
|
import eu.kanade.presentation.util.horizontalPadding
|
||||||
import eu.kanade.presentation.util.plus
|
import eu.kanade.presentation.util.plus
|
||||||
|
@ -87,7 +87,7 @@ fun SourceList(
|
||||||
|
|
||||||
var sourceState by remember { mutableStateOf<Source?>(null) }
|
var sourceState by remember { mutableStateOf<Source?>(null) }
|
||||||
|
|
||||||
LazyColumn(
|
ScrollbarLazyColumn(
|
||||||
modifier = Modifier.nestedScroll(nestedScrollConnection),
|
modifier = Modifier.nestedScroll(nestedScrollConnection),
|
||||||
contentPadding = WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
contentPadding = WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package eu.kanade.presentation.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.gestures.FlingBehavior
|
||||||
|
import androidx.compose.foundation.gestures.ScrollableDefaults
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.calculateEndPadding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import eu.kanade.presentation.util.drawVerticalScrollbar
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LazyColumn with scrollbar.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun ScrollbarLazyColumn(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
state: LazyListState = rememberLazyListState(),
|
||||||
|
contentPadding: PaddingValues = PaddingValues(0.dp),
|
||||||
|
reverseLayout: Boolean = false,
|
||||||
|
verticalArrangement: Arrangement.Vertical =
|
||||||
|
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
|
||||||
|
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
||||||
|
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
|
||||||
|
userScrollEnabled: Boolean = true,
|
||||||
|
content: LazyListScope.() -> Unit,
|
||||||
|
) {
|
||||||
|
val direction = LocalLayoutDirection.current
|
||||||
|
val density = LocalDensity.current
|
||||||
|
val positionOffset = remember(contentPadding) {
|
||||||
|
with(density) { contentPadding.calculateEndPadding(direction).toPx() }
|
||||||
|
}
|
||||||
|
LazyColumn(
|
||||||
|
modifier = modifier
|
||||||
|
.drawVerticalScrollbar(
|
||||||
|
state = state,
|
||||||
|
reverseScrolling = reverseLayout,
|
||||||
|
positionOffsetPx = positionOffset,
|
||||||
|
),
|
||||||
|
state = state,
|
||||||
|
contentPadding = contentPadding,
|
||||||
|
reverseLayout = reverseLayout,
|
||||||
|
verticalArrangement = verticalArrangement,
|
||||||
|
horizontalAlignment = horizontalAlignment,
|
||||||
|
flingBehavior = flingBehavior,
|
||||||
|
userScrollEnabled = userScrollEnabled,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
|
@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.selection.toggleable
|
import androidx.compose.foundation.selection.toggleable
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
@ -45,6 +44,7 @@ import eu.kanade.domain.history.model.HistoryWithRelations
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
import eu.kanade.presentation.components.MangaCover
|
import eu.kanade.presentation.components.MangaCover
|
||||||
|
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||||
import eu.kanade.presentation.util.horizontalPadding
|
import eu.kanade.presentation.util.horizontalPadding
|
||||||
import eu.kanade.presentation.util.plus
|
import eu.kanade.presentation.util.plus
|
||||||
import eu.kanade.presentation.util.topPaddingValues
|
import eu.kanade.presentation.util.topPaddingValues
|
||||||
|
@ -107,7 +107,7 @@ fun HistoryContent(
|
||||||
var removeState by remember { mutableStateOf<HistoryWithRelations?>(null) }
|
var removeState by remember { mutableStateOf<HistoryWithRelations?>(null) }
|
||||||
|
|
||||||
val scrollState = rememberLazyListState()
|
val scrollState = rememberLazyListState()
|
||||||
LazyColumn(
|
ScrollbarLazyColumn(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.nestedScroll(nestedScroll),
|
.nestedScroll(nestedScroll),
|
||||||
contentPadding = WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
contentPadding = WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
||||||
|
|
|
@ -3,7 +3,6 @@ package eu.kanade.presentation.more
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.CloudOff
|
import androidx.compose.material.icons.outlined.CloudOff
|
||||||
import androidx.compose.material.icons.outlined.GetApp
|
import androidx.compose.material.icons.outlined.GetApp
|
||||||
|
@ -24,6 +23,7 @@ import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import eu.kanade.presentation.components.Divider
|
import eu.kanade.presentation.components.Divider
|
||||||
import eu.kanade.presentation.components.PreferenceRow
|
import eu.kanade.presentation.components.PreferenceRow
|
||||||
|
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||||
import eu.kanade.presentation.components.SwitchPreference
|
import eu.kanade.presentation.components.SwitchPreference
|
||||||
import eu.kanade.presentation.util.quantityStringResource
|
import eu.kanade.presentation.util.quantityStringResource
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
@ -44,7 +44,7 @@ fun MoreScreen(
|
||||||
val uriHandler = LocalUriHandler.current
|
val uriHandler = LocalUriHandler.current
|
||||||
val downloadQueueState by presenter.downloadQueueState.collectAsState()
|
val downloadQueueState by presenter.downloadQueueState.collectAsState()
|
||||||
|
|
||||||
LazyColumn(
|
ScrollbarLazyColumn(
|
||||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||||
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Public
|
import androidx.compose.material.icons.outlined.Public
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
@ -20,6 +19,7 @@ import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import eu.kanade.presentation.components.LinkIcon
|
import eu.kanade.presentation.components.LinkIcon
|
||||||
import eu.kanade.presentation.components.PreferenceRow
|
import eu.kanade.presentation.components.PreferenceRow
|
||||||
|
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||||
import eu.kanade.presentation.more.LogoHeader
|
import eu.kanade.presentation.more.LogoHeader
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
@ -37,7 +37,7 @@ fun AboutScreen(
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val uriHandler = LocalUriHandler.current
|
val uriHandler = LocalUriHandler.current
|
||||||
|
|
||||||
LazyColumn(
|
ScrollbarLazyColumn(
|
||||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||||
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import androidx.annotation.StringRes
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
|
@ -12,13 +11,14 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import eu.kanade.presentation.components.PreferenceRow
|
import eu.kanade.presentation.components.PreferenceRow
|
||||||
|
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsMainScreen(
|
fun SettingsMainScreen(
|
||||||
nestedScrollInterop: NestedScrollConnection,
|
nestedScrollInterop: NestedScrollConnection,
|
||||||
sections: List<SettingsSection>,
|
sections: List<SettingsSection>,
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
ScrollbarLazyColumn(
|
||||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||||
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
||||||
) {
|
) {
|
||||||
|
|
257
app/src/main/java/eu/kanade/presentation/util/Scrollbar.kt
Normal file
257
app/src/main/java/eu/kanade/presentation/util/Scrollbar.kt
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
package eu.kanade.presentation.util
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 Albert Chang
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code taken from https://gist.github.com/mxalbert1996/33a360fcab2105a31e5355af98216f5a
|
||||||
|
* with some modifications to handle contentPadding.
|
||||||
|
*
|
||||||
|
* Modifiers for regular scrollable list is omitted.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.view.ViewConfiguration
|
||||||
|
import androidx.compose.animation.core.Animatable
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.foundation.gestures.Orientation
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.composed
|
||||||
|
import androidx.compose.ui.draw.CacheDrawScope
|
||||||
|
import androidx.compose.ui.draw.DrawResult
|
||||||
|
import androidx.compose.ui.draw.drawWithCache
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||||
|
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||||
|
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.util.fastSumBy
|
||||||
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
|
||||||
|
fun Modifier.drawHorizontalScrollbar(
|
||||||
|
state: LazyListState,
|
||||||
|
reverseScrolling: Boolean = false,
|
||||||
|
// The amount of offset the scrollbar position towards the top of the layout
|
||||||
|
positionOffsetPx: Float = 0f,
|
||||||
|
): Modifier = drawScrollbar(state, Orientation.Horizontal, reverseScrolling, positionOffsetPx)
|
||||||
|
|
||||||
|
fun Modifier.drawVerticalScrollbar(
|
||||||
|
state: LazyListState,
|
||||||
|
reverseScrolling: Boolean = false,
|
||||||
|
// The amount of offset the scrollbar position towards the start of the layout
|
||||||
|
positionOffsetPx: Float = 0f,
|
||||||
|
): Modifier = drawScrollbar(state, Orientation.Vertical, reverseScrolling, positionOffsetPx)
|
||||||
|
|
||||||
|
private fun Modifier.drawScrollbar(
|
||||||
|
state: LazyListState,
|
||||||
|
orientation: Orientation,
|
||||||
|
reverseScrolling: Boolean,
|
||||||
|
positionOffset: Float,
|
||||||
|
): Modifier = drawScrollbar(
|
||||||
|
orientation, reverseScrolling,
|
||||||
|
) { reverseDirection, atEnd, thickness, color, alpha ->
|
||||||
|
val layoutInfo = state.layoutInfo
|
||||||
|
val viewportSize = if (orientation == Orientation.Horizontal) {
|
||||||
|
layoutInfo.viewportSize.width
|
||||||
|
} else {
|
||||||
|
layoutInfo.viewportSize.height
|
||||||
|
} - layoutInfo.beforeContentPadding - layoutInfo.afterContentPadding
|
||||||
|
val items = layoutInfo.visibleItemsInfo
|
||||||
|
val itemsSize = items.fastSumBy { it.size }
|
||||||
|
val showScrollbar = items.size < layoutInfo.totalItemsCount || itemsSize > viewportSize
|
||||||
|
val estimatedItemSize = if (items.isEmpty()) 0f else itemsSize.toFloat() / items.size
|
||||||
|
val totalSize = estimatedItemSize * layoutInfo.totalItemsCount
|
||||||
|
val thumbSize = viewportSize / totalSize * viewportSize
|
||||||
|
val startOffset = if (items.isEmpty()) 0f else items
|
||||||
|
.first()
|
||||||
|
.run {
|
||||||
|
val startPadding = if (reverseDirection) layoutInfo.afterContentPadding else layoutInfo.beforeContentPadding
|
||||||
|
startPadding + ((estimatedItemSize * index - offset) / totalSize * viewportSize)
|
||||||
|
}
|
||||||
|
val drawScrollbar = onDrawScrollbar(
|
||||||
|
orientation, reverseDirection, atEnd, showScrollbar,
|
||||||
|
thickness, color, alpha, thumbSize, startOffset, positionOffset,
|
||||||
|
)
|
||||||
|
onDrawWithContent {
|
||||||
|
drawContent()
|
||||||
|
drawScrollbar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CacheDrawScope.onDrawScrollbar(
|
||||||
|
orientation: Orientation,
|
||||||
|
reverseDirection: Boolean,
|
||||||
|
atEnd: Boolean,
|
||||||
|
showScrollbar: Boolean,
|
||||||
|
thickness: Float,
|
||||||
|
color: Color,
|
||||||
|
alpha: () -> Float,
|
||||||
|
thumbSize: Float,
|
||||||
|
scrollOffset: Float,
|
||||||
|
positionOffset: Float,
|
||||||
|
): DrawScope.() -> Unit {
|
||||||
|
val topLeft = if (orientation == Orientation.Horizontal) {
|
||||||
|
Offset(
|
||||||
|
if (reverseDirection) size.width - scrollOffset - thumbSize else scrollOffset,
|
||||||
|
if (atEnd) size.height - positionOffset - thickness else positionOffset,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Offset(
|
||||||
|
if (atEnd) size.width - positionOffset - thickness else positionOffset,
|
||||||
|
if (reverseDirection) size.height - scrollOffset - thumbSize else scrollOffset,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val size = if (orientation == Orientation.Horizontal) {
|
||||||
|
Size(thumbSize, thickness)
|
||||||
|
} else {
|
||||||
|
Size(thickness, thumbSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
if (showScrollbar) {
|
||||||
|
drawRect(
|
||||||
|
color = color,
|
||||||
|
topLeft = topLeft,
|
||||||
|
size = size,
|
||||||
|
alpha = alpha(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Modifier.drawScrollbar(
|
||||||
|
orientation: Orientation,
|
||||||
|
reverseScrolling: Boolean,
|
||||||
|
onBuildDrawCache: CacheDrawScope.(
|
||||||
|
reverseDirection: Boolean,
|
||||||
|
atEnd: Boolean,
|
||||||
|
thickness: Float,
|
||||||
|
color: Color,
|
||||||
|
alpha: () -> Float,
|
||||||
|
) -> DrawResult,
|
||||||
|
): Modifier = composed {
|
||||||
|
val scrolled = remember {
|
||||||
|
MutableSharedFlow<Unit>(
|
||||||
|
extraBufferCapacity = 1,
|
||||||
|
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val nestedScrollConnection = remember(orientation, scrolled) {
|
||||||
|
object : NestedScrollConnection {
|
||||||
|
override fun onPostScroll(
|
||||||
|
consumed: Offset,
|
||||||
|
available: Offset,
|
||||||
|
source: NestedScrollSource,
|
||||||
|
): Offset {
|
||||||
|
val delta = if (orientation == Orientation.Horizontal) consumed.x else consumed.y
|
||||||
|
if (delta != 0f) scrolled.tryEmit(Unit)
|
||||||
|
return Offset.Zero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val alpha = remember { Animatable(0f) }
|
||||||
|
LaunchedEffect(scrolled, alpha) {
|
||||||
|
scrolled.collectLatest {
|
||||||
|
alpha.snapTo(1f)
|
||||||
|
alpha.animateTo(0f, animationSpec = FadeOutAnimationSpec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr
|
||||||
|
val reverseDirection = if (orientation == Orientation.Horizontal) {
|
||||||
|
if (isLtr) reverseScrolling else !reverseScrolling
|
||||||
|
} else reverseScrolling
|
||||||
|
val atEnd = if (orientation == Orientation.Vertical) isLtr else true
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
|
val thickness = remember { ViewConfiguration.get(context).scaledScrollBarSize.toFloat() }
|
||||||
|
val color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.364f)
|
||||||
|
Modifier
|
||||||
|
.nestedScroll(nestedScrollConnection)
|
||||||
|
.drawWithCache {
|
||||||
|
onBuildDrawCache(reverseDirection, atEnd, thickness, color, alpha::value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val FadeOutAnimationSpec = tween<Float>(
|
||||||
|
durationMillis = ViewConfiguration.getScrollBarFadeDuration(),
|
||||||
|
delayMillis = ViewConfiguration.getScrollDefaultDelay(),
|
||||||
|
)
|
||||||
|
|
||||||
|
@Preview(widthDp = 400, heightDp = 400, showBackground = true)
|
||||||
|
@Composable
|
||||||
|
fun LazyListScrollbarPreview() {
|
||||||
|
val state = rememberLazyListState()
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.drawVerticalScrollbar(state),
|
||||||
|
state = state,
|
||||||
|
) {
|
||||||
|
items(50) {
|
||||||
|
Text(
|
||||||
|
text = "Item ${it + 1}",
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(widthDp = 400, showBackground = true)
|
||||||
|
@Composable
|
||||||
|
fun LazyListHorizontalScrollbarPreview() {
|
||||||
|
val state = rememberLazyListState()
|
||||||
|
LazyRow(
|
||||||
|
modifier = Modifier.drawHorizontalScrollbar(state),
|
||||||
|
state = state,
|
||||||
|
) {
|
||||||
|
items(50) {
|
||||||
|
Text(
|
||||||
|
text = (it + 1).toString(),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 8.dp, vertical = 16.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ activity = "androidx.activity:activity-compose:1.6.0-alpha03"
|
||||||
foundation = { module = "androidx.compose.foundation:foundation", version.ref="compose" }
|
foundation = { module = "androidx.compose.foundation:foundation", version.ref="compose" }
|
||||||
animation = { module = "androidx.compose.animation:animation", version.ref="compose" }
|
animation = { module = "androidx.compose.animation:animation", version.ref="compose" }
|
||||||
ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref="compose" }
|
ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref="compose" }
|
||||||
|
ui-util = { module = "androidx.compose.ui:ui-util", version.ref="compose" }
|
||||||
|
|
||||||
material3-core = "androidx.compose.material3:material3:1.0.0-alpha12"
|
material3-core = "androidx.compose.material3:material3:1.0.0-alpha12"
|
||||||
material3-adapter = "com.google.android.material:compose-theme-adapter-3:1.0.10"
|
material3-adapter = "com.google.android.material:compose-theme-adapter-3:1.0.10"
|
||||||
|
|
Loading…
Reference in a new issue