TextEdit caret is lost in Arabic/Hebrew text since 0.35: rows hold more glyphs than characters
Describe the bug
Since 0.35 (harfrust shaping, #8031) a galley row can hold more Glyphs than characters, and everything that edits text assumes they are equal (Row::char_at, x_offset, cursor_from_pos, the selection painter). Two ways to get there:
- Any right-to-left run. harfrust returns the glyphs in visual order, so cluster offsets decrease through the buffer, and the continuation-glyph bookkeeping in
layout_shaped_run(written for increasing offsets) pads the wrong ranges; the finalcluster_start_byte..run_text.len()range then re-pads most of the run. - Any font that composes a letter from a base glyph plus marks (Noto Naskh Arabic's
ب, Hack'sǻ): two shaped glyphs, one character, no padding rule for that direction.
Measured with layout_no_wrap at 26pt, Inter + Noto Naskh Arabic registered ahead of the defaults:
| text | chars | glyphs 0.34.3 | glyphs 0.36.2 |
|---|---|---|---|
عدد 123 مرحبا |
13 | 13 | 20 |
مرحبا World |
11 | 11 | 16 |
שלום 42 עולם |
12 | 12 | 18 |
Hello 123 world |
15 | 15 | 15 |
In a TextEdit holding عدد 123 مرحبا: click, End, ArrowLeft ×3, type x.
0.34.3 inserts at logical index 10 → عدد 123 مرxحبا. 0.36.2 → عدد 123 مرحباx: the caret never left the end.
The same rows also render with the words mirrored (مرحبا 123 عدد for عدد 123 مرحبا), because each RTL run comes back visually ordered but the runs are still placed left to right. That part is #1016; I list it here because the same code path causes both.
To Reproduce
// egui_kittest, any renderer
let galley = ctx.fonts_mut(|f| {
f.layout_no_wrap("عدد 123 مرحبا".to_owned(), FontId::proportional(26.0), Color32::WHITE)
});
assert_eq!(galley.rows[0].glyphs.len(), "عدد 123 مرحبا".chars().count()); // 20 != 13 on 0.36.2Then the TextEdit sequence above.
Expected behavior
One Glyph per character in every row, so the caret and selection track the text; RTL words in reading order.
Screenshots
0.34.3 (no shaping, logical order, editable):

0.36.2 (shaped, words mirrored, caret lost):

Desktop
- OS: Linux, native wgpu (llvmpipe under
egui_kittest) - Version: 0.36.2;
mainat 7ba3dbc4 has the samelayout_shaped_run.
Additional context
Found while moving an RTL-first widget library onto stock egui text. I have a fix for both on a branch and will open it as a draft PR against this issue.
Source: emilk/egui