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