[WIP] Pageturn
This commit is contained in:
17
.idea/deploymentTargetDropDown.xml
generated
17
.idea/deploymentTargetDropDown.xml
generated
@@ -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>
|
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user