font.c exported three wrappers for one function, and the fit == -1 mode existed only for the tests; textdraw() takes fit and colour. win.c tested the empty picture in the thread and again in winshow, interned two atoms by hand next to getatom(), kept a consumer-side drain that the producer's already guarantees never finds anything, zeroed ten globals before returning from the only thread that read them, and re-checked sizes that resizebacking had just established. The page marker is laid out once and drawn from the layout instead of being recomputed.
345 lines
9.5 KiB
C
345 lines
9.5 KiB
C
#include "test.h"
|
||
|
||
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 int tinyheight[] = {
|
||
1, PopupPad, 2*PopupPad + Fontsz - 1,
|
||
};
|
||
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);
|
||
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);
|
||
|
||
if(!CT_CHECK(t, textinit()))
|
||
return;
|
||
|
||
/* 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, PopupPad, p.numx);
|
||
CT_EQ_INT(t, p.numx + PopupNumw, p.textx);
|
||
CT_EQ_INT(t, PopupTextw, p.textw);
|
||
CT_EQ_INT(t, p.w - PopupPad, p.textx + 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, PopupPad, p.selx);
|
||
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, PopupPad, p.numx);
|
||
CT_EQ_INT(t, p.numx + PopupNumw, p.textx);
|
||
CT_EQ_INT(t, p.w - PopupPad, p.textx + 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 < p.selx || x >= p.selx + p.selw){
|
||
CT_EQ_UINT(t, Colbg, c);
|
||
continue;
|
||
}
|
||
if(c == (Colsel & Rgbmask)){
|
||
selbg++;
|
||
continue;
|
||
}
|
||
CT_CHECK(t,
|
||
(x >= p.numx && x < p.numx + PopupNumw) ||
|
||
(x >= p.textx && x < p.textx + p.textw));
|
||
if(x < p.numx + 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, p.textx + 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 >= p.textx && x < p.textx + 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 = p.numx; x < p.numx + 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 */
|
||
|
||
/* The selected row wins over preedit and footer in one-row height. */
|
||
dc.pre = mkstr("preedit");
|
||
popuplayout(&dc, PopupBasew, 2*PopupPad + Fontsz, &p);
|
||
CT_EQ_INT(t, -1, p.prey);
|
||
CT_EQ_INT(t, 1, p.n);
|
||
CT_EQ_INT(t, Maxdisp - 1, p.row0);
|
||
CT_EQ_INT(t, -1, p.marky);
|
||
|
||
/* With less than one padded row, selected content starts at the top. */
|
||
for(i = 0; i < nelem(tinyheight); i++){
|
||
popuplayout(&dc, PopupBasew, tinyheight[i], &p);
|
||
CT_EQ_INT(t, min(tinyheight[i], Fontsz), p.h);
|
||
CT_EQ_INT(t, 0, p.rowsy);
|
||
CT_EQ_INT(t, 0, p.sely);
|
||
CT_EQ_INT(t, 1, p.n);
|
||
CT_EQ_INT(t, Maxdisp - 1, p.row0);
|
||
popupdraw(img, &dc, &p);
|
||
CT_EQ_UINT(t, Colsel, pixel(img, &p, p.w - PopupPad - 1, 0));
|
||
}
|
||
free(img);
|
||
}
|