2 CLASS(ListBox) EXTENDS(Item)
3 METHOD(ListBox, resizeNotify, void(entity, vector, vector, vector, vector))
4 METHOD(ListBox, configureListBox, void(entity, float, float))
5 METHOD(ListBox, draw, void(entity))
6 METHOD(ListBox, keyDown, float(entity, float, float, float))
7 METHOD(ListBox, mousePress, float(entity, vector))
8 METHOD(ListBox, mouseDrag, float(entity, vector))
9 METHOD(ListBox, mouseRelease, float(entity, vector))
10 ATTRIB(ListBox, focusable, float, 1)
11 ATTRIB(ListBox, selectedItem, float, 0)
12 ATTRIB(ListBox, size, vector, '0 0 0')
13 ATTRIB(ListBox, scrollPos, float, 0) // measured in window heights, fixed when needed
14 ATTRIB(ListBox, previousValue, float, 0)
15 ATTRIB(ListBox, pressed, float, 0)
16 ATTRIB(ListBox, pressOffset, float, 0)
18 METHOD(ListBox, updateControlTopBottom, void(entity))
19 ATTRIB(ListBox, controlTop, float, 0)
20 ATTRIB(ListBox, controlBottom, float, 0)
21 ATTRIB(ListBox, controlWidth, float, 0)
22 ATTRIB(ListBox, dragScrollTimer, float, 0)
23 ATTRIB(ListBox, dragScrollPos, vector, '0 0 0')
25 ATTRIB(ListBox, src, string, "") // scrollbar
26 ATTRIB(ListBox, tolerance, vector, '0 0 0') // drag tolerance
27 ATTRIB(ListBox, scrollbarWidth, float, 0) // pixels
28 ATTRIB(ListBox, nItems, float, 42)
29 ATTRIB(ListBox, itemHeight, float, 0)
30 METHOD(ListBox, drawListBoxItem, void(entity, float, vector, float)) // item number, width/height, selected
31 METHOD(ListBox, clickListBoxItem, void(entity, float, vector)) // item number, relative clickpos
36 void resizeNotifyListBox(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
39 me.controlWidth = me.scrollbarWidth / absSize_x;
41 void configureListBoxListBox(entity me, float theScrollbarWidth, float theItemHeight)
43 me.scrollbarWidth = theScrollbarWidth;
44 me.itemHeight = theItemHeight;
46 float keyDownListBox(entity me, float key, float ascii, float shift)
48 if(key == K_DOWNARROW)
50 else if(key == K_UPARROW)
52 else if(key == K_MWHEELUP)
54 me.scrollPos = max(me.scrollPos - 0.5, 0);
55 me.selectedItem = min(me.selectedItem, floor((me.scrollPos + 1) / me.itemHeight - 1));
57 else if(key == K_MWHEELDOWN)
59 me.scrollPos = min(me.scrollPos + 0.5, me.nItems * me.itemHeight - 1);
60 me.selectedItem = max(me.selectedItem, ceil(me.scrollPos / me.itemHeight));
62 else if(key == K_PGUP)
63 me.selectedItem = me.selectedItem - 1 / me.itemHeight;
64 else if(key == K_PGDN)
65 me.selectedItem = me.selectedItem + 1 / me.itemHeight;
66 else if(key == K_HOME)
69 me.selectedItem = me.nItems - 1;
72 me.selectedItem = floor(0.5 + bound(0, me.selectedItem, me.nItems - 1));
75 float mouseDragListBox(entity me, vector pos)
78 me.updateControlTopBottom(me);
79 me.dragScrollPos = pos;
83 if(pos_x < 1 - me.controlWidth - me.tolerance_y * me.controlWidth) hit = 0;
84 if(pos_y < 0 - me.tolerance_x) hit = 0;
85 if(pos_x >= 1 + me.tolerance_y * me.controlWidth) hit = 0;
86 if(pos_y >= 1 + me.tolerance_x) hit = 0;
89 // calculate new pos to v
91 delta = (pos_y - me.pressOffset) / (1 - 1 / (me.nItems * me.itemHeight)) * (me.nItems * me.itemHeight - 1);
92 me.scrollPos = me.previousValue + delta;
95 me.scrollPos = me.previousValue;
96 me.scrollPos = min(me.scrollPos, me.nItems * me.itemHeight - 1);
97 me.scrollPos = max(me.scrollPos, 0);
98 me.selectedItem = min(me.selectedItem, floor((me.scrollPos + 1) / me.itemHeight - 1));
99 me.selectedItem = max(me.selectedItem, ceil(me.scrollPos / me.itemHeight));
101 else if(me.pressed == 2)
103 me.selectedItem = (me.scrollPos + pos_y) / me.itemHeight;
104 me.selectedItem = floor(0.5 + bound(0, me.selectedItem, me.nItems - 1));
108 float mousePressListBox(entity me, vector pos)
110 if(pos_x < 0) return 0;
111 if(pos_y < 0) return 0;
112 if(pos_x >= 1) return 0;
113 if(pos_y >= 1) return 0;
114 me.dragScrollPos = pos;
115 me.updateControlTopBottom(me);
116 if(pos_x >= 1 - me.controlWidth)
118 // if hit, set me.pressed, otherwise scroll by one page
119 if(pos_y < me.controlTop)
122 me.scrollPos = max(me.scrollPos - 1, 0);
123 me.selectedItem = min(me.selectedItem, floor((me.scrollPos + 1) / me.itemHeight - 1));
125 else if(pos_y > me.controlBottom)
128 me.scrollPos = min(me.scrollPos + 1, me.nItems * me.itemHeight - 1);
129 me.selectedItem = max(me.selectedItem, ceil(me.scrollPos / me.itemHeight));
134 me.pressOffset = pos_y;
135 me.previousValue = me.scrollPos;
140 // an item has been clicked. Select it, ...
141 me.selectedItem = (me.scrollPos + pos_y) / me.itemHeight;
142 me.selectedItem = floor(0.5 + bound(0, me.selectedItem, me.nItems - 1));
143 // continue doing that while dragging (even when dragging outside). When releasing, forward the click to the then selected item.
148 float mouseReleaseListBox(entity me, vector pos)
153 // slider dragging mode
154 // in that case, nothing happens on releasing
156 else if(me.pressed == 2)
158 // item dragging mode
159 // select current one one last time...
160 me.selectedItem = (me.scrollPos + pos_y) / me.itemHeight;
161 me.selectedItem = floor(0.5 + bound(0, me.selectedItem, me.nItems - 1));
162 // and give it a nice click event
165 absSize = boxToGlobalSize(me.size, eX * (1 - me.controlWidth) + eY);
166 me.clickListBoxItem(me, me.selectedItem, globalToBox(pos, eX * (me.selectedItem * me.itemHeight - me.scrollPos), absSize));
172 void updateControlTopBottomListBox(entity me)
174 // scrollPos is in 0..1 and indicates where the "page" currently shown starts.
175 if(me.nItems * me.itemHeight <= 1)
177 // we don't need no stinkin' scrollbar, we don't need no view control...
179 me.controlBottom = 1;
184 if(frametime) // only do this in draw frames
186 me.dragScrollTimer -= frametime;
187 if(me.dragScrollTimer < 0)
191 // if selected item is below listbox, increase scrollpos so it is in
192 me.scrollPos = max(me.scrollPos, me.selectedItem * me.itemHeight - 1 + me.itemHeight);
193 // if selected item is above listbox, decrease scrollpos so it is in
194 me.scrollPos = min(me.scrollPos, me.selectedItem * me.itemHeight);
195 if(me.scrollPos != save)
196 me.dragScrollTimer = 0.2;
199 // if scroll pos is below end of list, fix it
200 me.scrollPos = min(me.scrollPos, me.nItems * me.itemHeight - 1);
201 // if scroll pos is above beginning of list, fix it
202 me.scrollPos = max(me.scrollPos, 0);
203 // now that we know where the list is scrolled to, find out where to draw the control
204 me.controlTop = max(0, me.scrollPos / (me.nItems * me.itemHeight));
205 me.controlBottom = min((me.scrollPos + 1) / (me.nItems * me.itemHeight), 1);
208 void drawListBox(entity me)
212 vector oldshift, oldscale;
214 me.mouseDrag(me, me.dragScrollPos); // simulate mouseDrag event
215 me.updateControlTopBottom(me);
216 draw_VertButtonPicture(eX * (1 - me.controlWidth), strcat(me.src, "_s"), eX * me.controlWidth + eY, '1 1 1', 1);
217 if(me.nItems * me.itemHeight > 1)
220 o = eX * (1 - me.controlWidth) + eY * me.controlTop;
221 s = eX * me.controlWidth + eY * (me.controlBottom - me.controlTop);
223 draw_VertButtonPicture(o, strcat(me.src, "_c"), s, '1 1 1', 1);
225 draw_VertButtonPicture(o, strcat(me.src, "_f"), s, '1 1 1', 1);
227 draw_VertButtonPicture(o, strcat(me.src, "_n"), s, '1 1 1', 1);
230 oldshift = draw_shift;
231 oldscale = draw_scale;
232 absSize = boxToGlobalSize(me.size, eX * (1 - me.controlWidth) + eY * me.itemHeight);
233 for(i = floor(me.scrollPos / me.itemHeight); i < me.nItems; ++i)
236 y = i * me.itemHeight - me.scrollPos;
239 draw_shift = boxToGlobal(eY * y, oldshift, oldscale);
240 draw_scale = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), oldscale);
241 me.drawListBoxItem(me, i, absSize, (me.selectedItem == i));
246 void clickListBoxItemListBox(entity me, float i, vector where)
248 // itemclick, itemclick, does whatever itemclick does
251 void drawListBoxItemListBox(entity me, float i, vector absSize, float selected)
253 draw_Text('0 0 0', strcat("Item ", ftos(i)), eX * (8 / absSize_x) + eY * (8 / absSize_y), (selected ? '0 1 0' : '1 1 1'), 1);