Repository navigation
mixin_markdown_widget: resolve selection geometry from text leaves - #544
Merged
boyan01 merged 4 commits intoOct 21, 2026
Conversation
boyan01
added this pull request to stack #545
October 20, 2026 12:01
… leaf refactor Add safety-net tests that pass on the current renderer so the selection geometry can be restructured without visual or behavioral drift. - selection_geometry_snapshot_test.dart records the block-local rects, clip rect, and paint order that each block's selection painter receives for paragraphs, list items with marker prefixes, nested lists, nested quotes with headings, details, definition lists, footnotes, table cells across rows, a scrolled table inside a quote, a scrolled code block, and inline code/math. Rects are compared within 0.5px. - selection_gesture_edge_test.dart drives mouse gestures for gaps between quote/list children, list markers, pointers past a line end, drags across top-level blocks and into blank space, double/triple clicks in nested lists, quotes and table cells, link taps with selection enabled, and copying a drag selection that spans a block scrolled out of a ListView.
Each selectable text leaf now answers its own selection geometry, and container blocks only order leaves. - Cache StructuredBlockSelection and serialized block text per BlockNode. The plain text serializer is the single source for copy, clamping, and select-all; the selection controller delegates to it, so a serializer subclass changes both consistently. - Add MarkdownSelectableLeaf: text, code, table cell, and placeholder leaves register by PathInBlock in a per-block scope and expose rects, offsets, line ranges, an optional viewport clip, and their origin. - Replace the per-container resolvers with one container algorithm for quotes, details, lists, footnotes, and tables. Leaf-local and block display offsets are converted once, through the structured leaves. Container specs now take their text from the structured selection. - Remove the old selection resolver, descriptor-based unit ranges, and the per-child GlobalKey maps for list items, quote/details children, and table cells. Block keys, scroll controllers, and viewport keys stay. Public API and selection semantics are unchanged.
Flutter tooling updated the example project when running it on macOS.
…f a leaf Double clicking the right half of the last character of a table cell, quote paragraph, list item, or details summary selected the word of the next leaf instead. Hit testing turns the pointer into a block display offset and resolves it downstream, so the end of a leaf lands on the start of the next leaf, and word selection then looked up the word there. For a table such as `| Alpha | | Charlie |`, the word was `Charlie` and copy returned the wrong cell. selectWord already detects that the pointer is over the character before the offset; it now resolves the position upstream in that case, so the word comes from the leaf that ends there.
boyan01
force-pushed
the
mixin_markdown_widget/selection-leaf-refactor
branch
from
October 20, 2026 13:29
6e90993 to
d49526b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #543. Selection geometry moves from one resolver that walks GlobalKey registries and re-implements hit testing per container kind, to text leaves that answer their own geometry. Offsets convert between leaf-local and block display space once, in
StructuredBlockSelection.StructuredBlockSelection.forBlockis cached per block (Expando) and is the single source for serializer, clamp, and select-all. The controller always delegates to its serializer; theruntimeTypefast path is gone, and custom serializer subclasses still work.AGENTS.mdupdated; removed the staleTableCellSelectiondescription.markdown_selection_resolver.dart→markdown_block_keys_registry.dartmarkdown_descriptor_extractor.dartmarkdown_block_builder.dartselection_controller.dartmarkdown_selectable_leaf.dartmarkdown_selection_geometry.dartNet: −4803 / +2233 lines in
lib/.Commits:
pin selection geometry and gestures before the leaf refactor: tests only, on the old implementation.resolve selection geometry from text leaves: the refactor, no test file changed.Evidence
selection_geometry_snapshot_test.dart: painter rect snapshots within 0.5px for paragraphs, nested lists/quotes, details, deflists, footnotes, tables incl. scrolled-in-quote clipping, scrolled code;selection_gesture_edge_test.dart: gaps, markers, line ends, cross-block drags, blank-space drags, multi-click in nested containers, link taps, copy across unmounted ListView rows). Before: 44/44 pass on the oldlib/. After: 44/44 pass on the newlib/with no test file edited.flutter test: 258 passed.flutter analyze: no issues.benchmark/selection_geometry_benchmark.dart(avg per selection update): medium 2056 → 2086–2298 µs, large 1846 → 1568–2129 µs; run-to-run noise, no clear regression.incremental_append_benchmark.dart: 468x → 499x, 1533x → 1603x.Merge Danger
Door: two-way
Internal only; public API and
DocumentPositionsemantics unchanged. Revert restores the resolver.Blast Radius: selection
plainTextnow comes from the structure index instead of descriptors; they agreed in all tested cases.codeBlockBuilderbecomes a placeholder leaf instead of throwing; empty table cells are no longer hit targets; ragged tables no longer risk a RangeError.SelectableMarkdownBlock.selectionRangeaftersetSelection).MixinSelectionAreacross-widget drag, context menu and Cmd+A/C.