Listbox: highlight item under the cursor
[xonotic/xonotic-data.pk3dir.git] / qcsrc / menu / item / image.qc
1 #ifdef INTERFACE
2 CLASS(Image) EXTENDS(Item)
3         METHOD(Image, configureImage, void(entity, string))
4         METHOD(Image, draw, void(entity))
5         METHOD(Image, toString, string(entity))
6         METHOD(Image, resizeNotify, void(entity, vector, vector, vector, vector))
7         METHOD(Image, updateAspect, void(entity))
8         METHOD(Image, initZoom, void(entity))
9         METHOD(Image, setZoom, void(entity, float, float))
10         METHOD(Image, drag_setStartPos, float(entity, vector))
11         METHOD(Image, drag, float(entity, vector))
12         ATTRIB(Image, src, string, string_null)
13         ATTRIB(Image, color, vector, '1 1 1')
14         ATTRIB(Image, forcedAspect, float, 0) // special values: -1 keep image aspect ratio, -2 keep image size but bound to the containing box, -3 always keep image size
15         ATTRIB(Image, zoomBox, float, 0) // used by forcedAspect -2 when the image is larger than the containing box
16         ATTRIB(Image, zoomFactor, float, 1)
17         ATTRIB(Image, zoomOffset, vector, '0.5 0.5 0')
18         ATTRIB(Image, zoomSnapToTheBox, float, 1) // snap the zoomed in image to the box borders when zooming/dragging it
19         ATTRIB(Image, zoomTime, float, 0)
20         ATTRIB(Image, zoomLimitedByTheBox, float, 0) // forbids zoom if image would be larger than the containing box
21         ATTRIB(Image, zoomMax, float, 0)
22         ATTRIB(Image, start_zoomOffset, vector, '0 0 0')
23         ATTRIB(Image, start_coords, vector, '0 0 0')
24         ATTRIB(Image, imgOrigin, vector, '0 0 0')
25         ATTRIB(Image, imgSize, vector, '0 0 0')
26 ENDCLASS(Image)
27 #endif
28
29 #ifdef IMPLEMENTATION
30 string Image_toString(entity me)
31 {
32         return me.src;
33 }
34 void Image_configureImage(entity me, string path)
35 {
36         me.src = path;
37 }
38 void Image_initZoom(entity me)
39 {
40         me.zoomOffset = '0.5 0.5 0';
41         me.zoomFactor = 1;
42         if (me.forcedAspect == -2)
43                 me.zoomBox = -1; // calculate zoomBox at the first updateAspect call
44         if (me.zoomLimitedByTheBox)
45                 me.zoomMax = -1; // calculate zoomMax at the first updateAspect call
46 }
47
48 void Image_draw(entity me)
49 {
50         if(me.imgSize.x > 1 || me.imgSize.y > 1)
51                 draw_SetClip();
52         draw_Picture(me.imgOrigin, me.src, me.imgSize, me.color, 1);
53         if(me.imgSize.x > 1 || me.imgSize.y > 1)
54                 draw_ClearClip();
55         SUPER(Image).draw(me);
56 }
57 void Image_updateAspect(entity me)
58 {
59         float asp = 0;
60         if(me.size.x <= 0 || me.size.y <= 0)
61                 return;
62         if(me.forcedAspect == 0)
63         {
64                 me.imgOrigin = '0 0 0';
65                 me.imgSize = '1 1 0';
66         }
67         else
68         {
69                 vector sz = '0 0 0';
70                 if(me.forcedAspect < 0)
71                 {
72                         if (me.src != "")
73                                 sz = draw_PictureSize(me.src);
74                         if(sz.x <= 0 || sz.y <= 0)
75                         {
76                                 // image is broken or doesn't exist, set the size for the placeholder image
77                                 sz.x = me.size.x;
78                                 sz.y = me.size.y;
79                         }
80                         asp = sz.x / sz.y;
81                 }
82                 else
83                         asp = me.forcedAspect;
84
85                 if(me.forcedAspect <= -2)
86                 {
87                         me.imgSize_x = sz.x / me.size.x;
88                         me.imgSize_y = sz.y / me.size.y;
89                         if(me.zoomBox < 0 && (me.imgSize.x > 1 || me.imgSize.y > 1))
90                         {
91                                 // image larger than the containing box, zoom it out to fit into the box
92                                 if(me.size.x > asp * me.size.y)
93                                         me.zoomBox = (me.size.y * asp / me.size.x) / me.imgSize.x;
94                                 else
95                                         me.zoomBox = (me.size.x / (asp * me.size.y)) / me.imgSize.y;
96                                 me.zoomFactor = me.zoomBox;
97                         }
98                 }
99                 else
100                 {
101                         if(me.size.x > asp * me.size.y)
102                         {
103                                 // x too large, so center x-wise
104                                 me.imgSize = eY + eX * (me.size.y * asp / me.size.x);
105                         }
106                         else
107                         {
108                                 // y too large, so center y-wise
109                                 me.imgSize = eX + eY * (me.size.x / (asp * me.size.y));
110                         }
111                 }
112         }
113
114         if (me.zoomMax < 0)
115         {
116                 if(me.zoomBox > 0)
117                         me.zoomMax = me.zoomBox;
118                 else
119                 {
120                         if(me.size.x > asp * me.size.y)
121                                 me.zoomMax = (me.size.y * asp / me.size.x) / me.imgSize.x;
122                         else
123                                 me.zoomMax = (me.size.x / (asp * me.size.y)) / me.imgSize.y;
124                 }
125         }
126
127         if (me.zoomMax > 0 && me.zoomFactor > me.zoomMax)
128                 me.zoomFactor = me.zoomMax;
129         if (me.zoomFactor)
130                 me.imgSize = me.imgSize * me.zoomFactor;
131
132         if(me.imgSize.x > 1 || me.imgSize.y > 1)
133         {
134                 if(me.zoomSnapToTheBox)
135                 {
136                         if(me.imgSize.x > 1)
137                                 me.zoomOffset_x = bound(0.5/me.imgSize.x, me.zoomOffset.x, 1 - 0.5/me.imgSize.x);
138                         else
139                                 me.zoomOffset_x = bound(1 - 0.5/me.imgSize.x, me.zoomOffset.x, 0.5/me.imgSize.x);
140
141                         if(me.imgSize.y > 1)
142                                 me.zoomOffset_y = bound(0.5/me.imgSize.y, me.zoomOffset.y, 1 - 0.5/me.imgSize.y);
143                         else
144                                 me.zoomOffset_y = bound(1 - 0.5/me.imgSize.y, me.zoomOffset.y, 0.5/me.imgSize.y);
145                 }
146                 else
147                 {
148                         me.zoomOffset_x = bound(0, me.zoomOffset.x, 1);
149                         me.zoomOffset_y = bound(0, me.zoomOffset.y, 1);
150                 }
151         }
152         else
153                 me.zoomOffset = '0.5 0.5 0';
154
155         me.imgOrigin_x = 0.5 - me.zoomOffset.x * me.imgSize.x;
156         me.imgOrigin_y = 0.5 - me.zoomOffset.y * me.imgSize.y;
157 }
158 float Image_drag_setStartPos(entity me, vector coords)
159 {
160         //if(me.imgSize_x > 1 || me.imgSize_y > 1) // check disabled: mousewheel zoom may start from a non-zoomed-in image
161         {
162                 me.start_zoomOffset = me.zoomOffset;
163                 me.start_coords = coords;
164         }
165         return 1;
166 }
167 float Image_drag(entity me, vector coords)
168 {
169         if(me.imgSize.x > 1 || me.imgSize.y > 1)
170         {
171                 me.zoomOffset_x = me.start_zoomOffset.x + (me.start_coords.x - coords.x) / me.imgSize.x;
172                 me.zoomOffset_y = me.start_zoomOffset.y + (me.start_coords.y - coords.y) / me.imgSize.y;
173                 me.updateAspect(me);
174         }
175         return 1;
176 }
177 void Image_setZoom(entity me, float z, float atMousePosition)
178 {
179         float prev_zoomFactor;
180         prev_zoomFactor = me.zoomFactor;
181         if (z < 0) // multiply by the current zoomFactor (but can also snap to real dimensions or to box)
182         {
183                 me.zoomFactor *= -z;
184                 float realSize_in_the_middle, boxSize_in_the_middle;
185                 realSize_in_the_middle = ((prev_zoomFactor - 1) * (me.zoomFactor - 1) < 0);
186                 boxSize_in_the_middle = (me.zoomBox > 0 && (prev_zoomFactor - me.zoomBox) * (me.zoomFactor - me.zoomBox) < 0);
187                 if (realSize_in_the_middle && boxSize_in_the_middle)
188                 {
189                         // snap to real dimensions or to box
190                         if (prev_zoomFactor < me.zoomFactor)
191                                 me.zoomFactor = min(1, me.zoomBox);
192                         else
193                                 me.zoomFactor = max(1, me.zoomBox);
194                 }
195                 else if (realSize_in_the_middle)
196                         me.zoomFactor = 1; // snap to real dimensions
197                 else if (boxSize_in_the_middle)
198                         me.zoomFactor = me.zoomBox; // snap to box
199         }
200         else if (z == 0) // reset (no zoom)
201         {
202                 if (me.zoomBox > 0)
203                         me.zoomFactor = me.zoomBox;
204                 else
205                         me.zoomFactor = 1;
206         }
207         else // directly set
208                 me.zoomFactor = z;
209         me.zoomFactor = bound(1/16, me.zoomFactor, 16);
210         if (me.zoomMax > 0 && me.zoomFactor > me.zoomMax)
211                 me.zoomFactor = me.zoomMax;
212         if (prev_zoomFactor != me.zoomFactor)
213         {
214                 me.zoomTime = time;
215                 if (atMousePosition)
216                 {
217                         me.zoomOffset_x = me.start_zoomOffset.x + (me.start_coords.x - 0.5) / me.imgSize.x;
218                         me.zoomOffset_y = me.start_zoomOffset.y + (me.start_coords.y - 0.5) / me.imgSize.y;
219                         // updateAspect will reset zoomOffset to '0.5 0.5 0' if
220                         // with this zoomFactor the image will not be zoomed in
221                         // (updateAspect will check the new values of imgSize).
222                 }
223         }
224         me.updateAspect(me);
225 }
226 void Image_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
227 {
228         SUPER(Image).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
229         me.updateAspect(me);
230 }
231 #endif