popuplayout had two refit stages for a work area shorter than one padded row — 40 pixels — and popupdraw re-checked the layout it had just been handed, against an Imgh that bounded no buffer any more. Pango and Cairo abort rather than return nil, so textinit is void and the layout is never nil; the stride Cairo computes for RGB24 is w*4 by definition; and textdraw set the layout's width and ellipsis twice.
324 lines
8.9 KiB
C
324 lines
8.9 KiB
C
#include "dat.h"
|
||
#include "fn.h"
|
||
#include "test.h"
|
||
|
||
/* A popup with every section, tall enough for the tests. */
|
||
#define Imgh (2*PopupPad + (Maxdisp + 2)*Fontsz + PopupSep)
|
||
|
||
enum {
|
||
Rgbmask = 0xffffff,
|
||
};
|
||
|
||
static u32int
|
||
pixel(u32int *img, Popup *p, int x, int y)
|
||
{
|
||
return img[y*p->w + x] & Rgbmask;
|
||
}
|
||
|
||
static int
|
||
rectcolor(u32int *img, Popup *p, int x0, int y0, int x1, int y1,
|
||
u32int color)
|
||
{
|
||
int x, y;
|
||
|
||
color &= Rgbmask;
|
||
for(y = y0; y < y1; y++)
|
||
for(x = x0; x < x1; x++)
|
||
if(pixel(img, p, x, y) != color)
|
||
return 0;
|
||
return 1;
|
||
}
|
||
|
||
static void
|
||
checkpadding(struct ct *t, u32int *img, Popup *p)
|
||
{
|
||
CT_CHECK(t, rectcolor(img, p, 0, 0, p->w, PopupPad, Colbg));
|
||
CT_CHECK(t, rectcolor(img, p, 0, p->h - PopupPad,
|
||
p->w, p->h, Colbg));
|
||
CT_CHECK(t, rectcolor(img, p, 0, 0, PopupPad, p->h, Colbg));
|
||
CT_CHECK(t, rectcolor(img, p, p->w - PopupPad, 0,
|
||
p->w, p->h, Colbg));
|
||
}
|
||
|
||
void
|
||
popup_layout(struct ct *t)
|
||
{
|
||
static const struct {
|
||
int first;
|
||
int shown;
|
||
int total;
|
||
char *want;
|
||
} markers[] = {
|
||
{ 0, 0, 0, "" },
|
||
{ 0, 9, 9, "" },
|
||
{ 0, 3, 9, "1-3/9" },
|
||
{ 8, 1, 9, "9-9/9" },
|
||
{ 0, 9, 10, "1-9/10" },
|
||
{ 9, 1, 10, "10-10/10" },
|
||
{ 0, 9, 18, "1-9/18" },
|
||
{ 9, 9, 18, "10-18/18" },
|
||
{ 9, 9, 32, "10-18/32" },
|
||
{ 18, 9, 32, "19-27/32" },
|
||
{ 27, 5, 32, "28-32/32" },
|
||
};
|
||
char buf[32];
|
||
Area area, mon[2], out, work;
|
||
Caret caret;
|
||
Drawcmd dc;
|
||
Popup p;
|
||
Str longrow;
|
||
u32int c, *img;
|
||
int i, ink, markink, n, numink, selbg, textink, x, y;
|
||
|
||
for(i = 0; i < nelem(markers); i++){
|
||
n = pagemarker(buf, sizeof buf, markers[i].first,
|
||
markers[i].shown, markers[i].total);
|
||
CT_EQ_INT(t, strlen(markers[i].want), n);
|
||
CT_CHECK(t, strcmp(markers[i].want, buf) == 0);
|
||
}
|
||
|
||
/* Monitor selection retains origins and intersects the EWMH work area. */
|
||
mon[0] = (Area){0, 0, 1920, 1080};
|
||
mon[1] = (Area){1920, 200, 1280, 1024};
|
||
work = (Area){0, 30, 3200, 1070};
|
||
popuparea(mon, 2, &work, 2500, 500, &out);
|
||
CT_EQ_INT(t, 1920, out.x);
|
||
CT_EQ_INT(t, 200, out.y);
|
||
CT_EQ_INT(t, 1280, out.w);
|
||
CT_EQ_INT(t, 900, out.h);
|
||
work = (Area){60, 0, 3140, 1224};
|
||
popuparea(mon, 2, &work, 100, 100, &out);
|
||
CT_EQ_INT(t, 60, out.x);
|
||
CT_EQ_INT(t, 0, out.y);
|
||
CT_EQ_INT(t, 1860, out.w);
|
||
CT_EQ_INT(t, 1080, out.h);
|
||
work = (Area){5000, 0, 100, 100};
|
||
popuparea(mon, 2, &work, 2500, 500, &out);
|
||
CT_EQ_INT(t, mon[1].x, out.x);
|
||
CT_EQ_INT(t, mon[1].y, out.y);
|
||
CT_EQ_INT(t, mon[1].w, out.w);
|
||
CT_EQ_INT(t, mon[1].h, out.h);
|
||
mon[0] = (Area){0, 0, 100, 100};
|
||
mon[1] = (Area){200, 0, 100, 100};
|
||
popuparea(mon, 2, nil, 150, 50, &out);
|
||
CT_EQ_INT(t, 0, out.x);
|
||
|
||
memset(&caret, 0, sizeof caret);
|
||
area = (Area){1920, 30, 1280, 900};
|
||
popupposition(&caret, 2000, 50, &area, 100, 60, &x, &y);
|
||
CT_EQ_INT(t, 2010, x);
|
||
CT_EQ_INT(t, 60, y);
|
||
popupposition(&caret, 2000, 900, &area, 100, 60, &x, &y);
|
||
CT_EQ_INT(t, 830, y); /* above the pointer, not over it */
|
||
caret.valid = 1;
|
||
caret.x = 2100;
|
||
caret.y = 80;
|
||
caret.h = 20;
|
||
popupposition(&caret, 0, 0, &area, 100, 60, &x, &y);
|
||
CT_EQ_INT(t, 2100, x);
|
||
CT_EQ_INT(t, 100, y);
|
||
caret.x = 3150;
|
||
caret.y = 850;
|
||
popupposition(&caret, 0, 0, &area, 200, 100, &x, &y);
|
||
CT_EQ_INT(t, 3000, x);
|
||
CT_EQ_INT(t, 750, y);
|
||
|
||
/* A popup no larger than a small work area stays inside its origin. */
|
||
area = (Area){100, 200, 80, 80};
|
||
caret.x = 120;
|
||
caret.y = 230;
|
||
caret.h = 20;
|
||
popupposition(&caret, 0, 0, &area, 80, 80, &x, &y);
|
||
CT_EQ_INT(t, 100, x);
|
||
CT_EQ_INT(t, 200, y);
|
||
|
||
textinit();
|
||
|
||
/* Empty sections reserve nothing. */
|
||
memset(&dc, 0, sizeof dc);
|
||
dc.sel = -1;
|
||
popuplayout(&dc, PopupBasew, Imgh, &p);
|
||
CT_EQ_INT(t, 0, p.w);
|
||
CT_EQ_INT(t, 0, p.h);
|
||
CT_EQ_INT(t, -1, p.prey);
|
||
CT_EQ_INT(t, -1, p.rowsy);
|
||
CT_EQ_INT(t, -1, p.marky);
|
||
|
||
/* Preedit only. */
|
||
dc.pre = mkstr("preedit");
|
||
popuplayout(&dc, 2*PopupBasew, Imgh, &p);
|
||
CT_EQ_INT(t, PopupBasew, p.w);
|
||
CT_EQ_INT(t, PopupPad, p.prey);
|
||
CT_EQ_INT(t, -1, p.sepy);
|
||
CT_EQ_INT(t, -1, p.rowsy);
|
||
CT_EQ_INT(t, -1, p.marky);
|
||
CT_EQ_INT(t, 2*PopupPad + Fontsz, p.h);
|
||
|
||
/* Candidates only. */
|
||
memset(&dc, 0, sizeof dc);
|
||
dc.sel = -1;
|
||
dc.nkouho = 2;
|
||
dc.kouho[0] = mkstr("short");
|
||
dc.kouho[1] = mkstr("candidate");
|
||
popuplayout(&dc, 2*PopupBasew, Imgh, &p);
|
||
CT_EQ_INT(t, PopupBasew, p.w);
|
||
CT_EQ_INT(t, -1, p.prey);
|
||
CT_EQ_INT(t, -1, p.sepy);
|
||
CT_EQ_INT(t, PopupPad, p.rowsy);
|
||
CT_EQ_INT(t, -1, p.marky);
|
||
CT_EQ_INT(t, 2*PopupPad + 2*Fontsz, p.h);
|
||
CT_EQ_INT(t, PopupTextw, p.textw);
|
||
CT_EQ_INT(t, p.w - PopupPad, PopupPad + PopupNumw + p.textw);
|
||
|
||
/* Preedit and candidates meet at one separator, with no empty rows. */
|
||
dc.pre = mkstr("preedit");
|
||
popuplayout(&dc, 2*PopupBasew, Imgh, &p);
|
||
CT_EQ_INT(t, PopupBasew, p.w);
|
||
CT_EQ_INT(t, PopupPad, p.prey);
|
||
CT_EQ_INT(t, p.prey + Fontsz, p.sepy);
|
||
CT_EQ_INT(t, p.sepy + PopupSep, p.rowsy);
|
||
CT_EQ_INT(t, -1, p.marky);
|
||
CT_EQ_INT(t, p.rowsy + 2*Fontsz + PopupPad, p.h);
|
||
CT_EQ_INT(t, 2*PopupPad + 3*Fontsz + PopupSep, p.h);
|
||
|
||
/* Long preedit expands to the monitor cap just like a candidate. */
|
||
sclear(&longrow);
|
||
for(i = 0; i < Maxrunes; i++)
|
||
sputr(&longrow, 'W');
|
||
memset(&dc, 0, sizeof dc);
|
||
dc.pre = longrow;
|
||
popuplayout(&dc, 2*PopupBasew, Imgh, &p);
|
||
CT_EQ_INT(t, 2*PopupBasew, p.w);
|
||
|
||
/* The largest panel includes all sections and fits its backing image. */
|
||
memset(&dc, 0, sizeof dc);
|
||
dc.pre = mkstr("preedit");
|
||
dc.nkouho = Maxdisp;
|
||
dc.sel = 4;
|
||
dc.first = 0;
|
||
dc.total = Maxdisp + 1;
|
||
for(i = 0; i < Maxdisp; i++)
|
||
dc.kouho[i] = mkstr(i == dc.sel ? "M" : "candidate");
|
||
dc.kouho[0] = longrow;
|
||
popuplayout(&dc, 2*PopupBasew, Imgh, &p);
|
||
CT_EQ_INT(t, Maxdisp, p.n);
|
||
CT_EQ_INT(t, PopupPad, p.prey);
|
||
CT_EQ_INT(t, p.prey + Fontsz, p.sepy);
|
||
CT_EQ_INT(t, p.sepy + PopupSep, p.rowsy);
|
||
CT_EQ_INT(t, p.rowsy + Maxdisp*Fontsz, p.marky);
|
||
CT_EQ_INT(t, p.marky + Fontsz + PopupPad, p.h);
|
||
CT_EQ_INT(t, Imgh, p.h);
|
||
CT_EQ_INT(t, 2*PopupBasew, p.w);
|
||
CT_CHECK(t, p.h <= Imgh);
|
||
CT_EQ_INT(t, p.w - PopupPad, p.markx + p.markw);
|
||
CT_EQ_INT(t, p.w - 2*PopupPad, p.selw);
|
||
CT_EQ_INT(t, p.rowsy + dc.sel*Fontsz, p.sely);
|
||
CT_EQ_INT(t, p.w - PopupPad, PopupPad + PopupNumw + p.textw);
|
||
|
||
img = emalloc(2*PopupBasew*Imgh*sizeof img[0]);
|
||
popupdraw(img, &dc, &p);
|
||
checkpadding(t, img, &p);
|
||
for(x = 0; x < p.w; x++)
|
||
CT_EQ_UINT(t,
|
||
x >= PopupPad && x < p.w - PopupPad ? Colsep : Colbg,
|
||
pixel(img, &p, x, p.sepy));
|
||
|
||
/* Selection paint and both foreground columns stay inside the row. */
|
||
selbg = numink = textink = 0;
|
||
for(y = p.sely; y < p.sely + Fontsz; y++){
|
||
for(x = 0; x < p.w; x++){
|
||
c = pixel(img, &p, x, y);
|
||
if(x < PopupPad || x >= PopupPad + p.selw){
|
||
CT_EQ_UINT(t, Colbg, c);
|
||
continue;
|
||
}
|
||
if(c == (Colsel & Rgbmask)){
|
||
selbg++;
|
||
continue;
|
||
}
|
||
CT_CHECK(t,
|
||
(x >= PopupPad && x < PopupPad + PopupNumw) ||
|
||
(x >= (PopupPad + PopupNumw) && x < (PopupPad + PopupNumw) + p.textw));
|
||
if(x < PopupPad + PopupNumw)
|
||
numink++;
|
||
else
|
||
textink++;
|
||
}
|
||
}
|
||
CT_CHECK(t, Colsel != Colselfg);
|
||
CT_CHECK(t, selbg > p.selw*Fontsz/2);
|
||
CT_CHECK(t, numink > 0);
|
||
CT_CHECK(t, textink > 0);
|
||
|
||
/* The marker occupies only its compact, right-aligned footer box. */
|
||
markink = 0;
|
||
for(y = p.marky; y < p.marky + Fontsz; y++){
|
||
for(x = 0; x < p.w; x++){
|
||
if(pixel(img, &p, x, y) == (Colbg & Rgbmask))
|
||
continue;
|
||
markink++;
|
||
CT_CHECK(t, x >= p.markx && x < p.markx + p.markw);
|
||
}
|
||
}
|
||
CT_CHECK(t, markink > 0);
|
||
|
||
/* A long candidate grows from the stable base to the monitor cap. */
|
||
memset(&dc, 0, sizeof dc);
|
||
dc.sel = -1;
|
||
dc.nkouho = 1;
|
||
dc.kouho[0] = longrow;
|
||
n = textwidth(&longrow);
|
||
popuplayout(&dc, 2*PopupBasew, Imgh, &p);
|
||
CT_EQ_INT(t, 2*PopupBasew, p.w);
|
||
CT_CHECK(t, p.textw > PopupTextw);
|
||
CT_CHECK(t, n > p.textw);
|
||
CT_EQ_INT(t, PopupPad, p.rowsy);
|
||
CT_EQ_INT(t, p.w - PopupPad, PopupPad + PopupNumw + p.textw);
|
||
popupdraw(img, &dc, &p);
|
||
ink = 0;
|
||
for(y = p.rowsy; y < p.rowsy + Fontsz; y++){
|
||
for(x = 0; x < p.w; x++){
|
||
c = pixel(img, &p, x, y);
|
||
if(x < PopupPad || x >= p.w - PopupPad)
|
||
CT_EQ_UINT(t, Colbg, c);
|
||
if(x >= (PopupPad + PopupNumw) && x < (PopupPad + PopupNumw) + p.textw &&
|
||
c != (Colbg & Rgbmask))
|
||
ink++;
|
||
}
|
||
}
|
||
CT_CHECK(t, ink > 0);
|
||
CT_EQ_INT(t, n, textwidth(&longrow));
|
||
|
||
popuplayout(&dc, PopupBasew - 1, Imgh, &p);
|
||
CT_EQ_INT(t, PopupBasew - 1, p.w);
|
||
|
||
/* Height truncation scrolls the row window to the selected candidate. */
|
||
memset(&dc, 0, sizeof dc);
|
||
dc.nkouho = Maxdisp;
|
||
dc.sel = Maxdisp - 1;
|
||
dc.total = Maxdisp;
|
||
for(i = 0; i < Maxdisp; i++)
|
||
dc.kouho[i] = mkstr("candidate");
|
||
popuplayout(&dc, PopupBasew, 2*PopupPad + 2*Fontsz, &p);
|
||
CT_EQ_INT(t, 1, p.n);
|
||
CT_EQ_INT(t, Maxdisp - 1, p.row0);
|
||
CT_EQ_INT(t, PopupPad, p.sely);
|
||
CT_CHECK(t, p.marky >= 0);
|
||
CT_CHECK(t, p.h <= 2*PopupPad + 2*Fontsz);
|
||
popupdraw(img, &dc, &p);
|
||
numink = 0;
|
||
for(y = p.rowsy; y < p.rowsy + Fontsz; y++)
|
||
for(x = PopupPad; x < PopupPad + PopupNumw; x++)
|
||
if(pixel(img, &p, x, y) != (Colsel & Rgbmask))
|
||
numink++;
|
||
markink = 0;
|
||
for(y = p.marky; y < p.marky + Fontsz; y++)
|
||
for(x = p.markx; x < p.markx + p.markw; x++)
|
||
if(pixel(img, &p, x, y) != (Colbg & Rgbmask))
|
||
markink++;
|
||
CT_CHECK(t, numink > 0); /* full-width 9 */
|
||
CT_CHECK(t, markink > 0); /* 9-9/9 */
|
||
|
||
free(img);
|
||
}
|