mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +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, | ||||
| ) | ||||
		Reference in New Issue
	
	Block a user