refactor: support code comments and some tweaks for all components

This commit is contained in:
2024-01-06 11:02:25 +08:00
parent 6e04a33fa7
commit 01574c28d3
30 changed files with 1431 additions and 232 deletions

View File

@@ -30,6 +30,10 @@ import androidx.compose.ui.platform.LocalContext
import com.highcapable.betterandroid.ui.extension.component.feature.SystemColors import com.highcapable.betterandroid.ui.extension.component.feature.SystemColors
import com.highcapable.betterandroid.ui.extension.graphics.mixColorOf import com.highcapable.betterandroid.ui.extension.graphics.mixColorOf
/**
* Whether dynamic color is available for current system.
* @return [Boolean]
*/
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
actual fun isDynamicColorAvailable() = SystemColors.isAvailable actual fun isDynamicColorAvailable() = SystemColors.isAvailable
@@ -46,8 +50,7 @@ internal actual val DynamicLightColors
themeSecondary = Color(DynamicColors.materialDynamicPrimary(60)).copy(alpha = 0.65f), themeSecondary = Color(DynamicColors.materialDynamicPrimary(60)).copy(alpha = 0.65f),
themeTertiary = Color(DynamicColors.materialDynamicPrimary(60)).copy(alpha = 0.15f), themeTertiary = Color(DynamicColors.materialDynamicPrimary(60)).copy(alpha = 0.15f),
textPrimary = DefaultLightColors.textPrimary, textPrimary = DefaultLightColors.textPrimary,
textSecondary = DefaultLightColors.textSecondary, textSecondary = DefaultLightColors.textSecondary
isLight = true
) else DefaultLightColors ) else DefaultLightColors
internal actual val DynamicDarkColors internal actual val DynamicDarkColors
@@ -62,8 +65,7 @@ internal actual val DynamicDarkColors
themeSecondary = Color(DynamicColors.materialDynamicSecondary(60)).copy(alpha = 0.65f), themeSecondary = Color(DynamicColors.materialDynamicSecondary(60)).copy(alpha = 0.65f),
themeTertiary = Color(DynamicColors.materialDynamicSecondary(60)).copy(alpha = 0.25f), themeTertiary = Color(DynamicColors.materialDynamicSecondary(60)).copy(alpha = 0.25f),
textPrimary = DefaultDarkColors.textPrimary, textPrimary = DefaultDarkColors.textPrimary,
textSecondary = DefaultDarkColors.textSecondary, textSecondary = DefaultDarkColors.textSecondary
isLight = false
) else DefaultDarkColors ) else DefaultDarkColors
internal actual val DynamicBlackColors internal actual val DynamicBlackColors
@@ -78,8 +80,7 @@ internal actual val DynamicBlackColors
themeSecondary = Color(DynamicColors.materialDynamicSecondary(60)).copy(alpha = 0.65f), themeSecondary = Color(DynamicColors.materialDynamicSecondary(60)).copy(alpha = 0.65f),
themeTertiary = Color(DynamicColors.materialDynamicSecondary(60)).copy(alpha = 0.27f), themeTertiary = Color(DynamicColors.materialDynamicSecondary(60)).copy(alpha = 0.27f),
textPrimary = DefaultBlackColors.textPrimary, textPrimary = DefaultBlackColors.textPrimary,
textSecondary = DefaultBlackColors.textSecondary, textSecondary = DefaultBlackColors.textSecondary
isLight = false
) else DefaultBlackColors ) else DefaultBlackColors
private val DynamicColors private val DynamicColors

View File

