diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index aa474d4b..63bebe63 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -41,5 +41,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index be6c96e5..b3da8a31 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -137,7 +137,7 @@ dependencies {
implementation("ru.noties.markwon:core:3.1.0")
implementation("xyz.quaver:documentfilex:0.7.1")
- implementation("xyz.quaver:subsampledimage:0.0.1-alpha13-SNAPSHOT")
+ implementation("xyz.quaver:subsampledimage:0.0.1-alpha14-SNAPSHOT-DEV01")
implementation("com.google.guava:guava:31.0.1-jre")
diff --git a/app/src/main/java/xyz/quaver/pupil/sources/composable/ReaderBase.kt b/app/src/main/java/xyz/quaver/pupil/sources/composable/ReaderBase.kt
index 4e632682..9258fdae 100644
--- a/app/src/main/java/xyz/quaver/pupil/sources/composable/ReaderBase.kt
+++ b/app/src/main/java/xyz/quaver/pupil/sources/composable/ReaderBase.kt
@@ -25,8 +25,7 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.border
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.lazy.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AutoFixHigh
@@ -39,15 +38,20 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
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.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.toSize
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
@@ -78,6 +82,7 @@ import xyz.quaver.pupil.db.AppDatabase
import xyz.quaver.pupil.proto.ReaderOptions
import xyz.quaver.pupil.proto.settingsDataStore
import xyz.quaver.pupil.ui.theme.Orange500
+import xyz.quaver.pupil.util.FileXImageSource
import xyz.quaver.pupil.util.NetworkCache
import xyz.quaver.pupil.util.activity
import xyz.quaver.pupil.util.rememberFileXImageSource
@@ -248,12 +253,356 @@ open class ReaderBaseViewModel(app: Application) : AndroidViewModel(app), DIAwar
}
}
+val ReaderOptions.Orientation.isVertical: Boolean
+ get() =
+ this == ReaderOptions.Orientation.VERTICAL_DOWN ||
+ this == ReaderOptions.Orientation.VERTICAL_UP
+val ReaderOptions.Orientation.isReverse: Boolean
+ get() =
+ this == ReaderOptions.Orientation.VERTICAL_UP ||
+ this == ReaderOptions.Orientation.HORIZONTAL_LEFT
+
+@Composable
+fun ReaderOptionsSheet(readerOptions: ReaderOptions, onOptionsChange: (ReaderOptions.Builder.() -> Unit) -> Unit) {
+ CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.h6) {
+ Column(Modifier.padding(16.dp, 0.dp)) {
+ val layout = readerOptions.layout
+ val snap = readerOptions.snap
+ val orientation = readerOptions.orientation
+ val padding = readerOptions.padding
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text("Layout")
+
+ Row {
+ listOf(
+ ReaderOptions.Layout.SINGLE_PAGE to SingleImage,
+ ReaderOptions.Layout.DOUBLE_PAGE to DoubleImage,
+ ReaderOptions.Layout.AUTO to Icons.Default.AutoFixHigh
+ ).forEach { (option, icon) ->
+ IconButton(onClick = {
+ onOptionsChange {
+ setLayout(option)
+ }
+ }) {
+ Icon(
+ icon,
+ contentDescription = null,
+ tint =
+ if (layout == option) MaterialTheme.colors.secondary
+ else LocalContentColor.current
+ )
+ }
+ }
+ }
+ }
+
+ val infiniteTransition = rememberInfiniteTransition()
+
+ val isReverse = orientation.isReverse
+ val isVertical = orientation.isVertical
+
+ val animationOrientation = if (isReverse) -1f else 1f
+ val animationSpacing by animateFloatAsState(if (padding) 48f else 32f)
+ val animationOffset by infiniteTransition.animateFloat(
+ initialValue = animationOrientation * (if (snap) 0f else animationSpacing/2),
+ targetValue = animationOrientation * (if (snap) -animationSpacing else -animationSpacing/2),
+ animationSpec = infiniteRepeatable(
+ animation = tween(
+ durationMillis = 1000,
+ easing = if(snap) FastOutSlowInEasing else LinearEasing
+ ),
+ repeatMode = RepeatMode.Restart
+ )
+ )
+ val animationRotation by animateFloatAsState(if (isVertical) 90f else 0f)
+
+ val setOrientation: (Boolean, Boolean) -> Unit = { isVertical, isReverse ->
+ val orientation = when {
+ isVertical && !isReverse -> ReaderOptions.Orientation.VERTICAL_DOWN
+ isVertical && isReverse -> ReaderOptions.Orientation.VERTICAL_UP
+ !isVertical && !isReverse -> ReaderOptions.Orientation.HORIZONTAL_RIGHT
+ !isVertical && isReverse -> ReaderOptions.Orientation.HORIZONTAL_LEFT
+ else -> error("Invalid value")
+ }
+
+ onOptionsChange {
+ setOrientation(orientation)
+ }
+ }
+
+ Box(
+ modifier = Modifier
+ .size(48.dp)
+ .clipToBounds()
+ .rotate(animationRotation)
+ .align(Alignment.CenterHorizontally)
+ ) {
+ for (i in 0..4)
+ Icon(
+ SingleImage,
+ contentDescription = null,
+ modifier = Modifier
+ .size(48.dp)
+ .align(Alignment.CenterStart)
+ .offset((animationOffset + animationSpacing * (i - 2)).dp, 0.dp)
+ )
+ }
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text("Orientation")
+
+ CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.caption) {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Text("H")
+ Switch(checked = isVertical, onCheckedChange = {
+ setOrientation(!isVertical, isReverse)
+ })
+ Text("V")
+ }
+ }
+ }
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text("Reverse")
+ Switch(checked = isReverse, onCheckedChange = {
+ setOrientation(isVertical, !isReverse)
+ })
+ }
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text("Snap")
+
+ Switch(checked = snap, onCheckedChange = {
+ onOptionsChange {
+ setSnap(!snap)
+ }
+ })
+ }
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text("Padding")
+
+ Switch(checked = padding, onCheckedChange = {
+ onOptionsChange {
+ setPadding(!padding)
+ }
+ })
+ }
+
+ Box(
+ Modifier
+ .fillMaxWidth()
+ .height(8.dp))
+ }
+ }
+}
+
+@Composable
+fun BoxScope.ReaderLazyList(
+ modifier: Modifier = Modifier,
+ state: LazyListState = rememberLazyListState(),
+ orientation: ReaderOptions.Orientation,
+ onScroll: (direction: Float) -> Unit,
+ content: LazyListScope.() -> Unit
+) {
+ val isReverse = orientation.isReverse
+
+ val nestedScrollConnection = remember(orientation) { object: NestedScrollConnection {
+ override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ onScroll(
+ when (orientation) {
+ ReaderOptions.Orientation.VERTICAL_DOWN -> available.y.sign
+ ReaderOptions.Orientation.VERTICAL_UP -> -(available.y.sign)
+ ReaderOptions.Orientation.HORIZONTAL_RIGHT -> available.x.sign
+ ReaderOptions.Orientation.HORIZONTAL_LEFT -> -(available.x.sign)
+ }
+ )
+
+ return Offset.Zero
+ }
+ } }
+
+ when (orientation) {
+ ReaderOptions.Orientation.VERTICAL_DOWN,
+ ReaderOptions.Orientation.VERTICAL_UP ->
+ LazyColumn(
+ modifier = modifier
+ .fillMaxSize()
+ .align(Alignment.TopStart)
+ .nestedScroll(nestedScrollConnection),
+ state = state,
+ verticalArrangement = Arrangement.spacedBy(4.dp),
+ contentPadding = rememberInsetsPaddingValues(LocalWindowInsets.current.navigationBars),
+ reverseLayout = isReverse,
+ content = content
+ )
+ ReaderOptions.Orientation.HORIZONTAL_RIGHT,
+ ReaderOptions.Orientation.HORIZONTAL_LEFT ->
+ LazyRow(
+ modifier = modifier
+ .fillMaxSize()
+ .align(Alignment.CenterStart)
+ .nestedScroll(nestedScrollConnection),
+ state = state,
+ horizontalArrangement = Arrangement.spacedBy(4.dp),
+ reverseLayout = isReverse,
+ content = content
+ )
+ }
+}
+
+@Composable
+fun ReaderLayoutItem(
+ modifier: Modifier = Modifier,
+ isVertical: Boolean,
+ content: @Composable (Modifier) -> Unit
+) {
+ if (isVertical)
+ Row(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ content(modifier.weight(1f))
+ }
+ else
+ Column(
+ modifier = Modifier.fillMaxHeight()
+ ) {
+ content(modifier.weight(1f))
+ }
+}
+
+@ExperimentalFoundationApi
+@Composable
+fun ReaderItem(
+ model: ReaderBaseViewModel,
+ readerOptions: ReaderOptions,
+ listSize: Size,
+ imageSources: List,
+) {
+ val context = LocalContext.current
+ val state = rememberSubSampledImageState(
+ when {
+ readerOptions.padding -> ScaleTypes.CENTER_INSIDE
+ readerOptions.orientation.isVertical -> ScaleTypes.FIT_WIDTH
+ else -> ScaleTypes.FIT_HEIGHT
+ }
+ )
+
+ val listSizeDp = LocalDensity.current.run { listSize.width.toDp() to listSize.height.toDp() }
+
+ val modifier = when {
+ readerOptions.padding -> Modifier.size(listSizeDp.first, listSizeDp.second)
+ readerOptions.orientation.isVertical -> Modifier
+ .wrapContentHeight(state, listSizeDp.second)
+ .fillMaxWidth()
+ else -> Modifier
+ .wrapContentWidth(state, listSizeDp.first)
+ .fillMaxHeight()
+ }
+
+ ReaderLayoutItem(modifier, readerOptions.orientation.isVertical) { modifier ->
+ indices.forEach { index ->
+ Box(
+ modifier.border(1.dp, Color.Gray),
+ contentAlignment = Alignment.Center
+ ) {
+ val progress = model.progressList.getOrNull(index) ?: 0f
+ val uri = model.imageList.getOrNull(index)
+
+ if (progress == Float.NEGATIVE_INFINITY)
+ Icon(Icons.Filled.BrokenImage, null, tint = Orange500)
+ else if (progress.isFinite())
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ LinearProgressIndicator(progress)
+ Text((index + 1).toString())
+ }
+ else if (uri != null && progress == Float.POSITIVE_INFINITY) {
+ val imageSource = kotlin.runCatching {
+ rememberFileXImageSource(FileX(context, uri))
+ }.getOrNull()
+
+ if (imageSource != null)
+ SubSampledImage(
+ modifier = Modifier
+ .fillMaxSize()
+ .run {
+ if (model.fullscreen)
+ doubleClickCycleZoom(state, 2f)
+ else
+ combinedClickable(
+ onLongClick = {
+
+ }
+ ) {
+ model.fullscreen = true
+ }
+ },
+ imageSource = imageSource,
+ state = state,
+ onError = {
+ model.error(index)
+ }
+ )
+ }
+ }
+ }
+ }
+}
+
+@ExperimentalFoundationApi
+fun LazyListScope.ReaderLazyListContent(
+ model: ReaderBaseViewModel,
+ listSize: Size,
+ imageSources: List,
+ imageSizes: List,
+ readerOptions: ReaderOptions
+) {
+ when {
+ readerOptions.layout == ReaderOptions.Layout.SINGLE_PAGE ->
+ items(imageSources) { source ->
+ ReaderItem(model, readerOptions, listSize, listOf(source))
+ }
+ readerOptions.layout == ReaderOptions.Layout.DOUBLE_PAGE ->
+ items(imageSources.size/2 + (imageSources.size and 0x1)) { i ->
+ ReaderItem(model, readerOptions, listSize, imageSources.subList(2*i, 2*i+2))
+ }
+ else ->
+ items(imageSources) { source ->
+ ReaderItem(model, readerOptions, listSize, listOf(source))
+ }
+ }
+}
+
@ExperimentalMaterialApi
@ExperimentalFoundationApi
@Composable
fun ReaderBase(
modifier: Modifier = Modifier,
- model: ReaderBaseViewModel
+ model: ReaderBaseViewModel,
+ onScroll: (direction: Float) -> Unit = { }
) {
val context = LocalContext.current
val haptic = LocalHapticFeedback.current
@@ -265,18 +614,14 @@ fun ReaderBase(
var scrollDirection by remember { mutableStateOf(0f) }
val handleOffset by animateDpAsState(if (model.fullscreen || scrollDirection < 0f) (-36).dp else 0.dp)
-
+
val mainReaderOptions by remember {
context.settingsDataStore.data.map { it.mainReaderOption }
}.collectAsState(ReaderOptions.getDefaultInstance())
- val nestedScrollConnection = remember { object: NestedScrollConnection {
- override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
- scrollDirection = available.y.sign
-
- return Offset.Zero
- }
- } }
+ LaunchedEffect(scrollDirection) {
+ onScroll(scrollDirection)
+ }
LaunchedEffect(model.fullscreen) {
context.activity?.window?.let { window ->
@@ -305,249 +650,85 @@ fun ReaderBase(
ModalTopSheetLayout(
modifier = Modifier.offset(0.dp, handleOffset),
drawerContent = {
- CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.h6) {
- Column(Modifier.padding(16.dp, 0.dp)) {
- val layout = mainReaderOptions.layout
- val snap = mainReaderOptions.snap
- val orientation = mainReaderOptions.orientation
- val padding = mainReaderOptions.padding
-
- Row(
- modifier = Modifier.fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween
- ) {
- Text("Layout")
-
- Row {
- listOf(
- ReaderOptions.Layout.SINGLE_PAGE to SingleImage,
- ReaderOptions.Layout.DOUBLE_PAGE to DoubleImage,
- ReaderOptions.Layout.AUTO to Icons.Default.AutoFixHigh
- ).forEach { (option, icon) ->
- IconButton(onClick = {
- coroutineScope.launch {
- context.settingsDataStore.updateData {
- it.toBuilder().setMainReaderOption(
- it.mainReaderOption.toBuilder()
- .setLayout(option)
- .build()
- ).build()
- }
- }
- }) {
- Icon(
- icon,
- contentDescription = null,
- tint =
- if (layout == option) MaterialTheme.colors.secondary
- else LocalContentColor.current
- )
- }
- }
- }
+ ReaderOptionsSheet(mainReaderOptions) { readerOptionsBlock ->
+ coroutineScope.launch {
+ context.settingsDataStore.updateData {
+ it.toBuilder().setMainReaderOption(
+ mainReaderOptions.toBuilder().apply(readerOptionsBlock).build()
+ ).build()
}
-
- val infiniteTransition = rememberInfiniteTransition()
-
- val isVertical =
- orientation == ReaderOptions.Orientation.VERTICAL_DOWN ||
- orientation == ReaderOptions.Orientation.VERTICAL_UP
- val isReverse =
- orientation == ReaderOptions.Orientation.VERTICAL_UP ||
- orientation == ReaderOptions.Orientation.HORIZONTAL_LEFT
-
- val animationOrientation = if (isReverse) -1f else 1f
- val animationSpacing by animateFloatAsState(if (padding) 48f else 32f)
- val animationOffset by infiniteTransition.animateFloat(
- initialValue = animationOrientation * (if (snap) 0f else animationSpacing/2),
- targetValue = animationOrientation * (if (snap) -animationSpacing else -animationSpacing/2),
- animationSpec = infiniteRepeatable(
- animation = tween(
- durationMillis = 1000,
- easing = if(snap) FastOutSlowInEasing else LinearEasing
- ),
- repeatMode = RepeatMode.Restart
- )
- )
- val animationRotation by animateFloatAsState(if (isVertical) 90f else 0f)
-
- val setOrientation: (Boolean, Boolean) -> Unit = { isVertical, isReverse ->
- val orientation = when {
- isVertical && !isReverse -> ReaderOptions.Orientation.VERTICAL_DOWN
- isVertical && isReverse -> ReaderOptions.Orientation.VERTICAL_UP
- !isVertical && !isReverse -> ReaderOptions.Orientation.HORIZONTAL_RIGHT
- !isVertical && isReverse -> ReaderOptions.Orientation.HORIZONTAL_LEFT
- else -> error("Invalid value")
- }
-
- coroutineScope.launch {
- context.settingsDataStore.updateData {
- it.toBuilder().setMainReaderOption(
- mainReaderOptions.toBuilder()
- .setOrientation(orientation)
- .build()
- ).build()
- }
- }
- }
-
- Box(
- modifier = Modifier
- .size(48.dp)
- .clipToBounds()
- .rotate(animationRotation)
- .align(Alignment.CenterHorizontally)
- ) {
- for (i in 0..4)
- Icon(
- SingleImage,
- contentDescription = null,
- modifier = Modifier
- .size(48.dp)
- .align(Alignment.CenterStart)
- .offset((animationOffset + animationSpacing * (i - 2)).dp, 0.dp)
- )
- }
-
- Row(
- modifier = Modifier.fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween
- ) {
- Text("Orientation")
-
- CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.caption) {
- Row(verticalAlignment = Alignment.CenterVertically) {
- Text("H")
- Switch(checked = isVertical, onCheckedChange = {
- setOrientation(!isVertical, isReverse)
- })
- Text("V")
- }
- }
- }
-
- Row(
- modifier = Modifier.fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween
- ) {
- Text("Reverse")
- Switch(checked = isReverse, onCheckedChange = {
- setOrientation(isVertical, !isReverse)
- })
- }
-
- Row(
- modifier = Modifier.fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween
- ) {
- Text("Snap")
-
- Switch(checked = snap, onCheckedChange = {
- coroutineScope.launch {
- context.settingsDataStore.updateData {
- it.toBuilder().setMainReaderOption(
- mainReaderOptions.toBuilder()
- .setSnap(!snap)
- .build()
- ).build()
- }
- }
- })
- }
-
- Row(
- modifier = Modifier.fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween
- ) {
- Text("Padding")
-
- Switch(checked = padding, onCheckedChange = {
- coroutineScope.launch {
- context.settingsDataStore.updateData {
- it.toBuilder().setMainReaderOption(
- mainReaderOptions.toBuilder()
- .setPadding(!padding)
- .build()
- ).build()
- }
- }
- })
- }
-
- Box(
- Modifier
- .fillMaxWidth()
- .height(8.dp))
}
}
}
) {
- LazyColumn(
- Modifier
- .fillMaxSize()
- .align(Alignment.TopStart)
- .nestedScroll(nestedScrollConnection),
- verticalArrangement = Arrangement.spacedBy(4.dp),
- contentPadding = rememberInsetsPaddingValues(LocalWindowInsets.current.navigationBars)
- ) {
- itemsIndexed(model.imageList) { i, uri ->
- val state = rememberSubSampledImageState(ScaleTypes.FIT_WIDTH)
+ var listSize: Size? by remember { mutableStateOf(null) }
+ val listState = rememberLazyListState()
- Box(
- Modifier
- .wrapContentHeight(state, 500.dp)
- .fillMaxWidth()
- .border(1.dp, Color.Gray),
- contentAlignment = Alignment.Center
- ) {
- val progress = model.progressList.getOrNull(i) ?: 0f
-
- if (progress == Float.NEGATIVE_INFINITY)
- Icon(Icons.Filled.BrokenImage, null, tint = Orange500)
- else if (progress.isFinite())
- Column(
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- LinearProgressIndicator(progress)
- Text((i + 1).toString())
- }
- else if (uri != null && progress == Float.POSITIVE_INFINITY) {
- val imageSource = kotlin.runCatching {
- rememberFileXImageSource(FileX(context, uri))
- }.getOrNull()
-
- if (imageSource != null)
- SubSampledImage(
- modifier = Modifier
- .fillMaxSize()
- .run {
- if (model.fullscreen)
- doubleClickCycleZoom(state, 2f)
- else
- combinedClickable(
- onLongClick = {
-
- }
- ) {
- model.fullscreen = true
- }
- },
- imageSource = imageSource,
- state = state,
- onError = {
- model.error(i)
- }
- )
+ val nestedScrollConnection = remember { object: NestedScrollConnection {
+ override suspend fun onPreFling(available: Velocity): Velocity {
+ return if (mainReaderOptions.snap) {
+ val velocity = when (mainReaderOptions.orientation) {
+ ReaderOptions.Orientation.VERTICAL_DOWN -> available.y
+ ReaderOptions.Orientation.VERTICAL_UP -> -(available.y)
+ ReaderOptions.Orientation.HORIZONTAL_RIGHT -> available.x
+ ReaderOptions.Orientation.HORIZONTAL_LEFT -> -(available.x)
}
+
+ val index = listState.firstVisibleItemIndex
+
+ coroutineScope.launch {
+ when {
+ velocity < 0f -> listState.animateScrollToItem(index+1)
+ else -> listState.animateScrollToItem(index)
+ }
+ }
+
+ available
+ } else Velocity.Zero
+
+ }
+ } }
+
+ val imageSources = remember { mutableStateListOf() }
+ val imageSizes = remember { mutableStateListOf() }
+
+ LaunchedEffect(model.imageList.count { it != null }) {
+ if (imageSources.size != model.imageList.size)
+ imageSources.addAll(List (model.imageList.size-imageSources.size) { null })
+
+ if (imageSizes.size != model.imageList.size)
+ imageSizes.addAll(List (model.imageList.size-imageSources.size) { null })
+
+ coroutineScope.launch {
+ model.imageList.forEachIndexed { i, uri ->
+ if (imageSources[i] == null && uri != null)
+ imageSources[i] = FileXImageSource(FileX(context, uri))
+
+ if (imageSizes[i] == null)
+ imageSources[i]?.let {
+ imageSizes[i] = it.imageSize
+ }
}
}
}
+ ReaderLazyList(
+ Modifier
+ .onGloballyPositioned { listSize = it.size.toSize() }
+ .nestedScroll(nestedScrollConnection),
+ listState,
+ mainReaderOptions.orientation,
+ onScroll = { scrollDirection = it },
+ ) {
+ ReaderLazyListContent(
+ model,
+ listSize ?: Size.Zero,
+ imageSources,
+ imageSizes,
+ mainReaderOptions
+ )
+ }
+
if (model.progressList.any { it.isFinite() })
LinearProgressIndicator(
modifier = Modifier
diff --git a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/Manatoki.kt b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/Manatoki.kt
index 3e22f56c..96952191 100644
--- a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/Manatoki.kt
+++ b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/Manatoki.kt
@@ -415,13 +415,6 @@ class Manatoki(app: Application) : Source(), DIAware {
val listState = rememberLazyListState()
var scrollDirection by remember { mutableStateOf(0f) }
- val nestedScrollConnection = remember { object: NestedScrollConnection {
- override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
- scrollDirection = available.y.sign
-
- return Offset.Zero
- }
- } }
BackHandler {
when {
@@ -572,8 +565,9 @@ class Manatoki(app: Application) : Source(), DIAware {
}
) { contentPadding ->
ReaderBase(
- Modifier.padding(contentPadding).nestedScroll(nestedScrollConnection),
- model
+ Modifier.padding(contentPadding),
+ model = model,
+ onScroll = { scrollDirection = it }
)
}
}