1 #include "modalcontroller.qh"
2 #ifndef ITEM_MODALCONTROLLER_H
3 #define ITEM_MODALCONTROLLER_H
4 #include "container.qc"
5 CLASS(ModalController, Container)
6 METHOD(ModalController, resizeNotify, void(entity, vector, vector, vector, vector));
7 METHOD(ModalController, draw, void(entity));
8 METHOD(ModalController, showChild, void(entity, entity, vector, vector, float));
9 METHOD(ModalController, hideChild, void(entity, entity, float));
10 METHOD(ModalController, hideAll, void(entity, float));
11 METHOD(ModalController, addItem, void(entity, entity, vector, vector, float));
12 METHOD(ModalController, addTab, void(entity, entity, entity));
14 METHOD(ModalController, initializeDialog, void(entity, entity));
16 METHOD(ModalController, switchState, void(entity, entity, float, float));
17 ATTRIB(ModalController, origin, vector, '0 0 0')
18 ATTRIB(ModalController, size, vector, '0 0 0')
19 ATTRIB(ModalController, previousButton, entity, NULL)
20 ATTRIB(ModalController, fadedAlpha, float, 0.3)
21 ENDCLASS(ModalController)
23 .entity tabSelectingButton;
26 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
27 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
28 void DialogOpenButton_Click_withCoords(entity button, entity tab, vector theOrigin, vector theSize);
29 void DialogCloseButton_Click(entity button, entity tab); // assumes a button has set the above fields to the tab to close
34 // modal dialog controller
35 // handles a stack of dialog elements
36 // each element can have one of the following states:
37 // 0: hidden (fading out)
38 // 1: visible (zooming in)
39 // 2: greyed out (inactive)
40 // While an animation is running, no item has focus. When an animation is done,
41 // the topmost item gets focus.
42 // The items are assumed to be added in overlapping order, that is, the lowest
43 // window must get added first.
46 // - to control a modal dialog:
47 // - show modal dialog: me.showChild(me, childItem, buttonAbsOrigin, buttonAbsSize, 0) // childItem also gets focus
48 // - dismiss modal dialog: me.hideChild(me, childItem, 0) // childItem fades out and relinquishes focus
49 // - show first screen in m_show: me.hideAll(me, 1); me.showChild(me, me.firstChild, '0 0 0', '0 0 0', 1);
50 // - 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);
51 // - as a tabbed dialog control:
52 // - to initialize: me.hideAll(me, 1); me.showChild(me, me.firstChild, '0 0 0', '0 0 0', 1);
53 // - to show a tab: me.hideChild(me, currentTab, 0); me.showChild(me, newTab, buttonAbsOrigin, buttonAbsSize, 0);
55 .vector ModalController_initialSize;
56 .vector ModalController_initialOrigin;
57 .vector ModalController_initialFontScale;
58 .float ModalController_initialAlpha;
59 .vector ModalController_buttonSize;
60 .vector ModalController_buttonOrigin;
61 .float ModalController_state;
62 .float ModalController_factor;
63 .entity ModalController_controllingButton;
65 void ModalController_initializeDialog(entity me, entity root)
68 me.showChild(me, root, '0 0 0', '0 0 0', 1); // someone else animates for us
71 void TabButton_Click(entity button, entity tab)
73 if (tab.ModalController_state == 1) return;
74 tab.parent.hideAll(tab.parent, 0);
75 button.forcePressed = 1;
76 tab.ModalController_controllingButton = button;
77 tab.parent.showChild(tab.parent, tab, button.origin, button.size, 0);
80 void DialogOpenButton_Click(entity button, entity tab)
82 DialogOpenButton_Click_withCoords(button, tab, button.origin, button.size);
85 void DialogOpenButton_Click_withCoords(entity button, entity tab, vector theOrigin, vector theSize)
87 if (tab.ModalController_state) return;
88 if (button) button.forcePressed = 1;
89 if (tab.parent.focusedChild) tab.parent.focusedChild.saveFocus(tab.parent.focusedChild);
90 tab.ModalController_controllingButton = button;
91 tab.parent.showChild(tab.parent, tab, theOrigin, theSize, 0);
94 void DialogCloseButton_Click(entity button, entity tab)
96 tab.parent.hideChild(tab.parent, tab, 0);
99 void ModalController_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
101 me.resizeNotifyLie(me, relOrigin, relSize, absOrigin, absSize, ModalController_initialOrigin, ModalController_initialSize, ModalController_initialFontScale);
104 void ModalController_switchState(entity me, entity other, float state, float skipAnimation)
107 previousState = other.ModalController_state;
108 if (state == previousState && !skipAnimation) return;
109 other.ModalController_state = state;
113 other.ModalController_factor = 1 - other.Container_alpha / other.ModalController_initialAlpha;
117 other.ModalController_factor = other.Container_alpha / other.ModalController_initialAlpha;
118 if (previousState == 0 && !skipAnimation)
120 other.Container_origin = other.ModalController_buttonOrigin;
121 other.Container_size = other.ModalController_buttonSize;
126 other.ModalController_factor = bound(0, (1 - other.Container_alpha / other.ModalController_initialAlpha) / me.fadedAlpha, 1);
127 // fading out halfway
130 if (skipAnimation) other.ModalController_factor = 1;
133 void ModalController_draw(entity me)
138 float f; // animation factor
139 float df; // animation step size
140 float prevFactor, targetFactor;
141 vector targetOrigin, targetSize;
147 for (e = me.firstChild; e; e = e.nextSibling)
148 if (e.ModalController_state)
150 if (front) me.switchState(me, front, 2, 0);
153 if (front) me.switchState(me, front, 1, 0);
155 df = frametime * 3; // animation speed
157 for (e = me.firstChild; e; e = e.nextSibling)
159 if (e.ModalController_state == 2)
161 // fading out partially
162 targetOrigin = e.Container_origin; // stay as is
163 targetSize = e.Container_size; // stay as is
164 targetAlpha = me.fadedAlpha * e.ModalController_initialAlpha;
166 else if (e.ModalController_state == 1)
169 targetOrigin = e.ModalController_initialOrigin;
170 targetSize = e.ModalController_initialSize;
171 targetAlpha = e.ModalController_initialAlpha;
176 targetOrigin = e.Container_origin; // stay as is
177 targetSize = e.Container_size; // stay as is
181 f = (e.ModalController_factor = min(1, e.ModalController_factor + df));
186 e.Container_origin = targetOrigin;
187 e.Container_size = targetSize;
188 me.setAlphaOf(me, e, targetAlpha);
192 prevFactor = (1 - f) / (1 - f + df);
193 if (!e.ModalController_state) // optimize code and avoid precision errors
195 me.setAlphaOf(me, e, e.Container_alpha * prevFactor);
200 targetFactor = df / (1 - f + df);
202 if (e.ModalController_state == 1)
204 e.Container_origin = e.Container_origin * prevFactor + targetOrigin * targetFactor;
205 e.Container_size = e.Container_size * prevFactor + targetSize * targetFactor;
207 me.setAlphaOf(me, e, e.Container_alpha * prevFactor + targetAlpha * targetFactor);
210 // assume: o == to * f_prev + X * (1 - f_prev)
211 // make: o' = to * f + X * (1 - f)
213 // X == (o - to * f_prev) / (1 - f_prev)
214 // o' = to * f + (o - to * f_prev) / (1 - f_prev) * (1 - f)
216 // o' = (to * (f - f_prev) + o * (1 - f)) / (1 - f_prev)
218 if (e.ModalController_state == 1)
220 fs = globalToBoxSize(e.Container_size, e.ModalController_initialSize);
221 e.Container_fontscale_x = fs.x * e.ModalController_initialFontScale.x;
222 e.Container_fontscale_y = fs.y * e.ModalController_initialFontScale.y;
226 if (animating || !me.focused) me.setFocus(me, NULL);
227 else me.setFocus(me, front);
228 SUPER(ModalController).draw(me);
231 void ModalController_addTab(entity me, entity other, entity tabButton)
233 me.addItem(me, other, '0 0 0', '1 1 1', 1);
234 tabButton.onClick = TabButton_Click;
235 tabButton.onClickEntity = other;
236 other.tabSelectingButton = tabButton;
237 if (other == me.firstChild)
239 tabButton.forcePressed = 1;
240 other.ModalController_controllingButton = tabButton;
241 me.showChild(me, other, '0 0 0', '0 0 0', 1);
245 void ModalController_addItem(entity me, entity other, vector theOrigin, vector theSize, float theAlpha)
247 SUPER(ModalController).addItem(me, other, theOrigin, theSize, (other == me.firstChild) ? theAlpha : 0);
248 other.ModalController_initialFontScale = other.Container_fontscale;
249 other.ModalController_initialSize = other.Container_size;
250 other.ModalController_initialOrigin = other.Container_origin;
251 other.ModalController_initialAlpha = theAlpha; // hope Container never modifies this
252 if (other.ModalController_initialFontScale == '0 0 0') other.ModalController_initialFontScale = '1 1 0';
255 void ModalController_showChild(entity me, entity other, vector theOrigin, vector theSize, float skipAnimation)
257 if (other.ModalController_state == 0 || skipAnimation)
259 me.setFocus(me, NULL);
262 other.ModalController_buttonOrigin = globalToBox(theOrigin, me.origin, me.size);
263 other.ModalController_buttonSize = globalToBoxSize(theSize, me.size);
265 me.switchState(me, other, 1, skipAnimation);
266 } // zoom in from button (factor increases)
269 void ModalController_hideAll(entity me, float skipAnimation)
272 for (e = me.firstChild; e; e = e.nextSibling)
273 me.hideChild(me, e, skipAnimation);
276 void ModalController_hideChild(entity me, entity other, float skipAnimation)
278 if (other.ModalController_state || skipAnimation)
280 me.setFocus(me, NULL);
281 me.switchState(me, other, 0, skipAnimation);
282 if (other.ModalController_controllingButton)
284 other.ModalController_controllingButton.forcePressed = 0;
285 other.ModalController_controllingButton = NULL;
287 } // just alpha fade out (factor increases and decreases alpha)