Update to Mpp Compose 1.2.0 dev builds(with Android 1.2.0 release)

This commit is contained in:
Syer10
2022-07-29 09:56:42 -04:00
parent 3e9136250f
commit b255023d07
18 changed files with 281 additions and 120 deletions

View File

@@ -53,7 +53,7 @@ class ReaderActivity : AppCompatActivity() {
chapterIndex = chapterIndex,
mangaId = mangaId,
hotkeyFlow = hotkeyFlow,
onCloseRequest = ::onBackPressed
onCloseRequest = onBackPressedDispatcher::onBackPressed
)
}
}

View File

@@ -48,9 +48,9 @@ tasks.withType<com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
subprojects {
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
kotlinOptions {
freeCompilerArgs = freeCompilerArgs + listOf(
/*freeCompilerArgs = freeCompilerArgs + listOf(
"-Xjvm-default=compatibility",
)
)*/
}
}
tasks.withType<org.jmailen.gradle.kotlinter.tasks.LintTask> {
@@ -64,7 +64,7 @@ subprojects {
plugins.withType<com.android.build.gradle.BasePlugin> {
configure<com.android.build.gradle.BaseExtension> {
// 32 requires a higher android plugin version
compileSdkVersion(31)
compileSdkVersion(33)
defaultConfig {
minSdk = 21
targetSdk = 31

View File

@@ -96,7 +96,7 @@ internal class ObjectAdapter<T>(
) : AndroidPreference.Adapter<T> {
override fun get(key: String, preferences: ObservableSettings): T {
return deserializer(preferences.getString(key)) // Not called unless key is present.
return deserializer(preferences.getString(key, "")) // Not called unless key is present.
}
override fun set(key: String, value: T, editor: ObservableSettings) {

View File

@@ -17,7 +17,7 @@ import kotlinx.serialization.modules.SerializersModule
internal object StringAdapter : JvmPreference.Adapter<String> {
override fun get(key: String, preferences: ObservableSettings): String {
return preferences.getString(key) // Not called unless key is present.
return preferences.getString(key, "") // Not called unless key is present.
}
override fun set(key: String, value: String, editor: ObservableSettings) {
@@ -96,7 +96,7 @@ internal class ObjectAdapter<T>(
) : JvmPreference.Adapter<T> {
override fun get(key: String, preferences: ObservableSettings): T {
return deserializer(preferences.getString(key)) // Not called unless key is present.
return deserializer(preferences.getString(key, "")) // Not called unless key is present.
}
override fun set(key: String, value: T, editor: ObservableSettings) {

View File

@@ -13,7 +13,6 @@ import org.apache.logging.log4j.Level
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.core.LoggerContext
import org.apache.logging.log4j.core.appender.ConsoleAppender
import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory
import org.lighthousegames.logging.KmLogging
import org.lighthousegames.logging.logging
@@ -71,14 +70,14 @@ fun initializeLogger(loggingLocation: Path) {
.addAttribute("pattern", filePattern)
)
.addComponent(
newComponent<ComponentBuilder<*>>("Policies")
newComponent<_>("Policies")
.addComponent(
newComponent<ComponentBuilder<*>>("CronTriggeringPolicy")
newComponent<_>("CronTriggeringPolicy")
.addAttribute("schedule", "0 0 0 * * ?")
.addAttribute("evaluateOnStartup", "true")
)
.addComponent(
newComponent<ComponentBuilder<*>>("SizeBasedTriggeringPolicy")
newComponent<_>("SizeBasedTriggeringPolicy")
.addAttribute("size", "100M")
)
)

View File

@@ -1,34 +1,34 @@
[versions]
# Kotlin
kotlin = "1.6.10"
coroutines = "1.6.3"
kotlin = "1.7.0"
coroutines = "1.6.4"
# Serialization
json = "1.3.3"
# Compose
composeGradle = "1.1.1"
composeAndroid = "1.1.1"
composeGradle = "1.2.0-alpha01-dev753"
composeAndroid = "1.2.0"
voyager = "1.0.0-beta16"
accompanist = "0.24.4"
kamel = "0.3.0"
kamel = "0.4.0"
materialDialogs = "0.7.1"
# Android
androidGradle = "7.0.4"
androidGradle = "7.3.0-beta05"
core = "1.8.0"
appCompat = "1.6.0-alpha04" # alpha5+ requires android 32
appCompat = "1.6.0-alpha05"
activityCompose = "1.5.0"
work = "2.7.1"
# Android Lifecycle
lifecycle = "2.5.0" # 2.6.0 alpha+ requires android 32
lifecycle = "2.6.0-alpha01"
# Swing
darklaf = "3.0.0"
# Dependency Injection
ksp = "1.6.10-1.0.4"
ksp = "1.7.0-1.0.6"
kotlinInject = "0.4.1"
# Network
@@ -41,27 +41,27 @@ log4j = "2.18.0"
kmlogging = "1.2.0"
# Storage
okio = "3.0.0" # 3.1.0+ requires Kotlin 1.6.21+
okio = "3.2.0"
appDirs = "1.2.1"
# Preferences
multiplatformSettings = "0.8.1" # 0.9+ requires Kotlin 1.6.21+
multiplatformSettings = "1.0.0-alpha01"
# Utility
kroki = "1.22"
desugarJdkLibs = "1.1.6" # 1.2.0+ requires Android plugin 7.3 alpha
aboutLibraries = "10.3.1"
dateTime = "0.3.2"
dateTime = "0.4.0"
# Localization
moko = "0.20.1"
# BuildConfigs
buildconfig = "3.0.3"
buildkonfig = "0.11.0" # 0.11.0+ requires Kotlin 1.7.0+
buildkonfig = "0.12.0"
# Linter
kotlinter = "3.9.0" # 3.10.0+ requires Kotlin 1.7.0+
kotlinter = "3.11.1"
# Version updates
versions = "0.42.0"

View File

@@ -49,4 +49,9 @@ android {
lint {
disable += "MissingTranslation"
}
sourceSets.getByName("main") {
assets.srcDir(File(buildDir, "generated/moko/androidMain/assets"))
res.srcDir(File(buildDir, "generated/moko/androidMain/res"))
}
}

View File

@@ -26,9 +26,9 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.GridCells
import androidx.compose.foundation.lazy.LazyVerticalGrid
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
@@ -139,7 +139,7 @@ private fun ColorPresets(
val borderColor = MaterialTheme.colors.onBackground.copy(alpha = 0.54f)
Column {
LazyVerticalGrid(cells = GridCells.Fixed(5)) {
LazyVerticalGrid(columns = GridCells.Fixed(5)) {
items(presets) { color ->
ColorPresetItem(
color = color,
@@ -158,7 +158,7 @@ private fun ColorPresets(
.background(MaterialTheme.colors.onBackground.copy(alpha = 0.2f))
)
LazyVerticalGrid(cells = GridCells.Fixed(5)) {
LazyVerticalGrid(columns = GridCells.Fixed(5)) {
items(shades) { color ->
ColorPresetItem(
color = color,

View File

@@ -13,10 +13,10 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.GridCells
import androidx.compose.foundation.lazy.LazyVerticalGrid
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
@@ -51,14 +51,14 @@ fun LibraryMangaComfortableGrid(
showLocal: Boolean
) {
Box {
val state = rememberLazyListState()
val state = rememberLazyGridState()
val cells = if (gridColumns < 1) {
GridCells.Adaptive(gridSize.dp)
} else {
GridCells.Fixed(gridColumns)
}
LazyVerticalGrid(
cells = cells,
columns = cells,
state = state,
modifier = Modifier.fillMaxSize().padding(4.dp)
) {
@@ -77,7 +77,7 @@ fun LibraryMangaComfortableGrid(
}
}
VerticalScrollbar(
rememberScrollbarAdapter(state),
rememberScrollbarAdapter(state, cells),
Modifier.align(Alignment.CenterEnd)
.fillMaxHeight()
.scrollbarPadding()

View File

@@ -12,10 +12,10 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.GridCells
import androidx.compose.foundation.lazy.LazyVerticalGrid
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
@@ -59,14 +59,14 @@ fun LibraryMangaCompactGrid(
showLocal: Boolean
) {
Box {
val state = rememberLazyListState()
val state = rememberLazyGridState()
val cells = if (gridColumns < 1) {
GridCells.Adaptive(gridSize.dp)
} else {
GridCells.Fixed(gridColumns)
}
LazyVerticalGrid(
cells = cells,
columns = cells,
state = state,
modifier = Modifier.fillMaxSize().padding(4.dp)
) {
@@ -85,7 +85,7 @@ fun LibraryMangaCompactGrid(
}
}
VerticalScrollbar(
rememberScrollbarAdapter(state),
rememberScrollbarAdapter(state, cells),
Modifier.align(Alignment.CenterEnd)
.fillMaxHeight()
.scrollbarPadding()

View File

@@ -12,10 +12,10 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.GridCells
import androidx.compose.foundation.lazy.LazyVerticalGrid
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -45,14 +45,14 @@ fun LibraryMangaCoverOnlyGrid(
showLocal: Boolean
) {
Box {
val state = rememberLazyListState()
val state = rememberLazyGridState()
val cells = if (gridColumns < 1) {
GridCells.Adaptive(gridSize.dp)
} else {
GridCells.Fixed(gridColumns)
}
LazyVerticalGrid(
cells = cells,
columns = cells,
state = state,
modifier = Modifier.fillMaxSize().padding(4.dp)
) {
@@ -71,7 +71,7 @@ fun LibraryMangaCoverOnlyGrid(
}
}
VerticalScrollbar(
rememberScrollbarAdapter(state),
rememberScrollbarAdapter(state, cells),
Modifier.align(Alignment.CenterEnd)
.fillMaxHeight()
.scrollbarPadding()

View File

@@ -14,10 +14,10 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.GridCells
import androidx.compose.foundation.lazy.LazyVerticalGrid
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
@@ -50,14 +50,14 @@ fun SourceMangaComfortableGrid(
onLoadNextPage: () -> Unit,
) {
Box {
val state = rememberLazyListState()
val state = rememberLazyGridState()
val cells = if (gridColumns < 1) {
GridCells.Adaptive(gridSize.dp)
} else {
GridCells.Fixed(gridColumns)
}
LazyVerticalGrid(
cells = cells,
columns = cells,
state = state,
modifier = Modifier.fillMaxSize().padding(4.dp)
) {
@@ -75,7 +75,7 @@ fun SourceMangaComfortableGrid(
}
}
VerticalScrollbar(
rememberScrollbarAdapter(state),
rememberScrollbarAdapter(state, cells),
Modifier.align(Alignment.CenterEnd)
.fillMaxHeight()
.scrollbarPadding()

View File

@@ -13,10 +13,10 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.GridCells
import androidx.compose.foundation.lazy.LazyVerticalGrid
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
@@ -53,14 +53,14 @@ fun SourceMangaCompactGrid(
onLoadNextPage: () -> Unit,
) {
Box {
val state = rememberLazyListState()
val state = rememberLazyGridState()
val cells = if (gridColumns < 1) {
GridCells.Adaptive(gridSize.dp)
} else {
GridCells.Fixed(gridColumns)
}
LazyVerticalGrid(
cells = cells,
columns = cells,
state = state,
modifier = Modifier.fillMaxSize().padding(4.dp)
) {
@@ -78,7 +78,7 @@ fun SourceMangaCompactGrid(
}
}
VerticalScrollbar(
rememberScrollbarAdapter(state),
rememberScrollbarAdapter(state, cells),
Modifier.align(Alignment.CenterEnd)
.fillMaxHeight()
.scrollbarPadding()

View File

@@ -7,7 +7,6 @@
package ca.gosyer.jui.ui.sources.home.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -20,10 +19,11 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.GridCells
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -86,27 +86,41 @@ fun SourceHomeScreenContent(
if (sources.isEmpty()) {
LoadingScreen(isLoading)
} else {
Box(Modifier.fillMaxSize().padding(it), Alignment.TopCenter) {
val state = rememberLazyListState()
SourceCategory(sources, onAddSource, state)
/*val sourcesByLang = sources.groupBy { it.lang.toLowerCase() }.toList()
LazyColumn(state = state) {
items(sourcesByLang) { (lang, sources) ->
SourceCategory(
lang,
sources,
onSourceClicked = sourceClicked
)
Spacer(Modifier.height(8.dp))
BoxWithConstraints(Modifier.fillMaxSize().padding(it), Alignment.TopCenter) {
if (maxWidth > 720.dp) {
val state = rememberLazyGridState()
val cells = GridCells.Adaptive(120.dp)
LazyVerticalGrid(cells, state = state) {
items(sources) { source ->
WideSourceItem(
source,
onSourceClicked = onAddSource
)
}
}
}*/
VerticalScrollbar(
modifier = Modifier.align(Alignment.CenterEnd)
.fillMaxHeight()
.scrollbarPadding(),
adapter = rememberScrollbarAdapter(state)
)
VerticalScrollbar(
modifier = Modifier.align(Alignment.CenterEnd)
.fillMaxHeight()
.scrollbarPadding(),
adapter = rememberScrollbarAdapter(state, cells)
)
} else {
val state = rememberLazyListState()
LazyColumn(state = state) {
items(sources) { source ->
ThinSourceItem(
source,
onSourceClicked = onAddSource
)
}
}
VerticalScrollbar(
modifier = Modifier.align(Alignment.CenterEnd)
.fillMaxHeight()
.scrollbarPadding(),
adapter = rememberScrollbarAdapter(state)
)
}
}
}
}
@@ -137,35 +151,6 @@ fun SourceHomeScreenToolbar(
)
}
@Composable
fun SourceCategory(
sources: List<Source>,
onSourceClicked: (Source) -> Unit,
state: LazyListState
) {
BoxWithConstraints {
if (maxWidth > 720.dp) {
LazyVerticalGrid(GridCells.Adaptive(120.dp), state = state) {
items(sources) { source ->
WideSourceItem(
source,
onSourceClicked = onSourceClicked
)
}
}
} else {
LazyColumn(state = state) {
items(sources) { source ->
ThinSourceItem(
source,
onSourceClicked = onSourceClicked
)
}
}
}
}
}
@Composable
fun WideSourceItem(
source: Source,

View File

@@ -15,6 +15,8 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
@@ -38,6 +40,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastMaxBy
import androidx.compose.ui.util.fastSumBy
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
@@ -48,6 +51,8 @@ class ScrollStateScrollbarAdapter(val scrollState: ScrollState) : ScrollbarAdapt
class LazyListStateScrollbarAdapter(val lazyListState: LazyListState) : ScrollbarAdapter
class LazyGridStateScrollbarAdapter(val lazyGridState: LazyGridState, val gridCells: GridCells) : ScrollbarAdapter
@Immutable
actual class ScrollbarStyle
@@ -70,6 +75,9 @@ internal actual fun RealVerticalScrollbar(
is LazyListStateScrollbarAdapter -> {
Modifier.drawScrollbar(adapter.lazyListState, Orientation.Vertical, reverseLayout)
}
is LazyGridStateScrollbarAdapter -> {
Modifier.drawScrollbar(adapter.lazyGridState, adapter.gridCells, Orientation.Vertical, reverseLayout)
}
else -> Modifier
}
Box(modifier then Modifier.fillMaxSize() then scrollbarModifier)
@@ -113,6 +121,16 @@ actual fun rememberScrollbarAdapter(
}
}
@Composable
actual fun rememberScrollbarAdapter(
scrollState: LazyGridState,
gridCells: GridCells,
): ScrollbarAdapter {
return remember(scrollState, gridCells) {
LazyGridStateScrollbarAdapter(scrollState, gridCells)
}
}
actual fun Modifier.scrollbarPadding() = this
// Based on https://gist.github.com/mxalbert1996/33a360fcab2105a31e5355af98216f5a
@@ -186,6 +204,50 @@ private fun Modifier.drawScrollbar(
}
}
private fun Modifier.drawScrollbar(
state: LazyGridState,
gridCells: GridCells,
orientation: Orientation,
reverseScrolling: Boolean
): Modifier = drawScrollbar(
orientation, reverseScrolling, snapshotFlow { state.isScrollInProgress }
) { reverseDirection, atEnd, thickness, color, alpha ->
val layoutInfo = state.layoutInfo
val viewportSize = layoutInfo.viewportEndOffset - layoutInfo.viewportStartOffset
val items = layoutInfo.visibleItemsInfo
// TODO Fix spacing
val itemsSize = items.chunked(
with(gridCells) {
calculateCrossAxisCellSizes(viewportSize, 0).size
}
).sumOf { it.fastMaxBy { it.size.height }?.size?.height ?: 0 }
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 canvasSize = if (orientation == Orientation.Horizontal) size.width else size.height
val thumbSize = viewportSize / totalSize * canvasSize
val startOffset = if (items.isEmpty()) 0f else items
.first()
.run {
(estimatedItemSize * index - if (orientation == Orientation.Vertical) offset.y else offset.x) / totalSize * canvasSize
}
val drawScrollbar = onDrawScrollbar(
orientation = orientation,
reverseDirection = reverseDirection,
atEnd = atEnd,
showScrollbar = showScrollbar,
thickness = thickness,
color = color,
alpha = alpha,
thumbSize = thumbSize,
startOffset = startOffset
)
onDrawWithContent {
drawContent()
drawScrollbar()
}
}
private fun CacheDrawScope.onDrawScrollbar(
orientation: Orientation,
reverseDirection: Boolean,

View File

@@ -50,8 +50,7 @@ data class PopupProperties @ExperimentalComposeUiApi constructor(
val securePolicy: SecureFlagPolicy = SecureFlagPolicy.Inherit,
val excludeFromSystemGesture: Boolean = true,
val clippingEnabled: Boolean = true,
@Suppress("EXPERIMENTAL_ANNOTATION_ON_WRONG_TARGET")
@get:ExperimentalComposeUiApi
@property:ExperimentalComposeUiApi
val usePlatformDefaultWidth: Boolean = false
)

View File

@@ -9,6 +9,8 @@ package ca.gosyer.jui.uicore.components
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.remember
@@ -66,4 +68,10 @@ expect fun rememberScrollbarAdapter(
scrollState: LazyListState,
): ScrollbarAdapter
@Composable
expect fun rememberScrollbarAdapter(
scrollState: LazyGridState,
gridCells: GridCells,
): ScrollbarAdapter
expect fun Modifier.scrollbarPadding(): Modifier

View File

@@ -7,13 +7,22 @@
package ca.gosyer.jui.uicore.components
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import kotlin.math.abs
actual typealias ScrollbarAdapter = androidx.compose.foundation.ScrollbarAdapter
@@ -58,4 +67,98 @@ actual fun rememberScrollbarAdapter(
return androidx.compose.foundation.rememberScrollbarAdapter(scrollState)
}
@Composable
actual fun rememberScrollbarAdapter(
scrollState: LazyGridState,
gridCells: GridCells,
): ScrollbarAdapter {
val density = LocalDensity.current
return remember(scrollState, gridCells, density) { GridScrollbarAdapter(scrollState, gridCells, density) }
}
// TODO deal with item spacing
class GridScrollbarAdapter(
private val scrollState: LazyGridState,
private val gridCells: GridCells,
private val density: Density,
) : ScrollbarAdapter {
override val scrollOffset: Float
get() = (scrollState.firstVisibleItemIndex / itemsPerRow).coerceAtLeast(0) * averageItemSize + scrollState.firstVisibleItemScrollOffset
override fun maxScrollOffset(containerSize: Int): Float {
val size = with(gridCells) {
density.calculateCrossAxisCellSizes(containerSize, 0).size
}
return (averageItemSize * (itemCount / size) - containerSize).coerceAtLeast(0f)
}
override suspend fun scrollTo(containerSize: Int, scrollOffset: Float) {
val distance = scrollOffset - this@GridScrollbarAdapter.scrollOffset
// if we scroll less than containerSize we need to use scrollBy function to avoid
// undesirable scroll jumps (when an item size is different)
//
// if we scroll more than containerSize we should immediately jump to this position
// without recreating all items between the current and the new position
if (abs(distance) <= containerSize) {
scrollState.scrollBy(distance)
} else {
snapTo(containerSize, scrollOffset)
}
}
private suspend fun snapTo(containerSize: Int, scrollOffset: Float) {
// In case of very big values, we can catch an overflow, so convert values to double and
// coerce them
// val averageItemSize = 26.000002f
// val scrollOffsetCoerced = 2.54490608E8.toFloat()
// val index = (scrollOffsetCoerced / averageItemSize).toInt() // 9788100
// val offset = (scrollOffsetCoerced - index * averageItemSize) // -16.0
// println(offset)
val maximumValue = maxScrollOffset(containerSize).toDouble()
val scrollOffsetCoerced = scrollOffset.toDouble().coerceIn(0.0, maximumValue)
val averageItemSize = averageItemSize.toDouble()
val index = (scrollOffsetCoerced / averageItemSize)
.toInt()
.div(
with(gridCells) {
density.calculateCrossAxisCellSizes(containerSize, 0).size
}
)
.coerceAtLeast(0)
.coerceAtMost(itemCount - 1)
val offset = (scrollOffsetCoerced - index * averageItemSize)
.toInt()
.coerceAtLeast(0)
scrollState.scrollToItem(index = index, scrollOffset = offset)
}
private val itemCount get() = scrollState.layoutInfo.totalItemsCount
private val averageItemSize: Float by derivedStateOf {
scrollState
.layoutInfo
.visibleItemsInfo
.asSequence()
.map { it.size.height }
.average()
.toFloat()
}
private val itemsPerRow
get() = with(gridCells) {
density.calculateCrossAxisCellSizes(
(scrollState.layoutInfo.viewportEndOffset - scrollState.layoutInfo.viewportStartOffset)
.coerceAtLeast(0),
0
).size
}
}
actual fun Modifier.scrollbarPadding() = padding(horizontal = 4.dp, vertical = 8.dp)