Merge remote-tracking branch 'origin/divVerent/allow-override-item-model'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / menu / item / container.c
1 #ifdef INTERFACE
2 CLASS(Container) EXTENDS(Item)
3         METHOD(Container, draw, void(entity))
4         METHOD(Container, keyUp, float(entity, float, float, float))
5         METHOD(Container, keyDown, float(entity, float, float, float))
6         METHOD(Container, mouseMove, float(entity, vector))
7         METHOD(Container, mousePress, float(entity, vector))
8         METHOD(Container, mouseDrag, float(entity, vector))
9         METHOD(Container, mouseRelease, float(entity, vector))
10         METHOD(Container, focusLeave, void(entity))
11         METHOD(Container, resizeNotify, void(entity, vector, vector, vector, vector))
12         METHOD(Container, resizeNotifyLie, void(entity, vector, vector, vector, vector, .vector, .vector, .vector))
13         METHOD(Container, addItem, void(entity, entity, vector, vector, float))
14         METHOD(Container, addItemCentered, void(entity, entity, vector, float))
15         METHOD(Container, moveItemAfter, void(entity, entity, entity))
16         METHOD(Container, removeItem, void(entity, entity))
17         METHOD(Container, setFocus, void(entity, entity))
18         METHOD(Container, setAlphaOf, void(entity, entity, float))
19         METHOD(Container, itemFromPoint, entity(entity, vector))
20         METHOD(Container, showNotify, void(entity))
21         METHOD(Container, hideNotify, void(entity))
22         METHOD(Container, preferredFocusedGrandChild, entity(entity))
23         ATTRIB(Container, focusable, float, 0)
24         ATTRIB(Container, firstChild, entity, NULL)
25         ATTRIB(Container, lastChild, entity, NULL)
26         ATTRIB(Container, focusedChild, entity, NULL)
27         ATTRIB(Container, shown, float, 0)
28
29         METHOD(Container, enterSubitem, void(entity, entity))
30         METHOD(Container, enterLieSubitem, void(entity, vector, vector, vector, float))
31         METHOD(Container, leaveSubitem, void(entity))
32 ENDCLASS(Container)
33 .entity nextSibling;
34 .entity prevSibling;
35 .float resized;
36 .vector Container_origin;
37 .vector Container_size;
38 .vector Container_fontscale;
39 .float Container_alpha;
40 .vector Container_save_shift;
41 .vector Container_save_scale;
42 .vector Container_save_fontscale;
43 .float Container_save_alpha;
44 #endif
45
46 #ifdef IMPLEMENTATION
47 void Container_enterSubitem(entity me, entity sub)
48 {
49         me.enterLieSubitem(me, sub.Container_origin, sub.Container_size, sub.Container_fontscale, sub.Container_alpha);
50 }
51
52 void Container_enterLieSubitem(entity me, vector o, vector s, vector f, float a)
53 {
54         me.Container_save_shift = draw_shift;
55         me.Container_save_scale = draw_scale;
56         me.Container_save_alpha = draw_alpha;
57         me.Container_save_fontscale = draw_fontscale;
58
59         draw_shift = boxToGlobal(o, draw_shift, draw_scale);
60         draw_scale = boxToGlobalSize(s, draw_scale);
61         if(f != '0 0 0')
62                 draw_fontscale = boxToGlobalSize(f, draw_fontscale);
63         draw_alpha *= a;
64 }
65
66 void Container_leaveSubitem(entity me)
67 {
68         draw_shift = me.Container_save_shift;
69         draw_scale = me.Container_save_scale;
70         draw_alpha = me.Container_save_alpha;
71         draw_fontscale = me.Container_save_fontscale;
72 }
73
74 void Container_showNotify(entity me)
75 {
76         entity e;
77         if(me.shown)
78                 return;
79         me.shown = 1;
80         for(e = me.firstChild; e; e = e.nextSibling)
81                 if(e.Container_alpha > 0)
82                         e.showNotify(e);
83 }
84
85 void Container_hideNotify(entity me)
86 {
87         entity e;
88         if not(me.shown)
89                 return;
90         me.shown = 0;
91         for(e = me.firstChild; e; e = e.nextSibling)
92                 if(e.Container_alpha > 0)
93                         e.hideNotify(e);
94 }
95
96 void Container_setAlphaOf(entity me, entity other, float theAlpha)
97 {
98         if(theAlpha <= 0)
99         {
100                 if(other.Container_alpha > 0)
101                         other.hideNotify(other);
102         }
103         else // value > 0
104         {
105                 if(other.Container_alpha <= 0)
106                         other.showNotify(other);
107         }
108         other.Container_alpha = theAlpha;
109 }
110
111 void Container_resizeNotifyLie(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize, .vector originField, .vector sizeField, .vector fontScaleField)
112 {
113         entity e;
114         vector o, s;
115         float d;
116         for(e = me.firstChild; e; e = e.nextSibling)
117         {
118                 o = e.originField;
119                 s = e.sizeField;
120                 me.enterLieSubitem(me, o, s, e.fontScaleField, e.Container_alpha);
121                 e.resizeNotify(e, o, s, boxToGlobal(o, absOrigin, absSize), boxToGlobalSize(s, absSize));
122                 me.leaveSubitem(me);
123         }
124         do
125         {
126                 d = 0;
127                 for(e = me.firstChild; e; e = e.nextSibling)
128                         if(e.resized)
129                         {
130                                 e.resized = 0;
131                                 d = 1;
132                                 o = e.originField;
133                                 s = e.sizeField;
134                                 me.enterLieSubitem(me, o, s, e.fontScaleField, e.Container_alpha);
135                                 e.resizeNotify(e, o, s, boxToGlobal(o, absOrigin, absSize), boxToGlobalSize(s, absSize));
136                                 me.leaveSubitem(me);
137                         }
138         }
139         while(d);
140         SUPER(Container).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
141 }
142
143 void Container_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
144 {
145         me.resizeNotifyLie(me, relOrigin, relSize, absOrigin, absSize, Container_origin, Container_size, Container_fontscale);
146 }
147
148 entity Container_itemFromPoint(entity me, vector pos)
149 {
150         entity e;
151         vector o, s;
152         for(e = me.lastChild; e; e = e.prevSibling)
153         {
154                 o = e.Container_origin;
155                 s = e.Container_size;
156                 if(pos_x < o_x) continue;
157                 if(pos_y < o_y) continue;
158                 if(pos_x >= o_x + s_x) continue;
159                 if(pos_y >= o_y + s_y) continue;
160                 return e;
161         }
162         return NULL;
163 }
164
165 void Container_draw(entity me)
166 {
167         entity e;
168
169         me.focusable = 0;
170         for(e = me.firstChild; e; e = e.nextSibling)
171         {
172                 if(e.focusable)
173                         me.focusable += 1;
174                 if(e.Container_alpha < 0.003) // can't change color values anyway
175                         continue;
176                 me.enterSubitem(me, e);
177                 e.draw(e);
178                 me.leaveSubitem(me);
179         }
180 }
181
182 void Container_focusLeave(entity me)
183 {
184         me.setFocus(me, NULL);
185 }
186
187 float Container_keyUp(entity me, float scan, float ascii, float shift)
188 {
189         entity f;
190         float r;
191         f = me.focusedChild;
192         if(f)
193         {
194                 me.enterSubitem(me, f);
195                 r = f.keyUp(f, scan, ascii, shift);
196                 me.leaveSubitem(me);
197                 return r;
198         }
199         return 0;
200 }
201
202 float Container_keyDown(entity me, float scan, float ascii, float shift)
203 {
204         entity f;
205         float r;
206         f = me.focusedChild;
207         if(f)
208         {
209                 me.enterSubitem(me, f);
210                 r = f.keyDown(f, scan, ascii, shift);
211                 me.leaveSubitem(me);
212                 return r;
213         }
214         return 0;
215 }
216
217 float Container_mouseMove(entity me, vector pos)
218 {
219         entity f;
220         float r;
221         f = me.focusedChild;
222         if(f)
223         {
224                 me.enterSubitem(me, f);
225                 r = f.mouseMove(f, globalToBox(pos, f.Container_origin, f.Container_size));
226                 me.leaveSubitem(me);
227                 return r;
228         }
229         return 0;
230 }
231 float Container_mousePress(entity me, vector pos)
232 {
233         entity f;
234         float r;
235         f = me.focusedChild;
236         if(f)
237         {
238                 me.enterSubitem(me, f);
239                 r = f.mousePress(f, globalToBox(pos, f.Container_origin, f.Container_size));
240                 me.leaveSubitem(me);
241                 return r;
242         }
243         return 0;
244 }
245 float Container_mouseDrag(entity me, vector pos)
246 {
247         entity f;
248         float r;
249         f = me.focusedChild;
250         if(f)
251         {
252                 me.enterSubitem(me, f);
253                 r = f.mouseDrag(f, globalToBox(pos, f.Container_origin, f.Container_size));
254                 me.leaveSubitem(me);
255                 return r;
256         }
257         return 0;
258 }
259 float Container_mouseRelease(entity me, vector pos)
260 {
261         entity f;
262         float r;
263         f = me.focusedChild;
264         if(f)
265         {
266                 me.enterSubitem(me, f);
267                 r = f.mouseRelease(f, globalToBox(pos, f.Container_origin, f.Container_size));
268                 me.leaveSubitem(me);
269                 return r;
270         }
271         return 0;
272 }
273
274 void Container_addItemCentered(entity me, entity other, vector theSize, float theAlpha)
275 {
276         me.addItem(me, other, '0.5 0.5 0' - 0.5 * theSize, theSize, theAlpha);
277 }
278
279 void Container_addItem(entity me, entity other, vector theOrigin, vector theSize, float theAlpha)
280 {
281         if(other.parent)
282                 error("Can't add already added item!");
283
284         if(other.focusable)
285                 me.focusable += 1;
286
287         if(theSize_x > 1)
288         {
289                 theOrigin_x -= 0.5 * (theSize_x - 1);
290                 theSize_x = 1;
291         }
292         if(theSize_y > 1)
293         {
294                 theOrigin_y -= 0.5 * (theSize_y - 1);
295                 theSize_y = 1;
296         }
297         theOrigin_x = bound(0, theOrigin_x, 1 - theSize_x);
298         theOrigin_y = bound(0, theOrigin_y, 1 - theSize_y);
299
300         other.parent = me;
301         other.Container_origin = theOrigin;
302         other.Container_size = theSize;
303         me.setAlphaOf(me, other, theAlpha);
304
305         entity f, l;
306         f = me.firstChild;
307         l = me.lastChild;
308
309         if(l)
310                 l.nextSibling = other;
311         else
312                 me.firstChild = other;
313
314         other.prevSibling = l;
315         other.nextSibling = NULL;
316         me.lastChild = other;
317
318         draw_NeedResizeNotify = 1;
319 }
320
321 void Container_removeItem(entity me, entity other)
322 {
323         if(other.parent != me)
324                 error("Can't remove from wrong container!");
325
326         if(other.focusable)
327                 me.focusable -= 1;
328
329         other.parent = NULL;
330
331         entity n, p, f, l;
332         f = me.firstChild;
333         l = me.lastChild;
334         n = other.nextSibling;
335         p = other.prevSibling;
336
337         if(p)
338                 p.nextSibling = n;
339         else
340                 me.firstChild = n;
341
342         if(n)
343                 n.prevSibling = p;
344         else
345                 me.lastChild = p;
346 }
347
348 void Container_setFocus(entity me, entity other)
349 {
350         if(other)
351                 if not(me.focused)
352                         error("Trying to set focus in a non-focused control!");
353         if(me.focusedChild == other)
354                 return;
355         //print(etos(me), ": focus changes from ", etos(me.focusedChild), " to ", etos(other), "\n");
356         if(me.focusedChild)
357         {
358                 me.focusedChild.focused = 0;
359                 me.focusedChild.focusLeave(me.focusedChild);
360         }
361         if(other)
362         {
363                 other.focused = 1;
364                 other.focusEnter(other);
365         }
366         me.focusedChild = other;
367 }
368
369 void Container_moveItemAfter(entity me, entity other, entity dest)
370 {
371         // first: remove other from the chain
372         entity n, p, f, l;
373
374         if(other.parent != me)
375                 error("Can't move in wrong container!");
376
377         f = me.firstChild;
378         l = me.lastChild;
379         n = other.nextSibling;
380         p = other.prevSibling;
381
382         if(p)
383                 p.nextSibling = n;
384         else
385                 me.firstChild = n;
386
387         if(n)
388                 n.prevSibling = p;
389         else
390                 me.lastChild = p;
391         
392         // now other got removed. Insert it behind dest now.
393         other.prevSibling = dest;
394         if(dest)
395                 other.nextSibling = dest.nextSibling;
396         else
397                 other.nextSibling = me.firstChild;
398
399         if(dest)
400                 dest.nextSibling = other;
401         else
402                 me.firstChild = other;
403
404         if(other.nextSibling)
405                 other.nextSibling.prevSibling = other;
406         else
407                 me.lastChild = other;
408 }
409
410 entity Container_preferredFocusedGrandChild(entity me)
411 {
412         entity e, e2;
413         entity best;
414
415         best = NULL;
416
417         for(e = me.firstChild; e; e = e.nextSibling)
418         {
419                 if(e.instanceOfContainer)
420                 {
421                         e2 = e.preferredFocusedGrandChild(e);
422                         if(e2)
423                                 if(!best || best.preferredFocusPriority < e2.preferredFocusPriority)
424                                         best = e2;
425                 }
426                 if(e)
427                         if(!best || best.preferredFocusPriority < e.preferredFocusPriority)
428                                 best = e;
429         }
430
431         return best;
432 }
433 #endif