feat: Compose Widgets
This commit is contained in:
parent
9be00b177a
commit
f1b5c083ef
|
|
@ -132,8 +132,6 @@ import kotlinx.coroutines.launch
|
|||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
private val TabWidth = 100.dp
|
||||
|
||||
fun getSortType(
|
||||
context: Context,
|
||||
forumName: String
|
||||
|
|
|
|||
|
|
@ -126,8 +126,8 @@ fun HotPage(
|
|||
item(key = "TopicList") {
|
||||
VerticalGrid(
|
||||
column = 2,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
itemsIndexed(
|
||||
items = topicList,
|
||||
|
|
|
|||
|
|
@ -107,8 +107,8 @@ fun Dislike(
|
|||
bottom.linkTo(parent.bottom)
|
||||
}
|
||||
.padding(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
items(
|
||||
items = dislikeResource,
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ import com.huanchengfly.tieba.post.ui.page.destinations.SettingsPageDestination
|
|||
import com.huanchengfly.tieba.post.ui.page.destinations.ThreadStorePageDestination
|
||||
import com.huanchengfly.tieba.post.ui.widgets.compose.Avatar
|
||||
import com.huanchengfly.tieba.post.ui.widgets.compose.HorizontalDivider
|
||||
import com.huanchengfly.tieba.post.ui.widgets.compose.ListMenuItem
|
||||
import com.huanchengfly.tieba.post.ui.widgets.compose.Sizes
|
||||
import com.huanchengfly.tieba.post.ui.widgets.compose.Switch
|
||||
import com.huanchengfly.tieba.post.ui.widgets.compose.VerticalDivider
|
||||
|
|
@ -377,36 +378,3 @@ fun UserPage(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ListMenuItem(
|
||||
icon: ImageVector,
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: (() -> Unit)? = null,
|
||||
customContent: @Composable (RowScope.() -> Unit)? = null,
|
||||
) {
|
||||
val menuModifier = if (onClick != null) {
|
||||
Modifier.clickable(onClick = onClick)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier
|
||||
.then(menuModifier)
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
) {
|
||||
Icon(imageVector = icon, contentDescription = text, tint = ExtendedTheme.colors.accent)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
Text(
|
||||
text = text,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
customContent?.invoke(this)
|
||||
}
|
||||
}
|
||||
|
|
@ -108,6 +108,7 @@ private fun calcRows(column: Int, items: List<GridScopeImpl.Item>): List<List<Gr
|
|||
fun VerticalGrid(
|
||||
column: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
rowModifier: Modifier = Modifier,
|
||||
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
|
||||
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
|
||||
content: GridScope.() -> Unit
|
||||
|
|
@ -118,7 +119,8 @@ fun VerticalGrid(
|
|||
rows.forEach {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = horizontalArrangement
|
||||
horizontalArrangement = horizontalArrangement,
|
||||
modifier = rowModifier
|
||||
) {
|
||||
it.forEach {
|
||||
Box(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
package com.huanchengfly.tieba.post.ui.widgets.compose
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
|
||||
|
||||
@Composable
|
||||
fun ListMenuItem(
|
||||
icon: ImageVector,
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
iconColor: Color = ExtendedTheme.colors.accent,
|
||||
onClick: (() -> Unit)? = null,
|
||||
customContent: @Composable (RowScope.() -> Unit)? = null,
|
||||
) {
|
||||
val menuModifier = if (onClick != null) {
|
||||
Modifier.clickable(onClick = onClick)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier
|
||||
.then(menuModifier)
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
) {
|
||||
Icon(imageVector = icon, contentDescription = text, tint = iconColor)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
Text(
|
||||
text = text,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
customContent?.invoke(this)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.huanchengfly.tieba.post.ui.widgets.compose
|
||||
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.text.InlineTextContent
|
||||
import androidx.compose.material.Icon
|
||||
|
|
@ -10,6 +11,7 @@ import androidx.compose.material.Text
|
|||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.AccountCircle
|
||||
import androidx.compose.material.icons.rounded.Link
|
||||
import androidx.compose.material.icons.rounded.SlowMotionVideo
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -17,6 +19,7 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.graphics.takeOrElse
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.ExperimentalTextApi
|
||||
import androidx.compose.ui.text.Placeholder
|
||||
import androidx.compose.ui.text.PlaceholderVerticalAlign
|
||||
import androidx.compose.ui.text.TextLayoutResult
|
||||
|
|
@ -24,6 +27,7 @@ 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.rememberTextMeasurer
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
|
|
@ -38,7 +42,6 @@ import com.huanchengfly.tieba.post.utils.EmoticonManager
|
|||
import com.huanchengfly.tieba.post.utils.EmoticonUtil.emoticonString
|
||||
import com.huanchengfly.tieba.post.utils.getEmoticonHeightPx
|
||||
|
||||
|
||||
@Composable
|
||||
fun EmoticonText(
|
||||
text: String,
|
||||
|
|
@ -100,6 +103,7 @@ fun EmoticonText(
|
|||
softWrap: Boolean = true,
|
||||
maxLines: Int = Int.MAX_VALUE,
|
||||
minLines: Int = 1,
|
||||
emoticonSize: Float = 0.9f,
|
||||
inlineContent: Map<String, InlineTextContent> = emptyMap(),
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
||||
style: TextStyle = LocalTextStyle.current
|
||||
|
|
@ -122,7 +126,7 @@ fun EmoticonText(
|
|||
letterSpacing = letterSpacing
|
||||
)
|
||||
)
|
||||
val sizePx = getEmoticonHeightPx(mergedStyle)
|
||||
val sizePx = getEmoticonHeightPx(mergedStyle) * emoticonSize
|
||||
val emoticonInlineContent =
|
||||
remember(sizePx) { EmoticonManager.getEmoticonInlineContent(sizePx) }
|
||||
IconText(
|
||||
|
|
@ -203,7 +207,22 @@ fun IconText(
|
|||
Icons.Rounded.Link,
|
||||
contentDescription = stringResource(id = R.string.link),
|
||||
modifier = Modifier.size(sizeDp),
|
||||
tint = ExtendedTheme.colors.accent,
|
||||
tint = ExtendedTheme.colors.primary,
|
||||
)
|
||||
}
|
||||
),
|
||||
"video_icon" to InlineTextContent(
|
||||
placeholder = Placeholder(
|
||||
width = sizeSp,
|
||||
height = sizeSp,
|
||||
placeholderVerticalAlign = PlaceholderVerticalAlign.TextCenter
|
||||
),
|
||||
children = {
|
||||
Icon(
|
||||
Icons.Rounded.SlowMotionVideo,
|
||||
contentDescription = stringResource(id = R.string.desc_video),
|
||||
modifier = Modifier.size(sizeDp),
|
||||
tint = ExtendedTheme.colors.primary,
|
||||
)
|
||||
}
|
||||
),
|
||||
|
|
@ -218,7 +237,7 @@ fun IconText(
|
|||
Icons.Rounded.AccountCircle,
|
||||
contentDescription = stringResource(id = R.string.user),
|
||||
modifier = Modifier.size(sizeDp),
|
||||
tint = ExtendedTheme.colors.accent,
|
||||
tint = ExtendedTheme.colors.primary,
|
||||
)
|
||||
}
|
||||
),
|
||||
|
|
@ -244,4 +263,61 @@ fun IconText(
|
|||
onTextLayout,
|
||||
style
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A [Text] composable that supports setting its minimum width to width of the specified number of Chinese characters
|
||||
*/
|
||||
@OptIn(ExperimentalTextApi::class)
|
||||
@Composable
|
||||
fun TextWithMinWidth(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = Color.Unspecified,
|
||||
fontSize: TextUnit = TextUnit.Unspecified,
|
||||
fontStyle: FontStyle? = null,
|
||||
fontWeight: FontWeight? = null,
|
||||
fontFamily: FontFamily? = null,
|
||||
letterSpacing: TextUnit = TextUnit.Unspecified,
|
||||
textDecoration: TextDecoration? = null,
|
||||
textAlign: TextAlign? = null,
|
||||
lineHeight: TextUnit = TextUnit.Unspecified,
|
||||
overflow: TextOverflow = TextOverflow.Clip,
|
||||
softWrap: Boolean = true,
|
||||
maxLines: Int = Int.MAX_VALUE,
|
||||
minLines: Int = 1,
|
||||
minLength: Int = 0,
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
||||
style: TextStyle = LocalTextStyle.current,
|
||||
) {
|
||||
val textMeasurer = rememberTextMeasurer()
|
||||
val singleChar = stringResource(id = R.string.single_chinese_char)
|
||||
val singleCharWidth = remember(style) {
|
||||
textMeasurer.measure(
|
||||
text = singleChar,
|
||||
style = style
|
||||
).size.width.pxToDp().dp
|
||||
}
|
||||
|
||||
Text(
|
||||
text = text,
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minWidth = singleCharWidth * minLength)
|
||||
.then(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
|
||||
)
|
||||
}
|
||||
|
|
@ -231,6 +231,58 @@ fun TitleCentredToolbar(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TitleCentredToolbar(
|
||||
title: @Composable () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
navigationIcon: @Composable (() -> Unit)? = null,
|
||||
actions: @Composable (RowScope.() -> Unit) = {},
|
||||
content: @Composable (ColumnScope.() -> Unit) = {},
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(ExtendedTheme.colors.topBar)
|
||||
.then(modifier)
|
||||
) {
|
||||
TopAppBar(
|
||||
backgroundColor = ExtendedTheme.colors.topBar,
|
||||
contentColor = ExtendedTheme.colors.onTopBar,
|
||||
elevation = 0.dp
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxHeight()
|
||||
) {
|
||||
navigationIcon?.invoke()
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
actions()
|
||||
}
|
||||
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.align(Alignment.Center),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
title()
|
||||
}
|
||||
}
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(color = ExtendedTheme.colors.topBar)
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Toolbar(
|
||||
title: String,
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ object EmoticonManager {
|
|||
private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
fun getEmoticonInlineContent(
|
||||
sizePx: Int
|
||||
sizePx: Float
|
||||
): Map<String, InlineTextContent> {
|
||||
return emoticonIds.associate { id ->
|
||||
"Emoticon#$id" to InlineTextContent(
|
||||
|
|
|
|||
Loading…
Reference in New Issue