diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/entry_backend/harfbuzz.c | 135 | ||||
-rw-r--r-- | src/entry_backend/pango.c | 77 |
2 files changed, 137 insertions, 75 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); diff --git a/src/entry_backend/pango.c b/src/entry_backend/pango.c index 575bba2..3173d6f 100644 --- a/src/entry_backend/pango.c +++ b/src/entry_backend/pango.c @@ -57,6 +57,12 @@ static bool size_overflows(struct entry *entry, uint32_t width, uint32_t height) return false; } +/* + * This is pretty much a direct translation of the corresponding function in + * the harfbuzz backend. As that's the one that I care about most, there are + * more explanatory comments than there are here, so go look at that if you + * want to understand how tofi's text rendering works. + */ void entry_backend_pango_update(struct entry *entry) { cairo_t *cr = entry->cairo[entry->index].cr; @@ -71,18 +77,18 @@ void entry_backend_pango_update(struct entry *entry) pango_cairo_update_layout(cr, layout); pango_cairo_show_layout(cr, layout); - int width; - int height; - pango_layout_get_pixel_size(entry->pango.layout, &width, NULL); - cairo_translate(cr, width, 0); + PangoRectangle ink_rect; + PangoRectangle logical_rect; + pango_layout_get_pixel_extents(entry->pango.layout, &ink_rect, &logical_rect); + cairo_translate(cr, logical_rect.width + logical_rect.x, 0); /* Render the entry text */ pango_layout_set_text(layout, entry->input_mb, -1); pango_cairo_update_layout(cr, layout); pango_cairo_show_layout(cr, layout); - pango_layout_get_size(layout, &width, &height); - width = MAX(width, (int)entry->input_width * PANGO_SCALE); + pango_layout_get_pixel_extents(entry->pango.layout, &ink_rect, &logical_rect); + logical_rect.width = MAX(logical_rect.width, (int)entry->input_width); uint32_t num_results; if (entry->num_results == 0) { @@ -94,9 +100,9 @@ void entry_backend_pango_update(struct entry *entry) size_t i; for (i = 0; i < num_results; i++) { if (entry->horizontal) { - cairo_translate(cr, (int)(width / PANGO_SCALE) + entry->result_spacing, 0); + cairo_translate(cr, logical_rect.x + logical_rect.width + entry->result_spacing, 0); } else { - cairo_translate(cr, 0, (int)(height / PANGO_SCALE) + entry->result_spacing); + cairo_translate(cr, 0, logical_rect.height + entry->result_spacing); } if (entry->num_results == 0) { if (size_overflows(entry, 0, 0)) { @@ -126,21 +132,21 @@ void entry_backend_pango_update(struct entry *entry) if (entry->num_results > 0) { pango_cairo_show_layout(cr, layout); - pango_layout_get_size(layout, &width, &height); + pango_layout_get_pixel_extents(entry->pango.layout, &ink_rect, &logical_rect); } else if (!entry->horizontal) { - if (size_overflows(entry, 0, height / PANGO_SCALE)) { + if (size_overflows(entry, 0, logical_rect.height)) { entry->num_results_drawn = i; break; } else { pango_cairo_show_layout(cr, layout); - pango_layout_get_size(layout, &width, &height); + pango_layout_get_pixel_extents(entry->pango.layout, &ink_rect, &logical_rect); } } else { cairo_push_group(cr); pango_cairo_show_layout(cr, layout); - pango_layout_get_size(layout, &width, &height); + pango_layout_get_pixel_extents(entry->pango.layout, &ink_rect, &logical_rect); cairo_pattern_t *group = cairo_pop_group(cr); - if (size_overflows(entry, width / PANGO_SCALE, 0)) { + if (size_overflows(entry, logical_rect.width, 0)) { entry->num_results_drawn = i; cairo_pattern_destroy(group); break; @@ -154,12 +160,18 @@ void entry_backend_pango_update(struct entry *entry) } } else { ssize_t prematch_len = -1; + ssize_t postmatch_len = -1; size_t match_len = entry->input_mb_length; - int32_t subwidth; + PangoRectangle ink_subrect; + PangoRectangle logical_subrect; if (entry->input_mb_length > 0 && entry->selection_highlight_color.a != 0) { char *match_pos = strcasestr(str, entry->input_mb); if (match_pos != NULL) { prematch_len = (match_pos - str); + postmatch_len = strlen(str) - prematch_len - match_len; + if (postmatch_len <= 0) { + postmatch_len = -1; + } } } @@ -170,27 +182,43 @@ void entry_backend_pango_update(struct entry *entry) pango_layout_set_text(layout, str, prematch_len); pango_cairo_update_layout(cr, layout); pango_cairo_show_layout(cr, layout); - pango_layout_get_size(layout, &subwidth, &height); - width = subwidth; + pango_layout_get_pixel_extents(entry->pango.layout, &ink_subrect, &logical_subrect); + ink_rect = ink_subrect; + logical_rect = logical_subrect; if (prematch_len != -1) { - cairo_translate(cr, (int)(subwidth / PANGO_SCALE), 0); + cairo_translate(cr, logical_subrect.x + logical_subrect.width, 0); color = entry->selection_highlight_color; cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); pango_layout_set_text(layout, &str[prematch_len], match_len); pango_cairo_update_layout(cr, layout); pango_cairo_show_layout(cr, layout); - pango_layout_get_size(layout, &subwidth, &height); - width += subwidth; + pango_layout_get_pixel_extents(entry->pango.layout, &ink_subrect, &logical_subrect); + if (prematch_len == 0) { + ink_rect = ink_subrect; + logical_rect = logical_subrect; + } else { + ink_rect.width = logical_rect.width + - ink_rect.x + + ink_subrect.x + + ink_subrect.width; + logical_rect.width += logical_subrect.x + logical_subrect.width; + } + } - cairo_translate(cr, (int)(subwidth / PANGO_SCALE), 0); + if (postmatch_len != -1) { + cairo_translate(cr, logical_subrect.x + logical_subrect.width, 0); color = entry->selection_foreground_color; cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); pango_layout_set_text(layout, &str[prematch_len + match_len], -1); pango_cairo_update_layout(cr, layout); pango_cairo_show_layout(cr, layout); - pango_layout_get_size(layout, &subwidth, &height); - width += subwidth; + pango_layout_get_pixel_extents(entry->pango.layout, &ink_subrect, &logical_subrect); + ink_rect.width = logical_rect.width + - ink_rect.x + + ink_subrect.x + + ink_subrect.width; + logical_rect.width += logical_subrect.x + logical_subrect.width; } @@ -202,9 +230,8 @@ void entry_backend_pango_update(struct entry *entry) if (pad < 0) { pad = entry->clip_width; } - cairo_translate(cr, -pad, 0); - cairo_rectangle(cr, 0, 0, (int)(width / PANGO_SCALE) + pad * 2, (int)(height / PANGO_SCALE)); - cairo_translate(cr, pad, 0); + cairo_translate(cr, floor(-pad + ink_rect.x), 0); + cairo_rectangle(cr, 0, 0, ceil(ink_rect.width + pad * 2), ceil(logical_rect.height)); cairo_fill(cr); cairo_restore(cr); cairo_paint(cr); |