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