Improve indentation consistency in client.qc
[xonotic/xonotic-data.pk3dir.git] / qcsrc / menu / draw.qc
1 #include "draw.qh"
2 #include "../common/util.qh"
3 #include "../common/constants.qh"
4
5 string draw_mousepointer;
6 vector draw_mousepointer_offset;
7 vector draw_mousepointer_size;
8
9 void draw_setMousePointer(string pic, vector theSize, vector theOffset)
10 {
11         draw_mousepointer = strzone(draw_UseSkinFor(pic));
12         draw_mousepointer_size = theSize;
13         draw_mousepointer_offset = eX * (theOffset.x * theSize.x) + eY * (theOffset.y * theSize.y);
14 }
15
16 void draw_drawMousePointer(vector where)
17 {
18         drawpic(boxToGlobal(where, draw_shift, draw_scale) - draw_mousepointer_offset, draw_mousepointer, draw_mousepointer_size, '1 1 1', draw_alpha, 0);
19 }
20
21 void draw_reset(float cw, float ch, float ox, float oy)
22 {
23         draw_shift = '1 0 0' * ox + '0 1 0' * oy;
24         draw_scale = '1 0 0' * cw + '0 1 0' * ch;
25         draw_alpha = 1;
26         draw_fontscale = '1 1 0';
27         draw_endBoldFont();
28 }
29
30 vector globalToBox(vector v, vector theOrigin, vector theScale)
31 {
32         v -= theOrigin;
33         v.x /= theScale.x;
34         v.y /= theScale.y;
35         return v;
36 }
37
38 vector globalToBoxSize(vector v, vector theScale)
39 {
40         v.x /= theScale.x;
41         v.y /= theScale.y;
42         return v;
43 }
44
45 vector boxToGlobal(vector v, vector theOrigin, vector theScale)
46 {
47         v.x *= theScale.x;
48         v.y *= theScale.y;
49         v += theOrigin;
50         return v;
51 }
52
53 vector boxToGlobalSize(vector v, vector theScale)
54 {
55         v.x *= theScale.x;
56         v.y *= theScale.y;
57         return v;
58 }
59
60 string draw_PreloadPicture(string pic)
61 {
62         pic = draw_UseSkinFor(pic);
63         return precache_pic(pic);
64 }
65
66 string draw_PreloadPictureWithFlags(string pic, float f)
67 {
68         pic = draw_UseSkinFor(pic);
69         return precache_pic(pic, f);
70 }
71
72 void draw_Picture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha)
73 {
74         if(theSize.x == 0 || theSize.y <= 0) // no default sizing please
75                 return;
76         pic = draw_UseSkinFor(pic);
77         drawpic(boxToGlobal(theOrigin, draw_shift, draw_scale), pic, boxToGlobalSize(theSize, draw_scale), theColor, theAlpha * draw_alpha, 0);
78 }
79
80 vector draw_PictureSize(string pic)
81 {
82         pic = draw_UseSkinFor(pic);
83         return drawgetimagesize(pic);
84 }
85
86 void draw_Fill(vector theOrigin, vector theSize, vector theColor, float theAlpha)
87 {
88         drawfill(boxToGlobal(theOrigin, draw_shift, draw_scale), boxToGlobalSize(theSize, draw_scale), theColor, theAlpha * draw_alpha, 0);
89 }
90
91 // a button picture is a texture containing three parts:
92 //   1/4 width: left part
93 //   1/2 width: middle part (stretched)
94 //   1/4 width: right part
95 // it is assumed to be 4x as wide as high for aspect ratio purposes, which
96 // means, the parts are a square, two squares and a square.
97 void draw_ButtonPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha)
98 {
99         vector square;
100         vector width, height;
101         vector bW;
102         pic = draw_UseSkinFor(pic);
103         theOrigin = boxToGlobal(theOrigin, draw_shift, draw_scale);
104         theSize = boxToGlobalSize(theSize, draw_scale);
105         theAlpha *= draw_alpha;
106         width = eX * theSize.x;
107         height = eY * theSize.y;
108         if(theSize.x <= theSize.y * 2)
109         {
110                 // button not wide enough
111                 // draw just left and right part then
112                 square = eX * theSize.x * 0.5;
113                 bW = eX * (0.25 * theSize.x / (theSize.y * 2));
114                 drawsubpic(theOrigin,          square + height, pic, '0 0 0', eY + bW, theColor, theAlpha, 0);
115                 drawsubpic(theOrigin + square, square + height, pic, eX - bW, eY + bW, theColor, theAlpha, 0);
116         }
117         else
118         {
119                 square = eX * theSize.y;
120                 drawsubpic(theOrigin,                  height  +     square, pic, '0    0 0', '0.25 1 0', theColor, theAlpha, 0);
121                 drawsubpic(theOrigin +         square, theSize - 2 * square, pic, '0.25 0 0', '0.5  1 0', theColor, theAlpha, 0);
122                 drawsubpic(theOrigin + width - square, height  +     square, pic, '0.75 0 0', '0.25 1 0', theColor, theAlpha, 0);
123         }
124 }
125
126 // a vertical button picture is a texture containing three parts:
127 //   1/4 height: left part
128 //   1/2 height: middle part (stretched)
129 //   1/4 height: right part
130 // it is assumed to be 4x as high as wide for aspect ratio purposes, which
131 // means, the parts are a square, two squares and a square.
132 void draw_VertButtonPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha)
133 {
134         vector square;
135         vector width, height;
136         vector bH;
137         pic = draw_UseSkinFor(pic);
138         theOrigin = boxToGlobal(theOrigin, draw_shift, draw_scale);
139         theSize = boxToGlobalSize(theSize, draw_scale);
140         theAlpha *= draw_alpha;
141         width = eX * theSize.x;
142         height = eY * theSize.y;
143         if(theSize.y <= theSize.x * 2)
144         {
145                 // button not high enough
146                 // draw just upper and lower part then
147                 square = eY * theSize.y * 0.5;
148                 bH = eY * (0.25 * theSize.y / (theSize.x * 2));
149                 drawsubpic(theOrigin,          square + width, pic, '0 0 0', eX + bH, theColor, theAlpha, 0);
150                 drawsubpic(theOrigin + square, square + width, pic, eY - bH, eX + bH, theColor, theAlpha, 0);
151         }
152         else
153         {
154                 square = eY * theSize.x;
155                 drawsubpic(theOrigin,                   width   +     square, pic, '0 0    0', '1 0.25 0', theColor, theAlpha, 0);
156                 drawsubpic(theOrigin +          square, theSize - 2 * square, pic, '0 0.25 0', '1 0.5  0', theColor, theAlpha, 0);
157                 drawsubpic(theOrigin + height - square, width   +     square, pic, '0 0.75 0', '1 0.25 0', theColor, theAlpha, 0);
158         }
159 }
160
161 // a border picture is a texture containing nine parts:
162 //   1/4 width: left part
163 //   1/2 width: middle part (stretched)
164 //   1/4 width: right part
165 // divided into
166 //   1/4 height: top part
167 //   1/2 height: middle part (stretched)
168 //   1/4 height: bottom part
169 void draw_BorderPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha, vector theBorderSize)
170 {
171         vector dX, dY;
172         vector width, height;
173         vector bW, bH;
174         pic = draw_UseSkinFor(pic);
175         theOrigin = boxToGlobal(theOrigin, draw_shift, draw_scale);
176         theSize = boxToGlobalSize(theSize, draw_scale);
177         theBorderSize = boxToGlobalSize(theBorderSize, draw_scale);
178         theAlpha *= draw_alpha;
179         width = eX * theSize.x;
180         height = eY * theSize.y;
181         // zero size? bail out, we cannot handle this
182         if(theSize.x <= 0 || theSize.y <= 0)
183                 return;
184         if(theBorderSize.x <= 0) // no x border
185         {
186                 if(theBorderSize.y <= 0)
187                 {
188                         drawsubpic(theOrigin,                            width          + height,          pic, '0.25 0.25 0', '0.5  0.5  0', theColor, theAlpha, 0);
189                 }
190                 else if(theSize.y <= theBorderSize.y * 2)
191                 {
192                         // not high enough... draw just top and bottom then
193                         bH = eY * (0.25 * theSize.y / (theBorderSize.y * 2));
194                         drawsubpic(theOrigin,                             width          + height * 0.5, pic, '0.25 0 0',           '0.5  0 0' + bH, theColor, theAlpha, 0);
195                         drawsubpic(theOrigin              + height * 0.5, width          + height * 0.5, pic, '0.25 0 0' + eY - bH, '0.5  0 0' + bH, theColor, theAlpha, 0);
196                 }
197                 else
198                 {
199                         dY = theBorderSize.y * eY;
200                         drawsubpic(theOrigin,                            width                   +     dY, pic, '0.25 0    0', '0.5  0.25 0', theColor, theAlpha, 0);
201                         drawsubpic(theOrigin          + dY,              width          + height - 2 * dY, pic, '0.25 0.25 0', '0.5  0.5  0', theColor, theAlpha, 0);
202                         drawsubpic(theOrigin + height - dY,              width                   +     dY, pic, '0.25 0.75 0', '0.5  0.25 0', theColor, theAlpha, 0);
203                 }
204         }
205         else if(theSize.x <= theBorderSize.x * 2)
206         {
207                 // not wide enough... draw just left and right then
208                 bW = eX * (0.25 * theSize.x / (theBorderSize.x * 2));
209                 if(theBorderSize.y <= 0)
210                 {
211                         drawsubpic(theOrigin,                             width * 0.5 + height,          pic, '0 0.25 0',           '0 0.5  0' + bW, theColor, theAlpha, 0);
212                         drawsubpic(theOrigin + width * 0.5,               width * 0.5 + height,          pic, '0 0.25 0' + eX - bW, '0 0.5  0' + bW, theColor, theAlpha, 0);
213                 }
214                 else if(theSize.y <= theBorderSize.y * 2)
215                 {
216                         // not high enough... draw just corners
217                         bH = eY * (0.25 * theSize.y / (theBorderSize.y * 2));
218                         drawsubpic(theOrigin,                 width * 0.5 + height * 0.5, pic, '0 0 0',           bW + bH, theColor, theAlpha, 0);
219                         drawsubpic(theOrigin + width   * 0.5, width * 0.5 + height * 0.5, pic, eX - bW,           bW + bH, theColor, theAlpha, 0);
220                         drawsubpic(theOrigin + height  * 0.5, width * 0.5 + height * 0.5, pic, eY - bH,           bW + bH, theColor, theAlpha, 0);
221                         drawsubpic(theOrigin + theSize * 0.5, width * 0.5 + height * 0.5, pic, eX + eY - bW - bH, bW + bH, theColor, theAlpha, 0);
222                 }
223                 else
224                 {
225                         dY = theBorderSize.y * eY;
226                         drawsubpic(theOrigin,                             width * 0.5          +     dY, pic, '0 0    0',           '0 0.25 0' + bW, theColor, theAlpha, 0);
227                         drawsubpic(theOrigin + width * 0.5,               width * 0.5          +     dY, pic, '0 0    0' + eX - bW, '0 0.25 0' + bW, theColor, theAlpha, 0);
228                         drawsubpic(theOrigin                        + dY, width * 0.5 + height - 2 * dY, pic, '0 0.25 0',           '0 0.5  0' + bW, theColor, theAlpha, 0);
229                         drawsubpic(theOrigin + width * 0.5          + dY, width * 0.5 + height - 2 * dY, pic, '0 0.25 0' + eX - bW, '0 0.5  0' + bW, theColor, theAlpha, 0);
230                         drawsubpic(theOrigin               + height - dY, width * 0.5          +     dY, pic, '0 0.75 0',           '0 0.25 0' + bW, theColor, theAlpha, 0);
231                         drawsubpic(theOrigin + width * 0.5 + height - dY, width * 0.5          +     dY, pic, '0 0.75 0' + eX - bW, '0 0.25 0' + bW, theColor, theAlpha, 0);
232                 }
233         }
234         else
235         {
236                 if(theBorderSize.y <= 0)
237                 {
238                         dX = theBorderSize.x * eX;
239                         drawsubpic(theOrigin,                                        dX + height,          pic, '0    0.25 0', '0.25 0.5  0', theColor, theAlpha, 0);
240                         drawsubpic(theOrigin                       + dX, width - 2 * dX + height,          pic, '0.25 0.25 0', '0.5  0.5  0', theColor, theAlpha, 0);
241                         drawsubpic(theOrigin               + width - dX,             dX + height,          pic, '0.75 0.25 0', '0.25 0.5  0', theColor, theAlpha, 0);
242                 }
243                 else if(theSize.y <= theBorderSize.y * 2)
244                 {
245                         // not high enough... draw just top and bottom then
246                         bH = eY * (0.25 * theSize.y / (theBorderSize.y * 2));
247                         dX = theBorderSize.x * eX;
248                         drawsubpic(theOrigin,                                         dX + height * 0.5, pic, '0    0 0',           '0.25 0 0' + bH, theColor, theAlpha, 0);
249                         drawsubpic(theOrigin + dX,                        width - 2 * dX + height * 0.5, pic, '0.25 0 0',           '0.5  0 0' + bH, theColor, theAlpha, 0);
250                         drawsubpic(theOrigin + width - dX,                            dX + height * 0.5, pic, '0.75 0 0',           '0.25 0 0' + bH, theColor, theAlpha, 0);
251                         drawsubpic(theOrigin              + height * 0.5,             dX + height * 0.5, pic, '0    0 0' + eY - bH, '0.25 0 0' + bH, theColor, theAlpha, 0);
252                         drawsubpic(theOrigin + dX         + height * 0.5, width - 2 * dX + height * 0.5, pic, '0.25 0 0' + eY - bH, '0.5  0 0' + bH, theColor, theAlpha, 0);
253                         drawsubpic(theOrigin + width - dX + height * 0.5,             dX + height * 0.5, pic, '0.75 0 0' + eY - bH, '0.25 0 0' + bH, theColor, theAlpha, 0);
254                 }
255                 else
256                 {
257                         dX = theBorderSize.x * eX;
258                         dY = theBorderSize.y * eY;
259                         drawsubpic(theOrigin,                                        dX          +     dY, pic, '0    0    0', '0.25 0.25 0', theColor, theAlpha, 0);
260                         drawsubpic(theOrigin                  + dX,      width - 2 * dX          +     dY, pic, '0.25 0    0', '0.5  0.25 0', theColor, theAlpha, 0);
261                         drawsubpic(theOrigin          + width - dX,                  dX          +     dY, pic, '0.75 0    0', '0.25 0.25 0', theColor, theAlpha, 0);
262                         drawsubpic(theOrigin          + dY,                          dX + height - 2 * dY, pic, '0    0.25 0', '0.25 0.5  0', theColor, theAlpha, 0);
263                         drawsubpic(theOrigin          + dY         + dX, width - 2 * dX + height - 2 * dY, pic, '0.25 0.25 0', '0.5  0.5  0', theColor, theAlpha, 0);
264                         drawsubpic(theOrigin          + dY + width - dX,             dX + height - 2 * dY, pic, '0.75 0.25 0', '0.25 0.5  0', theColor, theAlpha, 0);
265                         drawsubpic(theOrigin + height - dY,                          dX          +     dY, pic, '0    0.75 0', '0.25 0.25 0', theColor, theAlpha, 0);
266                         drawsubpic(theOrigin + height - dY         + dX, width - 2 * dX          +     dY, pic, '0.25 0.75 0', '0.5  0.25 0', theColor, theAlpha, 0);
267                         drawsubpic(theOrigin + height - dY + width - dX,             dX          +     dY, pic, '0.75 0.75 0', '0.25 0.25 0', theColor, theAlpha, 0);
268                 }
269         }
270 }
271 void draw_Text(vector theOrigin, string theText, vector theSize, vector theColor, float theAlpha, float ICanHasKallerz)
272 {
273         if(theSize.x <= 0 || theSize.y <= 0) {
274                 LOG_TRACE("Drawing zero size text?");
275                 return;
276         }
277
278         //float wi;
279         //wi = draw_TextWidth(theText, ICanHasKallerz, theSize);
280         //draw_Fill(theOrigin, '1 0 0' * wi + '0 1 0' * theSize_y, '1 0 0', 0.3);
281
282         if(ICanHasKallerz)
283                 drawcolorcodedstring(boxToGlobal(theOrigin, draw_shift, draw_scale), theText, globalToBoxSize(boxToGlobalSize(theSize, draw_scale), draw_fontscale), theAlpha * draw_alpha, 0);
284         else
285                 drawstring(boxToGlobal(theOrigin, draw_shift, draw_scale), theText, globalToBoxSize(boxToGlobalSize(theSize, draw_scale), draw_fontscale), theColor, theAlpha * draw_alpha, 0);
286 }
287 void draw_CenterText(vector theOrigin, string theText, vector theSize, vector theColor, float theAlpha, float ICanHasKallerz)
288 {
289         //dprint(strcat("orig = ", vtos(theOrigin) ," tx = ", ftos(draw_TextWidth(theText, ICanHasKallerz, theSize)), "\n"));
290         draw_Text(theOrigin - eX * 0.5 * draw_TextWidth(theText, ICanHasKallerz, theSize), theText, theSize, theColor, theAlpha, ICanHasKallerz);
291 }
292
293 float draw_TextWidth(string theText, float ICanHasKallerz, vector SizeThxBye)
294 {
295         //return strlen(theText);
296         //dprint("draw_TextWidth \"", theText, "\"\n");
297         vector v;
298         v = '0 0 0';
299         //float r;
300         v.x = stringwidth(theText, ICanHasKallerz, globalToBoxSize(boxToGlobalSize(SizeThxBye, draw_scale), draw_fontscale));
301         v = globalToBoxSize(v, draw_scale);
302         return v.x;
303 }
304
305 float draw_CondensedFontFactor(string theText, float ICanHasKallerz, vector SizeThxBye, float maxWidth)
306 {
307         float w = draw_TextWidth(theText, ICanHasKallerz, SizeThxBye);
308         if (w > maxWidth) {
309                 //dprintf("NOTE: label text %s too wide for label, condensed by factor %f\n", theText, maxWidth / w);
310                 return maxWidth / w;
311         }
312         return 1.0;
313 }
314
315 IntrusiveList draw_clip;
316 STATIC_INIT(draw_clip) { draw_clip = IL_NEW(); }
317 CLASS(ClipFrame, Object)
318         ATTRIB(ClipFrame, clip_shift, vector, '0 0 0');
319         ATTRIB(ClipFrame, clip_scale, vector, '0 0 0');
320 ENDCLASS(ClipFrame)
321
322 void _draw_SetClip(vector o, vector s)
323 {
324         ClipFrame prev = IL_PEEK(draw_clip);
325         if (prev) {
326                 o.x = bound(prev.clip_shift.x, o.x, prev.clip_shift.x + prev.clip_scale.x);
327                 o.y = bound(prev.clip_shift.y, o.y, prev.clip_shift.y + prev.clip_scale.y);
328                 s.x = bound(0, s.x, prev.clip_scale.x - (o.x - prev.clip_shift.x));
329                 s.y = bound(0, s.y, prev.clip_scale.y - (o.y - prev.clip_shift.y));
330         }
331         ClipFrame e = NEW(ClipFrame);
332         e.clip_shift = o;
333         e.clip_scale = s;
334         IL_PUSH(draw_clip, e);
335         drawsetcliparea(o.x, o.y, s.x, s.y);
336 }
337
338 void draw_SetClip()
339 {
340         _draw_SetClip(draw_shift, draw_scale);
341 }
342
343 void draw_SetClipRect(vector theOrigin, vector theScale)
344 {
345         _draw_SetClip(
346                 boxToGlobal(theOrigin, draw_shift, draw_scale),
347                 boxToGlobalSize(theScale, draw_scale)
348         );
349 }
350
351 void draw_ClearClip()
352 {
353         if (IL_EMPTY(draw_clip)) {
354                 LOG_FATAL("Not clipping, can't clear it then");
355         }
356         entity currentSettings = IL_PEEK(draw_clip);
357         IL_REMOVE(draw_clip, currentSettings);
358         delete(currentSettings);
359         drawresetcliparea();
360         ClipFrame e = IL_PEEK(draw_clip);
361         if (e) {
362                 drawsetcliparea(e.clip_shift.x, e.clip_shift.y, e.clip_scale.x, e.clip_scale.y);
363         }
364 }
365
366 string draw_TextShortenToWidth(string theText, float maxWidth, float ICanHasKallerz, vector SizeThxBye)
367 {
368         /*
369         if(draw_TextWidth(theText, ICanHasKallerz, SizeThxBye) <= maxWidth)
370                 return theText;
371         else
372                 return strcat(substring(theText, 0, draw_TextLengthUpToWidth(theText, maxWidth - draw_TextWidth("...", ICanHasKallerz, SizeThxBye), ICanHasKallerz, SizeThxBye)), "...");
373         */
374         if(ICanHasKallerz)
375                 return textShortenToWidth(theText, maxWidth, SizeThxBye, draw_TextWidth_WithColors);
376         else
377                 return textShortenToWidth(theText, maxWidth, SizeThxBye, draw_TextWidth_WithoutColors);
378 }
379
380 float draw_TextWidth_WithColors(string s, vector theFontSize)
381 {
382         return draw_TextWidth(s, true, theFontSize);
383 }
384
385 float draw_TextWidth_WithoutColors(string s, vector theFontSize)
386 {
387         return draw_TextWidth(s, false, theFontSize);
388 }
389
390 float draw_TextLengthUpToWidth(string theText, float maxWidth, float allowColorCodes, vector theFontSize)
391 {
392         if(allowColorCodes)
393                 return textLengthUpToWidth(theText, maxWidth, theFontSize, draw_TextWidth_WithColors);
394         else
395                 return textLengthUpToWidth(theText, maxWidth, theFontSize, draw_TextWidth_WithoutColors);
396 }