summaryrefslogtreecommitdiff
path: root/src/entry_backend/harfbuzz.c
diff options
context:
space:
mode:
authorPhil Jones <philj56@gmail.com>2022-08-05 17:25:27 +0100
committerPhil Jones <philj56@gmail.com>2022-08-05 19:21:11 +0100
commitc9f1daea03a41c0cad56453e1dcc50f6133f19c6 (patch)
treea609e8a6145f2027d78876a2a775d48c06aafabc /src/entry_backend/harfbuzz.c
parent15f831bbb6f27d5a11879dd07f383400e83c97e3 (diff)
Correct selection background for slanted fonts.
Previously the selection background didn't really wrap text correctly, especially if the font was very slanted. This fixes that, so the width is tightly wrapped around any font.
Diffstat (limited to 'src/entry_backend/harfbuzz.c')
-rw-r--r--src/entry_backend/harfbuzz.c135
1 files changed, 85 insertions, 50 deletions
diff --git a/src/entry_backend/harfbuzz.c b/src/entry_backend/harfbuzz.c
index 946bca3..9868578 100644
--- a/src/entry_backend/harfbuzz.c
+++ b/src/entry_backend/harfbuzz.c
@@ -62,7 +62,7 @@ static void setup_hb_buffer(hb_buffer_t *buffer)
* Render a hb_buffer with Cairo, and return the width of the rendered area in
* Cairo units.
*/
-static uint32_t render_hb_buffer(cairo_t *cr, hb_buffer_t *buffer)
+static cairo_text_extents_t render_hb_buffer(cairo_t *cr, hb_buffer_t *buffer)
{
cairo_save(cr);
@@ -95,15 +95,34 @@ static uint32_t render_hb_buffer(cairo_t *cr, hb_buffer_t *buffer)
cairo_show_glyphs(cr, cairo_glyphs, glyph_count);
+ cairo_text_extents_t extents;
+ cairo_glyph_extents(cr, cairo_glyphs, glyph_count, &extents);
+
+ /* Account for the shifted baseline in our returned text extents. */
+ extents.y_bearing += font_extents.ascent;
+
free(cairo_glyphs);
cairo_restore(cr);
- double width = 0;
- for (unsigned int i=0; i < glyph_count; i++) {
- width += glyph_pos[i].x_advance / 64.0;
+ return extents;
+}
+
+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 ceil(width);
+ return false;
}
void entry_backend_harfbuzz_init(
@@ -188,28 +207,11 @@ 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;
hb_buffer_t *buffer = entry->harfbuzz.hb_buffer;
- uint32_t width;
+ cairo_text_extents_t extents;
cairo_save(cr);
struct color color = entry->foreground_color;
@@ -220,17 +222,17 @@ void entry_backend_harfbuzz_update(struct entry *entry)
setup_hb_buffer(buffer);
hb_buffer_add_utf8(entry->harfbuzz.hb_buffer, entry->prompt_text, -1, 0, -1);
hb_shape(entry->harfbuzz.hb_font, entry->harfbuzz.hb_buffer, NULL, 0);
- width = render_hb_buffer(cr, buffer);
+ extents = render_hb_buffer(cr, buffer);
- cairo_translate(cr, width, 0);
+ cairo_translate(cr, extents.x_advance, 0);
/* Render the entry text */
hb_buffer_clear_contents(buffer);
setup_hb_buffer(buffer);
hb_buffer_add_utf8(buffer, entry->input_mb, -1, 0, -1);
hb_shape(entry->harfbuzz.hb_font, buffer, NULL, 0);
- width = render_hb_buffer(cr, buffer);
- width = MAX(width, entry->input_width);
+ extents = render_hb_buffer(cr, buffer);
+ extents.x_advance = MAX(extents.x_advance, entry->input_width);
cairo_font_extents_t font_extents;
cairo_font_extents(cr, &font_extents);
@@ -245,7 +247,7 @@ void entry_backend_harfbuzz_update(struct entry *entry)
size_t i;
for (i = 0; i < num_results; i++) {
if (entry->horizontal) {
- cairo_translate(cr, width + entry->result_spacing, 0);
+ cairo_translate(cr, extents.x_advance + entry->result_spacing, 0);
} else {
cairo_translate(cr, 0, font_extents.height + entry->result_spacing);
}
@@ -280,7 +282,7 @@ void entry_backend_harfbuzz_update(struct entry *entry)
* We're not auto-detecting how many results we
* can fit, so just render the text.
*/
- width = render_hb_buffer(cr, buffer);
+ extents = render_hb_buffer(cr, buffer);
} else if (!entry->horizontal) {
/*
* The height of the text doesn't change, so
@@ -290,7 +292,7 @@ void entry_backend_harfbuzz_update(struct entry *entry)
entry->num_results_drawn = i;
break;
} else {
- width = render_hb_buffer(cr, buffer);
+ extents = render_hb_buffer(cr, buffer);
}
} else {
/*
@@ -302,9 +304,9 @@ void entry_backend_harfbuzz_update(struct entry *entry)
* to the main canvas only if it will fit.
*/
cairo_push_group(cr);
- width = render_hb_buffer(cr, buffer);
+ extents = render_hb_buffer(cr, buffer);
cairo_pattern_t *group = cairo_pop_group(cr);
- if (size_overflows(entry, width, 0)) {
+ if (size_overflows(entry, extents.x_advance, 0)) {
entry->num_results_drawn = i;
cairo_pattern_destroy(group);
break;
@@ -339,17 +341,21 @@ void entry_backend_harfbuzz_update(struct entry *entry)
* do so.
*/
size_t prematch_len;
+ size_t postmatch_len;
char *prematch = xstrdup(result);
char *match = NULL;
char *postmatch = NULL;
- uint32_t subwidth;
+ cairo_text_extents_t subextents;
if (entry->input_mb_length > 0 && entry->selection_highlight_color.a != 0) {
char *match_pos = strcasestr(prematch, entry->input_mb);
if (match_pos != NULL) {
match = xstrdup(result);
- postmatch = xstrdup(result);
prematch_len = (match_pos - prematch);
prematch[prematch_len] = '\0';
+ postmatch_len = strlen(result) - prematch_len - entry->input_mb_length;
+ if (postmatch_len > 0) {
+ postmatch = xstrdup(result);
+ }
match[entry->input_mb_length + prematch_len] = '\0';
}
}
@@ -362,39 +368,69 @@ void entry_backend_harfbuzz_update(struct entry *entry)
setup_hb_buffer(buffer);
hb_buffer_add_utf8(buffer, prematch, -1, 0, -1);
hb_shape(entry->harfbuzz.hb_font, buffer, NULL, 0);
- subwidth = render_hb_buffer(cr, buffer);
- width = subwidth;
+ subextents = render_hb_buffer(cr, buffer);
+ extents = subextents;
+
+ free(prematch);
+ prematch = NULL;
if (match != NULL) {
- cairo_translate(cr, subwidth, 0);
+ cairo_translate(cr, subextents.x_advance, 0);
color = entry->selection_highlight_color;
cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
hb_buffer_clear_contents(buffer);
setup_hb_buffer(buffer);
hb_buffer_add_utf8(buffer, &match[prematch_len], -1, 0, -1);
hb_shape(entry->harfbuzz.hb_font, buffer, NULL, 0);
- subwidth = render_hb_buffer(cr, buffer);
- width += subwidth;
+ subextents = render_hb_buffer(cr, buffer);
+
+ if (prematch_len == 0) {
+ extents = subextents;
+ } else {
+ /*
+ * This calculation is a little
+ * complex, but it's basically:
+ *
+ * (distance from leftmost pixel of
+ * prematch to logical end of prematch)
+ *
+ * +
+ *
+ * (distance from logical start of match
+ * to rightmost pixel of match).
+ */
+ extents.width = extents.x_advance
+ - extents.x_bearing
+ + subextents.x_bearing
+ + subextents.width;
+ extents.x_advance += subextents.x_advance;
+ }
- cairo_translate(cr, subwidth, 0);
+ free(match);
+ match = NULL;
+ }
+
+ if (postmatch != NULL) {
+ cairo_translate(cr, subextents.x_advance, 0);
color = entry->selection_foreground_color;
cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
hb_buffer_clear_contents(buffer);
setup_hb_buffer(buffer);
hb_buffer_add_utf8(buffer, &postmatch[entry->input_mb_length + prematch_len], -1, 0, -1);
hb_shape(entry->harfbuzz.hb_font, buffer, NULL, 0);
- subwidth = render_hb_buffer(cr, buffer);
- width += subwidth;
+ subextents = render_hb_buffer(cr, buffer);
+
+ extents.width = extents.x_advance
+ - extents.x_bearing
+ + subextents.x_bearing
+ + subextents.width;
+ extents.x_advance += subextents.x_advance;
+
- free(match);
free(postmatch);
- match = NULL;
postmatch = NULL;
}
- free(prematch);
- prematch = NULL;
-
cairo_pop_group_to_source(cr);
cairo_save(cr);
color = entry->selection_background_color;
@@ -403,9 +439,8 @@ void entry_backend_harfbuzz_update(struct entry *entry)
if (pad < 0) {
pad = entry->clip_width;
}
- cairo_translate(cr, -pad, 0);
- cairo_rectangle(cr, 0, 0, width + pad * 2, font_extents.height);
- cairo_translate(cr, pad, 0);
+ cairo_translate(cr, floor(-pad + extents.x_bearing), 0);
+ cairo_rectangle(cr, 0, 0, ceil(extents.width + pad * 2), ceil(font_extents.height));
cairo_fill(cr);
cairo_restore(cr);
cairo_paint(cr);