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