#include #include #include #include #include #include "submenu.h" int gensubmenuhit(int but, Mousectl *mc, Submenu *menu, Point pos, int sub); 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 */ Openspacing = 5, Closespacing = 8, // 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 */ }; #define Itemheight (font->height+Vspacing) 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; } static Rectangle menurect(Rectangle r, int i) { if(i < 0) return Rect(0, 0, 0, 0); r.min.y += Itemheight*i; r.max.y = r.min.y+Itemheight; return insetrect(r, Border-Margin); } static void paintitem(Image *m, Submenu *menu, Rectangle textr, int i, int highlight) { char *item; Rectangle r; Point pt; if(i < 0) return; r = menurect(textr, i); item = menu->item[i].str; pt.x = (textr.min.x+textr.max.x-stringwidth(font, item))/2; pt.y = textr.min.y+i*Itemheight; draw(m, r, highlight? high : back, nil, pt); string(m, pt, highlight? htext : text, pt, font, item); if(menu->item[i].submenu){ pt.x = textr.max.x - stringwidth(font, ">"); string(m, pt, highlight? htext : text, pt, font, ">"); } } static int menusel(Rectangle r, Point p) { if(!ptinrect(p, r)) return -1; return (p.y-r.min.y)/Itemheight; } /* while mouse is over menu */ static int menuscan(Image *m, Submenu *menu, int but, Mousectl *mc, Rectangle textr) { int i, lasti, cmd; Submenu *sub; lasti = menusel(textr, mc->xy); paintitem(m, menu, textr, lasti, 1); readmouse(mc); while(mc->buttons & (1<<(but-1))){ i = menusel(textr, mc->xy); if(i != lasti) paintitem(m, menu, textr, lasti, 0); if(i == -1) return -1; lasti = i; paintitem(m, menu, textr, lasti, 1); sub = menu->item[lasti].submenu; if(sub && mc->xy.x > textr.max.x-Openspacing){ Point pt = Pt(textr.max.x + Margin, textr.min.y + Itemheight*lasti); cmd = gensubmenuhit(but, mc, sub, pt, 1); // print("subhit: %d\n", cmd); if(cmd >= 0){ menu->lasthit = menu->item[i].cmd; return cmd; } /* redraw with current mouse state */ continue; } readmouse(mc); } if(lasti == -1) return -1; menu->lasthit = menu->item[lasti].cmd; return menu->lasthit; } static void menupaint(Image *m, Submenu *menu, Rectangle textr, int nitem) { int i; draw(m, insetrect(textr, Border-Margin), back, nil, ZP); for(i = 0; i < nitem; i++) paintitem(m, menu, textr, i, 0); } static Point clampscreen(Rectangle r) { Point pt; 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; return pt; } int gensubmenuhit(int but, Mousectl *mc, Submenu *menu, Point pos, int issub) { int i, lasti, cmd; Menuitem *item; int hassub; int width, nitem; Rectangle r, textr, menur, closer; Point delta; if(back == nil) menucolors(); hassub = 0; width = 0; lasti = 0; for(nitem = 0; item = &menu->item[nitem], item->str; nitem++){ i = stringwidth(font, item->str); if(i > width) width = i; if(menu->lasthit == item->cmd) lasti = nitem; if(item->submenu) hassub = 1; } if(hassub) width += stringwidth(font, " >"); r = insetrect(Rect(0, 0, width, nitem*Itemheight), -Margin); if(issub){ r = rectsubpt(r, Pt(-Margin, lasti*Itemheight)); menur = rectaddpt(r, pos); }else{ r = rectsubpt(r, Pt(width/2, lasti*Itemheight+Itemheight/2)); menur = rectaddpt(r, mc->xy); } delta = clampscreen(menur); menur = rectaddpt(menur, delta); if(issub){ // TODO: not sure about vertical spacing here closer = Rect(0, menur.min.y-2, menur.min.x-Closespacing, menur.max.y+2); }else{ closer = Rect(0,0,0,0); if(delta.x || delta.y) moveto(mc, addpt(mc->xy, delta)); } textr = insetrect(menur, Margin); Image *b, *backup; { b = screen; backup = allocimage(display, menur, screen->chan, 0, -1); draw(backup, menur, screen, nil, menur.min); } draw(b, menur, back, nil, ZP); border(b, menur, Blackborder, bord, ZP); menupaint(b, menu, textr, nitem); cmd = -1; while(mc->buttons & (1<<(but-1))){ cmd = menuscan(b, menu, but, mc, textr); //print("scan %d\n", cmd); if(cmd >= 0) break; if(ptinrect(mc->xy, closer)) break; } //print("menucmd: %d\n", cmd); if(backup){ draw(screen, menur, backup, nil, menur.min); freeimage(backup); } flushimage(display, 1); return cmd; } int submenuhit(int but, Mousectl *mc, Submenu *menu) { return gensubmenuhit(but, mc, menu, ZP, 0); }