summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/entry_backend/harfbuzz.c135
-rw-r--r--src/entry_backend/pango.c77
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);