]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/menu/item/modalcontroller.c
Initial checkout of Vore Tournament 0.1.alpha.
[voretournament/voretournament.git] / data / qcsrc / menu / item / modalcontroller.c
1 #ifdef INTERFACE\r
2 CLASS(ModalController) EXTENDS(Container)\r
3         METHOD(ModalController, resizeNotify, void(entity, vector, vector, vector, vector))\r
4         METHOD(ModalController, draw, void(entity))\r
5         METHOD(ModalController, addItem, void(entity, entity, vector, vector, float))\r
6         METHOD(ModalController, showChild, void(entity, entity, vector, vector, float))\r
7         METHOD(ModalController, hideChild, void(entity, entity, float))\r
8         METHOD(ModalController, hideAll, void(entity, float))\r
9         METHOD(ModalController, addItem, void(entity, entity, vector, vector, float))\r
10         METHOD(ModalController, addTab, void(entity, entity, entity))\r
11 \r
12         METHOD(ModalController, initializeDialog, void(entity, entity))\r
13 \r
14         METHOD(ModalController, switchState, void(entity, entity, float, float))\r
15         ATTRIB(ModalController, origin, vector, '0 0 0')\r
16         ATTRIB(ModalController, size, vector, '0 0 0')\r
17         ATTRIB(ModalController, previousButton, entity, NULL)\r
18         ATTRIB(ModalController, fadedAlpha, float, 0.3)\r
19 ENDCLASS(ModalController)\r
20 \r
21 .entity tabSelectingButton;\r
22 .vector origin;\r
23 .vector size;\r
24 void TabButton_Click(entity button, entity tab); // assumes a button has set the above fields to its own absolute origin, its size, and the tab to activate\r
25 void DialogOpenButton_Click(entity button, entity tab); // assumes a button has set the above fields to its own absolute origin, its size, and the tab to activate\r
26 void DialogOpenButton_Click_withCoords(entity button, entity tab, vector theOrigin, vector theSize);\r
27 void DialogCloseButton_Click(entity button, entity tab); // assumes a button has set the above fields to the tab to close\r
28 #endif\r
29 \r
30 #ifdef IMPLEMENTATION\r
31 \r
32 // modal dialog controller\r
33 // handles a stack of dialog elements\r
34 // each element can have one of the following states:\r
35 //   0: hidden (fading out)\r
36 //   1: visible (zooming in)\r
37 //   2: greyed out (inactive)\r
38 // While an animation is running, no item has focus. When an animation is done,\r
39 // the topmost item gets focus.\r
40 // The items are assumed to be added in overlapping order, that is, the lowest\r
41 // window must get added first.\r
42 //\r
43 // Possible uses:\r
44 // - to control a modal dialog:\r
45 //   - show modal dialog: me.showChild(me, childItem, buttonAbsOrigin, buttonAbsSize, 0) // childItem also gets focus\r
46 //   - dismiss modal dialog: me.hideChild(me, childItem, 0) // childItem fades out and relinquishes focus\r
47 //   - show first screen in m_show: me.hideAll(me, 1); me.showChild(me, me.firstChild, '0 0 0', '0 0 0', 1);\r
48 // - to show a temporary dialog instead of the menu (teamselect): me.hideAll(me, 1); me.showChild(me, teamSelectDialog, '0 0 0', '0 0 0', 1);\r
49 // - as a tabbed dialog control:\r
50 //   - to initialize: me.hideAll(me, 1); me.showChild(me, me.firstChild, '0 0 0', '0 0 0', 1);\r
51 //   - to show a tab: me.hideChild(me, currentTab, 0); me.showChild(me, newTab, buttonAbsOrigin, buttonAbsSize, 0);\r
52 \r
53 .vector ModalController_initialSize;\r
54 .vector ModalController_initialOrigin;\r
55 .float ModalController_initialAlpha;\r
56 .vector ModalController_buttonSize;\r
57 .vector ModalController_buttonOrigin;\r
58 .float ModalController_state;\r
59 .float ModalController_factor;\r
60 .entity ModalController_controllingButton;\r
61 \r
62 void initializeDialogModalController(entity me, entity root)\r
63 {\r
64         me.hideAll(me, 1);\r
65         me.showChild(me, root, '0 0 0', '0 0 0', 1); // someone else animates for us\r
66 }\r
67 \r
68 void TabButton_Click(entity button, entity tab)\r
69 {\r
70         if(tab.ModalController_state == 1)\r
71                 return;\r
72         tab.parent.hideAll(tab.parent, 0);\r
73         button.forcePressed = 1;\r
74         tab.ModalController_controllingButton = button;\r
75         tab.parent.showChild(tab.parent, tab, button.origin, button.size, 0);\r
76 }\r
77 \r
78 void DialogOpenButton_Click(entity button, entity tab)\r
79 {\r
80         DialogOpenButton_Click_withCoords(button, tab, button.origin, button.size);\r
81 }\r
82 \r
83 void DialogOpenButton_Click_withCoords(entity button, entity tab, vector theOrigin, vector theSize)\r
84 {\r
85         if(tab.ModalController_state)\r
86                 return;\r
87         if(button)\r
88                 button.forcePressed = 1;\r
89         tab.ModalController_controllingButton = button;\r
90         tab.parent.showChild(tab.parent, tab, theOrigin, theSize, 0);\r
91 }\r
92 \r
93 void DialogCloseButton_Click(entity button, entity tab)\r
94 {\r
95         tab.parent.hideChild(tab.parent, tab, 0);\r
96 }\r
97 \r
98 void resizeNotifyModalController(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)\r
99 {\r
100         me.resizeNotifyLie(me, relOrigin, relSize, absOrigin, absSize, ModalController_initialOrigin, ModalController_initialSize);\r
101 }\r
102 \r
103 void switchStateModalController(entity me, entity other, float state, float skipAnimation)\r
104 {\r
105         float previousState;\r
106         previousState = other.ModalController_state;\r
107         if(state == previousState && !skipAnimation)\r
108                 return;\r
109         other.ModalController_state = state;\r
110         switch(state)\r
111         {\r
112                 case 0:\r
113                         other.ModalController_factor = 1 - other.Container_alpha / other.ModalController_initialAlpha;\r
114                         // fading out\r
115                         break;\r
116                 case 1:\r
117                         other.ModalController_factor = other.Container_alpha / other.ModalController_initialAlpha;\r
118                         if(previousState == 0 && !skipAnimation)\r
119                         {\r
120                                 other.Container_origin = other.ModalController_buttonOrigin;\r
121                                 other.Container_size = other.ModalController_buttonSize;\r
122                         }\r
123                         // zooming in\r
124                         break;\r
125                 case 2:\r
126                         other.ModalController_factor = bound(0, (1 - other.Container_alpha / other.ModalController_initialAlpha) / me.fadedAlpha, 1);\r
127                         // fading out halfway\r
128                         break;\r
129         }\r
130         if(skipAnimation)\r
131                 other.ModalController_factor = 1;\r
132 }\r
133 \r
134 void drawModalController(entity me)\r
135 {\r
136         entity e;\r
137         entity front;\r
138         float animating;\r
139         float f; // animation factor\r
140         float df; // animation step size\r
141         float prevFactor, targetFactor;\r
142         vector targetOrigin, targetSize; float targetAlpha;\r
143         animating = 0;\r
144 \r
145         for(e = me.firstChild; e; e = e.nextSibling)\r
146                 if(e.ModalController_state)\r
147                 {\r
148                         if(front)\r
149                                 me.switchState(me, front, 2, 0);\r
150                         front = e;\r
151                 }\r
152         if(front)\r
153                 me.switchState(me, front, 1, 0);\r
154 \r
155         df = frametime * 3; // animation speed\r
156 \r
157         for(e = me.firstChild; e; e = e.nextSibling)\r
158         {\r
159                 f = (e.ModalController_factor = min(1, e.ModalController_factor + df));\r
160                 if(e.ModalController_state)\r
161                         if(f < 1)\r
162                                 animating = 1;\r
163 \r
164                 if(f < 1)\r
165                 {\r
166                         prevFactor   = (1 - f) / (1 - f + df);\r
167                         targetFactor =     df  / (1 - f + df);\r
168                 }\r
169                 else\r
170                 {\r
171                         prevFactor = 0;\r
172                         targetFactor = 1;\r
173                 }\r
174 \r
175                 if(e.ModalController_state == 2)\r
176                 {\r
177                         // fading out partially\r
178                         targetOrigin = e.Container_origin; // stay as is\r
179                         targetSize = e.Container_size; // stay as is\r
180                         targetAlpha = me.fadedAlpha * e.ModalController_initialAlpha;\r
181                 }\r
182                 else if(e.ModalController_state == 1)\r
183                 {\r
184                         // zooming in\r
185                         targetOrigin = e.ModalController_initialOrigin;\r
186                         targetSize = e.ModalController_initialSize;\r
187                         targetAlpha = e.ModalController_initialAlpha;\r
188                 }\r
189                 else\r
190                 {\r
191                         // fading out\r
192                         if(f < 1)\r
193                                 animating = 1;\r
194                         targetOrigin = e.Container_origin; // stay as is\r
195                         targetSize = e.Container_size; // stay as is\r
196                         targetAlpha = 0;\r
197                 }\r
198 \r
199                 if(f == 1)\r
200                 {\r
201                         e.Container_origin = targetOrigin;\r
202                         e.Container_size = targetSize;\r
203                         me.setAlphaOf(me, e, targetAlpha);\r
204                 }\r
205                 else\r
206                 {\r
207                         e.Container_origin = e.Container_origin * prevFactor + targetOrigin * targetFactor;\r
208                         e.Container_size   = e.Container_size   * prevFactor + targetSize   * targetFactor;\r
209                         me.setAlphaOf(me, e, e.Container_alpha  * prevFactor + targetAlpha  * targetFactor);\r
210                 }\r
211                 // assume: o == to * f_prev + X * (1 - f_prev)\r
212                 // make:   o' = to * f  + X * (1 - f)\r
213                 // -->\r
214                 // X == (o - to * f_prev) / (1 - f_prev)\r
215                 // o' = to * f + (o - to * f_prev) / (1 - f_prev) * (1 - f)\r
216                 // --> (maxima)\r
217                 // o' = (to * (f - f_prev) + o * (1 - f)) / (1 - f_prev)\r
218 \r
219                 e.Container_fontscale = globalToBoxSize(e.Container_size, e.ModalController_initialSize);\r
220         }\r
221         if(animating || !me.focused)\r
222                 me.setFocus(me, NULL);\r
223         else\r
224                 me.setFocus(me, front);\r
225         drawContainer(me);\r
226 };\r
227 \r
228 void addTabModalController(entity me, entity other, entity tabButton)\r
229 {\r
230         me.addItem(me, other, '0 0 0', '1 1 1', 1);\r
231         tabButton.onClick = TabButton_Click;\r
232         tabButton.onClickEntity = other;\r
233         other.tabSelectingButton = tabButton;\r
234         if(other == me.firstChild)\r
235         {\r
236                 tabButton.forcePressed = 1;\r
237                 other.ModalController_controllingButton = tabButton;\r
238                 me.showChild(me, other, '0 0 0', '0 0 0', 1);\r
239         }\r
240 }\r
241 \r
242 void addItemModalController(entity me, entity other, vector theOrigin, vector theSize, float theAlpha)\r
243 {\r
244         addItemContainer(me, other, theOrigin, theSize, (other == me.firstChild) ? theAlpha : 0);\r
245         other.ModalController_initialSize = other.Container_size;\r
246         other.ModalController_initialOrigin = other.Container_origin;\r
247         other.ModalController_initialAlpha = theAlpha; // hope Container never modifies this\r
248 }\r
249 \r
250 void showChildModalController(entity me, entity other, vector theOrigin, vector theSize, float skipAnimation)\r
251 {\r
252         if(other.ModalController_state == 0 || skipAnimation)\r
253         {\r
254                 me.setFocus(me, NULL);\r
255                 if(!skipAnimation)\r
256                 {\r
257                         other.ModalController_buttonOrigin = globalToBox(theOrigin, me.origin, me.size);\r
258                         other.ModalController_buttonSize = globalToBoxSize(theSize, me.size);\r
259                 }\r
260                 me.switchState(me, other, 1, skipAnimation);\r
261         } // zoom in from button (factor increases)\r
262 }\r
263 \r
264 void hideAllModalController(entity me, float skipAnimation)\r
265 {\r
266         entity e;\r
267         for(e = me.firstChild; e; e = e.nextSibling)\r
268                 me.hideChild(me, e, skipAnimation);\r
269 }\r
270 \r
271 void hideChildModalController(entity me, entity other, float skipAnimation)\r
272 {\r
273         if(other.ModalController_state || skipAnimation)\r
274         {\r
275                 me.setFocus(me, NULL);\r
276                 me.switchState(me, other, 0, skipAnimation);\r
277                 if(other.ModalController_controllingButton)\r
278                 {\r
279                         other.ModalController_controllingButton.forcePressed = 0;\r
280                         other.ModalController_controllingButton = NULL;\r
281                 }\r
282         } // just alpha fade out (factor increases and decreases alpha)\r
283 }\r
284 #endif\r