Listbox: highlight item under the cursor
[xonotic/xonotic-data.pk3dir.git] / qcsrc / menu / item / nexposee.qc
1 #ifdef INTERFACE
2 CLASS(Nexposee) EXTENDS(Container)
3         METHOD(Nexposee, draw, void(entity))
4         METHOD(Nexposee, keyDown, float(entity, float, float, float))
5         METHOD(Nexposee, keyUp, float(entity, float, float, float))
6         METHOD(Nexposee, mousePress, float(entity, vector))
7         METHOD(Nexposee, mouseMove, float(entity, vector))
8         METHOD(Nexposee, mouseRelease, float(entity, vector))
9         METHOD(Nexposee, mouseDrag, float(entity, vector))
10         METHOD(Nexposee, resizeNotify, void(entity, vector, vector, vector, vector))
11         METHOD(Nexposee, focusEnter, void(entity))
12         METHOD(Nexposee, close, void(entity))
13
14         ATTRIB(Nexposee, animationState, float, -1)
15         ATTRIB(Nexposee, animationFactor, float, 0)
16         ATTRIB(Nexposee, selectedChild, entity, NULL)
17         ATTRIB(Nexposee, mouseFocusedChild, entity, NULL)
18         METHOD(Nexposee, addItem, void(entity, entity, vector, vector, float))
19         METHOD(Nexposee, calc, void(entity))
20         METHOD(Nexposee, setNexposee, void(entity, entity, vector, float, float))
21         ATTRIB(Nexposee, mousePosition, vector, '0 0 0')
22         METHOD(Nexposee, pullNexposee, void(entity, entity, vector))
23 ENDCLASS(Nexposee)
24
25 void ExposeeCloseButton_Click(entity button, entity other); // un-exposees the current state
26 #endif
27
28 // animation states:
29 //   0 = thumbnails seen
30 //   1 = zooming in
31 //   2 = zoomed in
32 //   3 = zooming out
33 // animation factor: 0 = minimum theSize, 1 = maximum theSize
34
35 #ifdef IMPLEMENTATION
36
37 .vector Nexposee_initialSize;
38 .vector Nexposee_initialFontScale;
39 .vector Nexposee_initialOrigin;
40 .float Nexposee_initialAlpha;
41
42 .vector Nexposee_smallSize;
43 .vector Nexposee_smallOrigin;
44 .float Nexposee_smallAlpha;
45 .float Nexposee_mediumAlpha;
46 .vector Nexposee_scaleCenter;
47 .vector Nexposee_align;
48 .float Nexposee_animationFactor;
49
50 void Nexposee_close(entity me)
51 {
52         // user must override this
53 }
54
55 void ExposeeCloseButton_Click(entity button, entity other)
56 {
57         other.selectedChild = other.focusedChild;
58         other.setFocus(other, NULL);
59         other.animationState = 3;
60 }
61
62 void Nexposee_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
63 {
64         me.calc(me);
65         me.resizeNotifyLie(me, relOrigin, relSize, absOrigin, absSize, Nexposee_initialOrigin, Nexposee_initialSize, Nexposee_initialFontScale);
66 }
67
68 void Nexposee_Calc_Scale(entity me, float scale)
69 {
70         entity e;
71         for(e = me.firstChild; e; e = e.nextSibling)
72         {
73                 e.Nexposee_smallOrigin = (e.Nexposee_initialOrigin - e.Nexposee_scaleCenter) * scale + e.Nexposee_scaleCenter;
74                 e.Nexposee_smallSize = e.Nexposee_initialSize * scale;
75                 if(e.Nexposee_align.x > 0)
76                         e.Nexposee_smallOrigin_x = 1 - e.Nexposee_align.x * scale;
77                 if(e.Nexposee_align.x < 0)
78                         e.Nexposee_smallOrigin_x = -e.Nexposee_smallSize.x + e.Nexposee_align.x * scale;
79                 if(e.Nexposee_align.y > 0)
80                         e.Nexposee_smallOrigin_y = 1 - e.Nexposee_align.y * scale;
81                 if(e.Nexposee_align.y < 0)
82                         e.Nexposee_smallOrigin_y = -e.Nexposee_smallSize.y + e.Nexposee_align.y * scale;
83         }
84 }
85
86 void Nexposee_calc(entity me)
87 {
88         /*
89          * patented by Apple
90          * can't put that here ;)
91          */
92         float scale;
93         entity e, e2;
94         vector emins, emaxs, e2mins, e2maxs;
95
96         for(scale = 0.7;; scale *= 0.99)
97         {
98                 Nexposee_Calc_Scale(me, scale);
99
100                 for(e = me.firstChild; e; e = e.nextSibling)
101                 {
102                         emins = e.Nexposee_smallOrigin;
103                         emaxs = emins + e.Nexposee_smallSize;
104                         for(e2 = e.nextSibling; e2; e2 = e2.nextSibling)
105                         {
106                                 e2mins = e2.Nexposee_smallOrigin;
107                                 e2maxs = e2mins + e2.Nexposee_smallSize;
108
109                                 // two intervals [amins, amaxs] and [bmins, bmaxs] overlap if:
110                                 //   amins < bmins < amaxs < bmaxs
111                                 // for which suffices
112                                 //   bmins < amaxs
113                                 //   amins < bmaxs
114                                 if((e2mins.x - emaxs.x) * (emins.x - e2maxs.x) > 0) // x overlap
115                                         if((e2mins.y - emaxs.y) * (emins.y - e2maxs.y) > 0) // y overlap
116                                         {
117                                                 goto have_overlap;
118                                         }
119                         }
120                 }
121
122                 break;
123 :have_overlap
124         }
125
126         scale *= 0.95;
127
128         Nexposee_Calc_Scale(me, scale);
129 }
130
131 void Nexposee_setNexposee(entity me, entity other, vector scalecenter, float a0, float a1)
132 {
133         other.Nexposee_scaleCenter = scalecenter;
134         other.Nexposee_smallAlpha = a0;
135         me.setAlphaOf(me, other, a0);
136         other.Nexposee_mediumAlpha = a1;
137 }
138
139 void Nexposee_draw(entity me)
140 {
141         float a;
142         float a0;
143         entity e;
144         float f;
145         vector fs;
146
147         if(me.animationState == -1)
148         {
149                 me.animationState = 0;
150         }
151
152         f = min(1, frametime * 5);
153         switch(me.animationState)
154         {
155                 case 0:
156                         me.animationFactor = 0;
157                         break;
158                 case 1:
159                         me.animationFactor += f;
160                         if(me.animationFactor >= 1)
161                         {
162                                 me.animationFactor = 1;
163                                 me.animationState = 2;
164                                 SUPER(Nexposee).setFocus(me, me.selectedChild);
165                         }
166                         break;
167                 case 2:
168                         me.animationFactor = 1;
169                         break;
170                 case 3:
171                         me.animationFactor -= f;
172                         me.mouseFocusedChild = me.itemFromPoint(me, me.mousePosition);
173                         if(me.animationFactor <= 0)
174                         {
175                                 me.animationFactor = 0;
176                                 me.animationState = 0;
177                                 me.selectedChild = me.mouseFocusedChild;
178                         }
179                         break;
180         }
181
182         f = min(1, frametime * 10);
183         for(e = me.firstChild; e; e = e.nextSibling)
184         {
185                 if(e == me.selectedChild)
186                 {
187                         e.Container_origin = e.Nexposee_smallOrigin * (1 - me.animationFactor) + e.Nexposee_initialOrigin * me.animationFactor;
188                         e.Container_size = e.Nexposee_smallSize * (1 - me.animationFactor) + e.Nexposee_initialSize * me.animationFactor;
189                         e.Nexposee_animationFactor = me.animationFactor;
190                         a0 = e.Nexposee_mediumAlpha;
191                         if(me.animationState == 3)
192                                 if(e != me.mouseFocusedChild)
193                                         a0 = e.Nexposee_smallAlpha;
194                         a = a0 * (1 - me.animationFactor) + me.animationFactor;
195                 }
196                 else
197                 {
198                         // minimum theSize counts
199                         e.Container_origin = e.Nexposee_smallOrigin;
200                         e.Container_size = e.Nexposee_smallSize;
201                         e.Nexposee_animationFactor = 0;
202                         a = e.Nexposee_smallAlpha * (1 - me.animationFactor);
203                 }
204                 me.setAlphaOf(me, e, e.Container_alpha * (1 - f) + a * f);
205
206                 fs = globalToBoxSize(e.Container_size, e.Nexposee_initialSize);
207                 e.Container_fontscale_x = fs.x * e.Nexposee_initialFontScale.x;
208                 e.Container_fontscale_y = fs.y * e.Nexposee_initialFontScale.y;
209         }
210
211         SUPER(Nexposee).draw(me);
212 }
213
214 float Nexposee_mousePress(entity me, vector pos)
215 {
216         if(me.animationState == 0)
217         {
218                 me.mouseFocusedChild = NULL;
219                 Nexposee_mouseMove(me, pos);
220                 if(me.mouseFocusedChild)
221                 {
222                         m_play_click_sound(MENU_SOUND_OPEN);
223                         me.animationState = 1;
224                         SUPER(Nexposee).setFocus(me, NULL);
225                 }
226                 else
227                         me.close(me);
228                 return 1;
229         }
230         else if(me.animationState == 2)
231         {
232                 if (!(SUPER(Nexposee).mousePress(me, pos)))
233                 {
234                         m_play_click_sound(MENU_SOUND_CLOSE);
235                         me.animationState = 3;
236                         SUPER(Nexposee).setFocus(me, NULL);
237                 }
238                 return 1;
239         }
240         return 0;
241 }
242
243 float Nexposee_mouseRelease(entity me, vector pos)
244 {
245         if(me.animationState == 2)
246                 return SUPER(Nexposee).mouseRelease(me, pos);
247         return 0;
248 }
249
250 float Nexposee_mouseDrag(entity me, vector pos)
251 {
252         if(me.animationState == 2)
253                 return SUPER(Nexposee).mouseDrag(me, pos);
254         return 0;
255 }
256
257 float Nexposee_mouseMove(entity me, vector pos)
258 {
259         entity e;
260         me.mousePosition = pos;
261         e = me.mouseFocusedChild;
262         me.mouseFocusedChild = me.itemFromPoint(me, pos);
263         if(me.animationState == 2)
264                 return SUPER(Nexposee).mouseMove(me, pos);
265         if(me.animationState == 0)
266         {
267                 if(me.mouseFocusedChild)
268                         if(me.mouseFocusedChild != e || me.mouseFocusedChild != me.selectedChild)
269                                 me.selectedChild = me.mouseFocusedChild;
270                 return 1;
271         }
272         return 0;
273 }
274
275 float Nexposee_keyUp(entity me, float scan, float ascii, float shift)
276 {
277         if(me.animationState == 2)
278                 return SUPER(Nexposee).keyUp(me, scan, ascii, shift);
279         return 0;
280 }
281
282 float Nexposee_keyDown(entity me, float scan, float ascii, float shift)
283 {
284         float nexposeeKey = 0;
285         if(me.animationState == 2)
286                 if(SUPER(Nexposee).keyDown(me, scan, ascii, shift))
287                         return 1;
288         if(scan == K_TAB)
289         {
290                 if(me.animationState == 0)
291                 {
292                         if(shift & S_SHIFT)
293                         {
294                                 if(me.selectedChild)
295                                         me.selectedChild = me.selectedChild.prevSibling;
296                                 if (!me.selectedChild)
297                                         me.selectedChild = me.lastChild;
298                         }
299                         else
300                         {
301                                 if(me.selectedChild)
302                                         me.selectedChild = me.selectedChild.nextSibling;
303                                 if (!me.selectedChild)
304                                         me.selectedChild = me.firstChild;
305                         }
306                 }
307         }
308         switch(me.animationState)
309         {
310                 default:
311                 case 0:
312                 case 3:
313                         nexposeeKey = ((scan == K_SPACE) || (scan == K_ENTER) || (scan == K_KP_ENTER));
314                         break;
315                 case 1:
316                 case 2:
317                         nexposeeKey = (scan == K_ESCAPE);
318                         break;
319         }
320         if(nexposeeKey)
321         {
322                 switch(me.animationState)
323                 {
324                         default:
325                         case 0:
326                         case 3:
327                                 m_play_click_sound(MENU_SOUND_OPEN);
328                                 me.animationState = 1;
329                                 break;
330                         case 1:
331                         case 2:
332                                 m_play_click_sound(MENU_SOUND_CLOSE);
333                                 me.animationState = 3;
334                                 break;
335                 }
336                 if(me.focusedChild)
337                         me.selectedChild = me.focusedChild;
338                 if (!me.selectedChild)
339                         me.animationState = 0;
340                 SUPER(Nexposee).setFocus(me, NULL);
341                 return 1;
342         }
343         return 0;
344 }
345
346 void Nexposee_addItem(entity me, entity other, vector theOrigin, vector theSize, float theAlpha)
347 {
348         SUPER(Nexposee).addItem(me, other, theOrigin, theSize, theAlpha);
349         other.Nexposee_initialFontScale = other.Container_fontscale;
350         other.Nexposee_initialSize = other.Container_size;
351         other.Nexposee_initialOrigin = other.Container_origin;
352         other.Nexposee_initialAlpha = other.Container_alpha;
353         if(other.Nexposee_initialFontScale == '0 0 0')
354                 other.Nexposee_initialFontScale = '1 1 0';
355 }
356
357 void Nexposee_focusEnter(entity me)
358 {
359         if(me.animationState == 2)
360                 SUPER(Nexposee).setFocus(me, me.selectedChild);
361 }
362
363 void Nexposee_pullNexposee(entity me, entity other, vector theAlign)
364 {
365         other.Nexposee_align = theAlign;
366 }
367 #endif