From b4a0f5b13b44525bd1dbdbc12e4e412fc9a0a1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 5 Dec 2020 23:29:12 +0100 Subject: [PATCH] render: search: glyph_offset is in *cells*, cursor position in *characters* When calculating the offset into the search string, from where to start rendering, take into account that the cursor position is in *characters*, and the glyph-offset is in *cells*. --- render.c | 82 +++++++++++++++++++++++++++++++++++++++++++------------- search.c | 6 +++++ 2 files changed, 70 insertions(+), 18 deletions(-) diff --git a/render.c b/render.c index 9cbe85d6..7fdade1a 100644 --- a/render.c +++ b/render.c @@ -2117,8 +2117,19 @@ render_search_box(struct terminal *term) { assert(term->window->search_sub_surface != NULL); - const size_t wanted_visible_chars = max( - 20, wcswidth(term->search.buf, term->search.len)); + /* + * We treat the search box pretty much like a row of cells. That + * is, a glyph is either 1 or 2 (or more) “cells” wide. + * + * The search ‘length’, and ‘cursor’ (position) is in + * *characters*, not cells. This means we need to translate from + * character cound to cell count when calculating the length of + * the search box, where in the search string we should start + * rendering etc. + */ + + const size_t total_cells = wcswidth(term->search.buf, term->search.len); + const size_t wanted_visible_cells = max(20, total_cells); assert(term->scale >= 1); const int scale = term->scale; @@ -2128,12 +2139,12 @@ render_search_box(struct terminal *term) const size_t width = term->width - 2 * margin; const size_t visible_width = min( term->width - 2 * margin, - 2 * margin + wanted_visible_chars * term->cell_width); + 2 * margin + wanted_visible_cells * term->cell_width); const size_t height = min( term->height - 2 * margin, 2 * margin + 1 * term->cell_height); - const size_t visible_chars = (visible_width - 2 * margin) / term->cell_width; + const size_t visible_cells = (visible_width - 2 * margin) / term->cell_width; size_t glyph_offset = term->render.search_glyph_offset; unsigned long cookie = shm_cookie_search(term); @@ -2158,32 +2169,66 @@ 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 */ - if (term->search.cursor < glyph_offset) - term->render.search_glyph_offset = glyph_offset = term->search.cursor; - else if (term->search.cursor > glyph_offset + visible_chars) { - term->render.search_glyph_offset = glyph_offset = - term->search.cursor - min(term->search.cursor, visible_chars); + /* + * 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. + */ + for (size_t i = 0, cell_idx = 0; + i <= term->search.cursor; + cell_idx += max(1, wcwidth(term->search.buf[i])), i++) + { + if (i != term->search.cursor) + continue; + + if (cell_idx < glyph_offset) + term->render.search_glyph_offset = glyph_offset = cell_idx; + else if (cell_idx > glyph_offset + visible_cells) { + term->render.search_glyph_offset = glyph_offset = + cell_idx - min(cell_idx, visible_cells); + } + break; } /* Move offset if there is free space available */ - if (term->search.len - glyph_offset < visible_chars) + if (total_cells - glyph_offset < visible_cells) term->render.search_glyph_offset = glyph_offset = - term->search.len - min(term->search.len, visible_chars); + total_cells - min(total_cells, visible_cells); - /* Text (what the user entered - *not* match(es)) */ - for (size_t i = glyph_offset; - i < term->search.len && i - glyph_offset < visible_chars; - i++) + /* + * Render the search string, starting at ‘glyph_offset’. Note that + * glyph_offset is in cells, not characters + */ + for (size_t i = 0, + cell_idx = 0, + width = max(1, wcwidth(term->search.buf[i])), + next_cell_idx = width; + i < term->search.len; + i++, + cell_idx = next_cell_idx, + width = max(1, wcwidth(term->search.buf[i])), + next_cell_idx += width) { if (i == term->search.cursor) draw_bar(term, buf->pix[0], font, &fg, x, y); + if (next_cell_idx >= glyph_offset && next_cell_idx - glyph_offset > visible_cells) + break; + + if (cell_idx < glyph_offset) { + cell_idx = next_cell_idx; + continue; + } + const struct fcft_glyph *glyph = fcft_glyph_rasterize( font, term->search.buf[i], term->font_subpixel); - if (glyph == NULL) + if (glyph == NULL) { + cell_idx = next_cell_idx; continue; + } if (unlikely(pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8)) { /* Glyph surface is a pre-rendered image (typically a color emoji...) */ @@ -2200,7 +2245,8 @@ render_search_box(struct terminal *term) pixman_image_unref(src); } - x += max(1, wcwidth(term->search.buf[i])) * term->cell_width; + x += width * term->cell_width; + cell_idx = next_cell_idx; } if (term->search.cursor >= term->search.len) diff --git a/search.c b/search.c index a9f5499a..83f86390 100644 --- a/search.c +++ b/search.c @@ -18,6 +18,7 @@ #include "selection.h" #include "shm.h" #include "util.h" +#include "xmalloc.h" /* * Ensures a "new" viewport doesn't contain any unallocated rows. @@ -124,6 +125,11 @@ search_begin(struct terminal *term) term->search.view_followed_offset = term->grid->view == term->grid->offset; term->is_searching = true; + term->search.len = 0; + term->search.sz = 64; + term->search.buf = xmalloc(term->search.sz * sizeof(term->search.buf[0])); + term->search.buf[0] = L'\0'; + term_xcursor_update(term); render_refresh_search(term); }