From a69c9a63ae08b2201ee3ef615a356ace129b7f48 Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Sun, 20 Mar 2022 13:59:23 +0100 Subject: [PATCH] Fixing split multi-byte issue when drawing ellipsis --- dmenu.c | 9 +++--- drw.c | 85 ++++++++++++++++++++++++++------------------------------- drw.h | 3 +- 3 files changed, 46 insertions(+), 51 deletions(-) diff --git a/dmenu.c b/dmenu.c index eca67ac..5e1edd6 100644 --- a/dmenu.c +++ b/dmenu.c @@ -541,7 +541,7 @@ readstdin(void) { char buf[sizeof text], *p; size_t i, imax = 0, size = 0; - unsigned int tmpmax = 0; + XGlyphInfo ext; /* read each line from stdin and add it to the item list */ for (i = 0; fgets(buf, sizeof buf, stdin); i++) { @@ -553,9 +553,9 @@ readstdin(void) if (!(items[i].text = strdup(buf))) die("cannot strdup %u bytes:", strlen(buf) + 1); items[i].out = 0; - drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); - if (tmpmax > inputw) { - inputw = tmpmax; + XftTextExtentsUtf8(drw->fonts->dpy, drw->fonts->xfont, (XftChar8 *)buf, strlen(buf), &ext); + if (ext.xOff > inputw) { + inputw = ext.xOff; imax = i; } } @@ -768,6 +768,7 @@ main(int argc, char *argv[]) drw = drw_create(dpy, screen, root, wa.width, wa.height); if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) die("no fonts could be loaded."); + ellipsis_width = TEXTW("...") - lrpad; lrpad = drw->fonts->h; #ifdef __OpenBSD__ diff --git a/drw.c b/drw.c index 4cdbcbe..b82dab4 100644 --- a/drw.c +++ b/drw.c @@ -15,6 +15,7 @@ static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0} static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; +unsigned int ellipsis_width = 0; static long utf8decodebyte(const char c, size_t *i) @@ -252,18 +253,18 @@ int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) { char buf[1024]; - int ty; - unsigned int ew; + int ty, stop = 0; + unsigned int ew = 0, ellipsis_ew = 0; XftDraw *d = NULL; Fnt *usedfont, *curfont, *nextfont; - size_t i, len; - int utf8strlen, utf8charlen, render = x || y || w || h; + size_t i, b = 0, ellipsis_b = 0; + int utf8charlen, render = x || y || w || h; long utf8codepoint = 0; - const char *utf8str; FcCharSet *fccharset; FcPattern *fcpattern; FcPattern *match; XftResult result; + XGlyphInfo ext; int charexists = 0; if (!drw || (render && !drw->scheme) || !text || !drw->fonts) @@ -283,8 +284,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp usedfont = drw->fonts; while (1) { - utf8strlen = 0; - utf8str = text; + ellipsis_ew = ew = 0; + ellipsis_b = b = 0; nextfont = NULL; while (*text) { utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); @@ -292,8 +293,27 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); if (charexists) { if (curfont == usedfont) { - utf8strlen += utf8charlen; - text += utf8charlen; + XftTextExtentsUtf8(curfont->dpy, curfont->xfont, (XftChar8 *)text, utf8charlen, &ext); + if (ew + ext.xOff + lpad > w || b + utf8charlen > sizeof(buf) - 1) { + /* Only draw ellipsis if we have not recently started another font */ + if (render && ellipsis_b > 3) { + ew = ellipsis_ew; + b = ellipsis_b; + for (i = 0; i < 3; i++) + buf[b++] = '.'; + } + stop = 1; + break; + } + + /* Record the last buffer index where the ellipsis would still fit */ + if (ew + ellipsis_width + lpad <= w) { + ellipsis_ew = ew; + ellipsis_b = b; + } + for (i = 0; i < utf8charlen; i++) + buf[b++] = *text++; + ew += ext.xOff; } else { nextfont = curfont; } @@ -301,36 +321,24 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp } } - if (!charexists || nextfont) + if (stop || !charexists || nextfont) break; else charexists = 0; } + buf[b] = '\0'; - if (utf8strlen) { - drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); - /* shorten text if necessary */ - for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) - drw_font_getexts(usedfont, utf8str, len, &ew, NULL); - - if (len) { - memcpy(buf, utf8str, len); - buf[len] = '\0'; - if (len < utf8strlen) - for (i = len; i && i > len - 3; buf[--i] = '.') - ; /* NOP */ - - if (render) { - ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; - XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], - usedfont->xfont, x, ty, (XftChar8 *)buf, len); - } - x += ew; - w -= ew; + if (b) { + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)buf, b); } + x += ew; + w -= ew; } - if (!*text) { + if (stop || !*text) { break; } else if (nextfont) { charexists = 0; @@ -397,21 +405,6 @@ drw_fontset_getwidth(Drw *drw, const char *text) return drw_text(drw, 0, 0, 0, 0, 0, text, 0); } -void -drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) -{ - XGlyphInfo ext; - - if (!font || !text) - return; - - XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); - if (w) - *w = ext.xOff; - if (h) - *h = font->h; -} - Cur * drw_cur_create(Drw *drw, int shape) { diff --git a/drw.h b/drw.h index 4c67419..7c43b3d 100644 --- a/drw.h +++ b/drw.h @@ -26,6 +26,8 @@ typedef struct { Fnt *fonts; } Drw; +extern unsigned int ellipsis_width; + /* Drawable abstraction */ Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); void drw_resize(Drw *drw, unsigned int w, unsigned int h); @@ -35,7 +37,6 @@ void drw_free(Drw *drw); Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); void drw_fontset_free(Fnt* set); unsigned int drw_fontset_getwidth(Drw *drw, const char *text); -void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); /* Colorscheme abstraction */ void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); -- 2.35.1