Skip to content

Commit 8d43aeb

Browse files
omers-oaicodex
andcommitted
Avoid intrinsic sizing for RTL markdown documents
Co-authored-by: Codex <noreply@openai.com>
1 parent 1c98faf commit 8d43aeb

2 files changed

Lines changed: 74 additions & 19 deletions

File tree

android-sample/src/main/java/com/zachklipp/richtext/sample/RtlCompatibilityBehaviorSample.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,9 @@ private fun TableBehaviorPreview() {
100100
BehaviorPreviewSurface {
101101
BehaviorPreviewColumn {
102102
BehaviorSection(
103-
title = "Markdown table renders in RTL layout",
103+
title = "Markdown table renders with RTL compatibility enabled",
104104
markdown = tableMarkdown,
105+
richTextRenderOptions = RichTextRenderOptions(enableRtlCompatibility = true),
105106
)
106107
}
107108
}
@@ -138,6 +139,7 @@ private fun BehaviorPreviewColumn(
138139
private fun BehaviorSection(
139140
title: String,
140141
markdown: String,
142+
richTextRenderOptions: RichTextRenderOptions = RichTextRenderOptions(),
141143
) {
142144
Column(
143145
modifier = Modifier.fillMaxWidth(),
@@ -147,18 +149,22 @@ private fun BehaviorSection(
147149
text = title,
148150
style = MaterialTheme.typography.labelLarge,
149151
)
150-
BehaviorMarkdown(markdown = markdown)
152+
BehaviorMarkdown(
153+
markdown = markdown,
154+
richTextRenderOptions = richTextRenderOptions,
155+
)
151156
}
152157
}
153158

154159
@Composable
155160
private fun BehaviorMarkdown(
156161
markdown: String,
162+
richTextRenderOptions: RichTextRenderOptions,
157163
) {
158164
RichText(modifier = Modifier.fillMaxWidth()) {
159165
Markdown(
160166
content = markdown,
161-
richtextRenderOptions = RichTextRenderOptions(),
167+
richtextRenderOptions = richTextRenderOptions,
162168
)
163169
}
164170
}

richtext-markdown/src/commonMain/kotlin/com/halilibo/richtext/markdown/BasicMarkdown.kt

Lines changed: 65 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package com.halilibo.richtext.markdown
22

3-
import androidx.compose.foundation.layout.IntrinsicSize
43
import androidx.compose.foundation.layout.fillMaxWidth
5-
import androidx.compose.foundation.layout.width
64
import androidx.compose.foundation.text.BasicText
75
import androidx.compose.runtime.Composable
86
import androidx.compose.runtime.CompositionLocalProvider
97
import androidx.compose.runtime.remember
108
import androidx.compose.ui.Modifier
9+
import androidx.compose.ui.layout.SubcomposeLayout
1110
import androidx.compose.ui.semantics.heading
1211
import androidx.compose.ui.semantics.semantics
1312
import com.halilibo.richtext.markdown.rtl.LocalCompatibilityTextAlignOverride
@@ -95,29 +94,79 @@ public fun RichTextScope.BasicMarkdown(
9594
) {
9695
val markdownAnimationState = remember { MarkdownAnimationState() }
9796

98-
if (richTextRenderOptions.enableRtlCompatibility && astNode.type is AstDocument) {
99-
BasicRichText(modifier = Modifier.width(IntrinsicSize.Max)) {
100-
RecursiveRenderMarkdownAst(
101-
astNode = astNode,
102-
contentOverride = contentOverride,
103-
inlineContentOverride = inlineContentOverride,
104-
richTextRenderOptions = richTextRenderOptions,
105-
richTextDecorations = richTextDecorations,
106-
markdownAnimationState = markdownAnimationState,
107-
astNodeComposer = astBlockNodeComposer,
108-
)
109-
}
110-
} else {
97+
@Composable
98+
fun RenderMarkdown(renderOptions: RichTextRenderOptions) {
11199
RecursiveRenderMarkdownAst(
112100
astNode = astNode,
113101
contentOverride = contentOverride,
114102
inlineContentOverride = inlineContentOverride,
115-
richTextRenderOptions = richTextRenderOptions,
103+
richTextRenderOptions = renderOptions,
116104
richTextDecorations = richTextDecorations,
117105
markdownAnimationState = markdownAnimationState,
118106
astNodeComposer = astBlockNodeComposer,
119107
)
120108
}
109+
110+
if (richTextRenderOptions.enableRtlCompatibility && astNode.type is AstDocument) {
111+
val measureRenderOptions = remember(richTextRenderOptions) {
112+
richTextRenderOptions.copy(
113+
animate = false,
114+
enableRtlCompatibility = false,
115+
)
116+
}
117+
RtlCompatibilityDocument(
118+
measureContent = {
119+
BasicRichText {
120+
RenderMarkdown(measureRenderOptions)
121+
}
122+
},
123+
content = {
124+
BasicRichText {
125+
RenderMarkdown(richTextRenderOptions)
126+
}
127+
},
128+
)
129+
} else {
130+
RenderMarkdown(richTextRenderOptions)
131+
}
132+
}
133+
134+
@Composable
135+
private fun RtlCompatibilityDocument(
136+
measureContent: @Composable () -> Unit,
137+
content: @Composable () -> Unit,
138+
) {
139+
SubcomposeLayout { constraints ->
140+
val measureConstraints = constraints.copy(minWidth = 0, minHeight = 0)
141+
val measuredWidth = subcompose(RtlCompatibilityDocumentSlot.Measure, measureContent)
142+
.map { measurable -> measurable.measure(measureConstraints) }
143+
.maxOfOrNull { placeable -> placeable.width }
144+
?.coerceIn(constraints.minWidth, constraints.maxWidth)
145+
?: constraints.minWidth
146+
147+
val contentConstraints = constraints.copy(
148+
minWidth = measuredWidth,
149+
maxWidth = measuredWidth,
150+
minHeight = 0,
151+
)
152+
val contentPlaceables = subcompose(RtlCompatibilityDocumentSlot.Content, content)
153+
.map { measurable -> measurable.measure(contentConstraints) }
154+
val height = contentPlaceables
155+
.maxOfOrNull { placeable -> placeable.height }
156+
?.coerceIn(constraints.minHeight, constraints.maxHeight)
157+
?: constraints.minHeight
158+
159+
layout(measuredWidth, height) {
160+
contentPlaceables.forEach { placeable ->
161+
placeable.placeRelative(0, 0)
162+
}
163+
}
164+
}
165+
}
166+
167+
private enum class RtlCompatibilityDocumentSlot {
168+
Measure,
169+
Content,
121170
}
122171

123172
/**

0 commit comments

Comments
 (0)