Merge pull request #113 from amir1376/upgrade-compose-and-kotlin-version

Upgrade compose and kotlin version
This commit is contained in:
AmirHossein Abdolmotallebi 2024-10-17 01:46:33 +03:30 committed by GitHub
commit 488059fd8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 414 additions and 33 deletions

View File

@ -10,12 +10,6 @@ import com.abdownloadmanager.desktop.utils.div
import androidx.compose.animation.core.tween
import androidx.compose.foundation.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ripple.LocalRippleTheme
import androidx.compose.material.ripple.RippleAlpha
import androidx.compose.material.ripple.RippleTheme
import androidx.compose.material.ripple.RippleTheme.Companion.defaultRippleAlpha
import androidx.compose.material.ripple.RippleTheme.Companion.defaultRippleColor
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.TextUnit
@ -116,10 +110,7 @@ fun ABDownloaderTheme(
CompositionLocalProvider(
LocalContextMenuRepresentation provides myContextMenuRepresentation(),
LocalScrollbarStyle provides myDefaultScrollBarStyle(),
// there is a modification in newer version of compose that change line height
// I want to remove material design for good but for now I override this
LocalRippleTheme provides remember { MyRippleTheme() },
LocalIndication provides rememberRipple(),
LocalIndication provides ripple(),
LocalContentColor provides myColors.onBackground,
LocalContentAlpha provides 1f,
LocalTextSizes provides textSizes,
@ -133,24 +124,6 @@ fun ABDownloaderTheme(
}
}
private class MyRippleTheme:RippleTheme{
@Composable
override fun defaultColor():Color {
return defaultRippleColor(
contentColor = LocalContentColor.current,
lightTheme = myColors.isLight
)
}
@Composable
override fun rippleAlpha(): RippleAlpha {
return defaultRippleAlpha(
contentColor = LocalContentColor.current,
lightTheme = myColors.isLight
)
}
}
private class MyContextMenuRepresentation : ContextMenuRepresentation {
@Composable
override fun Representation(state: ContextMenuState, items: () -> List<ContextMenuItem>) {

View File

@ -0,0 +1,409 @@
/*
* Copyright 2023 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.
*/
package com.abdownloadmanager.desktop.ui.theme
import androidx.compose.foundation.Indication
import androidx.compose.foundation.IndicationNodeFactory
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.material.ripple.RippleAlpha
import androidx.compose.material.ripple.createRippleModifierNode
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.Stable
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorProducer
import androidx.compose.ui.graphics.isSpecified
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
import androidx.compose.ui.node.DelegatableNode
import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.node.ObserverModifierNode
import androidx.compose.ui.node.currentValueOf
import androidx.compose.ui.node.observeReads
import androidx.compose.ui.unit.Dp
import com.abdownloadmanager.utils.compose.LocalContentColor
/**
* Creates a Ripple using the provided values and values inferred from the theme.
*
* A Ripple is a Material implementation of [Indication] that expresses different [Interaction]s
* by drawing ripple animations and state layers.
*
* A Ripple responds to [PressInteraction.Press] by starting a new animation, and
* responds to other [Interaction]s by showing a fixed state layer with varying alpha values
* depending on the [Interaction].
*
* [MaterialTheme] provides Ripples using [androidx.compose.foundation.LocalIndication], so a Ripple
* will be used as the default [Indication] inside components such as
* [androidx.compose.foundation.clickable] and [androidx.compose.foundation.indication], in
* addition to Material provided components that use a Ripple as well.
*
* You can also explicitly create a Ripple and provide it to custom components in order to change
* the parameters from the default, such as to create an unbounded ripple with a fixed size.
*
* To create a Ripple with a manually defined color that can change over time, see the other
* [ripple] overload with a [ColorProducer] parameter. This will avoid unnecessary recompositions
* when changing the color, and preserve existing ripple state when the color changes.
*
* @param bounded If true, ripples are clipped by the bounds of the target layout. Unbounded
* ripples always animate from the target layout center, bounded ripples animate from the touch
* position.
* @param radius the radius for the ripple. If [Dp.Unspecified] is provided then the size will be
* calculated based on the target layout size.
* @param color the color of the ripple. This color is usually the same color used by the text or
* iconography in the component. This color will then have [RippleDefaults.rippleAlpha]
* applied to calculate the final color used to draw the ripple. If [Color.Unspecified] is
* provided the color used will be [RippleDefaults.rippleColor] instead.
*/
@Stable
fun ripple(
bounded: Boolean = true,
radius: Dp = Dp.Unspecified,
color: Color = Color.Unspecified,
): IndicationNodeFactory {
return if (radius == Dp.Unspecified && color == Color.Unspecified) {
if (bounded) return DefaultBoundedRipple else DefaultUnboundedRipple
} else {
RippleNodeFactory(bounded, radius, color)
}
}
/**
* Creates a Ripple using the provided values and values inferred from the theme.
*
* A Ripple is a Material implementation of [Indication] that expresses different [Interaction]s
* by drawing ripple animations and state layers.
*
* A Ripple responds to [PressInteraction.Press] by starting a new ripple animation, and
* responds to other [Interaction]s by showing a fixed state layer with varying alpha values
* depending on the [Interaction].
*
* [MaterialTheme] provides Ripples using [androidx.compose.foundation.LocalIndication], so a Ripple
* will be used as the default [Indication] inside components such as
* [androidx.compose.foundation.clickable] and [androidx.compose.foundation.indication], in
* addition to Material provided components that use a Ripple as well.
*
* You can also explicitly create a Ripple and provide it to custom components in order to change
* the parameters from the default, such as to create an unbounded ripple with a fixed size.
*
* To create a Ripple with a static color, see the [ripple] overload with a [Color] parameter. This
* overload is optimized for Ripples that have dynamic colors that change over time, to reduce
* unnecessary recompositions.
*
* @param color the color of the ripple. This color is usually the same color used by the text or
* iconography in the component. This color will then have [RippleDefaults.rippleAlpha]
* applied to calculate the final color used to draw the ripple. If you are creating this
* [ColorProducer] outside of composition (where it will be automatically remembered), make sure
* that its instance is stable (such as by remembering the object that holds it), or remember the
* returned [ripple] object to make sure that ripple nodes are not being created each recomposition.
* @param bounded If true, ripples are clipped by the bounds of the target layout. Unbounded
* ripples always animate from the target layout center, bounded ripples animate from the touch
* position.
* @param radius the radius for the ripple. If [Dp.Unspecified] is provided then the size will be
* calculated based on the target layout size.
*/
@Stable
fun ripple(
color: ColorProducer,
bounded: Boolean = true,
radius: Dp = Dp.Unspecified,
): IndicationNodeFactory {
return RippleNodeFactory(bounded, radius, color)
}
/**
* Default values used by [ripple].
*/
object RippleDefaults {
/**
* Represents the default color that will be used for a ripple if a color has not been
* explicitly set on the ripple instance.
*
* @param contentColor the color of content (text or iconography) in the component that
* contains the ripple.
* @param lightTheme whether the theme is light or not
*/
fun rippleColor(
contentColor: Color,
lightTheme: Boolean,
): Color {
val contentLuminance = contentColor.luminance()
// If we are on a colored surface (typically indicated by low luminance content), the
// ripple color should be white.
return if (!lightTheme && contentLuminance < 0.5) {
Color.White
// Otherwise use contentColor
} else {
contentColor
}
}
/**
* Represents the default [RippleAlpha] that will be used for a ripple to indicate different
* states.
*
* @param contentColor the color of content (text or iconography) in the component that
* contains the ripple.
* @param lightTheme whether the theme is light or not
*/
fun rippleAlpha(contentColor: Color, lightTheme: Boolean): RippleAlpha {
return when {
lightTheme -> {
if (contentColor.luminance() > 0.5) {
LightThemeHighContrastRippleAlpha
} else {
LightThemeLowContrastRippleAlpha
}
}
else -> {
DarkThemeRippleAlpha
}
}
}
}
/**
* CompositionLocal used for providing [RippleConfiguration] down the tree. This acts as a
* tree-local 'override' for ripples used inside components that you cannot directly control, such
* as to change the color of a specific component's ripple, or disable it entirely by providing
* `null`.
*
* In most cases you should rely on the default theme behavior for consistency with other components
* - this exists as an escape hatch for individual components and is not intended to be used for
* full theme customization across an application. For this use case you should instead build your
* own custom ripple that queries your design system theme values directly using
* [createRippleModifierNode].
*/
@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
val LocalRippleConfiguration: ProvidableCompositionLocal<RippleConfiguration?> =
compositionLocalOf { RippleConfiguration() }
/**
* Configuration for [ripple] appearance, provided using [LocalRippleConfiguration]. In most cases
* the default values should be used, for custom design system use cases you should instead
* build your own custom ripple using [createRippleModifierNode]. To disable the ripple, provide
* `null` using [LocalRippleConfiguration].
*
* @param color the color override for the ripple. If [Color.Unspecified], then the default color
* from the theme will be used instead. Note that if the ripple has a color explicitly set with
* the parameter on [ripple], that will always be used instead of this value.
* @param rippleAlpha the [RippleAlpha] override for this ripple. If null, then the default alpha
* will be used instead.
*/
@Immutable
class RippleConfiguration(
val color: Color = Color.Unspecified,
val rippleAlpha: RippleAlpha? = null,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is RippleConfiguration) return false
if (color != other.color) return false
if (rippleAlpha != other.rippleAlpha) return false
return true
}
override fun hashCode(): Int {
var result = color.hashCode()
result = 31 * result + (rippleAlpha?.hashCode() ?: 0)
return result
}
override fun toString(): String {
return "RippleConfiguration(color=$color, rippleAlpha=$rippleAlpha)"
}
}
@Stable
private class RippleNodeFactory private constructor(
private val bounded: Boolean,
private val radius: Dp,
private val colorProducer: ColorProducer?,
private val color: Color,
) : IndicationNodeFactory {
constructor(
bounded: Boolean,
radius: Dp,
colorProducer: ColorProducer,
) : this(bounded, radius, colorProducer, Color.Unspecified)
constructor(
bounded: Boolean,
radius: Dp,
color: Color,
) : this(bounded, radius, null, color)
override fun create(interactionSource: InteractionSource): DelegatableNode {
val colorProducer = colorProducer ?: ColorProducer { color }
return DelegatingThemeAwareRippleNode(interactionSource, bounded, radius, colorProducer)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is RippleNodeFactory) return false
if (bounded != other.bounded) return false
if (radius != other.radius) return false
if (colorProducer != other.colorProducer) return false
return color == other.color
}
override fun hashCode(): Int {
var result = bounded.hashCode()
result = 31 * result + radius.hashCode()
result = 31 * result + colorProducer.hashCode()
result = 31 * result + color.hashCode()
return result
}
}
private class DelegatingThemeAwareRippleNode(
private val interactionSource: InteractionSource,
private val bounded: Boolean,
private val radius: Dp,
private val color: ColorProducer,
) : DelegatingNode(), CompositionLocalConsumerModifierNode, ObserverModifierNode {
private var rippleNode: DelegatableNode? = null
override fun onAttach() {
updateConfiguration()
}
override fun onObservedReadsChanged() {
updateConfiguration()
}
/**
* Handles [LocalRippleConfiguration] changing between null / non-null. Changes to
* [RippleConfiguration.color] and [RippleConfiguration.rippleAlpha] are handled as part of
* the ripple definition.
*/
private fun updateConfiguration() {
observeReads {
val configuration = currentValueOf(LocalRippleConfiguration)
if (configuration == null) {
removeRipple()
} else {
if (rippleNode == null) attachNewRipple()
}
}
}
private fun attachNewRipple() {
val calculateColor = ColorProducer {
val userDefinedColor = color()
if (userDefinedColor.isSpecified) {
userDefinedColor
} else {
// If this is null, the ripple will be removed, so this should always be non-null in
// normal use
val rippleConfiguration = currentValueOf(LocalRippleConfiguration)
if (rippleConfiguration?.color?.isSpecified == true) {
rippleConfiguration.color
} else {
RippleDefaults.rippleColor(
contentColor = currentValueOf(LocalContentColor),
lightTheme = currentValueOf(LocalMyColors).isLight
)
}
}
}
val calculateRippleAlpha = {
// If this is null, the ripple will be removed, so this should always be non-null in
// normal use
val rippleConfiguration = currentValueOf(LocalRippleConfiguration)
rippleConfiguration?.rippleAlpha ?: RippleDefaults.rippleAlpha(
contentColor = currentValueOf(LocalContentColor),
lightTheme = currentValueOf(LocalMyColors).isLight
)
}
rippleNode = delegate(
createRippleModifierNode(
interactionSource,
bounded,
radius,
calculateColor,
calculateRippleAlpha
)
)
}
private fun removeRipple() {
rippleNode?.let { undelegate(it) }
}
}
private val DefaultBoundedRipple = RippleNodeFactory(
bounded = true,
radius = Dp.Unspecified,
color = Color.Unspecified
)
private val DefaultUnboundedRipple = RippleNodeFactory(
bounded = false,
radius = Dp.Unspecified,
color = Color.Unspecified
)
/**
* Alpha values for high luminance content in a light theme.
*
* This content will typically be placed on colored surfaces, so it is important that the
* contrast here is higher to meet accessibility standards, and increase legibility.
*
* These levels are typically used for text / iconography in primary colored tabs /
* bottom navigation / etc.
*/
private val LightThemeHighContrastRippleAlpha = RippleAlpha(
pressedAlpha = 0.24f,
focusedAlpha = 0.24f,
draggedAlpha = 0.16f,
hoveredAlpha = 0.08f
)
/**
* Alpha levels for low luminance content in a light theme.
*
* This content will typically be placed on grayscale surfaces, so the contrast here can be lower
* without sacrificing accessibility and legibility.
*
* These levels are typically used for body text on the main surface (white in light theme, grey
* in dark theme) and text / iconography in surface colored tabs / bottom navigation / etc.
*/
private val LightThemeLowContrastRippleAlpha = RippleAlpha(
pressedAlpha = 0.12f,
focusedAlpha = 0.12f,
draggedAlpha = 0.08f,
hoveredAlpha = 0.04f
)
/**
* Alpha levels for all content in a dark theme.
*/
private val DarkThemeRippleAlpha = RippleAlpha(
pressedAlpha = 0.10f,
focusedAlpha = 0.12f,
draggedAlpha = 0.08f,
hoveredAlpha = 0.04f
)

View File

@ -52,7 +52,7 @@ fun ShowOptions(
.then(itemPadding)
.basicMarquee(
iterations = Int.MAX_VALUE,
delayMillis = 0
initialDelayMillis = 0
),
fontSize = myTextSizes.base,
maxLines = 1,

View File

@ -1,7 +1,7 @@
[versions]
kotlin = "2.0.20"
ksp = "2.0.20-1.0.25"
compose = "1.6.11"
kotlin = "2.0.21"
ksp = "2.0.21-1.0.25"
compose = "1.7.0"
ktorVersion = "2.3.7"
kotlin-serialization = "1.7.2"
okhttp = "4.12.0"

View File

@ -5,7 +5,6 @@ import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.res.ClassLoaderResourceLoader
import androidx.compose.ui.res.painterResource
import okio.FileSystem
import okio.Path.Companion.toPath