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