Fix the cursor positioning issue when clicking on formatted list items and paragraphs (elements containing bold, italic, code, links, etc.) in rendered and split view modes. Currently, when a user clicks on these elements to enter edit mode, the cursor appears at an incorrect position - often significantly to the right of where they clicked.
Ferrite uses a "click-to-edit" pattern for formatted content:
- Display Mode: Shows rendered markdown (bold text appears styled,
**markers hidden) - Edit Mode: Shows raw markdown (includes
**,*,`,[](url)markers)
When the user clicks to enter edit mode, the cursor must be positioned in the raw text based on where they clicked in the displayed text. The challenge is that these have different character counts and the mapping is non-linear.
- Clicking near the middle of "Click here to see" positions cursor incorrectly
- The drift is worse on longer lines
- Users must manually reposition cursor after clicking
- Cursor should appear at or very near the clicked position
- Position should be accurate regardless of line length or formatting complexity
Use egui's Galley text layout system to accurately map click positions to character indices.
// Create a Galley from displayed text
let galley = ui.painter().layout_no_wrap(displayed_text, font_id, color);
// Convert click position to character cursor
let cursor = galley.cursor_from_pos(click_pos - text_rect.min);
let displayed_char_index = cursor.ccursor.index;-
On Click (Display Mode):
- Capture click position in screen coordinates
- Get the displayed text (without formatting markers)
- Create a Galley from the displayed text using the same font
- Use
galley.cursor_from_pos()to get exact character index in displayed text - Build a mapping from displayed text position to raw text position
- Store the raw text cursor position in
FormattedItemEditState.pending_cursor_pos
-
On Edit Mode Enter:
- Apply the pending cursor position to the TextEdit (existing code)
The displayed text and raw text differ due to formatting markers. We need to map positions:
| Raw Text | Displayed Text | Raw Index | Displayed Index |
|---|---|---|---|
C |
C |
0 | 0 |
l |
l |
1 | 1 |
| ... | ... | ... | ... |
* |
(hidden) | 6 | - |
* |
(hidden) | 7 | - |
h |
h |
8 | 6 |
e |
e |
9 | 7 |
| ... | ... | ... | ... |
Mapping Function: Walk through both texts in parallel, tracking formatting markers in raw text, to build the correspondence.
src/markdown/editor.rs:FormattedItemEditStatestructrender_paragraph_sk()function (2 locations)render_list_item()function (2 locations)
- Clicking on formatted text positions cursor within 1-2 characters of click location
- Works correctly for all inline formatting: bold, italic, code, links, strikethrough
- Works correctly for varying line lengths
- No performance regression (Galley creation is lightweight)
- Multi-line paragraph cursor positioning (complex, separate issue)
- Nested formatting edge cases (defer to 0.3.0 if problematic)
- Create test file with various formatted list items:
- This is **bold** text- Click *here* to edit- Seecodeand **more**- Link to [example](url) here
- Click at various positions in each item
- Verify cursor appears near click location