Files
strans/tests/popup_test.c
Hojun-Cho bab40c95a7 popup: a border, a preedit as wide as its text, one place by the pointer, and no dying
The popup was a white box on the usually white text it covered, with
no edge to tell them apart; it has a border in the separator's colour.
A syllable being composed came in a bar twelve ems wide, wiping out the
line under it: only candidate rows share that steady width now, a
preedit alone hugs its text.  A popup placed by the pointer, when the
client sends no caret, was placed again on every key and so followed
the mouse; it stays where it came up.  Clicks on it no longer fall
through to the root window and its menu.  And one failed draw — a lost
pointer reply, a pixmap the server refused — ended the draw thread and
the popup for the rest of the session; only a dead connection does now.
2026-08-17 01:41:54 +09:00

324 lines
9.0 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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: as wide as its text. */
dc.pre = mkstr("preedit");
popuplayout(&dc, 2*PopupBasew, Imgh, &p);
CT_EQ_INT(t, textwidth(&dc.pre) + 2*PopupPad, 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 */
CT_CHECK(t, markink > 0); /* 9-9/9 */
free(img);
}