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