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