1 #include "engine.h" 2 3 int uimillis = -1; 4 5 VAR(IDF_READONLY, guilayoutpass, 1, 0, -1); 6 VAR(0, guicursortype, 0, 0, 2); 7 bool guiactionon = false; 8 int mouseaction[2] = {0}; 9 10 static float firstx, firsty; 11 12 enum {FIELDCOMMIT, FIELDABORT, FIELDEDIT, FIELDSHOW, FIELDKEY}; 13 static int fieldmode = FIELDSHOW; 14 static bool fieldsactive = false; 15 16 FVAR(IDF_PERSIST, guiscale, FVAR_NONZERO, 0.00055f, VAR_MAX); 17 VAR(IDF_PERSIST, guiskinsize, 0, 48, VAR_MAX); // 0 = texture size, otherwise = size in pixels for skin scaling 18 VAR(IDF_PERSIST, guislidersize, 1, 58, VAR_MAX); 19 VAR(IDF_PERSIST, guisepsize, 1, 6, VAR_MAX); 20 VAR(IDF_PERSIST, guispacesize, 1, 48, VAR_MAX); 21 VAR(IDF_PERSIST, guitooltipwidth, -1, -1, VAR_MAX); 22 VAR(IDF_PERSIST, guistatuswidth, -1, -1, VAR_MAX); 23 24 VAR(IDF_PERSIST, guishadow, 0, 2, 8); 25 VAR(IDF_PERSIST, guiclicktab, 0, 1, 1); 26 VAR(IDF_PERSIST, guitabborder, 0, 1, 2); 27 VAR(IDF_PERSIST, guitextblend, 1, 255, 255); 28 VAR(IDF_PERSIST, guitextfade, 1, 200, 255); 29 VAR(IDF_PERSIST, guiscaletime, 0, 250, VAR_MAX); 30 31 VAR(IDF_PERSIST, guiskinned, 0, 3, 3); // 0 = no backgrounds, 1 = drawn backgrounds, 2 = skinned backgrounds, 3 = skinned with overlay border 32 33 VAR(IDF_PERSIST|IDF_HEX, guibgcolour, -1, 0x000000, 0xFFFFFF); 34 FVAR(IDF_PERSIST, guibgblend, 0, 0.8f, 1); 35 VAR(IDF_PERSIST|IDF_HEX, guibordercolour, -1, 0x000000, 0xFFFFFF); 36 FVAR(IDF_PERSIST, guiborderblend, 0, 0.6f, 1); 37 38 VAR(IDF_PERSIST|IDF_HEX, guihovercolour, -1, 0xF0A0A0, 0xFFFFFF); 39 FVAR(IDF_PERSIST, guihoverscale, 0, 0.3f, 1); 40 FVAR(IDF_PERSIST, guihoverblend, 0, 0.9f, 1); 41 42 VAR(IDF_PERSIST, guistatusline, 0, 1, 1); 43 VAR(IDF_PERSIST, guitooltips, 0, 1, 1); 44 VAR(IDF_PERSIST, guitooltiptime, 0, 500, VAR_MAX); 45 VAR(IDF_PERSIST, guitooltipfade, 0, 500, VAR_MAX); 46 VAR(IDF_PERSIST|IDF_HEX, guitooltipcolour, -1, 0x000000, 0xFFFFFF); 47 FVAR(IDF_PERSIST, guitooltipblend, 0, 0.8f, 1); 48 VAR(IDF_PERSIST|IDF_HEX, guitooltipbordercolour, -1, 0x808080, 0xFFFFFF); 49 FVAR(IDF_PERSIST, guitooltipborderblend, 0, 0.6f, 1); 50 VAR(IDF_PERSIST, guitooltipborderskin, 0, 1, 1); 51 52 VAR(IDF_PERSIST|IDF_HEX, guifieldbgcolour, -1, 0x202020, 0xFFFFFF); 53 FVAR(IDF_PERSIST, guifieldbgblend, 0, 0.3f, 1); 54 VAR(IDF_PERSIST|IDF_HEX, guifieldbordercolour, -1, 0xA0A0A0, 0xFFFFFF); 55 FVAR(IDF_PERSIST, guifieldborderblend, 0, 0.6f, 1); 56 57 VAR(IDF_PERSIST|IDF_HEX, guifieldhitcolour, -1, 0xF04040, 0xFFFFFF); 58 FVAR(IDF_PERSIST, guifieldhitblend, 0, 0.8f, 1); 59 VAR(IDF_PERSIST|IDF_HEX, guifieldactivecolour, -1, 0xF04040, 0xFFFFFF); 60 FVAR(IDF_PERSIST, guifieldactiveblend, 0, 0.9f, 1); 61 62 VAR(IDF_PERSIST|IDF_HEX, guislidercolour, -1, 0x000000, 0xFFFFFF); 63 FVAR(IDF_PERSIST, guisliderblend, 0, 0.3f, 1); 64 VAR(IDF_PERSIST|IDF_HEX, guisliderbordercolour, -1, 0xC0C0C0, 0xFFFFFF); 65 FVAR(IDF_PERSIST, guisliderborderblend, 0, 0.6f, 1); 66 VAR(IDF_PERSIST, guisliderborderskin, 0, 2, 2); 67 VAR(IDF_PERSIST|IDF_HEX, guislidermarkcolour, -1, 0x808080, 0xFFFFFF); 68 FVAR(IDF_PERSIST, guislidermarkblend, 0, 0.5f, 1); 69 VAR(IDF_PERSIST|IDF_HEX, guislidermarkbordercolour, -1, 0xC0C0C0, 0xFFFFFF); 70 FVAR(IDF_PERSIST, guislidermarkborderblend, 0, 0.6f, 1); 71 VAR(IDF_PERSIST, guislidermarkborderskin, 0, 0, 2); 72 VAR(IDF_PERSIST|IDF_HEX, guislideractivecolour, -1, 0xF04040, 0xFFFFFF); 73 FVAR(IDF_PERSIST, guislideractiveblend, 0, 0.9f, 1); 74 75 VAR(IDF_PERSIST, guiactiveskin, 0, 1, 1); 76 VAR(IDF_PERSIST|IDF_HEX, guiactivecolour, -1, 0xF02020, 0xFFFFFF); 77 78 VAR(IDF_PERSIST|IDF_HEX, guicheckboxcolour, -1, 0x20F020, 0xFFFFFF); 79 VAR(IDF_PERSIST|IDF_HEX, guicheckboxtwocolour, -1, 0xF020F0, 0xFFFFFF); 80 VAR(IDF_PERSIST|IDF_HEX, guiradioboxcolour, -1, 0xF02020, 0xFFFFFF); 81 82 static bool needsinput = false, hastitle = true, hasbgfx = true, tooltipforce = false; 83 static char *statusstr = NULL, *tooltipstr = NULL, *tooltip = NULL; 84 static int lasttooltip = 0, statuswidth = 0, tooltipwidth = 0; 85 86 #include "textedit.h" 87 struct gui : guient 88 { 89 struct list { int parent, w, h, springs, curspring, mouse[2]; }; 90 91 int nextlist; 92 static vector<list> lists; 93 static float hitx, hity; 94 static int curdepth, curlist, xsize, ysize, curx, cury, fontdepth, mergelist, mergedepth; 95 static bool hitfx, skinfx, cursorfx; 96 resetgui97 static void reset() 98 { 99 if(statusstr) DELETEA(statusstr); 100 if(tooltipstr) DELETEA(tooltipstr); 101 lists.shrink(0); 102 statuswidth = tooltipwidth = 0; 103 mergelist = mergedepth = -1; 104 tooltipforce = false; 105 } 106 107 static int ty, tx, tpos, *tcurrent, tcolor; //tracking tab size and position since uses different layout method... 108 setcursortypegui109 int setcursortype(int type) { return guicursortype = type; } getcursortypegui110 int getcursortype() { return guicursortype; } allowcursorfxgui111 bool allowcursorfx(bool on) { return hitfx = on; } allowhitfxgui112 bool allowhitfx(bool on) { return cursorfx = on; } allowskinfxgui113 bool allowskinfx(bool on) { return skinfx = on; } visibletabgui114 bool visibletab() { return !tcurrent || tpos == *tcurrent; } visiblegui115 bool visible() { return !guilayoutpass && visibletab(); } 116 skingui117 void skin(int x1, int y1, int x2, int y2, int c1 = -1, float b1 = -1.f, int c2 = -1, float b2 = -1.f, bool skinborder = false) 118 { 119 int colour1 = c1 >= 0 ? c1 : (guibgcolour >= 0 ? guibgcolour : (c2 >= 0 ? c2 : 0x000000)), 120 colour2 = c2 >= 0 ? c2 : (guibordercolour >= 0 ? guibordercolour : 0x808080); 121 float blend1 = b1 >= 0 ? b1 : guibgblend, blend2 = b2 >= 0 ? b2 : guiborderblend; 122 switch(guiskinned) 123 { 124 case 2: case 3: 125 { 126 loopk(guiskinned == 3 || skinborder ? 2 : 1) 127 { 128 int colour = colour1; 129 float blend = blend1; 130 Texture *t = NULL; 131 switch(k) 132 { 133 case 1: 134 colour = colour2; 135 blend = blend2; 136 if(!skinbordertex) skinbordertex = textureload(guiskinbordertex, 0, true, false); 137 t = skinbordertex; 138 break; 139 case 0: default: 140 if(!skintex) skintex = textureload(guiskintex, 0, true, false); 141 t = skintex; 142 break; 143 } 144 drawskin(t, x1, y1, x2, y2, colour, blend, guiskinsize); 145 } 146 break; 147 } 148 case 1: 149 { 150 if(colour1 >= 0) 151 { 152 hudnotextureshader->set(); 153 gle::color(vec::hexcolor(colour1), blend1); 154 gle::defvertex(2); 155 gle::begin(GL_TRIANGLE_STRIP); 156 gle::attribf(x1, y1); 157 gle::attribf(x2, y1); 158 gle::attribf(x1, y2); 159 gle::attribf(x2, y2); 160 xtraverts += gle::end(); 161 hudshader->set(); 162 } 163 if(skinborder && colour2 >= 0) 164 { 165 hudnotextureshader->set(); 166 gle::color(vec::hexcolor(colour2), blend2); 167 gle::defvertex(2); 168 gle::begin(GL_LINE_LOOP); 169 gle::attribf(x1, y1); 170 gle::attribf(x2, y1); 171 gle::attribf(x2, y2); 172 gle::attribf(x1, y2); 173 xtraverts += gle::end(); 174 hudshader->set(); 175 } 176 break; 177 } 178 case 0: default: break; 179 } 180 } 181 182 //tab is always at top of page tabgui183 void tab(const char *name, int color, bool front) 184 { 185 if(curdepth != 0) return; 186 tpos++; 187 if(front && tcurrent && *tcurrent != tpos) *tcurrent = tpos; 188 if(!hastitle) 189 { 190 if(guilayoutpass) 191 { 192 ty = max(ty, ysize); 193 ysize = 0; 194 } 195 else cury = -ysize; 196 return; 197 } 198 if(color) tcolor = color; 199 if(!name) name = intstr(tpos); 200 gui::pushfont("super"); 201 int width = 0, height = 0; 202 text_bounds(name, width, height, 0, 0, -1, TEXT_NO_INDENT); 203 if(guilayoutpass) 204 { 205 ty = max(ty, ysize); 206 ysize = 0; 207 } 208 else 209 { 210 cury = -ysize; 211 int x1 = curx+tx, x2 = x1+width+guispacesize, y1 = cury-guispacesize-height, y2 = cury-guispacesize*3/4, alpha = guitextblend, border = -1; 212 if(!visibletab()) 213 { 214 if(tcurrent && !passthrough && fieldmode != FIELDKEY && hitx>=x1 && hity>=y1 && hitx<x2 && hity<y2) 215 { 216 if(!guiclicktab || mouseaction[0]&GUI_UP) *tcurrent = tpos; // switch tab 217 tcolor = guiactivecolour; 218 alpha = max(alpha, guitextfade); 219 if(guitabborder) border = tcolor; 220 } 221 else 222 { 223 tcolor = vec::hexcolor(tcolor).mul(0.25f).tohexcolor(); 224 if(guitabborder == 2) border = tcolor; 225 } 226 } 227 else if(guitabborder == 2) border = guibordercolour; 228 if(hasbgfx) skin(x1, y1, x2, y2, guibgcolour, guibgblend, border >= 0 ? border : guibordercolour, guiborderblend, border >= 0); 229 text_(name, x1+guispacesize/2, y1+guispacesize/8, tcolor, alpha); 230 } 231 tx += width+guispacesize*3/2; 232 gui::popfont(); 233 } 234 uibuttonsgui235 void uibuttons() 236 { 237 gui::pushfont("super"); 238 tx += FONTH+guispacesize*2; // acts like a tab 239 if(!guilayoutpass) 240 { 241 cury = -ysize; 242 int x1 = curx+(xsize-FONTH+guispacesize/4), x2 = x1+FONTH+guispacesize/4, y1 = cury-guispacesize-FONTH, y2 = cury-guispacesize*3/4; 243 //int x1 = curx+(xsize-FONTH), x2 = x1+FONTH*3/2, y1 = cury-FONTH*225/100, y2 = cury-FONTH*3/4; 244 #define uibtn(a,b) \ 245 { \ 246 int border = -1; \ 247 bool hit = false; \ 248 if(!passthrough && fieldmode != FIELDKEY && hitx>=x1 && hity>=y1 && hitx<x2 && hity<y2) \ 249 { \ 250 if(mouseaction[0]&GUI_UP) { b; } \ 251 hit = true; \ 252 if(guitabborder) border = guiactivecolour; \ 253 } \ 254 else if(guitabborder == 2) border = vec::hexcolor(guibordercolour).mul(0.25f).tohexcolor(); \ 255 if(hasbgfx) skin(x1, y1, x2, y2, guibgcolour, guibgblend, border >= 0 ? border : guibordercolour, guiborderblend, border >= 0); \ 256 x1 += guispacesize/8; \ 257 y1 += guispacesize/8; \ 258 icon_(a, false, x1, y1, FONTH, hit, 0xFFFFFF); \ 259 y1 += FONTH*3/2; \ 260 } 261 if(!exittex) exittex = textureload(guiexittex, 3, true, false); \ 262 uibtn(exittex, cleargui(1)); 263 } 264 gui::popfont(); 265 } 266 ishorizontalgui267 bool ishorizontal() const { return curdepth&1; } isverticalgui268 bool isvertical() const { return !ishorizontal(); } 269 pushlistgui270 void pushlist(bool merge) 271 { 272 if(guilayoutpass) 273 { 274 if(curlist >= 0) 275 { 276 lists[curlist].w = xsize; 277 lists[curlist].h = ysize; 278 } 279 list &l = lists.add(); 280 l.parent = curlist; 281 l.springs = 0; 282 curlist = lists.length()-1; 283 l.mouse[0] = l.mouse[1] = xsize = ysize = 0; 284 } 285 else 286 { 287 curlist = nextlist++; 288 if(curlist >= lists.length()) // should never get here unless script code doesn't use same amount of lists in layout and render passes 289 { 290 list &l = lists.add(); 291 l.parent = curlist; 292 l.springs = 0; 293 l.w = l.h = l.mouse[0] = l.mouse[1] = 0; 294 } 295 list &l = lists[curlist]; 296 l.curspring = 0; 297 if(l.springs > 0) 298 { 299 if(ishorizontal()) xsize = l.w; 300 else ysize = l.h; 301 } 302 else 303 { 304 xsize = l.w; 305 ysize = l.h; 306 } 307 } 308 curdepth++; 309 if(!guilayoutpass && visible() && ishit(xsize, ysize)) loopi(2) lists[curlist].mouse[i] = mouseaction[i]|GUI_ROLLOVER; 310 if(merge) 311 { 312 mergelist = curlist; 313 mergedepth = curdepth; 314 } 315 } 316 poplistgui317 int poplist() 318 { 319 if(!lists.inrange(curlist)) return 0; 320 list &l = lists[curlist]; 321 if(guilayoutpass) 322 { 323 l.w = xsize; 324 l.h = ysize; 325 } 326 curlist = l.parent; 327 curdepth--; 328 if(mergelist >= 0 && curdepth < mergedepth) mergelist = mergedepth = -1; 329 if(lists.inrange(curlist)) 330 { 331 int w = xsize, h = ysize; 332 if(ishorizontal()) cury -= h; 333 else curx -= w; 334 list &p = lists[curlist]; 335 xsize = p.w; 336 ysize = p.h; 337 if(!guilayoutpass && p.springs > 0) 338 { 339 list &s = lists[p.parent]; 340 if(ishorizontal()) xsize = s.w; 341 else ysize = s.h; 342 } 343 return layout(w, h); 344 } 345 return 0; 346 } 347 setstatusgui348 void setstatus(const char *fmt, int width, ...) 349 { 350 if(statusstr) DELETEA(statusstr); 351 defvformatstring(str, width, fmt); 352 statusstr = newstring(str); 353 if(width) statuswidth = width; 354 } 355 settooltipgui356 void settooltip(const char *fmt, int width, ...) 357 { 358 if(tooltipforce) return; // overridden in code 359 if(tooltipstr) DELETEA(tooltipstr); 360 defvformatstring(str, width, fmt); 361 tooltipstr = newstring(str); 362 if(width) tooltipwidth = width; 363 } 364 pushfontgui365 void pushfont(const char *font) { ::pushfont(font); fontdepth++; } popfontgui366 void popfont() { if(fontdepth) { ::popfont(); fontdepth--; } } 367 textgui368 int text(const char *text, int color, const char *icon, int icolour, int wrap, bool faded, const char *oicon, int ocolor) 369 { 370 return button_(text, color, icon, icolour, false, wrap, faded, oicon, ocolor); 371 } buttongui372 int button(const char *text, int color, const char *icon, int icolour, int wrap, bool faded, const char *oicon, int ocolor) 373 { 374 return button_(text, color, icon, icolour, true, wrap, faded, oicon, ocolor); 375 } 376 separatorgui377 void separator(int size, int space, int colour, int border) { line_(size > 0 ? size : guisepsize, space > 0 ? space : 0, colour, border); } 378 379 //use to set min size (useful when you have progress bars) strutgui380 void strut(float size) { layout(isvertical() ? int(size*FONTW) : 0, isvertical() ? 0 : int(size*FONTH)); } 381 //add space between list items spacegui382 void space(float size) { layout(isvertical() ? 0 : int(size*FONTW), isvertical() ? int(size*FONTH) : 0); } 383 springgui384 void spring(int weight) 385 { 386 if(curlist < 0) return; 387 list &l = lists[curlist]; 388 if(guilayoutpass) { if(l.parent >= 0) l.springs += weight; return; } 389 int nextspring = min(l.curspring + weight, l.springs); 390 if(nextspring <= l.curspring) return; 391 if(ishorizontal()) 392 { 393 int w = xsize - l.w; 394 layout((w*nextspring)/l.springs - (w*l.curspring)/l.springs, 0); 395 } 396 else 397 { 398 int h = ysize - l.h; 399 layout(0, (h*nextspring)/l.springs - (h*l.curspring)/l.springs); 400 } 401 l.curspring = nextspring; 402 } 403 layoutgui404 int layout(int w, int h) 405 { 406 if(guilayoutpass) 407 { 408 if(ishorizontal()) 409 { 410 xsize += w; 411 ysize = max(ysize, h); 412 } 413 else 414 { 415 xsize = max(xsize, w); 416 ysize += h; 417 } 418 } 419 else 420 { 421 bool hit = ishit(w, h); 422 if(ishorizontal()) curx += w; 423 else cury += h; 424 if(hit && visible()) return mouseaction[0]|GUI_ROLLOVER; 425 } 426 return 0; 427 } 428 ishitgui429 bool ishit(int w, int h, int x = curx, int y = cury) 430 { 431 if(passthrough || fieldmode == FIELDKEY) return false; 432 if(mergelist >= 0 && curdepth >= mergedepth && lists[mergelist].mouse[0]) return true; 433 if(ishorizontal()) h = ysize; 434 else w = xsize; 435 return hitx >= x && hity >= y && hitx < x+w && hity < y+h; 436 } 437 imagegui438 int image(Texture *t, float scale, bool overlaid, int icolour, Texture *o, int ocolour) 439 { 440 if(scale == 0) scale = 1; 441 int size = (int)(scale*2*FONTH)-guishadow; 442 if(visible()) icon_(t, overlaid, curx, cury, size, ishit(size+guishadow, size+guishadow), icolour, o, ocolour); 443 return layout(size+guishadow, size+guishadow); 444 } 445 texturegui446 int texture(VSlot &vslot, float scale, bool overlaid) 447 { 448 if(scale == 0) scale = 1; 449 int size = (int)(scale*2*FONTH)-guishadow; 450 if(visible()) previewslot(vslot, overlaid, curx, cury, size, ishit(size+guishadow, size+guishadow)); 451 return layout(size+guishadow, size+guishadow); 452 } 453 playerpreviewgui454 int playerpreview(int model, int color, int team, int weap, const char *vanity, float sizescale, bool overlaid, float scale, float blend) 455 { 456 if(sizescale == 0) sizescale = 1; 457 int size = (int)(sizescale*2*FONTH)-guishadow; 458 if(visible()) 459 { 460 bool hit = ishit(size+guishadow, size+guishadow); 461 float xs = size, ys = size, xi = curx, yi = cury, xpad = 0, ypad = 0; 462 if(overlaid) 463 { 464 xpad = xs/32; 465 ypad = ys/32; 466 xi += xpad; 467 yi += ypad; 468 xs -= 2*xpad; 469 ys -= 2*ypad; 470 } 471 int x1 = int(floor(screenw*(xi*uiscale.x+uiorigin.x))), y1 = int(floor(screenh*(1 - ((yi+ys)*uiscale.y+uiorigin.y)))), 472 x2 = int(ceil(screenw*((xi+xs)*uiscale.x+uiorigin.x))), y2 = int(ceil(screenh*(1 - (yi*uiscale.y+uiorigin.y)))); 473 glDisable(GL_BLEND); 474 modelpreview::start(x1, y1, x2-x1, y2-y1, overlaid); 475 game::renderplayerpreview(model, color, team, weap, vanity, scale, blend); 476 modelpreview::end(); 477 hudshader->set(); 478 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 479 glEnable(GL_BLEND); 480 if(overlaid) 481 { 482 if(!overlaytex) overlaytex = textureload(guioverlaytex, 3, true, false); 483 gle::color(hit && hitfx ? vec::hexcolor(guiactivecolour) : vec(1, 1, 1)); 484 glBindTexture(GL_TEXTURE_2D, overlaytex->id); 485 rect_(xi - xpad, yi - ypad, xs + 2*xpad, ys + 2*ypad, 0); 486 } 487 if(hit && hitfx && cursorfx && !guicursortype) guicursortype = 1; 488 } 489 return layout(size+guishadow, size+guishadow); 490 } 491 modelpreviewgui492 int modelpreview(const char *name, int anim, float sizescale, bool overlaid, float scale, float blend) 493 { 494 if(sizescale == 0) sizescale = 1; 495 int size = (int)(sizescale*2*FONTH)-guishadow; 496 if(visible()) 497 { 498 bool hit = ishit(size+guishadow, size+guishadow); 499 float xs = size, ys = size, xi = curx, yi = cury, xpad = 0, ypad = 0; 500 if(overlaid) 501 { 502 xpad = xs/32; 503 ypad = ys/32; 504 xi += xpad; 505 yi += ypad; 506 xs -= 2*xpad; 507 ys -= 2*ypad; 508 } 509 int x1 = int(floor(screenw*(xi*uiscale.x+uiorigin.x))), y1 = int(floor(screenh*(1 - ((yi+ys)*uiscale.y+uiorigin.y)))), 510 x2 = int(ceil(screenw*((xi+xs)*uiscale.x+uiorigin.x))), y2 = int(ceil(screenh*(1 - (yi*uiscale.y+uiorigin.y)))); 511 glDisable(GL_BLEND); 512 modelpreview::start(x1, y1, x2-x1, y2-y1, overlaid); 513 model *m = loadmodel(name); 514 if(m) 515 { 516 entitylight light; 517 light.color = vec(1, 1, 1); 518 light.dir = vec(0, -1, 2).normalize(); 519 vec center, radius; 520 m->boundbox(center, radius); 521 float yaw; 522 vec o = calcmodelpreviewpos(radius, yaw).sub(center); 523 rendermodel(&light, name, anim|ANIM_NOTRANS, o, yaw, 0, 0, 0, NULL, NULL, 0, 0, blend, scale); 524 } 525 modelpreview::end(); 526 hudshader->set(); 527 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 528 glEnable(GL_BLEND); 529 if(overlaid) 530 { 531 if(!overlaytex) overlaytex = textureload(guioverlaytex, 3, true, false); 532 gle::color(hit && hitfx ? vec::hexcolor(guiactivecolour) : vec(1, 1, 1)); 533 glBindTexture(GL_TEXTURE_2D, overlaytex->id); 534 rect_(xi - xpad, yi - ypad, xs + 2*xpad, ys + 2*ypad, 0); 535 } 536 if(hit && hitfx && cursorfx && !guicursortype) guicursortype = 1; 537 } 538 return layout(size+guishadow, size+guishadow); 539 } 540 prefabpreviewgui541 int prefabpreview(const char *prefab, const vec &color, float sizescale, bool overlaid) 542 { 543 if(sizescale==0) sizescale = 1; 544 int size = (int)(sizescale*2*FONTH)-guishadow; 545 if(visible()) 546 { 547 bool hit = ishit(size+guishadow, size+guishadow); 548 float xs = size, ys = size, xi = curx, yi = cury, xpad = 0, ypad = 0; 549 if(overlaid) 550 { 551 xpad = xs/32; 552 ypad = ys/32; 553 xi += xpad; 554 yi += ypad; 555 xs -= 2*xpad; 556 ys -= 2*ypad; 557 } 558 int x1 = int(floor(screenw*(xi*uiscale.x+uiorigin.x))), y1 = int(floor(screenh*(1 - ((yi+ys)*uiscale.y+uiorigin.y)))), 559 x2 = int(ceil(screenw*((xi+xs)*uiscale.x+uiorigin.x))), y2 = int(ceil(screenh*(1 - (yi*uiscale.y+uiorigin.y)))); 560 glDisable(GL_BLEND); 561 modelpreview::start(x1, y1, x2-x1, y2-y1, overlaid); 562 previewprefab(prefab, color); 563 modelpreview::end(); 564 hudshader->set(); 565 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 566 glEnable(GL_BLEND); 567 if(overlaid) 568 { 569 if(!overlaytex) overlaytex = textureload(guioverlaytex, 3, true, false); 570 gle::color(hit && hitfx ? vec::hexcolor(guiactivecolour) : vec(1, 1, 1)); 571 glBindTexture(GL_TEXTURE_2D, overlaytex->id); 572 rect_(xi - xpad, yi - ypad, xs + 2*xpad, ys + 2*ypad, 0); 573 } 574 if(hit && hitfx && cursorfx && !guicursortype) guicursortype = 1; 575 } 576 return layout(size+guishadow, size+guishadow); 577 } 578 slicegui579 int slice(Texture *t, float scale, float start, float end, const char *text) 580 { 581 if(scale == 0) scale = 1; 582 int size = (int)(scale*2*FONTH); 583 if(t!=notexture && visible()) slice_(t, curx, cury, size, start, end, text); 584 return layout(size, size); 585 } 586 progressgui587 void progress(float percent, float scale) 588 { 589 if(scale == 0) scale = 1; 590 int size = (int)(scale*2*FONTH); 591 slice_(textureload(hud::progringtex, 3, true, false), curx, cury, size, (SDL_GetTicks()%1000)/1000.f, 0.1f); 592 string s; if(percent > 0) formatstring(s, "\fg%d%%", int(percent*100)); else formatstring(s, "\fg..."); 593 slice_(textureload(hud::progresstex, 3, true, false), curx, cury, size, 0, percent, s); 594 layout(size, size); 595 } 596 slidergui597 int slider(int &val, int vmin, int vmax, int colour, const char *label, bool reverse, bool scroll, int style, int scolour) 598 { 599 int x = curx, y = cury; 600 float percent = (val-vmin)/float(max(vmax-vmin, 1)); 601 bool hit = false; 602 int space = slider_(guislidersize, percent, ishorizontal() ? FONTW*3 : FONTH, hit, style, scolour); 603 if(visible()) 604 { 605 if(hit) 606 { 607 if(!label) label = intstr(val); 608 settooltip("\f[%d]%s", -1, colour, label); 609 tooltipforce = true; 610 if(mouseaction[0]&GUI_PRESSED) 611 { 612 int vnew = vmax-vmin+1; 613 if(ishorizontal()) vnew = int((vnew*(reverse ? hity-y-guislidersize/2 : y+ysize-guislidersize/2-hity))/(ysize-guislidersize)); 614 else vnew = int((vnew*(reverse ? x+xsize-guislidersize/2-hitx : hitx-x-guislidersize/2))/(xsize-guislidersize)); 615 vnew += vmin; 616 vnew = clamp(vnew, vmin, vmax); 617 if(vnew != val) val = vnew; 618 } 619 else if(mouseaction[1]&GUI_UP) 620 { 621 int vval = val+(reverse == !(mouseaction[1]&GUI_ALT) ? -1 : 1), 622 vnew = clamp(vval, vmin, vmax); 623 if(vnew != val) val = vnew; 624 } 625 } 626 else if(scroll && lists[curlist].mouse[1]&GUI_UP) 627 { 628 int vval = val+(reverse == !(lists[curlist].mouse[1]&GUI_ALT) ? -1 : 1), 629 vnew = clamp(vval, vmin, vmax); 630 if(vnew != val) val = vnew; 631 } 632 } 633 return space; 634 } 635 fieldgui636 char *field(const char *name, int color, int length, int height, const char *initval, int initmode, bool focus, const char *parent, const char *prompt, bool immediate) 637 { 638 return field_(name, color, length, height, initval, initmode, FIELDEDIT, focus, parent, prompt, immediate); 639 } 640 keyfieldgui641 char *keyfield(const char *name, int color, int length, int height, const char *initval, int initmode, bool focus, const char *parent, const char *prompt, bool immediate) 642 { 643 return field_(name, color, length, height, initval, initmode, FIELDKEY, focus, parent, prompt, immediate); 644 } 645 field_gui646 char *field_(const char *name, int color, int length, int height, const char *initval, int initmode, int fieldtype = FIELDEDIT, bool focus = false, const char *parent = NULL, const char *prompt = NULL, bool immediate = false) 647 { 648 editor *e = useeditor(name, initmode, false, initval, parent); // generate a new editor if necessary 649 if(guilayoutpass) 650 { 651 if(initval && (e->lines.empty() || (e->mode == EDITORFOCUSED && (e != currentfocus() || fieldmode == FIELDSHOW) && strcmp(e->lines[0].text, initval)))) 652 e->clear(initval); 653 e->linewrap = (length < 0); 654 e->maxx = e->linewrap ? -1 : length; 655 e->maxy = (height <= 0) ? 1 : -1; 656 e->pixelwidth = (abs(length)+1)*FONTW; 657 if(e->linewrap && e->maxy == 1) 658 { 659 int temp = 0; 660 text_bounds(e->lines[0].text, temp, e->pixelheight, 0, 0, e->pixelwidth, TEXT_NO_INDENT); // only single line editors can have variable height 661 int ph = e->pixelheight%FONTH; 662 if(ph) e->pixelheight += FONTH-ph; 663 } 664 else e->pixelheight = max(height, 1)*FONTH; 665 } 666 int hpad = FONTH/4, wpad = FONTW, h = e->pixelheight+hpad, w = e->pixelwidth+wpad; 667 668 bool wasvertical = isvertical(); 669 if(wasvertical && e->maxy != 1) pushlist(false); 670 671 char *result = NULL; 672 if(visible()) 673 { 674 e->rendered = true; 675 if(focus && e->unfocus) focus = false; 676 bool hit = ishit(w, h) && e->mode != EDITORREADONLY, clrs = fieldtype == FIELDKEY, 677 editing = (fieldmode != FIELDSHOW) && e == currentfocus() && e->mode != EDITORREADONLY; 678 if(mouseaction[0]&GUI_UP && mergedepth >= 0 && hit) mouseaction[0] &= ~GUI_UP; 679 if(mouseaction[0]&GUI_DOWN) //mouse request focus 680 { 681 if(hit) 682 { 683 focus = true; 684 if(mouseaction[0]&GUI_ALT) clrs = true; 685 if(e->unfocus) e->unfocus = false; 686 } 687 else if(editing) fieldmode = e->mode != EDITORREADONLY && e->mode != EDITORFOREVER ? FIELDCOMMIT : FIELDSHOW; 688 } 689 if(focus) 690 { 691 if(clrs) e->clear(); 692 useeditor(e->name, initmode, true, initval, parent); 693 e->mark(false); 694 if(fieldmode != FIELDCOMMIT && fieldmode != FIELDABORT) fieldmode = fieldtype; 695 } 696 if(hit && editing && (mouseaction[0]&GUI_PRESSED) && fieldtype == FIELDEDIT) 697 e->hit(int(floor(hitx-(curx+wpad/2))), int(floor(hity-(cury+hpad/2))), (mouseaction[0]&GUI_DRAGGED)!=0); //mouse request position 698 if(editing && (fieldmode == FIELDCOMMIT || fieldmode == FIELDABORT)) // commit field if user pressed enter 699 { 700 if(fieldmode == FIELDCOMMIT) result = e->currentline().text; 701 e->active = (e->mode != EDITORFOCUSED); 702 fieldmode = FIELDSHOW; 703 } 704 else 705 { 706 if(editing && immediate) result = e->currentline().text; 707 fieldsactive = true; 708 } 709 skin(curx, cury, curx+w, cury+h, guifieldbgcolour, guifieldbgblend, editing ? guifieldactivecolour : (hit ? guifieldhitcolour : guifieldbordercolour), editing ? guifieldactiveblend : (hit ? guifieldhitblend : guifieldborderblend), true); 710 e->draw(curx+wpad/2, cury+hpad/2, color, editing, prompt); 711 if(hit && !guicursortype) guicursortype = 2; 712 } 713 else if(e->unfocus) e->unfocus = false; 714 layout(w, h); 715 if(e->maxy != 1) 716 { 717 int slines = e->limitscrolly(); 718 if(slines > 0) 719 { 720 int oldpos = e->scrolly == editor::SCROLLEND ? slines : e->scrolly, newpos = oldpos; 721 slider(newpos, 0, slines, color, NULL, true, true, 0, -1); 722 if(oldpos != newpos) 723 { 724 e->cy = newpos; 725 e->scrolly = e->mode == EDITORREADONLY && newpos >= slines ? editor::SCROLLEND : newpos; 726 } 727 } 728 if(wasvertical) poplist(); 729 } 730 return result; 731 } 732 rect_gui733 void rect_(float x, float y, float w, float h, bool lines = false) 734 { 735 gle::defvertex(2); 736 gle::begin(lines ? GL_LINE_LOOP : GL_TRIANGLE_STRIP); 737 gle::attribf(x, y); 738 gle::attribf(x + w, y); 739 if(lines) gle::attribf(x + w, y + h); 740 gle::attribf(x, y + h); 741 if(!lines) gle::attribf(x + w, y + h); 742 xtraverts += gle::end(); 743 } 744 rect_gui745 void rect_(float x, float y, float w, float h, int usetc) 746 { 747 gle::defvertex(2); 748 gle::deftexcoord0(); 749 gle::begin(GL_TRIANGLE_STRIP); 750 static const vec2 tc[5] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1), vec2(0, 0) }; 751 gle::attribf(x, y); gle::attrib(tc[usetc]); 752 gle::attribf(x + w, y); gle::attrib(tc[usetc+1]); 753 gle::attribf(x, y + h); gle::attrib(tc[usetc+3]); 754 gle::attribf(x + w, y + h); gle::attrib(tc[usetc+2]); 755 xtraverts += gle::end(); 756 } 757 text_gui758 void text_(const char *text, int x, int y, int color, int alpha, bool hit = false, int wrap = -1) 759 { 760 int flags = TEXT_NO_INDENT; 761 if(hit) 762 { 763 if(guiactiveskin && skinfx) 764 { 765 flags |= TEXT_SKIN; 766 color = guiactivecolour; 767 } 768 else if(hitfx) 769 { 770 color = guiactivecolour; 771 if(cursorfx && !guicursortype) guicursortype = 1; 772 } 773 } 774 draw_textf("%s", x, y, 0, 0, color>>16, (color>>8)&0xFF, color&0xFF, alpha, flags, -1, wrap > 0 ? wrap : -1, 1, text); 775 } 776 fillgui777 void fill(int color, int inheritw, int inherith) 778 { 779 if(!visible()) return; 780 hudnotextureshader->set(); 781 gle::colorub(color>>16, (color>>8)&0xFF, color&0xFF, 0x80); 782 int w = xsize, h = ysize; 783 if(inheritw>0) 784 { 785 int parentw = curlist, parentdepth = 0; 786 for(;parentdepth < inheritw && lists[parentw].parent>=0; parentdepth++) 787 parentw = lists[parentw].parent; 788 list &p = lists[parentw]; 789 w = p.springs > 0 && (curdepth-parentdepth)&1 ? lists[p.parent].w : p.w; 790 } 791 if(inherith>0) 792 { 793 int parenth = curlist, parentdepth = 0; 794 for(;parentdepth < inherith && lists[parenth].parent>=0; parentdepth++) 795 parenth = lists[parenth].parent; 796 list &p = lists[parenth]; 797 h = p.springs > 0 && !((curdepth-parentdepth)&1) ? lists[p.parent].h : p.h; 798 } 799 rect_(curx, cury, w, h, false); 800 hudshader->set(); 801 } 802 outlinegui803 void outline(int color, int inheritw, int inherith, int offsetx, int offsety) 804 { 805 if(!visible()) return; 806 hudnotextureshader->set(); 807 gle::colorub(color>>16, (color>>8)&0xFF, color&0xFF, 0x80); 808 int w = xsize, h = ysize; 809 if(inheritw>0) 810 { 811 int parentw = curlist, parentdepth = 0; 812 for(;parentdepth < inheritw && lists[parentw].parent>=0; parentdepth++) 813 parentw = lists[parentw].parent; 814 list &p = lists[parentw]; 815 w = p.springs > 0 && (curdepth-parentdepth)&1 ? lists[p.parent].w : p.w; 816 } 817 if(inherith>0) 818 { 819 int parenth = curlist, parentdepth = 0; 820 for(;parentdepth < inherith && lists[parenth].parent>=0; parentdepth++) 821 parenth = lists[parenth].parent; 822 list &p = lists[parenth]; 823 h = p.springs > 0 && !((curdepth-parentdepth)&1) ? lists[p.parent].h : p.h; 824 } 825 rect_(curx+offsetx, cury+offsety, w-offsetx*2, h-offsety*2, true); 826 hudshader->set(); 827 } 828 backgroundgui829 void background(int colour1, float blend1, int colour2, float blend2, bool skinborder, int inheritw, int inherith) 830 { 831 if(!visible()) return; 832 int w = xsize, h = ysize; 833 if(inheritw>0) 834 { 835 int parentw = curlist, parentdepth = 0; 836 for(;parentdepth < inheritw && lists[parentw].parent>=0; parentdepth++) 837 parentw = lists[parentw].parent; 838 list &p = lists[parentw]; 839 w = p.springs > 0 && (curdepth-parentdepth)&1 ? lists[p.parent].w : p.w; 840 } 841 if(inherith>0) 842 { 843 int parenth = curlist, parentdepth = 0; 844 for(;parentdepth < inherith && lists[parenth].parent>=0; parentdepth++) 845 parenth = lists[parenth].parent; 846 list &p = lists[parenth]; 847 h = p.springs > 0 && !((curdepth-parentdepth)&1) ? lists[p.parent].h : p.h; 848 } 849 skin(curx, cury, curx+w, cury+h, colour1, blend1, colour2, blend2, skinborder); 850 } 851 icon_gui852 void icon_(Texture *t, bool overlaid, int x, int y, int size, bool hit, int icolour = 0xFFFFFF, Texture *o = NULL, int ocolor = 0xFFFFFF) 853 { 854 static const vec2 tc[4] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) }; 855 float xs = 0, ys = 0; 856 int textureid = -1; 857 if(t) 858 { 859 float scale = float(size)/max(t->xs, t->ys); //scale and preserve aspect ratio 860 xs = t->xs*scale; 861 ys = t->ys*scale; 862 x += int((size-xs)/2); 863 y += int((size-ys)/2); 864 textureid = t->id; 865 } 866 else 867 { 868 if(lightmapping && lmprogtex) 869 { 870 float scale = float(size)/256; //scale and preserve aspect ratio 871 xs = 256*scale; ys = 256*scale; 872 x += int((size-xs)/2); 873 y += int((size-ys)/2); 874 textureid = lmprogtex; 875 } 876 else 877 { 878 defformatstring(texname, "%s", mapname); 879 if((t = textureload(texname, 3, true, false)) == notexture) t = textureload(emblemtex, 3, true, false); 880 float scale = float(size)/max(t->xs, t->ys); //scale and preserve aspect ratio 881 xs = t->xs*scale; ys = t->ys*scale; 882 x += int((size-xs)/2); 883 y += int((size-ys)/2); 884 textureid = t->id; 885 } 886 } 887 float xi = x, yi = y, xpad = 0, ypad = 0; 888 if(overlaid) 889 { 890 xpad = xs/32; 891 ypad = ys/32; 892 xi += xpad; 893 yi += ypad; 894 xs -= 2*xpad; 895 ys -= 2*ypad; 896 } 897 gle::defvertex(2); 898 gle::deftexcoord0(); 899 if(hit && hitfx) 900 { 901 float offx = xs*guihoverscale, offy = ys*guihoverscale; 902 if(!hovertex) hovertex = textureload(guihovertex, 3, true, false); 903 glBindTexture(GL_TEXTURE_2D, hovertex->id); 904 gle::color(vec::hexcolor(guihovercolour), guihoverblend); 905 gle::begin(GL_TRIANGLE_STRIP); 906 gle::attribf(xi-offx, yi-offy); gle::attrib(tc[0]); 907 gle::attribf(xi+xs+offx, yi-offy); gle::attrib(tc[1]); 908 gle::attribf(xi-offx, yi+ys+offy); gle::attrib(tc[3]); 909 gle::attribf(xi+xs+offx, yi+ys+offy); gle::attrib(tc[2]); 910 xtraverts += gle::end(); 911 } 912 glBindTexture(GL_TEXTURE_2D, textureid); 913 gle::color(vec::hexcolor(icolour), 1.f); 914 gle::begin(GL_TRIANGLE_STRIP); 915 gle::attribf(xi, yi); gle::attrib(tc[0]); 916 gle::attribf(xi+xs, yi); gle::attrib(tc[1]); 917 gle::attribf(xi, yi+ys); gle::attrib(tc[3]); 918 gle::attribf(xi+xs, yi+ys); gle::attrib(tc[2]); 919 xtraverts += gle::end(); 920 if(o) 921 { 922 glBindTexture(GL_TEXTURE_2D, o->id); 923 gle::color(vec::hexcolor(ocolor), 1.f); 924 gle::begin(GL_TRIANGLE_STRIP); 925 gle::attribf(xi, yi); gle::attrib(tc[0]); 926 gle::attribf(xi+xs, yi); gle::attrib(tc[1]); 927 gle::attribf(xi, yi+ys); gle::attrib(tc[3]); 928 gle::attribf(xi+xs, yi+ys); gle::attrib(tc[2]); 929 xtraverts += gle::end(); 930 } 931 if(overlaid) 932 { 933 if(!overlaytex) overlaytex = textureload(guioverlaytex, 3, true, false); 934 gle::color(hit && hitfx ? vec::hexcolor(guiactivecolour) : vec(1, 1, 1), 1.f); 935 glBindTexture(GL_TEXTURE_2D, overlaytex->id); 936 rect_(xi - xpad, yi - ypad, xs + 2*xpad, ys + 2*ypad, 0); 937 } 938 if(hit && hitfx && cursorfx && !guicursortype) guicursortype = 1; 939 } 940 previewslotgui941 void previewslot(VSlot &vslot, bool overlaid, int x, int y, int size, bool hit) 942 { 943 Slot &slot = *vslot.slot; 944 if(slot.sts.empty()) return; 945 VSlot *layer = NULL; 946 Texture *t = NULL, *glowtex = NULL, *layertex = NULL; 947 if(slot.loaded) 948 { 949 t = slot.sts[0].t; 950 if(t == notexture) return; 951 Slot &slot = *vslot.slot; 952 if(slot.texmask&(1<<TEX_GLOW)) { loopvj(slot.sts) if(slot.sts[j].type==TEX_GLOW) { glowtex = slot.sts[j].t; break; } } 953 if(vslot.layer) 954 { 955 layer = &lookupvslot(vslot.layer); 956 if(!layer->slot->sts.empty()) layertex = layer->slot->sts[0].t; 957 } 958 } 959 else if(slot.thumbnail && slot.thumbnail != notexture) t = slot.thumbnail; 960 else return; 961 float xt = min(1.0f, t->xs/(float)t->ys), yt = min(1.0f, t->ys/(float)t->xs), xs = size, ys = size; 962 float xi = x, yi = y, xpad = 0, ypad = 0; 963 if(overlaid) 964 { 965 xpad = xs/32; 966 ypad = ys/32; 967 xi += xpad; 968 yi += ypad; 969 xs -= 2*xpad; 970 ys -= 2*ypad; 971 } 972 SETSHADER(hudrgb); 973 gle::defvertex(2); 974 gle::deftexcoord0(); 975 const vec &color = hit && hitfx && !overlaid ? vec(1.25f, 1.25f, 1.25f) : vec(1, 1, 1); 976 vec2 tc[4] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) }; 977 int xoff = vslot.offset.x, yoff = vslot.offset.y; 978 if(vslot.rotation) 979 { 980 if((vslot.rotation&5) == 1) { swap(xoff, yoff); loopk(4) swap(tc[k].x, tc[k].y); } 981 if(vslot.rotation >= 2 && vslot.rotation <= 4) { xoff *= -1; loopk(4) tc[k].x *= -1; } 982 if(vslot.rotation <= 2 || vslot.rotation == 5) { yoff *= -1; loopk(4) tc[k].y *= -1; } 983 } 984 loopk(4) { tc[k].x = tc[k].x/xt - xoff/t->xs; tc[k].y = tc[k].y/yt - yoff/t->ys; } 985 if(slot.loaded) gle::color(vec(color).mul(vslot.getcolorscale())); 986 else gle::color(color); 987 glBindTexture(GL_TEXTURE_2D, t->id); 988 gle::begin(GL_TRIANGLE_STRIP); 989 gle::attribf(xi, yi); gle::attrib(tc[0]); 990 gle::attribf(xi+xs, yi); gle::attrib(tc[1]); 991 gle::attribf(xi, yi+ys); gle::attrib(tc[3]); 992 gle::attribf(xi+xs, yi+ys); gle::attrib(tc[2]); 993 gle::end(); 994 if(glowtex) 995 { 996 glBlendFunc(GL_SRC_ALPHA, GL_ONE); 997 glBindTexture(GL_TEXTURE_2D, glowtex->id); 998 vec glowcolor = vslot.getglowcolor(); 999 if(hit || overlaid) gle::color(vec(glowcolor).mul(color)); 1000 else gle::color(glowcolor); 1001 gle::begin(GL_TRIANGLE_STRIP); 1002 gle::attribf(x, y); gle::attrib(tc[0]); 1003 gle::attribf(x+xs, y); gle::attrib(tc[1]); 1004 gle::attribf(x, y+ys); gle::attrib(tc[3]); 1005 gle::attribf(x+xs, y+ys); gle::attrib(tc[2]); 1006 gle::end(); 1007 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 1008 } 1009 if(layertex) 1010 { 1011 glBindTexture(GL_TEXTURE_2D, layertex->id); 1012 gle::color(vec(color).mul(layer->getcolorscale())); 1013 gle::begin(GL_TRIANGLE_STRIP); 1014 gle::attribf(x+xs/2, y+ys/2); gle::attrib(tc[0]); 1015 gle::attribf(x+xs, y+ys/2); gle::attrib(tc[1]); 1016 gle::attribf(x+xs/2, y+ys); gle::attrib(tc[3]); 1017 gle::attribf(x+xs, y+ys); gle::attrib(tc[2]); 1018 gle::end(); 1019 } 1020 1021 hudshader->set(); 1022 if(overlaid) 1023 { 1024 if(!overlaytex) overlaytex = textureload(guioverlaytex, 3, true, false); 1025 gle::color(hit && hitfx ? vec::hexcolor(guiactivecolour) : vec(1, 1, 1)); 1026 glBindTexture(GL_TEXTURE_2D, overlaytex->id); 1027 rect_(xi - xpad, yi - ypad, xs + 2*xpad, ys + 2*ypad, 0); 1028 } 1029 if(hit && hitfx && cursorfx && !guicursortype) guicursortype = 1; 1030 } 1031 slice_gui1032 void slice_(Texture *t, int x, int y, int size, float start = 0, float end = 1, const char *text = NULL) 1033 { 1034 float scale = float(size)/max(t->xs, t->ys), xs = t->xs*scale, ys = t->ys*scale, fade = 1; 1035 if(start == end) { end = 1; fade = 0.5f; } 1036 glBindTexture(GL_TEXTURE_2D, t->id); 1037 gle::colorf(1, 1, 1, fade); 1038 int s = max(xs,ys)/2; 1039 drawslice(start, end, x+s/2, y+s/2, s); 1040 if(text && *text) 1041 { 1042 int w = int(ceil(text_widthf(text, 0, 0, TEXT_NO_INDENT))); 1043 text_(text, x+s/2-w/2, y+s/2-FONTH/2, 0xFFFFFF, guitextblend); 1044 } 1045 } 1046 slider_gui1047 int slider_(int size, float percent, int space, bool &hit, int style, int colour) 1048 { 1049 space = max(max(space, FONTW), size); 1050 if(visible()) 1051 { 1052 int x = ishorizontal() ? curx+space/2-size/2 : curx, w = ishorizontal() ? size : xsize, 1053 y = ishorizontal() ? cury : cury+space/2-size/2, h = ishorizontal() ? ysize : size; 1054 hit = ishit(w, h, x, y); 1055 skin(x, y, x+w, y+h, guislidercolour, guisliderblend, hit ? guislideractivecolour : guisliderbordercolour, hit ? guislideractiveblend : guisliderborderblend, guisliderborderskin >= (hit ? 1 : 2)); 1056 if(percent >= 0 && percent <= 1) 1057 { 1058 int px = x+size/8, py = y+size/8, pw = size*3/4, ph = size*3/4; 1059 switch(style) 1060 { 1061 case 1: 1062 if(ishorizontal()) ph = (h-size/4)*percent; 1063 else pw = (w-size/4)*percent; 1064 break; 1065 case 0: default: 1066 if(ishorizontal()) py += (h-size)*percent; 1067 else px += (w-size)*percent; 1068 break; 1069 } 1070 skin(px, py, px+pw, py+ph, colour >= 0 ? colour : guislidermarkcolour, guislidermarkblend, hit ? guislideractivecolour : guislidermarkbordercolour, hit ? guislideractiveblend : guislidermarkborderblend, guislidermarkborderskin >= (hit ? 1 : 2)); 1071 } 1072 if(hit && hitfx && cursorfx && !guicursortype) guicursortype = 1; 1073 } 1074 layout(ishorizontal() ? space : 0, ishorizontal() ? 0 : space); 1075 return space; 1076 } 1077 line_gui1078 int line_(int size, int space = 0, int colour = -1, int border = -1) 1079 { 1080 space = max(max(space, FONTW), size); 1081 if(visible()) 1082 { 1083 int colour1 = colour >= 0 ? colour : (guibgcolour >= 0 ? guibgcolour : (guibordercolour >= 0 ? guibordercolour : 0x000000)), 1084 colour2 = border >= 0 ? border : (guibordercolour >= 0 ? guibordercolour : 0x808080), 1085 x1 = ishorizontal() ? curx+space/2-size/2 : curx, x2 = x1+(ishorizontal() ? size : xsize), 1086 y1 = ishorizontal() ? cury : cury+space/2-size/2, y2 = y1+(ishorizontal() ? ysize : size); 1087 if(colour1 >= 0) 1088 { 1089 hudnotextureshader->set(); 1090 gle::color(vec::hexcolor(colour1), guibgblend); 1091 gle::defvertex(2); 1092 gle::begin(GL_TRIANGLE_STRIP); 1093 gle::attribf(x1, y1); 1094 gle::attribf(x2, y1); 1095 gle::attribf(x1, y2); 1096 gle::attribf(x2, y2); 1097 xtraverts += gle::end(); 1098 hudshader->set(); 1099 } 1100 if(colour2 >= 0) 1101 { 1102 hudnotextureshader->set(); 1103 gle::color(vec::hexcolor(colour2), guiborderblend); 1104 gle::defvertex(2); 1105 gle::begin(GL_LINE_LOOP); 1106 gle::attribf(x1, y1); 1107 gle::attribf(x2, y1); 1108 gle::attribf(x2, y2); 1109 gle::attribf(x1, y2); 1110 xtraverts += gle::end(); 1111 hudshader->set(); 1112 } 1113 } 1114 layout(ishorizontal() ? space : 0, ishorizontal() ? 0 : space); 1115 return space; 1116 } 1117 button_gui1118 int button_(const char *text, int color, const char *icon, int icolour, bool clickable, int wrap = -1, bool faded = true, const char *oicon = NULL, int ocolor = 0xFFFFFF) 1119 { 1120 int w = 0, h = 0; 1121 if((icon && *icon) || (oicon && *oicon)) 1122 { 1123 w += FONTH; 1124 if(text && *text) w += 8; 1125 } 1126 if(text && *text) 1127 { 1128 int tw = 0, th = 0; 1129 text_bounds(text, tw, th, 0, 0, wrap > 0 ? wrap : -1, TEXT_NO_INDENT); 1130 w += tw; 1131 h += th; 1132 } 1133 else h = FONTH; 1134 1135 if(visible()) 1136 { 1137 bool hit = ishit(w, h); 1138 int x = curx; 1139 if((icon && *icon) || (oicon && *oicon)) 1140 { 1141 Texture *ttex = icon && *icon ? textureload(strstr(icon, "textures/") ? icon : makerelpath("textures", icon), 3, true, false) : NULL, 1142 *otex = oicon && *oicon ? textureload(strstr(oicon, "textures/") ? oicon : makerelpath("textures", oicon), 3, true, false) : NULL; 1143 if(otex == notexture) otex = NULL; 1144 icon_(ttex, false, x, cury, FONTH, clickable && hit, icolour, otex, ocolor); 1145 x += FONTH; 1146 if(text && *text) x += 8; 1147 } 1148 if(text && *text) text_(text, x, cury, color, (hit && hitfx) || !faded || !clickable ? guitextblend : guitextfade, hit && clickable, wrap > 0 ? wrap : -1); 1149 if(clickable && hit && hitfx && cursorfx && !guicursortype) guicursortype = 1; 1150 } 1151 return layout(w, h); 1152 } 1153 1154 static Texture *skintex, *skinbordertex, *overlaytex, *exittex, *hovertex; 1155 1156 vec uiorigin, uiscale; 1157 guicb *cb; 1158 1159 static float basescale, maxscale; 1160 static bool passthrough; 1161 adjustscalegui1162 void adjustscale() 1163 { 1164 int w = xsize+FONTW*8, h = ysize+FONTH*8; 1165 float aspect = forceaspect ? 1.0f/forceaspect : float(screenh)/float(screenw), fit = 1.0f; 1166 if(w*aspect*basescale>1.0f) fit = 1.0f/(w*aspect*basescale); 1167 if(h*basescale*fit>maxscale) fit *= maxscale/(h*basescale*fit); 1168 uiscale = vec(aspect*uiscale.x*fit, uiscale.y*fit, 1); 1169 uiorigin = vec(0.5f - ((w-xsize)/2 - (FONTW*4))*uiscale.x, 0.5f + (0.5f*h-(FONTH*4))*uiscale.y, 0); 1170 } 1171 startgui1172 void start(int starttime, int *tab, bool allowinput, bool wantstitle, bool wantsbgfx) 1173 { 1174 fontdepth = 0; 1175 gui::pushfont("default"); 1176 basescale = guiscale; 1177 if(guilayoutpass) 1178 uiscale.x = uiscale.y = uiscale.z = guiscaletime ? min(basescale*(totalmillis-starttime)/float(guiscaletime), basescale) : basescale; 1179 needsinput = allowinput; 1180 hastitle = wantstitle; 1181 hasbgfx = wantsbgfx; 1182 passthrough = !allowinput; 1183 curdepth = curlist = mergedepth = mergelist = -1; 1184 tpos = ty = 0; 1185 tx = -FONTW; 1186 tcurrent = tab; 1187 tcolor = 0xFFFFFF; 1188 pushlist(false); 1189 if(guilayoutpass) nextlist = curlist; 1190 else 1191 { 1192 if(tcurrent && !*tcurrent) tcurrent = NULL; 1193 cury = -ysize; 1194 curx = -xsize/2; 1195 1196 hudmatrix.ortho(0, 1, 1, 0, -1, 1); 1197 hudmatrix.translate(uiorigin); 1198 hudmatrix.scale(uiscale); 1199 1200 resethudmatrix(); 1201 hudshader->set(); 1202 1203 if(hasbgfx) 1204 { 1205 int x1 = curx-FONTW, y1 = cury-FONTH/2, x2 = x1+xsize+FONTW*2, y2 = y1+ysize+FONTH; 1206 skin(x1, y1, x2, y2, guibgcolour, guibgblend, guibordercolour, guiborderblend); 1207 } 1208 } 1209 } 1210 endgui1211 void end() 1212 { 1213 if(guilayoutpass) 1214 { 1215 if(needsinput) uibuttons(); 1216 xsize = max(tx, xsize); 1217 ysize = max(ty, ysize); 1218 ysize = max(ysize, FONTH); 1219 1220 if(tcurrent) *tcurrent = max(1, min(*tcurrent, tpos)); 1221 adjustscale(); 1222 1223 if(!passthrough && fieldmode != FIELDKEY) 1224 { 1225 hitx = (cursorx - uiorigin.x)/uiscale.x; 1226 hity = (cursory - uiorigin.y)/uiscale.y; 1227 if((mouseaction[0]&GUI_PRESSED) && (fabs(hitx-firstx) > 2 || fabs(hity - firsty) > 2)) mouseaction[0] |= GUI_DRAGGED; 1228 } 1229 } 1230 else 1231 { 1232 if(guistatusline && statusstr && *statusstr) 1233 { 1234 gui::pushfont("reduced"); 1235 int width = 0, height = 0, tw = min(statuswidth ? statuswidth : (guistatuswidth ? guistatuswidth : -1), int(screenw*(1/uiscale.y))-FONTH*4); 1236 text_bounds(statusstr, width, height, 0, 0, tw, TEXT_CENTERED|TEXT_NO_INDENT); 1237 int w = width+FONTW*2, h = height+FONTH/2, x1 = -w/2, y1 = guispacesize, x2 = x1+w, y2 = y1+h; 1238 if(hasbgfx) skin(x1, y1, x2, y2, guibgcolour, guibgblend, guibordercolour, guiborderblend); 1239 draw_text(statusstr, x1+FONTW, y1+FONTH/4, 255, 255, 255, 255, TEXT_CENTERED|TEXT_NO_INDENT, -1, tw); 1240 gui::popfont(); 1241 } 1242 if(needsinput) uibuttons(); 1243 if(!passthrough && fieldmode != FIELDKEY && (guitooltips || tooltipforce) && tooltipstr && *tooltipstr) 1244 { 1245 if(!tooltip || !lasttooltip || strcmp(tooltip, tooltipstr)) 1246 { 1247 if(tooltip) DELETEA(tooltip); 1248 tooltip = newstring(tooltipstr); 1249 lasttooltip = totalmillis ? totalmillis : 1; 1250 } 1251 if(tooltipforce || totalmillis-lasttooltip >= guitooltiptime) 1252 { 1253 gui::pushfont("little"); 1254 int width, height, tw = min(tooltipwidth ? tooltipwidth : (guitooltipwidth ? guitooltipwidth : -1), int(screenw*(1/uiscale.y))-FONTH*4); 1255 text_bounds(tooltipstr, width, height, 0, 0, tw, TEXT_NO_INDENT); 1256 int w = width+FONTW*2, h = FONTH/2+height, x1 = hitx, y1 = hity-height-FONTH/2, x2 = x1+w, y2 = y1+h, 1257 offset = totalmillis-lasttooltip-guitooltiptime; 1258 float blend = tooltipforce ? 1.f : (offset > 0 ? (offset < guitooltipfade ? offset/float(guitooltipfade) : 1.f) : 0.f); 1259 skin(x1, y1, x2, y2, guitooltipcolour, guitooltipblend*blend, guitooltipbordercolour, guitooltipborderblend*blend, guitooltipborderskin!=0); 1260 draw_text(tooltip, x1+FONTW, y1+FONTH/4, 255, 255, 255, int(255*blend), TEXT_NO_INDENT, -1, tw); 1261 gui::popfont(); 1262 } 1263 } 1264 else 1265 { 1266 if(tooltip) DELETEA(tooltip); 1267 lasttooltip = 0; 1268 } 1269 } 1270 poplist(); 1271 while(fontdepth) gui::popfont(); 1272 } 1273 }; 1274 1275 Texture *gui::skintex = NULL, *gui::skinbordertex, *gui::overlaytex = NULL, *gui::exittex = NULL, *gui::hovertex = NULL; 1276 TVARN(IDF_PERSIST|IDF_PRELOAD, guiskintex, "textures/guiskin", gui::skintex, 0); 1277 TVARN(IDF_PERSIST|IDF_PRELOAD, guiskinbordertex, "textures/guiskinborder", gui::skinbordertex, 0); 1278 TVARN(IDF_PERSIST|IDF_PRELOAD, guioverlaytex, "textures/guioverlay", gui::overlaytex, 0); 1279 TVARN(IDF_PERSIST|IDF_PRELOAD, guiexittex, "textures/guiexit", gui::exittex, 0); 1280 TVARN(IDF_PERSIST|IDF_PRELOAD, guihovertex, "textures/guihover", gui::hovertex, 0); 1281 1282 vector<gui::list> gui::lists; 1283 float gui::basescale, gui::maxscale = 1, gui::hitx, gui::hity; 1284 bool gui::passthrough, gui::hitfx = true, gui::cursorfx = true, gui::skinfx = true; 1285 int gui::curdepth, gui::fontdepth, gui::curlist, gui::xsize, gui::ysize, gui::curx, gui::cury, gui::mergelist, gui::mergedepth; 1286 int gui::ty, gui::tx, gui::tpos, *gui::tcurrent, gui::tcolor; 1287 static vector<gui> guis; 1288 1289 namespace UI 1290 { 1291 bool isopen = false; 1292 textinput(const char * str,int len)1293 bool textinput(const char *str, int len) 1294 { 1295 editor *e = currentfocus(); 1296 if(fieldmode == FIELDKEY || fieldmode == FIELDSHOW || !e || e->mode == EDITORREADONLY) return false; 1297 1298 e->input(str, len); 1299 return true; 1300 } 1301 keypress(int code,bool isdown)1302 bool keypress(int code, bool isdown) 1303 { 1304 editor *e = currentfocus(); 1305 if(fieldmode == FIELDKEY && e && e->mode != EDITORREADONLY) 1306 { 1307 switch(code) 1308 { 1309 case SDLK_ESCAPE: 1310 if(isdown) 1311 { 1312 fieldmode = FIELDCOMMIT; 1313 e->unfocus = true; 1314 } 1315 return true; 1316 } 1317 const char *keyname = getkeyname(code); 1318 if(keyname && isdown) 1319 { 1320 if(e->lines.length()!=1 || !e->lines[0].empty()) e->insert(" "); 1321 e->insert(keyname); 1322 } 1323 return true; 1324 } 1325 1326 if(code < 0) switch(code) 1327 { // fall-through-o-rama 1328 case -5: mouseaction[1] |= GUI_ALT; 1329 case -4: mouseaction[1] |= isdown ? GUI_DOWN : GUI_UP; 1330 if(active()) return true; 1331 break; 1332 case -3: mouseaction[0] |= GUI_ALT; 1333 case -1: mouseaction[0] |= (guiactionon = isdown) ? GUI_DOWN : GUI_UP; 1334 if(isdown) 1335 { 1336 firstx = gui::hitx; 1337 firsty = gui::hity; 1338 } 1339 if(active()) return true; 1340 break; 1341 case -2: 1342 if(isdown) cleargui(1); 1343 if(active()) return true; 1344 break; 1345 } 1346 1347 if(fieldmode == FIELDSHOW || !e || e->mode == EDITORREADONLY) return false; 1348 switch(code) 1349 { 1350 case SDLK_ESCAPE: //cancel editing without commit 1351 if(isdown) 1352 { 1353 fieldmode = FIELDABORT; 1354 e->unfocus = true; 1355 } 1356 return e->mode != EDITORFOREVER; 1357 case SDLK_RETURN: 1358 case SDLK_TAB: 1359 if(e->maxy != 1) break; 1360 case SDLK_KP_ENTER: 1361 if(isdown) fieldmode = FIELDCOMMIT; //signal field commit (handled when drawing field) 1362 return true; 1363 case SDLK_PAGEUP: 1364 case SDLK_PAGEDOWN: 1365 case -4: 1366 case -5: 1367 if(e->parent && *e->parent) 1368 { // pass input on to parent 1369 editor *f = findeditor(e->parent); 1370 if(f) e = f; 1371 } 1372 break; 1373 } 1374 if(isdown) e->key(code); 1375 return true; 1376 } 1377 active(bool pass)1378 bool active(bool pass) { return guis.length() && (!pass || needsinput); } limitscale(float scale)1379 void limitscale(float scale) { gui::maxscale = scale; } 1380 addcb(guicb * cb)1381 void addcb(guicb *cb) 1382 { 1383 gui &g = guis.add(); 1384 g.cb = cb; 1385 g.adjustscale(); 1386 } 1387 update()1388 void update() 1389 { 1390 bool p = active(false); 1391 if(isopen != p) uimillis = (isopen = p) ? totalmillis : -totalmillis; 1392 setsvar("guirollovername", "", true); 1393 setsvar("guirolloveraction", "", true); 1394 setsvar("guirollovertype", "", true); 1395 } 1396 render()1397 void render() 1398 { 1399 float oldtextscale = curtextscale; 1400 curtextscale = 1; 1401 if(guiactionon) mouseaction[0] |= GUI_PRESSED; 1402 1403 gui::reset(); 1404 guis.shrink(0); 1405 1406 // call all places in the engine that may want to render a gui from here, they call addcb() 1407 if(progressing) progressmenu(); 1408 else 1409 { 1410 texturemenu(); 1411 hud::gamemenus(); 1412 mainmenu(); 1413 } 1414 1415 readyeditors(); 1416 bool wasfocused = (fieldmode!=FIELDSHOW); 1417 fieldsactive = false; 1418 1419 needsinput = false; 1420 hastitle = hasbgfx = true; 1421 1422 if(!guis.empty()) 1423 { 1424 guicursortype = 0; 1425 guilayoutpass = 1; 1426 //loopv(guis) guis[i].cb->gui(guis[i], true); 1427 guis.last().cb->gui(guis.last(), true); 1428 guilayoutpass = guicursortype = 0; 1429 1430 glEnable(GL_BLEND); 1431 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 1432 1433 //loopvrev(guis) guis[i].cb->gui(guis[i], false); 1434 guis.last().cb->gui(guis.last(), false); 1435 1436 glDisable(GL_BLEND); 1437 } 1438 1439 flusheditors(); 1440 if(!fieldsactive) fieldmode = FIELDSHOW; //didn't draw any fields, so lose focus - mainly for menu closed 1441 if((fieldmode!=FIELDSHOW) != wasfocused) 1442 { 1443 textinput(fieldmode!=FIELDSHOW, TI_GUI); 1444 keyrepeat(fieldmode!=FIELDSHOW, KR_GUI); 1445 } 1446 loopi(2) mouseaction[i] = 0; 1447 curtextscale = oldtextscale; 1448 } 1449 geteditor(const char * name,int mode,const char * init,const char * parent)1450 editor *geteditor(const char *name, int mode, const char *init, const char *parent) 1451 { 1452 return useeditor(name, mode, false, init, parent); 1453 } 1454 editorline(editor * e,const char * str,int limit)1455 void editorline(editor *e, const char *str, int limit) 1456 { 1457 if(!e) return; 1458 if(e->lines.length() != 1 || !e->lines[0].empty()) e->lines.add(); 1459 e->lines.last().set(str); 1460 if(limit >= 0 && e->lines.length() > limit) 1461 { 1462 int n = e->lines.length()-limit; 1463 e->removelines(0, n); 1464 e->cy = max(e->cy - n, 0); 1465 if(e->scrolly != editor::SCROLLEND) e->scrolly = max(e->scrolly - n, 0); 1466 } 1467 e->mark(false); 1468 } 1469 editorclear(editor * e,const char * init)1470 void editorclear(editor *e, const char *init) 1471 { 1472 if(!e) return; 1473 e->clear(init); 1474 } 1475 editoredit(editor * e,const char * init)1476 void editoredit(editor *e, const char *init) 1477 { 1478 if(!e) return; 1479 useeditor(e->name, e->mode, true, init, e->parent); 1480 e->clear(init); 1481 fieldmode = FIELDEDIT; 1482 e->unfocus = true; 1483 } 1484 }; 1485