diff options
author | Phil Jones <philj56@gmail.com> | 2022-08-04 09:57:53 +0100 |
---|---|---|
committer | Phil Jones <philj56@gmail.com> | 2022-08-04 09:57:53 +0100 |
commit | 15f831bbb6f27d5a11879dd07f383400e83c97e3 (patch) | |
tree | 94b43f3236069c42cdd76cc58ba8d65ae589c5e6 /src/entry_backend | |
parent | c0d3df8af0328280836ccfef85ae7e40eb3b6b87 (diff) |
Improve pagination.
Pagination was broken in horizontal mode before, and in general tried to
render too many results (leading to a cut-off last result). This commit
fixes both of those, with the minor caveat that you can no longer scroll
straight to the last result.
Diffstat (limited to 'src/entry_backend')
-rw-r--r-- | src/entry_backend/harfbuzz.c | 96 | ||||
-rw-r--r-- | src/entry_backend/pango.c | 80 |
2 files changed, 141 insertions, 35 deletions
diff --git a/src/entry_backend/harfbuzz.c b/src/entry_backend/harfbuzz.c index 45d5961..946bca3 100644 --- a/src/entry_backend/harfbuzz.c +++ b/src/entry_backend/harfbuzz.c @@ -26,6 +26,9 @@ const struct { #undef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + static const char *get_ft_error_string(int err_code) { for (size_t i = 0; i < N_ELEM(ft_errors); i++) { @@ -185,6 +188,23 @@ void entry_backend_harfbuzz_destroy(struct entry *entry) FT_Done_FreeType(entry->harfbuzz.ft_library); } +static bool size_overflows(struct entry *entry, uint32_t width, uint32_t height) +{ + cairo_t *cr = entry->cairo[entry->index].cr; + cairo_matrix_t mat; + cairo_get_matrix(cr, &mat); + if (entry->horizontal) { + if (mat.x0 + width > entry->clip_x + entry->clip_width) { + return true; + } + } else { + if (mat.y0 + height > entry->clip_y + entry->clip_height) { + return true; + } + } + return false; +} + void entry_backend_harfbuzz_update(struct entry *entry) { cairo_t *cr = entry->cairo[entry->index].cr; @@ -215,35 +235,30 @@ void entry_backend_harfbuzz_update(struct entry *entry) cairo_font_extents_t font_extents; cairo_font_extents(cr, &font_extents); - cairo_matrix_t mat; + uint32_t num_results; + if (entry->num_results == 0) { + num_results = entry->results.count; + } else { + num_results = MIN(entry->num_results, entry->results.count); + } /* Render our results */ - for (size_t i = 0; i < entry->results.count; i++) { + size_t i; + for (i = 0; i < num_results; i++) { if (entry->horizontal) { cairo_translate(cr, width + entry->result_spacing, 0); } else { cairo_translate(cr, 0, font_extents.height + entry->result_spacing); } if (entry->num_results == 0) { - cairo_get_matrix(cr, &mat); - if (entry->horizontal) { - if (mat.x0 > entry->clip_x + entry->clip_width) { - entry->num_results_drawn = i; - log_debug("Drew %zu results.\n", i); - break; - } - } else { - if (mat.y0 > entry->clip_y + entry->clip_height) { - entry->num_results_drawn = i; - log_debug("Drew %zu results.\n", i); - break; - } + if (size_overflows(entry, 0, 0)) { + break; } } else if (i >= entry->num_results) { break; } - size_t index = i + entry->num_results * entry->page; + size_t index = i + entry->first_result; /* * We may be on the last page, which could have fewer results * than expected, so check and break if necessary. @@ -259,7 +274,48 @@ void entry_backend_harfbuzz_update(struct entry *entry) setup_hb_buffer(buffer); hb_buffer_add_utf8(buffer, result, -1, 0, -1); hb_shape(entry->harfbuzz.hb_font, buffer, NULL, 0); - width = render_hb_buffer(cr, buffer); + + if (entry->num_results > 0) { + /* + * We're not auto-detecting how many results we + * can fit, so just render the text. + */ + width = render_hb_buffer(cr, buffer); + } else if (!entry->horizontal) { + /* + * The height of the text doesn't change, so + * we don't need to re-measure it each time. + */ + if (size_overflows(entry, 0, font_extents.height)) { + entry->num_results_drawn = i; + break; + } else { + width = render_hb_buffer(cr, buffer); + } + } else { + /* + * The difficult case: we're auto-detecting how + * many results to draw, but we can't know + * whether this results will fit without + * drawing it! To solve this, draw to a + * temporary group, measure that, then copy it + * to the main canvas only if it will fit. + */ + cairo_push_group(cr); + width = render_hb_buffer(cr, buffer); + cairo_pattern_t *group = cairo_pop_group(cr); + if (size_overflows(entry, width, 0)) { + entry->num_results_drawn = i; + cairo_pattern_destroy(group); + break; + } else { + cairo_save(cr); + cairo_set_source(cr, group); + cairo_paint(cr); + cairo_restore(cr); + cairo_pattern_destroy(group); + } + } } else { /* * For the selected result, there's a bit more to do. @@ -277,6 +333,10 @@ void entry_backend_harfbuzz_update(struct entry *entry) * portion of text - this is achieved simply by * splitting the text into prematch, match and * postmatch chunks, and drawing each separately. + * + * N.B. The size_overflows check isn't necessary here, + * as it's currently not possible for the selection to + * do so. */ size_t prematch_len; char *prematch = xstrdup(result); @@ -353,6 +413,8 @@ void entry_backend_harfbuzz_update(struct entry *entry) cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); } } + entry->num_results_drawn = i; + log_debug("Drew %zu results.\n", i); cairo_restore(cr); } diff --git a/src/entry_backend/pango.c b/src/entry_backend/pango.c index 41d8301..575bba2 100644 --- a/src/entry_backend/pango.c +++ b/src/entry_backend/pango.c @@ -9,6 +9,9 @@ #undef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + void entry_backend_pango_init(struct entry *entry, uint32_t *width, uint32_t *height) { cairo_t *cr = entry->cairo[0].cr; @@ -37,6 +40,23 @@ void entry_backend_pango_destroy(struct entry *entry) g_object_unref(entry->pango.context); } +static bool size_overflows(struct entry *entry, uint32_t width, uint32_t height) +{ + cairo_t *cr = entry->cairo[entry->index].cr; + cairo_matrix_t mat; + cairo_get_matrix(cr, &mat); + if (entry->horizontal) { + if (mat.x0 + width > entry->clip_x + entry->clip_width) { + return true; + } + } else { + if (mat.y0 + height > entry->clip_y + entry->clip_height) { + return true; + } + } + return false; +} + void entry_backend_pango_update(struct entry *entry) { cairo_t *cr = entry->cairo[entry->index].cr; @@ -64,33 +84,28 @@ void entry_backend_pango_update(struct entry *entry) pango_layout_get_size(layout, &width, &height); width = MAX(width, (int)entry->input_width * PANGO_SCALE); - cairo_matrix_t mat; + uint32_t num_results; + if (entry->num_results == 0) { + num_results = entry->results.count; + } else { + num_results = MIN(entry->num_results, entry->results.count); + } /* Render our results */ - for (size_t i = 0; i < entry->results.count; i++) { + size_t i; + for (i = 0; i < num_results; i++) { if (entry->horizontal) { cairo_translate(cr, (int)(width / PANGO_SCALE) + entry->result_spacing, 0); } else { cairo_translate(cr, 0, (int)(height / PANGO_SCALE) + entry->result_spacing); } if (entry->num_results == 0) { - cairo_get_matrix(cr, &mat); - if (entry->horizontal) { - if (mat.x0 > entry->clip_x + entry->clip_width) { - entry->num_results_drawn = i; - log_debug("Drew %zu results.\n", i); - break; - } - } else { - if (mat.y0 > entry->clip_y + entry->clip_height) { - entry->num_results_drawn = i; - log_debug("Drew %zu results.\n", i); - break; - } + if (size_overflows(entry, 0, 0)) { + break; } } else if (i >= entry->num_results) { break; } - size_t index = i + entry->num_results * entry->page; + size_t index = i + entry->first_result; /* * We may be on the last page, which could have fewer results * than expected, so check and break if necessary. @@ -108,8 +123,35 @@ void entry_backend_pango_update(struct entry *entry) if (i != entry->selection) { pango_layout_set_text(layout, str, -1); pango_cairo_update_layout(cr, layout); - pango_cairo_show_layout(cr, layout); - pango_layout_get_size(layout, &width, &height); + + if (entry->num_results > 0) { + pango_cairo_show_layout(cr, layout); + pango_layout_get_size(layout, &width, &height); + } else if (!entry->horizontal) { + if (size_overflows(entry, 0, height / PANGO_SCALE)) { + entry->num_results_drawn = i; + break; + } else { + pango_cairo_show_layout(cr, layout); + pango_layout_get_size(layout, &width, &height); + } + } else { + cairo_push_group(cr); + pango_cairo_show_layout(cr, layout); + pango_layout_get_size(layout, &width, &height); + cairo_pattern_t *group = cairo_pop_group(cr); + if (size_overflows(entry, width / PANGO_SCALE, 0)) { + entry->num_results_drawn = i; + cairo_pattern_destroy(group); + break; + } else { + cairo_save(cr); + cairo_set_source(cr, group); + cairo_paint(cr); + cairo_restore(cr); + cairo_pattern_destroy(group); + } + } } else { ssize_t prematch_len = -1; size_t match_len = entry->input_mb_length; @@ -170,6 +212,8 @@ void entry_backend_pango_update(struct entry *entry) cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); } } + entry->num_results_drawn = i; + log_debug("Drew %zu results.\n", i); cairo_restore(cr); } |