mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	Bump Compose M3 to 1.0.0-beta01 (#7867)
This commit is contained in:
		@@ -2,7 +2,8 @@ package eu.kanade.presentation.components
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.RowScope
 | 
			
		||||
import androidx.compose.foundation.layout.statusBarsPadding
 | 
			
		||||
import androidx.compose.foundation.layout.WindowInsets
 | 
			
		||||
import androidx.compose.foundation.layout.statusBars
 | 
			
		||||
import androidx.compose.material.icons.Icons
 | 
			
		||||
import androidx.compose.material.icons.filled.ArrowBack
 | 
			
		||||
import androidx.compose.material.icons.filled.Close
 | 
			
		||||
@@ -15,6 +16,7 @@ import androidx.compose.material3.SmallTopAppBar
 | 
			
		||||
import androidx.compose.material3.Text
 | 
			
		||||
import androidx.compose.material3.TopAppBarDefaults
 | 
			
		||||
import androidx.compose.material3.TopAppBarScrollBehavior
 | 
			
		||||
import androidx.compose.material3.surfaceColorAtElevation
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.derivedStateOf
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
@@ -22,12 +24,11 @@ import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.runtime.setValue
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.draw.drawBehind
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
import androidx.compose.ui.graphics.vector.ImageVector
 | 
			
		||||
import androidx.compose.ui.res.stringResource
 | 
			
		||||
import androidx.compose.ui.text.font.FontWeight
 | 
			
		||||
import androidx.compose.ui.text.style.TextOverflow
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
@@ -97,14 +98,10 @@ fun AppBar(
 | 
			
		||||
 | 
			
		||||
    scrollBehavior: TopAppBarScrollBehavior? = null,
 | 
			
		||||
) {
 | 
			
		||||
    val scrollFraction = if (isActionMode) 1f else scrollBehavior?.state?.overlappedFraction ?: 0f
 | 
			
		||||
    val backgroundColor by TopAppBarDefaults.smallTopAppBarColors().containerColor(scrollFraction)
 | 
			
		||||
 | 
			
		||||
    Column(
 | 
			
		||||
        modifier = modifier.drawBehind { drawRect(backgroundColor) },
 | 
			
		||||
        modifier = modifier,
 | 
			
		||||
    ) {
 | 
			
		||||
        SmallTopAppBar(
 | 
			
		||||
            modifier = Modifier.statusBarsPadding(),
 | 
			
		||||
            navigationIcon = {
 | 
			
		||||
                if (isActionMode) {
 | 
			
		||||
                    IconButton(onClick = onCancelActionMode) {
 | 
			
		||||
@@ -126,10 +123,11 @@ fun AppBar(
 | 
			
		||||
            },
 | 
			
		||||
            title = titleContent,
 | 
			
		||||
            actions = actions,
 | 
			
		||||
            // Background handled by parent
 | 
			
		||||
            windowInsets = WindowInsets.statusBars,
 | 
			
		||||
            colors = TopAppBarDefaults.smallTopAppBarColors(
 | 
			
		||||
                containerColor = Color.Transparent,
 | 
			
		||||
                scrolledContainerColor = Color.Transparent,
 | 
			
		||||
                containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
 | 
			
		||||
                    elevation = if (isActionMode) 3.dp else 0.dp,
 | 
			
		||||
                ),
 | 
			
		||||
            ),
 | 
			
		||||
            scrollBehavior = scrollBehavior,
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +1,45 @@
 | 
			
		||||
package eu.kanade.presentation.components
 | 
			
		||||
 | 
			
		||||
import androidx.compose.animation.core.Animatable
 | 
			
		||||
import androidx.compose.animation.core.VectorConverter
 | 
			
		||||
import androidx.compose.foundation.BorderStroke
 | 
			
		||||
import androidx.compose.foundation.interaction.FocusInteraction
 | 
			
		||||
import androidx.compose.foundation.interaction.HoverInteraction
 | 
			
		||||
import androidx.compose.foundation.interaction.Interaction
 | 
			
		||||
import androidx.compose.foundation.interaction.InteractionSource
 | 
			
		||||
import androidx.compose.foundation.interaction.MutableInteractionSource
 | 
			
		||||
import androidx.compose.foundation.interaction.PressInteraction
 | 
			
		||||
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.Button
 | 
			
		||||
import androidx.compose.material3.ColorScheme
 | 
			
		||||
import androidx.compose.material3.ElevatedButton
 | 
			
		||||
import androidx.compose.material3.LocalContentColor
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.ProvideTextStyle
 | 
			
		||||
import androidx.compose.material3.TextButton
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.CompositionLocalProvider
 | 
			
		||||
import androidx.compose.runtime.Immutable
 | 
			
		||||
import androidx.compose.runtime.LaunchedEffect
 | 
			
		||||
import androidx.compose.runtime.Stable
 | 
			
		||||
import androidx.compose.runtime.State
 | 
			
		||||
import androidx.compose.runtime.mutableStateListOf
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.runtime.rememberUpdatedState
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.geometry.Offset
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
import androidx.compose.ui.graphics.Shape
 | 
			
		||||
import androidx.compose.ui.unit.Dp
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import eu.kanade.presentation.util.animateElevation
 | 
			
		||||
import androidx.compose.material3.ButtonDefaults as M3ButtonDefaults
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun TextButton(
 | 
			
		||||
@@ -30,10 +49,15 @@ fun TextButton(
 | 
			
		||||
    enabled: Boolean = true,
 | 
			
		||||
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
 | 
			
		||||
    elevation: ButtonElevation? = null,
 | 
			
		||||
    shape: Shape = ButtonDefaults.textShape,
 | 
			
		||||
    shape: Shape = M3ButtonDefaults.textShape,
 | 
			
		||||
    border: BorderStroke? = null,
 | 
			
		||||
    colors: ButtonColors = ButtonDefaults.textButtonColors(),
 | 
			
		||||
    contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding,
 | 
			
		||||
    colors: ButtonColors = ButtonColors(
 | 
			
		||||
        containerColor = Color.Transparent,
 | 
			
		||||
        contentColor = MaterialTheme.colorScheme.primary,
 | 
			
		||||
        disabledContainerColor = Color.Transparent,
 | 
			
		||||
        disabledContentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f),
 | 
			
		||||
    ),
 | 
			
		||||
    contentPadding: PaddingValues = M3ButtonDefaults.TextButtonContentPadding,
 | 
			
		||||
    content: @Composable RowScope.() -> Unit,
 | 
			
		||||
) =
 | 
			
		||||
    Button(
 | 
			
		||||
@@ -58,10 +82,10 @@ fun Button(
 | 
			
		||||
    enabled: Boolean = true,
 | 
			
		||||
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
 | 
			
		||||
    elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
 | 
			
		||||
    shape: Shape = ButtonDefaults.textShape,
 | 
			
		||||
    shape: Shape = M3ButtonDefaults.textShape,
 | 
			
		||||
    border: BorderStroke? = null,
 | 
			
		||||
    colors: ButtonColors = ButtonDefaults.buttonColors(),
 | 
			
		||||
    contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
 | 
			
		||||
    contentPadding: PaddingValues = M3ButtonDefaults.ContentPadding,
 | 
			
		||||
    content: @Composable RowScope.() -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    val containerColor = colors.containerColor(enabled).value
 | 
			
		||||
@@ -86,8 +110,8 @@ fun Button(
 | 
			
		||||
            ProvideTextStyle(value = MaterialTheme.typography.labelLarge) {
 | 
			
		||||
                Row(
 | 
			
		||||
                    Modifier.defaultMinSize(
 | 
			
		||||
                        minWidth = ButtonDefaults.MinWidth,
 | 
			
		||||
                        minHeight = ButtonDefaults.MinHeight,
 | 
			
		||||
                        minWidth = M3ButtonDefaults.MinWidth,
 | 
			
		||||
                        minHeight = M3ButtonDefaults.MinHeight,
 | 
			
		||||
                    )
 | 
			
		||||
                        .padding(contentPadding),
 | 
			
		||||
                    horizontalArrangement = Arrangement.Center,
 | 
			
		||||
@@ -98,3 +122,255 @@ fun Button(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
object ButtonDefaults {
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a [ButtonColors] that represents the default container and content colors used in a
 | 
			
		||||
     * [Button].
 | 
			
		||||
     *
 | 
			
		||||
     * @param containerColor the container color of this [Button] when enabled.
 | 
			
		||||
     * @param contentColor the content color of this [Button] when enabled.
 | 
			
		||||
     * @param disabledContainerColor the container color of this [Button] when not enabled.
 | 
			
		||||
     * @param disabledContentColor the content color of this [Button] when not enabled.
 | 
			
		||||
     */
 | 
			
		||||
    @Composable
 | 
			
		||||
    fun buttonColors(
 | 
			
		||||
        containerColor: Color = MaterialTheme.colorScheme.primary,
 | 
			
		||||
        contentColor: Color = MaterialTheme.colorScheme.onPrimary,
 | 
			
		||||
        disabledContainerColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f),
 | 
			
		||||
        disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f),
 | 
			
		||||
    ): ButtonColors = ButtonColors(
 | 
			
		||||
        containerColor = containerColor,
 | 
			
		||||
        contentColor = contentColor,
 | 
			
		||||
        disabledContainerColor = disabledContainerColor,
 | 
			
		||||
        disabledContentColor = disabledContentColor,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a [ButtonElevation] that will animate between the provided values according to the
 | 
			
		||||
     * Material specification for a [Button].
 | 
			
		||||
     *
 | 
			
		||||
     * @param defaultElevation the elevation used when the [Button] is enabled, and has no other
 | 
			
		||||
     * [Interaction]s.
 | 
			
		||||
     * @param pressedElevation the elevation used when this [Button] is enabled and pressed.
 | 
			
		||||
     * @param focusedElevation the elevation used when the [Button] is enabled and focused.
 | 
			
		||||
     * @param hoveredElevation the elevation used when the [Button] is enabled and hovered.
 | 
			
		||||
     * @param disabledElevation the elevation used when the [Button] is not enabled.
 | 
			
		||||
     */
 | 
			
		||||
    @Composable
 | 
			
		||||
    fun buttonElevation(
 | 
			
		||||
        defaultElevation: Dp = 0.dp,
 | 
			
		||||
        pressedElevation: Dp = 0.dp,
 | 
			
		||||
        focusedElevation: Dp = 0.dp,
 | 
			
		||||
        hoveredElevation: Dp = 1.dp,
 | 
			
		||||
        disabledElevation: Dp = 0.dp,
 | 
			
		||||
    ): ButtonElevation = ButtonElevation(
 | 
			
		||||
        defaultElevation = defaultElevation,
 | 
			
		||||
        pressedElevation = pressedElevation,
 | 
			
		||||
        focusedElevation = focusedElevation,
 | 
			
		||||
        hoveredElevation = hoveredElevation,
 | 
			
		||||
        disabledElevation = disabledElevation,
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents the elevation for a button in different states.
 | 
			
		||||
 *
 | 
			
		||||
 * - See [M3ButtonDefaults.buttonElevation] for the default elevation used in a [Button].
 | 
			
		||||
 * - See [M3ButtonDefaults.elevatedButtonElevation] for the default elevation used in a
 | 
			
		||||
 * [ElevatedButton].
 | 
			
		||||
 */
 | 
			
		||||
@Stable
 | 
			
		||||
class ButtonElevation internal constructor(
 | 
			
		||||
    private val defaultElevation: Dp,
 | 
			
		||||
    private val pressedElevation: Dp,
 | 
			
		||||
    private val focusedElevation: Dp,
 | 
			
		||||
    private val hoveredElevation: Dp,
 | 
			
		||||
    private val disabledElevation: Dp,
 | 
			
		||||
) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Represents the tonal elevation used in a button, depending on its [enabled] state and
 | 
			
		||||
     * [interactionSource]. This should typically be the same value as the [shadowElevation].
 | 
			
		||||
     *
 | 
			
		||||
     * Tonal elevation is used to apply a color shift to the surface to give the it higher emphasis.
 | 
			
		||||
     * When surface's color is [ColorScheme.surface], a higher elevation will result in a darker
 | 
			
		||||
     * color in light theme and lighter color in dark theme.
 | 
			
		||||
     *
 | 
			
		||||
     * See [shadowElevation] which controls the elevation of the shadow drawn around the button.
 | 
			
		||||
     *
 | 
			
		||||
     * @param enabled whether the button is enabled
 | 
			
		||||
     * @param interactionSource the [InteractionSource] for this button
 | 
			
		||||
     */
 | 
			
		||||
    @Composable
 | 
			
		||||
    internal fun tonalElevation(enabled: Boolean, interactionSource: InteractionSource): State<Dp> {
 | 
			
		||||
        return animateElevation(enabled = enabled, interactionSource = interactionSource)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Represents the shadow elevation used in a button, depending on its [enabled] state and
 | 
			
		||||
     * [interactionSource]. This should typically be the same value as the [tonalElevation].
 | 
			
		||||
     *
 | 
			
		||||
     * Shadow elevation is used to apply a shadow around the button to give it higher emphasis.
 | 
			
		||||
     *
 | 
			
		||||
     * See [tonalElevation] which controls the elevation with a color shift to the surface.
 | 
			
		||||
     *
 | 
			
		||||
     * @param enabled whether the button is enabled
 | 
			
		||||
     * @param interactionSource the [InteractionSource] for this button
 | 
			
		||||
     */
 | 
			
		||||
    @Composable
 | 
			
		||||
    internal fun shadowElevation(
 | 
			
		||||
        enabled: Boolean,
 | 
			
		||||
        interactionSource: InteractionSource,
 | 
			
		||||
    ): State<Dp> {
 | 
			
		||||
        return animateElevation(enabled = enabled, interactionSource = interactionSource)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Composable
 | 
			
		||||
    private fun animateElevation(
 | 
			
		||||
        enabled: Boolean,
 | 
			
		||||
        interactionSource: InteractionSource,
 | 
			
		||||
    ): State<Dp> {
 | 
			
		||||
        val interactions = remember { mutableStateListOf<Interaction>() }
 | 
			
		||||
        LaunchedEffect(interactionSource) {
 | 
			
		||||
            interactionSource.interactions.collect { interaction ->
 | 
			
		||||
                when (interaction) {
 | 
			
		||||
                    is HoverInteraction.Enter -> {
 | 
			
		||||
                        interactions.add(interaction)
 | 
			
		||||
                    }
 | 
			
		||||
                    is HoverInteraction.Exit -> {
 | 
			
		||||
                        interactions.remove(interaction.enter)
 | 
			
		||||
                    }
 | 
			
		||||
                    is FocusInteraction.Focus -> {
 | 
			
		||||
                        interactions.add(interaction)
 | 
			
		||||
                    }
 | 
			
		||||
                    is FocusInteraction.Unfocus -> {
 | 
			
		||||
                        interactions.remove(interaction.focus)
 | 
			
		||||
                    }
 | 
			
		||||
                    is PressInteraction.Press -> {
 | 
			
		||||
                        interactions.add(interaction)
 | 
			
		||||
                    }
 | 
			
		||||
                    is PressInteraction.Release -> {
 | 
			
		||||
                        interactions.remove(interaction.press)
 | 
			
		||||
                    }
 | 
			
		||||
                    is PressInteraction.Cancel -> {
 | 
			
		||||
                        interactions.remove(interaction.press)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val interaction = interactions.lastOrNull()
 | 
			
		||||
 | 
			
		||||
        val target =
 | 
			
		||||
            if (!enabled) {
 | 
			
		||||
                disabledElevation
 | 
			
		||||
            } else {
 | 
			
		||||
                when (interaction) {
 | 
			
		||||
                    is PressInteraction.Press -> pressedElevation
 | 
			
		||||
                    is HoverInteraction.Enter -> hoveredElevation
 | 
			
		||||
                    is FocusInteraction.Focus -> focusedElevation
 | 
			
		||||
                    else -> defaultElevation
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        val animatable = remember { Animatable(target, Dp.VectorConverter) }
 | 
			
		||||
 | 
			
		||||
        if (!enabled) {
 | 
			
		||||
            // No transition when moving to a disabled state
 | 
			
		||||
            LaunchedEffect(target) { animatable.snapTo(target) }
 | 
			
		||||
        } else {
 | 
			
		||||
            LaunchedEffect(target) {
 | 
			
		||||
                val lastInteraction = when (animatable.targetValue) {
 | 
			
		||||
                    pressedElevation -> PressInteraction.Press(Offset.Zero)
 | 
			
		||||
                    hoveredElevation -> HoverInteraction.Enter()
 | 
			
		||||
                    focusedElevation -> FocusInteraction.Focus()
 | 
			
		||||
                    else -> null
 | 
			
		||||
                }
 | 
			
		||||
                animatable.animateElevation(
 | 
			
		||||
                    from = lastInteraction,
 | 
			
		||||
                    to = interaction,
 | 
			
		||||
                    target = target,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return animatable.asState()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun equals(other: Any?): Boolean {
 | 
			
		||||
        if (this === other) return true
 | 
			
		||||
        if (other == null || other !is ButtonElevation) return false
 | 
			
		||||
 | 
			
		||||
        if (defaultElevation != other.defaultElevation) return false
 | 
			
		||||
        if (pressedElevation != other.pressedElevation) return false
 | 
			
		||||
        if (focusedElevation != other.focusedElevation) return false
 | 
			
		||||
        if (hoveredElevation != other.hoveredElevation) return false
 | 
			
		||||
        if (disabledElevation != other.disabledElevation) return false
 | 
			
		||||
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun hashCode(): Int {
 | 
			
		||||
        var result = defaultElevation.hashCode()
 | 
			
		||||
        result = 31 * result + pressedElevation.hashCode()
 | 
			
		||||
        result = 31 * result + focusedElevation.hashCode()
 | 
			
		||||
        result = 31 * result + hoveredElevation.hashCode()
 | 
			
		||||
        result = 31 * result + disabledElevation.hashCode()
 | 
			
		||||
        return result
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents the container and content colors used in a button in different states.
 | 
			
		||||
 *
 | 
			
		||||
 * - See [M3ButtonDefaults.buttonColors] for the default colors used in a [Button].
 | 
			
		||||
 * - See [M3ButtonDefaults.elevatedButtonColors] for the default colors used in a [ElevatedButton].
 | 
			
		||||
 * - See [M3ButtonDefaults.textButtonColors] for the default colors used in a [TextButton].
 | 
			
		||||
 */
 | 
			
		||||
@Immutable
 | 
			
		||||
class ButtonColors internal constructor(
 | 
			
		||||
    private val containerColor: Color,
 | 
			
		||||
    private val contentColor: Color,
 | 
			
		||||
    private val disabledContainerColor: Color,
 | 
			
		||||
    private val disabledContentColor: Color,
 | 
			
		||||
) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Represents the container color for this button, depending on [enabled].
 | 
			
		||||
     *
 | 
			
		||||
     * @param enabled whether the button is enabled
 | 
			
		||||
     */
 | 
			
		||||
    @Composable
 | 
			
		||||
    internal fun containerColor(enabled: Boolean): State<Color> {
 | 
			
		||||
        return rememberUpdatedState(if (enabled) containerColor else disabledContainerColor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Represents the content color for this button, depending on [enabled].
 | 
			
		||||
     *
 | 
			
		||||
     * @param enabled whether the button is enabled
 | 
			
		||||
     */
 | 
			
		||||
    @Composable
 | 
			
		||||
    internal fun contentColor(enabled: Boolean): State<Color> {
 | 
			
		||||
        return rememberUpdatedState(if (enabled) contentColor else disabledContentColor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun equals(other: Any?): Boolean {
 | 
			
		||||
        if (this === other) return true
 | 
			
		||||
        if (other == null || other !is ButtonColors) return false
 | 
			
		||||
 | 
			
		||||
        if (containerColor != other.containerColor) return false
 | 
			
		||||
        if (contentColor != other.contentColor) return false
 | 
			
		||||
        if (disabledContainerColor != other.disabledContainerColor) return false
 | 
			
		||||
        if (disabledContentColor != other.disabledContentColor) return false
 | 
			
		||||
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun hashCode(): Int {
 | 
			
		||||
        var result = containerColor.hashCode()
 | 
			
		||||
        result = 31 * result + contentColor.hashCode()
 | 
			
		||||
        result = 31 * result + disabledContainerColor.hashCode()
 | 
			
		||||
        result = 31 * result + disabledContentColor.hashCode()
 | 
			
		||||
        return result
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,15 +23,20 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
import androidx.compose.foundation.layout.size
 | 
			
		||||
import androidx.compose.material.ripple.rememberRipple
 | 
			
		||||
import androidx.compose.material3.FilledIconButton
 | 
			
		||||
import androidx.compose.material3.Icon
 | 
			
		||||
import androidx.compose.material3.IconButtonColors
 | 
			
		||||
import androidx.compose.material3.IconButtonDefaults
 | 
			
		||||
import androidx.compose.material3.IconButton
 | 
			
		||||
import androidx.compose.material3.LocalContentColor
 | 
			
		||||
import androidx.compose.material3.OutlinedIconButton
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.CompositionLocalProvider
 | 
			
		||||
import androidx.compose.runtime.Immutable
 | 
			
		||||
import androidx.compose.runtime.State
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.runtime.rememberUpdatedState
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
import androidx.compose.ui.semantics.Role
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import eu.kanade.presentation.util.minimumTouchTargetSize
 | 
			
		||||
@@ -100,6 +105,88 @@ fun IconButton(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
object IconButtonDefaults {
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a [IconButtonColors] that represents the default colors used in a [IconButton].
 | 
			
		||||
     *
 | 
			
		||||
     * @param containerColor the container color of this icon button when enabled.
 | 
			
		||||
     * @param contentColor the content color of this icon button when enabled.
 | 
			
		||||
     * @param disabledContainerColor the container color of this icon button when not enabled.
 | 
			
		||||
     * @param disabledContentColor the content color of this icon button when not enabled.
 | 
			
		||||
     */
 | 
			
		||||
    @Composable
 | 
			
		||||
    fun iconButtonColors(
 | 
			
		||||
        containerColor: Color = Color.Transparent,
 | 
			
		||||
        contentColor: Color = LocalContentColor.current,
 | 
			
		||||
        disabledContainerColor: Color = Color.Transparent,
 | 
			
		||||
        disabledContentColor: Color = contentColor.copy(alpha = 0.38f),
 | 
			
		||||
    ): IconButtonColors =
 | 
			
		||||
        IconButtonColors(
 | 
			
		||||
            containerColor = containerColor,
 | 
			
		||||
            contentColor = contentColor,
 | 
			
		||||
            disabledContainerColor = disabledContainerColor,
 | 
			
		||||
            disabledContentColor = disabledContentColor,
 | 
			
		||||
        )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
object IconButtonTokens {
 | 
			
		||||
    val StateLayerSize = 40.0.dp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents the container and content colors used in an icon button in different states.
 | 
			
		||||
 *
 | 
			
		||||
 * - See [IconButtonDefaults.filledIconButtonColors] and
 | 
			
		||||
 * [IconButtonDefaults.filledTonalIconButtonColors] for the default colors used in a
 | 
			
		||||
 * [FilledIconButton].
 | 
			
		||||
 * - See [IconButtonDefaults.outlinedIconButtonColors] for the default colors used in an
 | 
			
		||||
 * [OutlinedIconButton].
 | 
			
		||||
 */
 | 
			
		||||
@Immutable
 | 
			
		||||
class IconButtonColors internal constructor(
 | 
			
		||||
    private val containerColor: Color,
 | 
			
		||||
    private val contentColor: Color,
 | 
			
		||||
    private val disabledContainerColor: Color,
 | 
			
		||||
    private val disabledContentColor: Color,
 | 
			
		||||
) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Represents the container color for this icon button, depending on [enabled].
 | 
			
		||||
     *
 | 
			
		||||
     * @param enabled whether the icon button is enabled
 | 
			
		||||
     */
 | 
			
		||||
    @Composable
 | 
			
		||||
    internal fun containerColor(enabled: Boolean): State<Color> {
 | 
			
		||||
        return rememberUpdatedState(if (enabled) containerColor else disabledContainerColor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Represents the content color for this icon button, depending on [enabled].
 | 
			
		||||
     *
 | 
			
		||||
     * @param enabled whether the icon button is enabled
 | 
			
		||||
     */
 | 
			
		||||
    @Composable
 | 
			
		||||
    internal fun contentColor(enabled: Boolean): State<Color> {
 | 
			
		||||
        return rememberUpdatedState(if (enabled) contentColor else disabledContentColor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun equals(other: Any?): Boolean {
 | 
			
		||||
        if (this === other) return true
 | 
			
		||||
        if (other == null || other !is IconButtonColors) return false
 | 
			
		||||
 | 
			
		||||
        if (containerColor != other.containerColor) return false
 | 
			
		||||
        if (contentColor != other.contentColor) return false
 | 
			
		||||
        if (disabledContainerColor != other.disabledContainerColor) return false
 | 
			
		||||
        if (disabledContentColor != other.disabledContentColor) return false
 | 
			
		||||
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun hashCode(): Int {
 | 
			
		||||
        var result = containerColor.hashCode()
 | 
			
		||||
        result = 31 * result + contentColor.hashCode()
 | 
			
		||||
        result = 31 * result + disabledContainerColor.hashCode()
 | 
			
		||||
        result = 31 * result + disabledContentColor.hashCode()
 | 
			
		||||
 | 
			
		||||
        return result
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,15 +17,12 @@ import androidx.compose.material3.IconButton
 | 
			
		||||
import androidx.compose.material3.LocalContentColor
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.Text
 | 
			
		||||
import androidx.compose.material3.TopAppBarDefaults
 | 
			
		||||
import androidx.compose.material3.TopAppBarScrollBehavior
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.LaunchedEffect
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.draw.drawBehind
 | 
			
		||||
import androidx.compose.ui.focus.FocusRequester
 | 
			
		||||
import androidx.compose.ui.focus.focusRequester
 | 
			
		||||
import androidx.compose.ui.graphics.SolidColor
 | 
			
		||||
@@ -138,12 +135,7 @@ fun LibrarySelectionToolbar(
 | 
			
		||||
    onClickSelectAll: () -> Unit,
 | 
			
		||||
    onClickInvertSelection: () -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    val backgroundColor by TopAppBarDefaults.smallTopAppBarColors().containerColor(1f)
 | 
			
		||||
    AppBar(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .drawBehind {
 | 
			
		||||
                drawRect(backgroundColor.copy(alpha = 1f))
 | 
			
		||||
            },
 | 
			
		||||
        titleContent = { Text(text = "${state.selection.size}") },
 | 
			
		||||
        actions = {
 | 
			
		||||
            IconButton(onClick = onClickSelectAll) {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,7 @@ package eu.kanade.presentation.manga.components
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.WindowInsets
 | 
			
		||||
import androidx.compose.foundation.layout.WindowInsetsSides
 | 
			
		||||
import androidx.compose.foundation.layout.only
 | 
			
		||||
import androidx.compose.foundation.layout.systemBars
 | 
			
		||||
import androidx.compose.foundation.layout.windowInsetsPadding
 | 
			
		||||
import androidx.compose.foundation.layout.statusBars
 | 
			
		||||
import androidx.compose.material.icons.Icons
 | 
			
		||||
import androidx.compose.material.icons.filled.ArrowBack
 | 
			
		||||
import androidx.compose.material.icons.filled.Close
 | 
			
		||||
@@ -18,19 +15,19 @@ import androidx.compose.material.icons.outlined.Share
 | 
			
		||||
import androidx.compose.material3.DropdownMenuItem
 | 
			
		||||
import androidx.compose.material3.Icon
 | 
			
		||||
import androidx.compose.material3.IconButton
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.SmallTopAppBar
 | 
			
		||||
import androidx.compose.material3.Text
 | 
			
		||||
import androidx.compose.material3.TopAppBarDefaults
 | 
			
		||||
import androidx.compose.material3.surfaceColorAtElevation
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.draw.alpha
 | 
			
		||||
import androidx.compose.ui.draw.drawBehind
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
import androidx.compose.ui.res.stringResource
 | 
			
		||||
import androidx.compose.ui.text.style.TextOverflow
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import eu.kanade.presentation.components.DownloadedOnlyModeBanner
 | 
			
		||||
import eu.kanade.presentation.components.DropdownMenu
 | 
			
		||||
import eu.kanade.presentation.components.IncognitoModeBanner
 | 
			
		||||
@@ -55,16 +52,11 @@ fun MangaAppBar(
 | 
			
		||||
    onSelectAll: () -> Unit,
 | 
			
		||||
    onInvertSelection: () -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    val isActionMode = actionModeCounter > 0
 | 
			
		||||
    val backgroundAlpha = if (isActionMode) 1f else backgroundAlphaProvider()
 | 
			
		||||
    val backgroundColor by TopAppBarDefaults.smallTopAppBarColors().containerColor(1f)
 | 
			
		||||
    Column(
 | 
			
		||||
        modifier = modifier.drawBehind {
 | 
			
		||||
            drawRect(backgroundColor.copy(alpha = backgroundAlpha))
 | 
			
		||||
        },
 | 
			
		||||
        modifier = modifier,
 | 
			
		||||
    ) {
 | 
			
		||||
        val isActionMode = actionModeCounter > 0
 | 
			
		||||
        SmallTopAppBar(
 | 
			
		||||
            modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top)),
 | 
			
		||||
            title = {
 | 
			
		||||
                Text(
 | 
			
		||||
                    text = if (isActionMode) actionModeCounter.toString() else title,
 | 
			
		||||
@@ -198,10 +190,11 @@ fun MangaAppBar(
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            // Background handled by parent
 | 
			
		||||
            windowInsets = WindowInsets.statusBars,
 | 
			
		||||
            colors = TopAppBarDefaults.smallTopAppBarColors(
 | 
			
		||||
                containerColor = Color.Transparent,
 | 
			
		||||
                scrolledContainerColor = Color.Transparent,
 | 
			
		||||
                containerColor = MaterialTheme.colorScheme
 | 
			
		||||
                    .surfaceColorAtElevation(3.dp)
 | 
			
		||||
                    .copy(alpha = if (isActionMode) 1f else backgroundAlphaProvider()),
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										125
									
								
								app/src/main/java/eu/kanade/presentation/util/Elevation.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								app/src/main/java/eu/kanade/presentation/util/Elevation.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,125 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2021 The Android Open Source Project
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Straight copy from Compose M3 for Button fork
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package eu.kanade.presentation.util
 | 
			
		||||
 | 
			
		||||
import androidx.compose.animation.core.Animatable
 | 
			
		||||
import androidx.compose.animation.core.AnimationSpec
 | 
			
		||||
import androidx.compose.animation.core.CubicBezierEasing
 | 
			
		||||
import androidx.compose.animation.core.Easing
 | 
			
		||||
import androidx.compose.animation.core.FastOutSlowInEasing
 | 
			
		||||
import androidx.compose.animation.core.TweenSpec
 | 
			
		||||
import androidx.compose.foundation.interaction.DragInteraction
 | 
			
		||||
import androidx.compose.foundation.interaction.FocusInteraction
 | 
			
		||||
import androidx.compose.foundation.interaction.HoverInteraction
 | 
			
		||||
import androidx.compose.foundation.interaction.Interaction
 | 
			
		||||
import androidx.compose.foundation.interaction.PressInteraction
 | 
			
		||||
import androidx.compose.ui.unit.Dp
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Animates the [Dp] value of [this] between [from] and [to] [Interaction]s, to [target]. The
 | 
			
		||||
 * [AnimationSpec] used depends on the values for [from] and [to], see
 | 
			
		||||
 * [ElevationDefaults.incomingAnimationSpecForInteraction] and
 | 
			
		||||
 * [ElevationDefaults.outgoingAnimationSpecForInteraction] for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * @param target the [Dp] target elevation for this component, corresponding to the elevation
 | 
			
		||||
 * desired for the [to] state.
 | 
			
		||||
 * @param from the previous [Interaction] that was used to calculate elevation. `null` if there
 | 
			
		||||
 * was no previous [Interaction], such as when the component is in its default state.
 | 
			
		||||
 * @param to the [Interaction] that this component is moving to, such as [PressInteraction.Press]
 | 
			
		||||
 * when this component is being pressed. `null` if this component is moving back to its default
 | 
			
		||||
 * state.
 | 
			
		||||
 */
 | 
			
		||||
internal suspend fun Animatable<Dp, *>.animateElevation(
 | 
			
		||||
    target: Dp,
 | 
			
		||||
    from: Interaction? = null,
 | 
			
		||||
    to: Interaction? = null,
 | 
			
		||||
) {
 | 
			
		||||
    val spec = when {
 | 
			
		||||
        // Moving to a new state
 | 
			
		||||
        to != null -> ElevationDefaults.incomingAnimationSpecForInteraction(to)
 | 
			
		||||
        // Moving to default, from a previous state
 | 
			
		||||
        from != null -> ElevationDefaults.outgoingAnimationSpecForInteraction(from)
 | 
			
		||||
        // Loading the initial state, or moving back to the baseline state from a disabled /
 | 
			
		||||
        // unknown state, so just snap to the final value.
 | 
			
		||||
        else -> null
 | 
			
		||||
    }
 | 
			
		||||
    if (spec != null) animateTo(target, spec) else snapTo(target)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Contains default [AnimationSpec]s used for animating elevation between different [Interaction]s.
 | 
			
		||||
 *
 | 
			
		||||
 * Typically you should use [animateElevation] instead, which uses these [AnimationSpec]s
 | 
			
		||||
 * internally. [animateElevation] in turn is used by the defaults for cards and buttons.
 | 
			
		||||
 *
 | 
			
		||||
 * @see animateElevation
 | 
			
		||||
 */
 | 
			
		||||
private object ElevationDefaults {
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the [AnimationSpec]s used when animating elevation to [interaction], either from a
 | 
			
		||||
     * previous [Interaction], or from the default state. If [interaction] is unknown, then
 | 
			
		||||
     * returns `null`.
 | 
			
		||||
     *
 | 
			
		||||
     * @param interaction the [Interaction] that is being animated to
 | 
			
		||||
     */
 | 
			
		||||
    fun incomingAnimationSpecForInteraction(interaction: Interaction): AnimationSpec<Dp>? {
 | 
			
		||||
        return when (interaction) {
 | 
			
		||||
            is PressInteraction.Press -> DefaultIncomingSpec
 | 
			
		||||
            is DragInteraction.Start -> DefaultIncomingSpec
 | 
			
		||||
            is HoverInteraction.Enter -> DefaultIncomingSpec
 | 
			
		||||
            is FocusInteraction.Focus -> DefaultIncomingSpec
 | 
			
		||||
            else -> null
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the [AnimationSpec]s used when animating elevation away from [interaction], to the
 | 
			
		||||
     * default state. If [interaction] is unknown, then returns `null`.
 | 
			
		||||
     *
 | 
			
		||||
     * @param interaction the [Interaction] that is being animated away from
 | 
			
		||||
     */
 | 
			
		||||
    fun outgoingAnimationSpecForInteraction(interaction: Interaction): AnimationSpec<Dp>? {
 | 
			
		||||
        return when (interaction) {
 | 
			
		||||
            is PressInteraction.Press -> DefaultOutgoingSpec
 | 
			
		||||
            is DragInteraction.Start -> DefaultOutgoingSpec
 | 
			
		||||
            is HoverInteraction.Enter -> HoveredOutgoingSpec
 | 
			
		||||
            is FocusInteraction.Focus -> DefaultOutgoingSpec
 | 
			
		||||
            else -> null
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private val OutgoingSpecEasing: Easing = CubicBezierEasing(0.40f, 0.00f, 0.60f, 1.00f)
 | 
			
		||||
 | 
			
		||||
private val DefaultIncomingSpec = TweenSpec<Dp>(
 | 
			
		||||
    durationMillis = 120,
 | 
			
		||||
    easing = FastOutSlowInEasing,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
private val DefaultOutgoingSpec = TweenSpec<Dp>(
 | 
			
		||||
    durationMillis = 150,
 | 
			
		||||
    easing = OutgoingSpecEasing,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
private val HoveredOutgoingSpec = TweenSpec<Dp>(
 | 
			
		||||
    durationMillis = 120,
 | 
			
		||||
    easing = OutgoingSpecEasing,
 | 
			
		||||
)
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
compiler = "1.3.0-rc02"
 | 
			
		||||
compose = "1.2.1"
 | 
			
		||||
accompanist = "0.25.1"
 | 
			
		||||
material3 = "1.0.0-alpha16"
 | 
			
		||||
material3 = "1.0.0-beta01"
 | 
			
		||||
 | 
			
		||||
[libraries]
 | 
			
		||||
activity = "androidx.activity:activity-compose:1.6.0-beta01"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user