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