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