mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
render: search: render IME pre-edit text correctly, hopefully
This commit is contained in:
parent
a6ed9a9773
commit
b270f221d0
1 changed files with 117 additions and 62 deletions
179
render.c
179
render.c
|
|
@ -2134,14 +2134,30 @@ render_search_box(struct terminal *term)
|
|||
text_len += wcslen(term->ime.preedit.text);
|
||||
|
||||
wchar_t *text = xmalloc((text_len + 1) * sizeof(wchar_t));
|
||||
wcscpy(text, term->search.buf);
|
||||
text[0] = L'\0';
|
||||
|
||||
/* Copy everything up to the cursor */
|
||||
wcsncpy(text, term->search.buf, term->search.cursor);
|
||||
text[term->search.cursor] = L'\0';
|
||||
|
||||
/* Insert pre-edit text at cursor */
|
||||
if (term->ime.preedit.text != NULL)
|
||||
wcscat(text, term->ime.preedit.text);
|
||||
|
||||
/* And finally everything after the cursor */
|
||||
wcsncat(text, &term->search.buf[term->search.cursor],
|
||||
term->search.len - term->search.cursor);
|
||||
#else
|
||||
const wchar_t *text = term->search.buf;
|
||||
const size_t text_len = term->search.len;
|
||||
#endif
|
||||
|
||||
/* Calculate the width of each character */
|
||||
int widths[text_len + 1];
|
||||
for (size_t i = 0; i < text_len; i++)
|
||||
widths[i] = max(1, wcwidth(text[i]));
|
||||
widths[text_len] = 0;
|
||||
|
||||
const size_t total_cells = wcswidth(text, text_len);
|
||||
const size_t wanted_visible_cells = max(20, total_cells);
|
||||
|
||||
|
|
@ -2184,46 +2200,56 @@ render_search_box(struct terminal *term)
|
|||
int y = margin;
|
||||
pixman_color_t fg = color_hex_to_pixman(term->colors.table[0]);
|
||||
|
||||
/*
|
||||
* Ensure cursor is visible
|
||||
*
|
||||
* First, we need to map the cursor character position to a cell
|
||||
* position. Then we can ensure the cursor is within the rendered
|
||||
* part of the search string.
|
||||
*/
|
||||
size_t cursor_cell_idx = 0;
|
||||
|
||||
for (size_t i = 0, cell_idx = 0;
|
||||
i <= term->search.cursor;
|
||||
cell_idx += max(1, wcwidth(text[i])), i++)
|
||||
{
|
||||
/* Move offset we start rendering at, to ensure the cursor is visible */
|
||||
for (size_t i = 0, cell_idx = 0; i <= term->search.cursor; cell_idx += widths[i], i++) {
|
||||
if (i != term->search.cursor)
|
||||
continue;
|
||||
|
||||
#if 0
|
||||
#if (FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||
if (term->ime.preedit.cells != NULL)
|
||||
cell_idx += term->ime.preedit.count;
|
||||
#endif
|
||||
if (term->ime.preedit.cells != NULL) {
|
||||
if (term->ime.preedit.cursor.start == term->ime.preedit.cursor.end) {
|
||||
/* All IME's I've seen so far keeps the cursor at
|
||||
* index 0, so ensure the *end* of the pre-edit string
|
||||
* is visible */
|
||||
cell_idx += term->ime.preedit.count;
|
||||
} else {
|
||||
/* Try to predict in which direction we'll shift the text */
|
||||
if (cell_idx + term->ime.preedit.cursor.start > glyph_offset)
|
||||
cell_idx += term->ime.preedit.cursor.end;
|
||||
else
|
||||
cell_idx += term->ime.preedit.cursor.start;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cell_idx < glyph_offset)
|
||||
if (cell_idx < glyph_offset) {
|
||||
/* Shift to the *left*, making *this* character the
|
||||
* *first* visible one */
|
||||
term->render.search_glyph_offset = glyph_offset = cell_idx;
|
||||
}
|
||||
|
||||
else if (cell_idx > glyph_offset + visible_cells) {
|
||||
/* Shift to the *right*, making *this* character the
|
||||
* *last* visible one */
|
||||
term->render.search_glyph_offset = glyph_offset =
|
||||
cell_idx - min(cell_idx, visible_cells);
|
||||
}
|
||||
assert(cell_idx >= glyph_offset);
|
||||
cursor_cell_idx = cell_idx - glyph_offset;
|
||||
|
||||
/* Adjust offset if there is free space available */
|
||||
if (total_cells - glyph_offset < visible_cells) {
|
||||
term->render.search_glyph_offset = glyph_offset =
|
||||
total_cells - min(total_cells, visible_cells);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Move offset if there is free space available */
|
||||
if (total_cells - glyph_offset < visible_cells) {
|
||||
ssize_t old = glyph_offset;
|
||||
term->render.search_glyph_offset = glyph_offset =
|
||||
total_cells - min(total_cells, visible_cells);
|
||||
cursor_cell_idx += old - (ssize_t)glyph_offset;
|
||||
/* Ensure offset is at a character boundary */
|
||||
for (size_t i = 0, cell_idx = 0; i <= text_len; cell_idx += widths[i], i++) {
|
||||
if (cell_idx >= glyph_offset) {
|
||||
term->render.search_glyph_offset = glyph_offset = cell_idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2232,23 +2258,75 @@ render_search_box(struct terminal *term)
|
|||
*/
|
||||
for (size_t i = 0,
|
||||
cell_idx = 0,
|
||||
width = max(1, wcwidth(text[i])),
|
||||
width = widths[i],
|
||||
next_cell_idx = width;
|
||||
i < text_len;
|
||||
i++,
|
||||
cell_idx = next_cell_idx,
|
||||
width = max(1, wcwidth(text[i])),
|
||||
width = widths[i],
|
||||
next_cell_idx += width)
|
||||
{
|
||||
#if 0
|
||||
if (i == term->search.cursor)
|
||||
draw_bar(term, buf->pix[0], font, &fg, x, y);
|
||||
#endif
|
||||
/* Render cursor */
|
||||
if (i == term->search.cursor) {
|
||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||
bool have_preedit = term->ime.preedit.cells != NULL;
|
||||
bool hidden = term->ime.preedit.cursor.hidden;
|
||||
|
||||
if (next_cell_idx >= glyph_offset && next_cell_idx - glyph_offset > visible_cells)
|
||||
if (have_preedit && !hidden) {
|
||||
/* Cursor may be outside the visible area:
|
||||
* cell_idx-glyph_offset can be negative */
|
||||
int cells_left = visible_cells - max(
|
||||
(ssize_t)(cell_idx - glyph_offset), 0);
|
||||
|
||||
/* If cursor is outside the visible area, we need to
|
||||
* adjust our rectangle's position */
|
||||
int start = term->ime.preedit.cursor.start
|
||||
+ min((ssize_t)(cell_idx - glyph_offset), 0);
|
||||
int end = term->ime.preedit.cursor.end
|
||||
+ min((ssize_t)(cell_idx - glyph_offset), 0);
|
||||
|
||||
if (start == end) {
|
||||
int count = min(term->ime.preedit.count, cells_left);
|
||||
|
||||
/* Underline the entire (visible part of) pre-edit text */
|
||||
draw_underline(term, buf->pix[0], font, &fg, x, y, count);
|
||||
|
||||
/* Bar-styled cursor, if in the visible area */
|
||||
if (start >= 0 && start <= visible_cells)
|
||||
draw_bar(term, buf->pix[0], font, &fg, x + start * term->cell_width, y);
|
||||
} else {
|
||||
/* Underline everything before and after the cursor */
|
||||
int count1 = min(start, cells_left);
|
||||
int count2 = max(
|
||||
min(term->ime.preedit.count - term->ime.preedit.cursor.end,
|
||||
cells_left - end),
|
||||
0);
|
||||
draw_underline(term, buf->pix[0], font, &fg, x, y, count1);
|
||||
draw_underline(term, buf->pix[0], font, &fg, x + end * term->cell_width, y, count2);
|
||||
|
||||
/* TODO: how do we handle a partially hidden rectangle? */
|
||||
if (start >= 0 && end <= visible_cells) {
|
||||
draw_unfocused_block(
|
||||
term, buf->pix[0], &fg, x + start * term->cell_width, y, end - start);
|
||||
}
|
||||
}
|
||||
} else if (!have_preedit)
|
||||
#endif
|
||||
{
|
||||
/* Cursor *should* be in the visible area */
|
||||
assert(cell_idx >= glyph_offset);
|
||||
assert(cell_idx <= glyph_offset + visible_cells);
|
||||
draw_bar(term, buf->pix[0], font, &fg, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
if (next_cell_idx >= glyph_offset && next_cell_idx - glyph_offset > visible_cells) {
|
||||
/* We're now beyond the visible area - nothing more to render */
|
||||
break;
|
||||
}
|
||||
|
||||
if (cell_idx < glyph_offset) {
|
||||
/* We haven't yet reached the visible part of the string */
|
||||
cell_idx = next_cell_idx;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -2280,36 +2358,13 @@ render_search_box(struct terminal *term)
|
|||
cell_idx = next_cell_idx;
|
||||
}
|
||||
|
||||
|
||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||
|
||||
if (term->ime.preedit.cells != NULL) {
|
||||
int cells_left = visible_cells - cursor_cell_idx;
|
||||
int count = max(min(term->ime.preedit.count, cells_left), 0);
|
||||
|
||||
/* Underline the entire pre-edit text */
|
||||
draw_underline(term, buf->pix[0], font, &fg,
|
||||
x_left + cursor_cell_idx * term->cell_width, y, count);
|
||||
|
||||
/* Cursor, unless hidden */
|
||||
if (!term->ime.preedit.cursor.hidden) {
|
||||
/* TODO: we must ensure this is visible */
|
||||
const int start = cursor_cell_idx + term->ime.preedit.cursor.start;
|
||||
const int end = cursor_cell_idx + term->ime.preedit.cursor.end;
|
||||
|
||||
if (start == end) {
|
||||
draw_bar(term, buf->pix[0], font, &fg,
|
||||
x_left + start * term->cell_width, y);
|
||||
} else {
|
||||
draw_unfocused_block(
|
||||
term, buf->pix[0], &fg,
|
||||
x_left + start * term->cell_width, y, end - start);
|
||||
}
|
||||
}
|
||||
} else
|
||||
if (term->ime.preedit.cells != NULL)
|
||||
/* Already rendered */;
|
||||
else
|
||||
#endif
|
||||
draw_bar(term, buf->pix[0], font, &fg,
|
||||
x_left + cursor_cell_idx * term->cell_width, y);
|
||||
if (term->search.cursor >= term->search.len)
|
||||
draw_bar(term, buf->pix[0], font, &fg, x, y);
|
||||
|
||||
quirk_weston_subsurface_desync_on(term->window->search_sub_surface);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue