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