From 76193d7cb0457807b2f0b95f909ab5de19480cd7 Mon Sep 17 00:00:00 2001 From: rsc Date: Tue, 30 Sep 2003 17:47:42 +0000 Subject: Initial revision --- src/libdraw/menuhit.c | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 src/libdraw/menuhit.c (limited to 'src/libdraw/menuhit.c') diff --git a/src/libdraw/menuhit.c b/src/libdraw/menuhit.c new file mode 100644 index 00000000..88304338 --- /dev/null +++ b/src/libdraw/menuhit.c @@ -0,0 +1,277 @@ +#include +#include +#include +#include +#include + +enum +{ + Margin = 4, /* outside to text */ + Border = 2, /* outside to selection boxes */ + Blackborder = 2, /* width of outlining border */ + Vspacing = 2, /* extra spacing between lines of text */ + Maxunscroll = 25, /* maximum #entries before scrolling turns on */ + Nscroll = 20, /* number entries in scrolling part */ + Scrollwid = 14, /* width of scroll bar */ + Gap = 4, /* between text and scroll bar */ +}; + +static Image *menutxt; +static Image *back; +static Image *high; +static Image *bord; +static Image *text; +static Image *htext; + +static +void +menucolors(void) +{ + /* Main tone is greenish, with negative selection */ + back = allocimagemix(display, DPalegreen, DWhite); + high = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkgreen); /* dark green */ + bord = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedgreen); /* not as dark green */ + if(back==nil || high==nil || bord==nil) + goto Error; + text = display->black; + htext = back; + return; + + Error: + freeimage(back); + freeimage(high); + freeimage(bord); + back = display->white; + high = display->black; + bord = display->black; + text = display->black; + htext = display->white; +} + +/* + * r is a rectangle holding the text elements. + * return the rectangle, including its black edge, holding element i. + */ +static Rectangle +menurect(Rectangle r, int i) +{ + if(i < 0) + return Rect(0, 0, 0, 0); + r.min.y += (font->height+Vspacing)*i; + r.max.y = r.min.y+font->height+Vspacing; + return insetrect(r, Border-Margin); +} + +/* + * r is a rectangle holding the text elements. + * return the element number containing p. + */ +static int +menusel(Rectangle r, Point p) +{ + r = insetrect(r, Margin); + if(!ptinrect(p, r)) + return -1; + return (p.y-r.min.y)/(font->height+Vspacing); +} + +static +void +paintitem(Image *m, Menu *menu, Rectangle textr, int off, int i, int highlight, Image *save, Image *restore) +{ + char *item; + Rectangle r; + Point pt; + + if(i < 0) + return; + r = menurect(textr, i); + if(restore){ + draw(m, r, restore, nil, restore->r.min); + return; + } + if(save) + draw(save, save->r, m, nil, r.min); + item = menu->item? menu->item[i+off] : (*menu->gen)(i+off); + pt.x = (textr.min.x+textr.max.x-stringwidth(font, item))/2; + pt.y = textr.min.y+i*(font->height+Vspacing); + draw(m, r, highlight? high : back, nil, pt); + string(m, pt, highlight? htext : text, pt, font, item); +} + +/* + * menur is a rectangle holding all the highlightable text elements. + * track mouse while inside the box, return what's selected when button + * is raised, -1 as soon as it leaves box. + * invariant: nothing is highlighted on entry or exit. + */ +static int +menuscan(Image *m, Menu *menu, int but, Mousectl *mc, Rectangle textr, int off, int lasti, Image *save) +{ + int i; + + paintitem(m, menu, textr, off, lasti, 1, save, nil); + for(readmouse(mc); mc->m.buttons & (1<<(but-1)); readmouse(mc)){ + i = menusel(textr, mc->m.xy); + if(i != -1 && i == lasti) + continue; + paintitem(m, menu, textr, off, lasti, 0, nil, save); + if(i == -1) + return i; + lasti = i; + paintitem(m, menu, textr, off, lasti, 1, save, nil); + } + return lasti; +} + +static void +menupaint(Image *m, Menu *menu, Rectangle textr, int off, int nitemdrawn) +{ + int i; + + draw(m, insetrect(textr, Border-Margin), back, nil, ZP); + for(i = 0; ichan, 1, DDarkgreen); /* border color; BUG? */ + if(menutxt) + draw(m, insetrect(r, 1), menutxt, nil, ZP); +} + +int +menuhit(int but, Mousectl *mc, Menu *menu, Screen *scr) +{ + int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem; + int scrolling; + Rectangle r, menur, sc, textr, scrollr; + Image *b, *save, *backup; + Point pt; + char *item; + + if(back == nil) + menucolors(); + sc = screen->clipr; + replclipr(screen, 0, screen->r); + maxwid = 0; + for(nitem = 0; + item = menu->item? menu->item[nitem] : (*menu->gen)(nitem); + nitem++){ + i = stringwidth(font, item); + if(i > maxwid) + maxwid = i; + } + if(menu->lasthit<0 || menu->lasthit>=nitem) + menu->lasthit = 0; + screenitem = (Dy(screen->r)-10)/(font->height+Vspacing); + if(nitem>Maxunscroll || nitem>screenitem){ + scrolling = 1; + nitemdrawn = Nscroll; + if(nitemdrawn > screenitem) + nitemdrawn = screenitem; + wid = maxwid + Gap + Scrollwid; + off = menu->lasthit - nitemdrawn/2; + if(off < 0) + off = 0; + if(off > nitem-nitemdrawn) + off = nitem-nitemdrawn; + lasti = menu->lasthit-off; + }else{ + scrolling = 0; + nitemdrawn = nitem; + wid = maxwid; + off = 0; + lasti = menu->lasthit; + } + r = insetrect(Rect(0, 0, wid, nitemdrawn*(font->height+Vspacing)), -Margin); + r = rectsubpt(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/2)); + r = rectaddpt(r, mc->m.xy); + pt = ZP; + if(r.max.x>screen->r.max.x) + pt.x = screen->r.max.x-r.max.x; + if(r.max.y>screen->r.max.y) + pt.y = screen->r.max.y-r.max.y; + if(r.min.xr.min.x) + pt.x = screen->r.min.x-r.min.x; + if(r.min.yr.min.y) + pt.y = screen->r.min.y-r.min.y; + menur = rectaddpt(r, pt); + textr.max.x = menur.max.x-Margin; + textr.min.x = textr.max.x-maxwid; + textr.min.y = menur.min.y+Margin; + textr.max.y = textr.min.y + nitemdrawn*(font->height+Vspacing); + if(scrolling){ + scrollr = insetrect(menur, Border); + scrollr.max.x = scrollr.min.x+Scrollwid; + }else + scrollr = Rect(0, 0, 0, 0); + + if(scr){ + b = allocwindow(scr, menur, Refbackup, DWhite); + if(b == nil) + b = screen; + backup = nil; + }else{ + b = screen; + backup = allocimage(display, menur, screen->chan, 0, -1); + if(backup) + draw(backup, menur, screen, nil, menur.min); + } + draw(b, menur, back, nil, ZP); + border(b, menur, Blackborder, bord, ZP); + save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1); + r = menurect(textr, lasti); + moveto(mc, divpt(addpt(r.min, r.max), 2)); + menupaint(b, menu, textr, off, nitemdrawn); + if(scrolling) + menuscrollpaint(b, scrollr, off, nitem, nitemdrawn); + while(mc->m.buttons & (1<<(but-1))){ + lasti = menuscan(b, menu, but, mc, textr, off, lasti, save); + if(lasti >= 0) + break; + while(!ptinrect(mc->m.xy, textr) && (mc->m.buttons & (1<<(but-1)))){ + if(scrolling && ptinrect(mc->m.xy, scrollr)){ + noff = ((mc->m.xy.y-scrollr.min.y)*nitem)/Dy(scrollr); + noff -= nitemdrawn/2; + if(noff < 0) + noff = 0; + if(noff > nitem-nitemdrawn) + noff = nitem-nitemdrawn; + if(noff != off){ + off = noff; + menupaint(b, menu, textr, off, nitemdrawn); + menuscrollpaint(b, scrollr, off, nitem, nitemdrawn); + } + } + readmouse(mc); + } + } + if(b != screen) + freeimage(b); + if(backup){ + draw(screen, menur, backup, nil, menur.min); + freeimage(backup); + } + freeimage(save); + replclipr(screen, 0, sc); + flushimage(display, 1); + if(lasti >= 0){ + menu->lasthit = lasti+off; + return menu->lasthit; + } + return -1; +} -- cgit v1.2.3