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