]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/menu-div0test/item/listbox.c
add global "time" as in QC (to allow double click detection later); clean up warnings
[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, 0)
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_x) hit = 0;
84                 if(pos_y < 0 - me.tolerance_y) hit = 0;
85                 if(pos_x >= 1 + me.tolerance_x) hit = 0;
86                 if(pos_y >= 1 + me.tolerance_y) 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.updateControlTopBottom(me);
115         if(pos_x >= 1 - me.controlWidth)
116         {
117                 // if hit, set me.pressed, otherwise scroll by one page
118                 if(pos_y < me.controlTop)
119                 {
120                         // page up
121                         me.scrollPos = max(me.scrollPos - 1, 0);
122                         me.selectedItem = min(me.selectedItem, floor((me.scrollPos + 1) / me.itemHeight - 1));
123                 }
124                 else if(pos_y > me.controlBottom)
125                 {
126                         // page down
127                         me.scrollPos = min(me.scrollPos + 1, me.nItems * me.itemHeight - 1);
128                         me.selectedItem = max(me.selectedItem, ceil(me.scrollPos / me.itemHeight));
129                 }
130                 else
131                 {
132                         me.pressed = 1;
133                         me.pressOffset = pos_y;
134                         me.previousValue = me.scrollPos;
135                 }
136         }
137         else
138         {
139                 // an item has been clicked. Select it, ...
140                 me.selectedItem = (me.scrollPos + pos_y) / me.itemHeight;
141                 me.selectedItem = floor(0.5 + bound(0, me.selectedItem, me.nItems - 1));
142                 // continue doing that while dragging (even when dragging outside). When releasing, forward the click to the then selected item.
143                 me.pressed = 2;
144         }
145         return 1;
146 }
147 float mouseReleaseListBox(entity me, vector pos)
148 {
149         vector absSize;
150         if(me.pressed == 1)
151         {
152                 // slider dragging mode
153                 // in that case, nothing happens on releasing
154         }
155         else if(me.pressed == 2)
156         {
157                 // item dragging mode
158                 // select current one one last time...
159                 me.selectedItem = (me.scrollPos + pos_y) / me.itemHeight;
160                 me.selectedItem = floor(0.5 + bound(0, me.selectedItem, me.nItems - 1));
161                 // and give it a nice click event
162                 if(me.nItems > 0)
163                 {
164                         absSize = boxToGlobalSize(me.size, eX * (1 - me.controlWidth) + eY);
165                         me.clickListBoxItem(me, me.selectedItem, globalToBox(pos, eX * (me.selectedItem * me.itemHeight - me.scrollPos), absSize));
166                 }
167         }
168         me.pressed = 0;
169         return 1;
170 }
171 void updateControlTopBottomListBox(entity me)
172 {
173         // scrollPos is in 0..1 and indicates where the "page" currently shown starts.
174         if(me.nItems * me.itemHeight <= 1)
175         {
176                 // we don't need no stinkin' scrollbar, we don't need no view control...
177                 me.controlTop = 0;
178                 me.controlBottom = 1;
179                 me.scrollPos = 0;
180         }
181         else
182         {
183                 if(frametime) // only do this in draw frames
184                 {
185                         me.dragScrollTimer -= frametime;
186                         if(me.dragScrollTimer < 0)
187                         {
188                                 float save;
189                                 save = me.scrollPos;
190                                 // if selected item is below listbox, increase scrollpos so it is in
191                                 me.scrollPos = max(me.scrollPos, me.selectedItem * me.itemHeight - 1 + me.itemHeight);
192                                 // if selected item is above listbox, decrease scrollpos so it is in
193                                 me.scrollPos = min(me.scrollPos, me.selectedItem * me.itemHeight);
194                                 if(me.scrollPos != save)
195                                         me.dragScrollTimer = 0.2;
196                         }
197                 }
198                 // if scroll pos is below end of list, fix it
199                 me.scrollPos = min(me.scrollPos, me.nItems * me.itemHeight - 1);
200                 // if scroll pos is above beginning of list, fix it
201                 me.scrollPos = max(me.scrollPos, 0);
202                 // now that we know where the list is scrolled to, find out where to draw the control
203                 me.controlTop = max(0, me.scrollPos / (me.nItems * me.itemHeight));
204                 me.controlBottom = min((me.scrollPos + 1) / (me.nItems * me.itemHeight), 1);
205         }
206 }
207 void drawListBox(entity me)
208 {
209         float i;
210         vector absSize;
211         vector oldshift, oldscale;
212         if(me.pressed == 2)
213                 me.mouseDrag(me, me.dragScrollPos); // simulate mouseDrag event
214         me.updateControlTopBottom(me);
215         draw_VertButtonPicture(eX * (1 - me.controlWidth), strcat(me.src, "_s"), eX * me.controlWidth + eY, '1 1 1', 1);
216         if(me.nItems * me.itemHeight > 1)
217         {
218                 vector o, s;
219                 o = eX * (1 - me.controlWidth) + eY * me.controlTop;
220                 s = eX * me.controlWidth + eY * (me.controlBottom - me.controlTop);
221                 if(me.pressed)
222                         draw_VertButtonPicture(o, strcat(me.src, "_c"), s, '1 1 1', 1);
223                 else if(me.focused)
224                         draw_VertButtonPicture(o, strcat(me.src, "_f"), s, '1 1 1', 1);
225                 else
226                         draw_VertButtonPicture(o, strcat(me.src, "_n"), s, '1 1 1', 1);
227         }
228         draw_SetClip();
229         oldshift = draw_shift;
230         oldscale = draw_scale;
231         absSize = boxToGlobalSize(me.size, eX * (1 - me.controlWidth) + eY * me.itemHeight);
232         for(i = floor(me.scrollPos / me.itemHeight); i < me.nItems; ++i)
233         {
234                 float y;
235                 y = i * me.itemHeight - me.scrollPos;
236                 if(y >= 1)
237                         break;
238                 draw_shift = boxToGlobal(eY * y, oldshift, oldscale);
239                 draw_scale = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), oldscale);
240                 me.drawListBoxItem(me, i, absSize, (me.selectedItem == i));
241         }
242         draw_ClearClip();
243 }
244
245 void clickListBoxItemListBox(entity me, float i, vector where)
246 {
247         // itemclick, itemclick, does whatever itemclick does
248 }
249
250 void drawListBoxItemListBox(entity me, float i, vector absSize, float selected)
251 {
252         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);
253 }
254 #endif