plan9fox/libpanel/list.c
xfnw 856abd2f7d Squashed 'sys/src/cmd/gopher/' content from commit 3680728b6
git-subtree-dir: sys/src/cmd/gopher
git-subtree-split: 3680728b631ed65201b397f4ae3e5d1b03be42f9
2022-07-01 15:46:23 -04:00

191 lines
4.6 KiB
C

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
typedef struct List List;
struct List{
void (*hit)(Panel *, int, int); /* call user back on hit */
char *(*gen)(Panel *, int); /* return text given index or 0 if out of range */
int lo; /* indices of first, last items displayed */
int sel; /* index of hilited item */
int len; /* # of items in list */
Rectangle listr;
Point minsize;
int buttons;
};
#define MAXHGT 12
void pl_listsel(Panel *p, int sel, int on){
List *lp;
int hi;
Rectangle r;
lp=p->data;
hi=lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height;
if(lp->lo>=0 && lp->lo<=sel && sel<hi && sel<lp->len){
r=lp->listr;
r.min.y+=(sel-lp->lo)*font->height;
r.max.y=r.min.y+font->height;
if(on)
pl_highlight(p->b, r);
else{
pl_fill(p->b, r);
pl_drawicon(p->b, r, PLACEW, 0, lp->gen(p, sel));
}
}
}
void pl_liststrings(Panel *p, int lo, int hi, Rectangle r){
Panel *sb;
List *lp;
char *s;
int i;
lp=p->data;
for(i=lo;i!=hi && (s=lp->gen(p, i));i++){
r.max.y=r.min.y+font->height;
pl_drawicon(p->b, r, PLACEW, 0, s);
r.min.y+=font->height;
}
if(lo<=lp->sel && lp->sel<hi) pl_listsel(p, lp->sel, 1);
sb=p->yscroller;
if(sb && sb->setscrollbar)
sb->setscrollbar(sb, lp->lo,
lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height, lp->len);
}
void pl_drawlist(Panel *p){
List *lp;
lp=p->data;
lp->listr=pl_box(p->b, p->r, UP);
pl_liststrings(p, lp->lo, lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height,
lp->listr);
}
int pl_hitlist(Panel *p, Mouse *m){
int oldsel, hitme;
Point ul, size;
List *lp;
lp=p->data;
hitme=0;
ul=p->r.min;
size=subpt(p->r.max, p->r.min);
pl_interior(p->state, &ul, &size);
oldsel=lp->sel;
if(m->buttons&OUT){
p->state=UP;
if(m->buttons&~OUT) lp->sel=-1;
}
else if(p->state==DOWN || m->buttons&7){
lp->sel=(m->xy.y-ul.y)/font->height+lp->lo;
if(m->buttons&7){
lp->buttons=m->buttons;
p->state=DOWN;
}
else{
hitme=1;
p->state=UP;
}
}
if(oldsel!=lp->sel){
pl_listsel(p, oldsel, 0);
pl_listsel(p, lp->sel, 1);
}
if(hitme && 0<=lp->sel && lp->sel<lp->len && lp->hit)
lp->hit(p, lp->buttons, lp->sel);
return 0;
}
void pl_scrolllist(Panel *p, int dir, int buttons, int val, int len){
Point ul, size;
int nlist, oldlo, hi, nline, y;
List *lp;
Rectangle r;
lp=p->data;
ul=p->r.min;
size=subpt(p->r.max, p->r.min);
pl_interior(p->state, &ul, &size);
nlist=size.y/font->height;
oldlo=lp->lo;
if(dir==VERT) switch(buttons){
case 1: lp->lo-=nlist*val/len; break;
case 2: lp->lo=lp->len*val/len; break;
case 4: lp->lo+=nlist*val/len; break;
}
if(lp->lo<0) lp->lo=0;
if(lp->lo>=lp->len) lp->lo=lp->len-1;
if(lp->lo==oldlo) return;
p->scr.pos.y=lp->lo;
r=lp->listr;
nline=(r.max.y-r.min.y)/font->height;
hi=lp->lo+nline;
if(hi<=oldlo || lp->lo>=oldlo+nline){
pl_box(p->b, r, PASSIVE);
pl_liststrings(p, lp->lo, hi, r);
}
else if(lp->lo<oldlo){
y=r.min.y+(oldlo-lp->lo)*font->height;
pl_cpy(p->b, Pt(r.min.x, y),
Rect(r.min.x, r.min.y, r.max.x, r.min.y+(hi-oldlo)*font->height));
r.max.y=y;
pl_box(p->b, r, PASSIVE);
pl_liststrings(p, lp->lo, oldlo, r);
}
else{
pl_cpy(p->b, r.min, Rect(r.min.x, r.min.y+(lp->lo-oldlo)*font->height,
r.max.x, r.max.y));
r.min.y=r.min.y+(oldlo+nline-lp->lo)*font->height;
pl_box(p->b, r, PASSIVE);
pl_liststrings(p, oldlo+nline, hi, r);
}
}
void pl_typelist(Panel *g, Rune c){
USED(g, c);
}
Point pl_getsizelist(Panel *p, Point children){
USED(children);
return pl_boxsize(((List *)p->data)->minsize, p->state);
}
void pl_childspacelist(Panel *g, Point *ul, Point *size){
USED(g, ul, size);
}
void plinitlist(Panel *v, int flags, char *(*gen)(Panel *, int), int nlist, void (*hit)(Panel *, int, int)){
List *lp;
int wid, max;
char *str;
lp=v->data;
v->flags=flags|LEAF;
v->state=UP;
v->draw=pl_drawlist;
v->hit=pl_hitlist;
v->type=pl_typelist;
v->getsize=pl_getsizelist;
v->childspace=pl_childspacelist;
lp->gen=gen;
lp->hit=hit;
max=0;
for(lp->len=0;str=gen(v, lp->len);lp->len++){
wid=stringwidth(font, str);
if(wid>max) max=wid;
}
if(flags&(FILLX|EXPAND)){
for(lp->len=0;gen(v, lp->len);lp->len++);
lp->minsize=Pt(0, nlist*font->height);
}
else{
max=0;
for(lp->len=0;str=gen(v, lp->len);lp->len++){
wid=stringwidth(font, str);
if(wid>max) max=wid;
}
lp->minsize=Pt(max, nlist*font->height);
}
lp->sel=-1;
lp->lo=0;
v->scroll=pl_scrolllist;
v->scr.pos=Pt(0,0);
v->scr.size=Pt(0,lp->len);
v->kind="list";
}
Panel *pllist(Panel *parent, int flags, char *(*gen)(Panel *, int), int nlist, void (*hit)(Panel *, int, int)){
Panel *v;
v=pl_newpanel(parent, sizeof(List));
plinitlist(v, flags, gen, nlist, hit);
return v;
}