Skip to content

grid: span the cursor over both cells of a wide character - #1974

Open
legibet wants to merge 1 commit into
raphamorim:mainfrom
legibet:fix/wide-cursor
Open

legibet wants to merge 1 commit into
raphamorim:mainfrom
legibet:fix/wide-cursor

Conversation

@legibet

@legibet legibet commented Oct 10, 2026

Copy link
Copy Markdown

Fixes #1972

On a double-width cell the cursor covered only the left cell. The block cursor tinted one cell and inverted half the glyph, and the hollow and underline sprites were one cell wide.

Changes:

  • Crosswords::cursor() already moves the cursor from a spacer back to the wide cell. CursorState now also carries wide.
  • GridUniforms gets cursor_wide, taking half of the existing _pad_cursor, so the size and offsets stay the same (now checked with offset_of! asserts). The Metal, WGSL, GLSL and CPU paths extend the block cursor's background tint and foreground swap to cursor_pos.x + 1.
  • cursor_sprite_cell draws block, hollow and underline sprites two cells wide. The bar stays a left-edge stroke, as in Ghostty.
  • The trail cursor targets two cells for block and underline.
  • While an IME composition is shown the cursor stays one cell, since the composition replaces the cells under it.
  • libsugarloaf gets the same handling through a new RenderState::cursor_wide(). The librio C API is unchanged.

Note: CursorState is public in rio-vt, so code outside this repo that builds it with a struct literal needs the new field.

Testing:

  • New tests: cursor() on narrow, wide and spacer cells (rio-vt), sprite widths per style (rio-grid), background tint and foreground swap on both cells (CPU grid), and trail cursor target width.
  • Tests for rio-vt, rio-grid, sugarloaf, rioterm and librio pass on macOS, along with clippy and fmt. The WGSL passes the existing naga validation and the GLSL compiles with glslc.
  • Checked by hand on macOS (Metal) with block and hollow cursors on CJK text. Not tested on Vulkan, Linux or Windows.

On a double-width cell the cursor covered only the left cell: the block
tinted one cell and inverted half the glyph, and the hollow and
underline sprites were one cell wide.

`Crosswords::cursor()` already moves the cursor from a spacer back to
the wide cell; it now also reports `wide`. The block cursor passes it to
the grid shaders as `cursor_wide` (taking half of the existing uniform
padding), so the background tint and the foreground swap cover both
columns. Block, hollow and underline sprites, and the trail cursor
target, are two cells wide; the bar stays a left-edge stroke. While an
IME composition is shown the cursor stays one cell.

Fixes raphamorim#1972

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Block cursor covers only half of a wide (CJK) character

1 participant