mirror of
https://github.com/fankes/pagecurl-multiplatform.git
synced 2025-09-06 10:45:43 +08:00
Refactor page curl config
This commit is contained in:
@@ -20,13 +20,11 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.window.Popup
|
import androidx.compose.ui.window.Popup
|
||||||
import androidx.compose.ui.window.PopupProperties
|
import androidx.compose.ui.window.PopupProperties
|
||||||
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
||||||
import eu.wewox.pagecurl.config.InteractionConfig
|
import eu.wewox.pagecurl.config.PageCurlConfig
|
||||||
import eu.wewox.pagecurl.config.copy
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun SettingsPopup(
|
internal fun SettingsPopup(
|
||||||
interaction: InteractionConfig,
|
config: PageCurlConfig,
|
||||||
onConfigChange: (InteractionConfig) -> Unit,
|
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Popup(
|
Popup(
|
||||||
@@ -49,26 +47,26 @@ internal fun SettingsPopup(
|
|||||||
.padding(horizontal = 10.dp)
|
.padding(horizontal = 10.dp)
|
||||||
SwitchRow(
|
SwitchRow(
|
||||||
text = "Forward drag enabled",
|
text = "Forward drag enabled",
|
||||||
enabled = interaction.drag.forward.enabled,
|
enabled = config.dragForwardEnabled,
|
||||||
onChanged = { onConfigChange(interaction.copy(dragForwardEnabled = it)) },
|
onChanged = { config.dragForwardEnabled = it },
|
||||||
modifier = switchRowModifier
|
modifier = switchRowModifier
|
||||||
)
|
)
|
||||||
SwitchRow(
|
SwitchRow(
|
||||||
text = "Backward drag enabled",
|
text = "Backward drag enabled",
|
||||||
enabled = interaction.drag.backward.enabled,
|
enabled = config.dragBackwardEnabled,
|
||||||
onChanged = { onConfigChange(interaction.copy(dragBackwardEnabled = it)) },
|
onChanged = { config.dragBackwardEnabled = it },
|
||||||
modifier = switchRowModifier
|
modifier = switchRowModifier
|
||||||
)
|
)
|
||||||
SwitchRow(
|
SwitchRow(
|
||||||
text = "Forward tap enabled",
|
text = "Forward tap enabled",
|
||||||
enabled = interaction.tap.forward.enabled,
|
enabled = config.tapForwardEnabled,
|
||||||
onChanged = { onConfigChange(interaction.copy(tapForwardEnabled = it)) },
|
onChanged = { config.tapForwardEnabled = it },
|
||||||
modifier = switchRowModifier
|
modifier = switchRowModifier
|
||||||
)
|
)
|
||||||
SwitchRow(
|
SwitchRow(
|
||||||
text = "Backward tap enabled",
|
text = "Backward tap enabled",
|
||||||
enabled = interaction.tap.backward.enabled,
|
enabled = config.tapBackwardEnabled,
|
||||||
onChanged = { onConfigChange(interaction.copy(tapBackwardEnabled = it)) },
|
onChanged = { config.tapBackwardEnabled = it },
|
||||||
modifier = switchRowModifier
|
modifier = switchRowModifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.center
|
import androidx.compose.ui.unit.center
|
||||||
@@ -17,8 +18,7 @@ import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
|||||||
import eu.wewox.pagecurl.HowToPageData
|
import eu.wewox.pagecurl.HowToPageData
|
||||||
import eu.wewox.pagecurl.components.HowToPage
|
import eu.wewox.pagecurl.components.HowToPage
|
||||||
import eu.wewox.pagecurl.components.SettingsPopup
|
import eu.wewox.pagecurl.components.SettingsPopup
|
||||||
import eu.wewox.pagecurl.config.InteractionConfig
|
import eu.wewox.pagecurl.config.rememberPageCurlConfig
|
||||||
import eu.wewox.pagecurl.config.PageCurlConfig
|
|
||||||
import eu.wewox.pagecurl.page.PageCurl
|
import eu.wewox.pagecurl.page.PageCurl
|
||||||
import eu.wewox.pagecurl.page.rememberPageCurlState
|
import eu.wewox.pagecurl.page.rememberPageCurlState
|
||||||
|
|
||||||
@@ -26,45 +26,31 @@ import eu.wewox.pagecurl.page.rememberPageCurlState
|
|||||||
fun SettingsPageCurlScreen() {
|
fun SettingsPageCurlScreen() {
|
||||||
Box(Modifier.fillMaxSize()) {
|
Box(Modifier.fillMaxSize()) {
|
||||||
val pages = remember { HowToPageData.interactionHowToPages }
|
val pages = remember { HowToPageData.interactionHowToPages }
|
||||||
val state = rememberPageCurlState(max = pages.size)
|
|
||||||
|
|
||||||
var showPopup by remember { mutableStateOf(false) }
|
var showPopup by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
// Create a mutable interaction config with custom tap interaction
|
val state = rememberPageCurlState(
|
||||||
// In SettingsPopup config is mutated
|
max = pages.size,
|
||||||
var interaction by remember {
|
config = rememberPageCurlConfig(
|
||||||
mutableStateOf(
|
onCustomTap = { size, position ->
|
||||||
InteractionConfig(
|
// Detect tap somewhere in the center with 64 radius and show popup
|
||||||
tap = InteractionConfig.Tap(
|
if ((position - size.center.toOffset()).getDistance() < 64.dp.toPx()) {
|
||||||
custom = InteractionConfig.Tap.CustomInteraction(true) { size, position ->
|
showPopup = true
|
||||||
// Detect tap somewhere in the center with 64 radius and show popup
|
true
|
||||||
if ((position - size.center.toOffset()).getDistance() < 64.dp.toPx()) {
|
} else {
|
||||||
showPopup = true
|
false
|
||||||
true
|
}
|
||||||
} else {
|
}
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
|
|
||||||
PageCurl(
|
PageCurl(state = state) { index ->
|
||||||
state = state,
|
|
||||||
config = PageCurlConfig(
|
|
||||||
interaction = interaction
|
|
||||||
)
|
|
||||||
) { index ->
|
|
||||||
HowToPage(index, pages[index])
|
HowToPage(index, pages[index])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showPopup) {
|
if (showPopup) {
|
||||||
SettingsPopup(
|
SettingsPopup(
|
||||||
interaction = interaction,
|
config = state.config,
|
||||||
onConfigChange = {
|
|
||||||
interaction = it
|
|
||||||
},
|
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
showPopup = false
|
showPopup = false
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@ import androidx.compose.material.Button
|
|||||||
import androidx.compose.material.Card
|
import androidx.compose.material.Card
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
@@ -30,9 +31,7 @@ import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
|||||||
import eu.wewox.pagecurl.HowToPageData
|
import eu.wewox.pagecurl.HowToPageData
|
||||||
import eu.wewox.pagecurl.components.HowToPage
|
import eu.wewox.pagecurl.components.HowToPage
|
||||||
import eu.wewox.pagecurl.components.ZoomOutLayout
|
import eu.wewox.pagecurl.components.ZoomOutLayout
|
||||||
import eu.wewox.pagecurl.config.InteractionConfig
|
import eu.wewox.pagecurl.config.rememberPageCurlConfig
|
||||||
import eu.wewox.pagecurl.config.PageCurlConfig
|
|
||||||
import eu.wewox.pagecurl.config.copy
|
|
||||||
import eu.wewox.pagecurl.page.PageCurl
|
import eu.wewox.pagecurl.page.PageCurl
|
||||||
import eu.wewox.pagecurl.page.PageCurlState
|
import eu.wewox.pagecurl.page.PageCurlState
|
||||||
import eu.wewox.pagecurl.page.rememberPageCurlState
|
import eu.wewox.pagecurl.page.rememberPageCurlState
|
||||||
@@ -44,28 +43,34 @@ import kotlinx.coroutines.launch
|
|||||||
fun StatePageCurlScreen() {
|
fun StatePageCurlScreen() {
|
||||||
Box(Modifier.fillMaxSize()) {
|
Box(Modifier.fillMaxSize()) {
|
||||||
val pages = remember { HowToPageData.interactionHowToPages }
|
val pages = remember { HowToPageData.interactionHowToPages }
|
||||||
val state = rememberPageCurlState(max = pages.size)
|
|
||||||
|
|
||||||
var zoomOut by remember { mutableStateOf(false) }
|
var zoomOut by remember { mutableStateOf(false) }
|
||||||
|
val state = rememberPageCurlState(
|
||||||
val interactionConfig = remember {
|
max = pages.size,
|
||||||
InteractionConfig(
|
config = rememberPageCurlConfig(
|
||||||
tap = InteractionConfig.Tap(
|
onCustomTap = { size, position ->
|
||||||
custom = InteractionConfig.Tap.CustomInteraction(true) { size, position ->
|
// When PageCurl is zoomed out then zoom back in
|
||||||
// When PageCurl is zoomed out then zoom back in
|
// Else detect tap somewhere in the center with 64 radius and zoom out a PageCurl
|
||||||
// Else detect tap somewhere in the center with 64 radius and zoom out a PageCurl
|
if (zoomOut) {
|
||||||
if (zoomOut) {
|
zoomOut = false
|
||||||
zoomOut = false
|
true
|
||||||
true
|
} else if ((position - size.center.toOffset()).getDistance() < 64.dp.toPx()) {
|
||||||
} else if ((position - size.center.toOffset()).getDistance() < 64.dp.toPx()) {
|
zoomOut = true
|
||||||
zoomOut = true
|
true
|
||||||
true
|
} else {
|
||||||
} else {
|
false
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Disable all state interactions when PageCurl is zoomed out
|
||||||
|
LaunchedEffect(zoomOut) {
|
||||||
|
with(state.config) {
|
||||||
|
dragForwardEnabled = !zoomOut
|
||||||
|
dragBackwardEnabled = !zoomOut
|
||||||
|
tapForwardEnabled = !zoomOut
|
||||||
|
tapBackwardEnabled = !zoomOut
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ZoomOutLayout(
|
ZoomOutLayout(
|
||||||
@@ -79,17 +84,7 @@ fun StatePageCurlScreen() {
|
|||||||
shape = RoundedCornerShape(cornersAndElevation),
|
shape = RoundedCornerShape(cornersAndElevation),
|
||||||
elevation = cornersAndElevation,
|
elevation = cornersAndElevation,
|
||||||
) {
|
) {
|
||||||
PageCurl(
|
PageCurl(state = state) { index ->
|
||||||
state = state,
|
|
||||||
config = PageCurlConfig(
|
|
||||||
interaction = interactionConfig.copy(
|
|
||||||
dragForwardEnabled = !zoomOut,
|
|
||||||
dragBackwardEnabled = !zoomOut,
|
|
||||||
tapForwardEnabled = !zoomOut,
|
|
||||||
tapBackwardEnabled = !zoomOut,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) { index ->
|
|
||||||
HowToPage(index, pages[index])
|
HowToPage(index, pages[index])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,186 +0,0 @@
|
|||||||
package eu.wewox.pagecurl.config
|
|
||||||
|
|
||||||
import androidx.compose.ui.geometry.Offset
|
|
||||||
import androidx.compose.ui.geometry.Rect
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.unit.Density
|
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.DpOffset
|
|
||||||
import androidx.compose.ui.unit.IntSize
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The configuration for PageCurl.
|
|
||||||
*
|
|
||||||
* @property curl Configures how page curl looks like.
|
|
||||||
* @property interaction Configures interactions with a page curl. Such as if drag or tap to go on next or previous
|
|
||||||
* page is allowed, etc.
|
|
||||||
*/
|
|
||||||
@ExperimentalPageCurlApi
|
|
||||||
public data class PageCurlConfig(
|
|
||||||
val curl: CurlConfig = CurlConfig(),
|
|
||||||
val interaction: InteractionConfig = InteractionConfig(),
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures how page curl looks like.
|
|
||||||
*
|
|
||||||
* @property backPage Configures how back-page looks like (color, content alpha).
|
|
||||||
* @property shadow Configures how page shadow looks like (color, alpha, radius).
|
|
||||||
*/
|
|
||||||
@ExperimentalPageCurlApi
|
|
||||||
public data class CurlConfig(
|
|
||||||
val backPage: BackPageConfig = BackPageConfig(),
|
|
||||||
val shadow: ShadowConfig = ShadowConfig(),
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures how back-page of the page curl looks like.
|
|
||||||
*
|
|
||||||
* @property color Color of the back-page. In majority of use-cases it should be set to the content background color.
|
|
||||||
* @property contentAlpha The alpha which defines how content is "seen through" the back-page. From 0 (nothing is
|
|
||||||
* visible) to 1 (everything is visible).
|
|
||||||
*/
|
|
||||||
@ExperimentalPageCurlApi
|
|
||||||
public data class BackPageConfig(
|
|
||||||
val color: Color = Color.White,
|
|
||||||
val contentAlpha: Float = 0.1f,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures how page shadow looks like.
|
|
||||||
*
|
|
||||||
* @property color The color of the shadow. In majority of use-cases it should be set to the inverted color to the
|
|
||||||
* content background color. Should be a solid color, see [alpha] to adjust opacity.
|
|
||||||
* @property alpha The alpha of the [color].
|
|
||||||
* @property radius Defines how big the shadow is.
|
|
||||||
* @property offset Defines how shadow is shifted from the page. A little shift may add more realism.
|
|
||||||
*/
|
|
||||||
@ExperimentalPageCurlApi
|
|
||||||
public data class ShadowConfig(
|
|
||||||
val color: Color = Color.Black,
|
|
||||||
val alpha: Float = 0.2f,
|
|
||||||
val radius: Dp = 15.dp,
|
|
||||||
val offset: DpOffset = DpOffset((-5).dp, 0.dp),
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures interactions with a page curl.
|
|
||||||
*
|
|
||||||
* @property drag Configures drag interactions.
|
|
||||||
* @property tap Configures tap interactions.
|
|
||||||
*/
|
|
||||||
@ExperimentalPageCurlApi
|
|
||||||
public data class InteractionConfig(
|
|
||||||
val drag: Drag = Drag(),
|
|
||||||
val tap: Tap = Tap(),
|
|
||||||
) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures drag interactions.
|
|
||||||
*
|
|
||||||
* @property forward Configures forward drag interaction.
|
|
||||||
* @property backward Configures backward drag interaction.
|
|
||||||
*/
|
|
||||||
@ExperimentalPageCurlApi
|
|
||||||
public data class Drag(
|
|
||||||
val forward: Interaction = Interaction(true, rightHalf(), leftHalf()),
|
|
||||||
val backward: Interaction = Interaction(true, forward.end, forward.start),
|
|
||||||
) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The drag interaction setting.
|
|
||||||
*
|
|
||||||
* @property enabled True if this interaction is enabled or not.
|
|
||||||
* @property start Defines a rectangle where interaction should start. The rectangle coordinates are relative
|
|
||||||
* (from 0 to 1) and then scaled to the PageCurl bounds.
|
|
||||||
* @property end Defines a rectangle where interaction should end. The rectangle coordinates are relative
|
|
||||||
* (from 0 to 1) and then scaled to the PageCurl bounds.
|
|
||||||
*/
|
|
||||||
@ExperimentalPageCurlApi
|
|
||||||
public data class Interaction(
|
|
||||||
val enabled: Boolean,
|
|
||||||
val start: Rect = Rect.Zero,
|
|
||||||
val end: Rect = Rect.Zero,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures tap interactions.
|
|
||||||
*
|
|
||||||
* @property forward Configures forward tap interaction.
|
|
||||||
* @property backward Configures backward tap interaction.
|
|
||||||
* @property custom The custom tap interaction. Could be provided to implement custom taps in the PageCurl, e.g. to
|
|
||||||
* capture taps in the center, etc.
|
|
||||||
*/
|
|
||||||
@ExperimentalPageCurlApi
|
|
||||||
public data class Tap(
|
|
||||||
val forward: Interaction = Interaction(true, rightHalf()),
|
|
||||||
val backward: Interaction = Interaction(true, leftHalf()),
|
|
||||||
val custom: CustomInteraction = CustomInteraction(false)
|
|
||||||
) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The tap interaction setting.
|
|
||||||
*
|
|
||||||
* @property enabled True if this interaction is enabled or not.
|
|
||||||
* @property target Defines a rectangle where interaction captured. The rectangle coordinates are relative
|
|
||||||
* (from 0 to 1) and then scaled to the PageCurl bounds.
|
|
||||||
*/
|
|
||||||
@ExperimentalPageCurlApi
|
|
||||||
public data class Interaction(
|
|
||||||
val enabled: Boolean,
|
|
||||||
val target: Rect = Rect.Zero,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The custom tap interaction setting.
|
|
||||||
*
|
|
||||||
* @property enabled True if this interaction is enabled or not.
|
|
||||||
* @property onTap The lambda to invoke to check if tap is handled by custom tap or not. Receives the density
|
|
||||||
* scope, the PageCurl size and tap position. Returns true if tap is handled and false otherwise.
|
|
||||||
*/
|
|
||||||
@ExperimentalPageCurlApi
|
|
||||||
public data class CustomInteraction(
|
|
||||||
val enabled: Boolean,
|
|
||||||
val onTap: Density.(IntSize, Offset) -> Boolean = { _, _ -> false },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The utility function to create a new copy of the [InteractionConfig] with different enabled states of each
|
|
||||||
* interactions.
|
|
||||||
*
|
|
||||||
* @param dragForwardEnabled True to enable forward drag interaction.
|
|
||||||
* @param dragBackwardEnabled True to enable backward drag interaction.
|
|
||||||
* @param tapForwardEnabled True to enable forward tap interaction.
|
|
||||||
* @param tapBackwardEnabled True to enable backward tap interaction.
|
|
||||||
*/
|
|
||||||
@ExperimentalPageCurlApi
|
|
||||||
public fun InteractionConfig.copy(
|
|
||||||
dragForwardEnabled: Boolean = drag.forward.enabled,
|
|
||||||
dragBackwardEnabled: Boolean = drag.backward.enabled,
|
|
||||||
tapForwardEnabled: Boolean = tap.forward.enabled,
|
|
||||||
tapBackwardEnabled: Boolean = tap.backward.enabled,
|
|
||||||
): InteractionConfig = copy(
|
|
||||||
drag = drag.copy(
|
|
||||||
forward = drag.forward.copy(enabled = dragForwardEnabled),
|
|
||||||
backward = drag.backward.copy(enabled = dragBackwardEnabled)
|
|
||||||
),
|
|
||||||
tap = tap.copy(
|
|
||||||
forward = tap.forward.copy(enabled = tapForwardEnabled),
|
|
||||||
backward = tap.backward.copy(enabled = tapBackwardEnabled)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The left half of the PageCurl.
|
|
||||||
*/
|
|
||||||
private fun leftHalf(): Rect = Rect(0.0f, 0.0f, 0.5f, 1.0f)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The right half of the PageCurl.
|
|
||||||
*/
|
|
||||||
private fun rightHalf(): Rect = Rect(0.5f, 0.0f, 1.0f, 1.0f)
|
|
@@ -0,0 +1,304 @@
|
|||||||
|
package eu.wewox.pagecurl.config
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.saveable.listSaver
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Rect
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.Density
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.DpOffset
|
||||||
|
import androidx.compose.ui.unit.IntSize
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a PageCurlConfig with the default properties and memorizes it.
|
||||||
|
*
|
||||||
|
* @param backPageColor Color of the back-page. In majority of use-cases it should be set to the content background
|
||||||
|
* color.
|
||||||
|
* @param backPageContentAlpha The alpha which defines how content is "seen through" the back-page. From 0 (nothing
|
||||||
|
* is visible) to 1 (everything is visible).
|
||||||
|
* @param shadowColor The color of the shadow. In majority of use-cases it should be set to the inverted color to the
|
||||||
|
* content background color. Should be a solid color, see [alpha] to adjust opacity.
|
||||||
|
* @param shadowAlpha The alpha of the [color].
|
||||||
|
* @param shadowRadius Defines how big the shadow is.
|
||||||
|
* @param shadowOffset Defines how shadow is shifted from the page. A little shift may add more realism.
|
||||||
|
* @param dragForwardEnabled True if forward drag interaction is enabled or not.
|
||||||
|
* @param dragBackwardEnabled True if backward drag interaction is enabled or not.
|
||||||
|
* @param tapForwardEnabled True if forward tap interaction is enabled or not.
|
||||||
|
* @param tapBackwardEnabled True if backward tap interaction is enabled or not.
|
||||||
|
* @param tapCustomEnabled True if custom tap interaction is enabled or not, see [onCustomTap].
|
||||||
|
* @param dragForwardInteraction The forward drag interaction setting.
|
||||||
|
* @param dragBackwardInteraction The backward drag interaction setting.
|
||||||
|
* @param tapForwardInteraction The forward tap interaction setting.
|
||||||
|
* @param tapBackwardInteraction The backward tap interaction setting.
|
||||||
|
* @param onCustomTap The lambda to invoke to check if tap is handled by custom tap or not. Receives the density
|
||||||
|
* scope, the PageCurl size and tap position. Returns true if tap is handled and false otherwise.
|
||||||
|
*/
|
||||||
|
@ExperimentalPageCurlApi
|
||||||
|
@Composable
|
||||||
|
public fun rememberPageCurlConfig(
|
||||||
|
backPageColor: Color = Color.White,
|
||||||
|
backPageContentAlpha: Float = 0.1f,
|
||||||
|
shadowColor: Color = Color.Black,
|
||||||
|
shadowAlpha: Float = 0.2f,
|
||||||
|
shadowRadius: Dp = 15.dp,
|
||||||
|
shadowOffset: DpOffset = DpOffset((-5).dp, 0.dp),
|
||||||
|
dragForwardEnabled: Boolean = true,
|
||||||
|
dragBackwardEnabled: Boolean = true,
|
||||||
|
tapForwardEnabled: Boolean = true,
|
||||||
|
tapBackwardEnabled: Boolean = true,
|
||||||
|
tapCustomEnabled: Boolean = true,
|
||||||
|
dragForwardInteraction: PageCurlConfig.DragInteraction = PageCurlConfig.DragInteraction(rightHalf(), leftHalf()),
|
||||||
|
dragBackwardInteraction: PageCurlConfig.DragInteraction = PageCurlConfig.DragInteraction(leftHalf(), rightHalf()),
|
||||||
|
tapForwardInteraction: PageCurlConfig.TapInteraction = PageCurlConfig.TapInteraction(rightHalf()),
|
||||||
|
tapBackwardInteraction: PageCurlConfig.TapInteraction = PageCurlConfig.TapInteraction(leftHalf()),
|
||||||
|
onCustomTap: Density.(IntSize, Offset) -> Boolean = { _, _ -> false },
|
||||||
|
): PageCurlConfig =
|
||||||
|
rememberSaveable(
|
||||||
|
saver = listSaver(
|
||||||
|
save = {
|
||||||
|
listOf(
|
||||||
|
(it.backPageColor.value shr 32).toInt(),
|
||||||
|
it.backPageContentAlpha,
|
||||||
|
(it.shadowColor.value shr 32).toInt(),
|
||||||
|
it.shadowAlpha,
|
||||||
|
it.shadowRadius.value,
|
||||||
|
it.shadowOffset.x.value,
|
||||||
|
it.shadowOffset.y.value,
|
||||||
|
it.dragForwardEnabled,
|
||||||
|
it.dragBackwardEnabled,
|
||||||
|
it.tapForwardEnabled,
|
||||||
|
it.tapBackwardEnabled,
|
||||||
|
it.tapCustomEnabled,
|
||||||
|
it.dragForwardInteraction.start.topLeft.x,
|
||||||
|
it.dragForwardInteraction.start.topLeft.y,
|
||||||
|
it.dragForwardInteraction.start.bottomRight.x,
|
||||||
|
it.dragForwardInteraction.start.bottomRight.y,
|
||||||
|
it.dragForwardInteraction.end.topLeft.x,
|
||||||
|
it.dragForwardInteraction.end.topLeft.y,
|
||||||
|
it.dragForwardInteraction.end.bottomRight.x,
|
||||||
|
it.dragForwardInteraction.end.bottomRight.y,
|
||||||
|
it.dragBackwardInteraction.start.topLeft.x,
|
||||||
|
it.dragBackwardInteraction.start.topLeft.y,
|
||||||
|
it.dragBackwardInteraction.start.bottomRight.x,
|
||||||
|
it.dragBackwardInteraction.start.bottomRight.y,
|
||||||
|
it.dragBackwardInteraction.end.topLeft.x,
|
||||||
|
it.dragBackwardInteraction.end.topLeft.y,
|
||||||
|
it.dragBackwardInteraction.end.bottomRight.x,
|
||||||
|
it.dragBackwardInteraction.end.bottomRight.y,
|
||||||
|
it.tapForwardInteraction.target.topLeft.x,
|
||||||
|
it.tapForwardInteraction.target.topLeft.y,
|
||||||
|
it.tapForwardInteraction.target.bottomRight.x,
|
||||||
|
it.tapForwardInteraction.target.bottomRight.y,
|
||||||
|
it.tapBackwardInteraction.target.topLeft.x,
|
||||||
|
it.tapBackwardInteraction.target.topLeft.y,
|
||||||
|
it.tapBackwardInteraction.target.bottomRight.x,
|
||||||
|
it.tapBackwardInteraction.target.bottomRight.y,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
restore = {
|
||||||
|
PageCurlConfig(
|
||||||
|
Color(it[0] as Int),
|
||||||
|
it[1] as Float,
|
||||||
|
Color(it[2] as Int),
|
||||||
|
it[3] as Float,
|
||||||
|
Dp(it[4] as Float),
|
||||||
|
DpOffset(Dp(it[5] as Float), Dp(it[6] as Float)),
|
||||||
|
it[7] as Boolean,
|
||||||
|
it[8] as Boolean,
|
||||||
|
it[9] as Boolean,
|
||||||
|
it[10] as Boolean,
|
||||||
|
it[11] as Boolean,
|
||||||
|
PageCurlConfig.DragInteraction(
|
||||||
|
Rect(it[12] as Float, it[13] as Float, it[14] as Float, it[15] as Float),
|
||||||
|
Rect(it[16] as Float, it[17] as Float, it[18] as Float, it[19] as Float),
|
||||||
|
),
|
||||||
|
PageCurlConfig.DragInteraction(
|
||||||
|
Rect(it[20] as Float, it[21] as Float, it[22] as Float, it[23] as Float),
|
||||||
|
Rect(it[24] as Float, it[25] as Float, it[26] as Float, it[27] as Float),
|
||||||
|
),
|
||||||
|
PageCurlConfig.TapInteraction(
|
||||||
|
Rect(it[28] as Float, it[29] as Float, it[30] as Float, it[31] as Float),
|
||||||
|
),
|
||||||
|
PageCurlConfig.TapInteraction(
|
||||||
|
Rect(it[32] as Float, it[33] as Float, it[34] as Float, it[35] as Float),
|
||||||
|
),
|
||||||
|
onCustomTap
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
PageCurlConfig(
|
||||||
|
backPageColor = backPageColor,
|
||||||
|
backPageContentAlpha = backPageContentAlpha,
|
||||||
|
shadowColor = shadowColor,
|
||||||
|
shadowAlpha = shadowAlpha,
|
||||||
|
shadowRadius = shadowRadius,
|
||||||
|
shadowOffset = shadowOffset,
|
||||||
|
dragForwardEnabled = dragForwardEnabled,
|
||||||
|
dragBackwardEnabled = dragBackwardEnabled,
|
||||||
|
tapForwardEnabled = tapForwardEnabled,
|
||||||
|
tapBackwardEnabled = tapBackwardEnabled,
|
||||||
|
tapCustomEnabled = tapCustomEnabled,
|
||||||
|
dragForwardInteraction = dragForwardInteraction,
|
||||||
|
dragBackwardInteraction = dragBackwardInteraction,
|
||||||
|
tapForwardInteraction = tapForwardInteraction,
|
||||||
|
tapBackwardInteraction = tapBackwardInteraction,
|
||||||
|
onCustomTap = onCustomTap
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration for PageCurl.
|
||||||
|
*
|
||||||
|
* @param backPageColor Color of the back-page. In majority of use-cases it should be set to the content background
|
||||||
|
* color.
|
||||||
|
* @param backPageContentAlpha The alpha which defines how content is "seen through" the back-page. From 0 (nothing
|
||||||
|
* is visible) to 1 (everything is visible).
|
||||||
|
* @param shadowColor The color of the shadow. In majority of use-cases it should be set to the inverted color to the
|
||||||
|
* content background color. Should be a solid color, see [shadowAlpha] to adjust opacity.
|
||||||
|
* @param shadowAlpha The alpha of the [shadowColor].
|
||||||
|
* @param shadowRadius Defines how big the shadow is.
|
||||||
|
* @param shadowOffset Defines how shadow is shifted from the page. A little shift may add more realism.
|
||||||
|
* @param dragForwardEnabled True if forward drag interaction is enabled or not.
|
||||||
|
* @param dragBackwardEnabled True if backward drag interaction is enabled or not.
|
||||||
|
* @param tapForwardEnabled True if forward tap interaction is enabled or not.
|
||||||
|
* @param tapBackwardEnabled True if backward tap interaction is enabled or not.
|
||||||
|
* @param tapCustomEnabled True if custom tap interaction is enabled or not, see [onCustomTap].
|
||||||
|
* @param dragForwardInteraction The forward drag interaction setting.
|
||||||
|
* @param dragBackwardInteraction The backward drag interaction setting.
|
||||||
|
* @param tapForwardInteraction The forward tap interaction setting.
|
||||||
|
* @param tapBackwardInteraction The backward tap interaction setting.
|
||||||
|
* @param onCustomTap The lambda to invoke to check if tap is handled by custom tap or not. Receives the density
|
||||||
|
* scope, the PageCurl size and tap position. Returns true if tap is handled and false otherwise.
|
||||||
|
*/
|
||||||
|
@ExperimentalPageCurlApi
|
||||||
|
public class PageCurlConfig(
|
||||||
|
backPageColor: Color,
|
||||||
|
backPageContentAlpha: Float,
|
||||||
|
shadowColor: Color,
|
||||||
|
shadowAlpha: Float,
|
||||||
|
shadowRadius: Dp,
|
||||||
|
shadowOffset: DpOffset,
|
||||||
|
dragForwardEnabled: Boolean,
|
||||||
|
dragBackwardEnabled: Boolean,
|
||||||
|
tapForwardEnabled: Boolean,
|
||||||
|
tapBackwardEnabled: Boolean,
|
||||||
|
tapCustomEnabled: Boolean,
|
||||||
|
dragForwardInteraction: DragInteraction,
|
||||||
|
dragBackwardInteraction: DragInteraction,
|
||||||
|
tapForwardInteraction: TapInteraction,
|
||||||
|
tapBackwardInteraction: TapInteraction,
|
||||||
|
public val onCustomTap: Density.(IntSize, Offset) -> Boolean,
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* The color of the back-page. In majority of use-cases it should be set to the content background color.
|
||||||
|
*/
|
||||||
|
public var backPageColor: Color by mutableStateOf(backPageColor)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The alpha which defines how content is "seen through" the back-page. From 0 (nothing is visible) to
|
||||||
|
* 1 (everything is visible).
|
||||||
|
*/
|
||||||
|
public var backPageContentAlpha: Float by mutableStateOf(backPageContentAlpha)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color of the shadow. In majority of use-cases it should be set to the inverted color to the content
|
||||||
|
* background color. Should be a solid color, see [shadowAlpha] to adjust opacity.
|
||||||
|
*/
|
||||||
|
public var shadowColor: Color by mutableStateOf(shadowColor)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The alpha of the [shadowColor].
|
||||||
|
*/
|
||||||
|
public var shadowAlpha: Float by mutableStateOf(shadowAlpha)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines how big the shadow is.
|
||||||
|
*/
|
||||||
|
public var shadowRadius: Dp by mutableStateOf(shadowRadius)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines how shadow is shifted from the page. A little shift may add more realism.
|
||||||
|
*/
|
||||||
|
public var shadowOffset: DpOffset by mutableStateOf(shadowOffset)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if forward drag interaction is enabled or not.
|
||||||
|
*/
|
||||||
|
public var dragForwardEnabled: Boolean by mutableStateOf(dragForwardEnabled)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if backward drag interaction is enabled or not.
|
||||||
|
*/
|
||||||
|
public var dragBackwardEnabled: Boolean by mutableStateOf(dragBackwardEnabled)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if forward tap interaction is enabled or not.
|
||||||
|
*/
|
||||||
|
public var tapForwardEnabled: Boolean by mutableStateOf(tapForwardEnabled)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if backward tap interaction is enabled or not.
|
||||||
|
*/
|
||||||
|
public var tapBackwardEnabled: Boolean by mutableStateOf(tapBackwardEnabled)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if custom tap interaction is enabled or not, see [onCustomTap].
|
||||||
|
*/
|
||||||
|
public var tapCustomEnabled: Boolean by mutableStateOf(tapCustomEnabled)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The forward drag interaction setting.
|
||||||
|
*/
|
||||||
|
public var dragForwardInteraction: DragInteraction by mutableStateOf(dragForwardInteraction)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The backward drag interaction setting.
|
||||||
|
*/
|
||||||
|
public var dragBackwardInteraction: DragInteraction by mutableStateOf(dragBackwardInteraction)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The forward tap interaction setting.
|
||||||
|
*/
|
||||||
|
public var tapForwardInteraction: TapInteraction by mutableStateOf(tapForwardInteraction)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The backward tap interaction setting.
|
||||||
|
*/
|
||||||
|
public var tapBackwardInteraction: TapInteraction by mutableStateOf(tapBackwardInteraction)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The drag interaction setting.
|
||||||
|
*
|
||||||
|
* @property start Defines a rectangle where interaction should start. The rectangle coordinates are relative
|
||||||
|
* (from 0 to 1) and then scaled to the PageCurl bounds.
|
||||||
|
* @property end Defines a rectangle where interaction should end. The rectangle coordinates are relative
|
||||||
|
* (from 0 to 1) and then scaled to the PageCurl bounds.
|
||||||
|
*/
|
||||||
|
public data class DragInteraction(val start: Rect, val end: Rect)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tap interaction setting.
|
||||||
|
*
|
||||||
|
* @property target Defines a rectangle where interaction captured. The rectangle coordinates are relative
|
||||||
|
* (from 0 to 1) and then scaled to the PageCurl bounds.
|
||||||
|
*/
|
||||||
|
public data class TapInteraction(val target: Rect)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The left half of the PageCurl.
|
||||||
|
*/
|
||||||
|
private fun leftHalf(): Rect = Rect(0.0f, 0.0f, 0.5f, 1.0f)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The right half of the PageCurl.
|
||||||
|
*/
|
||||||
|
private fun rightHalf(): Rect = Rect(0.5f, 0.0f, 1.0f, 1.0f)
|
@@ -21,7 +21,7 @@ import androidx.compose.ui.graphics.nativeCanvas
|
|||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
||||||
import eu.wewox.pagecurl.config.CurlConfig
|
import eu.wewox.pagecurl.config.PageCurlConfig
|
||||||
import eu.wewox.pagecurl.utils.Polygon
|
import eu.wewox.pagecurl.utils.Polygon
|
||||||
import eu.wewox.pagecurl.utils.lineLineIntersection
|
import eu.wewox.pagecurl.utils.lineLineIntersection
|
||||||
import eu.wewox.pagecurl.utils.rotate
|
import eu.wewox.pagecurl.utils.rotate
|
||||||
@@ -30,7 +30,7 @@ import kotlin.math.atan2
|
|||||||
|
|
||||||
@ExperimentalPageCurlApi
|
@ExperimentalPageCurlApi
|
||||||
internal fun Modifier.drawCurl(
|
internal fun Modifier.drawCurl(
|
||||||
config: CurlConfig = CurlConfig(),
|
config: PageCurlConfig,
|
||||||
posA: Offset,
|
posA: Offset,
|
||||||
posB: Offset,
|
posB: Offset,
|
||||||
): Modifier = drawWithCache {
|
): Modifier = drawWithCache {
|
||||||
@@ -114,7 +114,7 @@ private fun CacheDrawScope.prepareClippedContent(
|
|||||||
|
|
||||||
@ExperimentalPageCurlApi
|
@ExperimentalPageCurlApi
|
||||||
private fun CacheDrawScope.prepareCurl(
|
private fun CacheDrawScope.prepareCurl(
|
||||||
config: CurlConfig,
|
config: PageCurlConfig,
|
||||||
topCurlOffset: Offset,
|
topCurlOffset: Offset,
|
||||||
bottomCurlOffset: Offset,
|
bottomCurlOffset: Offset,
|
||||||
): ContentDrawScope.() -> Unit {
|
): ContentDrawScope.() -> Unit {
|
||||||
@@ -176,8 +176,8 @@ private fun CacheDrawScope.prepareCurl(
|
|||||||
clipPath(polygon.toPath()) {
|
clipPath(polygon.toPath()) {
|
||||||
this@result.drawContent()
|
this@result.drawContent()
|
||||||
|
|
||||||
val overlayAlpha = 1f - config.backPage.contentAlpha
|
val overlayAlpha = 1f - config.backPageContentAlpha
|
||||||
drawRect(config.backPage.color.copy(alpha = overlayAlpha))
|
drawRect(config.backPageColor.copy(alpha = overlayAlpha))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,22 +185,20 @@ private fun CacheDrawScope.prepareCurl(
|
|||||||
|
|
||||||
@ExperimentalPageCurlApi
|
@ExperimentalPageCurlApi
|
||||||
private fun CacheDrawScope.prepareShadow(
|
private fun CacheDrawScope.prepareShadow(
|
||||||
config: CurlConfig,
|
config: PageCurlConfig,
|
||||||
polygon: Polygon,
|
polygon: Polygon,
|
||||||
angle: Float
|
angle: Float
|
||||||
): ContentDrawScope.() -> Unit {
|
): ContentDrawScope.() -> Unit {
|
||||||
val shadow = config.shadow
|
|
||||||
|
|
||||||
// Quick exit if no shadow is requested
|
// Quick exit if no shadow is requested
|
||||||
if (shadow.alpha == 0f || shadow.radius == 0.dp) {
|
if (config.shadowAlpha == 0f || config.shadowRadius == 0.dp) {
|
||||||
return { /* No shadow is requested */ }
|
return { /* No shadow is requested */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare shadow parameters
|
// Prepare shadow parameters
|
||||||
val radius = shadow.radius.toPx()
|
val radius = config.shadowRadius.toPx()
|
||||||
val shadowColor = shadow.color.copy(alpha = shadow.alpha).toArgb()
|
val shadowColor = config.shadowColor.copy(alpha = config.shadowAlpha).toArgb()
|
||||||
val transparent = shadow.color.copy(alpha = 0f).toArgb()
|
val transparent = config.shadowColor.copy(alpha = 0f).toArgb()
|
||||||
val shadowOffset = Offset(-shadow.offset.x.toPx(), shadow.offset.y.toPx())
|
val shadowOffset = Offset(-config.shadowOffset.x.toPx(), config.shadowOffset.y.toPx())
|
||||||
.rotate(2 * Math.PI.toFloat() - angle)
|
.rotate(2 * Math.PI.toFloat() - angle)
|
||||||
|
|
||||||
// Prepare shadow paint with a shadow layer
|
// Prepare shadow paint with a shadow layer
|
||||||
@@ -208,7 +206,7 @@ private fun CacheDrawScope.prepareShadow(
|
|||||||
val frameworkPaint = asFrameworkPaint()
|
val frameworkPaint = asFrameworkPaint()
|
||||||
frameworkPaint.color = transparent
|
frameworkPaint.color = transparent
|
||||||
frameworkPaint.setShadowLayer(
|
frameworkPaint.setShadowLayer(
|
||||||
shadow.radius.toPx(),
|
config.shadowRadius.toPx(),
|
||||||
shadowOffset.x,
|
shadowOffset.x,
|
||||||
shadowOffset.y,
|
shadowOffset.y,
|
||||||
shadowColor
|
shadowColor
|
||||||
|
@@ -10,10 +10,10 @@ import androidx.compose.foundation.gestures.drag
|
|||||||
import androidx.compose.foundation.gestures.forEachGesture
|
import androidx.compose.foundation.gestures.forEachGesture
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Rect
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.input.pointer.util.VelocityTracker
|
import androidx.compose.ui.input.pointer.util.VelocityTracker
|
||||||
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
||||||
import eu.wewox.pagecurl.config.InteractionConfig
|
|
||||||
import eu.wewox.pagecurl.utils.multiply
|
import eu.wewox.pagecurl.utils.multiply
|
||||||
import eu.wewox.pagecurl.utils.rotate
|
import eu.wewox.pagecurl.utils.rotate
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -25,20 +25,22 @@ internal fun Modifier.curlGesture(
|
|||||||
state: PageCurlState.InternalState,
|
state: PageCurlState.InternalState,
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
direction: InteractionConfig.Drag.Interaction,
|
targetStart: Rect,
|
||||||
start: Edge,
|
targetEnd: Rect,
|
||||||
end: Edge,
|
edgeStart: Edge,
|
||||||
|
edgeEnd: Edge,
|
||||||
edge: Animatable<Edge, AnimationVector4D>,
|
edge: Animatable<Edge, AnimationVector4D>,
|
||||||
onChange: () -> Unit,
|
onChange: () -> Unit,
|
||||||
): Modifier =
|
): Modifier =
|
||||||
curlGesture(
|
curlGesture(
|
||||||
key = state,
|
key = state,
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
direction = direction,
|
targetStart = targetStart,
|
||||||
|
targetEnd = targetEnd,
|
||||||
onStart = {
|
onStart = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
state.animateJob?.cancel()
|
state.animateJob?.cancel()
|
||||||
edge.snapTo(start)
|
edge.snapTo(edgeStart)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onCurl = { a, b ->
|
onCurl = { a, b ->
|
||||||
@@ -49,16 +51,16 @@ internal fun Modifier.curlGesture(
|
|||||||
onEnd = {
|
onEnd = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
try {
|
try {
|
||||||
edge.animateTo(end)
|
edge.animateTo(edgeEnd)
|
||||||
} finally {
|
} finally {
|
||||||
onChange()
|
onChange()
|
||||||
edge.snapTo(start)
|
edge.snapTo(edgeStart)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onCancel = {
|
onCancel = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
edge.animateTo(start)
|
edge.animateTo(edgeStart)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -67,7 +69,8 @@ internal fun Modifier.curlGesture(
|
|||||||
internal fun Modifier.curlGesture(
|
internal fun Modifier.curlGesture(
|
||||||
key: Any?,
|
key: Any?,
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
direction: InteractionConfig.Drag.Interaction,
|
targetStart: Rect,
|
||||||
|
targetEnd: Rect,
|
||||||
onStart: () -> Unit,
|
onStart: () -> Unit,
|
||||||
onCurl: (Offset, Offset) -> Unit,
|
onCurl: (Offset, Offset) -> Unit,
|
||||||
onEnd: () -> Unit,
|
onEnd: () -> Unit,
|
||||||
@@ -79,8 +82,8 @@ internal fun Modifier.curlGesture(
|
|||||||
|
|
||||||
// Use velocity tracker to support flings
|
// Use velocity tracker to support flings
|
||||||
val velocityTracker = VelocityTracker()
|
val velocityTracker = VelocityTracker()
|
||||||
val startRect by lazy { direction.start.multiply(size) }
|
val startRect by lazy { targetStart.multiply(size) }
|
||||||
val endRect by lazy { direction.end.multiply(size) }
|
val endRect by lazy { targetEnd.multiply(size) }
|
||||||
forEachGesture {
|
forEachGesture {
|
||||||
awaitPointerEventScope {
|
awaitPointerEventScope {
|
||||||
val down = awaitFirstDown(requireUnconsumed = false)
|
val down = awaitFirstDown(requireUnconsumed = false)
|
||||||
|
@@ -15,7 +15,6 @@ import eu.wewox.pagecurl.config.PageCurlConfig
|
|||||||
*
|
*
|
||||||
* @param state The state of the PageCurl. Use this to programmatically change the current page or observe changes.
|
* @param state The state of the PageCurl. Use this to programmatically change the current page or observe changes.
|
||||||
* @param modifier The modifier for this composable.
|
* @param modifier The modifier for this composable.
|
||||||
* @param config The configuration for PageCurl. Configures how page curl looks like and interacts.
|
|
||||||
* @param content The content lambda to provide the page composable. Receives the page number.
|
* @param content The content lambda to provide the page composable. Receives the page number.
|
||||||
*/
|
*/
|
||||||
@ExperimentalPageCurlApi
|
@ExperimentalPageCurlApi
|
||||||
@@ -23,7 +22,6 @@ import eu.wewox.pagecurl.config.PageCurlConfig
|
|||||||
public fun PageCurl(
|
public fun PageCurl(
|
||||||
state: PageCurlState,
|
state: PageCurlState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
config: PageCurlConfig = PageCurlConfig(),
|
|
||||||
content: @Composable (Int) -> Unit
|
content: @Composable (Int) -> Unit
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
@@ -34,32 +32,35 @@ public fun PageCurl(
|
|||||||
|
|
||||||
val internalState by rememberUpdatedState(state.internalState ?: return@BoxWithConstraints)
|
val internalState by rememberUpdatedState(state.internalState ?: return@BoxWithConstraints)
|
||||||
|
|
||||||
|
val config by rememberUpdatedState(state.config)
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.curlGesture(
|
.curlGesture(
|
||||||
state = internalState,
|
state = internalState,
|
||||||
enabled = config.interaction.drag.forward.enabled && updatedCurrent < state.max - 1,
|
enabled = state.config.dragForwardEnabled && updatedCurrent < state.max - 1,
|
||||||
scope = scope,
|
scope = scope,
|
||||||
direction = config.interaction.drag.forward,
|
targetStart = config.dragForwardInteraction.start,
|
||||||
start = internalState.rightEdge,
|
targetEnd = config.dragForwardInteraction.end,
|
||||||
end = internalState.leftEdge,
|
edgeStart = internalState.rightEdge,
|
||||||
|
edgeEnd = internalState.leftEdge,
|
||||||
edge = internalState.forward,
|
edge = internalState.forward,
|
||||||
onChange = { state.current = updatedCurrent + 1 }
|
onChange = { state.current = updatedCurrent + 1 }
|
||||||
)
|
)
|
||||||
.curlGesture(
|
.curlGesture(
|
||||||
state = internalState,
|
state = internalState,
|
||||||
enabled = config.interaction.drag.backward.enabled && updatedCurrent > 0,
|
enabled = state.config.dragBackwardEnabled && updatedCurrent > 0,
|
||||||
scope = scope,
|
scope = scope,
|
||||||
direction = config.interaction.drag.backward,
|
targetStart = config.dragBackwardInteraction.start,
|
||||||
start = internalState.leftEdge,
|
targetEnd = config.dragBackwardInteraction.end,
|
||||||
end = internalState.rightEdge,
|
edgeStart = internalState.leftEdge,
|
||||||
|
edgeEnd = internalState.rightEdge,
|
||||||
edge = internalState.backward,
|
edge = internalState.backward,
|
||||||
onChange = { state.current = updatedCurrent - 1 }
|
onChange = { state.current = updatedCurrent - 1 }
|
||||||
)
|
)
|
||||||
.tapGesture(
|
.tapGesture(
|
||||||
state = internalState,
|
config = config,
|
||||||
scope = scope,
|
scope = scope,
|
||||||
interaction = config.interaction.tap,
|
|
||||||
onTapForward = state::next,
|
onTapForward = state::next,
|
||||||
onTapBackward = state::prev,
|
onTapBackward = state::prev,
|
||||||
)
|
)
|
||||||
@@ -69,13 +70,13 @@ public fun PageCurl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (updatedCurrent < state.max) {
|
if (updatedCurrent < state.max) {
|
||||||
Box(Modifier.drawCurl(config.curl, internalState.forward.value.top, internalState.forward.value.bottom)) {
|
Box(Modifier.drawCurl(config, internalState.forward.value.top, internalState.forward.value.bottom)) {
|
||||||
content(updatedCurrent)
|
content(updatedCurrent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updatedCurrent > 0) {
|
if (updatedCurrent > 0) {
|
||||||
Box(Modifier.drawCurl(config.curl, internalState.backward.value.top, internalState.backward.value.bottom)) {
|
Box(Modifier.drawCurl(config, internalState.backward.value.top, internalState.backward.value.bottom)) {
|
||||||
content(updatedCurrent - 1)
|
content(updatedCurrent - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,8 @@ import androidx.compose.ui.geometry.Offset
|
|||||||
import androidx.compose.ui.geometry.Size
|
import androidx.compose.ui.geometry.Size
|
||||||
import androidx.compose.ui.unit.Constraints
|
import androidx.compose.ui.unit.Constraints
|
||||||
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
||||||
|
import eu.wewox.pagecurl.config.PageCurlConfig
|
||||||
|
import eu.wewox.pagecurl.config.rememberPageCurlConfig
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.NonCancellable
|
import kotlinx.coroutines.NonCancellable
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
@@ -27,6 +29,7 @@ import kotlinx.coroutines.withContext
|
|||||||
*
|
*
|
||||||
* @param max The max number of pages.
|
* @param max The max number of pages.
|
||||||
* @param initialCurrent The initial current page.
|
* @param initialCurrent The initial current page.
|
||||||
|
* @param config The configuration for PageCurl.
|
||||||
* @return The remembered [PageCurlState].
|
* @return The remembered [PageCurlState].
|
||||||
*/
|
*/
|
||||||
@ExperimentalPageCurlApi
|
@ExperimentalPageCurlApi
|
||||||
@@ -34,6 +37,7 @@ import kotlinx.coroutines.withContext
|
|||||||
public fun rememberPageCurlState(
|
public fun rememberPageCurlState(
|
||||||
max: Int,
|
max: Int,
|
||||||
initialCurrent: Int = 0,
|
initialCurrent: Int = 0,
|
||||||
|
config: PageCurlConfig = rememberPageCurlConfig()
|
||||||
): PageCurlState =
|
): PageCurlState =
|
||||||
rememberSaveable(
|
rememberSaveable(
|
||||||
max, initialCurrent,
|
max, initialCurrent,
|
||||||
@@ -41,15 +45,17 @@ public fun rememberPageCurlState(
|
|||||||
save = { it.current },
|
save = { it.current },
|
||||||
restore = {
|
restore = {
|
||||||
PageCurlState(
|
PageCurlState(
|
||||||
max = it,
|
max = max,
|
||||||
initialCurrent = initialCurrent
|
initialCurrent = it,
|
||||||
|
config = config,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
PageCurlState(
|
PageCurlState(
|
||||||
max = max,
|
max = max,
|
||||||
initialCurrent = initialCurrent
|
initialCurrent = initialCurrent,
|
||||||
|
config = config,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +68,7 @@ public fun rememberPageCurlState(
|
|||||||
@ExperimentalPageCurlApi
|
@ExperimentalPageCurlApi
|
||||||
public class PageCurlState(
|
public class PageCurlState(
|
||||||
public val max: Int,
|
public val max: Int,
|
||||||
|
public val config: PageCurlConfig,
|
||||||
initialCurrent: Int = 0,
|
initialCurrent: Int = 0,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
|
@@ -6,19 +6,18 @@ import androidx.compose.foundation.gestures.waitForUpOrCancellation
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
import eu.wewox.pagecurl.ExperimentalPageCurlApi
|
||||||
import eu.wewox.pagecurl.config.InteractionConfig
|
import eu.wewox.pagecurl.config.PageCurlConfig
|
||||||
import eu.wewox.pagecurl.utils.multiply
|
import eu.wewox.pagecurl.utils.multiply
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@ExperimentalPageCurlApi
|
@ExperimentalPageCurlApi
|
||||||
internal fun Modifier.tapGesture(
|
internal fun Modifier.tapGesture(
|
||||||
state: PageCurlState.InternalState,
|
config: PageCurlConfig,
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
interaction: InteractionConfig.Tap,
|
|
||||||
onTapForward: suspend () -> Unit,
|
onTapForward: suspend () -> Unit,
|
||||||
onTapBackward: suspend () -> Unit,
|
onTapBackward: suspend () -> Unit,
|
||||||
): Modifier = pointerInput(interaction, state) {
|
): Modifier = pointerInput(config) {
|
||||||
forEachGesture {
|
forEachGesture {
|
||||||
awaitPointerEventScope {
|
awaitPointerEventScope {
|
||||||
val down = awaitFirstDown().also { it.consume() }
|
val down = awaitFirstDown().also { it.consume() }
|
||||||
@@ -28,18 +27,18 @@ internal fun Modifier.tapGesture(
|
|||||||
return@awaitPointerEventScope
|
return@awaitPointerEventScope
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interaction.custom.enabled && interaction.custom.onTap(this, size, up.position)) {
|
if (config.tapCustomEnabled && config.onCustomTap(this, size, up.position)) {
|
||||||
return@awaitPointerEventScope
|
return@awaitPointerEventScope
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interaction.forward.enabled && interaction.forward.target.multiply(size).contains(up.position)) {
|
if (config.tapForwardEnabled && config.tapForwardInteraction.target.multiply(size).contains(up.position)) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
onTapForward()
|
onTapForward()
|
||||||
}
|
}
|
||||||
return@awaitPointerEventScope
|
return@awaitPointerEventScope
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interaction.backward.enabled && interaction.backward.target.multiply(size).contains(up.position)) {
|
if (config.tapBackwardEnabled && config.tapBackwardInteraction.target.multiply(size).contains(up.position)) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
onTapBackward()
|
onTapBackward()
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user