Files
strans/popup_layout.c
Hojun-Cho 87cb03fdf8 popup: no fallbacks for work areas no desktop has; text setup cannot fail
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.
2026-08-17 01:36:16 +09:00

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));
}