@@ -29,6 +29,9 @@ 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
/**
* Colors defines for Flexi UI.
*/
@Stable @Stable
data class Colors( data class Colors(
var backgroundPrimary: Color, var backgroundPrimary: Color,
@@ -39,8 +42,7 @@ data class Colors(
var themeSecondary: Color, var themeSecondary: Color,
var themeTertiary: Color, var themeTertiary: Color,
var textPrimary: Color, var textPrimary: Color,
var textSecondary: Color, var textSecondary: Color
var isLight: Boolean
) )
@get:Composable @get:Composable
@@ -64,8 +66,7 @@ internal val DefaultLightColors = Colors(
themeSecondary = Color(0xA6777777), themeSecondary = Color(0xA6777777),
themeTertiary = Color(0x27777777), themeTertiary = Color(0x27777777),
textPrimary = Color(0xFF323B42), textPrimary = Color(0xFF323B42),
textSecondary = Color(0xFF777777), textSecondary = Color(0xFF777777)
isLight = true
) )
internal val DefaultDarkColors = Colors( internal val DefaultDarkColors = Colors(
@@ -77,8 +78,7 @@ internal val DefaultDarkColors = Colors(
themeSecondary = Color(0xA6888888), themeSecondary = Color(0xA6888888),
themeTertiary = Color(0x40888888), themeTertiary = Color(0x40888888),
textPrimary = Color(0xFFE3E3E3), textPrimary = Color(0xFFE3E3E3),
textSecondary = Color(0xFFBBBBBB), textSecondary = Color(0xFFBBBBBB)
isLight = false
) )
internal val DefaultBlackColors = Colors( internal val DefaultBlackColors = Colors(
@@ -90,8 +90,7 @@ internal val DefaultBlackColors = Colors(
themeSecondary = Color(0xA65B5B5B), themeSecondary = Color(0xA65B5B5B),
themeTertiary = Color(0x455B5B5B), themeTertiary = Color(0x455B5B5B),
textPrimary = DefaultDarkColors.textPrimary, textPrimary = DefaultDarkColors.textPrimary,
textSecondary = DefaultDarkColors.textSecondary, textSecondary = DefaultDarkColors.textSecondary
isLight = false
) )
private val RedLightColors = Colors( private val RedLightColors = Colors(
@@ -103,8 +102,7 @@ private val RedLightColors = Colors(
themeSecondary = Color(0xA6FF5545), themeSecondary = Color(0xA6FF5545),
themeTertiary = Color(0x27FF5545), themeTertiary = Color(0x27FF5545),
textPrimary = DefaultLightColors.textPrimary, textPrimary = DefaultLightColors.textPrimary,
textSecondary = DefaultLightColors.textSecondary, textSecondary = DefaultLightColors.textSecondary
isLight = true
) )
private val PinkLightColors = Colors( private val PinkLightColors = Colors(
@@ -116,8 +114,7 @@ private val PinkLightColors = Colors(
themeSecondary = Color(0xA6FF4E7C), themeSecondary = Color(0xA6FF4E7C),
themeTertiary = Color(0x27FF4E7C), themeTertiary = Color(0x27FF4E7C),
textPrimary = DefaultLightColors.textPrimary, textPrimary = DefaultLightColors.textPrimary,
textSecondary = DefaultLightColors.textSecondary, textSecondary = DefaultLightColors.textSecondary
isLight = true
) )
private val PurpleLightColors = Colors( private val PurpleLightColors = Colors(
@@ -129,8 +126,7 @@ private val PurpleLightColors = Colors(
themeSecondary = Color(0xA6A476FF), themeSecondary = Color(0xA6A476FF),
themeTertiary = Color(0x27A476FF), themeTertiary = Color(0x27A476FF),
textPrimary = DefaultLightColors.textPrimary, textPrimary = DefaultLightColors.textPrimary,
textSecondary = DefaultLightColors.textSecondary, textSecondary = DefaultLightColors.textSecondary
isLight = true
) )
private val OrangeLightColors = Colors( private val OrangeLightColors = Colors(
@@ -142,8 +138,7 @@ private val OrangeLightColors = Colors(
themeSecondary = Color(0xA6D27C00), themeSecondary = Color(0xA6D27C00),
themeTertiary = Color(0x27D27C00), themeTertiary = Color(0x27D27C00),
textPrimary = DefaultLightColors.textPrimary, textPrimary = DefaultLightColors.textPrimary,
textSecondary = DefaultLightColors.textSecondary, textSecondary = DefaultLightColors.textSecondary
isLight = true
) )
private val YellowLightColors = Colors( private val YellowLightColors = Colors(
@@ -155,8 +150,7 @@ private val YellowLightColors = Colors(
themeSecondary = Color(0xA6BA8800), themeSecondary = Color(0xA6BA8800),
themeTertiary = Color(0x27BA8800), themeTertiary = Color(0x27BA8800),
textPrimary = DefaultLightColors.textPrimary, textPrimary = DefaultLightColors.textPrimary,
textSecondary = DefaultLightColors.textSecondary, textSecondary = DefaultLightColors.textSecondary
isLight = true
) )
private val GreenLightColors = Colors( private val GreenLightColors = Colors(
@@ -168,8 +162,7 @@ private val GreenLightColors = Colors(
themeSecondary = Color(0xA65B9E7A), themeSecondary = Color(0xA65B9E7A),
themeTertiary = Color(0x275B9E7A), themeTertiary = Color(0x275B9E7A),
textPrimary = DefaultLightColors.textPrimary, textPrimary = DefaultLightColors.textPrimary,
textSecondary = DefaultLightColors.textSecondary, textSecondary = DefaultLightColors.textSecondary
isLight = true
) )
private val BlueLightColors = Colors( private val BlueLightColors = Colors(
@@ -181,8 +174,7 @@ private val BlueLightColors = Colors(
themeSecondary = Color(0xA60099DF), themeSecondary = Color(0xA60099DF),
themeTertiary = Color(0x270099DF), themeTertiary = Color(0x270099DF),
textPrimary = DefaultLightColors.textPrimary, textPrimary = DefaultLightColors.textPrimary,
textSecondary = DefaultLightColors.textSecondary, textSecondary = DefaultLightColors.textSecondary
isLight = true
) )
private val RedDarkColors = Colors( private val RedDarkColors = Colors(
@@ -194,8 +186,7 @@ private val RedDarkColors = Colors(
themeSecondary = Color(0xA6B9856D), themeSecondary = Color(0xA6B9856D),
themeTertiary = Color(0x40B9856D), themeTertiary = Color(0x40B9856D),
textPrimary = DefaultDarkColors.textPrimary, textPrimary = DefaultDarkColors.textPrimary,
textSecondary = DefaultDarkColors.textSecondary, textSecondary = DefaultDarkColors.textSecondary
isLight = false
) )
private val PinkDarkColors = Colors( private val PinkDarkColors = Colors(
@@ -207,8 +198,7 @@ private val PinkDarkColors = Colors(
themeSecondary = Color(0xA6BA837B), themeSecondary = Color(0xA6BA837B),
themeTertiary = Color(0x40BA837B), themeTertiary = Color(0x40BA837B),
textPrimary = DefaultDarkColors.textPrimary, textPrimary = DefaultDarkColors.textPrimary,
textSecondary = DefaultDarkColors.textSecondary, textSecondary = DefaultDarkColors.textSecondary
isLight = false
) )
private val PurpleDarkColors = Colors( private val PurpleDarkColors = Colors(
@@ -220,8 +210,7 @@ private val PurpleDarkColors = Colors(
themeSecondary = Color(0xA69F88AD), themeSecondary = Color(0xA69F88AD),
themeTertiary = Color(0x409F88AD), themeTertiary = Color(0x409F88AD),
textPrimary = DefaultDarkColors.textPrimary, textPrimary = DefaultDarkColors.textPrimary,
textSecondary = DefaultDarkColors.textSecondary, textSecondary = DefaultDarkColors.textSecondary
isLight = false
) )
private val OrangeDarkColors = Colors( private val OrangeDarkColors = Colors(
@@ -233,8 +222,7 @@ private val OrangeDarkColors = Colors(
themeSecondary = Color(0xA6AE8B5D), themeSecondary = Color(0xA6AE8B5D),
themeTertiary = Color(0x40AE8B5D), themeTertiary = Color(0x40AE8B5D),
textPrimary = DefaultDarkColors.textPrimary, textPrimary = DefaultDarkColors.textPrimary,
textSecondary = DefaultDarkColors.textSecondary, textSecondary = DefaultDarkColors.textSecondary
isLight = false
) )
private val YellowDarkColors = Colors( private val YellowDarkColors = Colors(
@@ -246,8 +234,7 @@ private val YellowDarkColors = Colors(
themeSecondary = Color(0xA6A18F5C), themeSecondary = Color(0xA6A18F5C),
themeTertiary = Color(0x40A18F5C), themeTertiary = Color(0x40A18F5C),
textPrimary = DefaultDarkColors.textPrimary, textPrimary = DefaultDarkColors.textPrimary,
textSecondary = DefaultDarkColors.textSecondary, textSecondary = DefaultDarkColors.textSecondary
isLight = false
) )
private val GreenDarkColors = Colors( private val GreenDarkColors = Colors(
@@ -259,8 +246,7 @@ private val GreenDarkColors = Colors(
themeSecondary = Color(0xA67F9687), themeSecondary = Color(0xA67F9687),
themeTertiary = Color(0x407F9687), themeTertiary = Color(0x407F9687),
textPrimary = DefaultDarkColors.textPrimary, textPrimary = DefaultDarkColors.textPrimary,
textSecondary = DefaultDarkColors.textSecondary, textSecondary = DefaultDarkColors.textSecondary
isLight = false
) )
private val BlueDarkColors = Colors( private val BlueDarkColors = Colors(
@@ -272,8 +258,7 @@ private val BlueDarkColors = Colors(
themeSecondary = Color(0xA68091B1), themeSecondary = Color(0xA68091B1),
themeTertiary = Color(0x408091B1), themeTertiary = Color(0x408091B1),
textPrimary = DefaultDarkColors.textPrimary, textPrimary = DefaultDarkColors.textPrimary,
textSecondary = DefaultDarkColors.textSecondary, textSecondary = DefaultDarkColors.textSecondary
isLight = false
) )
private val RedBlackColors = Colors( private val RedBlackColors = Colors(
@@ -285,8 +270,7 @@ private val RedBlackColors = Colors(
themeSecondary = Color(0xA6B9856D), themeSecondary = Color(0xA6B9856D),
themeTertiary = Color(0x45B9856D), themeTertiary = Color(0x45B9856D),
textPrimary = DefaultBlackColors.textPrimary, textPrimary = DefaultBlackColors.textPrimary,
textSecondary = DefaultBlackColors.textSecondary, textSecondary = DefaultBlackColors.textSecondary
isLight = false
) )
private val PinkBlackColors = Colors( private val PinkBlackColors = Colors(
@@ -298,8 +282,7 @@ private val PinkBlackColors = Colors(
themeSecondary = Color(0xA6BA837B), themeSecondary = Color(0xA6BA837B),
themeTertiary = Color(0x45BA837B), themeTertiary = Color(0x45BA837B),
textPrimary = DefaultBlackColors.textPrimary, textPrimary = DefaultBlackColors.textPrimary,
textSecondary = DefaultBlackColors.textSecondary, textSecondary = DefaultBlackColors.textSecondary
isLight = false
) )
private val PurpleBlackColors = Colors( private val PurpleBlackColors = Colors(
@@ -311,8 +294,7 @@ private val PurpleBlackColors = Colors(
themeSecondary = Color(0xA69F88AD), themeSecondary = Color(0xA69F88AD),
themeTertiary = Color(0x459F88AD), themeTertiary = Color(0x459F88AD),
textPrimary = DefaultBlackColors.textPrimary, textPrimary = DefaultBlackColors.textPrimary,
textSecondary = DefaultBlackColors.textSecondary, textSecondary = DefaultBlackColors.textSecondary
isLight = false
) )
private val OrangeBlackColors = Colors( private val OrangeBlackColors = Colors(
@@ -324,8 +306,7 @@ private val OrangeBlackColors = Colors(
themeSecondary = Color(0xA6AE8B5D), themeSecondary = Color(0xA6AE8B5D),
themeTertiary = Color(0x45AE8B5D), themeTertiary = Color(0x45AE8B5D),
textPrimary = DefaultBlackColors.textPrimary, textPrimary = DefaultBlackColors.textPrimary,
textSecondary = DefaultBlackColors.textSecondary, textSecondary = DefaultBlackColors.textSecondary
isLight = false
) )
private val YellowBlackColors = Colors( private val YellowBlackColors = Colors(
@@ -337,8 +318,7 @@ private val YellowBlackColors = Colors(
themeSecondary = Color(0xA6A18F5C), themeSecondary = Color(0xA6A18F5C),
themeTertiary = Color(0x45A18F5C), themeTertiary = Color(0x45A18F5C),
textPrimary = DefaultBlackColors.textPrimary, textPrimary = DefaultBlackColors.textPrimary,
textSecondary = DefaultBlackColors.textSecondary, textSecondary = DefaultBlackColors.textSecondary
isLight = false
) )
private val GreenBlackColors = Colors( private val GreenBlackColors = Colors(
@@ -350,8 +330,7 @@ private val GreenBlackColors = Colors(
themeSecondary = Color(0xA67F9687), themeSecondary = Color(0xA67F9687),
themeTertiary = Color(0x457F9687), themeTertiary = Color(0x457F9687),
textPrimary = DefaultBlackColors.textPrimary, textPrimary = DefaultBlackColors.textPrimary,
textSecondary = DefaultBlackColors.textSecondary, textSecondary = DefaultBlackColors.textSecondary
isLight = false
) )
private val BlueBlackColors = Colors( private val BlueBlackColors = Colors(
@@ -363,14 +342,24 @@ private val BlueBlackColors = Colors(
themeSecondary = Color(0xA68091B1), themeSecondary = Color(0xA68091B1),
themeTertiary = Color(0x458091B1), themeTertiary = Color(0x458091B1),
textPrimary = DefaultBlackColors.textPrimary, textPrimary = DefaultBlackColors.textPrimary,
textSecondary = DefaultBlackColors.textSecondary, textSecondary = DefaultBlackColors.textSecondary
isLight = false
) )
/**
* Whether dynamic color is available for current system.
* @return [Boolean]
*/
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
expect fun isDynamicColorAvailable(): Boolean expect fun isDynamicColorAvailable(): Boolean
/**
* Returns a dynamic color scheme provided by system.
*
* You can use [isDynamicColorAvailable] check it first, otherwise it will return default colors.
* @param darkMode whether to use dark mode color scheme.
* @param blackDarkMode requires [darkMode] is true, whether to use a pure black color scheme.
*/
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
fun dynamicColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when { fun dynamicColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
@@ -378,41 +367,81 @@ fun dynamicColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = w
else -> DynamicLightColors else -> DynamicLightColors
} }
/**
* Returns a default color scheme.
* @param darkMode whether to use dark mode color scheme.
* @param blackDarkMode requires [darkMode] is true, whether to use a pure black color scheme.
*/
fun defaultColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when { fun defaultColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
darkMode -> if (blackDarkMode) DefaultBlackColors else DefaultDarkColors darkMode -> if (blackDarkMode) DefaultBlackColors else DefaultDarkColors
else -> DefaultLightColors else -> DefaultLightColors
} }
/**
* Returns a red color scheme.
* @param darkMode whether to use dark mode color scheme.
* @param blackDarkMode requires [darkMode] is true, whether to use a pure black color scheme.
*/
fun redColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when { fun redColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
darkMode -> if (blackDarkMode) RedBlackColors else RedDarkColors darkMode -> if (blackDarkMode) RedBlackColors else RedDarkColors
else -> RedLightColors else -> RedLightColors
} }
/**
* Returns a pink color scheme.
* @param darkMode whether to use dark mode color scheme.
* @param blackDarkMode requires [darkMode] is true, whether to use a pure black color scheme.
*/
fun pinkColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when { fun pinkColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
darkMode -> if (blackDarkMode) PinkBlackColors else PinkDarkColors darkMode -> if (blackDarkMode) PinkBlackColors else PinkDarkColors
else -> PinkLightColors else -> PinkLightColors
} }
/**
* Returns a purple color scheme.
* @param darkMode whether to use dark mode color scheme.
* @param blackDarkMode requires [darkMode] is true, whether to use a pure black color scheme.
*/
fun purpleColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when { fun purpleColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
darkMode -> if (blackDarkMode) PurpleBlackColors else PurpleDarkColors darkMode -> if (blackDarkMode) PurpleBlackColors else PurpleDarkColors
else -> PurpleLightColors else -> PurpleLightColors
} }
/**
* Returns a orange color scheme.
* @param darkMode whether to use dark mode color scheme.
* @param blackDarkMode requires [darkMode] is true, whether to use a pure black color scheme.
*/
fun orangeColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when { fun orangeColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
darkMode -> if (blackDarkMode) OrangeBlackColors else OrangeDarkColors darkMode -> if (blackDarkMode) OrangeBlackColors else OrangeDarkColors
else -> OrangeLightColors else -> OrangeLightColors
} }
/**
* Returns a yellow color scheme.
* @param darkMode whether to use dark mode color scheme.
* @param blackDarkMode requires [darkMode] is true, whether to use a pure black color scheme.
*/
fun yellowColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when { fun yellowColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
darkMode -> if (blackDarkMode) YellowBlackColors else YellowDarkColors darkMode -> if (blackDarkMode) YellowBlackColors else YellowDarkColors
else -> YellowLightColors else -> YellowLightColors
} }
/**
* Returns a green color scheme.
* @param darkMode whether to use dark mode color scheme.
* @param blackDarkMode requires [darkMode] is true, whether to use a pure black color scheme.
*/
fun greenColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when { fun greenColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
darkMode -> if (blackDarkMode) GreenBlackColors else GreenDarkColors darkMode -> if (blackDarkMode) GreenBlackColors else GreenDarkColors
else -> GreenLightColors else -> GreenLightColors
} }
/**
* Returns a blue color scheme.
* @param darkMode whether to use dark mode color scheme.
* @param blackDarkMode requires [darkMode] is true, whether to use a pure black color scheme.
*/
fun blueColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when { fun blueColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
darkMode -> if (blackDarkMode) BlueBlackColors else BlueDarkColors darkMode -> if (blackDarkMode) BlueBlackColors else BlueDarkColors
else -> BlueLightColors else -> BlueLightColors

View File

@@ -27,6 +27,15 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
/**
* Customize Flexi UI theme styles.
*
* Use this function to provide your own theme styles to the content.
* @param colors the colors, default is [FlexiTheme.colors].
* @param shapes the shapes, default is [FlexiTheme.shapes].
* @param typography the typography, default is [FlexiTheme.typography].
* @param content the content.
*/
@Composable @Composable
fun FlexiTheme( fun FlexiTheme(
colors: Colors = FlexiTheme.colors, colors: Colors = FlexiTheme.colors,
@@ -37,6 +46,17 @@ fun FlexiTheme(
FlexiTheme(colors, shapes, typography, FlexiTheme.sizes, content) FlexiTheme(colors, shapes, typography, FlexiTheme.sizes, content)
} }
/**
* Customize Flexi UI theme styles.
*
* Use this function to provide your own theme styles to the content.
*
* - Note: The [sizes] is experimental for now, its may be change in the future.
* @param colors the colors, default is [FlexiTheme.colors].
* @param shapes the shapes, default is [FlexiTheme.shapes].
* @param sizes the sizes.
* @param typography the typography, default is [FlexiTheme.typography].
*/
@Composable @Composable
fun FlexiTheme( fun FlexiTheme(
colors: Colors = FlexiTheme.colors, colors: Colors = FlexiTheme.colors,
@@ -53,6 +73,9 @@ fun FlexiTheme(
) { FlexiThemeContent(content) } ) { FlexiThemeContent(content) }
} }
/**
* Defaults of Flexi UI theme styles.
*/
object FlexiTheme { object FlexiTheme {
val colors: Colors val colors: Colors
@Composable @Composable

View File

@@ -30,6 +30,9 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
/**
* Shapes defines for Flexi UI.
*/
@Immutable @Immutable
data class Shapes( data class Shapes(
val primary: CornerBasedShape, val primary: CornerBasedShape,

View File

@@ -28,6 +28,9 @@ 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
/**
* Sizes defines for Flexi UI.
*/
@ExperimentalFlexiUISizesApi @ExperimentalFlexiUISizesApi
@Immutable @Immutable
data class Sizes( data class Sizes(

View File

@@ -30,6 +30,9 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.em import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
/**
* Typography defines for Flexi UI.
*/
@Immutable @Immutable
data class Typography( data class Typography(
val titlePrimary: TextStyle, val titlePrimary: TextStyle,

View File

@@ -56,6 +56,12 @@ import com.highcapable.flexiui.resources.Icons
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
/**
* Colors defines for action bar.
* @param titleTextColor the title text color.
* @param subTextColor the sub text color.
* @param actionContentColor the action content color, usually for icon tint and text color.
*/
@Immutable @Immutable
data class ActionBarColors( data class ActionBarColors(
val titleTextColor: Color, val titleTextColor: Color,
@@ -63,6 +69,16 @@ data class ActionBarColors(
val actionContentColor: Color val actionContentColor: Color
) )
/**
* Style defines for action 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.
*/
@Immutable @Immutable
data class ActionBarStyle( data class ActionBarStyle(
val padding: ComponentPadding, val padding: ComponentPadding,
@@ -74,6 +90,16 @@ data class ActionBarStyle(
val actionContentMaxWidth: Dp val actionContentMaxWidth: Dp
) )
/**
* Flexi UI large action bar.
* @see ActionBarDefaults
* @param modifier the [Modifier] to be applied to this action bar.
* @param colors the colors of this action bar, default is [ActionBarDefaults.colors].
* @param style the style of this action bar, default is [ActionBarDefaults.style].
* @param titleText the title text of this action bar, should typically be [Text].
* @param subText the sub text of this action bar, should typically be [Text].
* @param actions the actions displayed at the end of the action bar, should typically be [ActionBarScope.ActionIconButton].
*/
@Composable @Composable
fun TopActionBar( fun TopActionBar(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -96,6 +122,18 @@ fun TopActionBar(
) )
} }
/**
* Flexi UI middle action bar.
* @see TopActionBar
* @param modifier the [Modifier] to be applied to this action bar.
* @param colors the colors of this action bar, default is [ActionBarDefaults.colors].
* @param style the style of this action bar, default is [ActionBarDefaults.style].
* @param titleText the title text of this action bar, should typically be [Text].
* @param subText the sub text of this action bar, should typically be [Text].
* @param finishIcon the finish icon displayed at the start of the action bar, should typically be [ActionBarScope.FinishIconButton].
* @param navigationIcon the navigation icon displayed at the start of the action bar, should typically be [ActionBarScope.NavigationIconButton].
* @param actions the actions displayed at the end of the action bar, should typically be [ActionBarScope.ActionIconButton].
*/
@Composable @Composable
fun ActionBar( fun ActionBar(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -120,6 +158,9 @@ fun ActionBar(
) )
} }
/**
* Basic action bar for internal use.
*/
@Composable @Composable
private fun BasicActionBar( private fun BasicActionBar(
type: ActionBarType, type: ActionBarType,
@@ -133,8 +174,8 @@ private fun BasicActionBar(
actions: @Composable (ActionBarScope.() -> Unit)? actions: @Composable (ActionBarScope.() -> Unit)?
) { ) {
CompositionLocalProvider(LocalActionBarType provides type) { CompositionLocalProvider(LocalActionBarType provides type) {
val currentColors = colors ?: ActionBar.colors val currentColors = colors ?: ActionBarDefaults.colors
val currentStyle = style ?: ActionBar.style val currentStyle = style ?: ActionBarDefaults.style
Box(modifier = modifier.padding(currentStyle.padding)) { Box(modifier = modifier.padding(currentStyle.padding)) {
ActionBarImpl( ActionBarImpl(
type = type, type = type,
@@ -150,15 +191,27 @@ private fun BasicActionBar(
} }
} }
/**
* A scope for action bar.
*/
@Stable @Stable
interface ActionBarScope { interface ActionBarScope {
/**
* Action bar's finish icon button.
* @param onClick the callback when the icon button is clicked.
* @param modifier the [Modifier] to be applied to this icon button.
* @param colors the colors of this icon button, default is [IconButtonDefaults.colors].
* @param style the style of this icon button, default is [IconButtonDefaults.style].
* @param enabled whether this icon button is enabled, default is true.
* @param interactionSource the interaction source of this icon button.
*/
@Composable @Composable
fun FinishIconButton( fun FinishIconButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: ButtonColors = IconButton.colors, colors: ButtonColors = IconButtonDefaults.colors,
style: ButtonStyle = IconButton.style, style: ButtonStyle = IconButtonDefaults.style,
enabled: Boolean = true, enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) { ) {
@@ -172,12 +225,21 @@ interface ActionBarScope {
) { Icon(imageVector = Icons.FinishClose) } ) { Icon(imageVector = Icons.FinishClose) }
} }
/**
* Action bar's navigation icon button.
* @param onClick the callback when the icon button is clicked.
* @param modifier the [Modifier] to be applied to this icon button.
* @param colors the colors of this icon button, default is [IconButtonDefaults.colors].
* @param style the style of this icon button, default is [IconButtonDefaults.style].
* @param enabled whether this icon button is enabled, default is true.
* @param interactionSource the interaction source of this icon button.
*/
@Composable @Composable
fun NavigationIconButton( fun NavigationIconButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: ButtonColors = IconButton.colors, colors: ButtonColors = IconButtonDefaults.colors,
style: ButtonStyle = IconButton.style, style: ButtonStyle = IconButtonDefaults.style,
enabled: Boolean = true, enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) { ) {
@@ -191,12 +253,22 @@ interface ActionBarScope {
) { Icon(imageVector = Icons.ArrowNaviUp) } ) { Icon(imageVector = Icons.ArrowNaviUp) }
} }
/**
* Action bar's action icon button.
* @param onClick the callback when the icon button is clicked.
* @param modifier the [Modifier] to be applied to this icon button.
* @param colors the colors of this icon button, default is [IconButtonDefaults.colors].
* @param style the style of this icon button, default is [IconButtonDefaults.style].
* @param enabled whether this icon button is enabled, default is true.
* @param interactionSource the interaction source of this icon button.
* @param content the content of the [ActionIconButton], should typically be [Icon].
*/
@Composable @Composable
fun ActionIconButton( fun ActionIconButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: ButtonColors = IconButton.colors, colors: ButtonColors = IconButtonDefaults.colors,
style: ButtonStyle = IconButton.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
@@ -214,6 +286,9 @@ interface ActionBarScope {
} }
} }
/**
* Action bar's implementation.
*/
@Immutable @Immutable
private class ActionBarImpl( private class ActionBarImpl(
val type: ActionBarType, val type: ActionBarType,
@@ -226,6 +301,7 @@ private class ActionBarImpl(
val actions: @Composable (ActionBarScope.() -> Unit)? val actions: @Composable (ActionBarScope.() -> Unit)?
) : ActionBarScope { ) : ActionBarScope {
/** Build action bar's content. */
@Composable @Composable
fun Content() { fun Content() {
BoxWithConstraints(modifier = Modifier.fillMaxWidth()) { BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
@@ -250,6 +326,7 @@ private class ActionBarImpl(
} }
} }
/** Build action bar's start content. */
@Composable @Composable
private fun StartContent() { private fun StartContent() {
if (type == ActionBarType.MIDDLE && (finishIcon != null || navigationIcon != null)) if (type == ActionBarType.MIDDLE && (finishIcon != null || navigationIcon != null))
@@ -264,6 +341,7 @@ private class ActionBarImpl(
} }
} }
/** Build action bar's center content. */
@Composable @Composable
private fun CenterContent() { private fun CenterContent() {
Column( Column(
@@ -285,6 +363,7 @@ private class ActionBarImpl(
} }
} }
/** Build action bar's end content. */
@Composable @Composable
private fun EndContent() { private fun EndContent() {
actions?.also { content -> actions?.also { content ->
@@ -297,6 +376,7 @@ private class ActionBarImpl(
} }
} }
/** Provided the style for action bar's content. */
@Composable @Composable
private fun ContentStyle( private fun ContentStyle(
color: Color, color: Color,
@@ -317,7 +397,10 @@ private val ActionBarScope.impl get() = this as? ActionBarImpl? ?: error("Could
@Stable @Stable
private enum class ActionBarType { LARGE, MIDDLE } private enum class ActionBarType { LARGE, MIDDLE }
object ActionBar { /**
* Defaults of action bar.
*/
object ActionBarDefaults {
val colors: ActionBarColors val colors: ActionBarColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable

View File

@@ -55,6 +55,13 @@ import com.highcapable.flexiui.LocalColors
import com.highcapable.flexiui.LocalShapes import com.highcapable.flexiui.LocalShapes
import com.highcapable.flexiui.LocalSizes import com.highcapable.flexiui.LocalSizes
/**
* Style defines for area box.
* @param padding the padding of content.
* @param shape the shape of the box.
* @param border the border stroke of the box.
* @param shadowSize the shadow size of the box.
*/
@Immutable @Immutable
data class AreaBoxStyle( data class AreaBoxStyle(
val padding: ComponentPadding, val padding: ComponentPadding,
@@ -63,12 +70,24 @@ data class AreaBoxStyle(
val shadowSize: Dp val shadowSize: Dp
) )
/**
* Flexi UI area box of [Box].
* @see AreaRow
* @see AreaColumn
* @param modifier the [Modifier] to be applied to this area box.
* @param initializer the [Modifier] initializer, earlies than [modifier].
* @param color the background color of this area box, default is [AreaBoxDefaults.color].
* @param style the style of this area box, default is [AreaBoxDefaults.style].
* @param contentAlignment the alignment of the content inside this area box, default is [Alignment.TopStart].
* @param propagateMinConstraints whether to propagate the min constraints from the content to this area box.
* @param content the content of the [AreaBox].
*/
@Composable @Composable
fun AreaBox( fun AreaBox(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
initializer: @Composable Modifier.() -> Modifier = { Modifier }, initializer: @Composable Modifier.() -> Modifier = { Modifier },
color: Color = AreaBox.color, color: Color = AreaBoxDefaults.color,
style: AreaBoxStyle = AreaBox.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
@@ -86,12 +105,24 @@ fun AreaBox(
} }
} }
/**
* Flexi UI area box of [Row].
* @see AreaColumn
* @see AreaBox
* @param modifier the [Modifier] to be applied to this area row.
* @param initializer the [Modifier] initializer, earlies than [modifier].
* @param color the background color of this area row, default is [AreaBoxDefaults.color].
* @param style the style of this area row, default is [AreaBoxDefaults.style].
* @param horizontalArrangement the horizontal arrangement of the content inside this area row, default is [Arrangement.Start].
* @param verticalAlignment the vertical alignment of the content inside this area row, default is [Alignment.Top].
* @param content the content of the [AreaRow].
*/
@Composable @Composable
fun AreaRow( fun AreaRow(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
initializer: @Composable Modifier.() -> Modifier = { Modifier }, initializer: @Composable Modifier.() -> Modifier = { Modifier },
color: Color = AreaBox.color, color: Color = AreaBoxDefaults.color,
style: AreaBoxStyle = AreaBox.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
@@ -109,12 +140,24 @@ fun AreaRow(
} }
} }
/**
* Flexi UI area box of [Column].
* @see AreaRow
* @see AreaBox
* @param modifier the [Modifier] to be applied to this area column.
* @param initializer the [Modifier] initializer, earlies than [modifier].
* @param color the background color of this area column, default is [AreaBoxDefaults.color].
* @param style the style of this area column, default is [AreaBoxDefaults.style].
* @param verticalArrangement the vertical arrangement of the content inside this area column, default is [Arrangement.Top].
* @param horizontalAlignment the horizontal alignment of the content inside this area column, default is [Alignment.Start].
* @param content the content of the [AreaColumn].
*/
@Composable @Composable
fun AreaColumn( fun AreaColumn(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
initializer: @Composable Modifier.() -> Modifier = { Modifier }, initializer: @Composable Modifier.() -> Modifier = { Modifier },
color: Color = AreaBox.color, color: Color = AreaBoxDefaults.color,
style: AreaBoxStyle = AreaBox.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
@@ -153,7 +196,10 @@ private fun Modifier.areaBox(
.padding(style.padding) .padding(style.padding)
} }
object AreaBox { /**
* Defaults of area box.
*/
object AreaBoxDefaults {
val color: Color val color: Color
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable

View File

@@ -51,17 +51,29 @@ import com.highcapable.betterandroid.compose.extension.ui.orNull
import com.highcapable.flexiui.LocalColors import com.highcapable.flexiui.LocalColors
import com.highcapable.flexiui.LocalShapes import com.highcapable.flexiui.LocalShapes
import com.highcapable.flexiui.LocalSizes import com.highcapable.flexiui.LocalSizes
import com.highcapable.flexiui.interaction.Interaction import com.highcapable.flexiui.interaction.InteractionDefaults
import com.highcapable.flexiui.interaction.RippleStyle import com.highcapable.flexiui.interaction.RippleStyle
import com.highcapable.flexiui.interaction.rippleClickable import com.highcapable.flexiui.interaction.rippleClickable
import com.highcapable.flexiui.interaction.rippleToggleable import com.highcapable.flexiui.interaction.rippleToggleable
/**
* Colors defines for button.
* @param contentColor the content color, usually for icon tint and text color.
* @param backgroundColor the background color.
*/
@Immutable @Immutable
data class ButtonColors( data class ButtonColors(
val contentColor: Color, val contentColor: Color,
val backgroundColor: Color val backgroundColor: Color
) )
/**
* Style defines for button.
* @param rippleStyle the ripple style of this button.
* @param padding the padding of content.
* @param shape the shape.
* @param border the border stroke.
*/
@Immutable @Immutable
data class ButtonStyle( data class ButtonStyle(
val rippleStyle: RippleStyle, val rippleStyle: RippleStyle,
@@ -70,12 +82,26 @@ data class ButtonStyle(
val border: BorderStroke val border: BorderStroke
) )
/**
* Flexi UI button.
* @see IconButton
* @see IconToggleButton
* @param onClick the callback when button is clicked.
* @param modifier the [Modifier] to be applied to this button.
* @param colors the colors of this button, default is [ButtonDefaults.colors].
* @param style the style of this button, default is [ButtonDefaults.style].
* @param enabled whether this button is enabled, default is true.
* @param interactionSource the interaction source of this button.
* @param header the header content of the [Button], should typically be [Icon] or [Text].
* @param footer the footer content of the [Button], should typically be [Icon] or [Text].
* @param content the content of the [Button], should typically be [Icon] or [Text].
*/
@Composable @Composable
fun Button( fun Button(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: ButtonColors = Button.colors, colors: ButtonColors = ButtonDefaults.colors,
style: ButtonStyle = Button.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 = {},
@@ -119,12 +145,24 @@ fun Button(
} }
} }
/**
* Flexi UI icon button.
* @see IconToggleButton
* @see Button
* @param onClick the callback when button is clicked.
* @param modifier the [Modifier] to be applied to this button.
* @param colors the colors of this button, default is [IconButtonDefaults.colors].
* @param style the style of this button, default is [IconButtonDefaults.style].
* @param enabled whether this button is enabled, default is true.
* @param interactionSource the interaction source of this button.
* @param content the content of the [IconButton], should typically be [Icon].
*/
@Composable @Composable
fun IconButton( fun IconButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: ButtonColors = IconButton.colors, colors: ButtonColors = IconButtonDefaults.colors,
style: ButtonStyle = IconButton.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
@@ -151,13 +189,26 @@ fun IconButton(
} }
} }
/**
* Flexi UI icon toggle button.
* @see IconButton
* @see Button
* @param checked the checked state of this button.
* @param onCheckedChange the callback when checked state is changed.
* @param modifier the [Modifier] to be applied to this button.
* @param colors the colors of this button, default is [IconButtonDefaults.colors].
* @param style the style of this button, default is [IconButtonDefaults.style].
* @param enabled whether this button is enabled, default is true.
* @param interactionSource the interaction source of this button.
* @param content the content of the [IconToggleButton], should typically be [Icon].
*/
@Composable @Composable
fun IconToggleButton( fun IconToggleButton(
checked: Boolean, checked: Boolean,
onCheckedChange: (Boolean) -> Unit, onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: ButtonColors = IconButton.colors, colors: ButtonColors = IconButtonDefaults.colors,
style: ButtonStyle = IconButton.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
@@ -205,7 +256,10 @@ private fun Modifier.button(
.then(then) .then(then)
} }
object Button { /**
* Defaults of button.
*/
object ButtonDefaults {
val colors: ButtonColors val colors: ButtonColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
@@ -216,7 +270,10 @@ object Button {
get() = defaultButtonStyle() get() = defaultButtonStyle()
} }
object IconButton { /**
* Defaults of icon button.
*/
object IconButtonDefaults {
val colors: ButtonColors val colors: ButtonColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
@@ -246,12 +303,16 @@ private fun defaultContentButtonColors() = ButtonColors(
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun defaultPrimaryButtonRippleStyle() = private fun defaultPrimaryButtonRippleStyle() =
Interaction.rippleStyle.copy(color = LocalColors.current.foregroundSecondary) InteractionDefaults.rippleStyle.copy(color = LocalColors.current.foregroundSecondary)
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun defaultContentButtonRippleStyle() = private fun defaultContentButtonRippleStyle() =
Interaction.rippleStyle.copy(color = LocalColors.current.themeSecondary) InteractionDefaults.rippleStyle.copy(color = LocalColors.current.themeSecondary)
@Composable
@ReadOnlyComposable
private fun defaultIconButtonRippleStyle() = InteractionDefaults.rippleStyle.copy(bounded = false)
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
@@ -297,10 +358,6 @@ private fun defaultIconButtonStyle() = ButtonStyle(
border = defaultButtonBorder() border = defaultButtonBorder()
) )
@Composable
@ReadOnlyComposable
private fun defaultIconButtonRippleStyle() = Interaction.rippleStyle.copy(bounded = false)
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun defaultButtonBorder() = BorderStroke(LocalSizes.current.borderSizeTertiary, LocalColors.current.textPrimary) private fun defaultButtonBorder() = BorderStroke(LocalSizes.current.borderSizeTertiary, LocalColors.current.textPrimary)

View File

@@ -60,6 +60,12 @@ import com.highcapable.flexiui.LocalSizes
import com.highcapable.flexiui.resources.Icons import com.highcapable.flexiui.resources.Icons
import com.highcapable.flexiui.resources.icon.CheckMark import com.highcapable.flexiui.resources.icon.CheckMark
/**
* Colors defines for check box.
* @param contentColor the color of the check mark.
* @param inactiveColor the color of the unchecked box.
* @param activeColor the color of the checked box.
*/
@Immutable @Immutable
data class CheckBoxColors( data class CheckBoxColors(
val contentColor: Color, val contentColor: Color,
@@ -67,6 +73,16 @@ data class CheckBoxColors(
val activeColor: Color val activeColor: Color
) )
/**
* Style defines for check box.
* @param contentSpacing the spacing between the check mark and the content.
* @param contentSize the size of the check mark.
* @param strokeSize the stroke size.
* @param pressedGain the gain when pressed.
* @param hoveredGain the gain when hovered.
* @param shape the shape.
* @param border the border stroke.
*/
@Immutable @Immutable
data class CheckBoxStyle( data class CheckBoxStyle(
val contentSpacing: Dp, val contentSpacing: Dp,
@@ -78,13 +94,24 @@ data class CheckBoxStyle(
val border: BorderStroke val border: BorderStroke
) )
/**
* Flexi UI check box.
* @param checked the checked state of this check box.
* @param onCheckedChange the callback when checked state changed.
* @param modifier the [Modifier] to be applied to this check box.
* @param colors the colors of this check box, default is [CheckBoxDefaults.colors].
* @param style the style of this check box, default is [CheckBoxDefaults.style].
* @param enabled whether this check box is enabled, default is true.
* @param interactionSource the interaction source of this check box.
* @param content the content of the [RowScope] to be applied to the [CheckBox], should typically be [Text].
*/
@Composable @Composable
fun CheckBox( fun CheckBox(
checked: Boolean, checked: Boolean,
onCheckedChange: (Boolean) -> Unit, onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: CheckBoxColors = CheckBox.colors, colors: CheckBoxColors = CheckBoxDefaults.colors,
style: CheckBoxStyle = CheckBox.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
@@ -119,7 +146,7 @@ fun CheckBox(
scaleY = animatedContentLayer scaleY = animatedContentLayer
), ),
imageVector = Icons.CheckMark, imageVector = Icons.CheckMark,
style = Icon.style.copy(tint = colors.contentColor) style = IconDefaults.style.copy(tint = colors.contentColor)
) )
} }
content?.also { content -> content?.also { content ->
@@ -131,7 +158,10 @@ fun CheckBox(
} }
} }
object CheckBox { /**
* Defaults of check box.
*/
object CheckBoxDefaults {
val colors: CheckBoxColors val colors: CheckBoxColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable

View File

@@ -117,6 +117,14 @@ import com.highcapable.flexiui.resources.icon.Dropdown
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
/**
* Colors defines for dropdown list.
* @param endIconInactiveTint the tint of the end icon when inactive.
* @param endIconActiveTint the tint of the end icon when active.
* @param borderInactiveColor the color of the border when inactive.
* @param borderActiveColor the color of the border when active.
* @param backgroundColor the background color.
*/
@Immutable @Immutable
data class DropdownListColors( data class DropdownListColors(
val endIconInactiveTint: Color, val endIconInactiveTint: Color,
@@ -126,6 +134,12 @@ data class DropdownListColors(
val backgroundColor: Color val backgroundColor: Color
) )
/**
* Colors defines for dropdown menu.
* @param contentColor the color of the content.
* @param activeColor the color of the active item.
* @param borderColor the color of the border.
*/
@Immutable @Immutable
data class DropdownMenuColors( data class DropdownMenuColors(
val contentColor: Color, val contentColor: Color,
@@ -133,6 +147,14 @@ data class DropdownMenuColors(
val borderColor: Color val borderColor: Color
) )
/**
* Style defines for dropdown list.
* @param padding the padding of the content.
* @param shape the shape.
* @param endIconSize the size of the end icon.
* @param borderInactive the border stroke when inactive.
* @param borderActive the border stroke when active.
*/
@Immutable @Immutable
data class DropdownListStyle( data class DropdownListStyle(
val padding: ComponentPadding, val padding: ComponentPadding,
@@ -142,6 +164,13 @@ data class DropdownListStyle(
val borderActive: BorderStroke val borderActive: BorderStroke
) )
/**
* Style defines for dropdown menu.
* @param inTransitionDuration the duration of the in transition.
* @param outTransitionDuration the duration of the out transition.
* @param contentStyle the content style of area box.
* @param borderStyle the brder style of area box.
*/
@Immutable @Immutable
data class DropdownMenuStyle( data class DropdownMenuStyle(
val inTransitionDuration: Int, val inTransitionDuration: Int,
@@ -150,15 +179,33 @@ data class DropdownMenuStyle(
val borderStyle: AreaBoxStyle val borderStyle: AreaBoxStyle
) )
/**
* Flexi UI dropdown list.
* @see DropdownMenu
* @see DropdownMenuItem
* @param expanded whether the dropdown menu is expanded.
* @param onExpandedChange the callback when the expanded state is changed.
* @param modifier the [Modifier] to be applied to this dropdown list.
* @param colors the colors of this dropdown list, default is [DropdownListDefaults.colors].
* @param style the style of this dropdown list, default is [DropdownListDefaults.style].
* @param menuColors the colors of the dropdown menu, default is [DropdownMenuDefaults.colors].
* @param menuStyle the style of the dropdown menu, default is [DropdownMenuDefaults.style].
* @param enabled whether the dropdown list is enabled, default is true.
* @param scrollState the scroll state of the dropdown menu.
* @param properties the popup properties.
* @param interactionSource the interaction source of the dropdown list.
* @param text the text of the [DropdownList], should typically be [Text].
* @param content the content of the [DropdownMenu], should typically be [DropdownMenuItem].
*/
@Composable @Composable
fun DropdownList( fun DropdownList(
expanded: Boolean, expanded: Boolean,
onExpandedChange: (Boolean) -> Unit, onExpandedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: DropdownListColors = DropdownList.colors, colors: DropdownListColors = DropdownListDefaults.colors,
style: DropdownListStyle = DropdownList.style, style: DropdownListStyle = DropdownListDefaults.style,
menuColors: DropdownMenuColors = DropdownMenu.colors, menuColors: DropdownMenuColors = DropdownMenuDefaults.colors,
menuStyle: DropdownMenuStyle = DropdownMenu.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),
@@ -218,7 +265,7 @@ fun DropdownList(
rotationZ = animatedDirection rotationZ = animatedDirection
}.size(style.endIconSize), }.size(style.endIconSize),
imageVector = Icons.Dropdown, imageVector = Icons.Dropdown,
style = Icon.style.copy(tint = animatedEndIconTint) style = IconDefaults.style.copy(tint = animatedEndIconTint)
) )
} }
DropdownMenu( DropdownMenu(
@@ -235,13 +282,27 @@ fun DropdownList(
} }
} }
/**
* Flexi UI dropdown menu.
* @see DropdownList
* @see DropdownMenuItem
* @param expanded whether the dropdown menu is expanded.
* @param onDismissRequest the callback when the dropdown menu is dismissed.
* @param modifier the [Modifier] to be applied to this dropdown menu.
* @param colors the colors of this dropdown menu, default is [DropdownMenuDefaults.colors].
* @param style the style of this dropdown menu, default is [DropdownMenuDefaults.style].
* @param offset the offset of this dropdown menu.
* @param scrollState the scroll state of the dropdown menu.
* @param properties the popup properties.
* @param content the content of the [DropdownMenu], should typically be [DropdownMenuItem].
*/
@Composable @Composable
fun DropdownMenu( fun DropdownMenu(
expanded: Boolean, expanded: Boolean,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: DropdownMenuColors = DropdownMenu.colors, colors: DropdownMenuColors = DropdownMenuDefaults.colors,
style: DropdownMenuStyle = DropdownMenu.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),
@@ -278,6 +339,13 @@ fun DropdownMenu(
} }
} }
/**
* A box for manually measuring the size of the dropdown menu.
* @see DropdownList
* @see DropdownMenu
* @param modifier the [Modifier] to be applied to this box.
* @param content the content for measuring.
*/
@Composable @Composable
fun DropdownMenuBox( fun DropdownMenuBox(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -307,6 +375,20 @@ fun DropdownMenuBox(
} }
} }
/**
* Flexi UI dropdown menu item.
* @see DropdownList
* @see DropdownMenu
* @param onClick the callback when the dropdown menu item is clicked.
* @param modifier the [Modifier] to be applied to this dropdown menu item.
* @param contentColor the color of the content.
* @param activeColor the color of the active item.
* @param contentStyle the style of the content.
* @param enabled whether the dropdown menu item is enabled, default is true.
* @param actived whether the dropdown menu item is actived, default is false.
* @param interactionSource the interaction source of the dropdown menu item.
* @param content the content of the [DropdownMenuItem], should typically be [Icon] or [Text].
*/
@Composable @Composable
fun DropdownMenuItem( fun DropdownMenuItem(
onClick: () -> Unit, onClick: () -> Unit,
@@ -319,9 +401,15 @@ fun DropdownMenuItem(
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable RowScope.() -> Unit content: @Composable RowScope.() -> Unit
) { ) {
val currentColor = contentColor.orNull() ?: LocalDropdownMenuContentColor.current.orNull() ?: DropdownMenu.colors.contentColor val currentColor = contentColor.orNull()
val currentActiveColor = activeColor.orNull() ?: LocalDropdownMenuActiveColor.current.orNull() ?: DropdownMenu.colors.activeColor ?: LocalDropdownMenuContentColor.current.orNull()
val currentStyle = contentStyle ?: LocalDropdownMenuContentStyle.current ?: DropdownMenu.style.contentStyle ?: DropdownMenuDefaults.colors.contentColor
val currentActiveColor = activeColor.orNull()
?: LocalDropdownMenuActiveColor.current.orNull()
?: DropdownMenuDefaults.colors.activeColor
val currentStyle = contentStyle
?: LocalDropdownMenuContentStyle.current
?: DropdownMenuDefaults.style.contentStyle
AreaRow( AreaRow(
modifier = Modifier.componentState(enabled) modifier = Modifier.componentState(enabled)
.then(modifier) .then(modifier)
@@ -354,6 +442,9 @@ internal expect fun DropdownMenuMeasureBox(
content: @Composable BoxScope.() -> Unit content: @Composable BoxScope.() -> Unit
) )
/**
* A scope for dropdown menu box.
*/
@Stable @Stable
interface DropdownMenuBoxScope : BoxWithConstraintsScope { interface DropdownMenuBoxScope : BoxWithConstraintsScope {
val menuMaxHeight: Dp val menuMaxHeight: Dp
@@ -516,7 +607,10 @@ private data class DropdownMenuPositionProvider(
} }
} }
object DropdownList { /**
* Defaults of dropdown list.
*/
object DropdownListDefaults {
val colors: DropdownListColors val colors: DropdownListColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
@@ -527,7 +621,10 @@ object DropdownList {
get() = defaultDropdownListStyle() get() = defaultDropdownListStyle()
} }
object DropdownMenu { /**
* Defaults of dropdown menu.
*/
object DropdownMenuDefaults {
val colors: DropdownMenuColors val colors: DropdownMenuColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
@@ -577,11 +674,11 @@ private fun defaultDropdownListStyle() = DropdownListStyle(
private fun defaultDropdownMenuStyle() = DropdownMenuStyle( private fun defaultDropdownMenuStyle() = DropdownMenuStyle(
inTransitionDuration = DefaultInTransitionDuration, inTransitionDuration = DefaultInTransitionDuration,
outTransitionDuration = DefaultOutTransitionDuration, outTransitionDuration = DefaultOutTransitionDuration,
contentStyle = AreaBox.style.copy( contentStyle = AreaBoxDefaults.style.copy(
padding = ComponentPadding(horizontal = DefaultMenuContentPadding), padding = ComponentPadding(horizontal = DefaultMenuContentPadding),
shape = LocalShapes.current.secondary shape = LocalShapes.current.secondary
), ),
borderStyle = AreaBox.style.copy( borderStyle = AreaBoxDefaults.style.copy(
padding = ComponentPadding(LocalSizes.current.spacingTertiary), padding = ComponentPadding(LocalSizes.current.spacingTertiary),
shadowSize = LocalSizes.current.zoomSizeTertiary, shadowSize = LocalSizes.current.zoomSizeTertiary,
shape = LocalShapes.current.primary shape = LocalShapes.current.primary

View File

@@ -51,31 +51,49 @@ import androidx.compose.ui.unit.isSpecified
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.LocalSizes
/**
* Style defines for icon.
* @param size the size.
* @param tint the tint.
*/
@Immutable @Immutable
data class IconStyle( data class IconStyle(
val size: Dp, val size: Dp,
val tint: Color val tint: Color
) )
/**
* Flexi UI basic icon.
* @param imageVector the vector image to be drawn.
* @param contentDescription the content description for this icon.
* @param modifier the [Modifier] to be applied to this icon.
* @param style the style of this icon, default is [IconDefaults.style].
*/
@Composable @Composable
fun Icon( fun Icon(
imageVector: ImageVector, imageVector: ImageVector,
contentDescription: String? = null, contentDescription: String? = null,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
style: IconStyle = Icon.style style: IconStyle = IconDefaults.style
) { ) {
val painter = rememberVectorPainter(imageVector) val painter = rememberVectorPainter(imageVector)
Icon(painter, contentDescription, modifier, style) Icon(painter, contentDescription, modifier, style)
} }
/**
* Flexi UI basic icon.
* @param painter the painter to be drawn.
* @param contentDescription the content description for this icon.
* @param modifier the [Modifier] to be applied to this icon.
* @param style the style of this icon, default is [IconDefaults.style].
*/
@Composable @Composable
fun Icon( fun Icon(
painter: Painter, painter: Painter,
contentDescription: String? = null, contentDescription: String? = null,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
style: IconStyle = Icon.style style: IconStyle = IconDefaults.style
) { ) {
// TODO: b/149735981 semantics for content description
val colorFilter = if (style.tint.isUnspecified) null else ColorFilter.tint(style.tint) val colorFilter = if (style.tint.isUnspecified) null else ColorFilter.tint(style.tint)
val semantics = if (contentDescription != null) val semantics = if (contentDescription != null)
Modifier.semantics { Modifier.semantics {
@@ -114,7 +132,10 @@ private fun Modifier.defaultSizeFor(
}) })
} }
object Icon { /**
* Defaults of icon.
*/
object IconDefaults {
val style: IconStyle val style: IconStyle
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable

View File

@@ -23,6 +23,7 @@
package com.highcapable.flexiui.component package com.highcapable.flexiui.component
import androidx.compose.foundation.Image
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.Column import androidx.compose.foundation.layout.Column
@@ -47,6 +48,13 @@ import com.highcapable.flexiui.interaction.rippleClickable
import com.highcapable.flexiui.resources.Icons import com.highcapable.flexiui.resources.Icons
import com.highcapable.flexiui.resources.icon.ArrowForward import com.highcapable.flexiui.resources.icon.ArrowForward
/**
* Colors defines for item box.
* @param backgroundColor the background color.
* @param titleTextColor the title text color.
* @param subTextColor the sub text color.
* @param arrowIconTint the arrow icon tint.
*/
@Immutable @Immutable
data class ItemBoxColors( data class ItemBoxColors(
val backgroundColor: Color, val backgroundColor: Color,
@@ -55,6 +63,13 @@ data class ItemBoxColors(
val arrowIconTint: Color val arrowIconTint: Color
) )
/**
* Style defines for item box.
* @param boxStyle the style of area box.
* @param contentSpacing the spacing between the components of content.
* @param titleTextStyle the title text style.
* @param subTextStyle the sub text style.
*/
@Immutable @Immutable
data class ItemBoxStyle( data class ItemBoxStyle(
val boxStyle: AreaBoxStyle, val boxStyle: AreaBoxStyle,
@@ -63,12 +78,26 @@ data class ItemBoxStyle(
val subTextStyle: TextStyle val subTextStyle: TextStyle
) )
/**
* Flexi UI horizontal item box.
* @see VerticalItemBox
* @param onClick the callback when item is clicked.
* @param modifier the [Modifier] to be applied to this item box.
* @param colors the colors of item box, default is [ItemBoxDefaults.colors].
* @param style the style of item box, default is [ItemBoxDefaults.style].
* @param enabled whether this item box is enabled, default is true.
* @param showArrowIcon whether show arrow icon, default is true.
* @param interactionSource the interaction source of this item box.
* @param logoImage the logo image of the [HorizontalItemBox], should typically be [Icon] or [Image].
* @param titleText the title text of the [HorizontalItemBox], should typically be [Text].
* @param subText the sub text of the [HorizontalItemBox], should typically be [Text].
*/
@Composable @Composable
fun HorizontalItemBox( fun HorizontalItemBox(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: ItemBoxColors = ItemBox.colors, colors: ItemBoxColors = ItemBoxDefaults.colors,
style: ItemBoxStyle = ItemBox.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() },
@@ -105,7 +134,7 @@ fun HorizontalItemBox(
} }
if (showArrowIcon) Icon( if (showArrowIcon) Icon(
imageVector = Icons.ArrowForward, imageVector = Icons.ArrowForward,
style = Icon.style.copy( style = IconDefaults.style.copy(
size = DefaultArrowIconSize, size = DefaultArrowIconSize,
tint = colors.arrowIconTint tint = colors.arrowIconTint
) )
@@ -114,12 +143,25 @@ fun HorizontalItemBox(
} }
} }
/**
* Flexi UI vertical item box.
* @see HorizontalItemBox
* @param onClick the callback when item is clicked.
* @param modifier the [Modifier] to be applied to this item box.
* @param colors the colors of item box, default is [ItemBoxDefaults.colors].
* @param style the style of item box, default is [ItemBoxDefaults.style].
* @param enabled whether this item box is enabled, default is true.
* @param interactionSource the interaction source of this item box.
* @param logoImage the logo image of the [VerticalItemBox], should typically be [Icon] or [Image].
* @param titleText the title text of the [VerticalItemBox], should typically be [Text].
* @param subText the sub text of the [VerticalItemBox], should typically be [Text].
*/
@Composable @Composable
fun VerticalItemBox( fun VerticalItemBox(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: ItemBoxColors = ItemBox.colors, colors: ItemBoxColors = ItemBoxDefaults.colors,
style: ItemBoxStyle = ItemBox.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,
@@ -167,7 +209,10 @@ private fun ItemBoxContent(
} }
} }
object ItemBox { /**
* Defaults of item box.
*/
object ItemBoxDefaults {
val colors: ItemBoxColors val colors: ItemBoxColors
@Composable @Composable
get() = defaultItemBoxColors() get() = defaultItemBoxColors()
@@ -179,7 +224,7 @@ object ItemBox {
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun defaultItemBoxColors() = ItemBoxColors( private fun defaultItemBoxColors() = ItemBoxColors(
backgroundColor = AreaBox.color, backgroundColor = AreaBoxDefaults.color,
titleTextColor = LocalColors.current.textPrimary, titleTextColor = LocalColors.current.textPrimary,
subTextColor = LocalColors.current.textSecondary, subTextColor = LocalColors.current.textSecondary,
arrowIconTint = LocalColors.current.textSecondary arrowIconTint = LocalColors.current.textSecondary
@@ -188,7 +233,7 @@ private fun defaultItemBoxColors() = ItemBoxColors(
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun defaultItemBoxStyle() = ItemBoxStyle( private fun defaultItemBoxStyle() = ItemBoxStyle(
boxStyle = AreaBox.style, boxStyle = AreaBoxDefaults.style,
contentSpacing = LocalSizes.current.spacingSecondary, contentSpacing = LocalSizes.current.spacingSecondary,
titleTextStyle = LocalTypography.current.primary, titleTextStyle = LocalTypography.current.primary,
subTextStyle = LocalTypography.current.secondary subTextStyle = LocalTypography.current.secondary

View File

@@ -60,9 +60,16 @@ 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.LocalColors
import com.highcapable.flexiui.LocalSizes import com.highcapable.flexiui.LocalSizes
import com.highcapable.flexiui.interaction.Interaction import com.highcapable.flexiui.interaction.InteractionDefaults
import com.highcapable.flexiui.interaction.rippleClickable import com.highcapable.flexiui.interaction.rippleClickable
/**
* Colors defines for navigation bar.
* @param backgroundColor the background color.
* @param indicatorColor the indicator color.
* @param selectedContentColor the selected content color.
* @param unselectedContentColor the unselected content color.
*/
@Immutable @Immutable
data class NavigationBarColors( data class NavigationBarColors(
val backgroundColor: Color, val backgroundColor: Color,
@@ -71,6 +78,13 @@ data class NavigationBarColors(
val unselectedContentColor: Color val unselectedContentColor: Color
) )
/**
* Style defines for navigation bar.
* @param boxStyle the style of area box.
* @param contentSpacing the spacing between the components of content.
* @param contentPadding the padding of content.
* @param contentShape the content shape.
*/
@Immutable @Immutable
data class NavigationBarStyle( data class NavigationBarStyle(
val boxStyle: AreaBoxStyle, val boxStyle: AreaBoxStyle,
@@ -79,11 +93,21 @@ data class NavigationBarStyle(
val contentShape: Shape val contentShape: Shape
) )
/**
* Flexi UI horizontal navigation bar.
* @see NavigationBarColumn
* @see NavigationBarItem
* @param modifier the [Modifier] to be applied to this navigation bar.
* @param colors the colors of this navigation bar, default is [NavigationBarDefaults.colors].
* @param style the style of this navigation bar, default is [NavigationBarDefaults.style].
* @param arrangement the horizontal arrangement of this navigation bar, default is [Arrangement.SpaceBetween].
* @param content the content of the [NavigationBarRow], should typically be [NavigationBarItem].
*/
@Composable @Composable
fun NavigationRow( fun NavigationBarRow(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: NavigationBarColors = NavigationBar.colors, colors: NavigationBarColors = NavigationBarDefaults.colors,
style: NavigationBarStyle = NavigationBar.style, style: NavigationBarStyle = NavigationBarDefaults.style,
arrangement: Arrangement.Horizontal = Arrangement.SpaceBetween, arrangement: Arrangement.Horizontal = Arrangement.SpaceBetween,
content: @Composable RowScope.() -> Unit content: @Composable RowScope.() -> Unit
) { ) {
@@ -99,11 +123,21 @@ fun NavigationRow(
} }
} }
/**
* Flexi UI vertical navigation bar.
* @see NavigationBarRow
* @see NavigationBarItem
* @param modifier the [Modifier] to be applied to this navigation bar.
* @param colors the colors of this navigation bar, default is [NavigationBarDefaults.colors].
* @param style the style of this navigation bar, default is [NavigationBarDefaults.style].
* @param arrangement the vertical arrangement of this navigation bar, default is [Arrangement.SpaceBetween].
* @param content the content of the [NavigationBarColumn], should typically be [NavigationBarItem].
*/
@Composable @Composable
fun NavigationColumn( fun NavigationBarColumn(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: NavigationBarColors = NavigationBar.colors, colors: NavigationBarColors = NavigationBarDefaults.colors,
style: NavigationBarStyle = NavigationBar.style, style: NavigationBarStyle = NavigationBarDefaults.style,
arrangement: Arrangement.Vertical = Arrangement.SpaceBetween, arrangement: Arrangement.Vertical = Arrangement.SpaceBetween,
content: @Composable ColumnScope.() -> Unit content: @Composable ColumnScope.() -> Unit
) { ) {
@@ -119,6 +153,23 @@ fun NavigationColumn(
} }
} }
/**
* Flexi UI navigation bar item.
* @see NavigationBarRow
* @see NavigationBarColumn
* @param selected whether this item is selected.
* @param onClick the callback when item is clicked.
* @param horizontal whether this item is horizontal.
* @param modifier the [Modifier] to be applied to this item.
* @param enabled whether this item is enabled, default is true.
* @param colors the colors of this item.
* @param contentSpacing the spacing between the components of content of this item.
* @param contentPadding the padding of content of this item.
* @param contentShape the content shape of this item.
* @param interactionSource the interaction source of this item.
* @param icon the icon of the [NavigationBarItem], should typically be [Icon].
* @param text the text of the [NavigationBarItem], should typically be [Text].
*/
@Composable @Composable
fun NavigationBarItem( fun NavigationBarItem(
selected: Boolean, selected: Boolean,
@@ -135,10 +186,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 ?: NavigationBar.colors val currentColors = colors ?: LocalNavigationBarColors.current ?: NavigationBarDefaults.colors
val currentContentSpacing = contentSpacing.orNull() ?: LocalNavigationBarContentSpacing.current.orNull() ?: NavigationBar.style.contentSpacing val currentContentSpacing = contentSpacing.orNull()
val currentContentPadding = contentPadding ?: LocalNavigationBarContentPadding.current ?: NavigationBar.style.contentPadding ?: LocalNavigationBarContentSpacing.current.orNull()
val currentContentShape = contentShape ?: LocalNavigationBarContentShape.current ?: NavigationBar.style.contentShape ?: NavigationBarDefaults.style.contentSpacing
val currentContentPadding = contentPadding
?: LocalNavigationBarContentPadding.current
?: NavigationBarDefaults.style.contentPadding
val currentContentShape = contentShape
?: LocalNavigationBarContentShape.current
?: 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)
@@ -149,7 +206,7 @@ fun NavigationBarItem(
.then(modifier) .then(modifier)
.background(animatedIndicatorColor) .background(animatedIndicatorColor)
.rippleClickable( .rippleClickable(
rippleStyle = Interaction.rippleStyle.copy(color = currentColors.indicatorColor), rippleStyle = InteractionDefaults.rippleStyle.copy(color = currentColors.indicatorColor),
enabled = enabled, enabled = enabled,
role = Role.Tab, role = Role.Tab,
interactionSource = interactionSource, interactionSource = interactionSource,
@@ -214,7 +271,10 @@ private fun NavigationBarStyleBox(
} }
} }
object NavigationBar { /**
* Defaults of navigation bar.
*/
object NavigationBarDefaults {
val colors: NavigationBarColors val colors: NavigationBarColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
@@ -238,7 +298,7 @@ private val LocalNavigationBarContentShape = compositionLocalOf<Shape?> { null }
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun defaultNavigationBarColors() = NavigationBarColors( private fun defaultNavigationBarColors() = NavigationBarColors(
backgroundColor = AreaBox.color, backgroundColor = AreaBoxDefaults.color,
indicatorColor = LocalColors.current.themeTertiary, indicatorColor = LocalColors.current.themeTertiary,
selectedContentColor = LocalColors.current.themePrimary, selectedContentColor = LocalColors.current.themePrimary,
unselectedContentColor = LocalColors.current.textSecondary unselectedContentColor = LocalColors.current.textSecondary
@@ -247,7 +307,7 @@ private fun defaultNavigationBarColors() = NavigationBarColors(
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun defaultNavigationBarStyle() = NavigationBarStyle( private fun defaultNavigationBarStyle() = NavigationBarStyle(
boxStyle = AreaBox.style, boxStyle = AreaBoxDefaults.style,
contentSpacing = LocalSizes.current.spacingSecondary, contentSpacing = LocalSizes.current.spacingSecondary,
contentPadding = ComponentPadding( contentPadding = ComponentPadding(
horizontal = LocalSizes.current.spacingPrimary, horizontal = LocalSizes.current.spacingPrimary,

View File

@@ -58,17 +58,30 @@ 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.
*/
@Stable @Stable
interface ProgressIndicatorStyle { interface ProgressIndicatorStyle {
val strokeWidth: Dp val strokeWidth: Dp
val strokeCap: StrokeCap val strokeCap: StrokeCap
} }
/**
* Animation interface for progress indicator.
*/
@Stable @Stable
interface ProgressIndicatorAnimation { interface ProgressIndicatorAnimation {
val duration: Int val duration: Int
} }
/**
* Style defines for circular progress indicator.
* @param strokeWidth the stroke width of indicator.
* @param strokeCap the stroke cap of indicator.
* @param radius the radius of indicator.
* @param animation the animation of indicator.
*/
@Immutable @Immutable
data class CircularIndicatorStyle( data class CircularIndicatorStyle(
override val strokeWidth: Dp, override val strokeWidth: Dp,
@@ -77,6 +90,13 @@ data class CircularIndicatorStyle(
val animation: CircularIndicatorAnimation val animation: CircularIndicatorAnimation
) : ProgressIndicatorStyle ) : ProgressIndicatorStyle
/**
* Style defines for linear progress indicator.
* @param strokeWidth the stroke width of indicator.
* @param strokeCap the stroke cap of indicator.
* @param width the width of indicator.
* @param animation the animation of indicator.
*/
@Immutable @Immutable
data class LinearIndicatorStyle( data class LinearIndicatorStyle(
override val strokeWidth: Dp, override val strokeWidth: Dp,
@@ -85,6 +105,14 @@ data class LinearIndicatorStyle(
val animation: LinearIndicatorAnimation val animation: LinearIndicatorAnimation
) : ProgressIndicatorStyle ) : ProgressIndicatorStyle
/**
* Animation defines for circular progress indicator.
* @param duration the duration of animation.
* @param rotationsPerCycle the rotations per cycle of animation.
* @param startAngleOffset the start angle offset of animation.
* @param baseRotationAngle the base rotation angle of animation.
* @param jumpRotationAngle the jump rotation angle of animation.
*/
@Immutable @Immutable
data class CircularIndicatorAnimation( data class CircularIndicatorAnimation(
override val duration: Int, override val duration: Int,
@@ -94,6 +122,18 @@ data class CircularIndicatorAnimation(
val jumpRotationAngle: Float val jumpRotationAngle: Float
) : ProgressIndicatorAnimation ) : ProgressIndicatorAnimation
/**
* Animation defines for linear progress indicator.
* @param duration the duration of animation.
* @param firstLineHeadDuration the first line head duration of animation.
* @param firstLineTailDuration the first line tail duration of animation.
* @param secondLineHeadDuration the second line head duration of animation.
* @param secondLineTailDuration the second line tail duration of animation.
* @param firstLineHeadDelay the first line head delay of animation.
* @param firstLineTailDelay the first line tail delay of animation.
* @param secondLineHeadDelay the second line head delay of animation.
* @param secondLineTailDelay the second line tail delay of animation.
*/
@Immutable @Immutable
data class LinearIndicatorAnimation( data class LinearIndicatorAnimation(
override val duration: Int, override val duration: Int,
@@ -107,25 +147,42 @@ 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 @Immutable
data class ProgressIndicatorColors( data class ProgressIndicatorColors(
val foregroundColor: Color, val foregroundColor: Color,
val backgroundColor: Color val backgroundColor: Color
) )
/**
* Flexi UI circular progress indicator.
* @see LinearProgressIndicator
* @param modifier the [Modifier] to be applied to this indicator.
* @param progress the progress of indicator.
* @param min the min of indicator, default is 0f.
* @param max the max of indicator, default is 1f.
* @param indeterminate the indeterminate of indicator, default is false.
* @param colors the colors of indicator, default is [CircularIndicatorDefaults.colors].
* @param style the style of indicator, default is [CircularIndicatorDefaults.style].
*/
@Composable @Composable
fun CircularProgressIndicator( fun CircularProgressIndicator(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
progress: Float = -1f, progress: Float = -1f,
min: Float = 0f, min: Float = 0f,
max: Float = 100f, max: Float = 1f,
indeterminate: Boolean = progress < min, indeterminate: Boolean = progress < min,
colors: ProgressIndicatorColors = CircularProgressIndicator.colors, colors: ProgressIndicatorColors = CircularIndicatorDefaults.colors,
style: CircularIndicatorStyle = CircularProgressIndicator.style style: CircularIndicatorStyle = CircularIndicatorDefaults.style
) { ) {
val diameter = style.radius * 2 val diameter = style.radius * 2
val stroke = with(LocalDensity.current) { Stroke(width = style.strokeWidth.toPx(), cap = style.strokeCap) } val stroke = with(LocalDensity.current) { Stroke(width = style.strokeWidth.toPx(), cap = style.strokeCap) }
/** Build determinate progress indicator. */
@Composable @Composable
fun Determinate() { fun Determinate() {
val coercedProgress = progress.coerceIn(min, max) val coercedProgress = progress.coerceIn(min, max)
@@ -138,6 +195,7 @@ fun CircularProgressIndicator(
} }
} }
/** Build indeterminate progress indicator. */
@Composable @Composable
fun Indeterminate() { fun Indeterminate() {
val transition = rememberInfiniteTransition() val transition = rememberInfiniteTransition()
@@ -197,16 +255,28 @@ fun CircularProgressIndicator(
if (indeterminate) Indeterminate() else Determinate() if (indeterminate) Indeterminate() else Determinate()
} }
/**
* Flexi UI linear progress indicator.
* @see CircularProgressIndicator
* @param modifier the [Modifier] to be applied to this indicator.
* @param progress the progress of indicator.
* @param min the min of indicator, default is 0f.
* @param max the max of indicator, default is 1f.
* @param indeterminate the indeterminate of indicator, default is false.
* @param colors the colors of indicator, default is [LinearIndicatorDefaults.colors].
* @param style the style of indicator, default is [LinearIndicatorDefaults.style].
*/
@Composable @Composable
fun LinearProgressIndicator( fun LinearProgressIndicator(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
progress: Float = -1f, progress: Float = -1f,
min: Float = 0f, min: Float = 0f,
max: Float = 100f, max: Float = 1f,
indeterminate: Boolean = progress < min, indeterminate: Boolean = progress < min,
colors: ProgressIndicatorColors = LinearProgressIndicator.colors, colors: ProgressIndicatorColors = LinearIndicatorDefaults.colors,
style: LinearIndicatorStyle = LinearProgressIndicator.style style: LinearIndicatorStyle = LinearIndicatorDefaults.style
) { ) {
/** Build determinate progress indicator. */
@Composable @Composable
fun Determinate() { fun Determinate() {
val coercedProgress = progress.coerceIn(min, max) val coercedProgress = progress.coerceIn(min, max)
@@ -218,6 +288,7 @@ fun LinearProgressIndicator(
} }
} }
/** Build indeterminate progress indicator. */
@Composable @Composable
fun Indeterminate() { fun Indeterminate() {
val infiniteTransition = rememberInfiniteTransition() val infiniteTransition = rememberInfiniteTransition()
@@ -347,7 +418,10 @@ private fun DrawScope.drawLinearIndicator(
} }
} }
object CircularProgressIndicator { /**
* Defaults of circular progress indicator.
*/
object CircularIndicatorDefaults {
val colors: ProgressIndicatorColors val colors: ProgressIndicatorColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
@@ -363,7 +437,10 @@ object CircularProgressIndicator {
get() = defaultCircularIndicatorStyle() get() = defaultCircularIndicatorStyle()
} }
object LinearProgressIndicator { /**
* Defaults of linear progress indicator.
*/
object LinearIndicatorDefaults {
val colors: ProgressIndicatorColors val colors: ProgressIndicatorColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable

View File

@@ -58,6 +58,12 @@ import com.highcapable.flexiui.LocalColors
import com.highcapable.flexiui.LocalShapes import com.highcapable.flexiui.LocalShapes
import com.highcapable.flexiui.LocalSizes import com.highcapable.flexiui.LocalSizes
/**
* Colors defines for radio button.
* @param contentColor the color of the check mark.
* @param inactiveColor the color of the unchecked box.
* @param activeColor the color of the checked box.
*/
@Immutable @Immutable
data class RadioButtonColors( data class RadioButtonColors(
val contentColor: Color, val contentColor: Color,
@@ -65,6 +71,16 @@ data class RadioButtonColors(
val activeColor: Color val activeColor: Color
) )
/**
* Style defines for radio button.
* @param contentSpacing the spacing between the check mark and the content.
* @param contentRadius the radius of the check mark.
* @param strokeRadius the radius of the box.
* @param pressedGain the gain when pressed.
* @param hoveredGain the gain when hovered.
* @param shape the shape.
* @param border the border stroke.
*/
@Immutable @Immutable
data class RadioButtonStyle( data class RadioButtonStyle(
val contentSpacing: Dp, val contentSpacing: Dp,
@@ -77,13 +93,24 @@ data class RadioButtonStyle(
val border: BorderStroke val border: BorderStroke
) )
/**
* Flexi UI radio button.
* @param selected the selected state of this button.
* @param onClick the callback when button is clicked.
* @param modifier the [Modifier] to be applied to this button.
* @param colors the colors of this button, default is [RadioButtonDefaults.colors].
* @param style the style of this button, default is [RadioButtonDefaults.style].
* @param enabled whether this button is enabled, default is true.
* @param interactionSource the interaction source of this button.
* @param content the content of the [RowScope] to be applied to the [RadioButton], should typically be [Text].
*/
@Composable @Composable
fun RadioButton( fun RadioButton(
selected: Boolean, selected: Boolean,
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: RadioButtonColors = RadioButton.colors, colors: RadioButtonColors = RadioButtonDefaults.colors,
style: RadioButtonStyle = RadioButton.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
@@ -126,7 +153,10 @@ fun RadioButton(
} }
} }
object RadioButton { /**
* Defaults of radio button.
*/
object RadioButtonDefaults {
val colors: RadioButtonColors val colors: RadioButtonColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable

View File

@@ -34,19 +34,42 @@ import androidx.compose.ui.Modifier
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
// TODO: re-made it by SubcomposeLayout.
/**
* Scaffold implements the basic Flexi UI visual layout structure.
*
* You can add your own components into [actionBar], [tab], [navigationBar].
* @see TopActionBar
* @see ActionBar
* @see TabRow
* @see NavigationBarRow
* @see NavigationBarColumn
* @param modifier the [Modifier] to be applied to content.
* @param colors the colors of content, default is [SurfaceDefaults.colors].
* @param padding the padding of content, default is [SurfaceDefaults.padding].
* @param verticalArrangement the vertical arrangement of content, default is [Arrangement.Top].
* @param horizontalAlignment the horizontal alignment of content, default is [Alignment.Start].
* @param actionBar the action bar, should typically be [TopActionBar] or [ActionBar].
* @param tab the tab, should typically be [TabRow].
* @param navigationBar the navigation bar, should typically be [NavigationBarRow] or [NavigationBarColumn].
* @param content the content of the screen.
*/
@Composable @Composable
fun Scaffold( fun Scaffold(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: SurfaceColors = Surface.colors, colors: SurfaceColors = SurfaceDefaults.colors,
padding: ComponentPadding = Surface.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,
actionBar: @Composable () -> Unit = {}, actionBar: @Composable () -> Unit = {},
tab: @Composable () -> Unit = {}, tab: @Composable () -> Unit = {},
navigation: @Composable () -> Unit = {}, navigationBar: @Composable () -> Unit = {},
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
// When out of the box, we no need to match the top padding, it should be provided by the action bar.
val outBoxPadding = padding.copy(top = 0.dp) val outBoxPadding = padding.copy(top = 0.dp)
// When in the box, we no need to match the start and end padding, it should be provided by the surface.
val inBoxPadding = padding.copy(start = 0.dp, end = 0.dp) val inBoxPadding = padding.copy(start = 0.dp, end = 0.dp)
Surface( Surface(
modifier = modifier, modifier = modifier,
@@ -60,7 +83,7 @@ fun Scaffold(
actionBar() actionBar()
tab() tab()
Box(modifier = Modifier.fillMaxSize().padding(inBoxPadding).weight(1f)) { content() } Box(modifier = Modifier.fillMaxSize().padding(inBoxPadding).weight(1f)) { content() }
navigation() navigationBar()
} }
} }
} }

View File

@@ -69,6 +69,13 @@ import com.highcapable.flexiui.LocalSizes
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.roundToInt import kotlin.math.roundToInt
/**
* Colors defines for slider.
* @param trackInactiveColor the inactive color of track.
* @param trackActiveColor the active color of track.
* @param thumbColor the color of thumb.
* @param stepColor the color of step.
*/
@Immutable @Immutable
data class SliderColors( data class SliderColors(
val trackInactiveColor: Color, val trackInactiveColor: Color,
@@ -77,6 +84,20 @@ data class SliderColors(
val stepColor: Color val stepColor: Color
) )
/**
* Style defines for slider.
* @param thumbRadius the radius of thumb.
* @param thumbGain the gain of thumb.
* @param thumbShadowSize the shadow size of thumb.
* @param thumbShape the shape of thumb.
* @param stepShape the shape of step.
* @param trackShape the shape of track.
* @param thumbBorder the border of thumb.
* @param stepBorder the border of step.
* @param trackBorder the border of track.
* @param trackWidth the width of track.
* @param trackHeight the height of track.
*/
@Immutable @Immutable
data class SliderStyle( data class SliderStyle(
val thumbRadius: Dp, val thumbRadius: Dp,
@@ -92,16 +113,30 @@ data class SliderStyle(
val trackHeight: Dp val trackHeight: Dp
) )
/**
* Flexi UI slider.
* @param value the value of this slider.
* @param onValueChange the callback when the value changed.
* @param modifier the [Modifier] to be applied to this slider.
* @param colors the colors of this slider, default is [SliderDefaults.colors].
* @param style the style of this slider, default is [SliderDefaults.style].
* @param enabled whether this slider is enabled, default is true.
* @param min the min value of this slider, default is 0f.
* @param max the max value of this slider, default is 1f.
* @param steps the steps of this slider, default is 0.
* @param onValueChangeFinished the callback when the value changed finished.
* @param interactionSource the interaction source of this slider.
*/
@Composable @Composable
fun Slider( fun Slider(
value: Float, value: Float,
onValueChange: (Float) -> Unit, onValueChange: (Float) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: SliderColors = Slider.colors, colors: SliderColors = SliderDefaults.colors,
style: SliderStyle = Slider.style, style: SliderStyle = SliderDefaults.style,
enabled: Boolean = true, enabled: Boolean = true,
min: Float = 0f, min: Float = 0f,
max: Float = 100f, max: Float = 1f,
steps: Int = 0, steps: Int = 0,
onValueChangeFinished: (() -> Unit)? = null, onValueChangeFinished: (() -> Unit)? = null,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
@@ -119,6 +154,7 @@ fun Slider(
steppedOffsetXs = List(steps + 2) { index -> index * pOffsetX } steppedOffsetXs = List(steps + 2) { index -> index * pOffsetX }
} }
/** Get the stepped offset X. */
fun Float.withSteps() = fun Float.withSteps() =
if (steps > 0) if (steps > 0)
steppedOffsetXs.minByOrNull { abs(it - this) } ?: this steppedOffsetXs.minByOrNull { abs(it - this) } ?: this
@@ -131,11 +167,13 @@ fun Slider(
val animatedOffsetX by animateFloatAsState(offsetX) val animatedOffsetX by animateFloatAsState(offsetX)
val adoptedOffsetX = if (tapped && !dragging) animatedOffsetX else offsetX val adoptedOffsetX = if (tapped && !dragging) animatedOffsetX else offsetX
/** Update the value of slider. */
fun updateValue(offsetX: Float) { fun updateValue(offsetX: Float) {
val newValue = (offsetX / maxOffsetX) * (max - min) + min val newValue = (offsetX / maxOffsetX) * (max - min) + min
onValueChange(newValue) onValueChange(newValue)
} }
/** Build the track of slider. */
@Composable @Composable
fun Track(content: @Composable () -> Unit) { fun Track(content: @Composable () -> Unit) {
val cornerSize = (style.trackShape as? CornerBasedShape)?.topStart?.toPx(Size.Zero, LocalDensity.current) ?: 0f val cornerSize = (style.trackShape as? CornerBasedShape)?.topStart?.toPx(Size.Zero, LocalDensity.current) ?: 0f
@@ -156,6 +194,7 @@ fun Slider(
} }
} }
/** Build the step of slider. */
@Composable @Composable
fun Step() { fun Step() {
if (steps > 0) Row( if (steps > 0) Row(
@@ -172,6 +211,7 @@ fun Slider(
} }
} }
/** Build the thumb of slider. */
@Composable @Composable
fun Thumb() { fun Thumb() {
Box( Box(
@@ -231,7 +271,10 @@ fun Slider(
} }
} }
object Slider { /**
* Defaults of slider.
*/
object SliderDefaults {
val colors val colors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable

View File

@@ -40,18 +40,33 @@ import com.highcapable.betterandroid.compose.extension.ui.ComponentPadding
import com.highcapable.flexiui.LocalColors import com.highcapable.flexiui.LocalColors
import com.highcapable.flexiui.LocalSizes import com.highcapable.flexiui.LocalSizes
/**
* Colors defines for surface.
* @param contentColor the content color, usually for the text color.
* @param backgroundColor the background color.
*/
@Immutable @Immutable
data class SurfaceColors( data class SurfaceColors(
val contentColor: Color, val contentColor: Color,
val backgroundColor: Color val backgroundColor: Color
) )
/**
* Flexi UI surface.
*
* A surface have a background color and default padding.
* @param modifier the [Modifier] to be applied to this surface.
* @param initializer the [Modifier] initializer, earlies than [modifier].
* @param colors the colors of surface, default is [SurfaceDefaults.colors].
* @param padding the padding of surface, default is [SurfaceDefaults.padding].
* @param content the content of the [Surface].
*/
@Composable @Composable
fun Surface( fun Surface(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
initializer: @Composable Modifier.() -> Modifier = { Modifier }, initializer: @Composable Modifier.() -> Modifier = { Modifier },
colors: SurfaceColors = Surface.colors, colors: SurfaceColors = SurfaceDefaults.colors,
padding: ComponentPadding = Surface.padding, padding: ComponentPadding = SurfaceDefaults.padding,
content: @Composable BoxScope.() -> Unit content: @Composable BoxScope.() -> Unit
) { ) {
CompositionLocalProvider( CompositionLocalProvider(
@@ -81,7 +96,10 @@ private fun Modifier.surface(
.padding(padding) .padding(padding)
} }
object Surface { /**
* Defaults of surface.
*/
object SurfaceDefaults {
val colors: SurfaceColors val colors: SurfaceColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable

View File

@@ -67,6 +67,12 @@ import com.highcapable.flexiui.LocalShapes
import com.highcapable.flexiui.LocalSizes import com.highcapable.flexiui.LocalSizes
import kotlin.math.roundToInt import kotlin.math.roundToInt
/**
* Colors defines for switch.
* @param thumbColor the color of thumb.
* @param trackInactive the color of track when switch is inactive.
* @param trackActive the color of track when switch is active.
*/
@Immutable @Immutable
data class SwitchColors( data class SwitchColors(
val thumbColor: Color, val thumbColor: Color,
@@ -74,6 +80,20 @@ data class SwitchColors(
val trackActive: Color val trackActive: Color
) )
/**
* Style defines for switch.
* @param padding the padding between thumb and track.
* @param contentSpacing the spacing between content and track.
* @param thumbRadius the radius of thumb.
* @param thumbGain the gain of thumb when switch is hovered or dragging.
* @param thumbShadowSize the shadow size of thumb.
* @param thumbShape the shape of thumb.
* @param trackShape the shape of track.
* @param thumbBorder the border of thumb.
* @param trackBorder the border of track.
* @param trackWidth the width of track.
* @param trackHeight the height of track.
*/
@Immutable @Immutable
data class SwitchStyle( data class SwitchStyle(
val padding: ComponentPadding, val padding: ComponentPadding,
@@ -89,13 +109,24 @@ data class SwitchStyle(
val trackHeight: Dp val trackHeight: Dp
) )
/**
* Flexi UI switch.
* @param checked the checked state of switch.
* @param onCheckedChange the callback when switch checked state changed.
* @param modifier the [Modifier] to be applied to this switch.
* @param colors the colors of switch, default is [SwitchDefaults.colors].
* @param style the style of switch, default is [SwitchDefaults.style].
* @param enabled the enabled state of switch, default is true.
* @param interactionSource the interaction source of switch.
* @param content the content of the [RowScope] to be applied to the [Switch], should typically be [Text].
*/
@Composable @Composable
fun Switch( fun Switch(
checked: Boolean, checked: Boolean,
onCheckedChange: (Boolean) -> Unit, onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: SwitchColors = Switch.colors, colors: SwitchColors = SwitchDefaults.colors,
style: SwitchStyle = Switch.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
@@ -112,6 +143,8 @@ fun Switch(
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.trackInactive) }
/** 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.trackInactive, colors.trackActive, fraction)
@@ -120,6 +153,7 @@ fun Switch(
val animatedTrackColor by animateColorAsState(trackColor) val animatedTrackColor by animateColorAsState(trackColor)
val efficientDragging = dragging && distance > 5 val efficientDragging = dragging && distance > 5
/** Build the track of switch. */
@Composable @Composable
fun Track(content: @Composable RowScope.() -> Unit) { fun Track(content: @Composable RowScope.() -> Unit) {
Row( Row(
@@ -140,6 +174,7 @@ fun Switch(
) )
} }
/** Build the thumb of switch. */
@Composable @Composable
fun Thumb() { fun Thumb() {
Box( Box(
@@ -193,7 +228,10 @@ fun Switch(
} }
} }
object Switch { /**
* Defaults of switch.
*/
object SwitchDefaults {
val colors: SwitchColors val colors: SwitchColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable

View File

@@ -80,6 +80,12 @@ import com.highcapable.flexiui.interaction.rippleClickable
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
/**
* Colors defines for tab.
* @param indicatorColor the indicator color.
* @param selectedContentColor the selected content color.
* @param unselectedContentColor the unselected content color.
*/
@Immutable @Immutable
data class TabColors( data class TabColors(
val indicatorColor: Color, val indicatorColor: Color,
@@ -87,6 +93,14 @@ data class TabColors(
val unselectedContentColor: Color val unselectedContentColor: Color
) )
/**
* Style defines for tab.
* @param contentPadding the content padding.
* @param contentShape the content shape.
* @param indicatorWidth the indicator width.
* @param indicatorHeight the indicator height.
* @param indicatorShape the indicator shape.
*/
@Immutable @Immutable
data class TabStyle( data class TabStyle(
val contentPadding: ComponentPadding, val contentPadding: ComponentPadding,
@@ -96,12 +110,23 @@ data class TabStyle(
val indicatorShape: Shape val indicatorShape: Shape
) )
/**
* Flexi UI fixed tabs.
* @see ScrollableTabRow
* @see Tab
* @param selectedTabIndex the selected tab index.
* @param modifier the [Modifier] to be applied to tabs.
* @param colors the colors of tabs, default is [TabDefaults.colors].
* @param style the style of tabs, default is [TabDefaults.style].
* @param indicator the indicator of the [TabRow], see [TabRowScope.TabIndicator].
* @param tabs the tabs of the [TabRow], should typically be [Tab].
*/
@Composable @Composable
fun TabRow( fun TabRow(
selectedTabIndex: Int = 0, selectedTabIndex: Int = 0,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: TabColors = Tab.colors, colors: TabColors = TabDefaults.colors,
style: TabStyle = Tab.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
) { ) {
@@ -136,12 +161,24 @@ fun TabRow(
} }
} }
/**
* Flexi UI scrollable tabs.
* @see TabRow
* @see Tab
* @param selectedTabIndex the selected tab index.
* @param modifier the [Modifier] to be applied to tabs.
* @param colors the colors of tabs, default is [TabDefaults.colors].
* @param style the style of tabs, default is [TabDefaults.style].
* @param scrollState the scroll state of tabs.
* @param indicator the indicator of the [ScrollableTabRow], see [TabRowScope.TabIndicator].
* @param tabs the tabs of the [ScrollableTabRow], should typically be [Tab].
*/
@Composable @Composable
fun ScrollableTabRow( fun ScrollableTabRow(
selectedTabIndex: Int = 0, selectedTabIndex: Int = 0,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: TabColors = Tab.colors, colors: TabColors = TabDefaults.colors,
style: TabStyle = Tab.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
@@ -190,6 +227,21 @@ fun ScrollableTabRow(
} }
} }
/**
* Flexi UI tab.
* @see TabRow
* @see ScrollableTabRow
* @param selected whether this tab is selected.
* @param onClick the callback when this tab is clicked.
* @param modifier the [Modifier] to be applied to this tab.
* @param selectedContentColor the selected content color.
* @param unselectedContentColor the unselected content color.
* @param contentPadding the content padding.
* @param contentShape the content shape.
* @param enabled whether this tab is enabled.
* @param interactionSource the interaction source.
* @param content the content of the [Tab], should typically be [Icon] or [Text].
*/
@Composable @Composable
fun Tab( fun Tab(
selected: Boolean, selected: Boolean,
@@ -204,11 +256,11 @@ fun Tab(
content: @Composable RowScope.() -> Unit content: @Composable RowScope.() -> Unit
) { ) {
val currentSelectedContentColor = selectedContentColor.orNull() val currentSelectedContentColor = selectedContentColor.orNull()
?: LocalTabSelectedContentColor.current.orNull() ?: Tab.colors.selectedContentColor ?: LocalTabSelectedContentColor.current.orNull() ?: TabDefaults.colors.selectedContentColor
val currentUnselectedContentColor = unselectedContentColor.orNull() val currentUnselectedContentColor = unselectedContentColor.orNull()
?: LocalTabUnselectedContentColor.current.orNull() ?: Tab.colors.unselectedContentColor ?: LocalTabUnselectedContentColor.current.orNull() ?: TabDefaults.colors.unselectedContentColor
val currentContentPadding = contentPadding ?: LocalTabContentPadding.current ?: Tab.style.contentPadding val currentContentPadding = contentPadding ?: LocalTabContentPadding.current ?: TabDefaults.style.contentPadding
val currentContentShape = contentShape ?: LocalTabContentShape.current ?: Tab.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)
@@ -257,17 +309,41 @@ private fun rememberScrollableTabData(scrollState: ScrollState): ScrollableTabDa
return remember(scrollState, coroutineScope) { ScrollableTabData(scrollState, coroutineScope) } return remember(scrollState, coroutineScope) { ScrollableTabData(scrollState, coroutineScope) }
} }
/**
* Represents the position of a tab.
* @param left the left position.
* @param width the indicator width.
* @param tabWidth the tab width.
*/
@Immutable @Immutable
data class TabPosition(val left: Dp, val width: Dp, val tabWidth: Dp) { data class TabPosition(val left: Dp, val width: Dp, val tabWidth: Dp) {
/**
* Calculates the right of the tab.
* @return [Dp]
*/
val right get() = left + width val right get() = left + width
/**
* Calculates the center of the tab.
* @param currentWidth the current indicator width.
*/
fun calculateCenter(currentWidth: Dp) = left + width / 2 - currentWidth / 2 fun calculateCenter(currentWidth: Dp) = left + width / 2 - currentWidth / 2
} }
/**
* A scope for tab.
*/
@Stable @Stable
interface TabRowScope { interface TabRowScope {
/**
* Tab indicator.
* @param modifier the [Modifier] to be applied to this tab indicator.
* @param color the color of this tab indicator.
* @param height the height of this tab indicator.
* @param shape the shape of this tab indicator.
*/
@Composable @Composable
fun TabIndicator( fun TabIndicator(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -278,6 +354,11 @@ interface TabRowScope {
Box(modifier.height(height).background(color, shape)) Box(modifier.height(height).background(color, shape))
} }
/**
* [Modifier] that offsets the tab indicator.
* @param currentTabPosition the current tab position.
* @param indicatorWidth the indicator width.
*/
fun Modifier.tabIndicatorOffset( fun Modifier.tabIndicatorOffset(
currentTabPosition: TabPosition = impl.tabPositions[impl.selectedTabIndex], currentTabPosition: TabPosition = impl.tabPositions[impl.selectedTabIndex],
indicatorWidth: Dp = impl.style.indicatorWidth indicatorWidth: Dp = impl.style.indicatorWidth
@@ -303,6 +384,12 @@ interface TabRowScope {
.width(animatedWidh) .width(animatedWidh)
} }
/**
* [Modifier] that offsets the pager tab indicator.
* @param pagerState the pager state.
* @param tabPositions the tab positions.
* @param indicatorWidth the indicator width.
*/
fun Modifier.pagerTabIndicatorOffset( fun Modifier.pagerTabIndicatorOffset(
pagerState: PagerState, pagerState: PagerState,
tabPositions: List<TabPosition> = impl.tabPositions, tabPositions: List<TabPosition> = impl.tabPositions,
@@ -415,7 +502,10 @@ private class ScrollableTabData(private val scrollState: ScrollState, private va
@Stable @Stable
private enum class TabSlots { Tabs, TabsAverage, Indicator } private enum class TabSlots { Tabs, TabsAverage, Indicator }
object Tab { /**
* Defaults of tab.
*/
object TabDefaults {
val colors: TabColors val colors: TabColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable

View File

@@ -26,6 +26,7 @@ package com.highcapable.flexiui.component
import androidx.compose.foundation.text.BasicText 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.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -38,12 +39,26 @@ import com.highcapable.betterandroid.compose.extension.ui.orNull
import com.highcapable.flexiui.DefaultTypography import com.highcapable.flexiui.DefaultTypography
import com.highcapable.flexiui.LocalColors import com.highcapable.flexiui.LocalColors
/**
* Flexi UI basic text.
* @see BasicText
* @param text the text to be displayed.
* @param modifier the [Modifier] to be applied to this text.
* @param color the color of the text.
* @param style the style of the text.
* @param singleLine whether the text should be displayed on a single line, default is false.
* @param maxLines the maximum number of lines to display, when [singleLine] is false default is [Int.MAX_VALUE].
* @param minLines the minimum number of lines to display, default is 1.
* @param overflow the overflow strategy for displaying the text, default is [TextOverflow.Ellipsis].
* @param softWrap whether the text should break at soft line breaks.
* @param onTextLayout the callback to be invoked when the text layout is ready.
*/
@Composable @Composable
fun Text( fun Text(
text: String, text: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
color: Color = Color.Unspecified, color: Color = Color.Unspecified,
style: TextStyle = Text.style, style: TextStyle? = null,
singleLine: Boolean = false, singleLine: Boolean = false,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
minLines: Int = 1, minLines: Int = 1,
@@ -65,12 +80,27 @@ fun Text(
) )
} }
/**
* Flexi UI basic text.
* @see BasicText
* @param text the text to be displayed.
* @param modifier the [Modifier] to be applied to this text.
* @param color the color of the text.
* @param style the style of the text.
* @param singleLine whether the text should be displayed on a single line, default is false.
* @param maxLines the maximum number of lines to display, when [singleLine] is false default is [Int.MAX_VALUE].
* @param minLines the minimum number of lines to display, default is 1.
* @param overflow the overflow strategy for displaying the text, default is [TextOverflow.Ellipsis].
* @param softWrap whether the text should break at soft line breaks.
* @param inlineContent map of tags to [InlineTextContent]s that can be used to add composable content to the text.
* @param onTextLayout the callback to be invoked when the text layout is ready.
*/
@Composable @Composable
fun Text( fun Text(
text: AnnotatedString, text: AnnotatedString,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
color: Color = Color.Unspecified, color: Color = Color.Unspecified,
style: TextStyle = Text.style, style: TextStyle? = null,
singleLine: Boolean = false, singleLine: Boolean = false,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
minLines: Int = 1, minLines: Int = 1,
@@ -79,11 +109,12 @@ fun Text(
inlineContent: Map<String, InlineTextContent> = mapOf(), inlineContent: Map<String, InlineTextContent> = mapOf(),
onTextLayout: (TextLayoutResult) -> Unit = {} onTextLayout: (TextLayoutResult) -> Unit = {}
) { ) {
val currentColor = color.orNull() ?: style.color.orNull() ?: Text.color val currentStyle = style ?: LocalTextStyle.current
val currentColor = color.orNull() ?: currentStyle.color.orNull() ?: defaultTextColor()
BasicText( BasicText(
text = text, text = text,
modifier = modifier, modifier = modifier,
style = style.copy(color = currentColor), style = currentStyle.copy(color = currentColor),
onTextLayout = onTextLayout, onTextLayout = onTextLayout,
overflow = overflow, overflow = overflow,
softWrap = softWrap, softWrap = softWrap,
@@ -93,21 +124,28 @@ fun Text(
) )
} }
object Text { /**
val color: Color * CompositionLocal containing the preferred [TextStyle]
@Composable * that will be used by text by default.
@ReadOnlyComposable */
get() = defaultTextColor() val LocalTextStyle = compositionLocalOf { DefaultTextStyle }
val style: TextStyle
@Composable /**
@ReadOnlyComposable * This function is used to set the current value of [LocalTextStyle], merging the given style
get() = LocalTextStyle.current * with the current style values for any missing attributes. Any [Text] components included in
* this component's [content] will be styled with this style unless styled explicitly.
* @see LocalTextStyle
* @param value the merged text style to set.
* @param content the composable content.
*/
@Composable
fun ProvideTextStyle(value: TextStyle, content: @Composable () -> Unit) {
val mergedStyle = LocalTextStyle.current.merge(value)
CompositionLocalProvider(LocalTextStyle provides mergedStyle, content = content)
} }
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
internal fun defaultTextColor() = LocalColors.current.textPrimary internal fun defaultTextColor() = LocalColors.current.textPrimary
internal val LocalTextStyle = compositionLocalOf { DefaultTextStyle }
private val DefaultTextStyle = DefaultTypography.primary private val DefaultTextStyle = DefaultTypography.primary

View File

@@ -97,6 +97,19 @@ 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
/**
* Colors defines for text field.
* @param textColor the text color.
* @param cursorColor the cursor color.
* @param selectionColors the selection colors.
* @param completionColors the completion colors.
* @param placeholderContentColor the placeholder content color, usually for text color.
* @param decorInactiveTint the decoration inactive tint.
* @param decorActiveTint the decoration active tint.
* @param borderInactiveColor the border inactive color.
* @param borderActiveColor the border active color.
* @param backgroundColor the background color.
*/
@Immutable @Immutable
data class TextFieldColors( data class TextFieldColors(
val textColor: Color, val textColor: Color,
@@ -111,12 +124,26 @@ data class TextFieldColors(
val backgroundColor: Color val backgroundColor: Color
) )
/**
* Colors defines for auto complete box.
* @param highlightContentColor the highlight content color, usually for text color.
* @param menuColors the dropdown menu colors.
*/
@Immutable @Immutable
data class AutoCompleteBoxColors( data class AutoCompleteBoxColors(
val highlightContentColor: Color, val highlightContentColor: Color,
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 @Immutable
data class TextFieldStyle( data class TextFieldStyle(
val textStyle: TextStyle, val textStyle: TextStyle,
@@ -127,6 +154,13 @@ data class TextFieldStyle(
val completionStyle: DropdownMenuStyle val completionStyle: DropdownMenuStyle
) )
/**
* Options defines for auto complete.
* @param checkCase whether to check case, default is true.
* @param checkStartSpace whether to check start space, default is true.
* @param checkEndSpace whether to check end space, default is true.
* @param threshold the threshold, default is 2.
*/
@Immutable @Immutable
data class AutoCompleteOptions( data class AutoCompleteOptions(
val checkCase: Boolean = true, val checkCase: Boolean = true,
@@ -135,14 +169,41 @@ data class AutoCompleteOptions(
val threshold: Int = 2 val threshold: Int = 2
) )
/**
* Flexi UI text field.
* @see TextField
* @see PasswordTextField
* @see BackspaceTextField
* @param value the text field value.
* @param onValueChange the text field value change callback.
* @param completionValues the auto complete values, when you want to use auto complete.
* @param modifier the [Modifier] to be applied to this text field.
* @param colors the colors of this text field, default is [TextFieldDefaults.colors].
* @param style the style of this text field, default is [TextFieldDefaults.style].
* @param enabled whether this text field is enabled, default is true.
* @param readOnly whether this text field is read only, default is false.
* @param completionOptions the auto complete options.
* @param keyboardOptions the keyboard options, default is [KeyboardOptions.Default].
* @param keyboardActions the keyboard actions, default is [KeyboardActions.Default].
* @param singleLine whether this text field is single line, default is false.
* @param maxLines the max lines of this text field, when [singleLine] is false default is [Int.MAX_VALUE].
* @param minLines the min lines of this text field, default is 1.
* @param visualTransformation the visual transformation, default is [VisualTransformation.None].
* @param onTextLayout the callback to be invoked when the text layout is ready.
* @param focusRequester the focus requester of this text field.
* @param interactionSource the interaction source of this text field.
* @param header the header of the [TextField], should typically be [Icon] or [Text].
* @param placeholder the placeholder of the [TextField], should typically be [Icon] or [Text].
* @param footer the footer of the [TextField], should typically be [Icon] or [Text].
*/
@Composable @Composable
fun TextField( fun TextField(
value: TextFieldValue, value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit, onValueChange: (TextFieldValue) -> Unit,
completionValues: List<String> = emptyList(), completionValues: List<String> = emptyList(),
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: TextFieldColors = TextField.colors, colors: TextFieldColors = TextFieldDefaults.colors,
style: TextFieldStyle = TextField.style, style: TextFieldStyle = TextFieldDefaults.style,
enabled: Boolean = true, enabled: Boolean = true,
readOnly: Boolean = false, readOnly: Boolean = false,
completionOptions: AutoCompleteOptions = AutoCompleteOptions(), completionOptions: AutoCompleteOptions = AutoCompleteOptions(),
@@ -257,14 +318,41 @@ fun TextField(
} }
} }
/**
* Flexi UI text field.
* @see TextField
* @see PasswordTextField
* @see BackspaceTextField
* @param value the value of text.
* @param onValueChange the text field value change callback.
* @param completionValues the auto complete values, when you want to use auto complete.
* @param modifier the [Modifier] to be applied to this text field.
* @param colors the colors of this text field, default is [TextFieldDefaults.colors].
* @param style the style of this text field, default is [TextFieldDefaults.style].
* @param enabled whether this text field is enabled, default is true.
* @param readOnly whether this text field is read only, default is false.
* @param completionOptions the auto complete options.
* @param keyboardOptions the keyboard options, default is [KeyboardOptions.Default].
* @param keyboardActions the keyboard actions, default is [KeyboardActions.Default].
* @param singleLine whether this text field is single line, default is false.
* @param maxLines the max lines of this text field, when [singleLine] is false default is [Int.MAX_VALUE].
* @param minLines the min lines of this text field, default is 1.
* @param visualTransformation the visual transformation, default is [VisualTransformation.None].
* @param onTextLayout the callback to be invoked when the text layout is ready.
* @param focusRequester the focus requester of this text field.
* @param interactionSource the interaction source of this text field.
* @param header the header of the [TextField], should typically be [Icon] or [Text].
* @param placeholder the placeholder of the [TextField], should typically be [Icon] or [Text].
* @param footer the footer of the [TextField], should typically be [Icon] or [Text].
*/
@Composable @Composable
fun TextField( fun TextField(
value: String, value: String,
onValueChange: (String) -> Unit, onValueChange: (String) -> Unit,
completionValues: List<String> = emptyList(), completionValues: List<String> = emptyList(),
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: TextFieldColors = TextField.colors, colors: TextFieldColors = TextFieldDefaults.colors,
style: TextFieldStyle = TextField.style, style: TextFieldStyle = TextFieldDefaults.style,
enabled: Boolean = true, enabled: Boolean = true,
readOnly: Boolean = false, readOnly: Boolean = false,
completionOptions: AutoCompleteOptions = AutoCompleteOptions(), completionOptions: AutoCompleteOptions = AutoCompleteOptions(),
@@ -310,14 +398,37 @@ fun TextField(
) )
} }
/**
* Flexi UI password text field.
* @see TextField
* @see PasswordTextField
* @see BackspaceTextField
* @param value the text field value.
* @param onValueChange the text field value change callback.
* @param defaultPasswordVisible the default password visible, default is false.
* @param modifier the [Modifier] to be applied to this text field.
* @param colors the colors of this text field, default is [TextFieldDefaults.colors].
* @param style the style of this text field, default is [TextFieldDefaults.style].
* @param enabled whether this text field is enabled, default is true.
* @param readOnly whether this text field is read only, default is false.
* @param keyboardOptions the keyboard options, default is [KeyboardOptions.Default].
* @param keyboardActions the keyboard actions, default is [KeyboardActions.Default].
* @param normalVisualTransformation the normal visual transformation, default is [VisualTransformation.None].
* @param secretVisualTransformation the secret visual transformation, default is [PasswordVisualTransformation].
* @param onTextLayout the callback to be invoked when the text layout is ready.
* @param focusRequester the focus requester of this text field.
* @param interactionSource the interaction source of this text field.
* @param header the header of the [TextField], should typically be [Icon] or [Text].
* @param placeholder the placeholder of the [TextField], should typically be [Icon] or [Text].
*/
@Composable @Composable
fun PasswordTextField( fun PasswordTextField(
value: TextFieldValue, value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit, onValueChange: (TextFieldValue) -> Unit,
defaultPasswordVisible: Boolean = false, defaultPasswordVisible: Boolean = false,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: TextFieldColors = TextField.colors, colors: TextFieldColors = TextFieldDefaults.colors,
style: TextFieldStyle = TextField.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,
@@ -364,7 +475,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 = IconButton.style.copy(padding = TextDecorIconPadding), style = IconButtonDefaults.style.copy(padding = TextDecorIconPadding),
checked = passwordVisible, checked = passwordVisible,
onCheckedChange = { onCheckedChange = {
passwordVisible = it passwordVisible = it
@@ -378,14 +489,37 @@ fun PasswordTextField(
) )
} }
/**
* Flexi UI password text field.
* @see TextField
* @see PasswordTextField
* @see BackspaceTextField
* @param value the value of text.
* @param onValueChange the text field value change callback.
* @param defaultPasswordVisible the default password visible, default is false.
* @param modifier the [Modifier] to be applied to this text field.
* @param colors the colors of this text field, default is [TextFieldDefaults.colors].
* @param style the style of this text field, default is [TextFieldDefaults.style].
* @param enabled whether this text field is enabled, default is true.
* @param readOnly whether this text field is read only, default is false.
* @param keyboardOptions the keyboard options, default is [KeyboardOptions.Default].
* @param keyboardActions the keyboard actions, default is [KeyboardActions.Default].
* @param normalVisualTransformation the normal visual transformation, default is [VisualTransformation.None].
* @param secretVisualTransformation the secret visual transformation, default is [PasswordVisualTransformation].
* @param onTextLayout the callback to be invoked when the text layout is ready.
* @param focusRequester the focus requester of this text field.
* @param interactionSource the interaction source of this text field.
* @param header the header of the [TextField], should typically be [Icon] or [Text].
* @param placeholder the placeholder of the [TextField], should typically be [Icon] or [Text].
*/
@Composable @Composable
fun PasswordTextField( fun PasswordTextField(
value: String, value: String,
onValueChange: (String) -> Unit, onValueChange: (String) -> Unit,
defaultPasswordVisible: Boolean = false, defaultPasswordVisible: Boolean = false,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: TextFieldColors = TextField.colors, colors: TextFieldColors = TextFieldDefaults.colors,
style: TextFieldStyle = TextField.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,
@@ -423,14 +557,40 @@ fun PasswordTextField(
) )
} }
/**
* Flexi UI text field with backspace icon.
* @see TextField
* @see PasswordTextField
* @see BackspaceTextField
* @param value the text field value.
* @param onValueChange the text field value change callback.
* @param completionValues the auto complete values, when you want to use auto complete.
* @param modifier the [Modifier] to be applied to this text field.
* @param colors the colors of this text field, default is [TextFieldDefaults.colors].
* @param style the style of this text field, default is [TextFieldDefaults.style].
* @param enabled whether this text field is enabled, default is true.
* @param readOnly whether this text field is read only, default is false.
* @param completionOptions the auto complete options.
* @param keyboardOptions the keyboard options, default is [KeyboardOptions.Default].
* @param keyboardActions the keyboard actions, default is [KeyboardActions.Default].
* @param singleLine whether this text field is single line, default is false.
* @param maxLines the max lines of this text field, when [singleLine] is false default is [Int.MAX_VALUE].
* @param minLines the min lines of this text field, default is 1.
* @param visualTransformation the visual transformation, default is [VisualTransformation.None].
* @param onTextLayout the callback to be invoked when the text layout is ready.
* @param focusRequester the focus requester of this text field.
* @param interactionSource the interaction source of this text field.
* @param header the header of the [TextField], should typically be [Icon] or [Text].
* @param placeholder the placeholder of the [TextField], should typically be [Icon] or [Text].
*/
@Composable @Composable
fun BackspaceTextField( fun BackspaceTextField(
value: TextFieldValue, value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit, onValueChange: (TextFieldValue) -> Unit,
completionValues: List<String> = emptyList(), completionValues: List<String> = emptyList(),
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: TextFieldColors = TextField.colors, colors: TextFieldColors = TextFieldDefaults.colors,
style: TextFieldStyle = TextField.style, style: TextFieldStyle = TextFieldDefaults.style,
enabled: Boolean = true, enabled: Boolean = true,
readOnly: Boolean = false, readOnly: Boolean = false,
completionOptions: AutoCompleteOptions = AutoCompleteOptions(), completionOptions: AutoCompleteOptions = AutoCompleteOptions(),
@@ -487,7 +647,7 @@ fun BackspaceTextField(
focusRequester.requestFocus() focusRequester.requestFocus()
}, },
modifier = Modifier.width(animatedSize).pointerHoverState(TextFieldPointerState.Common), modifier = Modifier.width(animatedSize).pointerHoverState(TextFieldPointerState.Common),
style = IconButton.style.copy(padding = TextDecorIconPadding), style = IconButtonDefaults.style.copy(padding = TextDecorIconPadding),
enabled = enabled, enabled = enabled,
interactionSource = cInteractionSource interactionSource = cInteractionSource
) { Icon(imageVector = Icons.Backspace) } ) { Icon(imageVector = Icons.Backspace) }
@@ -496,14 +656,40 @@ fun BackspaceTextField(
) )
} }
/**
* Flexi UI text field with backspace icon.
* @see TextField
* @see PasswordTextField
* @see BackspaceTextField
* @param value the value of text.
* @param onValueChange the text field value change callback.
* @param completionValues the auto complete values, when you want to use auto complete.
* @param modifier the [Modifier] to be applied to this text field.
* @param colors the colors of this text field, default is [TextFieldDefaults.colors].
* @param style the style of this text field, default is [TextFieldDefaults.style].
* @param enabled whether this text field is enabled, default is true.
* @param readOnly whether this text field is read only, default is false.
* @param completionOptions the auto complete options.
* @param keyboardOptions the keyboard options, default is [KeyboardOptions.Default].
* @param keyboardActions the keyboard actions, default is [KeyboardActions.Default].
* @param singleLine whether this text field is single line, default is false.
* @param maxLines the max lines of this text field, when [singleLine] is false default is [Int.MAX_VALUE].
* @param minLines the min lines of this text field, default is 1.
* @param visualTransformation the visual transformation, default is [VisualTransformation.None].
* @param onTextLayout the callback to be invoked when the text layout is ready.
* @param focusRequester the focus requester of this text field.
* @param interactionSource the interaction source of this text field.
* @param header the header of the [TextField], should typically be [Icon] or [Text].
* @param placeholder the placeholder of the [TextField], should typically be [Icon] or [Text].
*/
@Composable @Composable
fun BackspaceTextField( fun BackspaceTextField(
value: String, value: String,
onValueChange: (String) -> Unit, onValueChange: (String) -> Unit,
completionValues: List<String> = emptyList(), completionValues: List<String> = emptyList(),
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: TextFieldColors = TextField.colors, colors: TextFieldColors = TextFieldDefaults.colors,
style: TextFieldStyle = TextField.style, style: TextFieldStyle = TextFieldDefaults.style,
enabled: Boolean = true, enabled: Boolean = true,
readOnly: Boolean = false, readOnly: Boolean = false,
completionOptions: AutoCompleteOptions = AutoCompleteOptions(), completionOptions: AutoCompleteOptions = AutoCompleteOptions(),
@@ -563,7 +749,7 @@ private fun AutoCompleteTextFieldBox(
if (completionValues.isEmpty()) return if (completionValues.isEmpty()) return
// We need to use some "last" to remember the last using data, // We need to use some "last" to remember the last using data,
// because we need to mantain the animation state of the dropdown menu. // because we need to mantain the animation state of the dropdown menu.
// This allows the animation to finish playing due to the next composable event. // This allows the animation to finish playing due to the next compostion.
var lastHandingModified by remember { mutableStateOf(false) } var lastHandingModified by remember { mutableStateOf(false) }
var lastMatchedValue by remember { mutableStateOf("") } var lastMatchedValue by remember { mutableStateOf("") }
var lastInputLength by remember { mutableStateOf(0) } var lastInputLength by remember { mutableStateOf(0) }
@@ -597,9 +783,13 @@ private fun AutoCompleteTextFieldBox(
lastHandingModified = false lastHandingModified = false
lastMatchedValue = "" lastMatchedValue = ""
} else selection = -1 } else selection = -1
/** Collapse the dropdown menu. */
fun collapse() { fun collapse() {
lastMatchedValue = inputText lastMatchedValue = inputText
} }
/** Select and collapse the dropdown menu. */
fun selectAndCollapse(position: Int) { fun selectAndCollapse(position: Int) {
if (position < 0) return if (position < 0) return
val newValue = TextFieldValue(matchedValues[position], TextRange(matchedValues[position].length)) val newValue = TextFieldValue(matchedValues[position], TextRange(matchedValues[position].length))
@@ -746,7 +936,10 @@ private fun Modifier.textFieldPadding(
} }
} }
object TextField { /**
* Defaults of text field.
*/
object TextFieldDefaults {
val colors: TextFieldColors val colors: TextFieldColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
@@ -768,7 +961,7 @@ private fun defaultTextFieldColors() = TextFieldColors(
), ),
completionColors = AutoCompleteBoxColors( completionColors = AutoCompleteBoxColors(
highlightContentColor = LocalColors.current.themePrimary, highlightContentColor = LocalColors.current.themePrimary,
menuColors = DropdownMenu.colors menuColors = DropdownMenuDefaults.colors
), ),
placeholderContentColor = LocalColors.current.textSecondary, placeholderContentColor = LocalColors.current.textSecondary,
decorInactiveTint = LocalColors.current.themeSecondary, decorInactiveTint = LocalColors.current.themeSecondary,
@@ -786,7 +979,7 @@ private fun defaultTextFieldStyle() = TextFieldStyle(
shape = withAreaBoxShape(), shape = withAreaBoxShape(),
borderInactive = defaultTextFieldInactiveBorder(), borderInactive = defaultTextFieldInactiveBorder(),
borderActive = defaultTextFieldActiveBorder(), borderActive = defaultTextFieldActiveBorder(),
completionStyle = DropdownMenu.style completionStyle = DropdownMenuDefaults.style
) )
@Composable @Composable

View File

@@ -23,6 +23,7 @@
package com.highcapable.flexiui.interaction package com.highcapable.flexiui.interaction
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
@@ -41,6 +42,12 @@ import com.highcapable.betterandroid.compose.extension.ui.toggleable
import com.highcapable.flexiui.LocalColors import com.highcapable.flexiui.LocalColors
import androidx.compose.material.ripple.rememberRipple as materialRememberRipple import androidx.compose.material.ripple.rememberRipple as materialRememberRipple
/**
* Style defines for ripple.
* @param bounded whether the ripple is bounded.
* @param radius the radius.
* @param color the color.
*/
@Immutable @Immutable
data class RippleStyle( data class RippleStyle(
val bounded: Boolean, val bounded: Boolean,
@@ -48,9 +55,26 @@ data class RippleStyle(
val color: Color val color: Color
) )
/**
* Creates and remember a ripple effect [Indication] with the given [style].
* @see materialRememberRipple
* @param style the style, default is [InteractionDefaults.rippleStyle].
* @return [Indication]
*/
@Composable @Composable
fun rememberRipple(style: RippleStyle) = materialRememberRipple(style.bounded, style.radius, style.color) fun rememberRipple(style: RippleStyle = InteractionDefaults.rippleStyle) =
materialRememberRipple(style.bounded, style.radius, style.color)
/**
* The clickable modifier has a ripple effect.
* @see Modifier.clickable
* @param rippleStyle the ripple style, default is [InteractionDefaults.rippleStyle].
* @param interactionSource the interaction source.
* @param enabled whether to enable the event, default is true.
* @param onClickLabel the click label.
* @param role the role.
* @param onClick the click event.
*/
fun Modifier.rippleClickable( fun Modifier.rippleClickable(
rippleStyle: RippleStyle? = null, rippleStyle: RippleStyle? = null,
interactionSource: MutableInteractionSource? = null, interactionSource: MutableInteractionSource? = null,
@@ -69,7 +93,7 @@ fun Modifier.rippleClickable(
properties["onClick"] = onClick properties["onClick"] = onClick
} }
) { ) {
val currentRippleStyle = rippleStyle ?: Interaction.rippleStyle val currentRippleStyle = rippleStyle ?: InteractionDefaults.rippleStyle
val currentIndication = rememberRipple(currentRippleStyle) val currentIndication = rememberRipple(currentRippleStyle)
clickable( clickable(
onClick = onClick, onClick = onClick,
@@ -81,6 +105,19 @@ fun Modifier.rippleClickable(
) )
} }
/**
* The combined clickable modifier has a ripple effect.
* @see Modifier.combinedClickable
* @param rippleStyle the ripple style, default is [InteractionDefaults.rippleStyle].
* @param interactionSource the interaction source.
* @param enabled whether to enable the event, default is true.
* @param onClickLabel the click label.
* @param role the role.
* @param onLongClickLabel the long click label.
* @param onLongClick the long click event.
* @param onDoubleClick the double click event.
* @param onClick the click event.
*/
fun Modifier.rippleCombinedClickable( fun Modifier.rippleCombinedClickable(
rippleStyle: RippleStyle? = null, rippleStyle: RippleStyle? = null,
interactionSource: MutableInteractionSource? = null, interactionSource: MutableInteractionSource? = null,
@@ -105,7 +142,7 @@ fun Modifier.rippleCombinedClickable(
properties["onClick"] = onClick properties["onClick"] = onClick
} }
) { ) {
val currentRippleStyle = rippleStyle ?: Interaction.rippleStyle val currentRippleStyle = rippleStyle ?: InteractionDefaults.rippleStyle
val currentIndication = rememberRipple(currentRippleStyle) val currentIndication = rememberRipple(currentRippleStyle)
combinedClickable( combinedClickable(
onClick = onClick, onClick = onClick,
@@ -120,6 +157,15 @@ fun Modifier.rippleCombinedClickable(
) )
} }
/**
* The toggleable modifier has a ripple effect.
* @see Modifier.toggleable
* @param rippleStyle the ripple style, default is [InteractionDefaults.rippleStyle].
* @param interactionSource the interaction source.
* @param enabled whether to enable the event, default is true.
* @param role the role.
* @param onValueChange the value change event.
*/
fun Modifier.rippleToggleable( fun Modifier.rippleToggleable(
value: Boolean, value: Boolean,
rippleStyle: RippleStyle? = null, rippleStyle: RippleStyle? = null,
@@ -138,7 +184,7 @@ fun Modifier.rippleToggleable(
properties["onValueChange"] = onValueChange properties["onValueChange"] = onValueChange
} }
) { ) {
val currentRippleStyle = rippleStyle ?: Interaction.rippleStyle val currentRippleStyle = rippleStyle ?: InteractionDefaults.rippleStyle
val currentIndication = rememberRipple(currentRippleStyle) val currentIndication = rememberRipple(currentRippleStyle)
toggleable( toggleable(
value = value, value = value,
@@ -150,6 +196,15 @@ fun Modifier.rippleToggleable(
) )
} }
/**
* The selectable modifier has a ripple effect.
* @see Modifier.selectable
* @param rippleStyle the ripple style, default is [InteractionDefaults.rippleStyle].
* @param interactionSource the interaction source.
* @param enabled whether to enable the event, default is true.
* @param role the role.
* @param onClick the click event.
*/
fun Modifier.rippleSelectable( fun Modifier.rippleSelectable(
selected: Boolean, selected: Boolean,
rippleStyle: RippleStyle? = null, rippleStyle: RippleStyle? = null,
@@ -168,7 +223,7 @@ fun Modifier.rippleSelectable(
properties["onClick"] = onClick properties["onClick"] = onClick
} }
) { ) {
val currentRippleStyle = rippleStyle ?: Interaction.rippleStyle val currentRippleStyle = rippleStyle ?: InteractionDefaults.rippleStyle
val currentIndication = rememberRipple(currentRippleStyle) val currentIndication = rememberRipple(currentRippleStyle)
selectable( selectable(
selected = selected, selected = selected,
@@ -180,13 +235,20 @@ fun Modifier.rippleSelectable(
) )
} }
object Interaction { /**
* Defaults of interaction.
*/
object InteractionDefaults {
val rippleStyle: RippleStyle val rippleStyle: RippleStyle
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
get() = LocalRippleStyle.current ?: defaultRippleStyle() get() = LocalRippleStyle.current ?: defaultRippleStyle()
} }
/**
* CompositionLocal containing the preferred [RippleStyle]
* that will be used by interaction by default.
*/
val LocalRippleStyle = compositionLocalOf<RippleStyle?> { null } val LocalRippleStyle = compositionLocalOf<RippleStyle?> { null }
@Composable @Composable

View File

@@ -31,6 +31,8 @@ import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.widthIn
@@ -55,17 +57,40 @@ import com.highcapable.flexiui.LocalColors
import com.highcapable.flexiui.LocalSizes import com.highcapable.flexiui.LocalSizes
import com.highcapable.flexiui.LocalTypography import com.highcapable.flexiui.LocalTypography
import com.highcapable.flexiui.component.AreaBox import com.highcapable.flexiui.component.AreaBox
import com.highcapable.flexiui.component.AreaBoxDefaults
import com.highcapable.flexiui.component.AreaBoxStyle import com.highcapable.flexiui.component.AreaBoxStyle
import com.highcapable.flexiui.component.Button
import com.highcapable.flexiui.component.Icon
import com.highcapable.flexiui.component.LocalIconStyle
import com.highcapable.flexiui.component.LocalPrimaryButton import com.highcapable.flexiui.component.LocalPrimaryButton
import com.highcapable.flexiui.component.LocalTextStyle import com.highcapable.flexiui.component.LocalTextStyle
import com.highcapable.flexiui.component.Text
import com.highcapable.flexiui.utils.SubcomposeRow import com.highcapable.flexiui.utils.SubcomposeRow
/**
* Colors defines for flexi dialog.
* @param titleTextColor the title text color.
* @param titleIconTint the title icon tint.
* @param contentTextColor the content text color.
*/
@Immutable @Immutable
data class FlexiDialogColors( data class FlexiDialogColors(
val titleTextColor: Color, val titleTextColor: Color,
val titleIconTint: Color,
val contentTextColor: Color val contentTextColor: Color
) )
/**
* Style defines for flexi dialog.
* @param boxStyle the style of area box.
* @param titleTextStyle the title text style.
* @param contentTextStyle the content text style.
* @param maxWidth the dialog's max width.
* @param 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 boxStyle: AreaBoxStyle,
@@ -78,29 +103,50 @@ data class FlexiDialogStyle(
val buttonsSpacing: Dp val buttonsSpacing: Dp
) )
/**
* Flexi UI dialog.
* @param visible the visible state of dialog.
* @param onDismissRequest the callback when dismiss dialog.
* @param modifier the [Modifier] to be applied to this dialog.
* @param animated whether to animate the dialog, default is true.
* @param colors the colors of dialog, default is [FlexiDialogDefaults.colors].
* @param style the style of dialog, default is [FlexiDialogDefaults.style].
* @param contentAlignment the alignment of dialog content, default is [Alignment.TopStart].
* @param properties the properties of dialog, default is [DefaultDialogProperties].
* @param title the title of the [FlexiDialog], should typically be [Icon] or [Text].
* @param content the content of the [FlexiDialog].
* @param confirmButton the confirm button of the [FlexiDialog], should typically be [Button].
* @param cancelButton the cancel button of the [FlexiDialog], should typically be [Button].
* @param neutralButton the neutral button of the [FlexiDialog], should typically be [Button].
*/
@Composable @Composable
fun FlexiDialog( fun FlexiDialog(
visible: Boolean, visible: Boolean,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
animated: Boolean = true, animated: Boolean = true,
colors: FlexiDialogColors = FlexiDialog.colors, colors: FlexiDialogColors = FlexiDialogDefaults.colors,
style: FlexiDialogStyle = FlexiDialog.style, style: FlexiDialogStyle = FlexiDialogDefaults.style,
contentAlignment: Alignment = Alignment.TopStart, contentAlignment: Alignment = Alignment.TopStart,
properties: DialogPropertiesWrapper = DefaultDialogProperties, properties: DialogPropertiesWrapper = DefaultDialogProperties,
title: (@Composable () -> Unit)? = null, title: (@Composable RowScope.() -> Unit)? = null,
content: @Composable () -> Unit, content: @Composable () -> Unit,
confirmButton: @Composable (() -> Unit)? = null, confirmButton: @Composable (() -> Unit)? = null,
cancelButton: @Composable (() -> Unit)? = null, cancelButton: @Composable (() -> Unit)? = null,
neutralButton: @Composable (() -> Unit)? = null neutralButton: @Composable (() -> Unit)? = null
) { ) {
/** Build the content of dialog. */
@Composable @Composable
fun Content() { fun Content() {
title?.also { content -> title?.also { titleContent ->
Box(modifier = Modifier.padding(style.titlePadding)) { CompositionLocalProvider(
CompositionLocalProvider( LocalIconStyle provides LocalIconStyle.current.copy(tint = colors.titleIconTint),
LocalTextStyle provides style.titleTextStyle.copy(color = colors.titleTextColor), LocalTextStyle provides style.titleTextStyle.copy(color = colors.titleTextColor)
content = content ) {
Row(
modifier = Modifier.padding(style.titlePadding),
verticalAlignment = Alignment.CenterVertically,
content = titleContent
) )
} }
} }
@@ -112,16 +158,17 @@ fun FlexiDialog(
} }
} }
/** Build the buttons of dialog. */
@Composable @Composable
fun Buttons() { fun Buttons() {
Column( Column(
modifier = Modifier.padding(top = style.buttonsSpacing), modifier = Modifier.padding(top = style.buttonsSpacing),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
neutralButton?.also { content -> neutralButton?.also { button ->
SubcomposeRow( SubcomposeRow(
modifier = Modifier.fillMaxWidth().padding(bottom = style.buttonsSpacing), modifier = Modifier.fillMaxWidth().padding(bottom = style.buttonsSpacing),
content = content content = button
) )
} }
SubcomposeRow( SubcomposeRow(
@@ -130,10 +177,10 @@ fun FlexiDialog(
spacingBetween = style.buttonsSpacing spacingBetween = style.buttonsSpacing
) { ) {
cancelButton?.invoke() cancelButton?.invoke()
confirmButton?.also { content -> confirmButton?.also { button ->
CompositionLocalProvider( CompositionLocalProvider(
LocalPrimaryButton provides true, LocalPrimaryButton provides true,
content = content content = button
) )
} }
} }
@@ -157,16 +204,19 @@ fun FlexiDialog(
} }
} }
/**
* Basic flexi dialog for internal use.
*/
@Composable @Composable
fun BasicFlexiDialog( private fun BasicFlexiDialog(
visible: Boolean, visible: Boolean,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier,
animated: Boolean = true, animated: Boolean,
boxStyle: AreaBoxStyle = AreaBox.style, boxStyle: AreaBoxStyle,
maxWidth: Dp = DefaultMaxWidth, maxWidth: Dp,
contentAlignment: Alignment = Alignment.TopStart, contentAlignment: Alignment,
properties: DialogPropertiesWrapper = DefaultDialogProperties, properties: DialogPropertiesWrapper,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
val animatedAlpha by animateFloatAsState(if (visible) 1f else 0f, tween(AnimationDuration)) val animatedAlpha by animateFloatAsState(if (visible) 1f else 0f, tween(AnimationDuration))
@@ -205,7 +255,10 @@ fun BasicFlexiDialog(
} }
} }
object FlexiDialog { /**
* Defaults of flexi dialog.
*/
object FlexiDialogDefaults {
val colors: FlexiDialogColors val colors: FlexiDialogColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
@@ -220,13 +273,14 @@ object FlexiDialog {
@ReadOnlyComposable @ReadOnlyComposable
private fun defaultFlexiDialogColors() = FlexiDialogColors( private fun defaultFlexiDialogColors() = FlexiDialogColors(
titleTextColor = LocalColors.current.textPrimary, titleTextColor = LocalColors.current.textPrimary,
titleIconTint = LocalColors.current.textPrimary,
contentTextColor = LocalColors.current.textSecondary contentTextColor = LocalColors.current.textSecondary
) )
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun defaultFlexiDialogStyle() = FlexiDialogStyle( private fun defaultFlexiDialogStyle() = FlexiDialogStyle(
boxStyle = AreaBox.style.copy(padding = ComponentPadding(LocalSizes.current.spacingSecondary)), boxStyle = AreaBoxDefaults.style.copy(padding = ComponentPadding(LocalSizes.current.spacingSecondary)),
titleTextStyle = LocalTypography.current.titleSecondary, titleTextStyle = LocalTypography.current.titleSecondary,
contentTextStyle = LocalTypography.current.primary, contentTextStyle = LocalTypography.current.primary,
maxWidth = DefaultMaxWidth, maxWidth = DefaultMaxWidth,

View File

@@ -26,6 +26,10 @@ package com.highcapable.flexiui
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
/**
* Whether dynamic color is available for current system.
* @return [Boolean]
*/
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
actual fun isDynamicColorAvailable() = false actual fun isDynamicColorAvailable() = false

View File

@@ -26,7 +26,7 @@ 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.DesktopContextMenu import com.highcapable.flexiui.component.DesktopContextMenuDefaults
import com.highcapable.flexiui.component.DesktopContextMenuRepresentation import com.highcapable.flexiui.component.DesktopContextMenuRepresentation
import com.highcapable.flexiui.component.LocalContextMenuColors import com.highcapable.flexiui.component.LocalContextMenuColors
import com.highcapable.flexiui.component.LocalContextMenuStyle import com.highcapable.flexiui.component.LocalContextMenuStyle
@@ -42,8 +42,8 @@ internal actual fun FlexiThemeContent(content: @Composable () -> Unit) {
CompositionLocalProvider( CompositionLocalProvider(
LocalContextMenuRepresentation provides LocalContextMenuRepresentation provides
DesktopContextMenuRepresentation( DesktopContextMenuRepresentation(
colors = DesktopContextMenu.colors, colors = DesktopContextMenuDefaults.colors,
style = DesktopContextMenu.style style = DesktopContextMenuDefaults.style
), ),
content = content content = content
) )

View File

@@ -68,12 +68,22 @@ import com.highcapable.flexiui.LocalSizes
import com.highcapable.flexiui.interaction.rippleClickable import com.highcapable.flexiui.interaction.rippleClickable
import java.awt.event.KeyEvent import java.awt.event.KeyEvent
/**
* Colors defines for the context menu.
* @param contentColor the content color, usually for the text color.
* @param borderColor the border color.
*/
@Immutable @Immutable
data class ContextMenuColors( data class ContextMenuColors(
val contentColor: Color, val contentColor: Color,
val borderColor: Color val borderColor: Color
) )
/**
* Style defines for the context menu.
* @param contentStyle the content style of area box.
* @param borderStyle the border style of area box.
*/
@Immutable @Immutable
data class ContextMenuStyle( data class ContextMenuStyle(
val contentStyle: AreaBoxStyle?, val contentStyle: AreaBoxStyle?,
@@ -172,7 +182,10 @@ private fun Modifier.onHover(onHover: (Boolean) -> Unit) = pointerInput(Unit) {
} }
} }
object DesktopContextMenu { /**
* Defaults of context menu.
*/
object DesktopContextMenuDefaults {
val colors: ContextMenuColors val colors: ContextMenuColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
@@ -183,6 +196,10 @@ object DesktopContextMenu {
get() = LocalContextMenuStyle.current get() = LocalContextMenuStyle.current
} }
/**
* CompositionLocal containing the preferred [ContextMenuColors]
* that will be used by context menu by default.
*/
val LocalContextMenuColors = compositionLocalOf { val LocalContextMenuColors = compositionLocalOf {
ContextMenuColors( ContextMenuColors(
borderColor = Color.Unspecified, borderColor = Color.Unspecified,
@@ -190,6 +207,10 @@ val LocalContextMenuColors = compositionLocalOf {
) )
} }
/**
* CompositionLocal containing the preferred [ContextMenuStyle]
* that will be used by context menu by default.
*/
val LocalContextMenuStyle = compositionLocalOf { val LocalContextMenuStyle = compositionLocalOf {
ContextMenuStyle( ContextMenuStyle(
contentStyle = null, contentStyle = null,
@@ -207,11 +228,11 @@ internal fun defaultContextMenuColors() = ContextMenuColors(
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
internal fun defaultContextMenuStyle() = ContextMenuStyle( internal fun defaultContextMenuStyle() = ContextMenuStyle(
contentStyle = LocalContextMenuStyle.current.contentStyle ?: AreaBox.style.copy( contentStyle = LocalContextMenuStyle.current.contentStyle ?: AreaBoxDefaults.style.copy(
padding = ComponentPadding(horizontal = DefaultMenuContentPadding), padding = ComponentPadding(horizontal = DefaultMenuContentPadding),
shape = LocalShapes.current.secondary shape = LocalShapes.current.secondary
), ),
borderStyle = LocalContextMenuStyle.current.borderStyle ?: AreaBox.style.copy( borderStyle = LocalContextMenuStyle.current.borderStyle ?: AreaBoxDefaults.style.copy(
padding = ComponentPadding(LocalSizes.current.spacingTertiary), padding = ComponentPadding(LocalSizes.current.spacingTertiary),
shadowSize = LocalSizes.current.zoomSizeTertiary, shadowSize = LocalSizes.current.zoomSizeTertiary,
shape = LocalShapes.current.primary shape = LocalShapes.current.primary

View File

@@ -26,6 +26,10 @@ package com.highcapable.flexiui
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
/**
* Whether dynamic color is available for current system.
* @return [Boolean]
*/
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
actual fun isDynamicColorAvailable() = false actual fun isDynamicColorAvailable() = false

View File

@@ -23,4 +23,7 @@
package com.highcapable.flexiui.resources package com.highcapable.flexiui.resources
/**
* The default icon resources for Flexi UI.
*/
object Icons object Icons