mirror of
https://github.com/BetterAndroid/FlexiUI.git
synced 2025-09-08 11:34:18 +08:00
refactor: heavy changes, use new norm rebuild components
This commit is contained in:
@@ -23,6 +23,8 @@
|
||||
|
||||
package com.highcapable.flexiui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -43,6 +45,22 @@ data class Colors(
|
||||
var textSecondary: Color
|
||||
)
|
||||
|
||||
/**
|
||||
* Descriptor for [Colors].
|
||||
*/
|
||||
@Stable
|
||||
internal enum class ColorsDescriptor {
|
||||
BackgroundPrimary,
|
||||
BackgroundSecondary,
|
||||
ForegroundPrimary,
|
||||
ForegroundSecondary,
|
||||
ThemePrimary,
|
||||
ThemeSecondary,
|
||||
ThemeTertiary,
|
||||
TextPrimary,
|
||||
TextSecondary
|
||||
}
|
||||
|
||||
internal val DefaultLightColors = Colors(
|
||||
backgroundPrimary = Color(0xFFF5F5F5),
|
||||
backgroundSecondary = Color(0xFFFAFAFA),
|
||||
@@ -420,3 +438,30 @@ fun blueColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when
|
||||
}
|
||||
|
||||
internal val LocalColors = staticCompositionLocalOf { DefaultLightColors }
|
||||
|
||||
/**
|
||||
* Gets a [Color] from descriptor.
|
||||
* @see ColorsDescriptor.toColor
|
||||
* @param value the descriptor.
|
||||
* @return [Color]
|
||||
*/
|
||||
internal fun Colors.fromDescriptor(value: ColorsDescriptor) = when (value) {
|
||||
ColorsDescriptor.BackgroundPrimary -> backgroundPrimary
|
||||
ColorsDescriptor.BackgroundSecondary -> backgroundSecondary
|
||||
ColorsDescriptor.ForegroundPrimary -> foregroundPrimary
|
||||
ColorsDescriptor.ForegroundSecondary -> foregroundSecondary
|
||||
ColorsDescriptor.ThemePrimary -> themePrimary
|
||||
ColorsDescriptor.ThemeSecondary -> themeSecondary
|
||||
ColorsDescriptor.ThemeTertiary -> themeTertiary
|
||||
ColorsDescriptor.TextPrimary -> textPrimary
|
||||
ColorsDescriptor.TextSecondary -> textSecondary
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a descriptor to a [Color].
|
||||
* @see Colors.fromDescriptor
|
||||
* @return [Color]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
internal fun ColorsDescriptor.toColor() = LocalColors.current.fromDescriptor(this)
|
@@ -78,18 +78,38 @@ fun FlexiTheme(
|
||||
* Defaults of Flexi UI theme styles.
|
||||
*/
|
||||
object FlexiTheme {
|
||||
|
||||
/**
|
||||
* Retrieves the current [Colors] at the call site's position in the hierarchy.
|
||||
* @return [Colors]
|
||||
*/
|
||||
val colors: Colors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalColors.current
|
||||
|
||||
/**
|
||||
* Retrieves the current [Shapes] at the call site's position in the hierarchy.
|
||||
* @return [Shapes]
|
||||
*/
|
||||
val shapes: Shapes
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalShapes.current
|
||||
|
||||
/**
|
||||
* Retrieves the current [Typography] at the call site's position in the hierarchy.
|
||||
* @return [Typography]
|
||||
*/
|
||||
val typography: Typography
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalTypography.current
|
||||
|
||||
/**
|
||||
* Retrieves the current [Sizes] at the call site's position in the hierarchy.
|
||||
* @return [Sizes]
|
||||
*/
|
||||
val sizes: Sizes
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
|
@@ -26,8 +26,12 @@ package com.highcapable.flexiui
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.CornerBasedShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
@@ -40,6 +44,16 @@ data class Shapes(
|
||||
val tertiary: CornerBasedShape
|
||||
)
|
||||
|
||||
/**
|
||||
* Descriptor for [Shapes].
|
||||
*/
|
||||
@Stable
|
||||
internal enum class ShapesDescriptor {
|
||||
Primary,
|
||||
Secondary,
|
||||
Tertiary
|
||||
}
|
||||
|
||||
internal val LocalShapes = staticCompositionLocalOf { DefaultShapes }
|
||||
|
||||
internal val DefaultShapes = Shapes(
|
||||
@@ -47,3 +61,24 @@ internal val DefaultShapes = Shapes(
|
||||
secondary = RoundedCornerShape(10.dp),
|
||||
tertiary = CircleShape
|
||||
)
|
||||
|
||||
/**
|
||||
* Gets a [Shape] from descriptor.
|
||||
* @see ShapesDescriptor.toShape
|
||||
* @param value the descriptor.
|
||||
* @return [Shape]
|
||||
*/
|
||||
internal fun Shapes.fromDescriptor(value: ShapesDescriptor): Shape = when (value) {
|
||||
ShapesDescriptor.Primary -> primary
|
||||
ShapesDescriptor.Secondary -> secondary
|
||||
ShapesDescriptor.Tertiary -> tertiary
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a descriptor to a [Shape].
|
||||
* @see Shapes.fromDescriptor
|
||||
* @return [Shape]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
internal fun ShapesDescriptor.toShape(): Shape = LocalShapes.current.fromDescriptor(this)
|
@@ -23,10 +23,14 @@
|
||||
|
||||
package com.highcapable.flexiui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.highcapable.betterandroid.compose.extension.ui.ComponentPadding
|
||||
|
||||
/**
|
||||
* Sizes defines for Flexi UI.
|
||||
@@ -48,6 +52,85 @@ data class Sizes(
|
||||
val borderSizeTertiary: Dp
|
||||
)
|
||||
|
||||
/**
|
||||
* Descriptor for [Sizes].
|
||||
*/
|
||||
@ExperimentalFlexiUISizesApi
|
||||
@Stable
|
||||
internal enum class SizesDescriptor {
|
||||
SpacingPrimary,
|
||||
SpacingSecondary,
|
||||
SpacingTertiary,
|
||||
IconSizePrimary,
|
||||
IconSizeSecondary,
|
||||
IconSizeTertiary,
|
||||
ZoomSizePrimary,
|
||||
ZoomSizeSecondary,
|
||||
ZoomSizeTertiary,
|
||||
BorderSizePrimary,
|
||||
BorderSizeSecondary,
|
||||
BorderSizeTertiary
|
||||
}
|
||||
|
||||
/**
|
||||
* Descriptor for [ComponentPadding].
|
||||
*/
|
||||
@Stable
|
||||
internal fun PaddingDescriptor(
|
||||
all: SizesDescriptor
|
||||
): PaddingDescriptor = PaddingDescriptorImpl(all, all, all, all)
|
||||
|
||||
/**
|
||||
* Descriptor for [ComponentPadding].
|
||||
*/
|
||||
@Stable
|
||||
internal fun PaddingDescriptor(
|
||||
horizontal: SizesDescriptor? = null,
|
||||
vertical: SizesDescriptor? = null
|
||||
): PaddingDescriptor = PaddingDescriptorImpl(horizontal, vertical, vertical, horizontal)
|
||||
|
||||
/**
|
||||
* Descriptor for [ComponentPadding].
|
||||
*/
|
||||
@Stable
|
||||
internal fun PaddingDescriptor(
|
||||
start: SizesDescriptor? = null,
|
||||
top: SizesDescriptor? = null,
|
||||
bottom: SizesDescriptor? = null,
|
||||
end: SizesDescriptor? = null
|
||||
): PaddingDescriptor = PaddingDescriptorImpl(start, top, bottom, end)
|
||||
|
||||
@Stable
|
||||
internal interface PaddingDescriptor {
|
||||
|
||||
val start: SizesDescriptor?
|
||||
val top: SizesDescriptor?
|
||||
val bottom: SizesDescriptor?
|
||||
val end: SizesDescriptor?
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
fun toPadding(): ComponentPadding
|
||||
}
|
||||
|
||||
@Immutable
|
||||
private class PaddingDescriptorImpl(
|
||||
override val start: SizesDescriptor?,
|
||||
override val top: SizesDescriptor?,
|
||||
override val bottom: SizesDescriptor?,
|
||||
override val end: SizesDescriptor?
|
||||
) : PaddingDescriptor {
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
override fun toPadding() = ComponentPadding(
|
||||
start = start?.toDp() ?: 0.dp,
|
||||
top = top?.toDp() ?: 0.dp,
|
||||
bottom = bottom?.toDp() ?: 0.dp,
|
||||
end = end?.toDp() ?: 0.dp
|
||||
)
|
||||
}
|
||||
|
||||
internal val LocalSizes = staticCompositionLocalOf { DefaultSizes }
|
||||
|
||||
internal val DefaultSizes = Sizes(
|
||||
@@ -65,6 +148,36 @@ internal val DefaultSizes = Sizes(
|
||||
borderSizeTertiary = 0.dp
|
||||
)
|
||||
|
||||
/**
|
||||
* Gets a [Dp] from descriptor.
|
||||
* @see SizesDescriptor.toDp
|
||||
* @param value the descriptor.
|
||||
* @return [Dp]
|
||||
*/
|
||||
internal fun Sizes.fromDescriptor(value: SizesDescriptor) = when (value) {
|
||||
SizesDescriptor.SpacingPrimary -> spacingPrimary
|
||||
SizesDescriptor.SpacingSecondary -> spacingSecondary
|
||||
SizesDescriptor.SpacingTertiary -> spacingTertiary
|
||||
SizesDescriptor.IconSizePrimary -> iconSizePrimary
|
||||
SizesDescriptor.IconSizeSecondary -> iconSizeSecondary
|
||||
SizesDescriptor.IconSizeTertiary -> iconSizeTertiary
|
||||
SizesDescriptor.ZoomSizePrimary -> zoomSizePrimary
|
||||
SizesDescriptor.ZoomSizeSecondary -> zoomSizeSecondary
|
||||
SizesDescriptor.ZoomSizeTertiary -> zoomSizeTertiary
|
||||
SizesDescriptor.BorderSizePrimary -> borderSizePrimary
|
||||
SizesDescriptor.BorderSizeSecondary -> borderSizeSecondary
|
||||
SizesDescriptor.BorderSizeTertiary -> borderSizeTertiary
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a descriptor to a [Dp].
|
||||
* @see Sizes.fromDescriptor
|
||||
* @return [Dp]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
internal fun SizesDescriptor.toDp() = LocalSizes.current.fromDescriptor(this)
|
||||
|
||||
/**
|
||||
* The [Sizes] is experimental, the relevant design specifications for size are still being improved,
|
||||
* this is the old design plan.
|
||||
|
@@ -23,7 +23,10 @@
|
||||
|
||||
package com.highcapable.flexiui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -42,6 +45,18 @@ data class Typography(
|
||||
val secondary: TextStyle
|
||||
)
|
||||
|
||||
/**
|
||||
* Descriptor for [Typography].
|
||||
*/
|
||||
@Stable
|
||||
internal enum class TypographyDescriptor {
|
||||
TitlePrimary,
|
||||
TitleSecondary,
|
||||
Subtitle,
|
||||
Primary,
|
||||
Secondary
|
||||
}
|
||||
|
||||
internal val LocalTypography = staticCompositionLocalOf { DefaultTypography }
|
||||
|
||||
private val DefaultLineHeight = 1.5.em
|
||||
@@ -70,3 +85,26 @@ internal val DefaultTypography = Typography(
|
||||
lineHeight = DefaultLineHeight
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* Gets a [TextStyle] from descriptor.
|
||||
* @see TypographyDescriptor.toTextStyle
|
||||
* @param value the descriptor.
|
||||
* @return [TextStyle]
|
||||
*/
|
||||
internal fun Typography.fromDescriptor(value: TypographyDescriptor) = when (value) {
|
||||
TypographyDescriptor.TitlePrimary -> titlePrimary
|
||||
TypographyDescriptor.TitleSecondary -> titleSecondary
|
||||
TypographyDescriptor.Subtitle -> subtitle
|
||||
TypographyDescriptor.Primary -> primary
|
||||
TypographyDescriptor.Secondary -> secondary
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a descriptor to a [TextStyle].
|
||||
* @see Typography.fromDescriptor
|
||||
* @return [TextStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
internal fun TypographyDescriptor.toTextStyle() = LocalTypography.current.fromDescriptor(this)
|
@@ -38,9 +38,7 @@ import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -49,18 +47,20 @@ import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.highcapable.betterandroid.compose.extension.ui.ComponentPadding
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.LocalTypography
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.PaddingDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.TypographyDescriptor
|
||||
import com.highcapable.flexiui.resources.FlexiIcons
|
||||
import com.highcapable.flexiui.resources.icon.ArrowNaviUp
|
||||
import com.highcapable.flexiui.resources.icon.FinishClose
|
||||
import com.highcapable.flexiui.toColor
|
||||
import com.highcapable.flexiui.toDp
|
||||
import com.highcapable.flexiui.toTextStyle
|
||||
|
||||
/**
|
||||
* Colors defines for app bar.
|
||||
* @param titleTextColor the title text color.
|
||||
* @param subTextColor the sub text color.
|
||||
* @param actionContentColor the action content color, usually for icon tint and text color.
|
||||
* @see AppBarDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class AppBarColors(
|
||||
@@ -71,13 +71,7 @@ data class AppBarColors(
|
||||
|
||||
/**
|
||||
* Style defines for app bar.
|
||||
* @param padding the padding of content.
|
||||
* @param contentSpacing the spacing between the components of content.
|
||||
* @param titleTextStyle the title text style.
|
||||
* @param subTextStyle the sub text style.
|
||||
* @param actionIconSize the size of action icon.
|
||||
* @param actionIconPadding the padding of action icon.
|
||||
* @param actionContentMaxWidth the max width of actions content.
|
||||
* @see AppBarDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class AppBarStyle(
|
||||
@@ -103,8 +97,8 @@ data class AppBarStyle(
|
||||
@Composable
|
||||
fun PrimaryAppBar(
|
||||
modifier: Modifier = Modifier,
|
||||
colors: AppBarColors? = null,
|
||||
style: AppBarStyle? = null,
|
||||
colors: AppBarColors = AppBarDefaults.colors(),
|
||||
style: AppBarStyle = AppBarDefaults.style(type = AppBarType.Primary),
|
||||
titleText: @Composable () -> Unit,
|
||||
subText: @Composable (() -> Unit)? = null,
|
||||
actions: @Composable (AppBarScope.() -> Unit)? = null
|
||||
@@ -137,8 +131,8 @@ fun PrimaryAppBar(
|
||||
@Composable
|
||||
fun SecondaryAppBar(
|
||||
modifier: Modifier = Modifier,
|
||||
colors: AppBarColors? = null,
|
||||
style: AppBarStyle? = null,
|
||||
colors: AppBarColors = AppBarDefaults.colors(),
|
||||
style: AppBarStyle = AppBarDefaults.style(type = AppBarType.Secondary),
|
||||
titleText: @Composable () -> Unit,
|
||||
subText: @Composable (() -> Unit)? = null,
|
||||
finishIcon: @Composable (AppBarScope.() -> Unit)? = null,
|
||||
@@ -165,22 +159,19 @@ fun SecondaryAppBar(
|
||||
private fun BasicAppBar(
|
||||
type: AppBarType,
|
||||
modifier: Modifier,
|
||||
colors: AppBarColors?,
|
||||
style: AppBarStyle?,
|
||||
colors: AppBarColors,
|
||||
style: AppBarStyle,
|
||||
titleText: @Composable () -> Unit,
|
||||
subText: @Composable (() -> Unit)?,
|
||||
finishIcon: @Composable (AppBarScope.() -> Unit)?,
|
||||
navigationIcon: @Composable (AppBarScope.() -> Unit)?,
|
||||
actions: @Composable (AppBarScope.() -> Unit)?
|
||||
) {
|
||||
CompositionLocalProvider(LocalAppBarType provides type) {
|
||||
val currentColors = colors ?: AppBarDefaults.colors
|
||||
val currentStyle = style ?: AppBarDefaults.style
|
||||
Box(modifier = modifier.padding(currentStyle.padding)) {
|
||||
Box(modifier = modifier.padding(style.padding)) {
|
||||
AppBarImpl(
|
||||
type = type,
|
||||
colors = currentColors,
|
||||
style = currentStyle,
|
||||
colors = colors,
|
||||
style = style,
|
||||
titleText = titleText,
|
||||
subText = subText,
|
||||
finishIcon = finishIcon,
|
||||
@@ -188,7 +179,6 @@ private fun BasicAppBar(
|
||||
actions = actions
|
||||
).Content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,8 +200,8 @@ interface AppBarScope {
|
||||
fun FinishIconButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: ButtonColors = IconButtonDefaults.colors,
|
||||
style: ButtonStyle = IconButtonDefaults.style,
|
||||
colors: ButtonColors = IconButtonDefaults.colors(),
|
||||
style: ButtonStyle = IconButtonDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
@@ -238,8 +228,8 @@ interface AppBarScope {
|
||||
fun NavigationIconButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: ButtonColors = IconButtonDefaults.colors,
|
||||
style: ButtonStyle = IconButtonDefaults.style,
|
||||
colors: ButtonColors = IconButtonDefaults.colors(),
|
||||
style: ButtonStyle = IconButtonDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
@@ -267,8 +257,8 @@ interface AppBarScope {
|
||||
fun ActionIconButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: ButtonColors = IconButtonDefaults.colors,
|
||||
style: ButtonStyle = IconButtonDefaults.style,
|
||||
colors: ButtonColors = IconButtonDefaults.colors(),
|
||||
style: ButtonStyle = IconButtonDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable () -> Unit
|
||||
@@ -394,55 +384,99 @@ private class AppBarImpl(
|
||||
@Stable
|
||||
private val AppBarScope.impl get() = this as? AppBarImpl? ?: error("Could not got AppBarScope's impl.")
|
||||
|
||||
/**
|
||||
* App bar's type definition.
|
||||
*/
|
||||
@Stable
|
||||
private enum class AppBarType { Primary, Secondary }
|
||||
enum class AppBarType {
|
||||
/** @see PrimaryAppBar */
|
||||
Primary,
|
||||
|
||||
/** @see SecondaryAppBar */
|
||||
Secondary
|
||||
}
|
||||
|
||||
/**
|
||||
* Defaults of app bar.
|
||||
*/
|
||||
object AppBarDefaults {
|
||||
val colors: AppBarColors
|
||||
|
||||
/**
|
||||
* Creates a [AppBarColors] with the default values.
|
||||
* @param titleTextColor the title text color.
|
||||
* @param subTextColor the sub text color.
|
||||
* @param actionContentColor the action content color, usually for icon tint and text color.
|
||||
* @return [AppBarColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultAppBarColors()
|
||||
val style: AppBarStyle
|
||||
fun colors(
|
||||
titleTextColor: Color = AppBarProperties.TitleTextColor.toColor(),
|
||||
subTextColor: Color = AppBarProperties.SubTextColor.toColor(),
|
||||
actionContentColor: Color = AppBarProperties.ActionContentColor.toColor()
|
||||
) = AppBarColors(
|
||||
titleTextColor = titleTextColor,
|
||||
subTextColor = subTextColor,
|
||||
actionContentColor = actionContentColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [AppBarStyle] with the default values.
|
||||
* @param type the type of app bar.
|
||||
* @param padding the padding of content.
|
||||
* @param contentSpacing the spacing between the components of content.
|
||||
* @param titleTextStyle the title text style.
|
||||
* @param subTextStyle the sub text style.
|
||||
* @param actionIconSize the size of action icon.
|
||||
* @param actionIconPadding the padding of action icon.
|
||||
* @param actionContentMaxWidth the max width of actions content.
|
||||
* @return [AppBarStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultAppBarStyle()
|
||||
fun style(
|
||||
type: AppBarType,
|
||||
padding: ComponentPadding = when {
|
||||
LocalInSurface.current || LocalInAreaBox.current ->
|
||||
AppBarProperties.InBoxPadding
|
||||
else -> AppBarProperties.Padding
|
||||
}.toPadding(),
|
||||
contentSpacing: Dp = AppBarProperties.ContentSpacing.toDp(),
|
||||
titleTextStyle: TextStyle = when (type) {
|
||||
AppBarType.Primary -> AppBarProperties.PrimaryTitleTextStyle
|
||||
AppBarType.Secondary -> AppBarProperties.SecondaryTitleTextStyle
|
||||
}.toTextStyle(),
|
||||
subTextStyle: TextStyle = AppBarProperties.SubTextStyle.toTextStyle(),
|
||||
actionIconSize: Dp = when (type) {
|
||||
AppBarType.Primary -> AppBarProperties.PrimaryActionIconSize
|
||||
AppBarType.Secondary -> AppBarProperties.SecondaryActionIconSize
|
||||
}.toDp(),
|
||||
actionIconPadding: Dp = AppBarProperties.ActionIconPadding.toDp(),
|
||||
actionContentMaxWidth: Dp = AppBarProperties.ActionContentMaxWidth
|
||||
) = AppBarStyle(
|
||||
padding = padding,
|
||||
contentSpacing = contentSpacing,
|
||||
titleTextStyle = titleTextStyle,
|
||||
subTextStyle = subTextStyle,
|
||||
actionIconSize = actionIconSize,
|
||||
actionIconPadding = actionIconPadding,
|
||||
actionContentMaxWidth = actionContentMaxWidth
|
||||
)
|
||||
}
|
||||
|
||||
private val LocalAppBarType = compositionLocalOf { AppBarType.Primary }
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultAppBarColors() = AppBarColors(
|
||||
titleTextColor = LocalColors.current.textPrimary,
|
||||
subTextColor = LocalColors.current.textSecondary,
|
||||
actionContentColor = LocalColors.current.textPrimary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultAppBarStyle() = AppBarStyle(
|
||||
padding = when {
|
||||
LocalInSurface.current || LocalInAreaBox.current ->
|
||||
ComponentPadding(vertical = LocalSizes.current.spacingPrimary)
|
||||
else -> ComponentPadding(LocalSizes.current.spacingPrimary)
|
||||
},
|
||||
contentSpacing = LocalSizes.current.spacingSecondary,
|
||||
titleTextStyle = when (LocalAppBarType.current) {
|
||||
AppBarType.Primary -> LocalTypography.current.titlePrimary
|
||||
AppBarType.Secondary -> LocalTypography.current.titleSecondary
|
||||
},
|
||||
subTextStyle = LocalTypography.current.subtitle,
|
||||
actionIconSize = when (LocalAppBarType.current) {
|
||||
AppBarType.Primary -> LocalSizes.current.iconSizePrimary
|
||||
AppBarType.Secondary -> LocalSizes.current.iconSizeSecondary
|
||||
},
|
||||
actionIconPadding = LocalSizes.current.spacingTertiary,
|
||||
actionContentMaxWidth = DefaultActionContentMaxWidth
|
||||
)
|
||||
|
||||
private val DefaultActionContentMaxWidth = 170.dp
|
||||
@Stable
|
||||
internal object AppBarProperties {
|
||||
val TitleTextColor = ColorsDescriptor.TextPrimary
|
||||
val SubTextColor = ColorsDescriptor.TextSecondary
|
||||
val ActionContentColor = ColorsDescriptor.TextPrimary
|
||||
val Padding = PaddingDescriptor(SizesDescriptor.SpacingPrimary)
|
||||
val InBoxPadding = PaddingDescriptor(vertical = SizesDescriptor.SpacingPrimary)
|
||||
val ContentSpacing = SizesDescriptor.SpacingSecondary
|
||||
val PrimaryTitleTextStyle = TypographyDescriptor.TitlePrimary
|
||||
val SecondaryTitleTextStyle = TypographyDescriptor.TitleSecondary
|
||||
val SubTextStyle = TypographyDescriptor.Subtitle
|
||||
val PrimaryActionIconSize = SizesDescriptor.IconSizePrimary
|
||||
val SecondaryActionIconSize = SizesDescriptor.IconSizeSecondary
|
||||
val ActionIconPadding = SizesDescriptor.SpacingTertiary
|
||||
val ActionContentMaxWidth = 170.dp
|
||||
}
|
||||
|
||||
private const val VerticalContentSpacingRatio = 1.6f
|
@@ -23,7 +23,6 @@
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -36,7 +35,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -50,23 +49,33 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.highcapable.betterandroid.compose.extension.ui.ComponentPadding
|
||||
import com.highcapable.betterandroid.compose.extension.ui.borderOrElse
|
||||
import com.highcapable.flexiui.DefaultShapes
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalShapes
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.PaddingDescriptor
|
||||
import com.highcapable.flexiui.ShapesDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.toColor
|
||||
import com.highcapable.flexiui.toDp
|
||||
import com.highcapable.flexiui.toShape
|
||||
|
||||
/**
|
||||
* Colors defines for area box.
|
||||
* @see AreaBoxDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class AreaBoxColors(
|
||||
val backgroundColor: Color,
|
||||
val borderColor: Color
|
||||
)
|
||||
|
||||
/**
|
||||
* Style defines for area box.
|
||||
* @param padding the padding of content.
|
||||
* @param shape the shape of the box.
|
||||
* @param border the border stroke of the box.
|
||||
* @param shadowSize the shadow size of the box.
|
||||
* @see AreaBoxDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class AreaBoxStyle(
|
||||
val padding: ComponentPadding,
|
||||
val shape: Shape,
|
||||
val border: BorderStroke,
|
||||
val borderWidth: Dp,
|
||||
val shadowSize: Dp
|
||||
)
|
||||
|
||||
@@ -76,7 +85,7 @@ data class AreaBoxStyle(
|
||||
* @see AreaColumn
|
||||
* @param modifier the [Modifier] to be applied to this area box.
|
||||
* @param initializer the [Modifier] initializer, earlies than [modifier].
|
||||
* @param color the background color of this area box, default is [AreaBoxDefaults.color].
|
||||
* @param colors the colors of this area box, default is [AreaBoxDefaults.colors].
|
||||
* @param style the style of this area box, default is [AreaBoxDefaults.style].
|
||||
* @param contentAlignment the alignment of the content inside this area box, default is [Alignment.TopStart].
|
||||
* @param propagateMinConstraints whether to propagate the min constraints from the content to this area box.
|
||||
@@ -86,8 +95,8 @@ data class AreaBoxStyle(
|
||||
fun AreaBox(
|
||||
modifier: Modifier = Modifier,
|
||||
initializer: @Composable Modifier.() -> Modifier = { Modifier },
|
||||
color: Color = AreaBoxDefaults.color,
|
||||
style: AreaBoxStyle = AreaBoxDefaults.style,
|
||||
colors: AreaBoxColors = AreaBoxDefaults.colors(),
|
||||
style: AreaBoxStyle = AreaBoxDefaults.style(),
|
||||
contentAlignment: Alignment = Alignment.TopStart,
|
||||
propagateMinConstraints: Boolean = false,
|
||||
content: @Composable BoxScope.() -> Unit
|
||||
@@ -97,7 +106,7 @@ fun AreaBox(
|
||||
LocalAreaBoxShape provides style.shape
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.areaBox(color, style, modifier, initializer),
|
||||
modifier = Modifier.areaBox(colors, style, modifier, initializer),
|
||||
contentAlignment = contentAlignment,
|
||||
propagateMinConstraints = propagateMinConstraints,
|
||||
content = content
|
||||
@@ -111,7 +120,7 @@ fun AreaBox(
|
||||
* @see AreaBox
|
||||
* @param modifier the [Modifier] to be applied to this area row.
|
||||
* @param initializer the [Modifier] initializer, earlies than [modifier].
|
||||
* @param color the background color of this area row, default is [AreaBoxDefaults.color].
|
||||
* @param colors the colors of this area row, default is [AreaBoxDefaults.colors].
|
||||
* @param style the style of this area row, default is [AreaBoxDefaults.style].
|
||||
* @param horizontalArrangement the horizontal arrangement of the content inside this area row, default is [Arrangement.Start].
|
||||
* @param verticalAlignment the vertical alignment of the content inside this area row, default is [Alignment.Top].
|
||||
@@ -121,8 +130,8 @@ fun AreaBox(
|
||||
fun AreaRow(
|
||||
modifier: Modifier = Modifier,
|
||||
initializer: @Composable Modifier.() -> Modifier = { Modifier },
|
||||
color: Color = AreaBoxDefaults.color,
|
||||
style: AreaBoxStyle = AreaBoxDefaults.style,
|
||||
colors: AreaBoxColors = AreaBoxDefaults.colors(),
|
||||
style: AreaBoxStyle = AreaBoxDefaults.style(),
|
||||
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
|
||||
verticalAlignment: Alignment.Vertical = Alignment.Top,
|
||||
content: @Composable RowScope.() -> Unit
|
||||
@@ -132,7 +141,7 @@ fun AreaRow(
|
||||
LocalAreaBoxShape provides style.shape
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.areaBox(color, style, modifier, initializer),
|
||||
modifier = Modifier.areaBox(colors, style, modifier, initializer),
|
||||
horizontalArrangement = horizontalArrangement,
|
||||
verticalAlignment = verticalAlignment,
|
||||
content = content
|
||||
@@ -146,7 +155,7 @@ fun AreaRow(
|
||||
* @see AreaBox
|
||||
* @param modifier the [Modifier] to be applied to this area column.
|
||||
* @param initializer the [Modifier] initializer, earlies than [modifier].
|
||||
* @param color the background color of this area column, default is [AreaBoxDefaults.color].
|
||||
* @param colors the colors of this area column, default is [AreaBoxDefaults.colors].
|
||||
* @param style the style of this area column, default is [AreaBoxDefaults.style].
|
||||
* @param verticalArrangement the vertical arrangement of the content inside this area column, default is [Arrangement.Top].
|
||||
* @param horizontalAlignment the horizontal alignment of the content inside this area column, default is [Alignment.Start].
|
||||
@@ -156,8 +165,8 @@ fun AreaRow(
|
||||
fun AreaColumn(
|
||||
modifier: Modifier = Modifier,
|
||||
initializer: @Composable Modifier.() -> Modifier = { Modifier },
|
||||
color: Color = AreaBoxDefaults.color,
|
||||
style: AreaBoxStyle = AreaBoxDefaults.style,
|
||||
colors: AreaBoxColors = AreaBoxDefaults.colors(),
|
||||
style: AreaBoxStyle = AreaBoxDefaults.style(),
|
||||
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
|
||||
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
@@ -167,7 +176,7 @@ fun AreaColumn(
|
||||
LocalAreaBoxShape provides style.shape
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.areaBox(color, style, modifier, initializer),
|
||||
modifier = Modifier.areaBox(colors, style, modifier, initializer),
|
||||
verticalArrangement = verticalArrangement,
|
||||
horizontalAlignment = horizontalAlignment,
|
||||
content = content
|
||||
@@ -176,22 +185,22 @@ fun AreaColumn(
|
||||
}
|
||||
|
||||
private fun Modifier.areaBox(
|
||||
color: Color,
|
||||
colors: AreaBoxColors,
|
||||
style: AreaBoxStyle,
|
||||
then: Modifier,
|
||||
initializer: @Composable Modifier.() -> Modifier
|
||||
) = composed(
|
||||
inspectorInfo = debugInspectorInfo {
|
||||
name = "areaBox"
|
||||
properties["color"] = color
|
||||
properties["colors"] = colors
|
||||
properties["style"] = style
|
||||
}
|
||||
) {
|
||||
initializer()
|
||||
.shadow(style.shadowSize, style.shape)
|
||||
.clip(style.shape)
|
||||
.background(color, style.shape)
|
||||
.borderOrElse(style.border, style.shape)
|
||||
.background(colors.backgroundColor, style.shape)
|
||||
.borderOrElse(style.borderWidth, colors.borderColor, style.shape)
|
||||
.then(then)
|
||||
.padding(style.padding)
|
||||
}
|
||||
@@ -200,47 +209,73 @@ private fun Modifier.areaBox(
|
||||
* Defaults of area box.
|
||||
*/
|
||||
object AreaBoxDefaults {
|
||||
val color: Color
|
||||
|
||||
/**
|
||||
* Creates a [AreaBoxColors] with the default values.
|
||||
* @param backgroundColor the background color of the box.
|
||||
* @param borderColor the border color of the box.
|
||||
* @return [AreaBoxColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultAreaBoxColor()
|
||||
val style: AreaBoxStyle
|
||||
fun colors(
|
||||
backgroundColor: Color = AreaBoxProperties.BackgroundColor.toColor(),
|
||||
borderColor: Color = AreaBoxProperties.BorderColor.toColor()
|
||||
) = AreaBoxColors(
|
||||
backgroundColor = backgroundColor,
|
||||
borderColor = borderColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [AreaBoxStyle] with the default values.
|
||||
* @param padding the padding of content.
|
||||
* @param shape the shape of the box.
|
||||
* @param borderWidth the border width of the box.
|
||||
* @param shadowSize the shadow size of the box.
|
||||
* @return [AreaBoxStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultAreaBoxStyle()
|
||||
fun style(
|
||||
padding: ComponentPadding = AreaBoxProperties.Padding.toPadding(),
|
||||
shape: Shape = AreaBoxProperties.Shape.toShape(),
|
||||
borderWidth: Dp = AreaBoxProperties.BorderWidth.toDp(),
|
||||
shadowSize: Dp = AreaBoxProperties.ShadowSize
|
||||
) = AreaBoxStyle(
|
||||
padding = padding,
|
||||
shape = shape,
|
||||
borderWidth = borderWidth,
|
||||
shadowSize = shadowSize
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns the child components shape of the current area box.
|
||||
*
|
||||
* Design specification: The shape of the components inside the area box
|
||||
* should be consistent with the shape of the area box.
|
||||
* @param inBox the shape of inner area box.
|
||||
* @param outBox the shape of outside area box.
|
||||
* @return [Shape]
|
||||
*/
|
||||
@Composable
|
||||
fun childShape(
|
||||
inBox: Shape = LocalAreaBoxShape.current ?: AreaBoxProperties.Shape.toShape(),
|
||||
outBox: Shape = AreaBoxProperties.OutBoxShape.toShape()
|
||||
) = when (LocalInAreaBox.current) {
|
||||
true -> inBox
|
||||
else -> outBox
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
internal object AreaBoxProperties {
|
||||
val BackgroundColor = ColorsDescriptor.ForegroundPrimary
|
||||
val BorderColor = ColorsDescriptor.TextPrimary
|
||||
val Padding = PaddingDescriptor(SizesDescriptor.SpacingPrimary)
|
||||
val Shape = ShapesDescriptor.Primary
|
||||
val OutBoxShape = ShapesDescriptor.Secondary
|
||||
val BorderWidth = SizesDescriptor.BorderSizeTertiary
|
||||
val ShadowSize = 0.dp
|
||||
}
|
||||
|
||||
internal val LocalInAreaBox = compositionLocalOf { false }
|
||||
|
||||
internal val LocalAreaBoxShape = compositionLocalOf { DefaultAreaBoxShape }
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
internal fun withAreaBoxShape(
|
||||
inBox: Shape = LocalAreaBoxShape.current,
|
||||
outBox: Shape = LocalShapes.current.secondary
|
||||
) = when (LocalInAreaBox.current) {
|
||||
true -> inBox
|
||||
else -> outBox
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultAreaBoxStyle() = AreaBoxStyle(
|
||||
padding = ComponentPadding(LocalSizes.current.spacingPrimary),
|
||||
shape = LocalAreaBoxShape.current,
|
||||
border = defaultAreaBoxBorder(),
|
||||
shadowSize = DefaultAreaBoxShadowSize
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultAreaBoxColor() = LocalColors.current.foregroundPrimary
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultAreaBoxBorder() = BorderStroke(LocalSizes.current.borderSizeTertiary, LocalColors.current.textPrimary)
|
||||
|
||||
private val DefaultAreaBoxShape: Shape = DefaultShapes.primary
|
||||
|
||||
private val DefaultAreaBoxShadowSize = 0.dp
|
||||
internal val LocalAreaBoxShape = compositionLocalOf<Shape?> { null }
|
@@ -19,11 +19,10 @@
|
||||
*
|
||||
* This file is created by fankes on 2023/11/5.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
@file:Suppress("unused", "ConstPropertyName")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -33,7 +32,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -44,42 +43,46 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.platform.debugInspectorInfo
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.highcapable.betterandroid.compose.extension.ui.ComponentPadding
|
||||
import com.highcapable.betterandroid.compose.extension.ui.borderOrElse
|
||||
import com.highcapable.betterandroid.compose.extension.ui.componentState
|
||||
import com.highcapable.betterandroid.compose.extension.ui.orNull
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalShapes
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.PaddingDescriptor
|
||||
import com.highcapable.flexiui.ShapesDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.component.interaction.InteractionDefaults
|
||||
import com.highcapable.flexiui.component.interaction.RippleStyle
|
||||
import com.highcapable.flexiui.component.interaction.rippleClickable
|
||||
import com.highcapable.flexiui.component.interaction.rippleToggleable
|
||||
import com.highcapable.flexiui.toColor
|
||||
import com.highcapable.flexiui.toDp
|
||||
import com.highcapable.flexiui.toShape
|
||||
|
||||
/**
|
||||
* Colors defines for button.
|
||||
* @param contentColor the content color, usually for icon tint and text color.
|
||||
* @param backgroundColor the background color.
|
||||
* @see ButtonDefaults.colors
|
||||
* @see IconButtonDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class ButtonColors(
|
||||
val contentColor: Color,
|
||||
val backgroundColor: Color
|
||||
val backgroundColor: Color,
|
||||
val borderColor: Color
|
||||
)
|
||||
|
||||
/**
|
||||
* Style defines for button.
|
||||
* @param rippleStyle the ripple style of this button.
|
||||
* @param padding the padding of content.
|
||||
* @param shape the shape.
|
||||
* @param border the border stroke.
|
||||
* @see ButtonDefaults.style
|
||||
* @see IconButtonDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class ButtonStyle(
|
||||
val rippleStyle: RippleStyle,
|
||||
val padding: ComponentPadding,
|
||||
val shape: Shape,
|
||||
val border: BorderStroke
|
||||
val borderWidth: Dp
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -100,8 +103,8 @@ data class ButtonStyle(
|
||||
fun Button(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: ButtonColors = ButtonDefaults.colors,
|
||||
style: ButtonStyle = ButtonDefaults.style,
|
||||
colors: ButtonColors = ButtonDefaults.colors(),
|
||||
style: ButtonStyle = ButtonDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
header: @Composable () -> Unit = {},
|
||||
@@ -109,7 +112,7 @@ fun Button(
|
||||
content: @Composable RowScope.() -> Unit
|
||||
) {
|
||||
val localTextStyle = LocalTextStyle.current.copy(color = colors.contentColor)
|
||||
val localProgressIndicatorColors = LocalProgressIndicatorColors.current.copy(
|
||||
val localProgressIndicatorColors = LocalProgressIndicatorColors.current?.copy(
|
||||
foregroundColor = colors.contentColor,
|
||||
backgroundColor = Color.Transparent
|
||||
)
|
||||
@@ -161,8 +164,8 @@ fun Button(
|
||||
fun IconButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: ButtonColors = IconButtonDefaults.colors,
|
||||
style: ButtonStyle = IconButtonDefaults.style,
|
||||
colors: ButtonColors = IconButtonDefaults.colors(),
|
||||
style: ButtonStyle = IconButtonDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable () -> Unit
|
||||
@@ -181,12 +184,7 @@ fun IconButton(
|
||||
onClick = onClick
|
||||
).padding(style.padding),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalIconStyle provides LocalIconStyle.current.copy(tint = colors.contentColor),
|
||||
content = content
|
||||
)
|
||||
}
|
||||
) { IconButtonStyle(colors, content) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,8 +205,8 @@ fun IconToggleButton(
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: ButtonColors = IconButtonDefaults.colors,
|
||||
style: ButtonStyle = IconButtonDefaults.style,
|
||||
colors: ButtonColors = IconButtonDefaults.colors(),
|
||||
style: ButtonStyle = IconButtonDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable () -> Unit
|
||||
@@ -228,12 +226,21 @@ fun IconToggleButton(
|
||||
interactionSource = interactionSource
|
||||
).padding(style.padding),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
) { IconButtonStyle(colors, content) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun IconButtonStyle(
|
||||
colors: ButtonColors,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
// The provided tint should have the highest priority,
|
||||
// this will allow the user to override the current style through [LocalIconStyle].
|
||||
val iconStyle = LocalIconStyle.current.let { it.copy(tint = it.tint.orNull() ?: colors.contentColor) }
|
||||
CompositionLocalProvider(
|
||||
LocalIconStyle provides LocalIconStyle.current.copy(tint = colors.contentColor),
|
||||
LocalIconStyle provides iconStyle,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Modifier.button(
|
||||
@@ -252,7 +259,7 @@ private fun Modifier.button(
|
||||
componentState(enabled)
|
||||
.clip(style.shape)
|
||||
.background(colors.backgroundColor, style.shape)
|
||||
.borderOrElse(style.border, style.shape)
|
||||
.borderOrElse(style.borderWidth, colors.borderColor, style.shape)
|
||||
.then(then)
|
||||
}
|
||||
|
||||
@@ -260,104 +267,127 @@ private fun Modifier.button(
|
||||
* Defaults of button.
|
||||
*/
|
||||
object ButtonDefaults {
|
||||
val colors: ButtonColors
|
||||
|
||||
/**
|
||||
* Creates a [ButtonColors] with the default values.
|
||||
* @param contentColor the content color, usually for icon tint and text color.
|
||||
* @param backgroundColor the background color.
|
||||
* @param borderColor the border color.
|
||||
* @return [ButtonColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultButtonColors()
|
||||
val style: ButtonStyle
|
||||
fun colors(
|
||||
contentColor: Color = when {
|
||||
LocalPrimaryButton.current -> ButtonProperties.PrimaryContentColor
|
||||
LocalInAreaBox.current -> ButtonProperties.ContentColor.toColor()
|
||||
else -> ButtonProperties.PrimaryContentColor
|
||||
},
|
||||
backgroundColor: Color = when {
|
||||
LocalPrimaryButton.current -> ButtonProperties.PrimaryBackgroundColor
|
||||
LocalInAreaBox.current -> ButtonProperties.BackgroundColor
|
||||
else -> ButtonProperties.PrimaryBackgroundColor
|
||||
}.toColor(),
|
||||
borderColor: Color = ButtonProperties.BorderColor.toColor()
|
||||
) = ButtonColors(
|
||||
contentColor = contentColor,
|
||||
backgroundColor = backgroundColor,
|
||||
borderColor = borderColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [ButtonStyle] with the default values.
|
||||
* @param rippleStyle the ripple style of this button.
|
||||
* @param padding the padding of content.
|
||||
* @param shape the shape.
|
||||
* @param borderWidth the border width.
|
||||
* @return [ButtonStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultButtonStyle()
|
||||
fun style(
|
||||
rippleStyle: RippleStyle = InteractionDefaults.rippleStyle(color = when {
|
||||
LocalPrimaryButton.current -> ButtonProperties.PrimaryRippleColor
|
||||
LocalInAreaBox.current -> ButtonProperties.RippleColor
|
||||
else -> ButtonProperties.PrimaryRippleColor
|
||||
}.toColor()),
|
||||
padding: ComponentPadding = ButtonProperties.Padding.toPadding(),
|
||||
shape: Shape = AreaBoxDefaults.childShape(),
|
||||
borderWidth: Dp = ButtonProperties.BorderWidth.toDp()
|
||||
) = ButtonStyle(
|
||||
rippleStyle = rippleStyle,
|
||||
padding = padding,
|
||||
shape = shape,
|
||||
borderWidth = borderWidth
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Defaults of icon button.
|
||||
*/
|
||||
object IconButtonDefaults {
|
||||
val colors: ButtonColors
|
||||
|
||||
/**
|
||||
* Creates a [ButtonColors] with the default values.
|
||||
* @param contentColor the content color, usually for icon tint and text color.
|
||||
* @param backgroundColor the background color.
|
||||
* @param borderColor the border color.
|
||||
* @return [ButtonColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultIconButtonColors()
|
||||
val style: ButtonStyle
|
||||
fun colors(
|
||||
contentColor: Color = IconButtonProperties.ContentColor.toColor(),
|
||||
backgroundColor: Color = IconButtonProperties.BackgroundColor,
|
||||
borderColor: Color = ButtonProperties.BorderColor.toColor()
|
||||
) = ButtonColors(
|
||||
contentColor = contentColor,
|
||||
backgroundColor = backgroundColor,
|
||||
borderColor = borderColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [ButtonStyle] with the default values.
|
||||
* @param rippleStyle the ripple style of this button.
|
||||
* @param padding the padding of content.
|
||||
* @param shape the shape.
|
||||
* @param borderWidth the border width.
|
||||
* @return [ButtonStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultIconButtonStyle()
|
||||
fun style(
|
||||
rippleStyle: RippleStyle = InteractionDefaults.rippleStyle(bounded = IconButtonProperties.RippleBounded),
|
||||
padding: ComponentPadding = IconButtonProperties.Padding,
|
||||
shape: Shape = IconButtonProperties.Shape.toShape(),
|
||||
borderWidth: Dp = ButtonProperties.BorderWidth.toDp()
|
||||
) = ButtonStyle(
|
||||
rippleStyle = rippleStyle,
|
||||
padding = padding,
|
||||
shape = shape,
|
||||
borderWidth = borderWidth
|
||||
)
|
||||
}
|
||||
|
||||
@Stable
|
||||
internal object ButtonProperties {
|
||||
val PrimaryContentColor = Color.White
|
||||
val ContentColor = ColorsDescriptor.TextPrimary
|
||||
val PrimaryBackgroundColor = ColorsDescriptor.ThemePrimary
|
||||
val BackgroundColor = ColorsDescriptor.ForegroundSecondary
|
||||
val BorderColor = ColorsDescriptor.TextPrimary
|
||||
val PrimaryRippleColor = ColorsDescriptor.ForegroundSecondary
|
||||
val RippleColor = ColorsDescriptor.ThemeSecondary
|
||||
val Padding = PaddingDescriptor(
|
||||
horizontal = SizesDescriptor.SpacingPrimary,
|
||||
vertical = SizesDescriptor.SpacingSecondary
|
||||
)
|
||||
val BorderWidth = SizesDescriptor.BorderSizeTertiary
|
||||
}
|
||||
|
||||
@Stable
|
||||
internal object IconButtonProperties {
|
||||
val ContentColor = ColorsDescriptor.ThemePrimary
|
||||
val BackgroundColor = Color.Transparent
|
||||
const val RippleBounded = false
|
||||
val Padding = ComponentPadding()
|
||||
val Shape = ShapesDescriptor.Tertiary
|
||||
}
|
||||
|
||||
internal val LocalPrimaryButton = compositionLocalOf { false }
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultPrimaryButtonColors() = ButtonColors(
|
||||
contentColor = Color.White,
|
||||
backgroundColor = LocalColors.current.themePrimary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultContentButtonColors() = ButtonColors(
|
||||
contentColor = LocalColors.current.textPrimary,
|
||||
backgroundColor = LocalColors.current.foregroundSecondary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultPrimaryButtonRippleStyle() =
|
||||
InteractionDefaults.rippleStyle.copy(color = LocalColors.current.foregroundSecondary)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultContentButtonRippleStyle() =
|
||||
InteractionDefaults.rippleStyle.copy(color = LocalColors.current.themeSecondary)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultIconButtonRippleStyle() = InteractionDefaults.rippleStyle.copy(bounded = false)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultButtonColors() = when {
|
||||
LocalPrimaryButton.current -> defaultPrimaryButtonColors()
|
||||
LocalInAreaBox.current -> defaultContentButtonColors()
|
||||
else -> defaultPrimaryButtonColors()
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultButtonStyle() = ButtonStyle(
|
||||
rippleStyle = defaultButtonRippleStyle(),
|
||||
padding = ComponentPadding(
|
||||
horizontal = LocalSizes.current.spacingPrimary,
|
||||
vertical = LocalSizes.current.spacingSecondary
|
||||
),
|
||||
shape = withAreaBoxShape(),
|
||||
border = defaultButtonBorder()
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultButtonRippleStyle() = when {
|
||||
LocalPrimaryButton.current -> defaultPrimaryButtonRippleStyle()
|
||||
LocalInAreaBox.current -> defaultContentButtonRippleStyle()
|
||||
else -> defaultPrimaryButtonRippleStyle()
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultIconButtonColors() = ButtonColors(
|
||||
contentColor = LocalIconStyle.current.tint.orNull() ?: LocalColors.current.themePrimary,
|
||||
backgroundColor = Color.Transparent
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultIconButtonStyle() = ButtonStyle(
|
||||
rippleStyle = defaultIconButtonRippleStyle(),
|
||||
padding = ComponentPadding(),
|
||||
shape = LocalShapes.current.tertiary,
|
||||
border = defaultButtonBorder()
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultButtonBorder() = BorderStroke(LocalSizes.current.borderSizeTertiary, LocalColors.current.textPrimary)
|
@@ -19,13 +19,12 @@
|
||||
*
|
||||
* This file is created by fankes on 2023/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
@file:Suppress("unused", "ConstPropertyName")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsHoveredAsState
|
||||
@@ -39,7 +38,7 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -55,33 +54,28 @@ import androidx.compose.ui.unit.dp
|
||||
import com.highcapable.betterandroid.compose.extension.ui.borderOrElse
|
||||
import com.highcapable.betterandroid.compose.extension.ui.clickable
|
||||
import com.highcapable.betterandroid.compose.extension.ui.componentState
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.resources.FlexiIcons
|
||||
import com.highcapable.flexiui.resources.icon.CheckMark
|
||||
import com.highcapable.flexiui.toColor
|
||||
import com.highcapable.flexiui.toDp
|
||||
|
||||
/**
|
||||
* Colors defines for check box.
|
||||
* @param contentColor the color of the check mark.
|
||||
* @param inactiveColor the color of the unchecked box.
|
||||
* @param activeColor the color of the checked box.
|
||||
* @see CheckBoxDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class CheckBoxColors(
|
||||
val contentColor: Color,
|
||||
val inactiveColor: Color,
|
||||
val activeColor: Color
|
||||
val activeColor: Color,
|
||||
val borderColor: Color
|
||||
)
|
||||
|
||||
/**
|
||||
* Style defines for check box.
|
||||
* @param contentSpacing the spacing between the check mark and the content.
|
||||
* @param contentSize the size of the check mark.
|
||||
* @param strokeSize the stroke size.
|
||||
* @param pressedGain the gain when pressed.
|
||||
* @param hoveredGain the gain when hovered.
|
||||
* @param shape the shape.
|
||||
* @param border the border stroke.
|
||||
* @see CheckBoxDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class CheckBoxStyle(
|
||||
@@ -91,7 +85,7 @@ data class CheckBoxStyle(
|
||||
val pressedGain: Float,
|
||||
val hoveredGain: Float,
|
||||
val shape: Shape,
|
||||
val border: BorderStroke
|
||||
val borderWidth: Dp
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -110,8 +104,8 @@ fun CheckBox(
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: CheckBoxColors = CheckBoxDefaults.colors,
|
||||
style: CheckBoxStyle = CheckBoxDefaults.style,
|
||||
colors: CheckBoxColors = CheckBoxDefaults.colors(),
|
||||
style: CheckBoxStyle = CheckBoxDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable (RowScope.() -> Unit)? = null
|
||||
@@ -133,7 +127,7 @@ fun CheckBox(
|
||||
.size(style.strokeSize)
|
||||
.scale(animatedStrokeScale)
|
||||
.background(animatedColor, style.shape)
|
||||
.borderOrElse(style.border, style.shape),
|
||||
.borderOrElse(style.borderWidth, colors.borderColor, style.shape),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
@@ -146,7 +140,7 @@ fun CheckBox(
|
||||
scaleY = animatedContentLayer
|
||||
),
|
||||
imageVector = FlexiIcons.CheckMark,
|
||||
style = IconDefaults.style.copy(tint = colors.contentColor)
|
||||
style = IconDefaults.style(tint = colors.contentColor)
|
||||
)
|
||||
}
|
||||
content?.also { content ->
|
||||
@@ -162,44 +156,70 @@ fun CheckBox(
|
||||
* Defaults of check box.
|
||||
*/
|
||||
object CheckBoxDefaults {
|
||||
val colors: CheckBoxColors
|
||||
|
||||
/**
|
||||
* Creates a [CheckBoxColors] with the default values.
|
||||
* @param contentColor the color of the check mark.
|
||||
* @param inactiveColor the color of the unchecked box.
|
||||
* @param activeColor the color of the checked box.
|
||||
* @param borderColor the color of the border.
|
||||
* @return [CheckBoxColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultCheckBoxColors()
|
||||
val style: CheckBoxStyle
|
||||
fun colors(
|
||||
contentColor: Color = CheckBoxProperties.ContentColor,
|
||||
inactiveColor: Color = CheckBoxProperties.InactiveColor.toColor(),
|
||||
activeColor: Color = CheckBoxProperties.ActiveColor.toColor(),
|
||||
borderColor: Color = CheckBoxProperties.BorderColor.toColor()
|
||||
) = CheckBoxColors(
|
||||
contentColor = contentColor,
|
||||
inactiveColor = inactiveColor,
|
||||
activeColor = activeColor,
|
||||
borderColor = borderColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [CheckBoxStyle] with the default values.
|
||||
* @param contentSpacing the spacing between the check mark and the content.
|
||||
* @param contentSize the size of the check mark.
|
||||
* @param strokeSize the stroke size.
|
||||
* @param pressedGain the gain when pressed.
|
||||
* @param hoveredGain the gain when hovered.
|
||||
* @param shape the shape.
|
||||
* @param borderWidth the border width.
|
||||
* @return [CheckBoxStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultCheckBoxStyle()
|
||||
fun style(
|
||||
contentSpacing: Dp = CheckBoxProperties.ContentSpacing.toDp(),
|
||||
contentSize: Dp = CheckBoxProperties.ContentSize,
|
||||
strokeSize: Dp = CheckBoxProperties.StrokeSize,
|
||||
pressedGain: Float = CheckBoxProperties.PressedGain,
|
||||
hoveredGain: Float = CheckBoxProperties.HoveredGain,
|
||||
shape: Shape = CheckBoxProperties.Shape,
|
||||
borderWidth: Dp = CheckBoxProperties.BorderWidth.toDp()
|
||||
) = CheckBoxStyle(
|
||||
contentSpacing = contentSpacing,
|
||||
contentSize = contentSize,
|
||||
strokeSize = strokeSize,
|
||||
pressedGain = pressedGain,
|
||||
hoveredGain = hoveredGain,
|
||||
shape = shape,
|
||||
borderWidth = borderWidth
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultCheckBoxColors() = CheckBoxColors(
|
||||
contentColor = Color.White,
|
||||
inactiveColor = LocalColors.current.themeTertiary,
|
||||
activeColor = LocalColors.current.themePrimary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultCheckBoxStyle() = CheckBoxStyle(
|
||||
contentSpacing = LocalSizes.current.spacingSecondary,
|
||||
contentSize = DefaultContentSize,
|
||||
strokeSize = DefaultStrokeSize,
|
||||
pressedGain = DefaultPressedGain,
|
||||
hoveredGain = DefaultHoveredGain,
|
||||
shape = DefaultCheckBoxShape,
|
||||
border = defaultCheckBoxBorder()
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultCheckBoxBorder() = BorderStroke(LocalSizes.current.borderSizeTertiary, LocalColors.current.textPrimary)
|
||||
|
||||
private val DefaultContentSize = 13.dp
|
||||
private val DefaultStrokeSize = 20.dp
|
||||
|
||||
private val DefaultCheckBoxShape = RoundedCornerShape(4.dp)
|
||||
|
||||
private const val DefaultPressedGain = 0.9f
|
||||
private const val DefaultHoveredGain = 1.1f
|
||||
@Stable
|
||||
internal object CheckBoxProperties {
|
||||
val ContentColor = Color.White
|
||||
val InactiveColor = ColorsDescriptor.ThemeTertiary
|
||||
val ActiveColor = ColorsDescriptor.ThemePrimary
|
||||
val BorderColor = ColorsDescriptor.TextPrimary
|
||||
val ContentSpacing = SizesDescriptor.SpacingSecondary
|
||||
val ContentSize = 13.dp
|
||||
val StrokeSize = 20.dp
|
||||
const val PressedGain = 0.9f
|
||||
const val HoveredGain = 1.1f
|
||||
val Shape: Shape = RoundedCornerShape(4.dp)
|
||||
val BorderWidth = SizesDescriptor.BorderSizeTertiary
|
||||
}
|
@@ -19,7 +19,7 @@
|
||||
*
|
||||
* This file is created by fankes on 2023/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
@file:Suppress("unused", "ConstPropertyName")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
@@ -31,7 +31,6 @@ import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.focusable
|
||||
@@ -60,7 +59,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -77,7 +75,6 @@ import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.TransformOrigin
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.InputMode
|
||||
@@ -106,77 +103,72 @@ import com.highcapable.betterandroid.compose.extension.ui.ComponentPadding
|
||||
import com.highcapable.betterandroid.compose.extension.ui.borderOrElse
|
||||
import com.highcapable.betterandroid.compose.extension.ui.componentState
|
||||
import com.highcapable.betterandroid.compose.extension.ui.orNull
|
||||
import com.highcapable.betterandroid.compose.extension.ui.solidColor
|
||||
import com.highcapable.betterandroid.compose.extension.ui.window.Popup
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalShapes
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.PaddingDescriptor
|
||||
import com.highcapable.flexiui.ShapesDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.component.interaction.rippleClickable
|
||||
import com.highcapable.flexiui.resources.FlexiIcons
|
||||
import com.highcapable.flexiui.resources.icon.Dropdown
|
||||
import com.highcapable.flexiui.toColor
|
||||
import com.highcapable.flexiui.toDp
|
||||
import com.highcapable.flexiui.toShape
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* Colors defines for dropdown list.
|
||||
* @param endIconInactiveTint the tint of the end icon when inactive.
|
||||
* @param endIconActiveTint the tint of the end icon when active.
|
||||
* @param borderInactiveColor the color of the border when inactive.
|
||||
* @param borderActiveColor the color of the border when active.
|
||||
* @param backgroundColor the background color.
|
||||
* @see DropdownListDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class DropdownListColors(
|
||||
val endIconInactiveTint: Color,
|
||||
val endIconActiveTint: Color,
|
||||
val backgroundColor: Color,
|
||||
val borderInactiveColor: Color,
|
||||
val borderActiveColor: Color,
|
||||
val backgroundColor: Color
|
||||
val borderActiveColor: Color
|
||||
)
|
||||
|
||||
/**
|
||||
* Colors defines for dropdown menu.
|
||||
* @param contentColor the color of the content.
|
||||
* @param activeColor the color of the active item.
|
||||
* @param borderColor the color of the border.
|
||||
* @see DropdownMenuDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class DropdownMenuColors(
|
||||
val contentColor: Color,
|
||||
val activeColor: Color,
|
||||
val backgroundColor: Color,
|
||||
val borderColor: Color
|
||||
)
|
||||
|
||||
/**
|
||||
* Style defines for dropdown list.
|
||||
* @param padding the padding of the content.
|
||||
* @param shape the shape.
|
||||
* @param endIconSize the size of the end icon.
|
||||
* @param borderInactive the border stroke when inactive.
|
||||
* @param borderActive the border stroke when active.
|
||||
* @see DropdownListDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class DropdownListStyle(
|
||||
val padding: ComponentPadding,
|
||||
val shape: Shape,
|
||||
val endIconSize: Dp,
|
||||
val borderInactive: BorderStroke,
|
||||
val borderActive: BorderStroke
|
||||
val borderInactiveWidth: Dp,
|
||||
val borderActiveWidth: Dp
|
||||
)
|
||||
|
||||
/**
|
||||
* Style defines for dropdown menu.
|
||||
* @param inTransitionDuration the duration of the in transition.
|
||||
* @param outTransitionDuration the duration of the out transition.
|
||||
* @param contentStyle the content style of area box.
|
||||
* @param borderStyle the brder style of area box.
|
||||
* @see DropdownMenuDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class DropdownMenuStyle(
|
||||
val padding: ComponentPadding,
|
||||
val shape: Shape,
|
||||
val borderWidth: Dp,
|
||||
val contentPadding: ComponentPadding,
|
||||
val contentShape: Shape,
|
||||
val shadowSize: Dp,
|
||||
val inTransitionDuration: Int,
|
||||
val outTransitionDuration: Int,
|
||||
val contentStyle: AreaBoxStyle,
|
||||
val borderStyle: AreaBoxStyle
|
||||
val outTransitionDuration: Int
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -202,10 +194,10 @@ fun DropdownList(
|
||||
expanded: Boolean,
|
||||
onExpandedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: DropdownListColors = DropdownListDefaults.colors,
|
||||
style: DropdownListStyle = DropdownListDefaults.style,
|
||||
menuColors: DropdownMenuColors = DropdownMenuDefaults.colors,
|
||||
menuStyle: DropdownMenuStyle = DropdownMenuDefaults.style,
|
||||
colors: DropdownListColors = DropdownListDefaults.colors(),
|
||||
style: DropdownListStyle = DropdownListDefaults.style(),
|
||||
menuColors: DropdownMenuColors = DropdownMenuDefaults.colors(),
|
||||
menuStyle: DropdownMenuStyle = DropdownMenuDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
scrollState: ScrollState = rememberScrollState(),
|
||||
properties: PopupProperties = PopupProperties(focusable = true),
|
||||
@@ -221,24 +213,21 @@ fun DropdownList(
|
||||
else -> colors.endIconInactiveTint
|
||||
})
|
||||
val animatedBorderColor by animateColorAsState(when {
|
||||
focused || hovered -> style.borderActive.solidColor
|
||||
else -> style.borderInactive.solidColor
|
||||
focused || hovered -> colors.borderActiveColor
|
||||
else -> colors.borderInactiveColor
|
||||
})
|
||||
val animatedDirection by animateFloatAsState(if (expanded) 180f else 0f)
|
||||
val animatedBorderWidth by animateDpAsState(when {
|
||||
focused -> style.borderActive.width
|
||||
else -> style.borderInactive.width
|
||||
focused -> style.borderActiveWidth
|
||||
else -> style.borderInactiveWidth
|
||||
})
|
||||
val border = when {
|
||||
focused || hovered -> style.borderInactive
|
||||
else -> style.borderInactive
|
||||
}.copy(animatedBorderWidth, SolidColor(animatedBorderColor))
|
||||
DropdownMenuBox(
|
||||
modifier = Modifier.dropdownList(
|
||||
enabled = enabled,
|
||||
colors = colors,
|
||||
style = style,
|
||||
border = border,
|
||||
borderColor = animatedBorderColor,
|
||||
borderWidth = animatedBorderWidth,
|
||||
focusRequester = focusRequester,
|
||||
interactionSource = interactionSource,
|
||||
then = modifier.rippleClickable(
|
||||
@@ -265,7 +254,7 @@ fun DropdownList(
|
||||
rotationZ = animatedDirection
|
||||
}.size(style.endIconSize),
|
||||
imageVector = FlexiIcons.Dropdown,
|
||||
style = IconDefaults.style.copy(tint = animatedEndIconTint)
|
||||
style = IconDefaults.style(tint = animatedEndIconTint)
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
@@ -301,8 +290,8 @@ fun DropdownMenu(
|
||||
expanded: Boolean,
|
||||
onDismissRequest: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: DropdownMenuColors = DropdownMenuDefaults.colors,
|
||||
style: DropdownMenuStyle = DropdownMenuDefaults.style,
|
||||
colors: DropdownMenuColors = DropdownMenuDefaults.colors(),
|
||||
style: DropdownMenuStyle = DropdownMenuDefaults.style(),
|
||||
offset: DpOffset = DpOffset(0.dp, 0.dp),
|
||||
scrollState: ScrollState = rememberScrollState(),
|
||||
properties: PopupProperties = PopupProperties(focusable = true),
|
||||
@@ -383,7 +372,8 @@ fun DropdownMenuBox(
|
||||
* @param modifier the [Modifier] to be applied to this dropdown menu item.
|
||||
* @param contentColor the color of the content.
|
||||
* @param activeColor the color of the active item.
|
||||
* @param contentStyle the style of the content.
|
||||
* @param contentPadding the padding of the content.
|
||||
* @param contentShape the shape of the content.
|
||||
* @param enabled whether the dropdown menu item is enabled, default is true.
|
||||
* @param actived whether the dropdown menu item is actived, default is false.
|
||||
* @param interactionSource the interaction source of the dropdown menu item.
|
||||
@@ -395,7 +385,8 @@ fun DropdownMenuItem(
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = Color.Unspecified,
|
||||
activeColor: Color = Color.Unspecified,
|
||||
contentStyle: AreaBoxStyle? = null,
|
||||
contentPadding: ComponentPadding? = null,
|
||||
contentShape: Shape? = null,
|
||||
enabled: Boolean = true,
|
||||
actived: Boolean = false,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
@@ -403,13 +394,16 @@ fun DropdownMenuItem(
|
||||
) {
|
||||
val currentColor = contentColor.orNull()
|
||||
?: LocalDropdownMenuContentColor.current.orNull()
|
||||
?: DropdownMenuDefaults.colors.contentColor
|
||||
?: DropdownMenuDefaults.colors().contentColor
|
||||
val currentActiveColor = activeColor.orNull()
|
||||
?: LocalDropdownMenuActiveColor.current.orNull()
|
||||
?: DropdownMenuDefaults.colors.activeColor
|
||||
val currentStyle = contentStyle
|
||||
?: LocalDropdownMenuContentStyle.current
|
||||
?: DropdownMenuDefaults.style.contentStyle
|
||||
?: DropdownMenuDefaults.colors().activeColor
|
||||
val currentPadding = contentPadding
|
||||
?: LocalDropdownMenuContentPadding.current
|
||||
?: DropdownMenuDefaults.style().contentPadding
|
||||
val currentShape = contentShape
|
||||
?: LocalDropdownMenuContentShape.current
|
||||
?: DropdownMenuDefaults.style().contentShape
|
||||
AreaRow(
|
||||
modifier = Modifier.componentState(enabled)
|
||||
.then(modifier)
|
||||
@@ -425,8 +419,8 @@ fun DropdownMenuItem(
|
||||
interactionSource = interactionSource,
|
||||
onClick = onClick
|
||||
),
|
||||
color = if (actived) currentActiveColor else Color.Transparent,
|
||||
style = currentStyle,
|
||||
colors = AreaBoxDefaults.colors(backgroundColor = if (actived) currentActiveColor else Color.Transparent),
|
||||
style = AreaBoxDefaults.style(padding = currentPadding, shape = currentShape),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
@@ -489,13 +483,22 @@ private fun DropdownMenuContent(
|
||||
transformOrigin = transformOriginState.value
|
||||
}
|
||||
},
|
||||
color = colors.borderColor,
|
||||
style = style.borderStyle
|
||||
colors = AreaBoxDefaults.colors(
|
||||
backgroundColor = colors.backgroundColor,
|
||||
borderColor = colors.borderColor
|
||||
),
|
||||
style = AreaBoxDefaults.style(
|
||||
padding = style.padding,
|
||||
shape = style.shape,
|
||||
borderWidth = style.borderWidth,
|
||||
shadowSize = style.shadowSize
|
||||
)
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalDropdownMenuContentColor provides colors.contentColor,
|
||||
LocalDropdownMenuActiveColor provides colors.activeColor,
|
||||
LocalDropdownMenuContentStyle provides style.contentStyle
|
||||
LocalDropdownMenuContentPadding provides style.contentPadding,
|
||||
LocalDropdownMenuContentShape provides style.contentShape
|
||||
) { content() }
|
||||
}
|
||||
}
|
||||
@@ -504,7 +507,8 @@ private fun Modifier.dropdownList(
|
||||
enabled: Boolean,
|
||||
colors: DropdownListColors,
|
||||
style: DropdownListStyle,
|
||||
border: BorderStroke,
|
||||
borderColor: Color,
|
||||
borderWidth: Dp,
|
||||
focusRequester: FocusRequester,
|
||||
interactionSource: MutableInteractionSource,
|
||||
then: Modifier
|
||||
@@ -514,7 +518,8 @@ private fun Modifier.dropdownList(
|
||||
properties["enabled"] = enabled
|
||||
properties["colors"] = colors
|
||||
properties["style"] = style
|
||||
properties["border"] = border
|
||||
properties["borderColor"] = borderColor
|
||||
properties["borderWidth"] = borderWidth
|
||||
}
|
||||
) {
|
||||
componentState(enabled)
|
||||
@@ -523,7 +528,7 @@ private fun Modifier.dropdownList(
|
||||
.hoverable(interactionSource, enabled)
|
||||
.clip(style.shape)
|
||||
.background(colors.backgroundColor, style.shape)
|
||||
.borderOrElse(border, style.shape)
|
||||
.borderOrElse(borderWidth, borderColor, style.shape)
|
||||
.then(then)
|
||||
.padding(style.padding)
|
||||
}
|
||||
@@ -611,92 +616,150 @@ private data class DropdownMenuPositionProvider(
|
||||
* Defaults of dropdown list.
|
||||
*/
|
||||
object DropdownListDefaults {
|
||||
val colors: DropdownListColors
|
||||
|
||||
/**
|
||||
* Creates a [DropdownListColors] with the default values.
|
||||
* @param endIconInactiveTint the tint of the end icon when inactive.
|
||||
* @param endIconActiveTint the tint of the end icon when active.
|
||||
* @param backgroundColor the background color.
|
||||
* @param borderInactiveColor the color of the border when inactive.
|
||||
* @param borderActiveColor the color of the border when active.
|
||||
* @return [DropdownListColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultDropdownListColors()
|
||||
val style: DropdownListStyle
|
||||
fun colors(
|
||||
endIconInactiveTint: Color = DropdownListProperties.EndIconInactiveTint.toColor(),
|
||||
endIconActiveTint: Color = DropdownListProperties.EndIconActiveTint.toColor(),
|
||||
backgroundColor: Color = DropdownListProperties.BackgroundColor,
|
||||
borderInactiveColor: Color = DropdownListProperties.BorderInactiveColor.toColor(),
|
||||
borderActiveColor: Color = DropdownListProperties.BorderActiveColor.toColor()
|
||||
) = DropdownListColors(
|
||||
endIconInactiveTint = endIconInactiveTint,
|
||||
endIconActiveTint = endIconActiveTint,
|
||||
backgroundColor = backgroundColor,
|
||||
borderInactiveColor = borderInactiveColor,
|
||||
borderActiveColor = borderActiveColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [DropdownListStyle] with the default values.
|
||||
* @param padding the padding of the content.
|
||||
* @param shape the shape.
|
||||
* @param endIconSize the size of the end icon.
|
||||
* @param borderInactiveWidth the width of the border when inactive.
|
||||
* @param borderActiveWidth the width of the border when active.
|
||||
* @return [DropdownListStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultDropdownListStyle()
|
||||
fun style(
|
||||
padding: ComponentPadding = DropdownListProperties.Padding.toPadding(),
|
||||
shape: Shape = AreaBoxDefaults.childShape(),
|
||||
endIconSize: Dp = DropdownListProperties.EndIconSize.toDp(),
|
||||
borderInactiveWidth: Dp = DropdownListProperties.BorderInactiveWidth.toDp(),
|
||||
borderActiveWidth: Dp = DropdownListProperties.BorderActiveWidth.toDp()
|
||||
) = DropdownListStyle(
|
||||
padding = padding,
|
||||
shape = shape,
|
||||
endIconSize = endIconSize,
|
||||
borderInactiveWidth = borderInactiveWidth,
|
||||
borderActiveWidth = borderActiveWidth
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Defaults of dropdown menu.
|
||||
*/
|
||||
object DropdownMenuDefaults {
|
||||
val colors: DropdownMenuColors
|
||||
|
||||
/**
|
||||
* Creates a [DropdownMenuColors] with the default values.
|
||||
* @param contentColor the color of the content.
|
||||
* @param activeColor the color of the active item.
|
||||
* @param backgroundColor the background color.
|
||||
* @param borderColor the color of the border.
|
||||
* @return [DropdownMenuColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultDropdownMenuColors()
|
||||
val style: DropdownMenuStyle
|
||||
fun colors(
|
||||
contentColor: Color = DropdownMenuProperties.ContentColor.toColor(),
|
||||
activeColor: Color = DropdownMenuProperties.ActiveColor.toColor().copy(alpha = 0.3f),
|
||||
backgroundColor: Color = DropdownMenuProperties.BackgroundColor.toColor(),
|
||||
borderColor: Color = DropdownMenuProperties.BorderColor.toColor()
|
||||
) = DropdownMenuColors(
|
||||
contentColor = contentColor,
|
||||
activeColor = activeColor,
|
||||
backgroundColor = backgroundColor,
|
||||
borderColor = borderColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [DropdownMenuStyle] with the default values.
|
||||
* @param padding the menu padding.
|
||||
* @param shape the menu shape.
|
||||
* @param borderWidth the menu border width.
|
||||
* @param contentPadding the content padding.
|
||||
* @param contentShape the content shape.
|
||||
* @param shadowSize the shadow size.
|
||||
* @param inTransitionDuration the duration of the in transition.
|
||||
* @param outTransitionDuration the duration of the out transition.
|
||||
* @return [DropdownMenuStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultDropdownMenuStyle()
|
||||
fun style(
|
||||
padding: ComponentPadding = DropdownMenuProperties.Padding.toPadding(),
|
||||
shape: Shape = DropdownMenuProperties.Shape.toShape(),
|
||||
borderWidth: Dp = DropdownMenuProperties.BorderWidth.toDp(),
|
||||
contentPadding: ComponentPadding = DropdownMenuProperties.ContentPadding,
|
||||
contentShape: Shape = DropdownMenuProperties.ContentShape.toShape(),
|
||||
shadowSize: Dp = DropdownMenuProperties.ShadowSize.toDp(),
|
||||
inTransitionDuration: Int = DropdownMenuProperties.InTransitionDuration,
|
||||
outTransitionDuration: Int = DropdownMenuProperties.OutTransitionDuration
|
||||
) = DropdownMenuStyle(
|
||||
padding = padding,
|
||||
shape = shape,
|
||||
borderWidth = borderWidth,
|
||||
contentPadding = contentPadding,
|
||||
contentShape = contentShape,
|
||||
shadowSize = shadowSize,
|
||||
inTransitionDuration = inTransitionDuration,
|
||||
outTransitionDuration = outTransitionDuration
|
||||
)
|
||||
}
|
||||
|
||||
private val LocalDropdownMenuContentColor = compositionLocalOf { Color.Unspecified }
|
||||
@Stable
|
||||
internal object DropdownListProperties {
|
||||
val EndIconInactiveTint = ColorsDescriptor.ThemeSecondary
|
||||
val EndIconActiveTint = ColorsDescriptor.ThemePrimary
|
||||
val BackgroundColor = Color.Transparent
|
||||
val BorderInactiveColor = ColorsDescriptor.ThemeSecondary
|
||||
val BorderActiveColor = ColorsDescriptor.ThemePrimary
|
||||
val Padding = PaddingDescriptor(SizesDescriptor.SpacingSecondary)
|
||||
val EndIconSize = SizesDescriptor.IconSizeTertiary
|
||||
val BorderInactiveWidth = SizesDescriptor.BorderSizeSecondary
|
||||
val BorderActiveWidth = SizesDescriptor.BorderSizePrimary
|
||||
}
|
||||
|
||||
@Stable
|
||||
internal object DropdownMenuProperties {
|
||||
val ContentColor = ColorsDescriptor.TextPrimary
|
||||
val ActiveColor = ColorsDescriptor.ThemePrimary
|
||||
val BackgroundColor = AreaBoxProperties.BackgroundColor
|
||||
val BorderColor = AreaBoxProperties.BorderColor
|
||||
val Padding = PaddingDescriptor(SizesDescriptor.SpacingTertiary)
|
||||
val Shape = ShapesDescriptor.Primary
|
||||
val BorderWidth = AreaBoxProperties.BorderWidth
|
||||
val ContentPadding = ComponentPadding(horizontal = 16.dp)
|
||||
val ContentShape = ShapesDescriptor.Secondary
|
||||
val ShadowSize = SizesDescriptor.ZoomSizeTertiary
|
||||
const val InTransitionDuration = 120
|
||||
const val OutTransitionDuration = 90
|
||||
}
|
||||
|
||||
private val LocalDropdownMenuActiveColor = compositionLocalOf { Color.Unspecified }
|
||||
|
||||
private val LocalDropdownMenuContentStyle = compositionLocalOf<AreaBoxStyle?> { null }
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultDropdownListColors() = DropdownListColors(
|
||||
endIconInactiveTint = LocalColors.current.themeSecondary,
|
||||
endIconActiveTint = LocalColors.current.themePrimary,
|
||||
borderInactiveColor = LocalColors.current.themeSecondary,
|
||||
borderActiveColor = LocalColors.current.themePrimary,
|
||||
backgroundColor = Color.Transparent
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultDropdownMenuColors() = DropdownMenuColors(
|
||||
contentColor = LocalColors.current.textPrimary,
|
||||
activeColor = LocalColors.current.themePrimary.copy(alpha = 0.3f),
|
||||
borderColor = LocalColors.current.backgroundSecondary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultDropdownListStyle() = DropdownListStyle(
|
||||
padding = ComponentPadding(LocalSizes.current.spacingSecondary),
|
||||
shape = withAreaBoxShape(),
|
||||
endIconSize = LocalSizes.current.iconSizeTertiary,
|
||||
borderInactive = defaultDropdownListInactiveBorder(),
|
||||
borderActive = defaultDropdownListActiveBorder()
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultDropdownMenuStyle() = DropdownMenuStyle(
|
||||
inTransitionDuration = DefaultInTransitionDuration,
|
||||
outTransitionDuration = DefaultOutTransitionDuration,
|
||||
contentStyle = AreaBoxDefaults.style.copy(
|
||||
padding = ComponentPadding(horizontal = DefaultMenuContentPadding),
|
||||
shape = LocalShapes.current.secondary
|
||||
),
|
||||
borderStyle = AreaBoxDefaults.style.copy(
|
||||
padding = ComponentPadding(LocalSizes.current.spacingTertiary),
|
||||
shadowSize = LocalSizes.current.zoomSizeTertiary,
|
||||
shape = LocalShapes.current.primary
|
||||
)
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultDropdownListInactiveBorder() = BorderStroke(LocalSizes.current.borderSizeSecondary, LocalColors.current.themeSecondary)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultDropdownListActiveBorder() = BorderStroke(LocalSizes.current.borderSizePrimary, LocalColors.current.themePrimary)
|
||||
|
||||
private val DefaultMenuContentPadding = 16.dp
|
||||
|
||||
private const val DefaultInTransitionDuration = 120
|
||||
private const val DefaultOutTransitionDuration = 90
|
||||
private val LocalDropdownMenuContentColor = compositionLocalOf { Color.Unspecified }
|
||||
private val LocalDropdownMenuContentPadding = compositionLocalOf<ComponentPadding?> { null }
|
||||
private val LocalDropdownMenuContentShape = compositionLocalOf<Shape?> { null }
|
||||
|
||||
private val MenuItemMinWidth = 112.dp
|
||||
private val MenuItemMaxWidth = 280.dp
|
||||
|
@@ -27,7 +27,7 @@ import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
@@ -48,13 +48,14 @@ import androidx.compose.ui.semantics.role
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.isSpecified
|
||||
import androidx.compose.ui.unit.isUnspecified
|
||||
import com.highcapable.betterandroid.compose.extension.ui.orNull
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.toDp
|
||||
|
||||
/**
|
||||
* Style defines for icon.
|
||||
* @param size the size.
|
||||
* @param tint the tint.
|
||||
* Style defines for basic icon.
|
||||
* @see IconDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class IconStyle(
|
||||
@@ -67,14 +68,14 @@ data class IconStyle(
|
||||
* @param imageVector the vector image to be drawn.
|
||||
* @param contentDescription the content description for this icon.
|
||||
* @param modifier the [Modifier] to be applied to this icon.
|
||||
* @param style the style of this icon, default is [IconDefaults.style].
|
||||
* @param style the style of this icon.
|
||||
*/
|
||||
@Composable
|
||||
fun Icon(
|
||||
imageVector: ImageVector,
|
||||
contentDescription: String? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
style: IconStyle = IconDefaults.style
|
||||
style: IconStyle? = null
|
||||
) {
|
||||
val painter = rememberVectorPainter(imageVector)
|
||||
Icon(painter, contentDescription, modifier, style)
|
||||
@@ -85,16 +86,23 @@ fun Icon(
|
||||
* @param painter the painter to be drawn.
|
||||
* @param contentDescription the content description for this icon.
|
||||
* @param modifier the [Modifier] to be applied to this icon.
|
||||
* @param style the style of this icon, default is [IconDefaults.style].
|
||||
* @param style the style of this icon.
|
||||
*/
|
||||
@Composable
|
||||
fun Icon(
|
||||
painter: Painter,
|
||||
contentDescription: String? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
style: IconStyle = IconDefaults.style
|
||||
style: IconStyle? = null
|
||||
) {
|
||||
val colorFilter = if (style.tint.isUnspecified) null else ColorFilter.tint(style.tint)
|
||||
val defaultStyle = LocalIconStyle.current.orNull() ?: IconDefaults.style()
|
||||
val currentStyle = style?.let {
|
||||
it.copy(
|
||||
size = it.size.orNull() ?: defaultStyle.size,
|
||||
tint = it.tint.orNull() ?: defaultStyle.tint
|
||||
)
|
||||
} ?: defaultStyle
|
||||
val colorFilter = if (currentStyle.tint.isUnspecified) null else ColorFilter.tint(currentStyle.tint)
|
||||
val semantics = if (contentDescription != null)
|
||||
Modifier.semantics {
|
||||
this.contentDescription = contentDescription
|
||||
@@ -103,7 +111,7 @@ fun Icon(
|
||||
else Modifier
|
||||
Box(
|
||||
modifier = modifier.toolingGraphicsLayer()
|
||||
.defaultSizeFor(style, painter)
|
||||
.defaultSizeFor(currentStyle, painter)
|
||||
.paint(
|
||||
painter,
|
||||
colorFilter = colorFilter,
|
||||
@@ -127,30 +135,49 @@ private fun Modifier.defaultSizeFor(
|
||||
style.size.isSpecified ||
|
||||
painter.intrinsicSize == Size.Unspecified ||
|
||||
painter.intrinsicSize.isInfinite() ->
|
||||
Modifier.size(style.size.orNull() ?: defaultIconSize())
|
||||
Modifier.size(style.size.orNull() ?: IconDefaults.style().size)
|
||||
else -> Modifier
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Defaults of icon.
|
||||
* Defaults of basic icon.
|
||||
*/
|
||||
object IconDefaults {
|
||||
val style: IconStyle
|
||||
|
||||
/**
|
||||
* Creates a [IconStyle] with the default values.
|
||||
* @param size the size.
|
||||
* @param tint the tint.
|
||||
* @return [IconStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalIconStyle.current
|
||||
fun style(
|
||||
size: Dp = IconProperties.Size.toDp(),
|
||||
tint: Color = IconProperties.Tint
|
||||
) = IconStyle(
|
||||
size = size,
|
||||
tint = tint
|
||||
)
|
||||
}
|
||||
|
||||
internal val LocalIconStyle = compositionLocalOf { DefaultIconStyle }
|
||||
@Stable
|
||||
internal object IconProperties {
|
||||
val Size = SizesDescriptor.IconSizePrimary
|
||||
val Tint = Color.Unspecified
|
||||
}
|
||||
|
||||
/**
|
||||
* Composition local containing the preferred [IconStyle]
|
||||
* that will be used by [Icon] by default.
|
||||
*/
|
||||
val LocalIconStyle = compositionLocalOf { DefaultIconStyle }
|
||||
|
||||
private val DefaultIconStyle = IconStyle(
|
||||
size = Dp.Unspecified,
|
||||
tint = Color.Unspecified
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultIconSize() = LocalSizes.current.iconSizePrimary
|
||||
private fun IconStyle.orNull() = if (size.isUnspecified && tint.isUnspecified) null else this
|
||||
|
||||
private fun Size.isInfinite() = width.isInfinite() && height.isInfinite()
|
@@ -32,50 +32,52 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.highcapable.betterandroid.compose.extension.ui.componentState
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.LocalTypography
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.TypographyDescriptor
|
||||
import com.highcapable.flexiui.component.interaction.rippleClickable
|
||||
import com.highcapable.flexiui.resources.FlexiIcons
|
||||
import com.highcapable.flexiui.resources.icon.ArrowForward
|
||||
import com.highcapable.flexiui.toColor
|
||||
import com.highcapable.flexiui.toDp
|
||||
import com.highcapable.flexiui.toShape
|
||||
import com.highcapable.flexiui.toTextStyle
|
||||
|
||||
/**
|
||||
* Colors defines for item box.
|
||||
* @param backgroundColor the background color.
|
||||
* @param titleTextColor the title text color.
|
||||
* @param subTextColor the sub text color.
|
||||
* @param arrowIconTint the arrow icon tint.
|
||||
* @see ItemBoxDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class ItemBoxColors(
|
||||
val backgroundColor: Color,
|
||||
val titleTextColor: Color,
|
||||
val subTextColor: Color,
|
||||
val arrowIconTint: Color
|
||||
val arrowIconTint: Color,
|
||||
val borderColor: Color
|
||||
)
|
||||
|
||||
/**
|
||||
* Style defines for item box.
|
||||
* @param boxStyle the style of area box.
|
||||
* @param contentSpacing the spacing between the components of content.
|
||||
* @param titleTextStyle the title text style.
|
||||
* @param subTextStyle the sub text style.
|
||||
* @see ItemBoxDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class ItemBoxStyle(
|
||||
val boxStyle: AreaBoxStyle,
|
||||
val contentSpacing: Dp,
|
||||
val titleTextStyle: TextStyle,
|
||||
val subTextStyle: TextStyle
|
||||
val subTextStyle: TextStyle,
|
||||
val shape: Shape,
|
||||
val borderWidth: Dp,
|
||||
val shadowSize: Dp
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -96,8 +98,8 @@ data class ItemBoxStyle(
|
||||
fun HorizontalItemBox(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: ItemBoxColors = ItemBoxDefaults.colors,
|
||||
style: ItemBoxStyle = ItemBoxDefaults.style,
|
||||
colors: ItemBoxColors = ItemBoxDefaults.colors(),
|
||||
style: ItemBoxStyle = ItemBoxDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
showArrowIcon: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
@@ -112,8 +114,15 @@ fun HorizontalItemBox(
|
||||
onClick = onClick
|
||||
),
|
||||
initializer = { componentState(enabled) },
|
||||
color = colors.backgroundColor,
|
||||
style = style.boxStyle
|
||||
colors = AreaBoxDefaults.colors(
|
||||
backgroundColor = colors.backgroundColor,
|
||||
borderColor = colors.borderColor
|
||||
),
|
||||
style = AreaBoxDefaults.style(
|
||||
shape = style.shape,
|
||||
borderWidth = style.borderWidth,
|
||||
shadowSize = style.shadowSize
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -134,7 +143,7 @@ fun HorizontalItemBox(
|
||||
}
|
||||
if (showArrowIcon) Icon(
|
||||
imageVector = FlexiIcons.ArrowForward,
|
||||
style = IconDefaults.style.copy(
|
||||
style = IconDefaults.style(
|
||||
size = DefaultArrowIconSize,
|
||||
tint = colors.arrowIconTint
|
||||
)
|
||||
@@ -160,8 +169,8 @@ fun HorizontalItemBox(
|
||||
fun VerticalItemBox(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: ItemBoxColors = ItemBoxDefaults.colors,
|
||||
style: ItemBoxStyle = ItemBoxDefaults.style,
|
||||
colors: ItemBoxColors = ItemBoxDefaults.colors(),
|
||||
style: ItemBoxStyle = ItemBoxDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
logoImage: @Composable (() -> Unit)? = null,
|
||||
@@ -175,8 +184,15 @@ fun VerticalItemBox(
|
||||
onClick = onClick
|
||||
),
|
||||
initializer = { componentState(enabled) },
|
||||
color = colors.backgroundColor,
|
||||
style = style.boxStyle,
|
||||
colors = AreaBoxDefaults.colors(
|
||||
backgroundColor = colors.backgroundColor,
|
||||
borderColor = colors.borderColor
|
||||
),
|
||||
style = AreaBoxDefaults.style(
|
||||
shape = style.shape,
|
||||
borderWidth = style.borderWidth,
|
||||
shadowSize = style.shadowSize
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(style.contentSpacing / VerticalContentSpacingRatio)
|
||||
) {
|
||||
@@ -213,31 +229,73 @@ private fun ItemBoxContent(
|
||||
* Defaults of item box.
|
||||
*/
|
||||
object ItemBoxDefaults {
|
||||
val colors: ItemBoxColors
|
||||
|
||||
/**
|
||||
* Creates a [ItemBoxColors] with the default values.
|
||||
* @param backgroundColor the background color.
|
||||
* @param titleTextColor the title text color.
|
||||
* @param subTextColor the sub text color.
|
||||
* @param arrowIconTint the arrow icon tint.
|
||||
* @param borderColor the border color.
|
||||
* @return [ItemBoxColors]
|
||||
*/
|
||||
@Composable
|
||||
get() = defaultItemBoxColors()
|
||||
val style: ItemBoxStyle
|
||||
fun colors(
|
||||
backgroundColor: Color = ItemBoxProperties.BackgroundColor.toColor(),
|
||||
titleTextColor: Color = ItemBoxProperties.TitleTextColor.toColor(),
|
||||
subTextColor: Color = ItemBoxProperties.SubTextColor.toColor(),
|
||||
arrowIconTint: Color = ItemBoxProperties.ArrowIconTint.toColor(),
|
||||
borderColor: Color = ItemBoxProperties.BorderColor.toColor()
|
||||
) = ItemBoxColors(
|
||||
backgroundColor = backgroundColor,
|
||||
titleTextColor = titleTextColor,
|
||||
subTextColor = subTextColor,
|
||||
arrowIconTint = arrowIconTint,
|
||||
borderColor = borderColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [ItemBoxStyle] with the default values.
|
||||
* @param contentSpacing the spacing between the components of content.
|
||||
* @param titleTextStyle the title text style.
|
||||
* @param subTextStyle the sub text style.
|
||||
* @param shape the shape.
|
||||
* @param borderWidth the border width.
|
||||
* @param shadowSize the shadow size.
|
||||
* @return [ItemBoxStyle]
|
||||
*/
|
||||
@Composable
|
||||
get() = defaultItemBoxStyle()
|
||||
fun style(
|
||||
contentSpacing: Dp = ItemBoxProperties.ContentSpacing.toDp(),
|
||||
titleTextStyle: TextStyle = ItemBoxProperties.TitleTextStyle.toTextStyle(),
|
||||
subTextStyle: TextStyle = ItemBoxProperties.SubTextStyle.toTextStyle(),
|
||||
shape: Shape = ItemBoxProperties.Shape.toShape(),
|
||||
borderWidth: Dp = ItemBoxProperties.BorderWidth.toDp(),
|
||||
shadowSize: Dp = ItemBoxProperties.ShadowSize
|
||||
) = ItemBoxStyle(
|
||||
contentSpacing = contentSpacing,
|
||||
titleTextStyle = titleTextStyle,
|
||||
subTextStyle = subTextStyle,
|
||||
shape = shape,
|
||||
borderWidth = borderWidth,
|
||||
shadowSize = shadowSize
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultItemBoxColors() = ItemBoxColors(
|
||||
backgroundColor = AreaBoxDefaults.color,
|
||||
titleTextColor = LocalColors.current.textPrimary,
|
||||
subTextColor = LocalColors.current.textSecondary,
|
||||
arrowIconTint = LocalColors.current.textSecondary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultItemBoxStyle() = ItemBoxStyle(
|
||||
boxStyle = AreaBoxDefaults.style,
|
||||
contentSpacing = LocalSizes.current.spacingSecondary,
|
||||
titleTextStyle = LocalTypography.current.primary,
|
||||
subTextStyle = LocalTypography.current.secondary
|
||||
)
|
||||
@Stable
|
||||
internal object ItemBoxProperties {
|
||||
val BackgroundColor = AreaBoxProperties.BackgroundColor
|
||||
val TitleTextColor = ColorsDescriptor.TextPrimary
|
||||
val SubTextColor = ColorsDescriptor.TextSecondary
|
||||
val ArrowIconTint = ColorsDescriptor.TextSecondary
|
||||
val BorderColor = AreaBoxProperties.BorderColor
|
||||
val ContentSpacing = SizesDescriptor.SpacingSecondary
|
||||
val TitleTextStyle = TypographyDescriptor.Primary
|
||||
val SubTextStyle = TypographyDescriptor.Secondary
|
||||
val Shape = AreaBoxProperties.Shape
|
||||
val BorderWidth = AreaBoxProperties.BorderWidth
|
||||
val ShadowSize = AreaBoxProperties.ShadowSize
|
||||
}
|
||||
|
||||
private val DefaultArrowIconSize = 15.dp
|
||||
|
||||
|
@@ -44,7 +44,7 @@ import androidx.compose.foundation.selection.selectableGroup
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -58,21 +58,23 @@ import androidx.compose.ui.unit.Dp
|
||||
import com.highcapable.betterandroid.compose.extension.ui.ComponentPadding
|
||||
import com.highcapable.betterandroid.compose.extension.ui.componentState
|
||||
import com.highcapable.betterandroid.compose.extension.ui.orNull
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.PaddingDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.component.interaction.InteractionDefaults
|
||||
import com.highcapable.flexiui.component.interaction.rippleClickable
|
||||
import com.highcapable.flexiui.toColor
|
||||
import com.highcapable.flexiui.toDp
|
||||
import com.highcapable.flexiui.toShape
|
||||
|
||||
/**
|
||||
* Colors defines for navigation bar.
|
||||
* @param backgroundColor the background color.
|
||||
* @param indicatorColor the indicator color.
|
||||
* @param selectedContentColor the selected content color.
|
||||
* @param unselectedContentColor the unselected content color.
|
||||
* @see NavigationBarDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class NavigationBarColors(
|
||||
val backgroundColor: Color,
|
||||
val borderColor: Color,
|
||||
val indicatorColor: Color,
|
||||
val selectedContentColor: Color,
|
||||
val unselectedContentColor: Color
|
||||
@@ -80,14 +82,14 @@ data class NavigationBarColors(
|
||||
|
||||
/**
|
||||
* Style defines for navigation bar.
|
||||
* @param boxStyle the style of area box.
|
||||
* @param contentSpacing the spacing between the components of content.
|
||||
* @param contentPadding the padding of content.
|
||||
* @param contentShape the content shape.
|
||||
* @see NavigationBarDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class NavigationBarStyle(
|
||||
val boxStyle: AreaBoxStyle,
|
||||
val padding: ComponentPadding,
|
||||
val shape: Shape,
|
||||
val borderWidth: Dp,
|
||||
val shadowSize: Dp,
|
||||
val contentSpacing: Dp,
|
||||
val contentPadding: ComponentPadding,
|
||||
val contentShape: Shape
|
||||
@@ -106,16 +108,24 @@ data class NavigationBarStyle(
|
||||
@Composable
|
||||
fun NavigationBarRow(
|
||||
modifier: Modifier = Modifier,
|
||||
colors: NavigationBarColors = NavigationBarDefaults.colors,
|
||||
style: NavigationBarStyle = NavigationBarDefaults.style,
|
||||
colors: NavigationBarColors = NavigationBarDefaults.colors(),
|
||||
style: NavigationBarStyle = NavigationBarDefaults.style(),
|
||||
arrangement: Arrangement.Horizontal = Arrangement.SpaceBetween,
|
||||
content: @Composable RowScope.() -> Unit
|
||||
) {
|
||||
NavigationBarStyleBox(modifier, horizontal = true, colors, style) {
|
||||
AreaRow(
|
||||
modifier = Modifier.fillMaxWidth().selectableGroup(),
|
||||
color = colors.backgroundColor,
|
||||
style = style.boxStyle,
|
||||
colors = AreaBoxDefaults.colors(
|
||||
backgroundColor = colors.backgroundColor,
|
||||
borderColor = colors.borderColor
|
||||
),
|
||||
style = AreaBoxDefaults.style(
|
||||
padding = style.padding,
|
||||
shape = style.shape,
|
||||
borderWidth = style.borderWidth,
|
||||
shadowSize = style.shadowSize
|
||||
),
|
||||
horizontalArrangement = arrangement,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
content = content
|
||||
@@ -136,16 +146,24 @@ fun NavigationBarRow(
|
||||
@Composable
|
||||
fun NavigationBarColumn(
|
||||
modifier: Modifier = Modifier,
|
||||
colors: NavigationBarColors = NavigationBarDefaults.colors,
|
||||
style: NavigationBarStyle = NavigationBarDefaults.style,
|
||||
colors: NavigationBarColors = NavigationBarDefaults.colors(),
|
||||
style: NavigationBarStyle = NavigationBarDefaults.style(),
|
||||
arrangement: Arrangement.Vertical = Arrangement.SpaceBetween,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
NavigationBarStyleBox(modifier, horizontal = false, colors, style) {
|
||||
AreaColumn(
|
||||
modifier = Modifier.fillMaxWidth().selectableGroup(),
|
||||
color = colors.backgroundColor,
|
||||
style = style.boxStyle,
|
||||
colors = AreaBoxDefaults.colors(
|
||||
backgroundColor = colors.backgroundColor,
|
||||
borderColor = colors.borderColor
|
||||
),
|
||||
style = AreaBoxDefaults.style(
|
||||
padding = style.padding,
|
||||
shape = style.shape,
|
||||
borderWidth = style.borderWidth,
|
||||
shadowSize = style.shadowSize
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = arrangement,
|
||||
content = content
|
||||
@@ -186,16 +204,16 @@ fun NavigationBarItem(
|
||||
text: @Composable (() -> Unit)? = null
|
||||
) {
|
||||
val currentHorizontal = horizontal ?: LocalHorizontalNavigationBar.current
|
||||
val currentColors = colors ?: LocalNavigationBarColors.current ?: NavigationBarDefaults.colors
|
||||
val currentColors = colors ?: LocalNavigationBarColors.current ?: NavigationBarDefaults.colors()
|
||||
val currentContentSpacing = contentSpacing.orNull()
|
||||
?: LocalNavigationBarContentSpacing.current.orNull()
|
||||
?: NavigationBarDefaults.style.contentSpacing
|
||||
?: NavigationBarDefaults.style().contentSpacing
|
||||
val currentContentPadding = contentPadding
|
||||
?: LocalNavigationBarContentPadding.current
|
||||
?: NavigationBarDefaults.style.contentPadding
|
||||
?: NavigationBarDefaults.style().contentPadding
|
||||
val currentContentShape = contentShape
|
||||
?: LocalNavigationBarContentShape.current
|
||||
?: NavigationBarDefaults.style.contentShape
|
||||
?: NavigationBarDefaults.style().contentShape
|
||||
val animatedIndicatorColor by animateColorAsState(if (selected) currentColors.indicatorColor else Color.Transparent)
|
||||
val animatedContentColor by animateColorAsState(if (selected) currentColors.selectedContentColor else currentColors.unselectedContentColor)
|
||||
val currentIconStyle = LocalIconStyle.current.copy(tint = animatedContentColor)
|
||||
@@ -206,7 +224,7 @@ fun NavigationBarItem(
|
||||
.then(modifier)
|
||||
.background(animatedIndicatorColor)
|
||||
.rippleClickable(
|
||||
rippleStyle = InteractionDefaults.rippleStyle.copy(color = currentColors.indicatorColor),
|
||||
rippleStyle = InteractionDefaults.rippleStyle(color = currentColors.indicatorColor),
|
||||
enabled = enabled,
|
||||
role = Role.Tab,
|
||||
interactionSource = interactionSource,
|
||||
@@ -275,45 +293,85 @@ private fun NavigationBarStyleBox(
|
||||
* Defaults of navigation bar.
|
||||
*/
|
||||
object NavigationBarDefaults {
|
||||
val colors: NavigationBarColors
|
||||
|
||||
/**
|
||||
* Creates a [NavigationBarColors] with the default values.
|
||||
* @param backgroundColor the background color.
|
||||
* @param borderColor the border color.
|
||||
* @param indicatorColor the indicator color.
|
||||
* @param selectedContentColor the selected content color.
|
||||
* @param unselectedContentColor the unselected content color.
|
||||
* @return [NavigationBarColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultNavigationBarColors()
|
||||
val style: NavigationBarStyle
|
||||
fun colors(
|
||||
backgroundColor: Color = NavigationBarProperties.BackgroundColor.toColor(),
|
||||
borderColor: Color = NavigationBarProperties.BorderColor.toColor(),
|
||||
indicatorColor: Color = NavigationBarProperties.IndicatorColor.toColor(),
|
||||
selectedContentColor: Color = NavigationBarProperties.SelectedContentColor.toColor(),
|
||||
unselectedContentColor: Color = NavigationBarProperties.UnselectedContentColor.toColor()
|
||||
) = NavigationBarColors(
|
||||
backgroundColor = backgroundColor,
|
||||
borderColor = borderColor,
|
||||
indicatorColor = indicatorColor,
|
||||
selectedContentColor = selectedContentColor,
|
||||
unselectedContentColor = unselectedContentColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [NavigationBarStyle] with the default values.
|
||||
* @param padding the padding.
|
||||
* @param shape the shape.
|
||||
* @param borderWidth the border width.
|
||||
* @param shadowSize the shadow size.
|
||||
* @param contentSpacing the spacing between the components of content.
|
||||
* @param contentPadding the padding of content.
|
||||
* @param contentShape the content shape.
|
||||
* @return [NavigationBarStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultNavigationBarStyle()
|
||||
fun style(
|
||||
padding: ComponentPadding = NavigationBarProperties.Padding.toPadding(),
|
||||
shape: Shape = NavigationBarProperties.Shape.toShape(),
|
||||
borderWidth: Dp = NavigationBarProperties.BorderWidth.toDp(),
|
||||
shadowSize: Dp = NavigationBarProperties.ShadowSize,
|
||||
contentSpacing: Dp = NavigationBarProperties.ContentSpacing.toDp(),
|
||||
contentPadding: ComponentPadding = NavigationBarProperties.ContentPadding.toPadding(),
|
||||
contentShape: Shape = NavigationBarProperties.Shape.toShape()
|
||||
) = NavigationBarStyle(
|
||||
padding = padding,
|
||||
shape = shape,
|
||||
borderWidth = borderWidth,
|
||||
shadowSize = shadowSize,
|
||||
contentSpacing = contentSpacing,
|
||||
contentPadding = contentPadding,
|
||||
contentShape = contentShape
|
||||
)
|
||||
}
|
||||
|
||||
@Stable
|
||||
internal object NavigationBarProperties {
|
||||
val BackgroundColor = AreaBoxProperties.BackgroundColor
|
||||
val BorderColor = AreaBoxProperties.BorderColor
|
||||
val IndicatorColor = ColorsDescriptor.ThemeTertiary
|
||||
val SelectedContentColor = ColorsDescriptor.ThemePrimary
|
||||
val UnselectedContentColor = ColorsDescriptor.TextSecondary
|
||||
val Padding = AreaBoxProperties.Padding
|
||||
val Shape = AreaBoxProperties.Shape
|
||||
val BorderWidth = AreaBoxProperties.BorderWidth
|
||||
val ShadowSize = AreaBoxProperties.ShadowSize
|
||||
val ContentSpacing = SizesDescriptor.SpacingPrimary
|
||||
val ContentPadding = PaddingDescriptor(
|
||||
horizontal = SizesDescriptor.SpacingPrimary,
|
||||
vertical = SizesDescriptor.SpacingSecondary
|
||||
)
|
||||
}
|
||||
|
||||
private val LocalHorizontalNavigationBar = compositionLocalOf { true }
|
||||
|
||||
private val LocalNavigationBarColors = compositionLocalOf<NavigationBarColors?> { null }
|
||||
|
||||
private val LocalNavigationBarContentSpacing = compositionLocalOf { Dp.Unspecified }
|
||||
|
||||
private val LocalNavigationBarContentPadding = compositionLocalOf<PaddingValues?> { null }
|
||||
|
||||
private val LocalNavigationBarContentPadding = compositionLocalOf<ComponentPadding?> { null }
|
||||
private val LocalNavigationBarContentShape = compositionLocalOf<Shape?> { null }
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultNavigationBarColors() = NavigationBarColors(
|
||||
backgroundColor = AreaBoxDefaults.color,
|
||||
indicatorColor = LocalColors.current.themeTertiary,
|
||||
selectedContentColor = LocalColors.current.themePrimary,
|
||||
unselectedContentColor = LocalColors.current.textSecondary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultNavigationBarStyle() = NavigationBarStyle(
|
||||
boxStyle = AreaBoxDefaults.style,
|
||||
contentSpacing = LocalSizes.current.spacingSecondary,
|
||||
contentPadding = ComponentPadding(
|
||||
horizontal = LocalSizes.current.spacingPrimary,
|
||||
vertical = LocalSizes.current.spacingSecondary
|
||||
),
|
||||
contentShape = withAreaBoxShape()
|
||||
)
|
||||
|
||||
private const val VerticalContentSpacingRatio = 1.6f
|
@@ -19,7 +19,7 @@
|
||||
*
|
||||
* This file is created by fankes on 2023/11/8.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
@file:Suppress("unused", "ObjectPropertyName")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
@@ -37,7 +37,6 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.progressSemantics
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -52,14 +51,16 @@ import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.highcapable.betterandroid.compose.extension.ui.orNull
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.toColor
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* Style interface for progress indicator.
|
||||
* @see CircularIndicatorStyle
|
||||
* @see LinearIndicatorStyle
|
||||
*/
|
||||
@Stable
|
||||
interface ProgressIndicatorStyle {
|
||||
@@ -69,40 +70,45 @@ interface ProgressIndicatorStyle {
|
||||
|
||||
/**
|
||||
* Animation interface for progress indicator.
|
||||
* @see CircularIndicatorAnimation
|
||||
* @see LinearIndicatorAnimation
|
||||
*/
|
||||
@Stable
|
||||
interface ProgressIndicatorAnimation {
|
||||
val duration: Int
|
||||
}
|
||||
|
||||
/**
|
||||
* Colors defines for progress indicator.
|
||||
* @see CircularIndicatorDefaults.colors
|
||||
* @see LinearIndicatorDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class ProgressIndicatorColors(
|
||||
val foregroundColor: Color,
|
||||
val backgroundColor: Color
|
||||
)
|
||||
|
||||
/**
|
||||
* Style defines for circular progress indicator.
|
||||
* @param strokeWidth the stroke width of indicator.
|
||||
* @param strokeCap the stroke cap of indicator.
|
||||
* @param radius the radius of indicator.
|
||||
* @param animation the animation of indicator.
|
||||
* @see CircularIndicatorDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class CircularIndicatorStyle(
|
||||
override val strokeWidth: Dp,
|
||||
override val strokeCap: StrokeCap,
|
||||
val radius: Dp,
|
||||
val animation: CircularIndicatorAnimation
|
||||
val radius: Dp
|
||||
) : ProgressIndicatorStyle
|
||||
|
||||
/**
|
||||
* Style defines for linear progress indicator.
|
||||
* @param strokeWidth the stroke width of indicator.
|
||||
* @param strokeCap the stroke cap of indicator.
|
||||
* @param width the width of indicator.
|
||||
* @param animation the animation of indicator.
|
||||
* @see LinearIndicatorDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class LinearIndicatorStyle(
|
||||
override val strokeWidth: Dp,
|
||||
override val strokeCap: StrokeCap,
|
||||
val width: Dp,
|
||||
val animation: LinearIndicatorAnimation
|
||||
val width: Dp
|
||||
) : ProgressIndicatorStyle
|
||||
|
||||
/**
|
||||
@@ -147,17 +153,6 @@ data class LinearIndicatorAnimation(
|
||||
val secondLineTailDelay: Int
|
||||
) : ProgressIndicatorAnimation
|
||||
|
||||
/**
|
||||
* Colors defines for progress indicator.
|
||||
* @param foregroundColor the foreground color of indicator.
|
||||
* @param backgroundColor the background color of indicator.
|
||||
*/
|
||||
@Immutable
|
||||
data class ProgressIndicatorColors(
|
||||
val foregroundColor: Color,
|
||||
val backgroundColor: Color
|
||||
)
|
||||
|
||||
/**
|
||||
* Flexi UI circular progress indicator.
|
||||
* @see LinearProgressIndicator
|
||||
@@ -166,8 +161,9 @@ data class ProgressIndicatorColors(
|
||||
* @param min the min of indicator, default is 0f.
|
||||
* @param max the max of indicator, default is 1f.
|
||||
* @param indeterminate the indeterminate of indicator, default is false.
|
||||
* @param colors the colors of indicator, default is [CircularIndicatorDefaults.colors].
|
||||
* @param style the style of indicator, default is [CircularIndicatorDefaults.style].
|
||||
* @param animation the animation of indicator.
|
||||
* @param colors the colors of indicator.
|
||||
* @param style the style of indicator.
|
||||
*/
|
||||
@Composable
|
||||
fun CircularProgressIndicator(
|
||||
@@ -176,11 +172,14 @@ fun CircularProgressIndicator(
|
||||
min: Float = 0f,
|
||||
max: Float = 1f,
|
||||
indeterminate: Boolean = progress < min,
|
||||
colors: ProgressIndicatorColors = CircularIndicatorDefaults.colors,
|
||||
style: CircularIndicatorStyle = CircularIndicatorDefaults.style
|
||||
animation: CircularIndicatorAnimation = DefaultCircularIndicatorAnimation,
|
||||
colors: ProgressIndicatorColors? = null,
|
||||
style: CircularIndicatorStyle? = null
|
||||
) {
|
||||
val diameter = style.radius * 2
|
||||
val stroke = with(LocalDensity.current) { Stroke(width = style.strokeWidth.toPx(), cap = style.strokeCap) }
|
||||
val currentColors = colors ?: LocalProgressIndicatorColors.current ?: CircularIndicatorDefaults.colors()
|
||||
val currentStyle = style ?: LocalProgressIndicatorStyle.current as? CircularIndicatorStyle? ?: CircularIndicatorDefaults.style()
|
||||
val diameter = currentStyle.radius * 2
|
||||
val stroke = with(LocalDensity.current) { Stroke(width = currentStyle.strokeWidth.toPx(), cap = currentStyle.strokeCap) }
|
||||
|
||||
/** Build determinate progress indicator. */
|
||||
@Composable
|
||||
@@ -190,8 +189,8 @@ fun CircularProgressIndicator(
|
||||
Canvas(modifier.progressSemantics(normalizedProgress).size(diameter)) {
|
||||
val startAngle = 270f
|
||||
val sweep = normalizedProgress * 360f
|
||||
drawCircularIndicatorBackground(colors.backgroundColor, stroke)
|
||||
drawCircularIndicator(startAngle, sweep, colors.foregroundColor, stroke)
|
||||
drawCircularIndicatorBackground(currentColors.backgroundColor, stroke)
|
||||
drawCircularIndicator(startAngle, sweep, currentColors.foregroundColor, stroke)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,55 +200,55 @@ fun CircularProgressIndicator(
|
||||
val transition = rememberInfiniteTransition()
|
||||
val currentRotation by transition.animateValue(
|
||||
initialValue = 0,
|
||||
style.animation.rotationsPerCycle,
|
||||
animation.rotationsPerCycle,
|
||||
Int.VectorConverter,
|
||||
infiniteRepeatable(
|
||||
animation = tween(
|
||||
durationMillis = style.animation.duration * style.animation.rotationsPerCycle,
|
||||
durationMillis = animation.duration * animation.rotationsPerCycle,
|
||||
easing = LinearEasing
|
||||
)
|
||||
)
|
||||
)
|
||||
val baseRotation by transition.animateFloat(
|
||||
initialValue = 0f,
|
||||
style.animation.baseRotationAngle,
|
||||
animation.baseRotationAngle,
|
||||
infiniteRepeatable(
|
||||
animation = tween(
|
||||
durationMillis = style.animation.duration,
|
||||
durationMillis = animation.duration,
|
||||
easing = LinearEasing
|
||||
)
|
||||
)
|
||||
)
|
||||
val headAndTailAnimationDuration = caleHeadAndTailAnimationDuration(style.animation.duration)
|
||||
val headAndTailAnimationDuration = caleHeadAndTailAnimationDuration(animation.duration)
|
||||
val endAngle by transition.animateFloat(
|
||||
initialValue = 0f,
|
||||
style.animation.jumpRotationAngle,
|
||||
animation.jumpRotationAngle,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = headAndTailAnimationDuration * 2
|
||||
0f at 0 with CircularEasing
|
||||
style.animation.jumpRotationAngle at headAndTailAnimationDuration
|
||||
animation.jumpRotationAngle at headAndTailAnimationDuration
|
||||
}
|
||||
)
|
||||
)
|
||||
val startAngle by transition.animateFloat(
|
||||
initialValue = 0f,
|
||||
style.animation.jumpRotationAngle,
|
||||
animation.jumpRotationAngle,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = headAndTailAnimationDuration * 2
|
||||
0f at headAndTailAnimationDuration with CircularEasing
|
||||
style.animation.jumpRotationAngle at durationMillis
|
||||
animation.jumpRotationAngle at durationMillis
|
||||
}
|
||||
)
|
||||
)
|
||||
Canvas(modifier.progressSemantics().size(diameter)) {
|
||||
drawCircularIndicatorBackground(colors.backgroundColor, stroke)
|
||||
val rotationAngleOffset = caleRotationAngleOffset(style.animation.baseRotationAngle, style.animation.jumpRotationAngle)
|
||||
drawCircularIndicatorBackground(currentColors.backgroundColor, stroke)
|
||||
val rotationAngleOffset = caleRotationAngleOffset(animation.baseRotationAngle, animation.jumpRotationAngle)
|
||||
val currentRotationAngleOffset = (currentRotation * rotationAngleOffset) % 360f
|
||||
val sweep = abs(endAngle - startAngle)
|
||||
val offset = style.animation.startAngleOffset + currentRotationAngleOffset + baseRotation
|
||||
drawIndeterminateCircularIndicator(startAngle + offset, style.strokeWidth, diameter, sweep, colors.foregroundColor, stroke)
|
||||
val offset = animation.startAngleOffset + currentRotationAngleOffset + baseRotation
|
||||
drawIndeterminateCircularIndicator(startAngle + offset, currentStyle.strokeWidth, diameter, sweep, currentColors.foregroundColor, stroke)
|
||||
}
|
||||
}
|
||||
if (indeterminate) Indeterminate() else Determinate()
|
||||
@@ -263,8 +262,9 @@ fun CircularProgressIndicator(
|
||||
* @param min the min of indicator, default is 0f.
|
||||
* @param max the max of indicator, default is 1f.
|
||||
* @param indeterminate the indeterminate of indicator, default is false.
|
||||
* @param colors the colors of indicator, default is [LinearIndicatorDefaults.colors].
|
||||
* @param style the style of indicator, default is [LinearIndicatorDefaults.style].
|
||||
* @param animation the animation of indicator.
|
||||
* @param colors the colors of indicator.
|
||||
* @param style the style of indicator.
|
||||
*/
|
||||
@Composable
|
||||
fun LinearProgressIndicator(
|
||||
@@ -273,18 +273,22 @@ fun LinearProgressIndicator(
|
||||
min: Float = 0f,
|
||||
max: Float = 1f,
|
||||
indeterminate: Boolean = progress < min,
|
||||
colors: ProgressIndicatorColors = LinearIndicatorDefaults.colors,
|
||||
style: LinearIndicatorStyle = LinearIndicatorDefaults.style
|
||||
animation: LinearIndicatorAnimation = DefaultLinearIndicatorAnimation,
|
||||
colors: ProgressIndicatorColors? = null,
|
||||
style: LinearIndicatorStyle? = null
|
||||
) {
|
||||
val currentColors = colors ?: LocalProgressIndicatorColors.current ?: CircularIndicatorDefaults.colors()
|
||||
val currentStyle = style ?: LocalProgressIndicatorStyle.current as? LinearIndicatorStyle? ?: LinearIndicatorDefaults.style()
|
||||
|
||||
/** Build determinate progress indicator. */
|
||||
@Composable
|
||||
fun Determinate() {
|
||||
val coercedProgress = progress.coerceIn(min, max)
|
||||
val normalizedProgress = (coercedProgress - min) / (max - min)
|
||||
Canvas(modifier.progressSemantics(normalizedProgress).size(style.width, style.strokeWidth)) {
|
||||
Canvas(modifier.progressSemantics(normalizedProgress).size(currentStyle.width, currentStyle.strokeWidth)) {
|
||||
val strokeWidth = size.height
|
||||
drawLinearIndicatorBackground(colors.backgroundColor, strokeWidth, style.strokeCap)
|
||||
drawLinearIndicator(startFraction = 0f, normalizedProgress, colors.foregroundColor, strokeWidth, style.strokeCap)
|
||||
drawLinearIndicatorBackground(currentColors.backgroundColor, strokeWidth, currentStyle.strokeCap)
|
||||
drawLinearIndicator(startFraction = 0f, normalizedProgress, currentColors.foregroundColor, strokeWidth, currentStyle.strokeCap)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,9 +301,9 @@ fun LinearProgressIndicator(
|
||||
targetValue = 1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = style.animation.duration
|
||||
0f at style.animation.firstLineHeadDelay with FirstLineHeadEasing
|
||||
1f at style.animation.firstLineHeadDuration + style.animation.firstLineHeadDelay
|
||||
durationMillis = animation.duration
|
||||
0f at animation.firstLineHeadDelay with FirstLineHeadEasing
|
||||
1f at animation.firstLineHeadDuration + animation.firstLineHeadDelay
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -308,9 +312,9 @@ fun LinearProgressIndicator(
|
||||
targetValue = 1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = style.animation.duration
|
||||
0f at style.animation.firstLineTailDelay with FirstLineTailEasing
|
||||
1f at style.animation.firstLineTailDuration + style.animation.firstLineTailDelay
|
||||
durationMillis = animation.duration
|
||||
0f at animation.firstLineTailDelay with FirstLineTailEasing
|
||||
1f at animation.firstLineTailDuration + animation.firstLineTailDelay
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -319,9 +323,9 @@ fun LinearProgressIndicator(
|
||||
targetValue = 1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = style.animation.duration
|
||||
0f at style.animation.secondLineHeadDelay with SecondLineHeadEasing
|
||||
1f at style.animation.secondLineHeadDuration + style.animation.secondLineHeadDelay
|
||||
durationMillis = animation.duration
|
||||
0f at animation.secondLineHeadDelay with SecondLineHeadEasing
|
||||
1f at animation.secondLineHeadDuration + animation.secondLineHeadDelay
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -330,19 +334,19 @@ fun LinearProgressIndicator(
|
||||
targetValue = 1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = style.animation.duration
|
||||
0f at style.animation.secondLineTailDelay with SecondLineTailEasing
|
||||
1f at style.animation.secondLineTailDuration + style.animation.secondLineTailDelay
|
||||
durationMillis = animation.duration
|
||||
0f at animation.secondLineTailDelay with SecondLineTailEasing
|
||||
1f at animation.secondLineTailDuration + animation.secondLineTailDelay
|
||||
}
|
||||
)
|
||||
)
|
||||
Canvas(modifier.progressSemantics().size(style.width, style.strokeWidth)) {
|
||||
Canvas(modifier.progressSemantics().size(currentStyle.width, currentStyle.strokeWidth)) {
|
||||
val strokeWidth = size.height
|
||||
drawLinearIndicatorBackground(colors.backgroundColor, strokeWidth, style.strokeCap)
|
||||
drawLinearIndicatorBackground(currentColors.backgroundColor, strokeWidth, currentStyle.strokeCap)
|
||||
if (firstLineHead - firstLineTail > 0)
|
||||
drawLinearIndicator(firstLineHead, firstLineTail, colors.foregroundColor, strokeWidth, style.strokeCap)
|
||||
drawLinearIndicator(firstLineHead, firstLineTail, currentColors.foregroundColor, strokeWidth, currentStyle.strokeCap)
|
||||
if (secondLineHead - secondLineTail > 0)
|
||||
drawLinearIndicator(secondLineHead, secondLineTail, colors.foregroundColor, strokeWidth, style.strokeCap)
|
||||
drawLinearIndicator(secondLineHead, secondLineTail, currentColors.foregroundColor, strokeWidth, currentStyle.strokeCap)
|
||||
}
|
||||
}
|
||||
if (indeterminate) Indeterminate() else Determinate()
|
||||
@@ -422,100 +426,107 @@ private fun DrawScope.drawLinearIndicator(
|
||||
* Defaults of circular progress indicator.
|
||||
*/
|
||||
object CircularIndicatorDefaults {
|
||||
val colors: ProgressIndicatorColors
|
||||
|
||||
/**
|
||||
* Creates a [ProgressIndicatorColors] with the default values.
|
||||
* @param foregroundColor the foreground color of indicator.
|
||||
* @param backgroundColor the background color of indicator.
|
||||
* @return [ProgressIndicatorColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalProgressIndicatorColors.current.copy(
|
||||
foregroundColor = LocalProgressIndicatorColors.current.foregroundColor.orNull()
|
||||
?: defaultCircularIndicatorColors().foregroundColor,
|
||||
backgroundColor = LocalProgressIndicatorColors.current.backgroundColor.orNull()
|
||||
?: defaultCircularIndicatorColors().backgroundColor
|
||||
fun colors(
|
||||
foregroundColor: Color = CircularIndicatorProperties.ForegroundColor.toColor(),
|
||||
backgroundColor: Color = CircularIndicatorProperties.BackgroundColor
|
||||
) = ProgressIndicatorColors(
|
||||
foregroundColor = foregroundColor,
|
||||
backgroundColor = backgroundColor
|
||||
)
|
||||
val style: CircularIndicatorStyle
|
||||
|
||||
/**
|
||||
* Creates a [CircularIndicatorStyle] with the default values.
|
||||
* @param strokeWidth the stroke width of indicator.
|
||||
* @param strokeCap the stroke cap of indicator.
|
||||
* @param radius the radius of indicator.
|
||||
* @return [CircularIndicatorStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultCircularIndicatorStyle()
|
||||
fun style(
|
||||
strokeWidth: Dp = CircularIndicatorProperties.StrokeWidth,
|
||||
strokeCap: StrokeCap = CircularIndicatorProperties._StrokeCap,
|
||||
radius: Dp = CircularIndicatorProperties.Radius
|
||||
) = CircularIndicatorStyle(
|
||||
strokeWidth = strokeWidth,
|
||||
strokeCap = strokeCap,
|
||||
radius = radius
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Defaults of linear progress indicator.
|
||||
*/
|
||||
object LinearIndicatorDefaults {
|
||||
val colors: ProgressIndicatorColors
|
||||
|
||||
/**
|
||||
* Creates a [ProgressIndicatorColors] with the default values.
|
||||
* @param foregroundColor the foreground color of indicator.
|
||||
* @param backgroundColor the background color of indicator.
|
||||
* @return [ProgressIndicatorColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalProgressIndicatorColors.current.copy(
|
||||
foregroundColor = LocalProgressIndicatorColors.current.foregroundColor.orNull()
|
||||
?: defaultLinearIndicatorColors().foregroundColor,
|
||||
backgroundColor = LocalProgressIndicatorColors.current.backgroundColor.orNull()
|
||||
?: defaultLinearIndicatorColors().backgroundColor
|
||||
fun colors(
|
||||
foregroundColor: Color = LinearIndicatorProperties.ForegroundColor.toColor(),
|
||||
backgroundColor: Color = LinearIndicatorProperties.BackgroundColor.toColor()
|
||||
) = ProgressIndicatorColors(
|
||||
foregroundColor = foregroundColor,
|
||||
backgroundColor = backgroundColor
|
||||
)
|
||||
val style: LinearIndicatorStyle
|
||||
|
||||
/**
|
||||
* Creates a [LinearIndicatorStyle] with the default values.
|
||||
* @param strokeWidth the stroke width of indicator.
|
||||
* @param strokeCap the stroke cap of indicator.
|
||||
* @param width the width of indicator.
|
||||
* @return [LinearIndicatorStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultLinearIndicatorStyle()
|
||||
fun style(
|
||||
strokeWidth: Dp = LinearIndicatorProperties.StrokeWidth,
|
||||
strokeCap: StrokeCap = LinearIndicatorProperties._StrokeCap,
|
||||
width: Dp = LinearIndicatorProperties.Width
|
||||
) = LinearIndicatorStyle(
|
||||
strokeWidth = strokeWidth,
|
||||
strokeCap = strokeCap,
|
||||
width = width
|
||||
)
|
||||
}
|
||||
|
||||
internal val LocalProgressIndicatorColors = compositionLocalOf { DefaultProgressIndicatorColors }
|
||||
@Stable
|
||||
internal object CircularIndicatorProperties {
|
||||
val ForegroundColor = ColorsDescriptor.ThemePrimary
|
||||
val BackgroundColor = Color.Transparent
|
||||
val StrokeWidth = 4.dp
|
||||
val _StrokeCap = StrokeCap.Round
|
||||
val Radius = 20.dp
|
||||
}
|
||||
|
||||
private val DefaultProgressIndicatorColors = ProgressIndicatorColors(Color.Unspecified, Color.Unspecified)
|
||||
@Stable
|
||||
internal object LinearIndicatorProperties {
|
||||
val ForegroundColor = ColorsDescriptor.ThemePrimary
|
||||
val BackgroundColor = ColorsDescriptor.ThemeTertiary
|
||||
val StrokeWidth = 4.dp
|
||||
val _StrokeCap = StrokeCap.Round
|
||||
val Width = 240.dp
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultCircularIndicatorColors() = ProgressIndicatorColors(
|
||||
foregroundColor = LocalColors.current.themePrimary,
|
||||
backgroundColor = Color.Transparent
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultLinearIndicatorColors() = ProgressIndicatorColors(
|
||||
foregroundColor = LocalColors.current.themePrimary,
|
||||
backgroundColor = LocalColors.current.themeTertiary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultCircularIndicatorStyle() = CircularIndicatorStyle(
|
||||
strokeWidth = DefaultIndicatorStrokeWidth,
|
||||
strokeCap = StrokeCap.Round,
|
||||
radius = DefaultCircularIndicatorRadius,
|
||||
animation = CircularIndicatorAnimation(
|
||||
duration = DefaultRotationDuration,
|
||||
rotationsPerCycle = DefaultRotationsPerCycle,
|
||||
startAngleOffset = DefaultStartAngleOffset,
|
||||
baseRotationAngle = DefaultBaseRotationAngle,
|
||||
jumpRotationAngle = DefaultJumpRotationAngle
|
||||
)
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultLinearIndicatorStyle() = LinearIndicatorStyle(
|
||||
strokeWidth = DefaultIndicatorStrokeWidth,
|
||||
strokeCap = StrokeCap.Round,
|
||||
width = DefaultLinearIndicatorWidth,
|
||||
animation = LinearIndicatorAnimation(
|
||||
duration = DefaultLinearAnimationDuration,
|
||||
firstLineHeadDuration = DefaultFirstLineHeadDuration,
|
||||
firstLineTailDuration = DefaultFirstLineTailDuration,
|
||||
secondLineHeadDuration = DefaultSecondLineHeadDuration,
|
||||
secondLineTailDuration = DefaultSecondLineTailDuration,
|
||||
firstLineHeadDelay = DefaultFirstLineHeadDelay,
|
||||
firstLineTailDelay = DefaultFirstLineTailDelay,
|
||||
secondLineHeadDelay = DefaultSecondLineHeadDelay,
|
||||
secondLineTailDelay = DefaultSecondLineTailDelay
|
||||
)
|
||||
)
|
||||
internal val LocalProgressIndicatorColors = compositionLocalOf<ProgressIndicatorColors?> { null }
|
||||
internal val LocalProgressIndicatorStyle = compositionLocalOf<ProgressIndicatorStyle?> { null }
|
||||
|
||||
private fun caleRotationAngleOffset(baseRotationAngle: Float, jumpRotationAngle: Float) = (baseRotationAngle + jumpRotationAngle) % 360f
|
||||
|
||||
private fun caleHeadAndTailAnimationDuration(rotationDuration: Int) = (rotationDuration * 0.5).toInt()
|
||||
|
||||
private val DefaultIndicatorStrokeWidth = 4.dp
|
||||
private val DefaultLinearIndicatorWidth = 240.dp
|
||||
private val DefaultCircularIndicatorRadius = 20.dp
|
||||
private const val DefaultLinearAnimationDuration = 1800
|
||||
private const val DefaultRotationAnimationDuration = 1332
|
||||
|
||||
private const val DefaultFirstLineHeadDuration = 750
|
||||
private const val DefaultFirstLineTailDuration = 850
|
||||
@@ -528,7 +539,6 @@ private const val DefaultSecondLineHeadDelay = 1000
|
||||
private const val DefaultSecondLineTailDelay = 1267
|
||||
|
||||
private const val DefaultRotationsPerCycle = 5
|
||||
private const val DefaultRotationDuration = 1332
|
||||
private const val DefaultStartAngleOffset = -90f
|
||||
private const val DefaultBaseRotationAngle = 286f
|
||||
private const val DefaultJumpRotationAngle = 290f
|
||||
@@ -538,3 +548,23 @@ private val FirstLineTailEasing = CubicBezierEasing(0.4f, 0f, 1f, 1f)
|
||||
private val SecondLineHeadEasing = CubicBezierEasing(0f, 0f, 0.65f, 1f)
|
||||
private val SecondLineTailEasing = CubicBezierEasing(0.1f, 0f, 0.45f, 1f)
|
||||
private val CircularEasing = CubicBezierEasing(0.4f, 0f, 0.2f, 1f)
|
||||
|
||||
private val DefaultLinearIndicatorAnimation = LinearIndicatorAnimation(
|
||||
duration = DefaultLinearAnimationDuration,
|
||||
firstLineHeadDuration = DefaultFirstLineHeadDuration,
|
||||
firstLineTailDuration = DefaultFirstLineTailDuration,
|
||||
secondLineHeadDuration = DefaultSecondLineHeadDuration,
|
||||
secondLineTailDuration = DefaultSecondLineTailDuration,
|
||||
firstLineHeadDelay = DefaultFirstLineHeadDelay,
|
||||
firstLineTailDelay = DefaultFirstLineTailDelay,
|
||||
secondLineHeadDelay = DefaultSecondLineHeadDelay,
|
||||
secondLineTailDelay = DefaultSecondLineTailDelay
|
||||
)
|
||||
|
||||
private val DefaultCircularIndicatorAnimation = CircularIndicatorAnimation(
|
||||
duration = DefaultRotationAnimationDuration,
|
||||
rotationsPerCycle = DefaultRotationsPerCycle,
|
||||
startAngleOffset = DefaultStartAngleOffset,
|
||||
baseRotationAngle = DefaultBaseRotationAngle,
|
||||
jumpRotationAngle = DefaultJumpRotationAngle
|
||||
)
|
@@ -19,14 +19,13 @@
|
||||
*
|
||||
* This file is created by fankes on 2023/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
@file:Suppress("unused", "ConstPropertyName")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsHoveredAsState
|
||||
@@ -39,7 +38,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -54,32 +53,28 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.highcapable.betterandroid.compose.extension.ui.clickable
|
||||
import com.highcapable.betterandroid.compose.extension.ui.componentState
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalShapes
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.ShapesDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.toColor
|
||||
import com.highcapable.flexiui.toDp
|
||||
import com.highcapable.flexiui.toShape
|
||||
|
||||
/**
|
||||
* Colors defines for radio button.
|
||||
* @param contentColor the color of the check mark.
|
||||
* @param inactiveColor the color of the unchecked box.
|
||||
* @param activeColor the color of the checked box.
|
||||
* @see RadioButtonDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class RadioButtonColors(
|
||||
val contentColor: Color,
|
||||
val inactiveColor: Color,
|
||||
val activeColor: Color
|
||||
val activeColor: Color,
|
||||
val borderColor: Color
|
||||
)
|
||||
|
||||
/**
|
||||
* Style defines for radio button.
|
||||
* @param contentSpacing the spacing between the check mark and the content.
|
||||
* @param contentRadius the radius of the check mark.
|
||||
* @param strokeRadius the radius of the box.
|
||||
* @param pressedGain the gain when pressed.
|
||||
* @param hoveredGain the gain when hovered.
|
||||
* @param shape the shape.
|
||||
* @param border the border stroke.
|
||||
* @see RadioButtonDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class RadioButtonStyle(
|
||||
@@ -90,7 +85,7 @@ data class RadioButtonStyle(
|
||||
val pressedGain: Float,
|
||||
val hoveredGain: Float,
|
||||
val shape: Shape,
|
||||
val border: BorderStroke
|
||||
val borderWidth: Dp
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -109,8 +104,8 @@ fun RadioButton(
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: RadioButtonColors = RadioButtonDefaults.colors,
|
||||
style: RadioButtonStyle = RadioButtonDefaults.style,
|
||||
colors: RadioButtonColors = RadioButtonDefaults.colors(),
|
||||
style: RadioButtonStyle = RadioButtonDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable (RowScope.() -> Unit)? = null
|
||||
@@ -157,45 +152,73 @@ fun RadioButton(
|
||||
* Defaults of radio button.
|
||||
*/
|
||||
object RadioButtonDefaults {
|
||||
val colors: RadioButtonColors
|
||||
|
||||
/**
|
||||
* Creates a [RadioButtonColors] with the default values.
|
||||
* @param contentColor the color of the check mark.
|
||||
* @param inactiveColor the color of the unchecked box.
|
||||
* @param activeColor the color of the checked box.
|
||||
* @param borderColor the color of the border.
|
||||
* @return [RadioButtonColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultRadioButtonColors()
|
||||
val style: RadioButtonStyle
|
||||
fun colors(
|
||||
contentColor: Color = RadioButtonProperties.ContentColor,
|
||||
inactiveColor: Color = RadioButtonProperties.InactiveColor.toColor(),
|
||||
activeColor: Color = RadioButtonProperties.ActiveColor.toColor(),
|
||||
borderColor: Color = RadioButtonProperties.BorderColor.toColor()
|
||||
) = RadioButtonColors(
|
||||
contentColor = contentColor,
|
||||
inactiveColor = inactiveColor,
|
||||
activeColor = activeColor,
|
||||
borderColor = borderColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [RadioButtonStyle] with the default values.
|
||||
* @param contentSpacing the spacing between the check mark and the content.
|
||||
* @param contentRadius the radius of the check mark.
|
||||
* @param strokeRadius the radius of the box.
|
||||
* @param pressedGain the gain when pressed.
|
||||
* @param hoveredGain the gain when hovered.
|
||||
* @param shape the shape.
|
||||
* @param borderWidth the border width.
|
||||
* @return [RadioButtonStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultRadioButtonStyle()
|
||||
fun style(
|
||||
contentSpacing: Dp = RadioButtonProperties.ContentSpacing.toDp(),
|
||||
contentRadius: Dp = RadioButtonProperties.ContentRadius,
|
||||
contentShadowSize: Dp = RadioButtonProperties.ContentShadowSize,
|
||||
strokeRadius: Dp = RadioButtonProperties.StrokeRadius,
|
||||
pressedGain: Float = RadioButtonProperties.PressedGain,
|
||||
hoveredGain: Float = RadioButtonProperties.HoveredGain,
|
||||
shape: Shape = RadioButtonProperties.Shape.toShape(),
|
||||
borderWidth: Dp = RadioButtonProperties.BorderWidth.toDp()
|
||||
) = RadioButtonStyle(
|
||||
contentSpacing = contentSpacing,
|
||||
contentRadius = contentRadius,
|
||||
contentShadowSize = contentShadowSize,
|
||||
strokeRadius = strokeRadius,
|
||||
pressedGain = pressedGain,
|
||||
hoveredGain = hoveredGain,
|
||||
shape = shape,
|
||||
borderWidth = borderWidth
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultRadioButtonColors() = RadioButtonColors(
|
||||
contentColor = Color.White,
|
||||
inactiveColor = LocalColors.current.themeTertiary,
|
||||
activeColor = LocalColors.current.themePrimary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultRadioButtonStyle() = RadioButtonStyle(
|
||||
contentSpacing = LocalSizes.current.spacingSecondary,
|
||||
contentRadius = DefaultContentRadius,
|
||||
contentShadowSize = DefaultContentShadowSize,
|
||||
strokeRadius = DefaultStrokeRadius,
|
||||
pressedGain = DefaultPressedGain,
|
||||
hoveredGain = DefaultHoveredGain,
|
||||
shape = LocalShapes.current.tertiary,
|
||||
border = defaultRadioButtonBorder()
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultRadioButtonBorder() = BorderStroke(LocalSizes.current.borderSizeTertiary, LocalColors.current.textPrimary)
|
||||
|
||||
private val DefaultContentRadius = 5.dp
|
||||
private val DefaultStrokeRadius = 10.dp
|
||||
|
||||
private const val DefaultPressedGain = 0.9f
|
||||
private const val DefaultHoveredGain = 1.2f
|
||||
|
||||
private val DefaultContentShadowSize = 0.5.dp
|
||||
@Stable
|
||||
internal object RadioButtonProperties {
|
||||
val ContentColor = Color.White
|
||||
val InactiveColor = ColorsDescriptor.ThemeTertiary
|
||||
val ActiveColor = ColorsDescriptor.ThemePrimary
|
||||
val BorderColor = ColorsDescriptor.TextPrimary
|
||||
val ContentSpacing = SizesDescriptor.SpacingSecondary
|
||||
val ContentRadius = 5.dp
|
||||
val ContentShadowSize = 0.5.dp
|
||||
val StrokeRadius = 10.dp
|
||||
const val PressedGain = 0.9f
|
||||
const val HoveredGain = 1.2f
|
||||
val Shape = ShapesDescriptor.Tertiary
|
||||
val BorderWidth = SizesDescriptor.BorderSizeTertiary
|
||||
}
|
@@ -58,7 +58,7 @@ import com.highcapable.betterandroid.compose.extension.ui.ComponentPadding
|
||||
@Composable
|
||||
fun Scaffold(
|
||||
modifier: Modifier = Modifier,
|
||||
colors: SurfaceColors = SurfaceDefaults.colors,
|
||||
colors: SurfaceColors = SurfaceDefaults.colors(),
|
||||
padding: ComponentPadding = SurfaceDefaults.padding,
|
||||
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
|
||||
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
||||
|
@@ -19,12 +19,11 @@
|
||||
*
|
||||
* This file is created by fankes on 2023/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
@file:Suppress("unused", "ConstPropertyName")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
@@ -42,7 +41,7 @@ import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CornerBasedShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -63,40 +62,33 @@ import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.highcapable.betterandroid.compose.extension.ui.borderOrElse
|
||||
import com.highcapable.betterandroid.compose.extension.ui.componentState
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalShapes
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.ShapesDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.toColor
|
||||
import com.highcapable.flexiui.toDp
|
||||
import com.highcapable.flexiui.toShape
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Colors defines for slider.
|
||||
* @param trackInactiveColor the inactive color of track.
|
||||
* @param trackActiveColor the active color of track.
|
||||
* @param thumbColor the color of thumb.
|
||||
* @param stepColor the color of step.
|
||||
* @see SliderDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class SliderColors(
|
||||
val trackInactiveColor: Color,
|
||||
val trackActiveColor: Color,
|
||||
val thumbColor: Color,
|
||||
val stepColor: Color
|
||||
val stepColor: Color,
|
||||
val thumbBorderColor: Color,
|
||||
val stepBorderColor: Color,
|
||||
val trackBorderColor: Color,
|
||||
val trackInactiveColor: Color,
|
||||
val trackActiveColor: Color
|
||||
)
|
||||
|
||||
/**
|
||||
* Style defines for slider.
|
||||
* @param thumbRadius the radius of thumb.
|
||||
* @param thumbGain the gain of thumb.
|
||||
* @param thumbShadowSize the shadow size of thumb.
|
||||
* @param thumbShape the shape of thumb.
|
||||
* @param stepShape the shape of step.
|
||||
* @param trackShape the shape of track.
|
||||
* @param thumbBorder the border of thumb.
|
||||
* @param stepBorder the border of step.
|
||||
* @param trackBorder the border of track.
|
||||
* @param trackWidth the width of track.
|
||||
* @param trackHeight the height of track.
|
||||
* @see SliderDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class SliderStyle(
|
||||
@@ -106,11 +98,11 @@ data class SliderStyle(
|
||||
val thumbShape: Shape,
|
||||
val stepShape: Shape,
|
||||
val trackShape: Shape,
|
||||
val thumbBorder: BorderStroke,
|
||||
val stepBorder: BorderStroke,
|
||||
val trackBorder: BorderStroke,
|
||||
val trackWidth: Dp,
|
||||
val trackHeight: Dp
|
||||
val trackHeight: Dp,
|
||||
val thumbBorderWidth: Dp,
|
||||
val stepBorderWidth: Dp,
|
||||
val trackBorderWidth: Dp
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -132,8 +124,8 @@ fun Slider(
|
||||
value: Float,
|
||||
onValueChange: (Float) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: SliderColors = SliderDefaults.colors,
|
||||
style: SliderStyle = SliderDefaults.style,
|
||||
colors: SliderColors = SliderDefaults.colors(),
|
||||
style: SliderStyle = SliderDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
min: Float = 0f,
|
||||
max: Float = 1f,
|
||||
@@ -181,7 +173,7 @@ fun Slider(
|
||||
Box(
|
||||
modifier = Modifier.size(trackAdoptWidth, style.trackHeight)
|
||||
.background(colors.trackInactiveColor, style.trackShape)
|
||||
.borderOrElse(style.trackBorder, style.trackShape)
|
||||
.borderOrElse(style.trackBorderWidth, colors.trackBorderColor, style.trackShape)
|
||||
.drawWithContent {
|
||||
drawRoundRect(
|
||||
color = colors.trackActiveColor,
|
||||
@@ -206,7 +198,7 @@ fun Slider(
|
||||
Box(
|
||||
modifier = Modifier.size(style.trackHeight)
|
||||
.background(colors.stepColor, style.stepShape)
|
||||
.borderOrElse(style.stepBorder, style.stepShape)
|
||||
.borderOrElse(style.stepBorderWidth, colors.stepBorderColor, style.stepShape)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -220,7 +212,7 @@ fun Slider(
|
||||
.scale(animatedScale)
|
||||
.shadow(style.thumbShadowSize, style.thumbShape)
|
||||
.background(colors.thumbColor, style.thumbShape)
|
||||
.borderOrElse(style.thumbBorder, style.thumbShape)
|
||||
.borderOrElse(style.thumbBorderWidth, colors.thumbBorderColor, style.thumbShape)
|
||||
.draggable(
|
||||
orientation = Orientation.Horizontal,
|
||||
state = rememberDraggableState { delta ->
|
||||
@@ -275,48 +267,98 @@ fun Slider(
|
||||
* Defaults of slider.
|
||||
*/
|
||||
object SliderDefaults {
|
||||
val colors
|
||||
|
||||
/**
|
||||
* Creates a [SliderColors] with the default values.
|
||||
* @param thumbColor the color of thumb.
|
||||
* @param stepColor the color of step.
|
||||
* @param thumbBorderColor the border color of thumb.
|
||||
* @param stepBorderColor the border color of step.
|
||||
* @param trackBorderColor the border color of track.
|
||||
* @param trackInactiveColor the inactive color of track.
|
||||
* @param trackActiveColor the active color of track.
|
||||
* @return [SliderColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultSliderColors()
|
||||
val style
|
||||
fun colors(
|
||||
thumbColor: Color = SliderProperties.ThumbColor.toColor(),
|
||||
stepColor: Color = SliderProperties.StepColor.toColor(),
|
||||
thumbBorderColor: Color = SliderProperties.ThumbBorderColor.toColor(),
|
||||
stepBorderColor: Color = SliderProperties.StepBorderColor.toColor(),
|
||||
trackBorderColor: Color = SliderProperties.TrackBorderColor.toColor(),
|
||||
trackInactiveColor: Color = SliderProperties.TrackInactiveColor.toColor(),
|
||||
trackActiveColor: Color = SliderProperties.TrackActiveColor.toColor()
|
||||
) = SliderColors(
|
||||
thumbColor = thumbColor,
|
||||
stepColor = stepColor,
|
||||
thumbBorderColor = thumbBorderColor,
|
||||
stepBorderColor = stepBorderColor,
|
||||
trackBorderColor = trackBorderColor,
|
||||
trackInactiveColor = trackInactiveColor,
|
||||
trackActiveColor = trackActiveColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [SliderStyle] with the default values.
|
||||
* @param thumbRadius the radius of thumb.
|
||||
* @param thumbGain the gain of thumb.
|
||||
* @param thumbShadowSize the shadow size of thumb.
|
||||
* @param thumbShape the shape of thumb.
|
||||
* @param stepShape the shape of step.
|
||||
* @param trackShape the shape of track.
|
||||
* @param trackWidth the width of track.
|
||||
* @param trackHeight the height of track.
|
||||
* @param thumbBorderWidth the border width of thumb.
|
||||
* @param stepBorderWidth the border width of step.
|
||||
* @param trackBorderWidth the border width of track.
|
||||
* @return [SliderStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultSliderStyle()
|
||||
fun style(
|
||||
thumbRadius: Dp = SliderProperties.ThumbRadius,
|
||||
thumbGain: Float = SliderProperties.ThumbGain,
|
||||
thumbShadowSize: Dp = SliderProperties.ThumbShadowSize,
|
||||
thumbShape: Shape = SliderProperties.ThumbShape.toShape(),
|
||||
stepShape: Shape = SliderProperties.StepShape.toShape(),
|
||||
trackShape: Shape = SliderProperties.TrackShape.toShape(),
|
||||
trackWidth: Dp = SliderProperties.TrackWidth,
|
||||
trackHeight: Dp = SliderProperties.TrackHeight,
|
||||
thumbBorderWidth: Dp = SliderProperties.ThumbBorderWidth.toDp(),
|
||||
stepBorderWidth: Dp = SliderProperties.StepBorderWidth.toDp(),
|
||||
trackBorderWidth: Dp = SliderProperties.TrackBorderWidth.toDp()
|
||||
) = SliderStyle(
|
||||
thumbRadius = thumbRadius,
|
||||
thumbGain = thumbGain,
|
||||
thumbShadowSize = thumbShadowSize,
|
||||
thumbShape = thumbShape,
|
||||
stepShape = stepShape,
|
||||
trackShape = trackShape,
|
||||
trackWidth = trackWidth,
|
||||
trackHeight = trackHeight,
|
||||
thumbBorderWidth = thumbBorderWidth,
|
||||
stepBorderWidth = stepBorderWidth,
|
||||
trackBorderWidth = trackBorderWidth
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultSliderColors() = SliderColors(
|
||||
trackInactiveColor = LocalColors.current.themeTertiary,
|
||||
trackActiveColor = LocalColors.current.themePrimary,
|
||||
thumbColor = LocalColors.current.themePrimary,
|
||||
stepColor = LocalColors.current.themeSecondary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultSliderStyle() = SliderStyle(
|
||||
thumbRadius = DefaultThumbRadius,
|
||||
thumbGain = DefaultThumbGain,
|
||||
thumbShadowSize = DefaultThumbShadowSize,
|
||||
thumbShape = LocalShapes.current.tertiary,
|
||||
stepShape = LocalShapes.current.tertiary,
|
||||
trackShape = LocalShapes.current.primary,
|
||||
thumbBorder = defaultSliderBorder(),
|
||||
stepBorder = defaultSliderBorder(),
|
||||
trackBorder = defaultSliderBorder(),
|
||||
trackWidth = DefaultTrackWidth,
|
||||
trackHeight = DefaultTrackHeight
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultSliderBorder() = BorderStroke(LocalSizes.current.borderSizeTertiary, LocalColors.current.textPrimary)
|
||||
|
||||
private val DefaultThumbRadius = 10.dp
|
||||
private const val DefaultThumbGain = 1.1f
|
||||
private val DefaultThumbShadowSize = 0.5.dp
|
||||
|
||||
private val DefaultTrackWidth = 240.dp
|
||||
private val DefaultTrackHeight = 4.dp
|
||||
@Stable
|
||||
internal object SliderProperties {
|
||||
val ThumbColor = ColorsDescriptor.ThemePrimary
|
||||
val StepColor = ColorsDescriptor.ThemeSecondary
|
||||
val ThumbBorderColor = ColorsDescriptor.TextPrimary
|
||||
val StepBorderColor = ColorsDescriptor.TextPrimary
|
||||
val TrackBorderColor = ColorsDescriptor.TextPrimary
|
||||
val TrackInactiveColor = ColorsDescriptor.ThemeTertiary
|
||||
val TrackActiveColor = ColorsDescriptor.ThemePrimary
|
||||
val ThumbRadius = 10.dp
|
||||
const val ThumbGain = 1.1f
|
||||
val ThumbShadowSize = 0.5.dp
|
||||
val ThumbShape = ShapesDescriptor.Tertiary
|
||||
val StepShape = ShapesDescriptor.Tertiary
|
||||
val TrackShape = ShapesDescriptor.Primary
|
||||
val TrackWidth = 240.dp
|
||||
val TrackHeight = 4.dp
|
||||
val ThumbBorderWidth = SizesDescriptor.BorderSizeTertiary
|
||||
val StepBorderWidth = SizesDescriptor.BorderSizeTertiary
|
||||
val TrackBorderWidth = SizesDescriptor.BorderSizeTertiary
|
||||
}
|
@@ -31,19 +31,22 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.debugInspectorInfo
|
||||
import com.highcapable.betterandroid.compose.extension.ui.ComponentPadding
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.PaddingDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.toColor
|
||||
|
||||
/**
|
||||
* Colors defines for surface.
|
||||
* @param contentColor the content color, usually for the text color.
|
||||
* @param backgroundColor the background color.
|
||||
* @see SurfaceDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class SurfaceColors(
|
||||
@@ -65,7 +68,7 @@ data class SurfaceColors(
|
||||
fun Surface(
|
||||
modifier: Modifier = Modifier,
|
||||
initializer: @Composable Modifier.() -> Modifier = { Modifier },
|
||||
colors: SurfaceColors = SurfaceDefaults.colors,
|
||||
colors: SurfaceColors = SurfaceDefaults.colors(),
|
||||
padding: ComponentPadding = SurfaceDefaults.padding,
|
||||
content: @Composable BoxScope.() -> Unit
|
||||
) {
|
||||
@@ -100,25 +103,34 @@ private fun Modifier.surface(
|
||||
* Defaults of surface.
|
||||
*/
|
||||
object SurfaceDefaults {
|
||||
val colors: SurfaceColors
|
||||
|
||||
/**
|
||||
* Creates a [SurfaceColors] with the default values.
|
||||
* @param contentColor the content color, usually for the text color.
|
||||
* @param backgroundColor the background color.
|
||||
* @return [SurfaceColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultSurfaceColors()
|
||||
fun colors(
|
||||
contentColor: Color = SurfaceProperties.ContentColor.toColor(),
|
||||
backgroundColor: Color = SurfaceProperties.BackgroundColor.toColor()
|
||||
) = SurfaceColors(contentColor, backgroundColor)
|
||||
|
||||
/**
|
||||
* Returns the default padding of surface.
|
||||
* @return [ComponentPadding]
|
||||
*/
|
||||
val padding: ComponentPadding
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultSurfacePadding()
|
||||
get() = SurfaceProperties.Padding.toPadding()
|
||||
}
|
||||
|
||||
@Stable
|
||||
internal object SurfaceProperties {
|
||||
val ContentColor = ColorsDescriptor.TextPrimary
|
||||
val BackgroundColor = ColorsDescriptor.BackgroundPrimary
|
||||
val Padding = PaddingDescriptor(SizesDescriptor.SpacingPrimary)
|
||||
}
|
||||
|
||||
internal val LocalInSurface = compositionLocalOf { false }
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultSurfaceColors() = SurfaceColors(
|
||||
contentColor = LocalColors.current.textPrimary,
|
||||
backgroundColor = LocalColors.current.backgroundPrimary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultSurfacePadding() = ComponentPadding(LocalSizes.current.spacingPrimary)
|
@@ -19,13 +19,12 @@
|
||||
*
|
||||
* This file is created by fankes on 2023/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
@file:Suppress("unused", "ConstPropertyName")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.draggable
|
||||
@@ -41,7 +40,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -62,37 +61,30 @@ import com.highcapable.betterandroid.compose.extension.ui.ComponentPadding
|
||||
import com.highcapable.betterandroid.compose.extension.ui.borderOrElse
|
||||
import com.highcapable.betterandroid.compose.extension.ui.clickable
|
||||
import com.highcapable.betterandroid.compose.extension.ui.componentState
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalShapes
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.ShapesDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.toColor
|
||||
import com.highcapable.flexiui.toDp
|
||||
import com.highcapable.flexiui.toShape
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Colors defines for switch.
|
||||
* @param thumbColor the color of thumb.
|
||||
* @param trackInactive the color of track when switch is inactive.
|
||||
* @param trackActive the color of track when switch is active.
|
||||
* @see SwitchDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class SwitchColors(
|
||||
val thumbColor: Color,
|
||||
val trackInactive: Color,
|
||||
val trackActive: Color
|
||||
val thumbBorderColor: Color,
|
||||
val trackBorderColor: Color,
|
||||
val trackInactiveColor: Color,
|
||||
val trackActiveColor: Color
|
||||
)
|
||||
|
||||
/**
|
||||
* Style defines for switch.
|
||||
* @param padding the padding between thumb and track.
|
||||
* @param contentSpacing the spacing between content and track.
|
||||
* @param thumbRadius the radius of thumb.
|
||||
* @param thumbGain the gain of thumb when switch is hovered or dragging.
|
||||
* @param thumbShadowSize the shadow size of thumb.
|
||||
* @param thumbShape the shape of thumb.
|
||||
* @param trackShape the shape of track.
|
||||
* @param thumbBorder the border of thumb.
|
||||
* @param trackBorder the border of track.
|
||||
* @param trackWidth the width of track.
|
||||
* @param trackHeight the height of track.
|
||||
* @see SwitchDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class SwitchStyle(
|
||||
@@ -103,10 +95,10 @@ data class SwitchStyle(
|
||||
val thumbShadowSize: Dp,
|
||||
val thumbShape: Shape,
|
||||
val trackShape: Shape,
|
||||
val thumbBorder: BorderStroke,
|
||||
val trackBorder: BorderStroke,
|
||||
val trackWidth: Dp,
|
||||
val trackHeight: Dp
|
||||
val trackHeight: Dp,
|
||||
val thumbBorderWidth: Dp,
|
||||
val trackBorderWidth: Dp
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -125,8 +117,8 @@ fun Switch(
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: SwitchColors = SwitchDefaults.colors,
|
||||
style: SwitchStyle = SwitchDefaults.style,
|
||||
colors: SwitchColors = SwitchDefaults.colors(),
|
||||
style: SwitchStyle = SwitchDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable (RowScope.() -> Unit)? = null
|
||||
@@ -142,12 +134,12 @@ fun Switch(
|
||||
if (!hovered && !dragging) offsetX = if (checked) maxOffsetX else 0f
|
||||
val animatedOffsetX by animateFloatAsState(offsetX)
|
||||
val animatedScale by animateFloatAsState(if (hovered || dragging) style.thumbGain else 1f)
|
||||
var trackColor by remember { mutableStateOf(colors.trackInactive) }
|
||||
var trackColor by remember { mutableStateOf(colors.trackInactiveColor) }
|
||||
|
||||
/** Update the track color of switch. */
|
||||
fun updateTrackColor() {
|
||||
val fraction = (offsetX / maxOffsetX).coerceIn(0f, 1f)
|
||||
trackColor = lerp(colors.trackInactive, colors.trackActive, fraction)
|
||||
trackColor = lerp(colors.trackInactiveColor, colors.trackActiveColor, fraction)
|
||||
}
|
||||
updateTrackColor()
|
||||
val animatedTrackColor by animateColorAsState(trackColor)
|
||||
@@ -166,7 +158,7 @@ fun Switch(
|
||||
offsetX = if (checked) 0f else maxOffsetX
|
||||
onCheckedChange(!checked)
|
||||
}.background(if (efficientDragging) trackColor else animatedTrackColor, style.trackShape)
|
||||
.borderOrElse(style.trackBorder, style.trackShape)
|
||||
.borderOrElse(style.trackBorderWidth, colors.trackBorderColor, style.trackShape)
|
||||
.size(style.trackWidth, style.trackHeight)
|
||||
.padding(style.padding),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
@@ -183,7 +175,7 @@ fun Switch(
|
||||
.scale(animatedScale)
|
||||
.shadow(style.thumbShadowSize, style.thumbShape)
|
||||
.background(colors.thumbColor, style.thumbShape)
|
||||
.borderOrElse(style.thumbBorder, style.thumbShape)
|
||||
.borderOrElse(style.thumbBorderWidth, colors.thumbBorderColor, style.thumbShape)
|
||||
.draggable(
|
||||
enabled = enabled,
|
||||
orientation = Orientation.Horizontal,
|
||||
@@ -232,49 +224,93 @@ fun Switch(
|
||||
* Defaults of switch.
|
||||
*/
|
||||
object SwitchDefaults {
|
||||
val colors: SwitchColors
|
||||
|
||||
/**
|
||||
* Creates a [SwitchColors] with the default values.
|
||||
* @param thumbColor the color of thumb.
|
||||
* @param thumbBorderColor the border color of thumb.
|
||||
* @param trackBorderColor the border color of track.
|
||||
* @param trackInactiveColor the color of track when switch is inactive.
|
||||
* @param trackActiveColor the color of track when switch is active.
|
||||
* @return [SwitchColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultSwitchColors()
|
||||
val style: SwitchStyle
|
||||
fun colors(
|
||||
thumbColor: Color = SwitchProperties.ThumbColor,
|
||||
thumbBorderColor: Color = SwitchProperties.ThumbBorderColor.toColor(),
|
||||
trackBorderColor: Color = SwitchProperties.TrackBorderColor.toColor(),
|
||||
trackInactiveColor: Color = SwitchProperties.TrackInactiveColor.toColor(),
|
||||
trackActiveColor: Color = SwitchProperties.TrackActiveColor.toColor()
|
||||
) = SwitchColors(
|
||||
thumbColor = thumbColor,
|
||||
thumbBorderColor = thumbBorderColor,
|
||||
trackBorderColor = trackBorderColor,
|
||||
trackInactiveColor = trackInactiveColor,
|
||||
trackActiveColor = trackActiveColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [SwitchStyle] with the default values.
|
||||
* @param padding the padding between thumb and track.
|
||||
* @param contentSpacing the spacing between content and track.
|
||||
* @param thumbRadius the radius of thumb.
|
||||
* @param thumbGain the gain of thumb when switch is hovered or dragging.
|
||||
* @param thumbShadowSize the shadow size of thumb.
|
||||
* @param thumbShape the shape of thumb.
|
||||
* @param trackShape the shape of track.
|
||||
* @param trackWidth the width of track.
|
||||
* @param trackHeight the height of track.
|
||||
* @param thumbBorderWidth the border width of thumb.
|
||||
* @param trackBorderWidth the border width of track.
|
||||
* @return [SwitchStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultSwitchStyle()
|
||||
fun style(
|
||||
padding: ComponentPadding = SwitchProperties.Padding,
|
||||
contentSpacing: Dp = SwitchProperties.ContentSpacing.toDp(),
|
||||
thumbRadius: Dp = SwitchProperties.ThumbRadius,
|
||||
thumbGain: Float = SwitchProperties.ThumbGain,
|
||||
thumbShadowSize: Dp = SwitchProperties.ThumbShadowSize,
|
||||
thumbShape: Shape = SwitchProperties.ThumbShape.toShape(),
|
||||
trackShape: Shape = SwitchProperties.TrackShape.toShape(),
|
||||
trackWidth: Dp = SwitchProperties.TrackWidth,
|
||||
trackHeight: Dp = SwitchProperties.TrackHeight,
|
||||
trackBorderWidth: Dp = SwitchProperties.TrackBorderWidth.toDp(),
|
||||
thumbBorderWidth: Dp = SwitchProperties.ThumbBorderWidth.toDp()
|
||||
) = SwitchStyle(
|
||||
padding = padding,
|
||||
contentSpacing = contentSpacing,
|
||||
thumbRadius = thumbRadius,
|
||||
thumbGain = thumbGain,
|
||||
thumbShadowSize = thumbShadowSize,
|
||||
thumbShape = thumbShape,
|
||||
trackShape = trackShape,
|
||||
trackWidth = trackWidth,
|
||||
trackHeight = trackHeight,
|
||||
thumbBorderWidth = thumbBorderWidth,
|
||||
trackBorderWidth = trackBorderWidth
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultSwitchColors() = SwitchColors(
|
||||
thumbColor = Color.White,
|
||||
trackInactive = LocalColors.current.themeTertiary,
|
||||
trackActive = LocalColors.current.themePrimary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultSwitchStyle() = SwitchStyle(
|
||||
padding = ComponentPadding(horizontal = DefaultSwitchPadding),
|
||||
contentSpacing = LocalSizes.current.spacingSecondary,
|
||||
thumbRadius = DefaultThumbRadius,
|
||||
thumbGain = DefaultThumbGain,
|
||||
thumbShadowSize = DefaultThumbShadowSize,
|
||||
thumbShape = LocalShapes.current.tertiary,
|
||||
trackShape = LocalShapes.current.tertiary,
|
||||
thumbBorder = defaultSwitchBorder(),
|
||||
trackBorder = defaultSwitchBorder(),
|
||||
trackWidth = DefaultTrackWidth,
|
||||
trackHeight = DefaultTrackHeight
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultSwitchBorder() = BorderStroke(LocalSizes.current.borderSizeTertiary, LocalColors.current.textPrimary)
|
||||
|
||||
private val DefaultSwitchPadding = 3.5.dp
|
||||
|
||||
private val DefaultThumbRadius = 6.5.dp
|
||||
private const val DefaultThumbGain = 1.1f
|
||||
private val DefaultThumbShadowSize = 0.5.dp
|
||||
|
||||
private val DefaultTrackWidth = 40.dp
|
||||
private val DefaultTrackHeight = 20.dp
|
||||
/**
|
||||
* Properties for [Switch].
|
||||
*/
|
||||
@Stable
|
||||
internal object SwitchProperties {
|
||||
val ThumbColor = Color.White
|
||||
val ThumbBorderColor = ColorsDescriptor.TextPrimary
|
||||
val TrackBorderColor = ColorsDescriptor.TextPrimary
|
||||
val TrackInactiveColor = ColorsDescriptor.ThemeTertiary
|
||||
val TrackActiveColor = ColorsDescriptor.ThemePrimary
|
||||
val Padding = ComponentPadding(horizontal = 3.5.dp)
|
||||
val ContentSpacing = SizesDescriptor.SpacingSecondary
|
||||
val ThumbRadius = 6.5.dp
|
||||
const val ThumbGain = 1.1f
|
||||
val ThumbShadowSize = 0.5.dp
|
||||
val ThumbShape = ShapesDescriptor.Tertiary
|
||||
val TrackShape = ShapesDescriptor.Tertiary
|
||||
val TrackWidth = 40.dp
|
||||
val TrackHeight = 20.dp
|
||||
val ThumbBorderWidth = SizesDescriptor.BorderSizeTertiary
|
||||
val TrackBorderWidth = SizesDescriptor.BorderSizeTertiary
|
||||
}
|
@@ -33,7 +33,6 @@ import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -48,7 +47,6 @@ import androidx.compose.foundation.selection.selectableGroup
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -73,18 +71,19 @@ import androidx.compose.ui.unit.lerp
|
||||
import com.highcapable.betterandroid.compose.extension.ui.ComponentPadding
|
||||
import com.highcapable.betterandroid.compose.extension.ui.componentState
|
||||
import com.highcapable.betterandroid.compose.extension.ui.orNull
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalShapes
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.PaddingDescriptor
|
||||
import com.highcapable.flexiui.ShapesDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.component.interaction.rippleClickable
|
||||
import com.highcapable.flexiui.toColor
|
||||
import com.highcapable.flexiui.toShape
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Colors defines for tab.
|
||||
* @param indicatorColor the indicator color.
|
||||
* @param selectedContentColor the selected content color.
|
||||
* @param unselectedContentColor the unselected content color.
|
||||
* @see TabDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class TabColors(
|
||||
@@ -95,11 +94,7 @@ data class TabColors(
|
||||
|
||||
/**
|
||||
* Style defines for tab.
|
||||
* @param contentPadding the content padding.
|
||||
* @param contentShape the content shape.
|
||||
* @param indicatorWidth the indicator width.
|
||||
* @param indicatorHeight the indicator height.
|
||||
* @param indicatorShape the indicator shape.
|
||||
* @see TabDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class TabStyle(
|
||||
@@ -125,8 +120,8 @@ data class TabStyle(
|
||||
fun TabRow(
|
||||
selectedTabIndex: Int = 0,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: TabColors = TabDefaults.colors,
|
||||
style: TabStyle = TabDefaults.style,
|
||||
colors: TabColors = TabDefaults.colors(),
|
||||
style: TabStyle = TabDefaults.style(),
|
||||
indicator: @Composable TabRowScope.() -> Unit = { TabIndicator(modifier = Modifier.tabIndicatorOffset()) },
|
||||
tabs: @Composable () -> Unit
|
||||
) {
|
||||
@@ -177,8 +172,8 @@ fun TabRow(
|
||||
fun ScrollableTabRow(
|
||||
selectedTabIndex: Int = 0,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: TabColors = TabDefaults.colors,
|
||||
style: TabStyle = TabDefaults.style,
|
||||
colors: TabColors = TabDefaults.colors(),
|
||||
style: TabStyle = TabDefaults.style(),
|
||||
scrollState: ScrollState = rememberScrollState(),
|
||||
indicator: @Composable TabRowScope.() -> Unit = { TabIndicator(modifier = Modifier.tabIndicatorOffset()) },
|
||||
tabs: @Composable () -> Unit
|
||||
@@ -249,18 +244,18 @@ fun Tab(
|
||||
modifier: Modifier = Modifier,
|
||||
selectedContentColor: Color = Color.Unspecified,
|
||||
unselectedContentColor: Color = Color.Unspecified,
|
||||
contentPadding: PaddingValues? = null,
|
||||
contentPadding: ComponentPadding? = null,
|
||||
contentShape: Shape? = null,
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable RowScope.() -> Unit
|
||||
) {
|
||||
val currentSelectedContentColor = selectedContentColor.orNull()
|
||||
?: LocalTabSelectedContentColor.current.orNull() ?: TabDefaults.colors.selectedContentColor
|
||||
?: LocalTabSelectedContentColor.current.orNull() ?: TabDefaults.colors().selectedContentColor
|
||||
val currentUnselectedContentColor = unselectedContentColor.orNull()
|
||||
?: LocalTabUnselectedContentColor.current.orNull() ?: TabDefaults.colors.unselectedContentColor
|
||||
val currentContentPadding = contentPadding ?: LocalTabContentPadding.current ?: TabDefaults.style.contentPadding
|
||||
val currentContentShape = contentShape ?: LocalTabContentShape.current ?: TabDefaults.style.contentShape
|
||||
?: LocalTabUnselectedContentColor.current.orNull() ?: TabDefaults.colors().unselectedContentColor
|
||||
val currentContentPadding = contentPadding ?: LocalTabContentPadding.current ?: TabDefaults.style().contentPadding
|
||||
val currentContentShape = contentShape ?: LocalTabContentShape.current ?: TabDefaults.style().contentShape
|
||||
val contentColor by animateColorAsState(if (selected) currentSelectedContentColor else currentUnselectedContentColor)
|
||||
val contentIconStyle = LocalIconStyle.current.copy(tint = contentColor)
|
||||
val contentTextStyle = LocalTextStyle.current.copy(color = contentColor)
|
||||
@@ -508,45 +503,68 @@ private enum class TabSlots { Tabs, TabsAverage, Indicator }
|
||||
* Defaults of tab.
|
||||
*/
|
||||
object TabDefaults {
|
||||
val colors: TabColors
|
||||
|
||||
/**
|
||||
* Creates a [TabColors] with the default values.
|
||||
* @param indicatorColor the indicator color.
|
||||
* @param selectedContentColor the selected content color.
|
||||
* @param unselectedContentColor the unselected content color.
|
||||
* @return [TabColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultTabColors()
|
||||
val style: TabStyle
|
||||
fun colors(
|
||||
indicatorColor: Color = TabProperties.IndicatorColor.toColor(),
|
||||
selectedContentColor: Color = TabProperties.SelectedContentColor.toColor(),
|
||||
unselectedContentColor: Color = TabProperties.UnselectedContentColor.toColor()
|
||||
) = TabColors(
|
||||
indicatorColor = indicatorColor,
|
||||
selectedContentColor = selectedContentColor,
|
||||
unselectedContentColor = unselectedContentColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [TabStyle] with the default values.
|
||||
* @param contentPadding the content padding.
|
||||
* @param contentShape the content shape.
|
||||
* @param indicatorWidth the indicator width.
|
||||
* @param indicatorHeight the indicator height.
|
||||
* @param indicatorShape the indicator shape.
|
||||
* @return [TabStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultTabStyle()
|
||||
fun style(
|
||||
contentPadding: ComponentPadding = TabProperties.ContentPadding.toPadding(),
|
||||
contentShape: Shape = AreaBoxDefaults.childShape(),
|
||||
indicatorWidth: Dp = TabProperties.IndicatorWidth,
|
||||
indicatorHeight: Dp = TabProperties.IndicatorHeight,
|
||||
indicatorShape: Shape = TabProperties.IndicatorShape.toShape()
|
||||
) = TabStyle(
|
||||
contentPadding = contentPadding,
|
||||
contentShape = contentShape,
|
||||
indicatorWidth = indicatorWidth,
|
||||
indicatorHeight = indicatorHeight,
|
||||
indicatorShape = indicatorShape
|
||||
)
|
||||
}
|
||||
|
||||
@Stable
|
||||
internal object TabProperties {
|
||||
val IndicatorColor = ColorsDescriptor.ThemePrimary
|
||||
val SelectedContentColor = ColorsDescriptor.ThemePrimary
|
||||
val UnselectedContentColor = ColorsDescriptor.TextSecondary
|
||||
val ContentPadding = PaddingDescriptor(
|
||||
horizontal = SizesDescriptor.SpacingPrimary,
|
||||
vertical = SizesDescriptor.SpacingSecondary
|
||||
)
|
||||
val IndicatorWidth = Dp.Unspecified
|
||||
val IndicatorHeight = 3.dp
|
||||
val IndicatorShape = ShapesDescriptor.Tertiary
|
||||
}
|
||||
|
||||
private val LocalTabSelectedContentColor = compositionLocalOf { Color.Unspecified }
|
||||
|
||||
private val LocalTabUnselectedContentColor = compositionLocalOf { Color.Unspecified }
|
||||
|
||||
private val LocalTabContentPadding = compositionLocalOf<PaddingValues?> { null }
|
||||
|
||||
private val LocalTabContentPadding = compositionLocalOf<ComponentPadding?> { null }
|
||||
private val LocalTabContentShape = compositionLocalOf<Shape?> { null }
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultTabColors() = TabColors(
|
||||
indicatorColor = LocalColors.current.themePrimary,
|
||||
selectedContentColor = LocalColors.current.themePrimary,
|
||||
unselectedContentColor = LocalColors.current.textSecondary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultTabStyle() = TabStyle(
|
||||
contentPadding = ComponentPadding(
|
||||
horizontal = LocalSizes.current.spacingPrimary,
|
||||
vertical = LocalSizes.current.spacingSecondary
|
||||
),
|
||||
contentShape = withAreaBoxShape(),
|
||||
indicatorWidth = Dp.Unspecified,
|
||||
indicatorHeight = DefaultTabIndicatorHeight,
|
||||
indicatorShape = LocalShapes.current.tertiary
|
||||
)
|
||||
|
||||
private val DefaultTabIndicatorHeight = 3.dp
|
||||
|
||||
private const val TabIndicatorDuration = 250
|
@@ -27,7 +27,7 @@ import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.foundation.text.InlineTextContent
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -36,8 +36,9 @@ import androidx.compose.ui.text.TextLayoutResult
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.highcapable.betterandroid.compose.extension.ui.orNull
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.DefaultTypography
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.toColor
|
||||
|
||||
/**
|
||||
* Flexi UI basic text.
|
||||
@@ -110,7 +111,7 @@ fun Text(
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {}
|
||||
) {
|
||||
val currentStyle = style ?: LocalTextStyle.current
|
||||
val currentColor = color.orNull() ?: currentStyle.color.orNull() ?: defaultTextColor()
|
||||
val currentColor = color.orNull() ?: currentStyle.color.orNull() ?: TextProperties.Color.toColor()
|
||||
BasicText(
|
||||
text = text,
|
||||
modifier = modifier,
|
||||
@@ -124,9 +125,14 @@ fun Text(
|
||||
)
|
||||
}
|
||||
|
||||
@Stable
|
||||
internal object TextProperties {
|
||||
val Color = ColorsDescriptor.TextPrimary
|
||||
}
|
||||
|
||||
/**
|
||||
* CompositionLocal containing the preferred [TextStyle]
|
||||
* that will be used by text by default.
|
||||
* Composition local containing the preferred [TextStyle]
|
||||
* that will be used by [Text] by default.
|
||||
*/
|
||||
val LocalTextStyle = compositionLocalOf { DefaultTextStyle }
|
||||
|
||||
@@ -144,8 +150,4 @@ fun ProvideTextStyle(value: TextStyle, content: @Composable () -> Unit) {
|
||||
CompositionLocalProvider(LocalTextStyle provides mergedStyle, content = content)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
internal fun defaultTextColor() = LocalColors.current.textPrimary
|
||||
|
||||
private val DefaultTextStyle = DefaultTypography.primary
|
@@ -26,7 +26,6 @@ package com.highcapable.flexiui.component
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.focusable
|
||||
import androidx.compose.foundation.hoverable
|
||||
@@ -50,7 +49,6 @@ import androidx.compose.foundation.text.selection.TextSelectionColors
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -89,26 +87,19 @@ import com.highcapable.betterandroid.compose.extension.ui.ComponentPadding
|
||||
import com.highcapable.betterandroid.compose.extension.ui.borderOrElse
|
||||
import com.highcapable.betterandroid.compose.extension.ui.componentState
|
||||
import com.highcapable.betterandroid.compose.extension.ui.orNull
|
||||
import com.highcapable.betterandroid.compose.extension.ui.solidColor
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.PaddingDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.resources.FlexiIcons
|
||||
import com.highcapable.flexiui.resources.icon.Backspace
|
||||
import com.highcapable.flexiui.resources.icon.ViewerClose
|
||||
import com.highcapable.flexiui.resources.icon.ViewerOpen
|
||||
import com.highcapable.flexiui.toColor
|
||||
import com.highcapable.flexiui.toDp
|
||||
|
||||
/**
|
||||
* Colors defines for text field.
|
||||
* @param textColor the text color.
|
||||
* @param cursorColor the cursor color.
|
||||
* @param selectionColors the selection colors.
|
||||
* @param completionColors the completion colors.
|
||||
* @param placeholderContentColor the placeholder content color, usually for text color.
|
||||
* @param decorInactiveTint the decoration inactive tint.
|
||||
* @param decorActiveTint the decoration active tint.
|
||||
* @param borderInactiveColor the border inactive color.
|
||||
* @param borderActiveColor the border active color.
|
||||
* @param backgroundColor the background color.
|
||||
* @see TextFieldDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class TextFieldColors(
|
||||
@@ -124,8 +115,23 @@ data class TextFieldColors(
|
||||
val backgroundColor: Color
|
||||
)
|
||||
|
||||
/**
|
||||
* Style defines for text field.
|
||||
* @see TextFieldDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class TextFieldStyle(
|
||||
val textStyle: TextStyle,
|
||||
val completionStyle: DropdownMenuStyle,
|
||||
val padding: ComponentPadding,
|
||||
val shape: Shape,
|
||||
val borderInactiveWith: Dp,
|
||||
val borderActiveWith: Dp
|
||||
)
|
||||
|
||||
/**
|
||||
* Colors defines for auto complete box.
|
||||
* @see TextFieldDefaults.colors
|
||||
* @param highlightContentColor the highlight content color, usually for text color.
|
||||
* @param menuColors the dropdown menu colors.
|
||||
*/
|
||||
@@ -135,25 +141,6 @@ data class AutoCompleteBoxColors(
|
||||
val menuColors: DropdownMenuColors
|
||||
)
|
||||
|
||||
/**
|
||||
* Style defines for text field.
|
||||
* @param textStyle the text style.
|
||||
* @param padding the padding of content.
|
||||
* @param shape the shape.
|
||||
* @param borderInactive the inactive border stroke.
|
||||
* @param borderActive the active border stroke.
|
||||
* @param completionStyle the completion dropdown menu style.
|
||||
*/
|
||||
@Immutable
|
||||
data class TextFieldStyle(
|
||||
val textStyle: TextStyle,
|
||||
val padding: ComponentPadding,
|
||||
val shape: Shape,
|
||||
val borderInactive: BorderStroke,
|
||||
val borderActive: BorderStroke,
|
||||
val completionStyle: DropdownMenuStyle
|
||||
)
|
||||
|
||||
/**
|
||||
* Options defines for auto complete.
|
||||
* @param checkCase whether to check case, default is true.
|
||||
@@ -202,8 +189,8 @@ fun TextField(
|
||||
onValueChange: (TextFieldValue) -> Unit,
|
||||
completionValues: List<String> = emptyList(),
|
||||
modifier: Modifier = Modifier,
|
||||
colors: TextFieldColors = TextFieldDefaults.colors,
|
||||
style: TextFieldStyle = TextFieldDefaults.style,
|
||||
colors: TextFieldColors = TextFieldDefaults.colors(),
|
||||
style: TextFieldStyle = TextFieldDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
completionOptions: AutoCompleteOptions = AutoCompleteOptions(),
|
||||
@@ -228,24 +215,21 @@ fun TextField(
|
||||
else -> colors.decorInactiveTint
|
||||
})
|
||||
val animatedBorderColor by animateColorAsState(when {
|
||||
focused || hovered -> style.borderActive.solidColor
|
||||
else -> style.borderInactive.solidColor
|
||||
focused || hovered -> colors.borderActiveColor
|
||||
else -> colors.borderInactiveColor
|
||||
})
|
||||
val animatedBorderWidth by animateDpAsState(when {
|
||||
focused -> style.borderActive.width
|
||||
else -> style.borderInactive.width
|
||||
focused -> style.borderActiveWith
|
||||
else -> style.borderInactiveWith
|
||||
})
|
||||
val border = when {
|
||||
focused || hovered -> style.borderInactive
|
||||
else -> style.borderInactive
|
||||
}.copy(animatedBorderWidth, SolidColor(animatedBorderColor))
|
||||
val textColor = style.textStyle.color.orNull() ?: colors.textColor
|
||||
BoxWithConstraints(
|
||||
modifier = Modifier.textField(
|
||||
enabled = enabled,
|
||||
colors = colors,
|
||||
style = style,
|
||||
border = border,
|
||||
borderColor = animatedBorderColor,
|
||||
borderWidth = animatedBorderWidth,
|
||||
interactionSource = interactionSource,
|
||||
then = modifier
|
||||
).pointerHoverState(TextFieldPointerState.Text)
|
||||
@@ -351,8 +335,8 @@ fun TextField(
|
||||
onValueChange: (String) -> Unit,
|
||||
completionValues: List<String> = emptyList(),
|
||||
modifier: Modifier = Modifier,
|
||||
colors: TextFieldColors = TextFieldDefaults.colors,
|
||||
style: TextFieldStyle = TextFieldDefaults.style,
|
||||
colors: TextFieldColors = TextFieldDefaults.colors(),
|
||||
style: TextFieldStyle = TextFieldDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
completionOptions: AutoCompleteOptions = AutoCompleteOptions(),
|
||||
@@ -427,8 +411,8 @@ fun PasswordTextField(
|
||||
onValueChange: (TextFieldValue) -> Unit,
|
||||
defaultPasswordVisible: Boolean = false,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: TextFieldColors = TextFieldDefaults.colors,
|
||||
style: TextFieldStyle = TextFieldDefaults.style,
|
||||
colors: TextFieldColors = TextFieldDefaults.colors(),
|
||||
style: TextFieldStyle = TextFieldDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||
@@ -475,7 +459,7 @@ fun PasswordTextField(
|
||||
if (value.text.isEmpty() && animatedSize == 0.dp) passwordVisible = defaultPasswordVisible
|
||||
IconToggleButton(
|
||||
modifier = Modifier.size(animatedSize).pointerHoverState(TextFieldPointerState.Common),
|
||||
style = IconButtonDefaults.style.copy(padding = TextDecorIconPadding),
|
||||
style = IconButtonDefaults.style(padding = TextDecorIconPadding),
|
||||
checked = passwordVisible,
|
||||
onCheckedChange = {
|
||||
passwordVisible = it
|
||||
@@ -518,8 +502,8 @@ fun PasswordTextField(
|
||||
onValueChange: (String) -> Unit,
|
||||
defaultPasswordVisible: Boolean = false,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: TextFieldColors = TextFieldDefaults.colors,
|
||||
style: TextFieldStyle = TextFieldDefaults.style,
|
||||
colors: TextFieldColors = TextFieldDefaults.colors(),
|
||||
style: TextFieldStyle = TextFieldDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||
@@ -589,8 +573,8 @@ fun BackspaceTextField(
|
||||
onValueChange: (TextFieldValue) -> Unit,
|
||||
completionValues: List<String> = emptyList(),
|
||||
modifier: Modifier = Modifier,
|
||||
colors: TextFieldColors = TextFieldDefaults.colors,
|
||||
style: TextFieldStyle = TextFieldDefaults.style,
|
||||
colors: TextFieldColors = TextFieldDefaults.colors(),
|
||||
style: TextFieldStyle = TextFieldDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
completionOptions: AutoCompleteOptions = AutoCompleteOptions(),
|
||||
@@ -647,7 +631,7 @@ fun BackspaceTextField(
|
||||
focusRequester.requestFocus()
|
||||
},
|
||||
modifier = Modifier.width(animatedSize).pointerHoverState(TextFieldPointerState.Common),
|
||||
style = IconButtonDefaults.style.copy(padding = TextDecorIconPadding),
|
||||
style = IconButtonDefaults.style(padding = TextDecorIconPadding),
|
||||
enabled = enabled,
|
||||
interactionSource = cInteractionSource
|
||||
) { Icon(imageVector = FlexiIcons.Backspace) }
|
||||
@@ -688,8 +672,8 @@ fun BackspaceTextField(
|
||||
onValueChange: (String) -> Unit,
|
||||
completionValues: List<String> = emptyList(),
|
||||
modifier: Modifier = Modifier,
|
||||
colors: TextFieldColors = TextFieldDefaults.colors,
|
||||
style: TextFieldStyle = TextFieldDefaults.style,
|
||||
colors: TextFieldColors = TextFieldDefaults.colors(),
|
||||
style: TextFieldStyle = TextFieldDefaults.style(),
|
||||
enabled: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
completionOptions: AutoCompleteOptions = AutoCompleteOptions(),
|
||||
@@ -883,9 +867,10 @@ private fun TextFieldDecorationBox(
|
||||
|
||||
@Composable
|
||||
private fun TextFieldStyle(colors: TextFieldColors, content: @Composable () -> Unit) {
|
||||
CompositionLocalProvider(LocalTextSelectionColors provides colors.selectionColors) {
|
||||
content()
|
||||
}
|
||||
CompositionLocalProvider(
|
||||
LocalTextSelectionColors provides colors.selectionColors,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
internal expect fun Modifier.pointerHoverState(state: TextFieldPointerState): Modifier
|
||||
@@ -902,7 +887,8 @@ private fun Modifier.textField(
|
||||
enabled: Boolean,
|
||||
colors: TextFieldColors,
|
||||
style: TextFieldStyle,
|
||||
border: BorderStroke,
|
||||
borderColor: Color,
|
||||
borderWidth: Dp,
|
||||
interactionSource: MutableInteractionSource,
|
||||
then: Modifier
|
||||
) = composed(
|
||||
@@ -911,7 +897,8 @@ private fun Modifier.textField(
|
||||
properties["enabled"] = enabled
|
||||
properties["colors"] = colors
|
||||
properties["style"] = style
|
||||
properties["border"] = border
|
||||
properties["borderColor"] = borderColor
|
||||
properties["borderWidth"] = borderWidth
|
||||
}
|
||||
) {
|
||||
componentState(enabled)
|
||||
@@ -919,7 +906,7 @@ private fun Modifier.textField(
|
||||
.hoverable(interactionSource, enabled)
|
||||
.clip(style.shape)
|
||||
.background(colors.backgroundColor, style.shape)
|
||||
.borderOrElse(border, style.shape)
|
||||
.borderOrElse(borderWidth, borderColor, style.shape)
|
||||
.then(then)
|
||||
}
|
||||
|
||||
@@ -940,55 +927,97 @@ private fun Modifier.textFieldPadding(
|
||||
* Defaults of text field.
|
||||
*/
|
||||
object TextFieldDefaults {
|
||||
val colors: TextFieldColors
|
||||
|
||||
/**
|
||||
* Creates a [TextFieldColors] with the default values.
|
||||
* @param textColor the text color.
|
||||
* @param cursorColor the cursor color.
|
||||
* @param selectionColors the selection colors.
|
||||
* @param completionColors the completion colors.
|
||||
* @param placeholderContentColor the placeholder content color, usually for text color.
|
||||
* @param decorInactiveTint the decoration inactive tint.
|
||||
* @param decorActiveTint the decoration active tint.
|
||||
* @param borderInactiveColor the border inactive color.
|
||||
* @param borderActiveColor the border active color.
|
||||
* @param backgroundColor the background color.
|
||||
* @return [TextFieldColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultTextFieldColors()
|
||||
val style: TextFieldStyle
|
||||
fun colors(
|
||||
textColor: Color = TextFieldProperties.TextColor.toColor(),
|
||||
cursorColor: Color = TextFieldProperties.CursorColor.toColor(),
|
||||
selectionColors: TextSelectionColors = TextSelectionColors(
|
||||
handleColor = TextFieldProperties.SelectionHandleColor.toColor(),
|
||||
backgroundColor = TextFieldProperties.SelectionBackgroundColor.toColor()
|
||||
),
|
||||
completionColors: AutoCompleteBoxColors = AutoCompleteBoxColors(
|
||||
highlightContentColor = TextFieldProperties.CompletionHighlightContentColor.toColor(),
|
||||
menuColors = DropdownMenuDefaults.colors()
|
||||
),
|
||||
placeholderContentColor: Color = TextFieldProperties.PlaceholderContentColor.toColor(),
|
||||
decorInactiveTint: Color = TextFieldProperties.DecorInactiveTint.toColor(),
|
||||
decorActiveTint: Color = TextFieldProperties.DecorActiveTint.toColor(),
|
||||
borderInactiveColor: Color = TextFieldProperties.BorderInactiveColor.toColor(),
|
||||
borderActiveColor: Color = TextFieldProperties.BorderActiveColor.toColor(),
|
||||
backgroundColor: Color = TextFieldProperties.BackgroundColor
|
||||
) = TextFieldColors(
|
||||
textColor = textColor,
|
||||
cursorColor = cursorColor,
|
||||
selectionColors = selectionColors,
|
||||
completionColors = completionColors,
|
||||
placeholderContentColor = placeholderContentColor,
|
||||
decorInactiveTint = decorInactiveTint,
|
||||
decorActiveTint = decorActiveTint,
|
||||
borderInactiveColor = borderInactiveColor,
|
||||
borderActiveColor = borderActiveColor,
|
||||
backgroundColor = backgroundColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [TextFieldStyle] with the default values.
|
||||
* @param textStyle the text style.
|
||||
* @param completionStyle the completion style.
|
||||
* @param padding the padding of content.
|
||||
* @param shape the shape.
|
||||
* @param borderInactiveWith the inactive border width.
|
||||
* @param borderActiveWith the active border width.
|
||||
* @return [TextFieldStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultTextFieldStyle()
|
||||
fun style(
|
||||
textStyle: TextStyle = LocalTextStyle.current,
|
||||
completionStyle: DropdownMenuStyle = DropdownMenuDefaults.style(),
|
||||
padding: ComponentPadding = TextFieldProperties.Padding.toPadding(),
|
||||
shape: Shape = AreaBoxDefaults.childShape(),
|
||||
borderInactiveWith: Dp = TextFieldProperties.BorderInactiveWith.toDp(),
|
||||
borderActiveWith: Dp = TextFieldProperties.BorderActiveWith.toDp()
|
||||
) = TextFieldStyle(
|
||||
textStyle = textStyle,
|
||||
completionStyle = completionStyle,
|
||||
padding = padding,
|
||||
shape = shape,
|
||||
borderInactiveWith = borderInactiveWith,
|
||||
borderActiveWith = borderActiveWith
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultTextFieldColors() = TextFieldColors(
|
||||
textColor = defaultTextColor(),
|
||||
cursorColor = LocalColors.current.themePrimary,
|
||||
selectionColors = TextSelectionColors(
|
||||
handleColor = LocalColors.current.themePrimary,
|
||||
backgroundColor = LocalColors.current.themeSecondary
|
||||
),
|
||||
completionColors = AutoCompleteBoxColors(
|
||||
highlightContentColor = LocalColors.current.themePrimary,
|
||||
menuColors = DropdownMenuDefaults.colors
|
||||
),
|
||||
placeholderContentColor = LocalColors.current.textSecondary,
|
||||
decorInactiveTint = LocalColors.current.themeSecondary,
|
||||
decorActiveTint = LocalColors.current.themePrimary,
|
||||
borderInactiveColor = LocalColors.current.themeSecondary,
|
||||
borderActiveColor = LocalColors.current.themePrimary,
|
||||
backgroundColor = Color.Transparent
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultTextFieldStyle() = TextFieldStyle(
|
||||
textStyle = LocalTextStyle.current,
|
||||
padding = ComponentPadding(LocalSizes.current.spacingSecondary),
|
||||
shape = withAreaBoxShape(),
|
||||
borderInactive = defaultTextFieldInactiveBorder(),
|
||||
borderActive = defaultTextFieldActiveBorder(),
|
||||
completionStyle = DropdownMenuDefaults.style
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultTextFieldInactiveBorder() = BorderStroke(LocalSizes.current.borderSizeSecondary, LocalColors.current.themeSecondary)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultTextFieldActiveBorder() = BorderStroke(LocalSizes.current.borderSizePrimary, LocalColors.current.themePrimary)
|
||||
@Stable
|
||||
internal object TextFieldProperties {
|
||||
val TextColor = TextProperties.Color
|
||||
val CursorColor = ColorsDescriptor.ThemePrimary
|
||||
val SelectionHandleColor = ColorsDescriptor.ThemePrimary
|
||||
val SelectionBackgroundColor = ColorsDescriptor.ThemeSecondary
|
||||
val CompletionHighlightContentColor = ColorsDescriptor.ThemePrimary
|
||||
val PlaceholderContentColor = ColorsDescriptor.TextSecondary
|
||||
val DecorInactiveTint = ColorsDescriptor.ThemeSecondary
|
||||
val DecorActiveTint = ColorsDescriptor.ThemePrimary
|
||||
val BorderInactiveColor = ColorsDescriptor.ThemeSecondary
|
||||
val BorderActiveColor = ColorsDescriptor.ThemePrimary
|
||||
val BackgroundColor = Color.Transparent
|
||||
val Padding = PaddingDescriptor(SizesDescriptor.SpacingSecondary)
|
||||
val BorderInactiveWith = SizesDescriptor.BorderSizeSecondary
|
||||
val BorderActiveWith = SizesDescriptor.BorderSizePrimary
|
||||
}
|
||||
|
||||
private val TextDecorIconSize = 24.dp
|
||||
private val TextDecorIconPadding = ComponentPadding(2.dp)
|
@@ -19,7 +19,7 @@
|
||||
*
|
||||
* This file is created by fankes on 2023/11/10.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
@file:Suppress("unused", "ConstPropertyName")
|
||||
|
||||
package com.highcapable.flexiui.component.interaction
|
||||
|
||||
@@ -27,7 +27,7 @@ import androidx.compose.foundation.Indication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
@@ -39,14 +39,13 @@ import com.highcapable.betterandroid.compose.extension.ui.clickable
|
||||
import com.highcapable.betterandroid.compose.extension.ui.combinedClickable
|
||||
import com.highcapable.betterandroid.compose.extension.ui.selectable
|
||||
import com.highcapable.betterandroid.compose.extension.ui.toggleable
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.toColor
|
||||
import androidx.compose.material.ripple.rememberRipple as materialRememberRipple
|
||||
|
||||
/**
|
||||
* Style defines for ripple.
|
||||
* @param bounded whether the ripple is bounded.
|
||||
* @param radius the radius.
|
||||
* @param color the color.
|
||||
* @see InteractionDefaults.rippleStyle
|
||||
*/
|
||||
@Immutable
|
||||
data class RippleStyle(
|
||||
@@ -62,7 +61,7 @@ data class RippleStyle(
|
||||
* @return [Indication]
|
||||
*/
|
||||
@Composable
|
||||
fun rememberRipple(style: RippleStyle = InteractionDefaults.rippleStyle) =
|
||||
fun rememberRipple(style: RippleStyle = InteractionDefaults.rippleStyle()) =
|
||||
materialRememberRipple(style.bounded, style.radius, style.color)
|
||||
|
||||
/**
|
||||
@@ -95,7 +94,7 @@ fun Modifier.rippleClickable(
|
||||
properties["onClick"] = onClick
|
||||
}
|
||||
) {
|
||||
val currentRippleStyle = rippleStyle ?: InteractionDefaults.rippleStyle
|
||||
val currentRippleStyle = rippleStyle ?: InteractionDefaults.rippleStyle()
|
||||
val currentIndication = rememberRipple(currentRippleStyle)
|
||||
clickable(
|
||||
onClick = onClick,
|
||||
@@ -146,7 +145,7 @@ fun Modifier.rippleCombinedClickable(
|
||||
properties["onClick"] = onClick
|
||||
}
|
||||
) {
|
||||
val currentRippleStyle = rippleStyle ?: InteractionDefaults.rippleStyle
|
||||
val currentRippleStyle = rippleStyle ?: InteractionDefaults.rippleStyle()
|
||||
val currentIndication = rememberRipple(currentRippleStyle)
|
||||
combinedClickable(
|
||||
onClick = onClick,
|
||||
@@ -190,7 +189,7 @@ fun Modifier.rippleToggleable(
|
||||
properties["onValueChange"] = onValueChange
|
||||
}
|
||||
) {
|
||||
val currentRippleStyle = rippleStyle ?: InteractionDefaults.rippleStyle
|
||||
val currentRippleStyle = rippleStyle ?: InteractionDefaults.rippleStyle()
|
||||
val currentIndication = rememberRipple(currentRippleStyle)
|
||||
toggleable(
|
||||
value = value,
|
||||
@@ -231,7 +230,7 @@ fun Modifier.rippleSelectable(
|
||||
properties["onClick"] = onClick
|
||||
}
|
||||
) {
|
||||
val currentRippleStyle = rippleStyle ?: InteractionDefaults.rippleStyle
|
||||
val currentRippleStyle = rippleStyle ?: InteractionDefaults.rippleStyle()
|
||||
val currentIndication = rememberRipple(currentRippleStyle)
|
||||
selectable(
|
||||
selected = selected,
|
||||
@@ -247,22 +246,35 @@ fun Modifier.rippleSelectable(
|
||||
* Defaults of interaction.
|
||||
*/
|
||||
object InteractionDefaults {
|
||||
val rippleStyle: RippleStyle
|
||||
|
||||
/**
|
||||
* Creates a [RippleStyle] with the default values.
|
||||
* @param bounded whether the ripple is bounded.
|
||||
* @param radius the radius.
|
||||
* @param color the color.
|
||||
* @return [RippleStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalRippleStyle.current ?: defaultRippleStyle()
|
||||
fun rippleStyle(
|
||||
bounded: Boolean = InteractionProperties.RippleBounded,
|
||||
radius: Dp = InteractionProperties.RippleRadius,
|
||||
color: Color = InteractionProperties.RippleColor.toColor()
|
||||
) = RippleStyle(
|
||||
bounded = bounded,
|
||||
radius = radius,
|
||||
color = color
|
||||
)
|
||||
}
|
||||
|
||||
@Stable
|
||||
internal object InteractionProperties {
|
||||
const val RippleBounded = true
|
||||
val RippleRadius = Dp.Unspecified
|
||||
val RippleColor = ColorsDescriptor.ThemeSecondary
|
||||
}
|
||||
|
||||
/**
|
||||
* CompositionLocal containing the preferred [RippleStyle]
|
||||
* Composition local containing the preferred [RippleStyle]
|
||||
* that will be used by interaction by default.
|
||||
*/
|
||||
val LocalRippleStyle = compositionLocalOf<RippleStyle?> { null }
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultRippleStyle() = RippleStyle(
|
||||
bounded = true,
|
||||
radius = Dp.Unspecified,
|
||||
color = LocalColors.current.themeSecondary
|
||||
)
|
@@ -41,12 +41,13 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.Dp
|
||||
@@ -55,11 +56,14 @@ import com.highcapable.betterandroid.compose.extension.ui.ComponentPadding
|
||||
import com.highcapable.betterandroid.compose.extension.ui.layout.AdaptiveRow
|
||||
import com.highcapable.betterandroid.compose.extension.ui.window.Dialog
|
||||
import com.highcapable.betterandroid.compose.extension.ui.window.DialogPropertiesWrapper
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.LocalTypography
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.PaddingDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.TypographyDescriptor
|
||||
import com.highcapable.flexiui.component.AreaBox
|
||||
import com.highcapable.flexiui.component.AreaBoxColors
|
||||
import com.highcapable.flexiui.component.AreaBoxDefaults
|
||||
import com.highcapable.flexiui.component.AreaBoxProperties
|
||||
import com.highcapable.flexiui.component.AreaBoxStyle
|
||||
import com.highcapable.flexiui.component.Button
|
||||
import com.highcapable.flexiui.component.Icon
|
||||
@@ -69,15 +73,19 @@ import com.highcapable.flexiui.component.LocalTextStyle
|
||||
import com.highcapable.flexiui.component.Text
|
||||
import com.highcapable.flexiui.platform.ActualPlatform
|
||||
import com.highcapable.flexiui.platform.Platform
|
||||
import com.highcapable.flexiui.toColor
|
||||
import com.highcapable.flexiui.toDp
|
||||
import com.highcapable.flexiui.toShape
|
||||
import com.highcapable.flexiui.toTextStyle
|
||||
|
||||
/**
|
||||
* Colors defines for flexi dialog.
|
||||
* @param titleTextColor the title text color.
|
||||
* @param titleIconTint the title icon tint.
|
||||
* @param contentTextColor the content text color.
|
||||
* @see FlexiDialogDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class FlexiDialogColors(
|
||||
val backgroundColor: Color,
|
||||
val borderColor: Color,
|
||||
val titleTextColor: Color,
|
||||
val titleIconTint: Color,
|
||||
val contentTextColor: Color
|
||||
@@ -85,26 +93,21 @@ data class FlexiDialogColors(
|
||||
|
||||
/**
|
||||
* Style defines for flexi dialog.
|
||||
* @param boxStyle the style of area box.
|
||||
* @param titleTextStyle the title text style.
|
||||
* @param contentTextStyle the content text style.
|
||||
* @param maxWidth the dialog's max width.
|
||||
* @param maxHeight the dialog's max height.
|
||||
* @param outPadding the dialog's out padding.
|
||||
* @param titlePadding the title padding.
|
||||
* @param contentPadding the content padding.
|
||||
* @param buttonsSpacing the spacing between buttons.
|
||||
* @see FlexiDialogDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class FlexiDialogStyle(
|
||||
val boxStyle: AreaBoxStyle,
|
||||
val titleTextStyle: TextStyle,
|
||||
val contentTextStyle: TextStyle,
|
||||
val maxWidth: Dp,
|
||||
val maxHeight: Dp,
|
||||
val outPadding: ComponentPadding,
|
||||
val padding: ComponentPadding,
|
||||
val insetsPadding: ComponentPadding,
|
||||
val titlePadding: ComponentPadding,
|
||||
val contentPadding: ComponentPadding,
|
||||
val shape: Shape,
|
||||
val borderWidth: Dp,
|
||||
val shadowSize: Dp,
|
||||
val buttonsSpacing: Dp
|
||||
)
|
||||
|
||||
@@ -130,8 +133,8 @@ fun FlexiDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
animated: Boolean = true,
|
||||
colors: FlexiDialogColors = FlexiDialogDefaults.colors,
|
||||
style: FlexiDialogStyle = FlexiDialogDefaults.style,
|
||||
colors: FlexiDialogColors = FlexiDialogDefaults.colors(),
|
||||
style: FlexiDialogStyle = FlexiDialogDefaults.style(),
|
||||
contentAlignment: Alignment = Alignment.TopStart,
|
||||
properties: DialogPropertiesWrapper = DefaultDialogProperties,
|
||||
title: (@Composable RowScope.() -> Unit)? = null,
|
||||
@@ -185,8 +188,8 @@ fun FlexiDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
animated: Boolean = true,
|
||||
scrimAnimated: Boolean = false,
|
||||
colors: FlexiDialogColors = FlexiDialogDefaults.colors,
|
||||
style: FlexiDialogStyle = FlexiDialogDefaults.style,
|
||||
colors: FlexiDialogColors = FlexiDialogDefaults.colors(),
|
||||
style: FlexiDialogStyle = FlexiDialogDefaults.style(),
|
||||
contentAlignment: Alignment = Alignment.TopStart,
|
||||
properties: DialogPropertiesWrapper = DefaultDialogProperties,
|
||||
title: (@Composable RowScope.() -> Unit)? = null,
|
||||
@@ -248,10 +251,20 @@ fun FlexiDialog(
|
||||
BasicFlexiDialog(
|
||||
visible = visible,
|
||||
onDismissRequest = onDismissRequest,
|
||||
modifier = Modifier.padding(style.outPadding).then(modifier),
|
||||
modifier = modifier,
|
||||
animated = animated,
|
||||
scrimAnimated = scrimAnimated,
|
||||
boxStyle = style.boxStyle,
|
||||
boxColors = AreaBoxDefaults.colors(
|
||||
backgroundColor = colors.backgroundColor,
|
||||
borderColor = colors.borderColor
|
||||
),
|
||||
boxStyle = AreaBoxDefaults.style(
|
||||
padding = style.padding,
|
||||
shape = style.shape,
|
||||
borderWidth = style.borderWidth,
|
||||
shadowSize = style.shadowSize
|
||||
),
|
||||
insetsPadding = style.insetsPadding,
|
||||
maxWidth = style.maxWidth,
|
||||
maxHeight = style.maxHeight,
|
||||
contentAlignment = contentAlignment,
|
||||
@@ -276,7 +289,9 @@ private fun BasicFlexiDialog(
|
||||
modifier: Modifier,
|
||||
animated: Boolean,
|
||||
scrimAnimated: Boolean,
|
||||
boxColors: AreaBoxColors,
|
||||
boxStyle: AreaBoxStyle,
|
||||
insetsPadding: ComponentPadding,
|
||||
maxWidth: Dp,
|
||||
maxHeight: Dp,
|
||||
contentAlignment: Alignment,
|
||||
@@ -305,17 +320,17 @@ private fun BasicFlexiDialog(
|
||||
properties = propertiesCopy,
|
||||
onDismissRequest = onDismissRequest
|
||||
) {
|
||||
Box(
|
||||
modifier = if (animated)
|
||||
val sModifier = if (animated)
|
||||
Modifier.graphicsLayer {
|
||||
scaleX = animatedScale
|
||||
scaleY = animatedScale
|
||||
alpha = animatedAlpha
|
||||
}.then(modifier)
|
||||
else Modifier.then(modifier)
|
||||
) {
|
||||
else modifier
|
||||
Box(modifier = Modifier.padding(insetsPadding).then(sModifier)) {
|
||||
AreaBox(
|
||||
modifier = Modifier.widthIn(max = maxWidth).heightIn(max = maxHeight),
|
||||
colors = boxColors,
|
||||
style = boxStyle,
|
||||
contentAlignment = contentAlignment
|
||||
) { content() }
|
||||
@@ -327,45 +342,101 @@ private fun BasicFlexiDialog(
|
||||
* Defaults of flexi dialog.
|
||||
*/
|
||||
object FlexiDialogDefaults {
|
||||
val colors: FlexiDialogColors
|
||||
|
||||
/**
|
||||
* Creates a [FlexiDialogColors] with the default values.
|
||||
* @param backgroundColor the background color.
|
||||
* @param borderColor the border color.
|
||||
* @param titleTextColor the title text color.
|
||||
* @param titleIconTint the title icon tint.
|
||||
* @param contentTextColor the content text color.
|
||||
* @return [FlexiDialogColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultFlexiDialogColors()
|
||||
val style: FlexiDialogStyle
|
||||
fun colors(
|
||||
backgroundColor: Color = FlexiDialogProperties.BackgroundColor.toColor(),
|
||||
borderColor: Color = FlexiDialogProperties.BorderColor.toColor(),
|
||||
titleTextColor: Color = FlexiDialogProperties.TitleTextColor.toColor(),
|
||||
titleIconTint: Color = FlexiDialogProperties.TitleIconTint.toColor(),
|
||||
contentTextColor: Color = FlexiDialogProperties.ContentTextColor.toColor()
|
||||
) = FlexiDialogColors(
|
||||
backgroundColor = backgroundColor,
|
||||
borderColor = borderColor,
|
||||
titleTextColor = titleTextColor,
|
||||
titleIconTint = titleIconTint,
|
||||
contentTextColor = contentTextColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [FlexiDialogStyle] with the default values.
|
||||
* @param titleTextStyle the title text style.
|
||||
* @param contentTextStyle the content text style.
|
||||
* @param maxWidth the dialog's max width.
|
||||
* @param maxHeight the dialog's max height.
|
||||
* @param padding the dialog's padding.
|
||||
* @param insetsPadding the dialog's insets padding.
|
||||
* @param titlePadding the title padding.
|
||||
* @param contentPadding the content padding.
|
||||
* @param shape the dialog's shape.
|
||||
* @param borderWidth the dialog's border width.
|
||||
* @param shadowSize the dialog's shadow size.
|
||||
* @param buttonsSpacing the spacing between buttons.
|
||||
* @return [FlexiDialogStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultFlexiDialogStyle()
|
||||
fun style(
|
||||
titleTextStyle: TextStyle = FlexiDialogProperties.TitleTextStyle.toTextStyle(),
|
||||
contentTextStyle: TextStyle = FlexiDialogProperties.ContentTextStyle.toTextStyle(),
|
||||
maxWidth: Dp = FlexiDialogProperties.MaxWidth,
|
||||
maxHeight: Dp = FlexiDialogProperties.MaxHeight,
|
||||
padding: ComponentPadding = FlexiDialogProperties.Padding.toPadding(),
|
||||
insetsPadding: ComponentPadding = FlexiDialogProperties.InsetsPadding,
|
||||
titlePadding: ComponentPadding = FlexiDialogProperties.TitlePadding.toPadding(),
|
||||
contentPadding: ComponentPadding = FlexiDialogProperties.ContentPadding.toPadding(),
|
||||
shape: Shape = FlexiDialogProperties.Shape.toShape(),
|
||||
borderWidth: Dp = FlexiDialogProperties.BorderWidth.toDp(),
|
||||
shadowSize: Dp = FlexiDialogProperties.ShadowSize,
|
||||
buttonsSpacing: Dp = FlexiDialogProperties.ButtonsSpacing.toDp()
|
||||
) = FlexiDialogStyle(
|
||||
titleTextStyle = titleTextStyle,
|
||||
contentTextStyle = contentTextStyle,
|
||||
maxWidth = maxWidth,
|
||||
maxHeight = maxHeight,
|
||||
padding = padding,
|
||||
insetsPadding = insetsPadding,
|
||||
titlePadding = titlePadding,
|
||||
contentPadding = contentPadding,
|
||||
shape = shape,
|
||||
borderWidth = borderWidth,
|
||||
shadowSize = shadowSize,
|
||||
buttonsSpacing = buttonsSpacing
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultFlexiDialogColors() = FlexiDialogColors(
|
||||
titleTextColor = LocalColors.current.textPrimary,
|
||||
titleIconTint = LocalColors.current.textPrimary,
|
||||
contentTextColor = LocalColors.current.textSecondary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultFlexiDialogStyle() = FlexiDialogStyle(
|
||||
boxStyle = AreaBoxDefaults.style.copy(padding = ComponentPadding(LocalSizes.current.spacingSecondary)),
|
||||
titleTextStyle = LocalTypography.current.titleSecondary,
|
||||
contentTextStyle = LocalTypography.current.primary,
|
||||
maxWidth = DefaultMaxWidth,
|
||||
maxHeight = DefaultMaxHeight,
|
||||
outPadding = ComponentPadding(horizontal = DefaultHorizontalOutPadding),
|
||||
titlePadding = ComponentPadding(LocalSizes.current.spacingSecondary),
|
||||
contentPadding = ComponentPadding(LocalSizes.current.spacingSecondary),
|
||||
buttonsSpacing = LocalSizes.current.spacingSecondary
|
||||
)
|
||||
@Stable
|
||||
internal object FlexiDialogProperties {
|
||||
val BackgroundColor = AreaBoxProperties.BackgroundColor
|
||||
val BorderColor = AreaBoxProperties.BorderColor
|
||||
val TitleTextColor = ColorsDescriptor.TextPrimary
|
||||
val TitleIconTint = ColorsDescriptor.TextPrimary
|
||||
val ContentTextColor = ColorsDescriptor.TextSecondary
|
||||
val TitleTextStyle = TypographyDescriptor.TitleSecondary
|
||||
val ContentTextStyle = TypographyDescriptor.Primary
|
||||
val MaxWidth = 300.dp
|
||||
val MaxHeight = 500.dp
|
||||
val Padding = PaddingDescriptor(SizesDescriptor.SpacingSecondary)
|
||||
val InsetsPadding = ComponentPadding(horizontal = 50.dp)
|
||||
val TitlePadding = PaddingDescriptor(SizesDescriptor.SpacingSecondary)
|
||||
val ContentPadding = PaddingDescriptor(SizesDescriptor.SpacingSecondary)
|
||||
val Shape = AreaBoxProperties.Shape
|
||||
val BorderWidth = AreaBoxProperties.BorderWidth
|
||||
val ShadowSize = AreaBoxProperties.ShadowSize
|
||||
val ButtonsSpacing = SizesDescriptor.SpacingSecondary
|
||||
}
|
||||
|
||||
private const val AnimationDuration = 250
|
||||
private const val AnimationMinmumScale = 0.8f
|
||||
|
||||
private val DefaultMaxWidth = 300.dp
|
||||
private val DefaultMaxHeight = 500.dp
|
||||
private val DefaultHorizontalOutPadding = 50.dp
|
||||
|
||||
private const val DefaultScrimOpacity = 0.35f
|
||||
private val DefaultScrimColor = Color.Black.copy(alpha = DefaultScrimOpacity)
|
||||
private val DefaultDialogProperties = DialogPropertiesWrapper(usePlatformDefaultWidth = false, scrimColor = DefaultScrimColor)
|
||||
|
@@ -26,25 +26,19 @@ package com.highcapable.flexiui
|
||||
import androidx.compose.foundation.LocalContextMenuRepresentation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import com.highcapable.flexiui.component.DesktopContextMenuDefaults
|
||||
import com.highcapable.flexiui.component.DesktopContextMenuRepresentation
|
||||
import com.highcapable.flexiui.component.ContextMenuDefaults
|
||||
import com.highcapable.flexiui.component.FlexiContextMenuRepresentation
|
||||
import com.highcapable.flexiui.component.LocalContextMenuColors
|
||||
import com.highcapable.flexiui.component.LocalContextMenuStyle
|
||||
import com.highcapable.flexiui.component.defaultContextMenuColors
|
||||
import com.highcapable.flexiui.component.defaultContextMenuStyle
|
||||
|
||||
@Composable
|
||||
internal actual fun FlexiThemeContent(content: @Composable () -> Unit) {
|
||||
CompositionLocalProvider(
|
||||
LocalContextMenuColors provides defaultContextMenuColors(),
|
||||
LocalContextMenuStyle provides defaultContextMenuStyle()
|
||||
LocalContextMenuColors provides ContextMenuDefaults.colors(),
|
||||
LocalContextMenuStyle provides ContextMenuDefaults.style(),
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalContextMenuRepresentation provides
|
||||
DesktopContextMenuRepresentation(
|
||||
colors = DesktopContextMenuDefaults.colors,
|
||||
style = DesktopContextMenuDefaults.style
|
||||
),
|
||||
LocalContextMenuRepresentation provides FlexiContextMenuRepresentation(),
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
@@ -35,7 +35,7 @@ import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -46,6 +46,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusDirection
|
||||
import androidx.compose.ui.focus.FocusManager
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.input.InputMode
|
||||
import androidx.compose.ui.input.InputModeManager
|
||||
import androidx.compose.ui.input.key.KeyEventType
|
||||
@@ -56,22 +57,25 @@ import androidx.compose.ui.input.pointer.PointerEventType
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalInputModeManager
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Popup
|
||||
import androidx.compose.ui.window.PopupProperties
|
||||
import androidx.compose.ui.window.rememberPopupPositionProviderAtPosition
|
||||
import com.highcapable.betterandroid.compose.extension.ui.ComponentPadding
|
||||
import com.highcapable.betterandroid.compose.extension.ui.orNull
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalShapes
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.ColorsDescriptor
|
||||
import com.highcapable.flexiui.PaddingDescriptor
|
||||
import com.highcapable.flexiui.ShapesDescriptor
|
||||
import com.highcapable.flexiui.SizesDescriptor
|
||||
import com.highcapable.flexiui.component.interaction.rippleClickable
|
||||
import com.highcapable.flexiui.toColor
|
||||
import com.highcapable.flexiui.toDp
|
||||
import com.highcapable.flexiui.toShape
|
||||
import java.awt.event.KeyEvent
|
||||
|
||||
/**
|
||||
* Colors defines for the context menu.
|
||||
* @param contentColor the content color, usually for the text color.
|
||||
* @param borderColor the border color.
|
||||
* @see ContextMenuDefaults.colors
|
||||
*/
|
||||
@Immutable
|
||||
data class ContextMenuColors(
|
||||
@@ -81,26 +85,26 @@ data class ContextMenuColors(
|
||||
|
||||
/**
|
||||
* Style defines for the context menu.
|
||||
* @param contentStyle the content style of area box.
|
||||
* @param borderStyle the border style of area box.
|
||||
* @see ContextMenuDefaults.style
|
||||
*/
|
||||
@Immutable
|
||||
data class ContextMenuStyle(
|
||||
val contentStyle: AreaBoxStyle?,
|
||||
val borderStyle: AreaBoxStyle?
|
||||
val padding: ComponentPadding,
|
||||
val shape: Shape,
|
||||
val borderWidth: Dp,
|
||||
val contentPadding: ComponentPadding,
|
||||
val contentShape: Shape,
|
||||
val shadowSize: Dp
|
||||
)
|
||||
|
||||
internal class DesktopContextMenuRepresentation(
|
||||
private val colors: ContextMenuColors,
|
||||
private val style: ContextMenuStyle
|
||||
) : ContextMenuRepresentation {
|
||||
internal class FlexiContextMenuRepresentation : ContextMenuRepresentation {
|
||||
|
||||
@Composable
|
||||
override fun Representation(state: ContextMenuState, items: () -> List<ContextMenuItem>) {
|
||||
val colors = LocalContextMenuColors.current ?: ContextMenuDefaults.colors()
|
||||
val style = LocalContextMenuStyle.current ?: ContextMenuDefaults.style()
|
||||
val status = state.status
|
||||
if (status is ContextMenuState.Status.Open) {
|
||||
val contentStyle = style.contentStyle ?: return
|
||||
val borderStyle = style.borderStyle ?: return
|
||||
var focusManager: FocusManager? by mutableStateOf(null)
|
||||
var inputModeManager: InputModeManager? by mutableStateOf(null)
|
||||
Popup(
|
||||
@@ -129,12 +133,20 @@ internal class DesktopContextMenuRepresentation(
|
||||
modifier = Modifier
|
||||
.width(IntrinsicSize.Max)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
color = colors.borderColor,
|
||||
style = borderStyle
|
||||
colors = AreaBoxDefaults.colors(backgroundColor = colors.borderColor),
|
||||
style = AreaBoxDefaults.style(
|
||||
padding = style.padding,
|
||||
shape = style.shape,
|
||||
borderWidth = style.borderWidth,
|
||||
shadowSize = style.shadowSize
|
||||
)
|
||||
) {
|
||||
items().forEach { item ->
|
||||
MenuItemContent(
|
||||
style = contentStyle,
|
||||
style = AreaBoxDefaults.style(
|
||||
padding = style.contentPadding,
|
||||
shape = style.contentShape
|
||||
),
|
||||
onClick = {
|
||||
state.status = ContextMenuState.Status.Closed
|
||||
item.onClick()
|
||||
@@ -164,7 +176,7 @@ private fun MenuItemContent(
|
||||
maxWidth = MenuContentMaxWidth,
|
||||
minHeight = MenuContentMinHeight
|
||||
),
|
||||
color = Color.Transparent,
|
||||
colors = AreaBoxDefaults.colors(backgroundColor = Color.Transparent),
|
||||
style = style,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) { content() }
|
||||
@@ -185,61 +197,74 @@ private fun Modifier.onHover(onHover: (Boolean) -> Unit) = pointerInput(Unit) {
|
||||
/**
|
||||
* Defaults of context menu.
|
||||
*/
|
||||
object DesktopContextMenuDefaults {
|
||||
val colors: ContextMenuColors
|
||||
object ContextMenuDefaults {
|
||||
|
||||
/**
|
||||
* Creates a [ContextMenuColors] with the default values.
|
||||
* @param contentColor the content color, usually for the text color.
|
||||
* @param borderColor the border color.
|
||||
* @return [ContextMenuColors]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalContextMenuColors.current
|
||||
val style: ContextMenuStyle
|
||||
fun colors(
|
||||
contentColor: Color = ContextMenuProperties.ContentColor.toColor(),
|
||||
borderColor: Color = ContextMenuProperties.BorderColor.toColor()
|
||||
) = ContextMenuColors(
|
||||
contentColor = contentColor,
|
||||
borderColor = borderColor
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [ContextMenuStyle] with the default values.
|
||||
* @param padding the menu padding.
|
||||
* @param shape the menu shape.
|
||||
* @param borderWidth the menu border width.
|
||||
* @param contentPadding the content padding.
|
||||
* @param contentShape the content shape.
|
||||
* @param shadowSize the shadow size.
|
||||
* @return [ContextMenuStyle]
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalContextMenuStyle.current
|
||||
fun style(
|
||||
padding: ComponentPadding = ContextMenuProperties.Padding.toPadding(),
|
||||
shape: Shape = ContextMenuProperties.Shape.toShape(),
|
||||
borderWidth: Dp = ContextMenuProperties.BorderWidth.toDp(),
|
||||
contentPadding: ComponentPadding = ContextMenuProperties.ContentPadding,
|
||||
contentShape: Shape = ContextMenuProperties.ContentShape.toShape(),
|
||||
shadowSize: Dp = ContextMenuProperties.ShadowSize.toDp()
|
||||
) = ContextMenuStyle(
|
||||
padding = padding,
|
||||
shape = shape,
|
||||
borderWidth = borderWidth,
|
||||
contentPadding = contentPadding,
|
||||
contentShape = contentShape,
|
||||
shadowSize = shadowSize
|
||||
)
|
||||
}
|
||||
|
||||
@Stable
|
||||
internal object ContextMenuProperties {
|
||||
val ContentColor = ColorsDescriptor.TextPrimary
|
||||
val BorderColor = ColorsDescriptor.BackgroundSecondary
|
||||
val Padding = PaddingDescriptor(SizesDescriptor.SpacingTertiary)
|
||||
val Shape = ShapesDescriptor.Primary
|
||||
val BorderWidth = AreaBoxProperties.BorderWidth
|
||||
val ContentPadding = ComponentPadding(horizontal = 16.dp)
|
||||
val ContentShape = ShapesDescriptor.Secondary
|
||||
val ShadowSize = SizesDescriptor.ZoomSizeTertiary
|
||||
}
|
||||
|
||||
/**
|
||||
* CompositionLocal containing the preferred [ContextMenuColors]
|
||||
* that will be used by context menu by default.
|
||||
* Composition local containing the preferred [ContextMenuColors]
|
||||
* that will be used by [ContextMenuRepresentation] by default.
|
||||
*/
|
||||
val LocalContextMenuColors = compositionLocalOf {
|
||||
ContextMenuColors(
|
||||
borderColor = Color.Unspecified,
|
||||
contentColor = Color.Unspecified
|
||||
)
|
||||
}
|
||||
val LocalContextMenuColors = compositionLocalOf<ContextMenuColors?> { null }
|
||||
|
||||
/**
|
||||
* CompositionLocal containing the preferred [ContextMenuStyle]
|
||||
* that will be used by context menu by default.
|
||||
* Composition local containing the preferred [ContextMenuStyle]
|
||||
* that will be used by [ContextMenuRepresentation] by default.
|
||||
*/
|
||||
val LocalContextMenuStyle = compositionLocalOf {
|
||||
ContextMenuStyle(
|
||||
contentStyle = null,
|
||||
borderStyle = null
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
internal fun defaultContextMenuColors() = ContextMenuColors(
|
||||
contentColor = LocalContextMenuColors.current.contentColor.orNull() ?: LocalColors.current.textPrimary,
|
||||
borderColor = LocalContextMenuColors.current.borderColor.orNull() ?: LocalColors.current.backgroundSecondary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
internal fun defaultContextMenuStyle() = ContextMenuStyle(
|
||||
contentStyle = LocalContextMenuStyle.current.contentStyle ?: AreaBoxDefaults.style.copy(
|
||||
padding = ComponentPadding(horizontal = DefaultMenuContentPadding),
|
||||
shape = LocalShapes.current.secondary
|
||||
),
|
||||
borderStyle = LocalContextMenuStyle.current.borderStyle ?: AreaBoxDefaults.style.copy(
|
||||
padding = ComponentPadding(LocalSizes.current.spacingTertiary),
|
||||
shadowSize = LocalSizes.current.zoomSizeTertiary,
|
||||
shape = LocalShapes.current.primary
|
||||
)
|
||||
)
|
||||
|
||||
private val DefaultMenuContentPadding = 16.dp
|
||||
val LocalContextMenuStyle = compositionLocalOf<ContextMenuStyle?> { null }
|
||||
|
||||
private val MenuContentMinWidth = 112.dp
|
||||
private val MenuContentMaxWidth = 280.dp
|
Reference in New Issue
Block a user