]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/menu-div0test/item/listbox.c
charmap in menu
[divverent/nexuiz.git] / data / qcsrc / menu-div0test / item / listbox.c
1 #ifdef INTERFACE
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)
17
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')
24
25         ATTRIB(ListBox, src, string, string_null) // scrollbar
26         ATTRIB(ListBox, color, vector, '1 1 1')
27         ATTRIB(ListBox, color2, vector, '1 1 1')
28         ATTRIB(ListBox, colorC, vector, '1 1 1')
29         ATTRIB(ListBox, colorF, vector, '1 1 1')
30         ATTRIB(ListBox, tolerance, vector, '0 0 0') // drag tolerance
31         ATTRIB(ListBox, scrollbarWidth, float, 0) // pixels
32         ATTRIB(ListBox, nItems, float, 42)
33         ATTRIB(ListBox, itemHeight, float, 0)
34         METHOD(ListBox, drawListBoxItem, void(entity, float, vector, float)) // item number, width/height, selected
35         METHOD(ListBox, clickListBoxItem, void(entity, float, vector)) // item number, relative clickpos
36         METHOD(ListBox, setSelected, void(entity, float))
37 ENDCLASS(ListBox)
38 #endif
39
40 #ifdef IMPLEMENTATION
41 void setSelectedListBox(entity me, float i)
42 {
43         me.selectedItem = floor(0.5 + bound(0, i, me.nItems - 1));
44 }
45 void resizeNotifyListBox(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
46 {
47         me.size = absSize;
48         me.controlWidth = me.scrollbarWidth / absSize_x;
49 }
50 void configureListBoxListBox(entity me, float theScrollbarWidth, float theItemHeight)
51 {
52         me.scrollbarWidth = theScrollbarWidth;
53         me.itemHeight = theItemHeight;
54 }
55 float keyDownListBox(entity me, float key, float ascii, float shift)
56 {
57         me.dragScrollTimer = 0;
58         if(key == K_MWHEELUP)
59         {
60                 me.scrollPos = max(me.scrollPos - 0.5, 0);
61                 me.setSelected(me, min(me.selectedItem, floor((me.scrollPos + 1) / me.itemHeight - 1)));
62         }
63         else if(key == K_MWHEELDOWN)
64         {
65                 me.scrollPos = min(me.scrollPos + 0.5, me.nItems * me.itemHeight - 1);
66                 me.setSelected(me, max(me.selectedItem, ceil(me.scrollPos / me.itemHeight)));
67         }
68         else if(key == K_PGUP)
69                 me.setSelected(me, me.selectedItem - 1 / me.itemHeight);
70         else if(key == K_PGDN)
71                 me.setSelected(me, me.selectedItem + 1 / me.itemHeight);
72         else if(key == K_UPARROW)
73                 me.setSelected(me, me.selectedItem - 1);
74         else if(key == K_DOWNARROW)
75                 me.setSelected(me, me.selectedItem + 1);
76         else if(key == K_HOME)
77                 me.setSelected(me, 0);
78         else if(key == K_END)
79                 me.setSelected(me, me.nItems - 1);
80         else
81                 return 0;
82         return 1;
83 }
84 float mouseDragListBox(entity me, vector pos)
85 {
86         float hit;
87         float i;
88         me.updateControlTopBottom(me);
89         me.dragScrollPos = pos;
90         if(me.pressed == 1)
91         {
92                 hit = 1;
93                 if(pos_x < 1 - me.controlWidth - me.tolerance_y * me.controlWidth) hit = 0;
94                 if(pos_y < 0 - me.tolerance_x) hit = 0;
95                 if(pos_x >= 1 + me.tolerance_y * me.controlWidth) hit = 0;
96                 if(pos_y >= 1 + me.tolerance_x) hit = 0;
97                 if(hit)
98                 {
99                         // calculate new pos to v
100                         float delta;
101                         delta = (pos_y - me.pressOffset) / (1 - (me.controlBottom - me.controlTop)) * (me.nItems * me.itemHeight - 1);
102                         me.scrollPos = me.previousValue + delta;
103                 }
104                 else
105                         me.scrollPos = me.previousValue;
106                 me.scrollPos = min(me.scrollPos, me.nItems * me.itemHeight - 1);
107                 me.scrollPos = max(me.scrollPos, 0);
108                 i = min(me.selectedItem, floor((me.scrollPos + 1) / me.itemHeight - 1));
109                 i = max(i, ceil(me.scrollPos / me.itemHeight));
110                 me.setSelected(me, i);
111         }
112         else if(me.pressed == 2)
113         {
114                 me.setSelected(me, floor((me.scrollPos + pos_y) / me.itemHeight));
115         }
116         return 1;
117 }
118 float mousePressListBox(entity me, vector pos)
119 {
120         if(pos_x < 0) return 0;
121         if(pos_y < 0) return 0;
122         if(pos_x >= 1) return 0;
123         if(pos_y >= 1) return 0;
124         me.dragScrollPos = pos;
125         me.updateControlTopBottom(me);
126         me.dragScrollTimer = 0;
127         if(pos_x >= 1 - me.controlWidth)
128         {
129                 // if hit, set me.pressed, otherwise scroll by one page
130                 if(pos_y < me.controlTop)
131                 {
132                         // page up
133                         me.scrollPos = max(me.scrollPos - 1, 0);
134                         me.setSelected(me, min(me.selectedItem, floor((me.scrollPos + 1) / me.itemHeight - 1)));
135                 }
136                 else if(pos_y > me.controlBottom)
137                 {
138                         // page down
139                         me.scrollPos = min(me.scrollPos + 1, me.nItems * me.itemHeight - 1);
140                         me.setSelected(me, max(me.selectedItem, ceil(me.scrollPos / me.itemHeight)));
141                 }
142                 else
143                 {
144                         me.pressed = 1;
145                         me.pressOffset = pos_y;
146                         me.previousValue = me.scrollPos;
147                 }
148         }
149         else
150         {
151                 // an item has been clicked. Select it, ...
152                 me.setSelected(me, floor((me.scrollPos + pos_y) / me.itemHeight));
153                 // continue doing that while dragging (even when dragging outside). When releasing, forward the click to the then selected item.
154                 me.pressed = 2;
155         }
156         return 1;
157 }
158 float mouseReleaseListBox(entity me, vector pos)
159 {
160         vector absSize;
161         if(me.pressed == 1)
162         {
163                 // slider dragging mode
164                 // in that case, nothing happens on releasing
165         }
166         else if(me.pressed == 2)
167         {
168                 // item dragging mode
169                 // select current one one last time...
170                 me.setSelected(me, floor((me.scrollPos + pos_y) / me.itemHeight));
171                 // and give it a nice click event
172                 if(me.nItems > 0)
173                 {
174                         absSize = boxToGlobalSize(me.size, eX * (1 - me.controlWidth) + eY * me.itemHeight);
175                         me.clickListBoxItem(me, me.selectedItem, globalToBox(pos, eY * (me.selectedItem * me.itemHeight - me.scrollPos), eX * (1 - me.controlWidth) + eY * me.itemHeight));
176                 }
177         }
178         me.pressed = 0;
179         return 1;
180 }
181 void updateControlTopBottomListBox(entity me)
182 {
183         float f;
184         // scrollPos is in 0..1 and indicates where the "page" currently shown starts.
185         if(me.nItems * me.itemHeight <= 1)
186         {
187                 // we don't need no stinkin' scrollbar, we don't need no view control...
188                 me.controlTop = 0;
189                 me.controlBottom = 1;
190                 me.scrollPos = 0;
191         }
192         else
193         {
194                 if(frametime) // only do this in draw frames
195                 {
196                         me.dragScrollTimer -= frametime;
197                         if(me.dragScrollTimer < 0)
198                         {
199                                 float save;
200                                 save = me.scrollPos;
201                                 // if selected item is below listbox, increase scrollpos so it is in
202                                 me.scrollPos = max(me.scrollPos, me.selectedItem * me.itemHeight - 1 + me.itemHeight);
203                                 // if selected item is above listbox, decrease scrollpos so it is in
204                                 me.scrollPos = min(me.scrollPos, me.selectedItem * me.itemHeight);
205                                 if(me.scrollPos != save)
206                                         me.dragScrollTimer = 0.2;
207                         }
208                 }
209                 // if scroll pos is below end of list, fix it
210                 me.scrollPos = min(me.scrollPos, me.nItems * me.itemHeight - 1);
211                 // if scroll pos is above beginning of list, fix it
212                 me.scrollPos = max(me.scrollPos, 0);
213                 // now that we know where the list is scrolled to, find out where to draw the control
214                 me.controlTop = max(0, me.scrollPos / (me.nItems * me.itemHeight));
215                 me.controlBottom = min((me.scrollPos + 1) / (me.nItems * me.itemHeight), 1);
216
217                 float fmin;
218                 fmin = 1 * me.controlWidth / me.size_y * me.size_x;
219                 f = me.controlBottom - me.controlTop;
220                 if(f < fmin) // FIXME good default?
221                 {
222                         // f * X + 1 * (1-X) = fmin
223                         // (f - 1) * X + 1 = fmin
224                         // (f - 1) * X = fmin - 1
225                         // X = (fmin - 1) / (f - 1)
226                         f = (fmin - 1) / (f - 1);
227                         me.controlTop = me.controlTop * f + 0 * (1 - f);
228                         me.controlBottom = me.controlBottom * f + 1 * (1 - f);
229                 }
230         }
231 }
232 void drawListBox(entity me)
233 {
234         float i;
235         vector absSize;
236         vector oldshift, oldscale;
237         if(me.pressed == 2)
238                 me.mouseDrag(me, me.dragScrollPos); // simulate mouseDrag event
239         me.updateControlTopBottom(me);
240         draw_VertButtonPicture(eX * (1 - me.controlWidth), strcat(me.src, "_s"), eX * me.controlWidth + eY, me.color2, 1);
241         if(me.nItems * me.itemHeight > 1)
242         {
243                 vector o, s;
244                 o = eX * (1 - me.controlWidth) + eY * me.controlTop;
245                 s = eX * me.controlWidth + eY * (me.controlBottom - me.controlTop);
246                 if(me.pressed == 1)
247                         draw_VertButtonPicture(o, strcat(me.src, "_c"), s, me.colorC, 1);
248                 else if(me.focused)
249                         draw_VertButtonPicture(o, strcat(me.src, "_f"), s, me.colorF, 1);
250                 else
251                         draw_VertButtonPicture(o, strcat(me.src, "_n"), s, me.color, 1);
252         }
253         draw_SetClip();
254         oldshift = draw_shift;
255         oldscale = draw_scale;
256         absSize = boxToGlobalSize(me.size, eX * (1 - me.controlWidth) + eY * me.itemHeight);
257         for(i = floor(me.scrollPos / me.itemHeight); i < me.nItems; ++i)
258         {
259                 float y;
260                 y = i * me.itemHeight - me.scrollPos;
261                 if(y >= 1)
262                         break;
263                 draw_shift = boxToGlobal(eY * y, oldshift, oldscale);
264                 draw_scale = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), oldscale);
265                 me.drawListBoxItem(me, i, absSize, (me.selectedItem == i));
266         }
267         draw_ClearClip();
268 }
269
270 void clickListBoxItemListBox(entity me, float i, vector where)
271 {
272         // itemclick, itemclick, does whatever itemclick does
273 }
274
275 void drawListBoxItemListBox(entity me, float i, vector absSize, float selected)
276 {
277         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, 0);
278 }
279 #endif