Draw table cells as span-linked text, and hit-test links by glyph
Table rows are now ours: each cell is a LinkedText, so a link in a cell is a span with a string annotation and one tap detector per cell rather than the layout node Compose builds for every LinkAnnotation -- the cost the paragraph change removed everywhere else. The renderer's outer table (width, sideways scroll, corners, dividers) stays; the row and cell were the only parts it offered no slot for. Cells still wrap and align to the top, with the same semantics the renderer gave them. Found while checking it: the hit test took the layout's nearest caret as the glyph under the finger, so a tap on the right half of any link glyph named the character after it and opened nothing. That was flaky in paragraphs already; it now checks the glyph on either side of the caret. Verified on the emulator: a linked cell and an autolink cell open their addresses, a plain cell opens nothing, a six-column table still scrolls sideways, and a link-free table draws as before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
3c3b4e62fd
commit
a9ab9b3e5a
2 files changed
+92
-61
No files matched your search
@@ -44,26 +44,35 @@ import org.intellij.markdown.flavours.gfm.GFMTokenTypes
|
||||
* style never defined a pressed style, so nothing visible changes.
|
||||
*
|
||||
* Every block the renderer dispatches through its component table comes here, which includes the
|
||||
* paragraphs inside lists, quotes and alerts. Table cells do not: the table draws its own cells and
|
||||
* offers no slot for them, so a link in a cell keeps the renderer's path -- correct, and dearer.
|
||||
* Reference-style links stay there too; the renderer resolves those against its definitions.
|
||||
* paragraphs inside lists, quotes and alerts, and so does every table cell through
|
||||
* [LinkedTableRow]. Reference-style links are the one kind still drawn the renderer's way; it
|
||||
* resolves those against its definitions.
|
||||
*/
|
||||
@Composable
|
||||
fun LinkedText(model: MarkdownComponentModel, style: TextStyle, heading: Boolean = false) {
|
||||
LinkedText(
|
||||
model.content,
|
||||
model.node,
|
||||
style,
|
||||
if (heading) Modifier.semantics { heading() } else Modifier,
|
||||
)
|
||||
}
|
||||
|
||||
/** The inline content of [node] within [content], drawn as [LinkedText] describes. */
|
||||
@Composable
|
||||
fun LinkedText(content: String, node: ASTNode, style: TextStyle, modifier: Modifier = Modifier) {
|
||||
val settings = plainLinkSettings()
|
||||
val text =
|
||||
remember(model.content, model.node, style) {
|
||||
model.content.buildMarkdownAnnotatedString(model.node, style, settings)
|
||||
remember(content, node, style) {
|
||||
content.buildMarkdownAnnotatedString(node, style, settings)
|
||||
}
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val layout = remember { Ref<TextLayoutResult>() }
|
||||
MarkdownText(
|
||||
content = text,
|
||||
node = model.node,
|
||||
node = node,
|
||||
modifier =
|
||||
Modifier.then(if (heading) Modifier.semantics { heading() } else Modifier).pointerInput(
|
||||
text
|
||||
) {
|
||||
modifier.pointerInput(text) {
|
||||
detectTapGestures { position ->
|
||||
val url = text.linkAt(layout.value, position) ?: return@detectTapGestures
|
||||
uriHandler.openUri(url)
|
||||
@@ -74,12 +83,22 @@ fun LinkedText(model: MarkdownComponentModel, style: TextStyle, heading: Boolean
|
||||
)
|
||||
}
|
||||
|
||||
/** The address under [position], if a link's glyph is there rather than merely nearest to it. */
|
||||
/**
|
||||
* The address under [position], if a link's glyph is there rather than merely nearest to it.
|
||||
*
|
||||
* The layout answers with a caret, the boundary nearest the finger, so a tap on the right half of a
|
||||
* glyph names the character after it; the glyph under the finger is the one on either side of that
|
||||
* boundary whose box holds the point. Checked with the box rather than assumed, so a tap past the
|
||||
* end of a line ending in a link opens nothing.
|
||||
*/
|
||||
private fun AnnotatedString.linkAt(layout: TextLayoutResult?, position: Offset): String? {
|
||||
layout ?: return null
|
||||
val offset = layout.getOffsetForPosition(position)
|
||||
if (offset >= length || !layout.getBoundingBox(offset).contains(position)) return null
|
||||
return getStringAnnotations(LINK_URL, offset, offset).firstOrNull()?.item
|
||||
val caret = layout.getOffsetForPosition(position)
|
||||
val glyph =
|
||||
(caret - 1..caret).firstOrNull {
|
||||
it in 0 until length && layout.getBoundingBox(it).contains(position)
|
||||
} ?: return null
|
||||
return getStringAnnotations(LINK_URL, glyph, glyph + 1).firstOrNull()?.item
|
||||
}
|
||||
|
||||
private const val LINK_URL = "url"
|
||||
|
||||
Reference in new issue
Block a user