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