Refactor page curl config

This commit is contained in:
Oleksandr Balan
2022-08-14 10:54:49 +02:00
parent 53dcc8401f
commit 0225c2700c
10 changed files with 418 additions and 313 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -21,7 +21,7 @@ import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.unit.dp
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.lineLineIntersection
import eu.wewox.pagecurl.utils.rotate
@@ -30,7 +30,7 @@ import kotlin.math.atan2
@ExperimentalPageCurlApi
internal fun Modifier.drawCurl(
config: CurlConfig = CurlConfig(),
config: PageCurlConfig,
posA: Offset,
posB: Offset,
): Modifier = drawWithCache {
@@ -114,7 +114,7 @@ private fun CacheDrawScope.prepareClippedContent(
@ExperimentalPageCurlApi
private fun CacheDrawScope.prepareCurl(
config: CurlConfig,
config: PageCurlConfig,
topCurlOffset: Offset,
bottomCurlOffset: Offset,
): ContentDrawScope.() -> Unit {
@@ -176,8 +176,8 @@ private fun CacheDrawScope.prepareCurl(
clipPath(polygon.toPath()) {
this@result.drawContent()
val overlayAlpha = 1f - config.backPage.contentAlpha
drawRect(config.backPage.color.copy(alpha = overlayAlpha))
val overlayAlpha = 1f - config.backPageContentAlpha
drawRect(config.backPageColor.copy(alpha = overlayAlpha))
}
}
}
@@ -185,22 +185,20 @@ private fun CacheDrawScope.prepareCurl(
@ExperimentalPageCurlApi
private fun CacheDrawScope.prepareShadow(
config: CurlConfig,
config: PageCurlConfig,
polygon: Polygon,
angle: Float
): ContentDrawScope.() -> Unit {
val shadow = config.shadow
// 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 */ }
}
// Prepare shadow parameters
val radius = shadow.radius.toPx()
val shadowColor = shadow.color.copy(alpha = shadow.alpha).toArgb()
val transparent = shadow.color.copy(alpha = 0f).toArgb()
val shadowOffset = Offset(-shadow.offset.x.toPx(), shadow.offset.y.toPx())
val radius = config.shadowRadius.toPx()
val shadowColor = config.shadowColor.copy(alpha = config.shadowAlpha).toArgb()
val transparent = config.shadowColor.copy(alpha = 0f).toArgb()
val shadowOffset = Offset(-config.shadowOffset.x.toPx(), config.shadowOffset.y.toPx())
.rotate(2 * Math.PI.toFloat() - angle)
// Prepare shadow paint with a shadow layer
@@ -208,7 +206,7 @@ private fun CacheDrawScope.prepareShadow(
val frameworkPaint = asFrameworkPaint()
frameworkPaint.color = transparent
frameworkPaint.setShadowLayer(
shadow.radius.toPx(),
config.shadowRadius.toPx(),
shadowOffset.x,
shadowOffset.y,
shadowColor

View File

@@ -10,10 +10,10 @@ import androidx.compose.foundation.gestures.drag
import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.ui.Modifier
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.util.VelocityTracker
import eu.wewox.pagecurl.ExperimentalPageCurlApi
import eu.wewox.pagecurl.config.InteractionConfig
import eu.wewox.pagecurl.utils.multiply
import eu.wewox.pagecurl.utils.rotate
import kotlinx.coroutines.CoroutineScope
@@ -25,20 +25,22 @@ internal fun Modifier.curlGesture(
state: PageCurlState.InternalState,
enabled: Boolean,
scope: CoroutineScope,
direction: InteractionConfig.Drag.Interaction,
start: Edge,
end: Edge,
targetStart: Rect,
targetEnd: Rect,
edgeStart: Edge,
edgeEnd: Edge,
edge: Animatable<Edge, AnimationVector4D>,
onChange: () -> Unit,
): Modifier =
curlGesture(
key = state,
enabled = enabled,
direction = direction,
targetStart = targetStart,
targetEnd = targetEnd,
onStart = {
scope.launch {
state.animateJob?.cancel()
edge.snapTo(start)
edge.snapTo(edgeStart)
}
},
onCurl = { a, b ->
@@ -49,16 +51,16 @@ internal fun Modifier.curlGesture(
onEnd = {
scope.launch {
try {
edge.animateTo(end)
edge.animateTo(edgeEnd)
} finally {
onChange()
edge.snapTo(start)
edge.snapTo(edgeStart)
}
}
},
onCancel = {
scope.launch {
edge.animateTo(start)
edge.animateTo(edgeStart)
}
},
)
@@ -67,7 +69,8 @@ internal fun Modifier.curlGesture(
internal fun Modifier.curlGesture(
key: Any?,
enabled: Boolean,
direction: InteractionConfig.Drag.Interaction,
targetStart: Rect,
targetEnd: Rect,
onStart: () -> Unit,
onCurl: (Offset, Offset) -> Unit,
onEnd: () -> Unit,
@@ -79,8 +82,8 @@ internal fun Modifier.curlGesture(
// Use velocity tracker to support flings
val velocityTracker = VelocityTracker()
val startRect by lazy { direction.start.multiply(size) }
val endRect by lazy { direction.end.multiply(size) }
val startRect by lazy { targetStart.multiply(size) }
val endRect by lazy { targetEnd.multiply(size) }
forEachGesture {
awaitPointerEventScope {
val down = awaitFirstDown(requireUnconsumed = false)

View File

@@ -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 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.
*/
@ExperimentalPageCurlApi
@@ -23,7 +22,6 @@ import eu.wewox.pagecurl.config.PageCurlConfig
public fun PageCurl(
state: PageCurlState,
modifier: Modifier = Modifier,
config: PageCurlConfig = PageCurlConfig(),
content: @Composable (Int) -> Unit
) {
val scope = rememberCoroutineScope()
@@ -34,32 +32,35 @@ public fun PageCurl(
val internalState by rememberUpdatedState(state.internalState ?: return@BoxWithConstraints)
val config by rememberUpdatedState(state.config)
Box(
Modifier
.curlGesture(
state = internalState,
enabled = config.interaction.drag.forward.enabled && updatedCurrent < state.max - 1,
enabled = state.config.dragForwardEnabled && updatedCurrent < state.max - 1,
scope = scope,
direction = config.interaction.drag.forward,
start = internalState.rightEdge,
end = internalState.leftEdge,
targetStart = config.dragForwardInteraction.start,
targetEnd = config.dragForwardInteraction.end,
edgeStart = internalState.rightEdge,
edgeEnd = internalState.leftEdge,
edge = internalState.forward,
onChange = { state.current = updatedCurrent + 1 }
)
.curlGesture(
state = internalState,
enabled = config.interaction.drag.backward.enabled && updatedCurrent > 0,
enabled = state.config.dragBackwardEnabled && updatedCurrent > 0,
scope = scope,
direction = config.interaction.drag.backward,
start = internalState.leftEdge,
end = internalState.rightEdge,
targetStart = config.dragBackwardInteraction.start,
targetEnd = config.dragBackwardInteraction.end,
edgeStart = internalState.leftEdge,
edgeEnd = internalState.rightEdge,
edge = internalState.backward,
onChange = { state.current = updatedCurrent - 1 }
)
.tapGesture(
state = internalState,
config = config,
scope = scope,
interaction = config.interaction.tap,
onTapForward = state::next,
onTapBackward = state::prev,
)
@@ -69,13 +70,13 @@ public fun PageCurl(
}
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)
}
}
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)
}
}

View File

@@ -16,6 +16,8 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.unit.Constraints
import eu.wewox.pagecurl.ExperimentalPageCurlApi
import eu.wewox.pagecurl.config.PageCurlConfig
import eu.wewox.pagecurl.config.rememberPageCurlConfig
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.coroutineScope
@@ -27,6 +29,7 @@ import kotlinx.coroutines.withContext
*
* @param max The max number of pages.
* @param initialCurrent The initial current page.
* @param config The configuration for PageCurl.
* @return The remembered [PageCurlState].
*/
@ExperimentalPageCurlApi
@@ -34,6 +37,7 @@ import kotlinx.coroutines.withContext
public fun rememberPageCurlState(
max: Int,
initialCurrent: Int = 0,
config: PageCurlConfig = rememberPageCurlConfig()
): PageCurlState =
rememberSaveable(
max, initialCurrent,
@@ -41,15 +45,17 @@ public fun rememberPageCurlState(
save = { it.current },
restore = {
PageCurlState(
max = it,
initialCurrent = initialCurrent
max = max,
initialCurrent = it,
config = config,
)
}
)
) {
PageCurlState(
max = max,
initialCurrent = initialCurrent
initialCurrent = initialCurrent,
config = config,
)
}
@@ -62,6 +68,7 @@ public fun rememberPageCurlState(
@ExperimentalPageCurlApi
public class PageCurlState(
public val max: Int,
public val config: PageCurlConfig,
initialCurrent: Int = 0,
) {
/**

View File

@@ -6,19 +6,18 @@ import androidx.compose.foundation.gestures.waitForUpOrCancellation
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
import eu.wewox.pagecurl.ExperimentalPageCurlApi
import eu.wewox.pagecurl.config.InteractionConfig
import eu.wewox.pagecurl.config.PageCurlConfig
import eu.wewox.pagecurl.utils.multiply
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@ExperimentalPageCurlApi
internal fun Modifier.tapGesture(
state: PageCurlState.InternalState,
config: PageCurlConfig,
scope: CoroutineScope,
interaction: InteractionConfig.Tap,
onTapForward: suspend () -> Unit,
onTapBackward: suspend () -> Unit,
): Modifier = pointerInput(interaction, state) {
): Modifier = pointerInput(config) {
forEachGesture {
awaitPointerEventScope {
val down = awaitFirstDown().also { it.consume() }
@@ -28,18 +27,18 @@ internal fun Modifier.tapGesture(
return@awaitPointerEventScope
}
if (interaction.custom.enabled && interaction.custom.onTap(this, size, up.position)) {
if (config.tapCustomEnabled && config.onCustomTap(this, size, up.position)) {
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 {
onTapForward()
}
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 {
onTapBackward()
}