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.
257 lines
5.6 KiB
C
257 lines
5.6 KiB
C
#include "dat.h"
|
|
#include "fn.h"
|
|
|
|
enum {
|
|
Asciitofull = 0xFEE0,
|
|
};
|
|
|
|
int popupscale = 1;
|
|
|
|
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, total, width, y;
|
|
|
|
memset(p, 0, sizeof *p);
|
|
p->prey = -1;
|
|
p->sepy = -1;
|
|
p->rowsy = -1;
|
|
p->marky = -1;
|
|
p->sely = -1;
|
|
nall = min(max(dc->nkouho, 0), Maxdisp);
|
|
npre = dc->pre.n != 0;
|
|
if((nall == 0 && !npre) || areaw <= 0 || areah <= 0)
|
|
return;
|
|
|
|
/* As many rows as fit, and a page marker when they do not all. */
|
|
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 && !npre)
|
|
return;
|
|
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 = PopupPad;
|
|
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 + PopupPad, areah);
|
|
p->textw = max(p->w - 2*PopupPad - PopupNumw, 0);
|
|
if(markrow){
|
|
p->markw = min(p->markw, max(p->w - 2*PopupPad, 0));
|
|
p->markx = p->w - PopupPad - p->markw;
|
|
}
|
|
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;
|
|
|
|
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, PopupPad, 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, PopupPad, y,
|
|
min(PopupNumw, max(p->w - 2*PopupPad, 0)), color, &num);
|
|
textdraw(img, p->w, p->h, PopupPad + PopupNumw, 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;
|
|
if(py + h > bottom)
|
|
py = (vlong)pointery - 10 - h;
|
|
}
|
|
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));
|
|
}
|