[WIP] Pageturn

This commit is contained in:
tom5079
2021-12-18 13:32:07 +09:00
parent adf18341d0
commit a57b1d5614
3 changed files with 87 additions and 40 deletions

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<runningDeviceTargetSelectedWithDropDown>
<Target>
<type value="RUNNING_DEVICE_TARGET" />
<deviceKey>
<Key>
<type value="SERIAL_NUMBER" />
<value value="ce021712e3b19b2b04" />
</Key>
</deviceKey>
</Target>
</runningDeviceTargetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2021-12-17T11:08:36.835795Z" />
</component>
</project>

View File

@@ -44,11 +44,13 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.*
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastFirstOrNull
import com.google.accompanist.drawablepainter.rememberDrawablePainter import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.kodein.di.DIAware import org.kodein.di.DIAware
@@ -58,14 +60,10 @@ import org.kodein.log.newLogger
import xyz.quaver.pupil.* import xyz.quaver.pupil.*
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.sources.SearchResultEvent import xyz.quaver.pupil.sources.SearchResultEvent
import xyz.quaver.pupil.ui.composable.FloatingActionButtonState import xyz.quaver.pupil.ui.composable.*
import xyz.quaver.pupil.ui.composable.FloatingSearchBar import xyz.quaver.pupil.ui.dialog.OpenWithItemIDDialog
import xyz.quaver.pupil.ui.composable.MultipleFloatingActionButton
import xyz.quaver.pupil.ui.composable.SubFabItem
import xyz.quaver.pupil.ui.dialog.SourceSelectDialog import xyz.quaver.pupil.ui.dialog.SourceSelectDialog
import xyz.quaver.pupil.ui.theme.PupilTheme import xyz.quaver.pupil.ui.theme.PupilTheme
import xyz.quaver.pupil.ui.composable.ProgressCard
import xyz.quaver.pupil.ui.dialog.OpenWithItemIDDialog
import xyz.quaver.pupil.ui.viewmodel.MainViewModel import xyz.quaver.pupil.ui.viewmodel.MainViewModel
import xyz.quaver.pupil.util.* import xyz.quaver.pupil.util.*
import kotlin.math.* import kotlin.math.*
@@ -90,6 +88,20 @@ class MainActivity : ComponentActivity(), DIAware {
PupilTheme { PupilTheme {
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
val maxPage by model.maxPage.collectAsState(0)
val pageTurnIndicatorHeight = LocalDensity.current.run { 64.dp.toPx() }
val prevPageAvailable by derivedStateOf {
model.currentPage > 1
}
val nextPageAvailable by derivedStateOf {
model.currentPage <= maxPage
}
var overscroll: Float? by remember { mutableStateOf(null) }
var isFabExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) } var isFabExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) }
var isFabVisible by remember { mutableStateOf(true) } var isFabVisible by remember { mutableStateOf(true) }
@@ -185,24 +197,40 @@ class MainActivity : ComponentActivity(), DIAware {
} }
) { ) {
Box(Modifier.fillMaxSize()) { Box(Modifier.fillMaxSize()) {
LazyColumn( LazyColumn(
Modifier Modifier
.fillMaxSize() .fillMaxSize()
.offset(0.dp, overscroll?.let { overscroll -> LocalDensity.current.run { overscroll.toDp() } } ?: 0.dp)
.nestedScroll(object : NestedScrollConnection { .nestedScroll(object : NestedScrollConnection {
override fun onPreScroll( override fun onPreScroll(
available: Offset, available: Offset,
source: NestedScrollSource source: NestedScrollSource
): Offset { ): Offset {
searchBarOffset = val overscrollSnapshot = overscroll
(searchBarOffset + available.y.roundToInt()).coerceIn(
-searchBarHeight,
0
)
isFabVisible = available.y > 0f if (overscrollSnapshot == null || overscrollSnapshot == 0f) {
searchBarOffset =
(searchBarOffset + available.y.roundToInt()).coerceIn(
-searchBarHeight,
0
)
return Offset.Zero isFabVisible = available.y > 0f
return Offset.Zero
} else {
val newOverscroll =
if (overscrollSnapshot > 0f && available.y < 0f)
max(overscrollSnapshot + available.y, 0f)
else if (overscrollSnapshot < 0f && available.y > 0f)
min(overscrollSnapshot + available.y, 0f)
else
overscrollSnapshot
return Offset(0f, newOverscroll - overscrollSnapshot).also {
overscroll = newOverscroll
}
}
} }
override fun onPostScroll( override fun onPostScroll(
@@ -210,11 +238,41 @@ class MainActivity : ComponentActivity(), DIAware {
available: Offset, available: Offset,
source: NestedScrollSource source: NestedScrollSource
): Offset { ): Offset {
if (available.y == 0f || source == NestedScrollSource.Fling) return Offset.Zero
return overscroll?.let {
val newOverscroll = (it + available.y).coerceIn(-pageTurnIndicatorHeight, pageTurnIndicatorHeight)
return super.onPostScroll(consumed, available, source) Offset(0f, newOverscroll - it).also {
overscroll = newOverscroll
}
} ?: Offset.Zero
} }
}), }).pointerInput(Unit) {
forEachGesture {
awaitPointerEventScope {
val down = awaitFirstDown(requireUnconsumed = false)
var pointer = down.id
overscroll = 0f
while (true) {
val event = awaitPointerEvent()
val dragEvent = event.changes.fastFirstOrNull { it.id == pointer }!!
if (dragEvent.changedToUpIgnoreConsumed()) {
val otherDown = event.changes.fastFirstOrNull { it.pressed }
if (otherDown == null) {
dragEvent.consumePositionChange()
overscroll = null
break
}
else
pointer = otherDown.id
}
}
}
}
},
contentPadding = PaddingValues(0.dp, 56.dp, 0.dp, 0.dp) contentPadding = PaddingValues(0.dp, 56.dp, 0.dp, 0.dp)
) { ) {
items(model.searchResults, key = { it.itemID }) { itemInfo -> items(model.searchResults, key = { it.itemID }) { itemInfo ->
@@ -251,9 +309,11 @@ class MainActivity : ComponentActivity(), DIAware {
Image( Image(
painterResource(model.source.iconResID), painterResource(model.source.iconResID),
contentDescription = null, contentDescription = null,
modifier = Modifier.size(24.dp).clickable { modifier = Modifier
sourceSelectDialog = true .size(24.dp)
} .clickable {
sourceSelectDialog = true
}
) )
Icon( Icon(
Icons.Default.Sort, Icons.Default.Sort,

View File

@@ -19,10 +19,7 @@
package xyz.quaver.pupil.ui.viewmodel package xyz.quaver.pupil.ui.viewmodel
import android.app.Application import android.app.Application
import androidx.compose.runtime.getValue import androidx.compose.runtime.*
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.asLiveData import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@@ -40,6 +37,7 @@ import xyz.quaver.pupil.sources.History
import xyz.quaver.pupil.sources.ItemInfo import xyz.quaver.pupil.sources.ItemInfo
import xyz.quaver.pupil.sources.Source import xyz.quaver.pupil.sources.Source
import xyz.quaver.pupil.util.source import xyz.quaver.pupil.util.source
import kotlin.math.ceil
import kotlin.random.Random import kotlin.random.Random
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@@ -78,6 +76,12 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
var totalItems by mutableStateOf(0) var totalItems by mutableStateOf(0)
private set private set
val maxPage by derivedStateOf {
resultsPerPage.map {
ceil(totalItems / it.toDouble()).toInt()
}
}
fun setSourceAndReset(sourceName: String) { fun setSourceAndReset(sourceName: String) {
source = sourceFactory(sourceName) source = sourceFactory(sourceName)
sortModeIndex = 0 sortModeIndex = 0