]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/menu-div0test/item/listbox.c
add somehow broken server list, far from done yet
[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, "") // 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
32 ENDCLASS(ListBox)
33 #endif
34
35 #ifdef IMPLEMENTATION
36 void resizeNotifyListBox(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
37 {
38         me.size = absSize;
39         me.controlWidth = me.scrollbarWidth / absSize_x;
40 }
41 void configureListBoxListBox(entity me, float theScrollbarWidth, float theItemHeight)
42 {
43         me.scrollbarWidth = theScrollbarWidth;
44         me.itemHeight = theItemHeight;
45 }
46 float keyDownListBox(entity me, float key, float ascii, float shift)
47 {
48         if(key == K_DOWNARROW)
49                 me.selectedItem += 1;
50         else if(key == K_UPARROW)
51                 me.selectedItem -= 1;
52         else if(key == K_MWHEELUP)
53         {
54                 me.scrollPos = max(me.scrollPos - 0.5, 0);
55                 me.selectedItem = min(me.selectedItem, floor((me.scrollPos + 1) / me.itemHeight - 1));
56         }
57         else if(key == K_MWHEELDOWN)
58         {
59                 me.scrollPos = min(me.scrollPos + 0.5, me.nItems * me.itemHeight - 1);
60                 me.selectedItem = max(me.selectedItem, ceil(me.scrollPos / me.itemHeight));
61         }
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)
67                 me.selectedItem = 0;
68         else if(key == K_END)
69                 me.selectedItem = me.nItems - 1;
70         else
71                 return 0;
72         me.selectedItem = floor(0.5 + bound(0, me.selectedItem, me.nItems - 1));
73         return 1;
74 }
75 float mouseDragListBox(entity me, vector pos)
76 {
77         float hit;
78         me.updateControlTopBottom(me);
79         me.dragScrollPos = pos;
80         if(me.pressed == 1)
81         {
82                 hit = 1;
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;
87                 if(hit)
88                 {
89                         // calculate new pos to v
90                         float delta;
91                         delta = (pos_y - me.pressOffset) / (1 - 1 / (me.nItems * me.itemHeight)) * (me.nItems * me.itemHeight - 1);
92                         me.scrollPos = me.previousValue + delta;
93                 }
94                 else
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));
100         }
101         else if(me.pressed == 2)
102         {
103                 me.selectedItem = (me.scrollPos + pos_y) / me.itemHeight;
104                 me.selectedItem = floor(0.5 + bound(0, me.selectedItem, me.nItems - 1));
105         }
106         return 1;
107 }
108 float mousePressListBox(entity me, vector pos)
109 {
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)
117         {
118                 // if hit, set me.pressed, otherwise scroll by one page
119                 if(pos_y < me.controlTop)
120                 {
121                         // page up
122                         me.scrollPos = max(me.scrollPos - 1, 0);
123                         me.selectedItem = min(me.selectedItem, floor((me.scrollPos + 1) / me.itemHeight - 1));
124                 }
125                 else if(pos_y > me.controlBottom)
126                 {
127                         // page down
128                         me.scrollPos = min(me.scrollPos + 1, me.nItems * me.itemHeight - 1);
129                         me.selectedItem = max(me.selectedItem, ceil(me.scrollPos / me.itemHeight));
130                 }
131                 else
132                 {
133                         me.pressed = 1;
134                         me.pressOffset = pos_y;
135                         me.previousValue = me.scrollPos;
136                 }
137         }
138         else
139         {
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.
144                 me.pressed = 2;
145         }
146         return 1;
147 }
148 float mouseReleaseListBox(entity me, vector pos)
149 {
150         vector absSize;
151         if(me.pressed == 1)
152         {
153                 // slider dragging mode
154                 // in that case, nothing happens on releasing
155         }
156         else if(me.pressed == 2)
157         {
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
163                 if(me.nItems > 0)
164                 {
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));
167                 }
168         }
169         me.pressed = 0;
170         return 1;
171 }
172 void updateControlTopBottomListBox(entity me)
173 {
174         // scrollPos is in 0..1 and indicates where the "page" currently shown starts.
175         if(me.nItems * me.itemHeight <= 1)
176         {
177                 // we don't need no stinkin' scrollbar, we don't need no view control...
178                 me.controlTop = 0;
179                 me.controlBottom = 1;
180                 me.scrollPos = 0;
181         }
182         else
183         {
184                 if(frametime) // only do this in draw frames
185                 {
186                         me.dragScrollTimer -= frametime;
187                         if(me.dragScrollTimer < 0)
188                         {
189                                 float save;
190                                 save = me.scrollPos;
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;
197                         }
198                 }
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);
206         }
207 }
208 void drawListBox(entity me)
209 {
210         float i;
211         vector absSize;
212         vector oldshift, oldscale;
213         if(me.pressed == 2)
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)
218         {
219                 vector o, s;
220                 o = eX * (1 - me.controlWidth) + eY * me.controlTop;
221                 s = eX * me.controlWidth + eY * (me.controlBottom - me.controlTop);
222                 if(me.pressed == 1)
223                         draw_VertButtonPicture(o, strcat(me.src, "_c"), s, '1 1 1', 1);
224                 else if(me.focused)
225                         draw_VertButtonPicture(o, strcat(me.src, "_f"), s, '1 1 1', 1);
226                 else
227                         draw_VertButtonPicture(o, strcat(me.src, "_n"), s, '1 1 1', 1);
228         }
229         draw_SetClip();
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)
234         {
235                 float y;
236                 y = i * me.itemHeight - me.scrollPos;
237                 if(y >= 1)
238                         break;
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));
242         }
243         draw_ClearClip();
244 }
245
246 void clickListBoxItemListBox(entity me, float i, vector where)
247 {
248         // itemclick, itemclick, does whatever itemclick does
249 }
250
251 void drawListBoxItemListBox(entity me, float i, vector absSize, float selected)
252 {
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);
254 }
255 #endif