]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/menu-div0test/item/listbox.c
Maybe draw window titles in "Nexposee" mode additionally? Commented out, as I don...
[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 - 1 / (me.nItems * me.itemHeight)) * (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);
175                         me.clickListBoxItem(me, me.selectedItem, globalToBox(pos, eX * (me.selectedItem * me.itemHeight - me.scrollPos), absSize));
176                 }
177         }
178         me.pressed = 0;
179         return 1;
180 }
181 void updateControlTopBottomListBox(entity me)
182 {
183         // scrollPos is in 0..1 and indicates where the "page" currently shown starts.
184         if(me.nItems * me.itemHeight <= 1)
185         {
186                 // we don't need no stinkin' scrollbar, we don't need no view control...
187                 me.controlTop = 0;
188                 me.controlBottom = 1;
189                 me.scrollPos = 0;
190         }
191         else
192         {
193                 if(frametime) // only do this in draw frames
194                 {
195                         me.dragScrollTimer -= frametime;
196                         if(me.dragScrollTimer < 0)
197                         {
198                                 float save;
199                                 save = me.scrollPos;
200                                 // if selected item is below listbox, increase scrollpos so it is in
201                                 me.scrollPos = max(me.scrollPos, me.selectedItem * me.itemHeight - 1 + me.itemHeight);
202                                 // if selected item is above listbox, decrease scrollpos so it is in
203                                 me.scrollPos = min(me.scrollPos, me.selectedItem * me.itemHeight);
204                                 if(me.scrollPos != save)
205                                         me.dragScrollTimer = 0.2;
206                         }
207                 }
208                 // if scroll pos is below end of list, fix it
209                 me.scrollPos = min(me.scrollPos, me.nItems * me.itemHeight - 1);
210                 // if scroll pos is above beginning of list, fix it
211                 me.scrollPos = max(me.scrollPos, 0);
212                 // now that we know where the list is scrolled to, find out where to draw the control
213                 me.controlTop = max(0, me.scrollPos / (me.nItems * me.itemHeight));
214                 me.controlBottom = min((me.scrollPos + 1) / (me.nItems * me.itemHeight), 1);
215         }
216 }
217 void drawListBox(entity me)
218 {
219         float i;
220         vector absSize;
221         vector oldshift, oldscale;
222         if(me.pressed == 2)
223                 me.mouseDrag(me, me.dragScrollPos); // simulate mouseDrag event
224         me.updateControlTopBottom(me);
225         draw_VertButtonPicture(eX * (1 - me.controlWidth), strcat(me.src, "_s"), eX * me.controlWidth + eY, me.color2, 1);
226         if(me.nItems * me.itemHeight > 1)
227         {
228                 vector o, s;
229                 o = eX * (1 - me.controlWidth) + eY * me.controlTop;
230                 s = eX * me.controlWidth + eY * (me.controlBottom - me.controlTop);
231                 if(me.pressed == 1)
232                         draw_VertButtonPicture(o, strcat(me.src, "_c"), s, me.colorC, 1);
233                 else if(me.focused)
234                         draw_VertButtonPicture(o, strcat(me.src, "_f"), s, me.colorF, 1);
235                 else
236                         draw_VertButtonPicture(o, strcat(me.src, "_n"), s, me.color, 1);
237         }
238         draw_SetClip();
239         oldshift = draw_shift;
240         oldscale = draw_scale;
241         absSize = boxToGlobalSize(me.size, eX * (1 - me.controlWidth) + eY * me.itemHeight);
242         for(i = floor(me.scrollPos / me.itemHeight); i < me.nItems; ++i)
243         {
244                 float y;
245                 y = i * me.itemHeight - me.scrollPos;
246                 if(y >= 1)
247                         break;
248                 draw_shift = boxToGlobal(eY * y, oldshift, oldscale);
249                 draw_scale = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), oldscale);
250                 me.drawListBoxItem(me, i, absSize, (me.selectedItem == i));
251         }
252         draw_ClearClip();
253 }
254
255 void clickListBoxItemListBox(entity me, float i, vector where)
256 {
257         // itemclick, itemclick, does whatever itemclick does
258 }
259
260 void drawListBoxItemListBox(entity me, float i, vector absSize, float selected)
261 {
262         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);
263 }
264 #endif