1 /** @file hu_lib.cpp HUD widget library.
2 *
3 * @authors Copyright © 2005-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2005-2014 Daniel Swanson <danij@dengine.net>
5 *
6 * @par License
7 * GPL: http://www.gnu.org/licenses/gpl.html
8 *
9 * <small>This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. This program is distributed in the hope that it
13 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15 * Public License for more details. You should have received a copy of the GNU
16 * General Public License along with this program; if not, write to the Free
17 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA</small>
19 */
20
21 #include "common.h"
22 #include "hu_lib.h"
23
24 #include <QList>
25
26 /// @todo remove me:
27 #include "hud/widgets/automapwidget.h"
28 #include "hud/widgets/chatwidget.h"
29 #include "hud/widgets/groupwidget.h"
30 #include "hud/widgets/playerlogwidget.h"
31 #include "menu/widgets/lineeditwidget.h"
32 #include "menu/widgets/sliderwidget.h"
33
34 using namespace de;
35 using namespace common;
36
37 static bool inited;
38
39 static QList<HudWidget *> widgets;
40
41 static ui_rendstate_t uiRS;
42 ui_rendstate_t const *uiRendState = &uiRS;
43
nextUnusedId()44 static uiwidgetid_t nextUnusedId()
45 {
46 return uiwidgetid_t(widgets.count());
47 }
48
clearWidgets()49 static void clearWidgets()
50 {
51 qDeleteAll(widgets); widgets.clear();
52 }
53
GUI_Init()54 void GUI_Init()
55 {
56 if(inited) return;
57
58 clearWidgets();
59 ChatWidget::loadMacros();
60
61 inited = true;
62
63 GUI_LoadResources();
64 }
65
GUI_Shutdown()66 void GUI_Shutdown()
67 {
68 if(!inited) return;
69 clearWidgets();
70 inited = false;
71 }
72
GUI_AddWidget(HudWidget * wi)73 HudWidget *GUI_AddWidget(HudWidget *wi)
74 {
75 DENG2_ASSERT(inited);
76 if(wi)
77 {
78 wi->setId(nextUnusedId());
79 widgets << wi;
80 }
81 return wi;
82 }
83
GUI_TryFindWidgetById(uiwidgetid_t id)84 HudWidget *GUI_TryFindWidgetById(uiwidgetid_t id)
85 {
86 if(!inited) return 0; // GUI not available.
87 if(id >= 0)
88 {
89 for(HudWidget *wi : widgets)
90 {
91 if(wi->id() == id) return wi;
92 }
93 }
94 return nullptr; // Not found.
95 }
96
GUI_FindWidgetById(uiwidgetid_t id)97 HudWidget &GUI_FindWidgetById(uiwidgetid_t id)
98 {
99 if(HudWidget *wi = GUI_TryFindWidgetById(id)) return *wi;
100 throw Error("GUI_FindWidgetById", "Unknown widget id #" + String::number(id));
101 }
102
GUI_UpdateWidgetGeometry(HudWidget * wi)103 void GUI_UpdateWidgetGeometry(HudWidget *wi)
104 {
105 if(!wi) return;
106
107 Rect_SetXY(&wi->geometry(), 0, 0);
108 wi->updateGeometry(wi);
109
110 if(Rect_Width (&wi->geometry()) <= 0 ||
111 Rect_Height(&wi->geometry()) <= 0) return;
112
113 if(wi->alignment() & ALIGN_RIGHT)
114 Rect_SetX(&wi->geometry(), Rect_X(&wi->geometry()) - Rect_Width(&wi->geometry()));
115 else if(!(wi->alignment() & ALIGN_LEFT))
116 Rect_SetX(&wi->geometry(), Rect_X(&wi->geometry()) - Rect_Width(&wi->geometry()) / 2);
117
118 if(wi->alignment() & ALIGN_BOTTOM)
119 Rect_SetY(&wi->geometry(), Rect_Y(&wi->geometry()) - Rect_Height(&wi->geometry()));
120 else if(!(wi->alignment() & ALIGN_TOP))
121 Rect_SetY(&wi->geometry(), Rect_Y(&wi->geometry()) - Rect_Height(&wi->geometry()) / 2);
122 }
123
124 #if defined(UI_DEBUG)
drawWidgetGeometry(HudWidget * wi)125 static void drawWidgetGeometry(HudWidget *wi)
126 {
127 DENG2_ASSERT(wi);
128
129 RectRaw geometry;
130 Rect_Raw(&wi->geometry(), &geometry);
131
132 DGL_Color3f(1, 1, 1);
133 DGL_Begin(DGL_LINES);
134 DGL_Vertex2f(geometry.origin.x, geometry.origin.y);
135 DGL_Vertex2f(geometry.origin.x + geometry.size.width, geometry.origin.y);
136 DGL_Vertex2f(geometry.origin.x + geometry.size.width, geometry.origin.y);
137 DGL_Vertex2f(geometry.origin.x + geometry.size.width, geometry.origin.y + geometry.size.height);
138 DGL_Vertex2f(geometry.origin.x + geometry.size.width, geometry.origin.y + geometry.size.height);
139 DGL_Vertex2f(geometry.origin.x, geometry.origin.y + geometry.size.height);
140 DGL_Vertex2f(geometry.origin.x, geometry.origin.y + geometry.size.height);
141 DGL_Vertex2f(geometry.origin.x, geometry.origin.y);
142 DGL_End();
143 }
144
drawWidgetAvailableSpace(HudWidget * wi)145 static void drawWidgetAvailableSpace(HudWidget *wi)
146 {
147 DENG2_ASSERT(wi);
148 DGL_Color4f(0, .4f, 0, .1f);
149 DGL_DrawRectf2(Rect_X(&wi->geometry()), Rect_Y(&wi->geometry()), wi->maximumSize().width, wi->maximumSize().height);
150 }
151 #endif
152
drawWidget2(HudWidget * wi,Point2Raw const * offset=nullptr)153 static void drawWidget2(HudWidget *wi, Point2Raw const *offset = nullptr)
154 {
155 DENG2_ASSERT(wi);
156
157 #if defined(UI_DEBUG)
158 drawWidgetAvailableSpace(wi);
159 #endif
160
161 if(wi->drawer && wi->opacity() > .0001f)
162 {
163 Point2Raw origin;
164 Point2_Raw(Rect_Origin(&wi->geometry()), &origin);
165
166 // Configure the page render state.
167 uiRS.pageAlpha = wi->opacity();
168
169 FR_PushAttrib();
170
171 DGL_MatrixMode(DGL_MODELVIEW);
172 DGL_Translatef(origin.x, origin.y, 0);
173
174 // Do not pass a zero length offset.
175 wi->drawer(wi, ((offset && (offset->x || offset->y))? offset : nullptr));
176
177 FR_PopAttrib();
178
179 DGL_MatrixMode(DGL_MODELVIEW);
180 DGL_Translatef(-origin.x, -origin.y, 0);
181 }
182
183 #if defined(UI_DEBUG)
184 drawWidgetGeometry(wi);
185 #endif
186 }
187
drawWidget(HudWidget * wi,Point2Raw const * origin=nullptr)188 static void drawWidget(HudWidget *wi, Point2Raw const *origin = nullptr)
189 {
190 DENG2_ASSERT(wi);
191
192 if(origin)
193 {
194 DGL_MatrixMode(DGL_MODELVIEW);
195 DGL_Translatef(origin->x, origin->y, 0);
196 }
197
198 // First ourself.
199 drawWidget2(wi);
200
201 if(auto *group = maybeAs<GroupWidget>(wi))
202 {
203 // Then our children.
204 group->forAllChildren([] (HudWidget &child)
205 {
206 drawWidget(&child);
207 return LoopContinue;
208 });
209 }
210
211 if(origin)
212 {
213 DGL_MatrixMode(DGL_MODELVIEW);
214 DGL_Translatef(-origin->x, -origin->y, 0);
215 }
216 }
217
GUI_DrawWidget(HudWidget * wi,Point2Raw const * offset)218 void GUI_DrawWidget(HudWidget *wi, Point2Raw const *offset)
219 {
220 if(!wi) return;
221 if(wi->maximumWidth() < 1 || wi->maximumHeight() < 1) return;
222 if(wi->opacity() <= 0) return;
223
224 FR_PushAttrib();
225 FR_LoadDefaultAttrib();
226 FR_SetLeading(0);
227
228 GUI_UpdateWidgetGeometry(wi);
229
230 FR_PopAttrib();
231
232 // Draw.
233 FR_PushAttrib();
234 FR_LoadDefaultAttrib();
235 FR_SetLeading(0);
236
237 // Do not pass a zero length offset.
238 drawWidget(wi, ((offset && (offset->x || offset->y))? offset : nullptr));
239
240 FR_PopAttrib();
241 }
242
GUI_DrawWidgetXY(HudWidget * wi,int x,int y)243 void GUI_DrawWidgetXY(HudWidget *wi, int x, int y)
244 {
245 Point2Raw origin = {{{x, y}}};
246 GUI_DrawWidget(wi, &origin);
247 }
248
GUI_SpriteSize(int sprite,float scale,int * width,int * height)249 void GUI_SpriteSize(int sprite, float scale, int *width, int *height)
250 {
251 spriteinfo_t info;
252 if(!width && !height) return;
253 if(!R_GetSpriteInfo(sprite, 0, &info)) return;
254
255 if(width) *width = info.geometry.size.width * scale;
256 if(height) *height = info.geometry.size.height * scale;
257 }
258
GUI_DrawSprite(int sprite,float x,float y,hotloc_t hotspot,float scale,float alpha,dd_bool flip,int * drawnWidth,int * drawnHeight)259 void GUI_DrawSprite(int sprite, float x, float y, hotloc_t hotspot,
260 float scale, float alpha, dd_bool flip, int* drawnWidth, int *drawnHeight)
261 {
262 spriteinfo_t info;
263
264 if(!(alpha > 0)) return;
265
266 alpha = MINMAX_OF(0.f, alpha, 1.f);
267 R_GetSpriteInfo(sprite, 0, &info);
268
269 switch(hotspot)
270 {
271 case HOT_BRIGHT:
272 y -= info.geometry.size.height * scale;
273 // Fall through.
274 case HOT_TRIGHT:
275 x -= info.geometry.size.width * scale;
276 break;
277
278 case HOT_BLEFT:
279 y -= info.geometry.size.height * scale;
280 break;
281 default: break;
282 }
283
284 DGL_SetPSprite(info.material);
285 DGL_Enable(DGL_TEXTURE_2D);
286
287 DGL_Color4f(1, 1, 1, alpha);
288 DGL_Begin(DGL_QUADS);
289 DGL_TexCoord2f(0, flip * info.texCoord[0], 0);
290 DGL_Vertex2f(x, y);
291
292 DGL_TexCoord2f(0, !flip * info.texCoord[0], 0);
293 DGL_Vertex2f(x + info.geometry.size.width * scale, y);
294
295 DGL_TexCoord2f(0, !flip * info.texCoord[0], info.texCoord[1]);
296 DGL_Vertex2f(x + info.geometry.size.width * scale, y + info.geometry.size.height * scale);
297
298 DGL_TexCoord2f(0, flip * info.texCoord[0], info.texCoord[1]);
299 DGL_Vertex2f(x, y + info.geometry.size.height * scale);
300 DGL_End();
301
302 DGL_Disable(DGL_TEXTURE_2D);
303
304 if(drawnWidth) *drawnWidth = info.geometry.size.width * scale;
305 if(drawnHeight) *drawnHeight = info.geometry.size.height * scale;
306 }
307
GUI_Register()308 void GUI_Register()
309 {
310 AutomapWidget::consoleRegister();
311 ChatWidget::consoleRegister();
312 PlayerLogWidget::consoleRegister();
313 }
314
GUI_LoadResources()315 void GUI_LoadResources()
316 {
317 if(Get(DD_NOVIDEO)) return;
318
319 AutomapWidget::prepareAssets();
320 menu::LineEditWidget::loadResources();
321 menu::SliderWidget::loadResources();
322 }
323
GUI_ReleaseResources()324 void GUI_ReleaseResources()
325 {
326 if(Get(DD_NOVIDEO)) return;
327
328 AutomapWidget::prepareAssets();
329
330 for(HudWidget *wi : widgets)
331 {
332 if(auto *automap = maybeAs<AutomapWidget>(wi))
333 {
334 automap->reset();
335 }
336 }
337 }
338