diff --git a/fn.h b/fn.h index c4c3712..30605ab 100644 --- a/fn.h +++ b/fn.h @@ -48,3 +48,4 @@ void* erealloc(void*, ulong); int textinit(void); int textwidth(Str*); void textdraw(u32int*, int, int, int, int, Str*); +void textdrawfit(u32int*, int, int, int, int, int, Str*); diff --git a/font.c b/font.c index ae867ed..29b573e 100644 --- a/font.c +++ b/font.c @@ -6,6 +6,13 @@ static PangoFontMap *fontmap; static PangoContext *context; static PangoLayout *layout; +static void +resetlayout(void) +{ + pango_layout_set_width(layout, -1); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE); +} + static void textclear(void) { @@ -100,20 +107,32 @@ textwidth(Str *s) { PangoRectangle r; + if(layout == nil) + return 0; + resetlayout(); if(!settext(s)) return 0; textextents(&r); return r.width; } -void -textdraw(u32int *buf, int w, int h, int x, int y, Str *s) +static void +drawtext(u32int *buf, int w, int h, int x, int y, int fit, Str *s) { PangoRectangle r; cairo_surface_t *surface; cairo_t *cr; int stride; + if(layout == nil) + return; + resetlayout(); + if(fit != -1 && fit <= 0) + return; + if(fit > 0){ + pango_layout_set_width(layout, fit * PANGO_SCALE); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + } if(buf == nil || w <= 0 || h <= 0 || !settext(s)) return; stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w); @@ -131,7 +150,10 @@ textdraw(u32int *buf, int w, int h, int x, int y, Str *s) pango_cairo_update_layout(cr, layout); textextents(&r); /* A fallback face must not paint into another popup row. */ - cairo_rectangle(cr, 0, y, w, Fontsz); + if(fit > 0) + cairo_rectangle(cr, x, y, fit, Fontsz); + else + cairo_rectangle(cr, 0, y, w, Fontsz); cairo_clip(cr); cairo_move_to(cr, x - r.x, y + (Fontsz - r.height) / 2 - r.y); cairo_set_source_rgb(cr, 0, 0, 0); @@ -141,3 +163,15 @@ textdraw(u32int *buf, int w, int h, int x, int y, Str *s) cairo_surface_flush(surface); cairo_surface_destroy(surface); } + +void +textdraw(u32int *buf, int w, int h, int x, int y, Str *s) +{ + drawtext(buf, w, h, x, y, -1, s); +} + +void +textdrawfit(u32int *buf, int w, int h, int x, int y, int fit, Str *s) +{ + drawtext(buf, w, h, x, y, fit, s); +} diff --git a/popup_layout.c b/popup_layout.c index bd9252a..fc7bfff 100644 --- a/popup_layout.c +++ b/popup_layout.c @@ -5,11 +5,15 @@ void popupposition(Caret *caret, int pointerx, int pointery, int screenw, int screenh, int w, int h, int *x, int *y) { - vlong px, py, xmax, ymax; + vlong below, px, py, xmax, ymax; if(caret->valid){ px = caret->x; - py = (vlong)caret->y + max(caret->h, 0); + below = (vlong)caret->y + max(caret->h, 0); + if(below >= 0 && below + h <= screenh) + py = below; + else + py = (vlong)caret->y - h; }else{ px = (vlong)pointerx + 10; py = (vlong)pointery + 10; diff --git a/tests/font_test.c b/tests/font_test.c index 653e5b0..4faf5c2 100644 --- a/tests/font_test.c +++ b/tests/font_test.c @@ -178,8 +178,8 @@ font_render(struct ct *t) { static char *plain[] = { "A", "가", "あ", "漢", "☆", "𠀋" }; u32int *buf, *mem; - Str a, heart, missing; - int i, w; + Str a, heart, longrow, missing; + int i, natural, w, x, y; mem = emalloc((Testn + 2 * Guard) * sizeof mem[0]); fillpixels(mem, Testn + 2 * Guard, guardcolor); @@ -216,6 +216,21 @@ font_render(struct ct *t) checkclip(t, mem, Fontsz, -Fontsz / 2, &a); checkclip(t, mem, Fontsz, Testh - Fontsz / 2, &a); + longrow = mkstr("abcdefghijklmnopqrstuvwxyz"); + natural = textwidth(&longrow); + CT_CHECK(t, natural > 2*Fontsz); + fillpixels(buf, Testn, Colbg); + textdrawfit(buf, Testw, Testh, 2*Fontsz, Fontsz, + 2*Fontsz, &longrow); + CT_CHECK(t, hasink(buf, Testn, Colbg)); + for(y = Fontsz; y < 2*Fontsz; y++){ + for(x = 0; x < 2*Fontsz; x++) + CT_EQ_UINT(t, Colbg, buf[y * Testw + x] & Rgbmask); + for(x = 4*Fontsz; x < Testw; x++) + CT_EQ_UINT(t, Colbg, buf[y * Testw + x] & Rgbmask); + } + CT_EQ_INT(t, natural, textwidth(&longrow)); + missing = mkstr("\xF4\x8F\xBF\xBF"); for(i = 0; i < 32; i++){ textdraw(buf, Testw, Testh, Fontsz, Fontsz, &heart); diff --git a/tests/popup_test.c b/tests/popup_test.c index d2df539..6923b66 100644 --- a/tests/popup_test.c +++ b/tests/popup_test.c @@ -22,11 +22,38 @@ popup_layout(struct ct *t) caret.y = 590; popupposition(&caret, 0, 0, 800, 600, 100, 60, &x, &y); CT_EQ_INT(t, 700, x); - CT_EQ_INT(t, 540, y); + CT_EQ_INT(t, 530, y); + + /* Neither side fits: choose above, then clamp to the screen. */ + caret.x = 20; + caret.y = 30; + caret.h = 20; + popupposition(&caret, 0, 0, 80, 80, 100, 100, &x, &y); + CT_EQ_INT(t, 0, x); + CT_EQ_INT(t, 0, y); + + caret.x = 70; + caret.y = 80; + caret.h = 0; + popupposition(&caret, 0, 0, 800, 600, 100, 60, &x, &y); + CT_EQ_INT(t, 70, x); + CT_EQ_INT(t, 80, y); + caret.h = -20; + popupposition(&caret, 0, 0, 800, 600, 100, 60, &x, &y); + CT_EQ_INT(t, 70, x); + CT_EQ_INT(t, 80, y); + caret.x = INT_MIN; caret.y = INT_MAX; caret.h = INT_MAX; popupposition(&caret, 0, 0, 800, 600, 100, 60, &x, &y); CT_EQ_INT(t, 0, x); CT_EQ_INT(t, 540, y); + caret.x = INT_MAX; + caret.y = INT_MIN; + caret.h = INT_MIN; + popupposition(&caret, 0, 0, INT_MAX, INT_MAX, + INT_MAX, INT_MAX, &x, &y); + CT_EQ_INT(t, 0, x); + CT_EQ_INT(t, 0, y); } diff --git a/win.c b/win.c index 0f8bbed..baf30b6 100644 --- a/win.c +++ b/win.c @@ -161,6 +161,8 @@ drawkouho(Drawcmd *dc, int n, int w, int h) int npre, sely, y, i; Str num, *s; + if(w <= 0 || w > Imgw || h <= 0 || h > Imgh) + return; fill(img, w * h, Colbg); npre = dc->pre.n != 0; if(npre) @@ -174,13 +176,15 @@ drawkouho(Drawcmd *dc, int n, int w, int h) sclear(&num); sputr(&num, '1' + i + Asciitofull); textdraw(img, w, h, 0, y, &num); - textdraw(img, w, h, 2*Fontsz, y, s); + textdrawfit(img, w, h, 2*Fontsz, y, w - 2*Fontsz, s); } } static void putimage(int w, int h) { + if(w <= 0 || w > Imgw || h <= 0 || h > Imgh) + return; xcb_put_image(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, w, h, 0, 0, 0, depth, w * h * 4, (u8int*)img); /* The retained background pixmap lets the server repaint exposures. */ @@ -213,7 +217,9 @@ winshow(Drawcmd *dc) maxw = max(maxw, textwidth(&dc->kouho[i])); vals[3] = h = (n + npre) * Fontsz; width = (vlong)maxw + 3*Fontsz; - vals[2] = w = min(max(width, 1), Imgw); + vals[2] = w = min(min(max(width, 1), Imgw), scr->width_in_pixels); + if(w <= 0 || w > Imgw || h <= 0 || h > Imgh) + return 0; px = py = 0; if(!dc->caret.valid){ cookie = xcb_query_pointer(conn, scr->root);