|
1 /* $Id$ */ |
|
2 |
|
3 #include "stdafx.h" |
|
4 #include "openttd.h" |
|
5 #include "functions.h" |
|
6 #include "player.h" |
|
7 #include "table/sprites.h" |
|
8 #include "table/strings.h" |
|
9 #include "window.h" |
|
10 #include "gfx.h" |
|
11 #include "viewport.h" |
|
12 |
|
13 static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom) |
|
14 { |
|
15 Point pt; |
|
16 int height, count, pos, cap; |
|
17 |
|
18 top += 10; |
|
19 bottom -= 9; |
|
20 |
|
21 height = (bottom - top); |
|
22 |
|
23 pos = sb->pos; |
|
24 count = sb->count; |
|
25 cap = sb->cap; |
|
26 |
|
27 if (count != 0) top += height * pos / count; |
|
28 |
|
29 if (cap > count) cap = count; |
|
30 if (count != 0) bottom -= (count - pos - cap) * height / count; |
|
31 |
|
32 pt.x = top; |
|
33 pt.y = bottom - 1; |
|
34 return pt; |
|
35 } |
|
36 |
|
37 /***************************************************** |
|
38 * Special handling for the scrollbar widget type. |
|
39 * Handles the special scrolling buttons and other |
|
40 * scrolling. |
|
41 * Parameters: |
|
42 * w - Window. |
|
43 * wi - Pointer to the scrollbar widget. |
|
44 * x - The X coordinate of the mouse click. |
|
45 * y - The Y coordinate of the mouse click. |
|
46 */ |
|
47 |
|
48 void ScrollbarClickHandler(Window *w, const Widget *wi, int x, int y) |
|
49 { |
|
50 int mi, ma, pos; |
|
51 Scrollbar *sb; |
|
52 |
|
53 switch (wi->type) { |
|
54 case WWT_SCROLLBAR: { |
|
55 // vertical scroller |
|
56 w->flags4 &= ~WF_HSCROLL; |
|
57 w->flags4 &= ~WF_SCROLL2; |
|
58 mi = wi->top; |
|
59 ma = wi->bottom; |
|
60 pos = y; |
|
61 sb = &w->vscroll; |
|
62 break; |
|
63 } |
|
64 case WWT_SCROLL2BAR: { |
|
65 // 2nd vertical scroller |
|
66 w->flags4 &= ~WF_HSCROLL; |
|
67 w->flags4 |= WF_SCROLL2; |
|
68 mi = wi->top; |
|
69 ma = wi->bottom; |
|
70 pos = y; |
|
71 sb = &w->vscroll2; |
|
72 break; |
|
73 } |
|
74 case WWT_HSCROLLBAR: { |
|
75 // horizontal scroller |
|
76 w->flags4 &= ~WF_SCROLL2; |
|
77 w->flags4 |= WF_HSCROLL; |
|
78 mi = wi->left; |
|
79 ma = wi->right; |
|
80 pos = x; |
|
81 sb = &w->hscroll; |
|
82 break; |
|
83 } |
|
84 default: return; //this should never happen |
|
85 } |
|
86 if (pos <= mi+9) { |
|
87 // Pressing the upper button? |
|
88 w->flags4 |= WF_SCROLL_UP; |
|
89 if (_scroller_click_timeout == 0) { |
|
90 _scroller_click_timeout = 6; |
|
91 if (sb->pos != 0) sb->pos--; |
|
92 } |
|
93 _left_button_clicked = false; |
|
94 } else if (pos >= ma-10) { |
|
95 // Pressing the lower button? |
|
96 w->flags4 |= WF_SCROLL_DOWN; |
|
97 |
|
98 if (_scroller_click_timeout == 0) { |
|
99 _scroller_click_timeout = 6; |
|
100 if ((byte)(sb->pos + sb->cap) < sb->count) |
|
101 sb->pos++; |
|
102 } |
|
103 _left_button_clicked = false; |
|
104 } else { |
|
105 // |
|
106 Point pt = HandleScrollbarHittest(sb, mi, ma); |
|
107 |
|
108 if (pos < pt.x) { |
|
109 sb->pos = max(sb->pos - sb->cap, 0); |
|
110 } else if (pos > pt.y) { |
|
111 sb->pos = min( |
|
112 sb->pos + sb->cap, |
|
113 max(sb->count - sb->cap, 0) |
|
114 ); |
|
115 } else { |
|
116 _scrollbar_start_pos = pt.x - mi - 9; |
|
117 _scrollbar_size = ma - mi - 23; |
|
118 w->flags4 |= WF_SCROLL_MIDDLE; |
|
119 _scrolling_scrollbar = true; |
|
120 _cursorpos_drag_start = _cursor.pos; |
|
121 } |
|
122 } |
|
123 |
|
124 SetWindowDirty(w); |
|
125 } |
|
126 |
|
127 /** Returns the index for the widget located at the given position |
|
128 * relative to the window. It includes all widget-corner pixels as well. |
|
129 * @param *w Window to look inside |
|
130 * @param x,y Window client coordinates |
|
131 * @return A widget index, or -1 if no widget was found. |
|
132 */ |
|
133 int GetWidgetFromPos(const Window *w, int x, int y) |
|
134 { |
|
135 uint index; |
|
136 int found_index = -1; |
|
137 |
|
138 // Go through the widgets and check if we find the widget that the coordinate is |
|
139 // inside. |
|
140 for (index = 0; index < w->widget_count; index++) { |
|
141 const Widget *wi = &w->widget[index]; |
|
142 if (wi->type == WWT_EMPTY || wi->type == WWT_FRAME) continue; |
|
143 |
|
144 if (x >= wi->left && x <= wi->right && y >= wi->top && y <= wi->bottom && |
|
145 !IsWindowWidgetHidden(w, index)) { |
|
146 found_index = index; |
|
147 } |
|
148 } |
|
149 |
|
150 return found_index; |
|
151 } |
|
152 |
|
153 |
|
154 void DrawFrameRect(int left, int top, int right, int bottom, int ctab, FrameFlags flags) |
|
155 { |
|
156 uint dark = _colour_gradient[ctab][3]; |
|
157 uint medium_dark = _colour_gradient[ctab][5]; |
|
158 uint medium_light = _colour_gradient[ctab][6]; |
|
159 uint light = _colour_gradient[ctab][7]; |
|
160 |
|
161 if (flags & FR_TRANSPARENT) { |
|
162 GfxFillRect(left, top, right, bottom, 0x322 | USE_COLORTABLE); |
|
163 } else { |
|
164 uint interior; |
|
165 |
|
166 if (flags & FR_LOWERED) { |
|
167 GfxFillRect(left, top, left, bottom, dark); |
|
168 GfxFillRect(left + 1, top, right, top, dark); |
|
169 GfxFillRect(right, top + 1, right, bottom - 1, light); |
|
170 GfxFillRect(left + 1, bottom, right, bottom, light); |
|
171 interior = (flags & FR_DARKENED ? medium_dark : medium_light); |
|
172 } else { |
|
173 GfxFillRect(left, top, left, bottom - 1, light); |
|
174 GfxFillRect(left + 1, top, right - 1, top, light); |
|
175 GfxFillRect(right, top, right, bottom - 1, dark); |
|
176 GfxFillRect(left, bottom, right, bottom, dark); |
|
177 interior = medium_dark; |
|
178 } |
|
179 if (!(flags & FR_BORDERONLY)) { |
|
180 GfxFillRect(left + 1, top + 1, right - 1, bottom - 1, interior); |
|
181 } |
|
182 } |
|
183 } |
|
184 |
|
185 |
|
186 void DrawWindowWidgets(const Window *w) |
|
187 { |
|
188 const DrawPixelInfo* dpi = _cur_dpi; |
|
189 Rect r; |
|
190 uint i; |
|
191 |
|
192 for (i = 0; i < w->widget_count; i++) { |
|
193 const Widget *wi = &w->widget[i]; |
|
194 bool clicked = IsWindowWidgetLowered(w, i); |
|
195 |
|
196 if (dpi->left > (r.right=/*w->left + */wi->right) || |
|
197 dpi->left + dpi->width <= (r.left=wi->left/* + w->left*/) || |
|
198 dpi->top > (r.bottom=/*w->top +*/ wi->bottom) || |
|
199 dpi->top + dpi->height <= (r.top = /*w->top +*/ wi->top) || |
|
200 IsWindowWidgetHidden(w, i)) { |
|
201 continue; |
|
202 } |
|
203 |
|
204 switch (wi->type & WWT_MASK) { |
|
205 case WWT_IMGBTN: |
|
206 case WWT_IMGBTN_2: { |
|
207 int img = wi->data; |
|
208 assert(img != 0); |
|
209 DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); |
|
210 |
|
211 /* show different image when clicked for WWT_IMGBTN_2 */ |
|
212 if ((wi->type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++; |
|
213 DrawSprite(img, r.left + 1 + clicked, r.top + 1 + clicked); |
|
214 goto draw_default; |
|
215 } |
|
216 |
|
217 case WWT_PANEL: { |
|
218 assert(wi->data == 0); |
|
219 DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); |
|
220 goto draw_default; |
|
221 } |
|
222 |
|
223 case WWT_TEXTBTN: |
|
224 case WWT_TEXTBTN_2: { |
|
225 DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); |
|
226 } |
|
227 /* fall through */ |
|
228 |
|
229 case WWT_LABEL: { |
|
230 StringID str = wi->data; |
|
231 |
|
232 if ((wi->type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++; |
|
233 |
|
234 DrawStringCentered(((r.left + r.right + 1) >> 1) + clicked, ((r.top + r.bottom + 1) >> 1) - 5 + clicked, str, 0); |
|
235 goto draw_default; |
|
236 } |
|
237 |
|
238 case WWT_INSET: { |
|
239 StringID str = wi->data; |
|
240 DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_LOWERED | FR_DARKENED); |
|
241 |
|
242 if (str != STR_NULL) DrawStringTruncated(r.left + 2, r.top + 1, str, 0, r.right - r.left - 10); |
|
243 goto draw_default; |
|
244 } |
|
245 |
|
246 case WWT_MATRIX: { |
|
247 int c, d, ctr; |
|
248 int x, amt1, amt2; |
|
249 int color; |
|
250 |
|
251 DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); |
|
252 |
|
253 c = GB(wi->data, 0, 8); |
|
254 amt1 = (wi->right - wi->left + 1) / c; |
|
255 |
|
256 d = GB(wi->data, 8, 8); |
|
257 amt2 = (wi->bottom - wi->top + 1) / d; |
|
258 |
|
259 color = _colour_gradient[wi->color & 0xF][6]; |
|
260 |
|
261 x = r.left; |
|
262 for (ctr = c; ctr > 1; ctr--) { |
|
263 x += amt1; |
|
264 GfxFillRect(x, r.top + 1, x, r.bottom - 1, color); |
|
265 } |
|
266 |
|
267 x = r.top; |
|
268 for (ctr = d; ctr > 1; ctr--) { |
|
269 x += amt2; |
|
270 GfxFillRect(r.left + 1, x, r.right - 1, x, color); |
|
271 } |
|
272 |
|
273 color = _colour_gradient[wi->color&0xF][4]; |
|
274 |
|
275 x = r.left - 1; |
|
276 for (ctr = c; ctr > 1; ctr--) { |
|
277 x += amt1; |
|
278 GfxFillRect(x, r.top + 1, x, r.bottom - 1, color); |
|
279 } |
|
280 |
|
281 x = r.top - 1; |
|
282 for (ctr = d; ctr > 1; ctr--) { |
|
283 x += amt2; |
|
284 GfxFillRect(r.left+1, x, r.right-1, x, color); |
|
285 } |
|
286 |
|
287 goto draw_default; |
|
288 } |
|
289 |
|
290 // vertical scrollbar |
|
291 case WWT_SCROLLBAR: { |
|
292 Point pt; |
|
293 int c1,c2; |
|
294 |
|
295 assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere! |
|
296 |
|
297 // draw up/down buttons |
|
298 clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_UP); |
|
299 DrawFrameRect(r.left, r.top, r.right, r.top + 9, wi->color, (clicked) ? FR_LOWERED : 0); |
|
300 DoDrawString(UPARROW, r.left + 2 + clicked, r.top + clicked, 0x10); |
|
301 |
|
302 clicked = (((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_DOWN)); |
|
303 DrawFrameRect(r.left, r.bottom - 9, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); |
|
304 DoDrawString(DOWNARROW, r.left + 2 + clicked, r.bottom - 9 + clicked, 0x10); |
|
305 |
|
306 c1 = _colour_gradient[wi->color&0xF][3]; |
|
307 c2 = _colour_gradient[wi->color&0xF][7]; |
|
308 |
|
309 // draw "shaded" background |
|
310 GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c2); |
|
311 GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c1 | PALETTE_MODIFIER_GREYOUT); |
|
312 |
|
313 // draw shaded lines |
|
314 GfxFillRect(r.left+2, r.top+10, r.left+2, r.bottom-10, c1); |
|
315 GfxFillRect(r.left+3, r.top+10, r.left+3, r.bottom-10, c2); |
|
316 GfxFillRect(r.left+7, r.top+10, r.left+7, r.bottom-10, c1); |
|
317 GfxFillRect(r.left+8, r.top+10, r.left+8, r.bottom-10, c2); |
|
318 |
|
319 pt = HandleScrollbarHittest(&w->vscroll, r.top, r.bottom); |
|
320 DrawFrameRect(r.left, pt.x, r.right, pt.y, wi->color, (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_MIDDLE ? FR_LOWERED : 0); |
|
321 break; |
|
322 } |
|
323 case WWT_SCROLL2BAR: { |
|
324 Point pt; |
|
325 int c1,c2; |
|
326 |
|
327 assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere! |
|
328 |
|
329 // draw up/down buttons |
|
330 clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_UP | WF_SCROLL2)); |
|
331 DrawFrameRect(r.left, r.top, r.right, r.top + 9, wi->color, (clicked) ? FR_LOWERED : 0); |
|
332 DoDrawString(UPARROW, r.left + 2 + clicked, r.top + clicked, 0x10); |
|
333 |
|
334 clicked = ((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_DOWN | WF_SCROLL2)); |
|
335 DrawFrameRect(r.left, r.bottom - 9, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); |
|
336 DoDrawString(DOWNARROW, r.left + 2 + clicked, r.bottom - 9 + clicked, 0x10); |
|
337 |
|
338 c1 = _colour_gradient[wi->color&0xF][3]; |
|
339 c2 = _colour_gradient[wi->color&0xF][7]; |
|
340 |
|
341 // draw "shaded" background |
|
342 GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c2); |
|
343 GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c1 | PALETTE_MODIFIER_GREYOUT); |
|
344 |
|
345 // draw shaded lines |
|
346 GfxFillRect(r.left+2, r.top+10, r.left+2, r.bottom-10, c1); |
|
347 GfxFillRect(r.left+3, r.top+10, r.left+3, r.bottom-10, c2); |
|
348 GfxFillRect(r.left+7, r.top+10, r.left+7, r.bottom-10, c1); |
|
349 GfxFillRect(r.left+8, r.top+10, r.left+8, r.bottom-10, c2); |
|
350 |
|
351 pt = HandleScrollbarHittest(&w->vscroll2, r.top, r.bottom); |
|
352 DrawFrameRect(r.left, pt.x, r.right, pt.y, wi->color, (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_MIDDLE | WF_SCROLL2) ? FR_LOWERED : 0); |
|
353 break; |
|
354 } |
|
355 |
|
356 // horizontal scrollbar |
|
357 case WWT_HSCROLLBAR: { |
|
358 Point pt; |
|
359 int c1,c2; |
|
360 |
|
361 assert(r.bottom - r.top == 11); // XXX - to ensure the same sizes are used everywhere! |
|
362 |
|
363 clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL)) == (WF_SCROLL_UP | WF_HSCROLL)); |
|
364 DrawFrameRect(r.left, r.top, r.left + 9, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); |
|
365 DrawSprite(SPR_ARROW_LEFT, r.left + 1 + clicked, r.top + 1 + clicked); |
|
366 |
|
367 clicked = ((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL)) == (WF_SCROLL_DOWN | WF_HSCROLL)); |
|
368 DrawFrameRect(r.right-9, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); |
|
369 DrawSprite(SPR_ARROW_RIGHT, r.right - 8 + clicked, r.top + 1 + clicked); |
|
370 |
|
371 c1 = _colour_gradient[wi->color&0xF][3]; |
|
372 c2 = _colour_gradient[wi->color&0xF][7]; |
|
373 |
|
374 // draw "shaded" background |
|
375 GfxFillRect(r.left+10, r.top, r.right-10, r.bottom, c2); |
|
376 GfxFillRect(r.left+10, r.top, r.right-10, r.bottom, c1 | PALETTE_MODIFIER_GREYOUT); |
|
377 |
|
378 // draw shaded lines |
|
379 GfxFillRect(r.left+10, r.top+2, r.right-10, r.top+2, c1); |
|
380 GfxFillRect(r.left+10, r.top+3, r.right-10, r.top+3, c2); |
|
381 GfxFillRect(r.left+10, r.top+7, r.right-10, r.top+7, c1); |
|
382 GfxFillRect(r.left+10, r.top+8, r.right-10, r.top+8, c2); |
|
383 |
|
384 // draw actual scrollbar |
|
385 pt = HandleScrollbarHittest(&w->hscroll, r.left, r.right); |
|
386 DrawFrameRect(pt.x, r.top, pt.y, r.bottom, wi->color, (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL)) == (WF_SCROLL_MIDDLE | WF_HSCROLL) ? FR_LOWERED : 0); |
|
387 |
|
388 break; |
|
389 } |
|
390 |
|
391 case WWT_FRAME: { |
|
392 int c1,c2; |
|
393 int x2 = r.left; // by default the left side is the left side of the widget |
|
394 |
|
395 if (wi->data != 0) x2 = DrawString(r.left + 6, r.top, wi->data, 0); |
|
396 |
|
397 c1 = _colour_gradient[wi->color][3]; |
|
398 c2 = _colour_gradient[wi->color][7]; |
|
399 |
|
400 //Line from upper left corner to start of text |
|
401 GfxFillRect(r.left, r.top+4, r.left+4,r.top+4, c1); |
|
402 GfxFillRect(r.left+1, r.top+5, r.left+4,r.top+5, c2); |
|
403 |
|
404 // Line from end of text to upper right corner |
|
405 GfxFillRect(x2, r.top+4, r.right-1,r.top+4,c1); |
|
406 GfxFillRect(x2, r.top+5, r.right-2,r.top+5,c2); |
|
407 |
|
408 // Line from upper left corner to bottom left corner |
|
409 GfxFillRect(r.left, r.top+5, r.left, r.bottom-1, c1); |
|
410 GfxFillRect(r.left+1, r.top+6, r.left+1, r.bottom-2, c2); |
|
411 |
|
412 //Line from upper right corner to bottom right corner |
|
413 GfxFillRect(r.right-1, r.top+5, r.right-1, r.bottom-2, c1); |
|
414 GfxFillRect(r.right, r.top+4, r.right, r.bottom-1, c2); |
|
415 |
|
416 GfxFillRect(r.left+1, r.bottom-1, r.right-1, r.bottom-1, c1); |
|
417 GfxFillRect(r.left, r.bottom, r.right, r.bottom, c2); |
|
418 |
|
419 goto draw_default; |
|
420 } |
|
421 |
|
422 case WWT_STICKYBOX: { |
|
423 assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere! |
|
424 |
|
425 clicked = !!(w->flags4 & WF_STICKY); |
|
426 DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); |
|
427 DrawSprite((clicked) ? SPR_PIN_UP : SPR_PIN_DOWN, r.left + 2 + clicked, r.top + 3 + clicked); |
|
428 break; |
|
429 } |
|
430 |
|
431 case WWT_RESIZEBOX: { |
|
432 assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere! |
|
433 |
|
434 clicked = !!(w->flags4 & WF_SIZING); |
|
435 DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0); |
|
436 DrawSprite(SPR_WINDOW_RESIZE, r.left + 3 + clicked, r.top + 3 + clicked); |
|
437 break; |
|
438 } |
|
439 |
|
440 case WWT_CLOSEBOX: { |
|
441 assert(r.right - r.left == 10); // ensure the same sizes are used everywhere |
|
442 |
|
443 DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, 0); |
|
444 DrawString(r.left + 2, r.top + 2, STR_00C5, 0); |
|
445 break; |
|
446 } |
|
447 |
|
448 case WWT_CAPTION: { |
|
449 assert(r.bottom - r.top == 13); // XXX - to ensure the same sizes are used everywhere! |
|
450 DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_BORDERONLY); |
|
451 DrawFrameRect(r.left+1, r.top+1, r.right-1, r.bottom-1, wi->color, (w->caption_color == 0xFF) ? FR_LOWERED | FR_DARKENED : FR_LOWERED | FR_DARKENED | FR_BORDERONLY); |
|
452 |
|
453 if (w->caption_color != 0xFF) { |
|
454 GfxFillRect(r.left+2, r.top+2, r.right-2, r.bottom-2, _colour_gradient[_player_colors[w->caption_color]][4]); |
|
455 } |
|
456 |
|
457 DrawStringCenteredTruncated(r.left + 2, r.right - 2, r.top+2, wi->data, 0x84); |
|
458 draw_default:; |
|
459 if (IsWindowWidgetDisabled(w, i)) { |
|
460 GfxFillRect(r.left+1, r.top+1, r.right-1, r.bottom-1, _colour_gradient[wi->color&0xF][2] | PALETTE_MODIFIER_GREYOUT); |
|
461 } |
|
462 } |
|
463 } |
|
464 } |
|
465 |
|
466 |
|
467 if (w->flags4 & WF_WHITE_BORDER_MASK) { |
|
468 //DrawFrameRect(w->left, w->top, w->left + w->width-1, w->top+w->height-1, 0xF, 0x10); |
|
469 DrawFrameRect(0, 0, w->width-1, w->height-1, 0xF, FR_BORDERONLY); |
|
470 } |
|
471 |
|
472 } |
|
473 |
|
474 static const Widget _dropdown_menu_widgets[] = { |
|
475 { WWT_PANEL, RESIZE_NONE, 0, 0, 0, 0, 0, 0x0, STR_NULL}, |
|
476 { WWT_SCROLLBAR, RESIZE_NONE, 0, 0, 0, 0, 0, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, |
|
477 { WIDGETS_END}, |
|
478 }; |
|
479 |
|
480 static int GetDropdownItem(const Window *w) |
|
481 { |
|
482 byte item, counter; |
|
483 int y; |
|
484 |
|
485 if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) < 0) |
|
486 return -1; |
|
487 |
|
488 y = _cursor.pos.y - w->top - 2 + w->vscroll.pos * 10; |
|
489 |
|
490 if (y < 0) |
|
491 return - 1; |
|
492 |
|
493 item = y / 10; |
|
494 if (item >= WP(w,dropdown_d).num_items || (HASBIT(WP(w,dropdown_d).disabled_state, item) && !HASBIT(WP(w,dropdown_d).hidden_state, item)) || WP(w,dropdown_d).items[item] == 0) |
|
495 return - 1; |
|
496 |
|
497 // Skip hidden items -- +1 for each hidden item before the clicked item. |
|
498 for (counter = 0; item >= counter; ++counter) |
|
499 if (HASBIT(WP(w,dropdown_d).hidden_state, counter)) item++; |
|
500 |
|
501 return item; |
|
502 } |
|
503 |
|
504 static void DropdownMenuWndProc(Window *w, WindowEvent *e) |
|
505 { |
|
506 int item; |
|
507 |
|
508 switch (e->event) { |
|
509 case WE_PAINT: { |
|
510 int x,y,i,sel; |
|
511 int width, height; |
|
512 |
|
513 DrawWindowWidgets(w); |
|
514 |
|
515 x = 1; |
|
516 y = 2 - w->vscroll.pos * 10; |
|
517 |
|
518 sel = WP(w,dropdown_d).selected_index; |
|
519 width = w->widget[0].right - 3; |
|
520 height = w->widget[0].bottom - 3; |
|
521 |
|
522 for (i = 0; WP(w,dropdown_d).items[i] != INVALID_STRING_ID; i++, sel--) { |
|
523 if (HASBIT(WP(w,dropdown_d).hidden_state, i)) continue; |
|
524 |
|
525 if (y >= 0 && y <= height) { |
|
526 if (WP(w,dropdown_d).items[i] != STR_NULL) { |
|
527 if (sel == 0) GfxFillRect(x + 1, y, x + width, y + 9, 0); |
|
528 DrawStringTruncated(x + 2, y, WP(w,dropdown_d).items[i], sel == 0 ? 12 : 16, x + width); |
|
529 |
|
530 if (HASBIT(WP(w,dropdown_d).disabled_state, i)) { |
|
531 GfxFillRect(x, y, x + width, y + 9, |
|
532 PALETTE_MODIFIER_GREYOUT | _colour_gradient[_dropdown_menu_widgets[0].color][5] |
|
533 ); |
|
534 } |
|
535 } else { |
|
536 int c1 = _colour_gradient[_dropdown_menu_widgets[0].color][3]; |
|
537 int c2 = _colour_gradient[_dropdown_menu_widgets[0].color][7]; |
|
538 |
|
539 GfxFillRect(x + 1, y + 3, x + w->width - 5, y + 3, c1); |
|
540 GfxFillRect(x + 1, y + 4, x + w->width - 5, y + 4, c2); |
|
541 } |
|
542 } |
|
543 y += 10; |
|
544 } |
|
545 } break; |
|
546 |
|
547 case WE_CLICK: { |
|
548 if (e->we.click.widget != 0) break; |
|
549 item = GetDropdownItem(w); |
|
550 if (item >= 0) { |
|
551 WP(w,dropdown_d).click_delay = 4; |
|
552 WP(w,dropdown_d).selected_index = item; |
|
553 SetWindowDirty(w); |
|
554 } |
|
555 } break; |
|
556 |
|
557 case WE_MOUSELOOP: { |
|
558 Window *w2 = FindWindowById(WP(w,dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num); |
|
559 if (w2 == NULL) { |
|
560 DeleteWindow(w); |
|
561 return; |
|
562 } |
|
563 |
|
564 if (WP(w,dropdown_d).click_delay != 0 && --WP(w,dropdown_d).click_delay == 0) { |
|
565 WindowEvent e; |
|
566 e.event = WE_DROPDOWN_SELECT; |
|
567 e.we.dropdown.button = WP(w,dropdown_d).parent_button; |
|
568 e.we.dropdown.index = WP(w,dropdown_d).selected_index; |
|
569 w2->wndproc(w2, &e); |
|
570 DeleteWindow(w); |
|
571 return; |
|
572 } |
|
573 |
|
574 if (WP(w,dropdown_d).drag_mode) { |
|
575 item = GetDropdownItem(w); |
|
576 |
|
577 if (!_left_button_clicked) { |
|
578 WP(w,dropdown_d).drag_mode = false; |
|
579 if (item < 0) return; |
|
580 WP(w,dropdown_d).click_delay = 2; |
|
581 } else { |
|
582 if (item < 0) return; |
|
583 } |
|
584 |
|
585 WP(w,dropdown_d).selected_index = item; |
|
586 SetWindowDirty(w); |
|
587 } |
|
588 } break; |
|
589 |
|
590 case WE_DESTROY: { |
|
591 Window *w2 = FindWindowById(WP(w,dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num); |
|
592 if (w2 != NULL) { |
|
593 RaiseWindowWidget(w2, WP(w,dropdown_d).parent_button); |
|
594 InvalidateWidget(w2, WP(w,dropdown_d).parent_button); |
|
595 } |
|
596 } break; |
|
597 } |
|
598 } |
|
599 |
|
600 void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask) |
|
601 { |
|
602 int i; |
|
603 const Widget *wi; |
|
604 Window *w2; |
|
605 const Window *w3; |
|
606 bool is_dropdown_menu_shown = IsWindowWidgetLowered(w, button); |
|
607 int top, height; |
|
608 int screen_top, screen_bottom; |
|
609 bool scroll = false; |
|
610 |
|
611 DeleteWindowById(WC_DROPDOWN_MENU, 0); |
|
612 |
|
613 if (is_dropdown_menu_shown) return; |
|
614 |
|
615 LowerWindowWidget(w, button); |
|
616 |
|
617 InvalidateWidget(w, button); |
|
618 |
|
619 for (i = 0; strings[i] != INVALID_STRING_ID; i++) {} |
|
620 if (i == 0) return; |
|
621 |
|
622 wi = &w->widget[button]; |
|
623 |
|
624 if (hidden_mask != 0) { |
|
625 uint j; |
|
626 |
|
627 for (j = 0; strings[j] != INVALID_STRING_ID; j++) { |
|
628 if (HASBIT(hidden_mask, j)) i--; |
|
629 } |
|
630 } |
|
631 |
|
632 /* The preferred position is just below the dropdown calling widget */ |
|
633 top = w->top + wi->bottom + 2; |
|
634 height = i * 10 + 4; |
|
635 |
|
636 w3 = FindWindowById(WC_STATUS_BAR, 0); |
|
637 screen_bottom = w3 == NULL ? _screen.height : w3->top; |
|
638 |
|
639 /* Check if the dropdown will fully fit below the widget */ |
|
640 if (top + height >= screen_bottom) { |
|
641 w3 = FindWindowById(WC_MAIN_TOOLBAR, 0); |
|
642 screen_top = w3 == NULL ? 0 : w3->top + w3->height; |
|
643 |
|
644 /* If not, check if it will fit above the widget */ |
|
645 if (w->top + wi->top - height - 1 > screen_top) { |
|
646 top = w->top + wi->top - height - 1; |
|
647 } else { |
|
648 /* ... and lastly if it won't, enable the scroll bar and fit the |
|
649 * list in below the widget */ |
|
650 int rows = (screen_bottom - 4 - top) / 10; |
|
651 height = rows * 10 + 4; |
|
652 scroll = true; |
|
653 } |
|
654 } |
|
655 |
|
656 w2 = AllocateWindow( |
|
657 w->left + wi[-1].left + 1, |
|
658 top, |
|
659 wi->right - wi[-1].left + 1, |
|
660 height, |
|
661 DropdownMenuWndProc, |
|
662 WC_DROPDOWN_MENU, |
|
663 _dropdown_menu_widgets); |
|
664 |
|
665 w2->widget[0].color = wi->color; |
|
666 w2->widget[0].right = wi->right - wi[-1].left; |
|
667 w2->widget[0].bottom = height - 1; |
|
668 |
|
669 SetWindowWidgetHiddenState(w2, 1, !scroll); |
|
670 |
|
671 if (scroll) { |
|
672 /* We're scrolling, so enable the scroll bar and shrink the list by |
|
673 * the scrollbar's width */ |
|
674 w2->widget[1].color = wi->color; |
|
675 w2->widget[1].right = w2->widget[0].right; |
|
676 w2->widget[1].left = w2->widget[1].right - 11; |
|
677 w2->widget[1].bottom = height - 1; |
|
678 w2->widget[0].right -= 12; |
|
679 |
|
680 w2->vscroll.cap = (height - 4) / 10; |
|
681 w2->vscroll.count = i; |
|
682 } |
|
683 |
|
684 w2->desc_flags = WDF_DEF_WIDGET; |
|
685 w2->flags4 &= ~WF_WHITE_BORDER_MASK; |
|
686 |
|
687 WP(w2,dropdown_d).disabled_state = disabled_mask; |
|
688 WP(w2,dropdown_d).hidden_state = hidden_mask; |
|
689 |
|
690 WP(w2,dropdown_d).parent_wnd_class = w->window_class; |
|
691 WP(w2,dropdown_d).parent_wnd_num = w->window_number; |
|
692 WP(w2,dropdown_d).parent_button = button; |
|
693 |
|
694 WP(w2,dropdown_d).num_items = i; |
|
695 WP(w2,dropdown_d).selected_index = selected; |
|
696 WP(w2,dropdown_d).items = strings; |
|
697 |
|
698 WP(w2,dropdown_d).click_delay = 0; |
|
699 WP(w2,dropdown_d).drag_mode = true; |
|
700 } |