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.
272 lines
5.9 KiB
C
272 lines
5.9 KiB
C
#include "dat.h"
|
|
#include "fn.h"
|
|
|
|
enum {
|
|
Asciitofull = 0xFEE0,
|
|
};
|
|
|
|
static void
|
|
fill(u32int *buf, int n, u32int color)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < n; i++)
|
|
buf[i] = color;
|
|
}
|
|
|
|
static void
|
|
fillrect(u32int *buf, int w, int h, int x, int y, int rw, int rh,
|
|
u32int color)
|
|
{
|
|
int x0, x1, y0, y1;
|
|
|
|
x0 = min(max(x, 0), w);
|
|
x1 = min(max(x + max(rw, 0), 0), w);
|
|
y0 = min(max(y, 0), h);
|
|
y1 = min(max(y + max(rh, 0), 0), h);
|
|
for(; y0 < y1; y0++)
|
|
fill(buf + y0*w + x0, max(x1 - x0, 0), color);
|
|
}
|
|
|
|
static int
|
|
validarea(Area *a)
|
|
{
|
|
return a != nil && a->w > 0 && a->h > 0;
|
|
}
|
|
|
|
static vlong
|
|
areadistance(Area *a, int x, int y)
|
|
{
|
|
vlong dx, dy, right, bottom;
|
|
|
|
right = (vlong)a->x + a->w;
|
|
bottom = (vlong)a->y + a->h;
|
|
dx = x < a->x ? (vlong)a->x - x : x >= right ? x - right : 0;
|
|
dy = y < a->y ? (vlong)a->y - y : y >= bottom ? y - bottom : 0;
|
|
return dx + dy;
|
|
}
|
|
|
|
void
|
|
popuparea(Area *mon, int nmon, Area *work, int x, int y, Area *out)
|
|
{
|
|
vlong best, bottom, d, right, x0, x1, y0, y1;
|
|
int i, pick;
|
|
|
|
memset(out, 0, sizeof *out);
|
|
pick = -1;
|
|
best = (vlong)1 << 62;
|
|
for(i = 0; i < nmon; i++){
|
|
if(!validarea(&mon[i]))
|
|
continue;
|
|
right = (vlong)mon[i].x + mon[i].w;
|
|
bottom = (vlong)mon[i].y + mon[i].h;
|
|
if(x >= mon[i].x && x < right &&
|
|
y >= mon[i].y && y < bottom){
|
|
pick = i;
|
|
break;
|
|
}
|
|
d = areadistance(&mon[i], x, y);
|
|
if(d < best){
|
|
best = d;
|
|
pick = i;
|
|
}
|
|
}
|
|
if(pick < 0)
|
|
return;
|
|
*out = mon[pick];
|
|
if(!validarea(work))
|
|
return;
|
|
x0 = max((vlong)out->x, work->x);
|
|
y0 = max((vlong)out->y, work->y);
|
|
x1 = min((vlong)out->x + out->w, (vlong)work->x + work->w);
|
|
y1 = min((vlong)out->y + out->h, (vlong)work->y + work->h);
|
|
if(x1 <= x0 || y1 <= y0)
|
|
return;
|
|
out->x = x0;
|
|
out->y = y0;
|
|
out->w = x1 - x0;
|
|
out->h = y1 - y0;
|
|
}
|
|
|
|
int
|
|
pagemarker(char *buf, int nbuf, int first, int shown, int total)
|
|
{
|
|
if(nbuf <= 0)
|
|
return 0;
|
|
buf[0] = 0;
|
|
if(shown <= 0 || (first == 0 && shown >= total))
|
|
return 0;
|
|
return snprint(buf, nbuf, "%d-%d/%d", first + 1,
|
|
first + shown, total);
|
|
}
|
|
|
|
static int
|
|
fitrows(int h, int pre, int mark, int n)
|
|
{
|
|
int fixed;
|
|
|
|
fixed = 2*PopupPad + mark*Fontsz;
|
|
if(pre)
|
|
fixed += Fontsz + (n != 0 ? PopupSep : 0);
|
|
if(h <= fixed)
|
|
return 0;
|
|
return min(n, (h - fixed) / Fontsz);
|
|
}
|
|
|
|
void
|
|
popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p)
|
|
{
|
|
char buf[32];
|
|
int first, i, markrow, nall, nmark, npre, pad, total, width, y;
|
|
|
|
memset(p, 0, sizeof *p);
|
|
p->prey = -1;
|
|
p->sepy = -1;
|
|
p->rowsy = -1;
|
|
p->markx = -1;
|
|
p->marky = -1;
|
|
p->sely = -1;
|
|
pad = PopupPad;
|
|
nall = min(max(dc->nkouho, 0), Maxdisp);
|
|
npre = dc->pre.n != 0;
|
|
if((nall == 0 && !npre) || areaw <= 0 || areah <= 0)
|
|
return;
|
|
|
|
total = max(dc->total, dc->first + nall);
|
|
markrow = dc->first > 0 || nall < total;
|
|
p->n = fitrows(areah, npre, markrow, nall);
|
|
if(p->n < nall && !markrow){
|
|
markrow = 1;
|
|
p->n = fitrows(areah, npre, markrow, nall);
|
|
}
|
|
if(p->n == 0 && markrow){
|
|
markrow = 0;
|
|
p->n = fitrows(areah, npre, markrow, nall);
|
|
}
|
|
if(p->n == 0 && npre && nall != 0){
|
|
npre = 0;
|
|
p->n = fitrows(areah, npre, markrow, nall);
|
|
}
|
|
if(p->n == 0 && nall != 0){
|
|
p->n = 1;
|
|
pad = 0;
|
|
}
|
|
if(p->n != 0 && dc->sel >= p->n)
|
|
p->row0 = min(dc->sel - p->n + 1, nall - p->n);
|
|
first = dc->first + p->row0;
|
|
nmark = markrow ? pagemarker(buf, sizeof buf, first, p->n, total) : 0;
|
|
if(nmark == 0)
|
|
markrow = 0;
|
|
|
|
width = PopupBasew;
|
|
if(npre)
|
|
width = max(width, textwidth(&dc->pre) + 2*PopupPad);
|
|
for(i = 0; i < p->n; i++)
|
|
width = max(width, textwidth(&dc->kouho[p->row0+i]) +
|
|
PopupNumw + 2*PopupPad);
|
|
if(markrow){
|
|
sinit(&p->mark, buf, nmark);
|
|
p->markw = textwidth(&p->mark);
|
|
width = max(width, p->markw + 2*PopupPad);
|
|
}
|
|
p->w = min(width, areaw);
|
|
|
|
y = pad;
|
|
if(npre){
|
|
p->prey = y;
|
|
y += Fontsz;
|
|
}
|
|
if(npre && p->n != 0){
|
|
p->sepy = y;
|
|
y += PopupSep;
|
|
}
|
|
if(p->n != 0){
|
|
p->rowsy = y;
|
|
y += p->n*Fontsz;
|
|
}
|
|
if(markrow){
|
|
p->marky = y;
|
|
y += Fontsz;
|
|
}
|
|
p->h = min(y + pad, areah);
|
|
p->numx = PopupPad;
|
|
p->textx = PopupPad + PopupNumw;
|
|
p->textw = max(p->w - PopupPad - p->textx, 0);
|
|
if(markrow){
|
|
p->markw = min(p->markw, max(p->w - 2*PopupPad, 0));
|
|
p->markx = p->w - PopupPad - p->markw;
|
|
}
|
|
p->selx = PopupPad;
|
|
p->selw = max(p->w - 2*PopupPad, 0);
|
|
if(dc->sel >= p->row0 && dc->sel < p->row0 + p->n)
|
|
p->sely = p->rowsy + (dc->sel - p->row0)*Fontsz;
|
|
}
|
|
|
|
void
|
|
popupdraw(u32int *img, Drawcmd *dc, Popup *p)
|
|
{
|
|
Str num;
|
|
u32int color;
|
|
int i, j, y;
|
|
|
|
if(img == nil || p->n < 0 || p->n > Maxdisp ||
|
|
p->row0 < 0 || p->row0 + p->n > dc->nkouho ||
|
|
p->w <= 0 || p->h <= 0 || p->h > Imgh)
|
|
return;
|
|
fill(img, p->w*p->h, Colbg);
|
|
if(p->prey >= 0)
|
|
textdraw(img, p->w, p->h, PopupPad, p->prey,
|
|
max(p->w - 2*PopupPad, 0), Colfg, &dc->pre);
|
|
if(p->sepy >= 0)
|
|
fillrect(img, p->w, p->h, PopupPad, p->sepy,
|
|
p->w - 2*PopupPad, PopupSep, Colsep);
|
|
if(p->sely >= 0)
|
|
fillrect(img, p->w, p->h, p->selx, p->sely,
|
|
p->selw, Fontsz, Colsel);
|
|
for(i = 0, y = p->rowsy; i < p->n; i++, y += Fontsz){
|
|
j = p->row0 + i;
|
|
color = j == dc->sel ? Colselfg : Colfg;
|
|
sclear(&num);
|
|
sputr(&num, '1' + j + Asciitofull);
|
|
textdraw(img, p->w, p->h, p->numx, y,
|
|
min(PopupNumw, max(p->w - PopupPad - p->numx, 0)),
|
|
color, &num);
|
|
textdraw(img, p->w, p->h, p->textx, y, p->textw, color,
|
|
&dc->kouho[j]);
|
|
}
|
|
if(p->marky >= 0)
|
|
textdraw(img, p->w, p->h, p->markx, p->marky, p->markw,
|
|
Colfg, &p->mark);
|
|
}
|
|
|
|
void
|
|
popupposition(Caret *caret, int pointerx, int pointery, Area *area,
|
|
int w, int h, int *x, int *y)
|
|
{
|
|
vlong below, bottom, px, py, right, xmax, ymax;
|
|
|
|
if(!validarea(area)){
|
|
*x = 0;
|
|
*y = 0;
|
|
return;
|
|
}
|
|
right = (vlong)area->x + area->w;
|
|
bottom = (vlong)area->y + area->h;
|
|
|
|
if(caret->valid){
|
|
px = caret->x;
|
|
below = (vlong)caret->y + max(caret->h, 0);
|
|
if(below >= area->y && below + h <= bottom)
|
|
py = below;
|
|
else
|
|
py = (vlong)caret->y - h;
|
|
}else{
|
|
px = (vlong)pointerx + 10;
|
|
py = (vlong)pointery + 10;
|
|
}
|
|
xmax = max(right - w, area->x);
|
|
ymax = max(bottom - h, area->y);
|
|
*x = max((vlong)area->x, min(px, xmax));
|
|
*y = max((vlong)area->y, min(py, ymax));
|
|
}
|