Hi NRK, you make some interesting points. I am curious as to what your queueing approach would look like.
I played around some more simplifying the ellipsis drawing and removing buf as you suggested. This would solve all of the aforementioned problems as far as I can tell, but it can result in a partly drawn emoji for example when the ellipsis cuts it off (which I think is a fair tradeoff). Thanks, -Stein On Mon, Mar 21, 2022 at 8:15 PM NRK <n...@disroot.org> wrote: > On Mon, Mar 21, 2022 at 07:00:32PM +0600, NRK wrote: > > The only "issue" is that it doesn't render the ellipsis in case font > > changes, but current upstream dmenu doesn't seem to do it either. > > OK, I think I have a solution to this. The problem here, as far as I > understand, is this: > > Let's say we have a maxwidth of 100, and printing the ellipsis takes 20. > In this case as long as our string is below 80, we're fine; and if it's > above 100 then we should print up to 80 and then print the ellipsis. > > The problem case is when we're between 80-100, and we need to change > font. The current code always renders whatever is available when we > change font, so if the text turns out to be bigger than 100 we've > already rendered some of the text into the problem area where the > ellipsis should've been. > > The solution I came up with is to simply not render anything if we're at > the problem case, and instead put the problem cases into a queue and > keep going forwards until either: > > 1) The text overflows, in which case we discard the queue and just print > the ellipsis instead. > 2) Encounter the end of text, which means the text barely fit into our > maxwidth (this would happen with the prompt); in which case we just > render the queue and don't print any ellipsis. > > I already have a patch that *roughly* does what I described above and > with it applied I don't see any truncation problems anymore. > > However the patch is quite messy atm, and in order to do things cleanly > and avoid special casing, I think a good portion of drw_text() will need > to be overhauled to use a queue like this: > > struct { const char *str; int len; Fnt *fnt; } q[32] = {0}; > > I'm not sure if such overhaul is going to be welcome or not, but if all > of this sounds acceptable then I can proceed with cleaning things up and > supplying the patch. > > - NRK > > -- Stein Gunnar Bakkeby OpenBet Developer bakk...@gmail.com
From d52d59bc32a9e5bb3f7dfc0a88a02715555b9870 Mon Sep 17 00:00:00 2001 From: Bakkeby <bakk...@gmail.com> Date: Mon, 21 Mar 2022 22:25:51 +0100 Subject: [PATCH] drw_text: print whole utf-8 characters only and simplify ellipsis handling --- drw.c | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drw.c b/drw.c index 4cdbcbe..ccb3b9a 100644 --- a/drw.c +++ b/drw.c @@ -251,12 +251,10 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int 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; XftDraw *d = NULL; Fnt *usedfont, *curfont, *nextfont; - size_t i, len; int utf8strlen, utf8charlen, render = x || y || w || h; long utf8codepoint = 0; const char *utf8str; @@ -264,7 +262,9 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp FcPattern *fcpattern; FcPattern *match; XftResult result; - int charexists = 0; + int charexists = 0, overflow = 0; + XGlyphInfo ext; + const char *ellipsis = "..."; if (!drw || (render && !drw->scheme) || !text || !drw->fonts) return 0; @@ -278,7 +278,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp DefaultVisual(drw->dpy, drw->screen), DefaultColormap(drw->dpy, drw->screen)); x += lpad; - w -= lpad; + w -= lpad * 2; } usedfont = drw->fonts; @@ -286,12 +286,19 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp utf8strlen = 0; utf8str = text; nextfont = NULL; + ew = 0; while (*text) { utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); for (curfont = drw->fonts; curfont; curfont = curfont->next) { charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); if (charexists) { if (curfont == usedfont) { + XftTextExtentsUtf8(usedfont->dpy, usedfont->xfont, (XftChar8 *)text, utf8charlen, &ext); + if (ew + ext.xOff > w) { + overflow = 1; + break; + } + ew += ext.xOff; utf8strlen += utf8charlen; text += utf8charlen; } else { @@ -301,36 +308,29 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp } } - if (!charexists || nextfont) + if (overflow || !charexists || nextfont) break; else charexists = 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 (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); + } + x += ew; + w -= ew; + if (render && overflow) { + XftTextExtentsUtf8(drw->fonts->dpy, drw->fonts->xfont, (XftChar8 *)ellipsis, 3, &ext); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x + w - ext.xOff, y, ext.xOff, h); + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x + w - ext.xOff, ty, (XftChar8 *)ellipsis, 3); } } - if (!*text) { + if (overflow || !*text) { break; } else if (nextfont) { charexists = 0; -- 2.25.1