Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
listbox.qc
Go to the documentation of this file.
1#include "listbox.qh"
2
4 {
5 return me.scrollPos != me.scrollPosTarget;
6 }
7
9 {
10 // scroll doesn't work properly until itemHeight is set to the correct value
11 // at the first resizeNotify call
12 if (me.itemHeight == 1) // initial temporary value of itemHeight is 1
13 {
14 me.needScrollToItem = i;
15 return;
16 }
17
18 i = bound(0, i, me.nItems - 1);
19
20 // scroll the list to make sure the selected item is visible
21 // (even if the selected item doesn't change).
22 if (i < me.getFirstFullyVisibleItemAtScrollPos(me, me.scrollPos))
23 {
24 // above visible area
25 me.scrollPosTarget = me.getItemStart(me, i);
26 }
27 else if (i > me.getLastFullyVisibleItemAtScrollPos(me, me.scrollPos))
28 {
29 // below visible area
30 if (i == me.nItems - 1)
31 me.scrollPosTarget = me.getTotalHeight(me) - 1;
32 else
33 me.scrollPosTarget = me.getItemStart(me, i + 1) - 1;
34 }
35 }
36
37 void ListBox_setSelected(entity me, float i)
38 {
39 i = bound(0, i, me.nItems - 1);
40 me.scrollToItem(me, i);
41 me.selectedItem = i;
42 }
43 void ListBox_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
44 {
45 SUPER(ListBox).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
46 me.controlWidth = me.scrollbarWidth / absSize.x;
47 }
48 void ListBox_configureListBox(entity me, float theScrollbarWidth, float theItemHeight)
49 {
50 me.scrollbarWidth = theScrollbarWidth;
51 me.itemHeight = theItemHeight;
52 }
53
55 {
56 return me.nItems * me.itemHeight;
57 }
58 float ListBox_getItemAtPos(entity me, float pos)
59 {
60 return floor(pos / me.itemHeight);
61 }
62 float ListBox_getItemStart(entity me, float i)
63 {
64 return me.itemHeight * i;
65 }
66 float ListBox_getItemHeight(entity me, float i)
67 {
68 return me.itemHeight;
69 }
70
72 {
73 return me.getItemAtPos(me, pos + 0.999) - 1;
74 }
76 {
77 return me.getItemAtPos(me, pos + 0.001) + 1;
78 }
79
80 float ListBox_keyDown(entity me, float key, float ascii, float shift)
81 {
82 if (key == K_MWHEELUP)
83 me.scrollPosTarget = max(me.scrollPosTarget - 0.5, 0);
84 else if (key == K_MWHEELDOWN)
85 me.scrollPosTarget = min(me.scrollPosTarget + 0.5, max(0, me.getTotalHeight(me) - 1));
86 else if (key == K_PGUP || key == K_KP_PGUP)
87 {
88 if (me.selectionDoesntMatter)
89 {
90 me.scrollPosTarget = max(me.scrollPosTarget - 0.5, 0);
91 return 1;
92 }
93
94 int i = me.selectedItem;
95 float a = me.getItemHeight(me, i);
96 for ( ; ; )
97 {
98 --i;
99 if (i < 0)
100 break;
101 a += me.getItemHeight(me, i);
102 if (a >= 1)
103 break;
104 }
105 me.setSelected(me, i + 1);
106 }
107 else if (key == K_PGDN || key == K_KP_PGDN)
108 {
109 if (me.selectionDoesntMatter)
110 {
111 me.scrollPosTarget = min(me.scrollPosTarget + 0.5, me.nItems * me.itemHeight - 1);
112 return 1;
113 }
114
115 int i = me.selectedItem;
116 float a = me.getItemHeight(me, i);
117 for ( ; ; )
118 {
119 ++i;
120 if (i >= me.nItems)
121 break;
122 a += me.getItemHeight(me, i);
123 if (a >= 1)
124 break;
125 }
126 me.setSelected(me, i - 1);
127 }
128 else if (key == K_UPARROW || key == K_KP_UPARROW)
129 {
130 if (me.selectionDoesntMatter)
131 {
132 me.scrollPosTarget = max(me.scrollPosTarget - me.itemHeight, 0);
133 return 1;
134 }
135
136 me.setSelected(me, me.selectedItem - 1);
137 }
138 else if (key == K_DOWNARROW || key == K_KP_DOWNARROW)
139 {
140 if (me.selectionDoesntMatter)
141 {
142 me.scrollPosTarget = min(me.scrollPosTarget + me.itemHeight, me.nItems * me.itemHeight - 1);
143 return 1;
144 }
145
146 me.setSelected(me, me.selectedItem + 1);
147 }
148 else if (key == K_HOME || key == K_KP_HOME)
149 me.setSelected(me, 0);
150 else if (key == K_END || key == K_KP_END)
151 me.setSelected(me, me.nItems - 1);
152 else
153 return 0;
154 return 1;
155 }
157 {
158 me.mouseMoveOffset = -1;
159 if (pos.x < 0 || pos.y < 0 || pos.x >= 1 || pos.y >= 1)
160 return 0;
161
162 if (pos.x < 1 - me.controlWidth)
163 me.mouseMoveOffset = pos.y;
164 else
165 {
166 me.setFocusedItem(me, -1);
167 me.mouseMoveOffset = -1;
168 }
169 return 1;
170 }
172 {
173 me.updateControlTopBottom(me);
174 me.dragScrollPos = pos;
175 if (me.pressed == 1 && me.getTotalHeight(me) > 1)
176 {
177 if (pos.x < 1 - me.controlWidth - me.tolerance.x * me.controlWidth
178 || pos.y < 0 - me.tolerance.y
179 || pos.x >= 1 + me.tolerance.x * me.controlWidth
180 || pos.y >= 1 + me.tolerance.y)
181 me.scrollPosTarget = me.previousValue;
182 else // hit
183 {
184 // calculate new pos to v
185 float d = (pos.y - me.pressOffset) / (1 - (me.controlBottom - me.controlTop)) * (me.getTotalHeight(me) - 1);
186 me.scrollPosTarget = me.previousValue + d;
187 }
188 me.scrollPosTarget = min(me.scrollPosTarget, me.getTotalHeight(me) - 1);
189 me.scrollPosTarget = max(me.scrollPosTarget, 0);
190 }
191 else if (me.pressed == 2)
192 {
193 int clickeditem = me.getItemAtPos(me, me.scrollPos + pos.y);
194 me.setSelected(me, clickeditem);
195 me.setFocusedItem(me, clickeditem);
196 me.mouseMoveOffset = -1;
197 }
198 return 1;
199 }
201 {
202 if (pos.x < 0 || pos.y < 0 || pos.x >= 1 || pos.y >= 1)
203 return false;
204
205 this.dragScrollPos = pos;
206 this.updateControlTopBottom(this);
207 if (pos.x >= 1 - this.controlWidth)
208 {
209 // if hit, set this.pressed, otherwise scroll by one page
210 if (pos.y < this.controlTop)
211 {
212 // page up
213 this.scrollPosTarget = max(this.scrollPosTarget - 1, 0);
214 }
215 else if (pos.y > this.controlBottom)
216 {
217 // page down
218 this.scrollPosTarget = min(this.scrollPosTarget + 1, this.getTotalHeight(this) - 1);
219 }
220 else
221 {
222 this.pressed = 1;
223 this.pressOffset = pos.y;
224 this.previousValue = this.scrollPos;
225 }
226 }
227 else
228 {
229 // continue doing that while dragging (even when dragging outside). When releasing, forward the click to the then selected item.
230 this.pressed = 2;
231 // an item has been clicked. Select it, ...
232 int clickeditem = this.getItemAtPos(this, this.scrollPos + pos.y);
233 this.setSelected(this, clickeditem);
234 this.setFocusedItem(this, clickeditem);
235 }
236 return true;
237 }
239 {
240 float focusedItem_save = me.focusedItem;
241 me.focusedItem = (item >= 0 && item < me.nItems) ? item : -1;
242 if (focusedItem_save != me.focusedItem)
243 {
244 me.focusedItemChangeNotify(me);
245 if (me.focusedItem >= 0)
246 me.focusedItemAlpha = SKINALPHA_LISTBOX_FOCUSED;
247 }
248 }
250 {
251 if (me.pressed == 1)
252 {
253 // slider dragging mode
254 // in that case, nothing happens on releasing
255 }
256 else if (me.pressed == 2)
257 {
258 me.pressed = 3; // do that here, so setSelected can know the mouse has been released
259 // item dragging mode
260 // select current one one last time...
261 int clickeditem = me.getItemAtPos(me, me.scrollPos + pos.y);
262 me.setSelected(me, clickeditem);
263 me.setFocusedItem(me, clickeditem);
264 // and give it a nice click event
265 if (me.nItems > 0)
266 {
267 vector where = globalToBox(pos, eY * (me.getItemStart(me, me.selectedItem) - me.scrollPos), eX * (1 - me.controlWidth) + eY * me.getItemHeight(me, me.selectedItem));
268
269 if ((me.selectedItem == me.lastClickedItem && clickeditem == me.selectedItem) && (time < me.lastClickedTime + 0.3))
270 me.doubleClickListBoxItem(me, me.selectedItem, where);
271 else
272 me.clickListBoxItem(me, me.selectedItem, where);
273
274 me.lastClickedItem = me.selectedItem;
275 me.lastClickedTime = time;
276 }
277 }
278 me.pressed = 0;
279 return 1;
280 }
282 {
283 // Reset the var pressed in case listbox loses focus
284 // by a mouse click on an item of the list
285 // for example showing a dialog on right click
286 me.pressed = 0;
287 me.setFocusedItem(me, -1);
288 me.mouseMoveOffset = -1;
289 }
291 {
292 // scrollPos is in 0..1 and indicates where the "page" currently shown starts.
293 if (me.getTotalHeight(me) <= 1)
294 {
295 // we don't need no stinkin' scrollbar, we don't need no view control...
296 me.controlTop = 0;
297 me.controlBottom = 1;
298 me.scrollPos = 0;
299 }
300 else
301 {
302 // if scroll pos is above beginning or below end of list, fix it
303 me.scrollPos = bound(0, me.scrollPos, me.getTotalHeight(me) - 1);
304 // now that we know where the list is scrolled to, find out where to draw the control
305 me.controlTop = max(0, me.scrollPos / me.getTotalHeight(me));
306 me.controlBottom = min((me.scrollPos + 1) / me.getTotalHeight(me), 1);
307
308 const float minfactor = 2 * me.controlWidth / me.size.y * me.size.x;
309 float f = me.controlBottom - me.controlTop;
310 if (f < minfactor) // FIXME good default?
311 {
312 // f * X + 1 * (1-X) = minfactor
313 // (f - 1) * X + 1 = minfactor
314 // (f - 1) * X = minfactor - 1
315 // X = (minfactor - 1) / (f - 1)
316 f = (minfactor - 1) / (f - 1);
317 me.controlTop = me.controlTop * f + 0 * (1 - f);
318 me.controlBottom = me.controlBottom * f + 1 * (1 - f);
319 }
320 }
321 }
322 AUTOCVAR(menu_scroll_averaging_time, float, 0.16, "smooth scroll averaging time");
323 // scroll faster while dragging the scrollbar
324 AUTOCVAR(menu_scroll_averaging_time_pressed, float, 0.06, "smooth scroll averaging time when dragging the scrollbar");
326 {
327 vector fillSize = '0 0 0';
328
329 // we can't do this in mouseMove as the list can scroll without moving the cursor
330 if (me.mouseMoveOffset != -1)
331 me.setFocusedItem(me, me.getItemAtPos(me, me.scrollPos + me.mouseMoveOffset));
332
333 if (me.needScrollToItem >= 0)
334 {
335 me.scrollToItem(me, me.needScrollToItem);
336 me.needScrollToItem = -1;
337 }
338 if (me.scrollPos != me.scrollPosTarget)
339 {
340 float averaging_time = (me.pressed == 1)
341 ? autocvar_menu_scroll_averaging_time_pressed
342 : autocvar_menu_scroll_averaging_time;
343 // this formula works with whatever framerate
344 float f = averaging_time ? exp(-frametime / averaging_time) : 0;
345 me.scrollPos = me.scrollPos * f + me.scrollPosTarget * (1 - f);
346 if (fabs(me.scrollPos - me.scrollPosTarget) < 0.001)
347 me.scrollPos = me.scrollPosTarget;
348 }
349
350 if (me.pressed == 2)
351 me.mouseDrag(me, me.dragScrollPos); // simulate mouseDrag event
352 me.updateControlTopBottom(me);
353 fillSize.x = (1 - me.controlWidth);
354 if (me.alphaBG)
355 draw_Fill('0 0 0', '0 1 0' + fillSize, me.colorBG, me.alphaBG);
356 if (me.controlWidth)
357 {
358 draw_VertButtonPicture(eX * (1 - me.controlWidth), strcat(me.src, "_s"), eX * me.controlWidth + eY, me.color2, 1);
359 if (me.getTotalHeight(me) > 1)
360 {
361 vector o, s;
362 o = eX * (1 - me.controlWidth) + eY * me.controlTop;
363 s = eX * me.controlWidth + eY * (me.controlBottom - me.controlTop);
364 if (me.pressed == 1)
365 draw_VertButtonPicture(o, strcat(me.src, "_c"), s, me.colorC, 1);
366 else if (me.focused)
367 draw_VertButtonPicture(o, strcat(me.src, "_f"), s, me.colorF, 1);
368 else
369 draw_VertButtonPicture(o, strcat(me.src, "_n"), s, me.color, 1);
370 }
371 }
372 draw_SetClip();
373 vector oldshift = draw_shift;
374 vector oldscale = draw_scale;
375
376 int i = me.getItemAtPos(me, me.scrollPos);
377 float y = me.getItemStart(me, i) - me.scrollPos;
378 for ( ; i < me.nItems && y < 1; ++i)
379 {
380 draw_shift = boxToGlobal(eY * y, oldshift, oldscale);
381 vector relSize = eX * (1 - me.controlWidth) + eY * me.getItemHeight(me, i);
382 vector absSize = boxToGlobalSize(relSize, me.size);
383 draw_scale = boxToGlobalSize(relSize, oldscale);
384 me.drawListBoxItem(me, i, absSize, (me.selectedItem == i), (me.focusedItem == i));
385 y += relSize.y;
386 }
388
389 draw_shift = oldshift;
390 draw_scale = oldscale;
391 SUPER(ListBox).draw(me);
392 }
393
396
397 void ListBox_clickListBoxItem(entity me, float i, vector where)
398 {
399 // template method
400 }
401
403 {
404 // template method
405 }
406
407 void ListBox_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused)
408 {
409 // should be overridden
410 draw_Text('0 0 0', sprintf(_("Item %d"), i), eX * (8 / absSize.x) + eY * (8 / absSize.y), (isSelected ? '0 1 0' : '1 1 1'), 1, 0);
411 }
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
virtual void setSelected()
virtual void getTotalHeight()
virtual void mousePress()
Definition listbox.qc:200
vector dragScrollPos
Definition listbox.qh:36
float pressOffset
Definition listbox.qh:30
virtual void getItemAtPos()
virtual void updateControlTopBottom()
float pressed
Definition listbox.qh:29
float previousValue
Definition listbox.qh:28
float scrollPosTarget
Definition listbox.qh:24
virtual void setFocusedItem()
float scrollPos
Definition listbox.qh:23
float frametime
float time
#define AUTOCVAR(...)
Definition cvar.qh:164
v y
Definition ent_cs.qc:146
float ListBox_getLastFullyVisibleItemAtScrollPos(entity me, float pos)
Definition listbox.qc:71
void ListBox_draw(entity me)
Definition listbox.qc:325
float ListBox_mouseRelease(entity me, vector pos)
Definition listbox.qc:249
void ListBox_updateControlTopBottom(entity me)
Definition listbox.qc:290
void ListBox_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused)
Definition listbox.qc:407
void ListBox_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
Definition listbox.qc:43
void ListBox_clickListBoxItem(entity me, float i, vector where)
Definition listbox.qc:397
float ListBox_getItemAtPos(entity me, float pos)
Definition listbox.qc:58
void ListBox_doubleClickListBoxItem(entity me, float i, vector where)
Definition listbox.qc:402
void ListBox_scrollToItem(entity me, int i)
Definition listbox.qc:8
void ListBox_setSelected(entity me, float i)
Definition listbox.qc:37
bool ListBox_isScrolling(entity me)
Definition listbox.qc:3
float ListBox_getFirstFullyVisibleItemAtScrollPos(entity me, float pos)
Definition listbox.qc:75
void ListBox_configureListBox(entity me, float theScrollbarWidth, float theItemHeight)
Definition listbox.qc:48
float ListBox_getItemHeight(entity me, float i)
Definition listbox.qc:66
void ListBox_focusedItemChangeNotify(entity me)
Definition listbox.qc:394
float ListBox_mouseDrag(entity me, vector pos)
Definition listbox.qc:171
void ListBox_setFocusedItem(entity me, int item)
Definition listbox.qc:238
float ListBox_getItemStart(entity me, float i)
Definition listbox.qc:62
float ListBox_keyDown(entity me, float key, float ascii, float shift)
Definition listbox.qc:80
void ListBox_focusLeave(entity me)
Definition listbox.qc:281
float ListBox_mouseMove(entity me, vector pos)
Definition listbox.qc:156
float ListBox_getTotalHeight(entity me)
Definition listbox.qc:54
float K_UPARROW
Definition keycodes.qc:15
float K_PGDN
Definition keycodes.qc:39
float K_DOWNARROW
Definition keycodes.qc:16
float K_MWHEELDOWN
Definition keycodes.qc:133
float K_KP_UPARROW
Definition keycodes.qc:64
float K_PGUP
Definition keycodes.qc:40
float K_KP_HOME
Definition keycodes.qc:62
float K_KP_DOWNARROW
Definition keycodes.qc:53
float K_HOME
Definition keycodes.qc:41
float K_END
Definition keycodes.qc:42
float K_KP_PGDN
Definition keycodes.qc:55
float K_KP_PGUP
Definition keycodes.qc:66
float K_KP_END
Definition keycodes.qc:51
float K_MWHEELUP
Definition keycodes.qc:132
float exp(float e)
Definition mathlib.qc:70
vector globalToBox(vector v, vector theOrigin, vector theScale)
Definition draw.qc:30
void draw_VertButtonPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha)
Definition draw.qc:143
void draw_SetClip()
Definition draw.qc:345
vector boxToGlobalSize(vector v, vector theScale)
Definition draw.qc:53
void draw_Text(vector theOrigin, string theText, vector theSize, vector theColor, float theAlpha, bool allowColorCodes)
Definition draw.qc:281
vector boxToGlobal(vector v, vector theOrigin, vector theScale)
Definition draw.qc:45
void draw_Fill(vector theOrigin, vector theSize, vector theColor, float theAlpha)
Definition draw.qc:97
void draw_ClearClip()
Definition draw.qc:355
vector draw_shift
Definition draw.qh:7
vector draw_scale
Definition draw.qh:8
float bound(float min, float value, float max)
float min(float f,...)
float fabs(float f)
float floor(float f)
float max(float f,...)
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
#define SUPER(cname)
Definition oo.qh:236
#define METHOD(cname, name, prototype)
Definition oo.qh:274
vector
Definition self.qh:96
const vector eY
Definition vector.qh:44
const vector eX
Definition vector.qh:43