chore(core/ui): add components
Signed-off-by: Alan Brault <alan.brault@visus.io>
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
package io.visus.orbis.core.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.paint
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.graphics.toolingGraphicsLayer
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.role
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.visus.orbis.core.ui.LocalContentColor
|
||||
|
||||
@Composable
|
||||
fun Icon(
|
||||
imageVector: ImageVector,
|
||||
modifier: Modifier = Modifier,
|
||||
contentDescription: String? = null,
|
||||
tint: Color = LocalContentColor.current,
|
||||
) {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(imageVector),
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Icon(
|
||||
bitmap: ImageBitmap,
|
||||
modifier: Modifier = Modifier,
|
||||
contentDescription: String? = null,
|
||||
tint: Color = LocalContentColor.current,
|
||||
) {
|
||||
val painter = remember(bitmap) { BitmapPainter(bitmap) }
|
||||
Icon(
|
||||
painter = painter,
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Icon(
|
||||
painter: Painter,
|
||||
modifier: Modifier = Modifier,
|
||||
contentDescription: String? = null,
|
||||
tint: Color = LocalContentColor.current,
|
||||
) {
|
||||
val colorFilter = if (tint == Color.Unspecified) null else ColorFilter.tint(tint)
|
||||
val semantics =
|
||||
if (contentDescription != null) {
|
||||
Modifier.semantics {
|
||||
this.contentDescription = contentDescription
|
||||
this.role = Role.Image
|
||||
}
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
Box(
|
||||
modifier
|
||||
.toolingGraphicsLayer()
|
||||
.defaultSizeFor(painter)
|
||||
.paint(painter, colorFilter = colorFilter, contentScale = ContentScale.Fit)
|
||||
.then(semantics),
|
||||
)
|
||||
}
|
||||
|
||||
private fun Modifier.defaultSizeFor(painter: Painter) =
|
||||
this.then(
|
||||
if (painter.intrinsicSize == Size.Unspecified || painter.intrinsicSize.isInfinite()) {
|
||||
DefaultIconSizeModifier
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
)
|
||||
|
||||
private fun Size.isInfinite() = width.isInfinite() && height.isInfinite()
|
||||
|
||||
private val DefaultIconSizeModifier = Modifier.size(IconDefaults.iconSize)
|
||||
|
||||
internal object IconDefaults {
|
||||
val iconSize = 24.dp
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
package io.visus.orbis.core.ui.components
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.selection.selectableGroup
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.Layout
|
||||
import androidx.compose.ui.layout.MeasureResult
|
||||
import androidx.compose.ui.layout.MeasureScope
|
||||
import androidx.compose.ui.layout.Placeable
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||
import androidx.compose.ui.semantics.role
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.visus.orbis.core.ui.OrbisTheme
|
||||
import io.visus.orbis.core.ui.LocalContentColor
|
||||
import io.visus.orbis.core.ui.components.NavigationBarDefaults.NavigationBarHeight
|
||||
import io.visus.orbis.core.ui.components.NavigationBarItemDefaults.ItemAnimationDurationMillis
|
||||
import io.visus.orbis.core.ui.components.NavigationBarItemDefaults.NavigationBarItemHorizontalPadding
|
||||
import io.visus.orbis.core.ui.components.NavigationBarItemDefaults.NavigationBarItemVerticalPadding
|
||||
import io.visus.orbis.core.ui.contentColorFor
|
||||
import io.visus.orbis.core.ui.foundation.ProvideTextStyle
|
||||
import io.visus.orbis.core.ui.foundation.systemBarsForVisualComponents
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
fun NavigationBar(
|
||||
modifier: Modifier = Modifier,
|
||||
containerColor: Color = NavigationBarDefaults.containerColor,
|
||||
contentColor: Color = contentColorFor(containerColor),
|
||||
windowInsets: WindowInsets = NavigationBarDefaults.windowInsets,
|
||||
content: @Composable RowScope.() -> Unit,
|
||||
) {
|
||||
Surface(
|
||||
color = containerColor,
|
||||
contentColor = contentColor,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.windowInsetsPadding(windowInsets)
|
||||
.height(NavigationBarHeight)
|
||||
.selectableGroup(),
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RowScope.NavigationBarItem(
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
icon: @Composable () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
label: @Composable (() -> Unit)? = null,
|
||||
alwaysShowLabel: Boolean = true,
|
||||
colors: NavigationBarItemColors = NavigationBarItemDefaults.colors(),
|
||||
textStyle: TextStyle = NavigationBarItemDefaults.textStyle(),
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
) {
|
||||
val styledIcon = @Composable {
|
||||
val iconColor by colors.iconColor(selected = selected, enabled = enabled)
|
||||
val clearSemantics = label != null && (alwaysShowLabel || selected)
|
||||
Box(modifier = if (clearSemantics) Modifier.clearAndSetSemantics {} else Modifier) {
|
||||
CompositionLocalProvider(LocalContentColor provides iconColor, content = icon)
|
||||
}
|
||||
}
|
||||
|
||||
val styledLabel: @Composable (() -> Unit)? =
|
||||
label?.let {
|
||||
@Composable {
|
||||
val textColor by colors.textColor(selected = selected, enabled = enabled)
|
||||
CompositionLocalProvider(LocalContentColor provides textColor) {
|
||||
ProvideTextStyle(textStyle, content = label)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var itemWidth by remember { mutableIntStateOf(0) }
|
||||
|
||||
Box(
|
||||
modifier
|
||||
.selectable(
|
||||
selected = selected,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
role = Role.Tab,
|
||||
interactionSource = interactionSource,
|
||||
indication = null,
|
||||
)
|
||||
.semantics {
|
||||
role = Role.Tab
|
||||
}
|
||||
.weight(1f)
|
||||
.onSizeChanged {
|
||||
itemWidth = it.width
|
||||
},
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
val animationProgress: Float by animateFloatAsState(
|
||||
targetValue = if (selected) 1f else 0f,
|
||||
animationSpec = tween(ItemAnimationDurationMillis),
|
||||
)
|
||||
|
||||
NavigationBarItemBaselineLayout(
|
||||
icon = styledIcon,
|
||||
label = styledLabel,
|
||||
alwaysShowLabel = alwaysShowLabel,
|
||||
animationProgress = animationProgress,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NavigationBarItemBaselineLayout(
|
||||
icon: @Composable () -> Unit,
|
||||
label: @Composable (() -> Unit)?,
|
||||
alwaysShowLabel: Boolean,
|
||||
animationProgress: Float,
|
||||
) {
|
||||
Layout({
|
||||
Box(Modifier.layoutId(IconLayoutIdTag)) { icon() }
|
||||
|
||||
if (label != null) {
|
||||
Box(
|
||||
Modifier
|
||||
.layoutId(LabelLayoutIdTag)
|
||||
.alpha(if (alwaysShowLabel) 1f else animationProgress)
|
||||
.padding(horizontal = NavigationBarItemHorizontalPadding / 2),
|
||||
) { label() }
|
||||
}
|
||||
}) { measurables, constraints ->
|
||||
val iconPlaceable =
|
||||
measurables.first { it.layoutId == IconLayoutIdTag }.measure(constraints)
|
||||
|
||||
val labelPlaceable =
|
||||
label?.let {
|
||||
measurables.first { it.layoutId == LabelLayoutIdTag }.measure(
|
||||
constraints.copy(minHeight = 0),
|
||||
)
|
||||
}
|
||||
|
||||
if (label == null) {
|
||||
placeIcon(iconPlaceable, constraints)
|
||||
} else {
|
||||
placeLabelAndIcon(
|
||||
labelPlaceable!!,
|
||||
iconPlaceable,
|
||||
constraints,
|
||||
alwaysShowLabel,
|
||||
animationProgress,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun MeasureScope.placeIcon(
|
||||
iconPlaceable: Placeable,
|
||||
constraints: Constraints,
|
||||
): MeasureResult {
|
||||
val width = constraints.maxWidth
|
||||
val height = constraints.maxHeight
|
||||
|
||||
val iconX = (width - iconPlaceable.width) / 2
|
||||
val iconY = (height - iconPlaceable.height) / 2
|
||||
|
||||
return layout(width, height) {
|
||||
iconPlaceable.placeRelative(iconX, iconY)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MeasureScope.placeLabelAndIcon(
|
||||
labelPlaceable: Placeable,
|
||||
iconPlaceable: Placeable,
|
||||
constraints: Constraints,
|
||||
alwaysShowLabel: Boolean,
|
||||
animationProgress: Float,
|
||||
): MeasureResult {
|
||||
val height = constraints.maxHeight
|
||||
|
||||
val labelY =
|
||||
height - labelPlaceable.height - NavigationBarItemVerticalPadding.roundToPx()
|
||||
|
||||
val selectedIconY = NavigationBarItemVerticalPadding.roundToPx()
|
||||
val unselectedIconY =
|
||||
if (alwaysShowLabel) selectedIconY else (height - iconPlaceable.height) / 2
|
||||
|
||||
val iconDistance = unselectedIconY - selectedIconY
|
||||
|
||||
val offset = (iconDistance * (1 - animationProgress)).roundToInt()
|
||||
|
||||
val containerWidth = constraints.maxWidth
|
||||
|
||||
val labelX = (containerWidth - labelPlaceable.width) / 2
|
||||
val iconX = (containerWidth - iconPlaceable.width) / 2
|
||||
|
||||
return layout(containerWidth, height) {
|
||||
if (alwaysShowLabel || animationProgress != 0f) {
|
||||
labelPlaceable.placeRelative(labelX, labelY + offset)
|
||||
}
|
||||
iconPlaceable.placeRelative(iconX, selectedIconY + offset)
|
||||
}
|
||||
}
|
||||
|
||||
internal object NavigationBarDefaults {
|
||||
internal val NavigationBarHeight: Dp = 80.0.dp
|
||||
val containerColor: Color @Composable get() = OrbisTheme.colors.background
|
||||
|
||||
val windowInsets: WindowInsets
|
||||
@Composable get() =
|
||||
WindowInsets.systemBarsForVisualComponents.only(
|
||||
WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom,
|
||||
)
|
||||
}
|
||||
|
||||
object NavigationBarItemDefaults {
|
||||
internal val NavigationBarItemHorizontalPadding: Dp = 8.dp
|
||||
internal val NavigationBarItemVerticalPadding: Dp = 18.dp
|
||||
internal const val ItemAnimationDurationMillis: Int = 100
|
||||
|
||||
@Composable
|
||||
fun colors(
|
||||
selectedIconColor: Color = OrbisTheme.colors.onBackground,
|
||||
selectedTextColor: Color = OrbisTheme.colors.onBackground,
|
||||
unselectedIconColor: Color = OrbisTheme.colors.onBackground.copy(alpha = 0.65f),
|
||||
unselectedTextColor: Color = OrbisTheme.colors.onBackground.copy(alpha = 0.65f),
|
||||
disabledIconColor: Color = OrbisTheme.colors.onBackground.copy(alpha = 0.3f),
|
||||
disabledTextColor: Color = OrbisTheme.colors.onBackground.copy(alpha = 0.3f),
|
||||
): NavigationBarItemColors =
|
||||
NavigationBarItemColors(
|
||||
selectedIconColor = selectedIconColor,
|
||||
selectedTextColor = selectedTextColor,
|
||||
unselectedIconColor = unselectedIconColor,
|
||||
unselectedTextColor = unselectedTextColor,
|
||||
disabledIconColor = disabledIconColor,
|
||||
disabledTextColor = disabledTextColor,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun textStyle(): TextStyle = OrbisTheme.typography.label2
|
||||
}
|
||||
|
||||
@ConsistentCopyVisibility
|
||||
@Stable
|
||||
data class NavigationBarItemColors internal constructor(
|
||||
private val selectedIconColor: Color,
|
||||
private val selectedTextColor: Color,
|
||||
private val unselectedIconColor: Color,
|
||||
private val unselectedTextColor: Color,
|
||||
private val disabledIconColor: Color,
|
||||
private val disabledTextColor: Color,
|
||||
) {
|
||||
@Composable
|
||||
internal fun iconColor(selected: Boolean, enabled: Boolean): State<Color> {
|
||||
val targetValue =
|
||||
when {
|
||||
!enabled -> disabledIconColor
|
||||
selected -> selectedIconColor
|
||||
else -> unselectedIconColor
|
||||
}
|
||||
return animateColorAsState(
|
||||
targetValue = targetValue,
|
||||
animationSpec = tween(NavigationBarItemDefaults.ItemAnimationDurationMillis),
|
||||
label = "icon-color",
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun textColor(selected: Boolean, enabled: Boolean): State<Color> {
|
||||
val targetValue =
|
||||
when {
|
||||
!enabled -> disabledTextColor
|
||||
selected -> selectedTextColor
|
||||
else -> unselectedTextColor
|
||||
}
|
||||
return animateColorAsState(
|
||||
targetValue = targetValue,
|
||||
animationSpec = tween(NavigationBarItemDefaults.ItemAnimationDurationMillis),
|
||||
label = "text-color",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private const val IconLayoutIdTag: String = "icon"
|
||||
private const val LabelLayoutIdTag: String = "label"
|
||||
180
core/ui/src/main/java/io/visus/orbis/core/ui/components/Text.kt
Normal file
180
core/ui/src/main/java/io/visus/orbis/core/ui/components/Text.kt
Normal file
@@ -0,0 +1,180 @@
|
||||
package io.visus.orbis.core.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.foundation.text.InlineTextContent
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.TextLayoutResult
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.visus.orbis.core.ui.LocalContentColor
|
||||
import io.visus.orbis.core.ui.LocalTextStyle
|
||||
import io.visus.orbis.core.ui.LocalTypography
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
||||
@Composable
|
||||
fun Text(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = LocalContentColor.current,
|
||||
fontSize: TextUnit = TextUnit.Unspecified,
|
||||
fontStyle: FontStyle? = null,
|
||||
fontWeight: FontWeight? = null,
|
||||
fontFamily: FontFamily? = null,
|
||||
letterSpacing: TextUnit = TextUnit.Unspecified,
|
||||
textDecoration: TextDecoration? = null,
|
||||
textAlign: TextAlign = TextAlign.Start,
|
||||
lineHeight: TextUnit = TextUnit.Unspecified,
|
||||
overflow: TextOverflow = TextOverflow.Clip,
|
||||
softWrap: Boolean = true,
|
||||
maxLines: Int = Int.MAX_VALUE,
|
||||
minLines: Int = 1,
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
||||
style: TextStyle = LocalTextStyle.current,
|
||||
) {
|
||||
Text(
|
||||
text = AnnotatedString(text = text),
|
||||
modifier = modifier,
|
||||
color = color,
|
||||
fontSize = fontSize,
|
||||
fontStyle = fontStyle,
|
||||
fontWeight = fontWeight,
|
||||
fontFamily = fontFamily,
|
||||
letterSpacing = letterSpacing,
|
||||
textDecoration = textDecoration,
|
||||
textAlign = textAlign,
|
||||
lineHeight = lineHeight,
|
||||
overflow = overflow,
|
||||
softWrap = softWrap,
|
||||
maxLines = maxLines,
|
||||
minLines = minLines,
|
||||
onTextLayout = onTextLayout,
|
||||
style = style,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun Text(
|
||||
text: AnnotatedString,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = LocalContentColor.current,
|
||||
fontSize: TextUnit = TextUnit.Unspecified,
|
||||
fontStyle: FontStyle? = null,
|
||||
fontWeight: FontWeight? = null,
|
||||
fontFamily: FontFamily? = null,
|
||||
letterSpacing: TextUnit = TextUnit.Unspecified,
|
||||
textDecoration: TextDecoration? = null,
|
||||
textAlign: TextAlign = TextAlign.Start,
|
||||
lineHeight: TextUnit = TextUnit.Unspecified,
|
||||
overflow: TextOverflow = TextOverflow.Clip,
|
||||
softWrap: Boolean = true,
|
||||
maxLines: Int = Int.MAX_VALUE,
|
||||
minLines: Int = 1,
|
||||
inlineContent: Map<String, InlineTextContent> = mapOf(),
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
||||
style: TextStyle = LocalTextStyle.current,
|
||||
) {
|
||||
val mergedStyle =
|
||||
style.merge(
|
||||
TextStyle(
|
||||
color = color,
|
||||
fontSize = fontSize,
|
||||
fontWeight = fontWeight,
|
||||
textAlign = textAlign,
|
||||
lineHeight = lineHeight,
|
||||
fontFamily = fontFamily,
|
||||
textDecoration = textDecoration,
|
||||
fontStyle = fontStyle,
|
||||
letterSpacing = letterSpacing,
|
||||
),
|
||||
)
|
||||
BasicText(
|
||||
text,
|
||||
modifier,
|
||||
mergedStyle,
|
||||
onTextLayout,
|
||||
overflow,
|
||||
softWrap,
|
||||
maxLines,
|
||||
minLines,
|
||||
inlineContent,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun TypographySample() {
|
||||
val typography = LocalTypography.current
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "H1 Heading",
|
||||
style = typography.h1,
|
||||
)
|
||||
Text(
|
||||
text = "H2 Heading",
|
||||
style = typography.h2,
|
||||
)
|
||||
Text(
|
||||
text = "H3 Heading",
|
||||
style = typography.h3,
|
||||
)
|
||||
Text(
|
||||
text = "H4 Heading",
|
||||
style = typography.h4,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = "This is body1 text.",
|
||||
style = typography.body1,
|
||||
)
|
||||
Text(
|
||||
text = "This is body2 text.",
|
||||
style = typography.body2,
|
||||
)
|
||||
Text(
|
||||
text = "Body3 text for fine print.",
|
||||
style = typography.body3,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = "Label1: Form Label",
|
||||
style = typography.label1,
|
||||
)
|
||||
Text(
|
||||
text = "Label2: Secondary Info",
|
||||
style = typography.label2,
|
||||
)
|
||||
Text(
|
||||
text = "Label3: Tiny Details",
|
||||
style = typography.label3,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.visus.orbis.core.ui.foundation
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import io.visus.orbis.core.ui.LocalContentColor
|
||||
import io.visus.orbis.core.ui.LocalTextStyle
|
||||
|
||||
@Composable
|
||||
fun ProvideTextStyle(value: TextStyle, content: @Composable () -> Unit) {
|
||||
val mergedStyle = LocalTextStyle.current.merge(value)
|
||||
CompositionLocalProvider(LocalTextStyle provides mergedStyle, content = content)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun ProvideContentColorTextStyle(
|
||||
contentColor: Color,
|
||||
textStyle: TextStyle,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val mergedStyle = LocalTextStyle.current.merge(textStyle)
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides contentColor,
|
||||
LocalTextStyle provides mergedStyle,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user