cdc8e2b9549d00e0c7e8a57351083016029e08de
[xonotic/xonotic-data.pk3dir.git] / qcsrc / menu / item / slider.qc
1 // Note:
2 //   to use this, you FIRST call configureSliderVisuals, then configureSliderValues
3 #ifndef ITEM_SLIDER_H
4 #define ITEM_SLIDER_H
5 #include "label.qc"
6 CLASS(Slider, Label)
7         METHOD(Slider, resizeNotify, void(entity, vector, vector, vector, vector));
8         METHOD(Slider, configureSliderVisuals, void(entity, float, float, float, string));
9         METHOD(Slider, configureSliderValues, void(entity, float, float, float, float, float, float));
10         METHOD(Slider, draw, void(entity));
11         METHOD(Slider, keyDown, float(entity, float, float, float));
12         METHOD(Slider, keyUp, float(entity, float, float, float));
13         METHOD(Slider, mousePress, float(entity, vector));
14         METHOD(Slider, mouseDrag, float(entity, vector));
15         METHOD(Slider, mouseRelease, float(entity, vector));
16         METHOD(Slider, valueToText, string(entity, float));
17         METHOD(Slider, toString, string(entity));
18         METHOD(Slider, setValue, void(entity, float));
19         METHOD(Slider, setSliderValue, void(entity, float));
20         METHOD(Slider, showNotify, void(entity));
21         ATTRIB(Slider, src, string, string_null)
22         ATTRIB(Slider, focusable, float, 1)
23         ATTRIB(Slider, allowFocusSound, float, 1)
24         ATTRIB(Slider, value, float, 0)
25         ATTRIB(Slider, animated, float, 1)
26         ATTRIB(Slider, sliderValue, float, 0)
27         ATTRIB(Slider, sliderAnim, entity, world)
28         ATTRIB(Slider, valueMin, float, 0)
29         ATTRIB(Slider, valueMax, float, 0)
30         ATTRIB(Slider, valueStep, float, 0)
31         ATTRIB(Slider, valueDigits, float, 0)
32         ATTRIB(Slider, valueKeyStep, float, 0)
33         ATTRIB(Slider, valuePageStep, float, 0)
34         ATTRIB(Slider, valueDisplayMultiplier, float, 1.0)
35         ATTRIB(Slider, textSpace, float, 0)
36         ATTRIB(Slider, controlWidth, float, 0)
37         ATTRIB(Slider, pressed, float, 0)
38         ATTRIB(Slider, pressOffset, float, 0)
39         ATTRIB(Slider, previousValue, float, 0)
40         ATTRIB(Slider, tolerance, vector, '0 0 0')
41         ATTRIB(Slider, disabled, float, 0)
42         ATTRIB(Slider, color, vector, '1 1 1')
43         ATTRIB(Slider, color2, vector, '1 1 1')
44         ATTRIB(Slider, colorD, vector, '1 1 1')
45         ATTRIB(Slider, colorC, vector, '1 1 1')
46         ATTRIB(Slider, colorF, vector, '1 1 1')
47         ATTRIB(Slider, disabledAlpha, float, 0.3)
48 ENDCLASS(Slider)
49 #endif
50
51 #ifdef IMPLEMENTATION
52 void Slider_setValue(entity me, float val)
53 {
54         if (me.animated) {
55                 float t = 0.5;
56                 if(me.pressed == 2)
57                         t = 0.3; // slightly more responsive while dragging the handle
58                 if(!me.sliderAnim)
59                         me.sliderAnim = makeHostedEasing(me, Slider_setSliderValue, easingQuadOut, t, me.sliderValue, val);
60                 else
61                         me.sliderAnim.update(me.sliderAnim, t, me.sliderValue, val);
62         } else {
63                 me.setSliderValue(me, val);
64         }
65         me.value = val;
66 }
67 void Slider_setSliderValue(entity me, float val)
68 {
69         me.sliderValue = val;
70 }
71 string Slider_toString(entity me)
72 {
73         return sprintf("%d (%s)", me.value, me.valueToText(me, me.value));
74 }
75 void Slider_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
76 {
77         SUPER(Slider).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
78         me.controlWidth = absSize.y / absSize.x;
79 }
80 string Slider_valueToText(entity me, float val)
81 {
82         if(almost_in_bounds(me.valueMin, val, me.valueMax))
83                 return ftos_decimals(val * me.valueDisplayMultiplier, me.valueDigits);
84         return "";
85 }
86 void Slider_configureSliderVisuals(entity me, float sz, float theAlign, float theTextSpace, string gfx)
87 {
88         SUPER(Slider).configureLabel(me, string_null, sz, theAlign);
89         me.textSpace = theTextSpace;
90         me.keepspaceLeft = (theTextSpace == 0) ? 0 : (1 - theTextSpace);
91         me.src = gfx;
92 }
93 void Slider_configureSliderValues(entity me, float theValueMin, float theValue, float theValueMax, float theValueStep, float theValueKeyStep, float theValuePageStep)
94 {
95         me.value = theValue;
96         me.sliderValue = theValue;
97         me.valueStep = theValueStep;
98         me.valueMin = theValueMin;
99         me.valueMax = theValueMax;
100         me.valueKeyStep = theValueKeyStep;
101         me.valuePageStep = theValuePageStep;
102         me.valueDigits = 3;
103         if(fabs(floor(me.valueStep * 100 + 0.5) - (me.valueStep * 100)) < 0.01) // about a whole number of 100ths
104                 me.valueDigits = 2;
105         if(fabs(floor(me.valueStep * 10 + 0.5) - (me.valueStep * 10)) < 0.01) // about a whole number of 10ths
106                 me.valueDigits = 1;
107         if(fabs(floor(me.valueStep * 1 + 0.5) - (me.valueStep * 1)) < 0.01) // about a whole number
108                 me.valueDigits = 0;
109 }
110 float Slider_keyDown(entity me, float key, float ascii, float shift)
111 {
112         float inRange;
113         if(me.disabled)
114                 return 0;
115         inRange = (almost_in_bounds(me.valueMin, me.value, me.valueMax));
116         if(key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_MWHEELDOWN)
117         {
118                 if(inRange)
119                         me.setValue(me, median(me.valueMin, me.value - me.valueKeyStep, me.valueMax));
120                 else
121                         me.setValue(me, me.valueMax);
122                 return 1;
123         }
124         if(key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_MWHEELUP)
125         {
126                 if(inRange)
127                         me.setValue(me, median(me.valueMin, me.value + me.valueKeyStep, me.valueMax));
128                 else
129                         me.setValue(me, me.valueMin);
130                 return 1;
131         }
132         if(key == K_PGDN || key == K_KP_PGDN)
133         {
134                 if(inRange)
135                         me.setValue(me, median(me.valueMin, me.value - me.valuePageStep, me.valueMax));
136                 else
137                         me.setValue(me, me.valueMax);
138                 return 1;
139         }
140         if(key == K_PGUP || key == K_KP_PGUP)
141         {
142                 if(inRange)
143                         me.setValue(me, median(me.valueMin, me.value + me.valuePageStep, me.valueMax));
144                 else
145                         me.setValue(me, me.valueMin);
146                 return 1;
147         }
148         if(key == K_HOME || key == K_KP_HOME)
149         {
150                 me.setValue(me, me.valueMin);
151                 return 1;
152         }
153         if(key == K_END || key == K_KP_END)
154         {
155                 me.setValue(me, me.valueMax);
156                 return 1;
157         }
158         // TODO more keys (NOTE also add them to Slider_keyUp)
159         return 0;
160 }
161 float Slider_keyUp(entity me, float key, float ascii, float shift)
162 {
163         if(me.disabled)
164                 return 0;
165         switch(key)
166         {
167                 case K_LEFTARROW:
168                 case K_KP_LEFTARROW:
169                 case K_RIGHTARROW:
170                 case K_KP_RIGHTARROW:
171                 case K_PGUP:
172                 case K_KP_PGUP:
173                 case K_PGDN:
174                 case K_KP_PGDN:
175                 case K_HOME:
176                 case K_KP_HOME:
177                 case K_END:
178                 case K_KP_END:
179                         m_play_click_sound(MENU_SOUND_SLIDE);
180         }
181         return 0;
182 }
183 float Slider_mouseDrag(entity me, vector pos)
184 {
185         float hit;
186         float v;
187         if(me.disabled)
188                 return 0;
189
190         if(me.pressed)
191         {
192                 hit = 1;
193                 if(pos.x < 0 - me.tolerance.x) hit = 0;
194                 if(pos.y < 0 - me.tolerance.y) hit = 0;
195                 if(pos.x >= 1 - me.textSpace + me.tolerance.x) hit = 0;
196                 if(pos.y >= 1 + me.tolerance.y) hit = 0;
197                 if(hit)
198                 {
199                         // handle dragging
200                         me.pressed = 2;
201
202                         v = median(0, (pos.x - me.pressOffset - 0.5 * me.controlWidth) / (1 - me.textSpace - me.controlWidth), 1) * (me.valueMax - me.valueMin) + me.valueMin;
203                         if(me.valueStep)
204                                 v = floor(0.5 + v / me.valueStep) * me.valueStep;
205                         me.setValue(me, v);
206                 }
207                 else
208                         me.setValue(me, me.previousValue);
209         }
210
211         return 1;
212 }
213 float Slider_mousePress(entity me, vector pos)
214 {
215         float controlCenter;
216         if(me.disabled)
217                 return 0;
218         if(pos.x < 0) return 0;
219         if(pos.y < 0) return 0;
220         if(pos.x >= 1 - me.textSpace) return 0;
221         if(pos.y >= 1) return 0;
222         controlCenter = (me.value - me.valueMin) / (me.valueMax - me.valueMin) * (1 - me.textSpace - me.controlWidth) + 0.5 * me.controlWidth;
223         if(fabs(pos.x - controlCenter) <= 0.5 * me.controlWidth)
224         {
225                 me.pressed = 1;
226                 me.pressOffset = pos.x - controlCenter;
227                 me.previousValue = me.value;
228                 //me.mouseDrag(me, pos);
229         }
230         else
231         {
232                 float clickValue, pageValue, inRange;
233                 clickValue = median(0, (pos.x - me.pressOffset - 0.5 * me.controlWidth) / (1 - me.textSpace - me.controlWidth), 1) * (me.valueMax - me.valueMin) + me.valueMin;
234                 inRange = (almost_in_bounds(me.valueMin, me.value, me.valueMax));
235                 if(pos.x < controlCenter)
236                 {
237                         pageValue = me.value - me.valuePageStep;
238                         if(me.valueStep)
239                                 clickValue = floor(clickValue / me.valueStep) * me.valueStep;
240                         pageValue = max(pageValue, clickValue);
241                         if(inRange)
242                                 me.setValue(me, median(me.valueMin, pageValue, me.valueMax));
243                         else
244                                 me.setValue(me, me.valueMax);
245                 }
246                 else
247                 {
248                         pageValue = me.value + me.valuePageStep;
249                         if(me.valueStep)
250                                 clickValue = ceil(clickValue / me.valueStep) * me.valueStep;
251                         pageValue = min(pageValue, clickValue);
252                         if(inRange)
253                                 me.setValue(me, median(me.valueMin, pageValue, me.valueMax));
254                         else
255                                 me.setValue(me, me.valueMax);
256                 }
257                 if(pageValue == clickValue)
258                 {
259                         controlCenter = (me.value - me.valueMin) / (me.valueMax - me.valueMin) * (1 - me.textSpace - me.controlWidth) + 0.5 * me.controlWidth;
260                         me.pressed = 1;
261                         me.pressOffset = pos.x - controlCenter;
262                         me.previousValue = me.value;
263                         //me.mouseDrag(me, pos);
264                 }
265         }
266         return 1;
267 }
268 float Slider_mouseRelease(entity me, vector pos)
269 {
270         me.pressed = 0;
271         if(me.disabled)
272                 return 0;
273         m_play_click_sound(MENU_SOUND_SLIDE);
274         return 1;
275 }
276 void Slider_showNotify(entity me)
277 {
278         me.focusable = !me.disabled;
279 }
280 void Slider_draw(entity me)
281 {
282         float controlLeft;
283         float save;
284         me.focusable = !me.disabled;
285         save = draw_alpha;
286         if(me.disabled)
287                 draw_alpha *= me.disabledAlpha;
288         draw_ButtonPicture('0 0 0', strcat(me.src, "_s"), eX * (1 - me.textSpace) + eY, me.color2, 1);
289         if(almost_in_bounds(me.valueMin, me.sliderValue, me.valueMax))
290         {
291                 controlLeft = (me.sliderValue - me.valueMin) / (me.valueMax - me.valueMin) * (1 - me.textSpace - me.controlWidth);
292                 if(me.disabled)
293                         draw_Picture(eX * controlLeft, strcat(me.src, "_d"), eX * me.controlWidth + eY, me.colorD, 1);
294                 else if(me.pressed)
295                         draw_Picture(eX * controlLeft, strcat(me.src, "_c"), eX * me.controlWidth + eY, me.colorC, 1);
296                 else if(me.focused)
297                         draw_Picture(eX * controlLeft, strcat(me.src, "_f"), eX * me.controlWidth + eY, me.colorF, 1);
298                 else
299                         draw_Picture(eX * controlLeft, strcat(me.src, "_n"), eX * me.controlWidth + eY, me.color, 1);
300         }
301
302         if(me.sliderAnim)
303         if(me.sliderAnim.isFinished(me.sliderAnim))
304         {
305                 anim.removeObjAnim(anim, me);
306                 me.sliderAnim = world;
307         }
308
309         me.setText(me, me.valueToText(me, me.value));
310         draw_alpha = save;
311         SUPER(Slider).draw(me);
312         me.text = string_null; // TEMPSTRING!
313 }
314 #endif