Calculate the text width of a space outside a composable.
This commit is contained in:
@@ -11,7 +11,7 @@ import com.vitorpamplona.amethyst.ui.components.ZoomableUrlImage
|
|||||||
import com.vitorpamplona.amethyst.ui.components.ZoomableUrlVideo
|
import com.vitorpamplona.amethyst.ui.components.ZoomableUrlVideo
|
||||||
import com.vitorpamplona.amethyst.ui.components.hashTagsPattern
|
import com.vitorpamplona.amethyst.ui.components.hashTagsPattern
|
||||||
import com.vitorpamplona.amethyst.ui.components.imageExtensions
|
import com.vitorpamplona.amethyst.ui.components.imageExtensions
|
||||||
import com.vitorpamplona.amethyst.ui.components.removeQueryParams
|
import com.vitorpamplona.amethyst.ui.components.removeQueryParamsForExtensionComparison
|
||||||
import com.vitorpamplona.amethyst.ui.components.tagIndex
|
import com.vitorpamplona.amethyst.ui.components.tagIndex
|
||||||
import com.vitorpamplona.amethyst.ui.components.videoExtensions
|
import com.vitorpamplona.amethyst.ui.components.videoExtensions
|
||||||
import com.vitorpamplona.quartz.events.ImmutableListOfLists
|
import com.vitorpamplona.quartz.events.ImmutableListOfLists
|
||||||
@@ -88,7 +88,7 @@ class RichTextParser() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val imagesForPager = urlSet.mapNotNull { fullUrl ->
|
val imagesForPager = urlSet.mapNotNull { fullUrl ->
|
||||||
val removedParamsFromUrl = removeQueryParams(fullUrl)
|
val removedParamsFromUrl = removeQueryParamsForExtensionComparison(fullUrl)
|
||||||
if (imageExtensions.any { removedParamsFromUrl.endsWith(it) }) {
|
if (imageExtensions.any { removedParamsFromUrl.endsWith(it) }) {
|
||||||
val frags = URI(fullUrl).fragments()
|
val frags = URI(fullUrl).fragments()
|
||||||
ZoomableUrlImage(
|
ZoomableUrlImage(
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class MarkdownParser {
|
|||||||
content.split('\n').forEach { paragraph ->
|
content.split('\n').forEach { paragraph ->
|
||||||
paragraph.split(' ').forEach { word: String ->
|
paragraph.split(' ').forEach { word: String ->
|
||||||
if (isValidURL(word)) {
|
if (isValidURL(word)) {
|
||||||
val removedParamsFromUrl = removeQueryParams(word)
|
val removedParamsFromUrl = removeQueryParamsForExtensionComparison(word)
|
||||||
if (imageExtensions.any { removedParamsFromUrl.endsWith(it) }) {
|
if (imageExtensions.any { removedParamsFromUrl.endsWith(it) }) {
|
||||||
returnContent += " "
|
returnContent += " "
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -30,17 +30,18 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.takeOrElse
|
import androidx.compose.ui.graphics.takeOrElse
|
||||||
import androidx.compose.ui.layout.SubcomposeLayout
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.platform.LocalFontFamilyResolver
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.Placeholder
|
import androidx.compose.ui.text.Placeholder
|
||||||
import androidx.compose.ui.text.PlaceholderVerticalAlign
|
import androidx.compose.ui.text.PlaceholderVerticalAlign
|
||||||
import androidx.compose.ui.text.SpanStyle
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.TextMeasurer
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.buildAnnotatedString
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
import androidx.compose.ui.text.withStyle
|
import androidx.compose.ui.text.withStyle
|
||||||
import androidx.compose.ui.unit.Constraints
|
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.LayoutDirection
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
import androidx.compose.ui.unit.em
|
import androidx.compose.ui.unit.em
|
||||||
@@ -95,7 +96,7 @@ val videoExtensions = listOf("mp4", "avi", "wmv", "mpg", "amv", "webm", "mov", "
|
|||||||
val tagIndex = Pattern.compile("\\#\\[([0-9]+)\\](.*)")
|
val tagIndex = Pattern.compile("\\#\\[([0-9]+)\\](.*)")
|
||||||
val hashTagsPattern: Pattern = Pattern.compile("#([^\\s!@#\$%^&*()=+./,\\[{\\]};:'\"?><]+)(.*)", Pattern.CASE_INSENSITIVE)
|
val hashTagsPattern: Pattern = Pattern.compile("#([^\\s!@#\$%^&*()=+./,\\[{\\]};:'\"?><]+)(.*)", Pattern.CASE_INSENSITIVE)
|
||||||
|
|
||||||
fun removeQueryParams(fullUrl: String): String {
|
fun removeQueryParamsForExtensionComparison(fullUrl: String): String {
|
||||||
return if (fullUrl.contains("?")) {
|
return if (fullUrl.contains("?")) {
|
||||||
fullUrl.split("?")[0].lowercase()
|
fullUrl.split("?")[0].lowercase()
|
||||||
} else if (fullUrl.contains("#")) {
|
} else if (fullUrl.contains("#")) {
|
||||||
@@ -170,59 +171,59 @@ private fun RenderRegular(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
MeasureSpaceWidth() { spaceWidth ->
|
val spaceWidth = measureSpaceWidth(textStyle)
|
||||||
Column() {
|
|
||||||
if (canPreview) {
|
|
||||||
// FlowRow doesn't work well with paragraphs. So we need to split them
|
|
||||||
state.paragraphs.forEach { paragraph ->
|
|
||||||
val direction = if (paragraph.isRTL) {
|
|
||||||
LayoutDirection.Rtl
|
|
||||||
} else {
|
|
||||||
LayoutDirection.Ltr
|
|
||||||
}
|
|
||||||
|
|
||||||
CompositionLocalProvider(LocalLayoutDirection provides direction) {
|
Column() {
|
||||||
FlowRow(
|
if (canPreview) {
|
||||||
modifier = Modifier.align(if (paragraph.isRTL) Alignment.End else Alignment.Start),
|
// FlowRow doesn't work well with paragraphs. So we need to split them
|
||||||
horizontalArrangement = Arrangement.spacedBy(spaceWidth)
|
state.paragraphs.forEach { paragraph ->
|
||||||
) {
|
val direction = if (paragraph.isRTL) {
|
||||||
paragraph.words.forEach { word ->
|
LayoutDirection.Rtl
|
||||||
RenderWordWithPreview(
|
} else {
|
||||||
word,
|
LayoutDirection.Ltr
|
||||||
state,
|
}
|
||||||
backgroundColor,
|
|
||||||
textStyle,
|
CompositionLocalProvider(LocalLayoutDirection provides direction) {
|
||||||
accountViewModel,
|
FlowRow(
|
||||||
nav
|
modifier = Modifier.align(if (paragraph.isRTL) Alignment.End else Alignment.Start),
|
||||||
)
|
horizontalArrangement = Arrangement.spacedBy(spaceWidth)
|
||||||
}
|
) {
|
||||||
|
paragraph.words.forEach { word ->
|
||||||
|
RenderWordWithPreview(
|
||||||
|
word,
|
||||||
|
state,
|
||||||
|
backgroundColor,
|
||||||
|
textStyle,
|
||||||
|
accountViewModel,
|
||||||
|
nav
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// FlowRow doesn't work well with paragraphs. So we need to split them
|
} else {
|
||||||
state.paragraphs.forEach { paragraph ->
|
// FlowRow doesn't work well with paragraphs. So we need to split them
|
||||||
val direction = if (paragraph.isRTL) {
|
state.paragraphs.forEach { paragraph ->
|
||||||
LayoutDirection.Rtl
|
val direction = if (paragraph.isRTL) {
|
||||||
} else {
|
LayoutDirection.Rtl
|
||||||
LayoutDirection.Ltr
|
} else {
|
||||||
}
|
LayoutDirection.Ltr
|
||||||
|
}
|
||||||
|
|
||||||
CompositionLocalProvider(LocalLayoutDirection provides direction) {
|
CompositionLocalProvider(LocalLayoutDirection provides direction) {
|
||||||
FlowRow(
|
FlowRow(
|
||||||
horizontalArrangement = Arrangement.spacedBy(spaceWidth),
|
horizontalArrangement = Arrangement.spacedBy(spaceWidth),
|
||||||
modifier = Modifier.align(if (paragraph.isRTL) Alignment.End else Alignment.Start)
|
modifier = Modifier.align(if (paragraph.isRTL) Alignment.End else Alignment.Start)
|
||||||
) {
|
) {
|
||||||
paragraph.words.forEach { word ->
|
paragraph.words.forEach { word ->
|
||||||
RenderWordWithoutPreview(
|
RenderWordWithoutPreview(
|
||||||
word,
|
word,
|
||||||
state,
|
state,
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
textStyle,
|
textStyle,
|
||||||
accountViewModel,
|
accountViewModel,
|
||||||
nav
|
nav
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,17 +233,15 @@ private fun RenderRegular(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MeasureSpaceWidth(
|
fun measureSpaceWidth(textStyle: TextStyle): Dp {
|
||||||
content: @Composable (measuredWidth: Dp) -> Unit
|
val fontFamilyResolver = LocalFontFamilyResolver.current
|
||||||
) {
|
val density = LocalDensity.current
|
||||||
SubcomposeLayout { constraints ->
|
val layoutDirection = LocalLayoutDirection.current
|
||||||
val measuredWidth = subcompose("viewToMeasure", { Text(" ") })[0].measure(Constraints()).width.toDp()
|
|
||||||
|
|
||||||
val contentPlaceable = subcompose("content") {
|
return remember(fontFamilyResolver, density, layoutDirection, textStyle) {
|
||||||
content(measuredWidth)
|
val widthPx = TextMeasurer(fontFamilyResolver, density, layoutDirection, 1).measure(" ", textStyle).size.width
|
||||||
}[0].measure(constraints)
|
with(density) {
|
||||||
layout(contentPlaceable.width, contentPlaceable.height) {
|
widthPx.toDp()
|
||||||
contentPlaceable.place(0, 0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -567,7 +566,7 @@ private fun RenderHashtag(
|
|||||||
) {
|
) {
|
||||||
val primary = MaterialTheme.colorScheme.primary
|
val primary = MaterialTheme.colorScheme.primary
|
||||||
val background = MaterialTheme.colorScheme.onBackground
|
val background = MaterialTheme.colorScheme.onBackground
|
||||||
val hashtagIcon = remember(segment.hashtag) {
|
val hashtagIcon: HashtagIcon? = remember(segment.hashtag) {
|
||||||
checkForHashtagWithIcon(segment.hashtag, primary)
|
checkForHashtagWithIcon(segment.hashtag, primary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ class ZoomableLocalVideo(
|
|||||||
) : ZoomablePreloadedContent(localFile, description, mimeType, isVerified, dim, uri)
|
) : ZoomablePreloadedContent(localFile, description, mimeType, isVerified, dim, uri)
|
||||||
|
|
||||||
fun figureOutMimeType(fullUrl: String): ZoomableContent {
|
fun figureOutMimeType(fullUrl: String): ZoomableContent {
|
||||||
val removedParamsFromUrl = removeQueryParams(fullUrl)
|
val removedParamsFromUrl = removeQueryParamsForExtensionComparison(fullUrl)
|
||||||
val isImage = imageExtensions.any { removedParamsFromUrl.endsWith(it) }
|
val isImage = imageExtensions.any { removedParamsFromUrl.endsWith(it) }
|
||||||
val isVideo = videoExtensions.any { removedParamsFromUrl.endsWith(it) }
|
val isVideo = videoExtensions.any { removedParamsFromUrl.endsWith(it) }
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ import com.vitorpamplona.amethyst.ui.components.CreateClickableTextWithEmoji
|
|||||||
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||||
import com.vitorpamplona.amethyst.ui.components.LoadThumbAndThenVideoView
|
import com.vitorpamplona.amethyst.ui.components.LoadThumbAndThenVideoView
|
||||||
import com.vitorpamplona.amethyst.ui.components.MeasureSpaceWidth
|
|
||||||
import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status
|
import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status
|
||||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||||
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
||||||
@@ -113,7 +112,8 @@ import com.vitorpamplona.amethyst.ui.components.ZoomableUrlImage
|
|||||||
import com.vitorpamplona.amethyst.ui.components.ZoomableUrlVideo
|
import com.vitorpamplona.amethyst.ui.components.ZoomableUrlVideo
|
||||||
import com.vitorpamplona.amethyst.ui.components.figureOutMimeType
|
import com.vitorpamplona.amethyst.ui.components.figureOutMimeType
|
||||||
import com.vitorpamplona.amethyst.ui.components.imageExtensions
|
import com.vitorpamplona.amethyst.ui.components.imageExtensions
|
||||||
import com.vitorpamplona.amethyst.ui.components.removeQueryParams
|
import com.vitorpamplona.amethyst.ui.components.measureSpaceWidth
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.removeQueryParamsForExtensionComparison
|
||||||
import com.vitorpamplona.amethyst.ui.screen.equalImmutableLists
|
import com.vitorpamplona.amethyst.ui.screen.equalImmutableLists
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ChannelHeader
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ChannelHeader
|
||||||
@@ -3024,19 +3024,19 @@ private fun DisplayQuoteAuthor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MeasureSpaceWidth {
|
val spaceWidth = measureSpaceWidth(textStyle = LocalTextStyle.current)
|
||||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(it), verticalArrangement = Arrangement.Center) {
|
|
||||||
userBase?.let { userBase ->
|
|
||||||
LoadAndDisplayUser(userBase, nav)
|
|
||||||
}
|
|
||||||
|
|
||||||
url?.let { url ->
|
FlowRow(horizontalArrangement = Arrangement.spacedBy(spaceWidth), verticalArrangement = Arrangement.Center) {
|
||||||
LoadAndDisplayUrl(url)
|
userBase?.let { userBase ->
|
||||||
}
|
LoadAndDisplayUser(userBase, nav)
|
||||||
|
}
|
||||||
|
|
||||||
postAddress?.let { address ->
|
url?.let { url ->
|
||||||
LoadAndDisplayPost(address, accountViewModel, nav)
|
LoadAndDisplayUrl(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postAddress?.let { address ->
|
||||||
|
LoadAndDisplayPost(address, accountViewModel, nav)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3455,7 +3455,7 @@ fun FileHeaderDisplay(note: Note, roundedCorner: Boolean, accountViewModel: Acco
|
|||||||
val hash = event.hash()
|
val hash = event.hash()
|
||||||
val dimensions = event.dimensions()
|
val dimensions = event.dimensions()
|
||||||
val description = event.alt() ?: event.content
|
val description = event.alt() ?: event.content
|
||||||
val isImage = imageExtensions.any { removeQueryParams(fullUrl).lowercase().endsWith(it) }
|
val isImage = imageExtensions.any { removeQueryParamsForExtensionComparison(fullUrl).lowercase().endsWith(it) }
|
||||||
val uri = note.toNostrUri()
|
val uri = note.toNostrUri()
|
||||||
|
|
||||||
mutableStateOf<ZoomableContent>(
|
mutableStateOf<ZoomableContent>(
|
||||||
|
|||||||
Reference in New Issue
Block a user