mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-15 13:37:29 +01:00
MangaController overhaul (#7244)
This commit is contained in:
101
app/src/main/java/eu/kanade/presentation/components/Button.kt
Normal file
101
app/src/main/java/eu/kanade/presentation/components/Button.kt
Normal file
@@ -0,0 +1,101 @@
|
||||
package eu.kanade.presentation.components
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ButtonElevation
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.material3.Shapes
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun TextButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
elevation: ButtonElevation? = null,
|
||||
shape: Shape = Shapes.Full,
|
||||
border: BorderStroke? = null,
|
||||
colors: ButtonColors = ButtonDefaults.textButtonColors(),
|
||||
contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding,
|
||||
content: @Composable RowScope.() -> Unit,
|
||||
) =
|
||||
Button(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
onLongClick = onLongClick,
|
||||
enabled = enabled,
|
||||
interactionSource = interactionSource,
|
||||
elevation = elevation,
|
||||
shape = shape,
|
||||
border = border,
|
||||
colors = colors,
|
||||
contentPadding = contentPadding,
|
||||
content = content,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun Button(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
|
||||
shape: Shape = Shapes.Full,
|
||||
border: BorderStroke? = null,
|
||||
colors: ButtonColors = ButtonDefaults.buttonColors(),
|
||||
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
|
||||
content: @Composable RowScope.() -> Unit,
|
||||
) {
|
||||
val containerColor = colors.containerColor(enabled).value
|
||||
val contentColor = colors.contentColor(enabled).value
|
||||
val shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp
|
||||
val tonalElevation = elevation?.tonalElevation(enabled, interactionSource)?.value ?: 0.dp
|
||||
|
||||
Surface(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
onLongClick = onLongClick,
|
||||
shape = shape,
|
||||
color = containerColor,
|
||||
contentColor = contentColor,
|
||||
tonalElevation = tonalElevation,
|
||||
shadowElevation = shadowElevation,
|
||||
border = border,
|
||||
interactionSource = interactionSource,
|
||||
enabled = enabled,
|
||||
) {
|
||||
CompositionLocalProvider(LocalContentColor provides contentColor) {
|
||||
ProvideTextStyle(value = MaterialTheme.typography.labelLarge) {
|
||||
Row(
|
||||
Modifier.defaultMinSize(
|
||||
minWidth = ButtonDefaults.MinWidth,
|
||||
minHeight = ButtonDefaults.MinHeight,
|
||||
)
|
||||
.padding(contentPadding),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package eu.kanade.presentation.components
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.CubicBezierEasing
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.expandHorizontally
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkHorizontally
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.FloatingActionButtonDefaults
|
||||
import androidx.compose.material3.FloatingActionButtonElevation
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun ExtendedFloatingActionButton(
|
||||
text: @Composable () -> Unit,
|
||||
icon: @Composable () -> Unit,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
expanded: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
shape: Shape = MaterialTheme.shapes.large,
|
||||
containerColor: Color = MaterialTheme.colorScheme.primaryContainer,
|
||||
contentColor: Color = contentColorFor(containerColor),
|
||||
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
|
||||
) {
|
||||
val minWidth by animateDpAsState(if (expanded) ExtendedFabMinimumWidth else FabContainerWidth)
|
||||
FloatingActionButton(
|
||||
modifier = modifier.sizeIn(minWidth = minWidth),
|
||||
onClick = onClick,
|
||||
interactionSource = interactionSource,
|
||||
shape = shape,
|
||||
containerColor = containerColor,
|
||||
contentColor = contentColor,
|
||||
elevation = elevation,
|
||||
) {
|
||||
val startPadding by animateDpAsState(if (expanded) ExtendedFabIconSize / 2 else 0.dp)
|
||||
val endPadding by animateDpAsState(if (expanded) ExtendedFabTextPadding else 0.dp)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.padding(start = startPadding, end = endPadding),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
icon()
|
||||
AnimatedVisibility(
|
||||
visible = expanded,
|
||||
enter = ExtendedFabExpandAnimation,
|
||||
exit = ExtendedFabCollapseAnimation,
|
||||
) {
|
||||
Row {
|
||||
Spacer(Modifier.width(ExtendedFabIconPadding))
|
||||
text()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val EasingLinearCubicBezier = CubicBezierEasing(0.0f, 0.0f, 1.0f, 1.0f)
|
||||
private val EasingEmphasizedCubicBezier = CubicBezierEasing(0.2f, 0.0f, 0.0f, 1.0f)
|
||||
|
||||
private val ExtendedFabMinimumWidth = 80.dp
|
||||
private val ExtendedFabIconSize = 24.0.dp
|
||||
private val ExtendedFabIconPadding = 12.dp
|
||||
private val ExtendedFabTextPadding = 20.dp
|
||||
|
||||
private val ExtendedFabCollapseAnimation = fadeOut(
|
||||
animationSpec = tween(
|
||||
durationMillis = 100,
|
||||
easing = EasingLinearCubicBezier,
|
||||
),
|
||||
) + shrinkHorizontally(
|
||||
animationSpec = tween(
|
||||
durationMillis = 500,
|
||||
easing = EasingEmphasizedCubicBezier,
|
||||
),
|
||||
shrinkTowards = Alignment.Start,
|
||||
)
|
||||
|
||||
private val ExtendedFabExpandAnimation = fadeIn(
|
||||
animationSpec = tween(
|
||||
durationMillis = 200,
|
||||
delayMillis = 100,
|
||||
easing = EasingLinearCubicBezier,
|
||||
),
|
||||
) + expandHorizontally(
|
||||
animationSpec = tween(
|
||||
durationMillis = 500,
|
||||
easing = EasingEmphasizedCubicBezier,
|
||||
),
|
||||
expandFrom = Alignment.Start,
|
||||
)
|
||||
|
||||
private val FabContainerWidth = 56.0.dp
|
||||
108
app/src/main/java/eu/kanade/presentation/components/Surface.kt
Normal file
108
app/src/main/java/eu/kanade/presentation/components/Surface.kt
Normal file
@@ -0,0 +1,108 @@
|
||||
package eu.kanade.presentation.components
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.LocalAbsoluteTonalElevation
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Shapes
|
||||
import androidx.compose.material3.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.NonRestartableComposable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.compositeOver
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.util.minimumTouchTargetSize
|
||||
import kotlin.math.ln
|
||||
|
||||
@Composable
|
||||
@NonRestartableComposable
|
||||
fun Surface(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
enabled: Boolean = true,
|
||||
shape: Shape = Shapes.None,
|
||||
color: Color = MaterialTheme.colorScheme.surface,
|
||||
contentColor: Color = contentColorFor(color),
|
||||
tonalElevation: Dp = 0.dp,
|
||||
shadowElevation: Dp = 0.dp,
|
||||
border: BorderStroke? = null,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val absoluteElevation = LocalAbsoluteTonalElevation.current + tonalElevation
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides contentColor,
|
||||
LocalAbsoluteTonalElevation provides absoluteElevation,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.minimumTouchTargetSize()
|
||||
.surface(
|
||||
shape = shape,
|
||||
backgroundColor = surfaceColorAtElevation(
|
||||
color = color,
|
||||
elevation = absoluteElevation,
|
||||
),
|
||||
border = border,
|
||||
shadowElevation = shadowElevation,
|
||||
)
|
||||
.combinedClickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = rememberRipple(),
|
||||
enabled = enabled,
|
||||
role = Role.Button,
|
||||
onLongClick = onLongClick,
|
||||
onClick = onClick,
|
||||
),
|
||||
propagateMinConstraints = true,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Modifier.surface(
|
||||
shape: Shape,
|
||||
backgroundColor: Color,
|
||||
border: BorderStroke?,
|
||||
shadowElevation: Dp,
|
||||
) = this
|
||||
.shadow(shadowElevation, shape, clip = false)
|
||||
.then(if (border != null) Modifier.border(border, shape) else Modifier)
|
||||
.background(color = backgroundColor, shape = shape)
|
||||
.clip(shape)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun surfaceColorAtElevation(color: Color, elevation: Dp): Color {
|
||||
return if (color == MaterialTheme.colorScheme.surface) {
|
||||
MaterialTheme.colorScheme.surfaceColorAtElevation(elevation)
|
||||
} else {
|
||||
color
|
||||
}
|
||||
}
|
||||
|
||||
private fun ColorScheme.surfaceColorAtElevation(
|
||||
elevation: Dp,
|
||||
): Color {
|
||||
if (elevation == 0.dp) return surface
|
||||
val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 100f
|
||||
return surfaceTint.copy(alpha = alpha).compositeOver(surface)
|
||||
}
|
||||
Reference in New Issue
Block a user