summaryrefslogtreecommitdiff
path: root/src/entry_backend
diff options
context:
space:
mode:
authorPhil Jones <philj56@gmail.com>2022-08-04 09:57:53 +0100
committerPhil Jones <philj56@gmail.com>2022-08-04 09:57:53 +0100
commit15f831bbb6f27d5a11879dd07f383400e83c97e3 (patch)
tree94b43f3236069c42cdd76cc58ba8d65ae589c5e6 /src/entry_backend
parentc0d3df8af0328280836ccfef85ae7e40eb3b6b87 (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.c96
-rw-r--r--src/entry_backend/pango.c80
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);
}