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