emenuhit.c (7160B)
1 #include <u.h> 2 #include <libc.h> 3 #include <draw.h> 4 #include <event.h> 5 6 enum 7 { 8 Margin = 4, /* outside to text */ 9 Border = 2, /* outside to selection boxes */ 10 Blackborder = 2, /* width of outlining border */ 11 Vspacing = 2, /* extra spacing between lines of text */ 12 Maxunscroll = 25, /* maximum #entries before scrolling turns on */ 13 Nscroll = 20, /* number entries in scrolling part */ 14 Scrollwid = 14, /* width of scroll bar */ 15 Gap = 4 /* between text and scroll bar */ 16 }; 17 18 static Image *menutxt; 19 static Image *back; 20 static Image *high; 21 static Image *bord; 22 static Image *text; 23 static Image *htext; 24 25 static 26 void 27 menucolors(void) 28 { 29 /* Main tone is greenish, with negative selection */ 30 back = allocimagemix(display, DPalegreen, DWhite); 31 high = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DDarkgreen); /* dark green */ 32 bord = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedgreen); /* not as dark green */ 33 if(back==nil || high==nil || bord==nil) 34 goto Error; 35 text = display->black; 36 htext = back; 37 return; 38 39 Error: 40 freeimage(back); 41 freeimage(high); 42 freeimage(bord); 43 back = display->white; 44 high = display->black; 45 bord = display->black; 46 text = display->black; 47 htext = display->white; 48 } 49 50 /* 51 * r is a rectangle holding the text elements. 52 * return the rectangle, including its black edge, holding element i. 53 */ 54 static Rectangle 55 menurect(Rectangle r, int i) 56 { 57 if(i < 0) 58 return Rect(0, 0, 0, 0); 59 r.min.y += (font->height+Vspacing)*i; 60 r.max.y = r.min.y+font->height+Vspacing; 61 return insetrect(r, Border-Margin); 62 } 63 64 /* 65 * r is a rectangle holding the text elements. 66 * return the element number containing p. 67 */ 68 static int 69 menusel(Rectangle r, Point p) 70 { 71 r = insetrect(r, Margin); 72 if(!ptinrect(p, r)) 73 return -1; 74 return (p.y-r.min.y)/(font->height+Vspacing); 75 } 76 77 static 78 void 79 paintitem(Menu *menu, Rectangle textr, int off, int i, int highlight, Image *save, Image *restore) 80 { 81 char *item; 82 Rectangle r; 83 Point pt; 84 85 if(i < 0) 86 return; 87 r = menurect(textr, i); 88 if(restore){ 89 draw(screen, r, restore, nil, restore->r.min); 90 return; 91 } 92 if(save) 93 draw(save, save->r, screen, nil, r.min); 94 item = menu->item? menu->item[i+off] : (*menu->gen)(i+off); 95 pt.x = (textr.min.x+textr.max.x-stringwidth(font, item))/2; 96 pt.y = textr.min.y+i*(font->height+Vspacing); 97 draw(screen, r, highlight? high : back, nil, pt); 98 string(screen, pt, highlight? htext : text, pt, font, item); 99 } 100 101 /* 102 * menur is a rectangle holding all the highlightable text elements. 103 * track mouse while inside the box, return what's selected when button 104 * is raised, -1 as soon as it leaves box. 105 * invariant: nothing is highlighted on entry or exit. 106 */ 107 static int 108 menuscan(Menu *menu, int but, Mouse *m, Rectangle textr, int off, int lasti, Image *save) 109 { 110 int i; 111 112 paintitem(menu, textr, off, lasti, 1, save, nil); 113 flushimage(display, 1); /* in case display->locking is set */ 114 *m = emouse(); 115 while(m->buttons & (1<<(but-1))){ 116 flushimage(display, 1); /* in case display->locking is set */ 117 *m = emouse(); 118 i = menusel(textr, m->xy); 119 if(i != -1 && i == lasti) 120 continue; 121 paintitem(menu, textr, off, lasti, 0, nil, save); 122 if(i == -1) 123 return i; 124 lasti = i; 125 paintitem(menu, textr, off, lasti, 1, save, nil); 126 } 127 return lasti; 128 } 129 130 static void 131 menupaint(Menu *menu, Rectangle textr, int off, int nitemdrawn) 132 { 133 int i; 134 135 draw(screen, insetrect(textr, Border-Margin), back, nil, ZP); 136 for(i = 0; i<nitemdrawn; i++) 137 paintitem(menu, textr, off, i, 0, nil, nil); 138 } 139 140 static void 141 menuscrollpaint(Rectangle scrollr, int off, int nitem, int nitemdrawn) 142 { 143 Rectangle r; 144 145 draw(screen, scrollr, back, nil, ZP); 146 r.min.x = scrollr.min.x; 147 r.max.x = scrollr.max.x; 148 r.min.y = scrollr.min.y + (Dy(scrollr)*off)/nitem; 149 r.max.y = scrollr.min.y + (Dy(scrollr)*(off+nitemdrawn))/nitem; 150 if(r.max.y < r.min.y+2) 151 r.max.y = r.min.y+2; 152 border(screen, r, 1, bord, ZP); 153 if(menutxt == 0) 154 menutxt = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DDarkgreen); 155 if(menutxt) 156 draw(screen, insetrect(r, 1), menutxt, nil, ZP); 157 } 158 159 int 160 emenuhit(int but, Mouse *m, Menu *menu) 161 { 162 int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem; 163 int scrolling; 164 Rectangle r, menur, sc, textr, scrollr; 165 Image *b, *save; 166 Point pt; 167 char *item; 168 169 if(back == nil) 170 menucolors(); 171 sc = screen->clipr; 172 replclipr(screen, 0, screen->r); 173 maxwid = 0; 174 for(nitem = 0; 175 item = menu->item? menu->item[nitem] : (*menu->gen)(nitem); 176 nitem++){ 177 i = stringwidth(font, item); 178 if(i > maxwid) 179 maxwid = i; 180 } 181 if(menu->lasthit<0 || menu->lasthit>=nitem) 182 menu->lasthit = 0; 183 screenitem = (Dy(screen->r)-10)/(font->height+Vspacing); 184 if(nitem>Maxunscroll || nitem>screenitem){ 185 scrolling = 1; 186 nitemdrawn = Nscroll; 187 if(nitemdrawn > screenitem) 188 nitemdrawn = screenitem; 189 wid = maxwid + Gap + Scrollwid; 190 off = menu->lasthit - nitemdrawn/2; 191 if(off < 0) 192 off = 0; 193 if(off > nitem-nitemdrawn) 194 off = nitem-nitemdrawn; 195 lasti = menu->lasthit-off; 196 }else{ 197 scrolling = 0; 198 nitemdrawn = nitem; 199 wid = maxwid; 200 off = 0; 201 lasti = menu->lasthit; 202 } 203 r = insetrect(Rect(0, 0, wid, nitemdrawn*(font->height+Vspacing)), -Margin); 204 r = rectsubpt(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/2)); 205 r = rectaddpt(r, m->xy); 206 pt = ZP; 207 if(r.max.x>screen->r.max.x) 208 pt.x = screen->r.max.x-r.max.x; 209 if(r.max.y>screen->r.max.y) 210 pt.y = screen->r.max.y-r.max.y; 211 if(r.min.x<screen->r.min.x) 212 pt.x = screen->r.min.x-r.min.x; 213 if(r.min.y<screen->r.min.y) 214 pt.y = screen->r.min.y-r.min.y; 215 menur = rectaddpt(r, pt); 216 textr.max.x = menur.max.x-Margin; 217 textr.min.x = textr.max.x-maxwid; 218 textr.min.y = menur.min.y+Margin; 219 textr.max.y = textr.min.y + nitemdrawn*(font->height+Vspacing); 220 if(scrolling){ 221 scrollr = insetrect(menur, Border); 222 scrollr.max.x = scrollr.min.x+Scrollwid; 223 }else 224 scrollr = Rect(0, 0, 0, 0); 225 226 b = allocimage(display, menur, screen->chan, 0, 0); 227 if(b == 0) 228 b = screen; 229 draw(b, menur, screen, nil, menur.min); 230 draw(screen, menur, back, nil, ZP); 231 border(screen, menur, Blackborder, bord, ZP); 232 save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1); 233 r = menurect(textr, lasti); 234 emoveto(divpt(addpt(r.min, r.max), 2)); 235 menupaint(menu, textr, off, nitemdrawn); 236 if(scrolling) 237 menuscrollpaint(scrollr, off, nitem, nitemdrawn); 238 while(m->buttons & (1<<(but-1))){ 239 lasti = menuscan(menu, but, m, textr, off, lasti, save); 240 if(lasti >= 0) 241 break; 242 while(!ptinrect(m->xy, textr) && (m->buttons & (1<<(but-1)))){ 243 if(scrolling && ptinrect(m->xy, scrollr)){ 244 noff = ((m->xy.y-scrollr.min.y)*nitem)/Dy(scrollr); 245 noff -= nitemdrawn/2; 246 if(noff < 0) 247 noff = 0; 248 if(noff > nitem-nitemdrawn) 249 noff = nitem-nitemdrawn; 250 if(noff != off){ 251 off = noff; 252 menupaint(menu, textr, off, nitemdrawn); 253 menuscrollpaint(scrollr, off, nitem, nitemdrawn); 254 } 255 } 256 flushimage(display, 1); /* in case display->locking is set */ 257 *m = emouse(); 258 } 259 } 260 draw(screen, menur, b, nil, menur.min); 261 if(b != screen) 262 freeimage(b); 263 freeimage(save); 264 replclipr(screen, 0, sc); 265 flushimage(display, 1); 266 if(lasti >= 0){ 267 menu->lasthit = lasti+off; 268 return menu->lasthit; 269 } 270 return -1; 271 }