LCOV - code coverage report
Current view: top level - engine/interface - ui.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 9.0 % 2459 221
Test Date: 2026-05-09 04:28:55 Functions: 20.5 % 708 145

            Line data    Source code
       1              : /**
       2              :  * @file ui.cpp
       3              :  * @brief cubescript user interfaces and menu functionality
       4              :  *
       5              :  * ui.cpp defines a series of convenient objects that can be created and destroyed
       6              :  * as groups to be used as interfaces by programs. They can be configured to grab
       7              :  * user input or not, (the former being useful for interactive menus, the latter
       8              :  * for passive interface material such as a HUD).
       9              :  *
      10              :  * ui.cpp uses font functionality in rendertext as well as textedit for interactive
      11              :  * text modification objects
      12              :  */
      13              : 
      14              : #include "../libprimis-headers/cube.h"
      15              : #include "../../shared/geomexts.h"
      16              : #include "../../shared/glemu.h"
      17              : #include "../../shared/glexts.h"
      18              : 
      19              : #include <memory>
      20              : #include <optional>
      21              : 
      22              : #include "console.h"
      23              : #include "control.h"
      24              : #include "input.h"
      25              : #include "ui.h"
      26              : #include "cs.h"
      27              : 
      28              : #include "world/entities.h"
      29              : #include "world/octaedit.h"
      30              : #include "world/bih.h"
      31              : 
      32              : #include "render/hud.h"
      33              : 
      34              : //model needs bih's objects
      35              : #include "model/model.h"
      36              : //textedit.h needs rendertext's objects
      37              : #include "render/rendergl.h"
      38              : #include "render/renderlights.h"
      39              : #include "render/rendermodel.h"
      40              : #include "render/rendertext.h"
      41              : #include "render/renderttf.h"
      42              : #include "render/shader.h"
      43              : #include "render/shaderparam.h"
      44              : #include "render/texture.h"
      45              : 
      46              : #include "textedit.h"
      47              : 
      48              : #include "render/renderwindow.h"
      49              : 
      50              : /* a quick note on unnamed function arguments, used here for many derived functions:
      51              :  *
      52              :  * c++ does legally allow functions to be defined with parameters with no name:
      53              :  * this is to allow the derived functions to match the same function "signature"
      54              :  * as the parent class without needlessly defining parameter names that don't
      55              :  * actually get used -- this can be confusing (one expects to see parameters in
      56              :  * a function actually get used)
      57              :  *
      58              :  * obviously, anything actually passed to them will be lost as there is no name
      59              :  * with which to access them inside the function body
      60              :  *
      61              :  * example:
      62              :  *
      63              :  * bool target(float, float) //note unnamed function parameters
      64              :  * {
      65              :  *      return true; //note that neither parameter was used in the body
      66              :  * }
      67              :  */
      68              : 
      69              : static ModelPreview modelpreview = ModelPreview();
      70              : 
      71              : namespace UI
      72              : {
      73              :     class ClipArea final
      74              :     {
      75              :         public:
      76            0 :             ClipArea(float x, float y, float w, float h) : x1(x), y1(y), x2(x+w), y2(y+h) {}
      77              : 
      78            0 :             void intersect(const ClipArea &c)
      79              :             {
      80            0 :                 x1 = std::max(x1, c.x1);
      81            0 :                 y1 = std::max(y1, c.y1);
      82            0 :                 x2 = std::max(x1, std::min(x2, c.x2));
      83            0 :                 y2 = std::max(y1, std::min(y2, c.y2));
      84              : 
      85            0 :             }
      86              : 
      87            0 :             bool isfullyclipped(float x, float y, float w, float h) const
      88              :             {
      89            0 :                 return x1 == x2 || y1 == y2 || x >= x2 || y >= y2 || x+w <= x1 || y+h <= y1;
      90              :             }
      91              : 
      92              :             void scissor();
      93              :         private:
      94              :             float x1, y1, x2, y2;
      95              :     };
      96              : 
      97              :     class Object;
      98              : 
      99              :     namespace
     100              :     {
     101              :         float cursorx = 0.499f,
     102              :               cursory = 0.499f;
     103              : 
     104            0 :         void quads(float x, float y, float w, float h, float tx = 0, float ty = 0, float tw = 1, float th = 1)
     105              :         {
     106            0 :             gle::defvertex(2);
     107            0 :             gle::deftexcoord0();
     108            0 :             gle::begin(GL_TRIANGLE_STRIP);
     109            0 :             gle::attribf(x+w, y);   gle::attribf(tx+tw, ty);
     110            0 :             gle::attribf(x,   y);   gle::attribf(tx,    ty);
     111            0 :             gle::attribf(x+w, y+h); gle::attribf(tx+tw, ty+th);
     112            0 :             gle::attribf(x,   y+h); gle::attribf(tx,    ty+th);
     113            0 :             gle::end();
     114            0 :         }
     115              : 
     116            0 :         void quad(float x, float y, float w, float h, const std::array<vec2, 4> &tc)
     117              :         {
     118            0 :             gle::defvertex(2);
     119            0 :             gle::deftexcoord0();
     120            0 :             gle::begin(GL_TRIANGLE_STRIP);
     121            0 :             gle::attribf(x+w, y);   gle::attrib(tc[1]);
     122            0 :             gle::attribf(x,   y);   gle::attrib(tc[0]);
     123            0 :             gle::attribf(x+w, y+h); gle::attrib(tc[2]);
     124            0 :             gle::attribf(x,   y+h); gle::attrib(tc[3]);
     125            0 :             gle::end();
     126            0 :         }
     127              : 
     128              :         std::vector<ClipArea> clipstack;
     129              : 
     130            0 :         void pushclip(float x, float y, float w, float h)
     131              :         {
     132            0 :             if(clipstack.empty())
     133              :             {
     134            0 :                 glEnable(GL_SCISSOR_TEST);
     135              :             }
     136            0 :             ClipArea &c = clipstack.emplace_back(ClipArea(x, y, w, h));
     137            0 :             if(clipstack.size() >= 2)
     138              :             {
     139            0 :                 c.intersect(clipstack[clipstack.size()-2]);
     140              :             }
     141            0 :             c.scissor();
     142            0 :         }
     143              : 
     144            0 :         void popclip()
     145              :         {
     146            0 :             clipstack.pop_back();
     147            0 :             if(clipstack.empty())
     148              :             {
     149            0 :                 glDisable(GL_SCISSOR_TEST);
     150              :             }
     151              :             else
     152              :             {
     153            0 :                 clipstack.back().scissor();
     154              :             }
     155            0 :         }
     156              : 
     157            0 :         bool isfullyclipped(float x, float y, float w, float h)
     158              :         {
     159            0 :             if(clipstack.empty())
     160              :             {
     161            0 :                 return false;
     162              :             }
     163            0 :             return clipstack.back().isfullyclipped(x, y, w, h);
     164              :         }
     165              : 
     166              :         enum Alignment
     167              :         {
     168              :             Align_Mask = 0xF,
     169              : 
     170              :             Align_HMask   = 0x3,
     171              :             Align_HShift  = 0,
     172              :             Align_HNone   = 0,
     173              :             Align_Left    = 1,
     174              :             Align_HCenter = 2,
     175              :             Align_Right   = 3,
     176              : 
     177              :             Align_VMask   = 0xC,
     178              :             Align_VShift  = 2,
     179              :             Align_VNone   = 0 << 2,
     180              :             Align_Top     = 1 << 2,
     181              :             Align_VCenter = 2 << 2,
     182              :             Align_Bottom  = 3 << 2,
     183              :         };
     184              : 
     185              :         enum ClampDirection
     186              :         {
     187              :             Clamp_Mask    = 0xF0,
     188              :             Clamp_Left    = 0x10,
     189              :             Clamp_Right   = 0x20,
     190              :             Clamp_Top     = 0x40,
     191              :             Clamp_Bottom  = 0x80,
     192              : 
     193              :             NO_ADJUST     = Align_HNone | Align_VNone,
     194              :         };
     195              : 
     196              :         enum ElementState
     197              :         {
     198              :             State_Hover       = 1 << 0,
     199              :             State_Press       = 1 << 1,
     200              :             State_Hold        = 1 << 2,
     201              :             State_Release     = 1 << 3,
     202              :             State_AltPress    = 1 << 4,
     203              :             State_AltHold     = 1 << 5,
     204              :             State_AltRelease  = 1 << 6,
     205              :             State_EscPress    = 1 << 7,
     206              :             State_EscHold     = 1 << 8,
     207              :             State_EscRelease  = 1 << 9,
     208              :             State_ScrollUp    = 1 << 10,
     209              :             State_ScrollDown  = 1 << 11,
     210              :             State_Hidden      = 1 << 12,
     211              : 
     212              :             State_HoldMask = State_Hold | State_AltHold | State_EscHold
     213              :         };
     214              : 
     215              :         enum ElementBlend
     216              :         {
     217              :             Blend_Alpha,
     218              :             Blend_Mod
     219              :         };
     220              : 
     221              :         enum ChangeDrawFlags
     222              :         {
     223              :             Change_Shader = 1 << 0,
     224              :             Change_Color  = 1 << 1,
     225              :             Change_Blend  = 1 << 2
     226              :         };
     227              : 
     228              :         Object *buildparent = nullptr;
     229              :         int buildchild = -1;
     230              : 
     231              :         int changed = 0;
     232              : 
     233              :         const Object *drawing = nullptr;
     234              : 
     235              :         int blendtype = Blend_Alpha;
     236              : 
     237            0 :         void changeblend(int type, GLenum src, GLenum dst)
     238              :         {
     239            0 :             if(blendtype != type)
     240              :             {
     241            0 :                 blendtype = type;
     242            0 :                 glBlendFunc(src, dst);
     243              :             }
     244            0 :         }
     245              :     }
     246              : 
     247              :     //type: the type of object to build
     248              :     //o the name of the temp variable to use
     249              :     //setup: a snippet of c++ to run to set up the object
     250              :     //contents: the content passed to the buildchildren() call
     251              :     #define BUILD(type, o, setup, contents) do { \
     252              :         if(buildparent) \
     253              :         { \
     254              :             type *o = buildparent->buildtype<type>(); \
     255              :             setup; \
     256              :             o->buildchildren(contents); \
     257              :         } \
     258              :     } while(0)
     259              : 
     260            0 :     void resetblend()
     261              :     {
     262            0 :         changeblend(Blend_Alpha, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     263            0 :     }
     264              : 
     265            0 :     void modblend()
     266              :     {
     267            0 :         changeblend(Blend_Mod, GL_ZERO, GL_SRC_COLOR);
     268            0 :     }
     269              : 
     270              :     class Object
     271              :     {
     272              :         public:
     273              :             float x, y, w, h;
     274              :             std::vector<Object *> children;
     275              :             uchar adjust;
     276              :             ushort state, childstate;
     277              :             Object *parent;
     278              : 
     279              :             //note reverse iteration
     280              :             #define LOOP_CHILDREN_REV(o, body) do { \
     281              :                 for(int i = static_cast<int>(children.size()); --i >=0;) \
     282              :                 { \
     283              :                     Object *o = children.at(i); \
     284              :                     body; \
     285              :                 } \
     286              :             } while(0)
     287              : 
     288              :             #define LOOP_CHILD_RANGE(start, end, o, body) do { \
     289              :                 for(int i = start; i < end; i++) \
     290              :                 { \
     291              :                     Object *o = children.at(i); \
     292              :                     body; \
     293              :                 } \
     294              :             } while(0)
     295              : 
     296            1 :             Object() :  x(), y(), w(), h(), children(), adjust(0), state(0), childstate(0), parent() {}
     297              : 
     298              :             Object(const Object &o) = delete;
     299            0 :             virtual ~Object()
     300            0 :             {
     301            0 :                 clearchildren();
     302            0 :             }
     303              :             template<class T>
     304            0 :             T *buildtype()
     305              :             {
     306              :                 T *t;
     307            0 :                 if(static_cast<int>(children.size()) > buildchild )
     308              :                 {
     309            0 :                     Object *o = children[buildchild];
     310            0 :                     if(o->istype<T>())
     311              :                     {
     312            0 :                         t = static_cast<T *>(o);
     313              :                     }
     314              :                     else
     315              :                     {
     316            0 :                         delete o;
     317            0 :                         t = new T;
     318            0 :                         children[buildchild] = t;
     319              :                     }
     320              :                 }
     321              :                 else
     322              :                 {
     323            0 :                     t = new T;
     324            0 :                     children.push_back(t);
     325              :                 }
     326            0 :                 t->reset(this);
     327            0 :                 buildchild++;
     328            0 :                 return t;
     329              :             }
     330              : 
     331            0 :             virtual void layout()
     332              :             {
     333            0 :                 w = h = 0;
     334            0 :                 for(Object *o : children)
     335              :                 {
     336            0 :                     o->x = o->y = 0;
     337            0 :                     o->layout();
     338            0 :                     w = std::max(w, o->x + o->w);
     339            0 :                     h = std::max(h, o->y + o->h);
     340              :                 }
     341            0 :             }
     342              : 
     343            0 :             void buildchildren(const uint *contents)
     344              :             {
     345            0 :                 if((*contents&Code_OpMask) == Code_Exit)
     346              :                 {
     347            0 :                     children.erase(children.begin(), children.end());
     348              :                 }
     349              :                 else
     350              :                 {
     351            0 :                     Object *oldparent = buildparent;
     352            0 :                     int oldchild = buildchild;
     353            0 :                     buildparent = this;
     354            0 :                     buildchild = 0;
     355            0 :                     executeret(contents);
     356            0 :                     while(static_cast<int>(children.size()) > buildchild)
     357              :                     {
     358            0 :                         children.pop_back();
     359              :                     }
     360            0 :                     buildparent = oldparent;
     361            0 :                     buildchild = oldchild;
     362              :                 }
     363            0 :                 resetstate();
     364            0 :             }
     365              : 
     366            0 :             void clearstate(int flags)
     367              :             {
     368            0 :                 state &= ~flags;
     369            0 :                 if(childstate & flags)
     370              :                 {
     371            0 :                     for(Object *o : children)
     372              :                     {
     373            0 :                         if((o->state | o->childstate) & flags) o->clearstate(flags);
     374              :                     }
     375            0 :                     childstate &= ~flags;
     376              :                 }
     377            0 :             }
     378              : 
     379            0 :             void enddraw(int change) const
     380              :             {
     381            0 :                 enddraw();
     382            0 :                 changed &= ~change;
     383            0 :                 if(changed)
     384              :                 {
     385            0 :                     if(changed & Change_Shader)
     386              :                     {
     387            0 :                         hudshader->set();
     388              :                     }
     389            0 :                     if(changed & Change_Color)
     390              :                     {
     391            0 :                         gle::colorf(1, 1, 1);
     392              :                     }
     393            0 :                     if(changed & Change_Blend)
     394              :                     {
     395            0 :                         resetblend();
     396              :                     }
     397              :                 }
     398            0 :             }
     399              : 
     400            0 :             void resetchildstate()
     401              :             {
     402            0 :                 resetstate();
     403            0 :                 for(Object *o : children)
     404              :                 {
     405            0 :                      o->resetchildstate();
     406              :                 }
     407            0 :             }
     408              : 
     409            0 :             bool hasstate(int flags) const
     410              :             {
     411            0 :                 return ((state & ~childstate) & flags) != 0;
     412              :             }
     413              : 
     414            0 :             bool haschildstate(int flags) const
     415              :             {
     416            0 :                 return ((state | childstate) & flags) != 0;
     417              :             }
     418              : 
     419            0 :             virtual void draw(float sx, float sy)
     420              :             {
     421            0 :                 for(Object *o : children)
     422              :                 {
     423            0 :                     if(!isfullyclipped(sx + o->x, sy + o->y, o->w, o->h))
     424              :                     {
     425            0 :                         o->draw(sx + o->x, sy + o->y);
     426              :                     }
     427              :                 }
     428            0 :             }
     429              : 
     430              :             /**
     431              :              * @brief executes the DOSTATE macro for the applicable special keys
     432              :              *
     433              :              * ***NOTE***: DOSTATE is not defined by default, and is defined manually
     434              :              * in the code at every location the DOSTATES macro is called --
     435              :              * you cannot merely call DOSTATES whenever you wish;
     436              :              * you must also define DOSTATE (and then undefine it)
     437              :              */
     438              :             #define DOSTATES \
     439              :                 DOSTATE(State_Hover, hover) \
     440              :                 DOSTATE(State_Press, press) \
     441              :                 DOSTATE(State_Hold, hold) \
     442              :                 DOSTATE(State_Release, release) \
     443              :                 DOSTATE(State_AltHold, althold) \
     444              :                 DOSTATE(State_AltPress, altpress) \
     445              :                 DOSTATE(State_AltRelease, altrelease) \
     446              :                 DOSTATE(State_EscHold, eschold) \
     447              :                 DOSTATE(State_EscPress, escpress) \
     448              :                 DOSTATE(State_EscRelease, escrelease) \
     449              :                 DOSTATE(State_ScrollUp, scrollup) \
     450              :                 DOSTATE(State_ScrollDown, scrolldown)
     451              : 
     452            0 :             bool setstate(int state, float cx, float cy, int mask = 0, bool inside = true, int setflags = 0)
     453              :             {
     454            0 :                 switch(state)
     455              :                 {
     456              :                 #define DOSTATE(flags, func) case flags: func##children(cx, cy, mask, inside, setflags | flags); return haschildstate(flags);
     457            0 :                 DOSTATES
     458              :                 #undef DOSTATE
     459              :                 }
     460            0 :                 return false;
     461              :             }
     462              : 
     463            0 :             void setup()
     464              :             {
     465            0 :             }
     466              : 
     467              :             template<class T>
     468            0 :             bool istype() const
     469              :             {
     470            0 :                 return T::typestr() == gettype();
     471              :             }
     472              : 
     473            0 :             virtual bool rawkey(int code, bool isdown)
     474              :             {
     475            0 :                 LOOP_CHILDREN_REV(o,
     476              :                 {
     477              :                     if(o->rawkey(code, isdown))
     478              :                     {
     479              :                         return true;
     480              :                     }
     481              :                 });
     482            0 :                 return false;
     483              :             }
     484              : 
     485            0 :             virtual bool key(int code, bool isdown)
     486              :             {
     487            0 :                 LOOP_CHILDREN_REV(o,
     488              :                 {
     489              :                     if(o->key(code, isdown))
     490              :                     {
     491              :                         return true;
     492              :                     }
     493              :                 });
     494            0 :                 return false;
     495              :             }
     496              : 
     497            0 :             virtual bool textinput(const char *str, int len)
     498              :             {
     499            0 :                 LOOP_CHILDREN_REV(o,
     500              :                 {
     501              :                     if(o->textinput(str, len))
     502              :                     {
     503              :                         return true;
     504              :                     }
     505              :                 });
     506            0 :                 return false;
     507              :             }
     508              : 
     509            0 :             virtual int childcolumns() const
     510              :             {
     511            0 :                 return static_cast<int>(children.size());
     512              :             }
     513              : 
     514            0 :             void adjustlayout(float px, float py, float pw, float ph)
     515              :             {
     516            0 :                 switch(adjust & Align_HMask)
     517              :                 {
     518            0 :                     case Align_Left:
     519              :                     {
     520            0 :                         x = px;
     521            0 :                         break;
     522              :                     }
     523            0 :                     case Align_HCenter:
     524              :                     {
     525            0 :                         x = px + (pw - w) / 2;
     526            0 :                         break;
     527              :                     }
     528            0 :                     case Align_Right:
     529              :                     {
     530            0 :                         x = px + pw - w;
     531            0 :                         break;
     532              :                     }
     533              :                 }
     534              : 
     535            0 :                 switch(adjust & Align_VMask)
     536              :                 {
     537            0 :                     case Align_Top:
     538              :                     {
     539            0 :                         y = py;
     540            0 :                         break;
     541              :                     }
     542            0 :                     case Align_VCenter:
     543              :                     {
     544            0 :                         y = py + (ph - h) / 2;
     545            0 :                         break;
     546              :                     }
     547            0 :                     case Align_Bottom:
     548              :                     {
     549            0 :                         y = py + ph - h;
     550            0 :                         break;
     551              :                     }
     552              :                 }
     553              : 
     554            0 :                 if(adjust & Clamp_Mask)
     555              :                 {
     556            0 :                     if(adjust & Clamp_Left)
     557              :                     {
     558            0 :                         w += x - px;
     559            0 :                         x = px;
     560              :                     }
     561            0 :                     if(adjust & Clamp_Right)
     562              :                     {
     563            0 :                         w = px + pw - x;
     564              :                     }
     565            0 :                     if(adjust & Clamp_Top)
     566              :                     {
     567            0 :                         h += y - py;
     568            0 :                         y = py;
     569              :                     }
     570            0 :                     if(adjust & Clamp_Bottom)
     571              :                     {
     572            0 :                         h = py + ph - y;
     573              :                     }
     574              :                 }
     575              : 
     576            0 :                 adjustchildren();
     577            0 :             }
     578              : 
     579            0 :             void setalign(int xalign, int yalign)
     580              :             {
     581            0 :                 adjust &= ~Align_Mask;
     582            0 :                 adjust |= (std::clamp(xalign, -2, 1)+2) << Align_HShift;
     583            0 :                 adjust |= (std::clamp(yalign, -2, 1)+2) << Align_VShift;
     584            0 :             }
     585              : 
     586            0 :             void setclamp(int left, int right, int top, int bottom)
     587              :             {
     588            0 :                 adjust &= ~Clamp_Mask;
     589            0 :                 if(left)
     590              :                 {
     591            0 :                     adjust |= Clamp_Left;
     592              :                 }
     593            0 :                 if(right)
     594              :                 {
     595            0 :                     adjust |= Clamp_Right;
     596              :                 }
     597            0 :                 if(top)
     598              :                 {
     599            0 :                     adjust |= Clamp_Top;
     600              :                 }
     601            0 :                 if(bottom)
     602              :                 {
     603            0 :                     adjust |= Clamp_Bottom;
     604              :                 }
     605            0 :             }
     606              :         protected:
     607              : 
     608            0 :             void reset()
     609              :             {
     610            0 :                 resetlayout();
     611            0 :                 parent = nullptr;
     612            0 :                 adjust = Align_HCenter | Align_VCenter;
     613            0 :             }
     614              : 
     615            0 :             virtual uchar childalign() const
     616              :             {
     617            0 :                 return Align_HCenter | Align_VCenter;
     618              :             }
     619              : 
     620            0 :             void reset(Object *parent_)
     621              :             {
     622            0 :                 resetlayout();
     623            0 :                 parent = parent_;
     624            0 :                 adjust = parent->childalign();
     625            0 :             }
     626              : 
     627            0 :             void clearchildren()
     628              :             {
     629            0 :                 for(Object *o : children)
     630              :                 {
     631            0 :                     delete o;
     632              :                 }
     633            0 :                 children.clear();
     634            0 :             }
     635              : 
     636            0 :             void adjustchildrento(float px, float py, float pw, float ph)
     637              :             {
     638            0 :                 for(Object *o : children)
     639              :                 {
     640            0 :                     o->adjustlayout(px, py, pw, ph);
     641              :                 }
     642            0 :             }
     643              : 
     644            0 :             virtual void adjustchildren()
     645              :             {
     646            0 :                 adjustchildrento(0, 0, w, h);
     647            0 :             }
     648              : 
     649            0 :             virtual bool target(float, float) //note unnamed function parameters
     650              :             {
     651            0 :                 return false;
     652              :             }
     653              : 
     654            0 :             virtual void startdraw() const {}
     655            0 :             virtual void enddraw() const {}
     656              : 
     657            0 :             void changedraw(int change = 0) const
     658              :             {
     659            0 :                 if(!drawing)
     660              :                 {
     661            0 :                     startdraw();
     662            0 :                     changed = change;
     663              :                 }
     664            0 :                 else if(drawing->gettype() != gettype())
     665              :                 {
     666            0 :                     drawing->enddraw(change);
     667            0 :                     startdraw();
     668            0 :                     changed = change;
     669              :                 }
     670            0 :                 drawing = this;
     671            0 :             }
     672              : 
     673            0 :             void resetstate()
     674              :             {
     675            0 :                 state &= State_HoldMask;
     676            0 :                 childstate &= State_HoldMask;
     677            0 :             }
     678              : 
     679            0 :             void changechildstate(Object * o, void (Object::*member)(float, float, int, bool, int), float ox, float oy, int mask, bool inside, int setflags)
     680              :             {
     681            0 :                 (o->*member)(ox, oy, mask, inside, setflags); /*child's->func##children fxn called*/
     682            0 :                 childstate |= (o->state | o->childstate) & (setflags); /*set childstate*/
     683            0 :             }
     684              : 
     685            0 :             void propagatestate(float cx, float cy, int mask, bool inside, int setflags, void (UI::Object::*method)(float, float, int, bool, int))
     686              :             {
     687            0 :                 for(int i = static_cast<int>(children.size()); --i >= 0;)
     688              :                 {
     689            0 :                     Object *o = children.at(i);
     690            0 :                     if(((o->state | o->childstate) & mask) != mask)
     691              :                     {
     692            0 :                         continue;
     693              :                     }
     694            0 :                     float ox = cx - o->x; /*offset x*/
     695            0 :                     float oy = cy - o->y; /*offset y*/
     696            0 :                     if(!inside)
     697              :                     {
     698            0 :                         ox = std::clamp(ox, 0.0f, o->w); /*clamp offsets to Object bounds in x*/
     699            0 :                         oy = std::clamp(oy, 0.0f, o->h); /*clamp offsets to Object bounds in y*/
     700            0 :                         changechildstate(o, method, ox, oy, mask, inside, setflags);
     701              :                     }
     702            0 :                     else if(ox >= 0 && ox < o->w && oy >= 0 && oy < o->h) /*if in bounds execute body*/
     703              :                     {
     704            0 :                         changechildstate(o, method, ox, oy, mask, inside, setflags);
     705              :                     }
     706              :                 }
     707            0 :             }
     708              : 
     709              :             #define DOSTATE(flags, func) \
     710              :                 virtual void func##children(float cx, float cy, int mask, bool inside, int setflags) \
     711              :                 { \
     712              :                     propagatestate(cx, cy, mask, inside, setflags, &UI::Object::func##children); \
     713              :                     if(target(cx, cy)) \
     714              :                     { \
     715              :                         state |= (setflags); \
     716              :                     } \
     717              :                     func(cx, cy); \
     718              :                 } \
     719              :                 virtual void func(float, float) {} /*note unnamed function parameters*/
     720            0 :             DOSTATES
     721              :             #undef DOSTATE
     722              : 
     723            0 :             virtual const char *gettype() const
     724              :             {
     725            0 :                 return typestr();
     726              :             }
     727              : 
     728            0 :             virtual const char *gettypename() const
     729              :             {
     730            0 :                 return gettype();
     731              :             }
     732              : 
     733              :             /**
     734              :              * @brief Searches for an object within this object's children.
     735              :              *
     736              :              * Searches this object's children for an object bearing the name given.
     737              :              * Returns the first matching entry; if recurse is enabled, searches
     738              :              * children of children for a matching entry. If no entry is found,
     739              :              * returns nullptr.
     740              :              *
     741              :              * If exclude is not `nullptr`, search will skip the object at that
     742              :              * address.
     743              :              *
     744              :              * @param name the name of the object to search for
     745              :              * @param recurse whether to recursively search children of children
     746              :              * @param exclude pointer of object to not accept as a result.
     747              :              *
     748              :              * @param pointer to the found object, or nullptr
     749              :              */
     750            0 :             Object *find(const char *name, bool recurse = true, const Object *exclude = nullptr) const
     751              :             {
     752            0 :                 for(Object *o : children)
     753              :                 {
     754            0 :                     if(o != exclude && o->isnamed(name))
     755              :                     {
     756            0 :                         return o;
     757              :                     }
     758              :                 }
     759            0 :                 if(recurse)
     760              :                 {
     761            0 :                     for(Object *o : children)
     762              :                     {
     763            0 :                         if(o != exclude)
     764              :                         {
     765            0 :                             Object *found = o->find(name);
     766            0 :                             if(found)
     767              :                             {
     768            0 :                                 return found;
     769              :                             }
     770              :                         }
     771              :                     }
     772              :                 }
     773            0 :                 return nullptr;
     774              :             }
     775              : 
     776              :             /**
     777              :              * @brief Searches for an object in the same tree as this object.
     778              :              *
     779              :              * This is the tree structure defined by the `parent` and `children`
     780              :              * fields in the object. Searches recursively upwards starting with the
     781              :              * children of this object and then the children of its parent object, until
     782              :              * the entire tree is searched.
     783              :              *
     784              :              * Returns the object farthest down the tree with a matching name. If
     785              :              * none are found, returns nullptr.
     786              :              *
     787              :              * @param name the name of the element to find
     788              :              *
     789              :              * @return pointer to the object if found, or nullptr if not.
     790              :              */
     791            0 :             Object *findsibling(const char *name) const
     792              :             {
     793            0 :                 for(const Object *prev = this, *cur = parent; cur; prev = cur, cur = cur->parent)
     794              :                 {
     795            0 :                     Object *o = cur->find(name, true, prev);
     796            0 :                     if(o)
     797              :                     {
     798            0 :                         return o;
     799              :                     }
     800              :                 }
     801            0 :                 return nullptr;
     802              :             }
     803              : 
     804              :         private:
     805              : 
     806            0 :             virtual const char *getname() const
     807              :             {
     808            0 :                 return gettype();
     809              :             }
     810              : 
     811            0 :             void resetlayout()
     812              :             {
     813            0 :                 x = y = w = h = 0;
     814            0 :             }
     815              : 
     816            0 :             static const char *typestr()
     817              :             {
     818            0 :                 return "#Object";
     819              :             }
     820              : 
     821            0 :             bool isnamed(const char *name) const
     822              :             {
     823            0 :                 return name[0] == '#' ? name == gettypename() : !std::strcmp(name, getname());
     824              :             }
     825              : 
     826              :     };
     827              : 
     828            0 :     static void stopdrawing()
     829              :     {
     830            0 :         if(drawing)
     831              :         {
     832            0 :             drawing->enddraw(0);
     833            0 :             drawing = nullptr;
     834              :         }
     835            0 :     }
     836              : 
     837              :     struct Window;
     838              : 
     839              :     static Window *window = nullptr;
     840              : 
     841              :     struct Window final : Object
     842              :     {
     843              :         std::string name;
     844              :         uint *contents, *onshow, *onhide;
     845              :         bool allowinput, eschide, abovehud;
     846              :         float px, py, pw, ph;
     847              :         vec2 sscale, soffset;
     848              : 
     849            1 :         Window(const char *name, const char *contents, const char *onshow, const char *onhide) :
     850            1 :             name(name),
     851            1 :             contents(compilecode(contents)),
     852            1 :             onshow(onshow && onshow[0] ? compilecode(onshow) : nullptr),
     853            1 :             onhide(onhide && onhide[0] ? compilecode(onhide) : nullptr),
     854            1 :             allowinput(true), eschide(true), abovehud(false),
     855            1 :             px(0), py(0), pw(0), ph(0),
     856            2 :             sscale(1, 1), soffset(0, 0)
     857              :         {
     858            1 :         }
     859            0 :         ~Window()
     860            0 :         {
     861            0 :             freecode(contents);
     862            0 :             freecode(onshow);
     863            0 :             freecode(onhide);
     864            0 :         }
     865              : 
     866            0 :         static const char *typestr()
     867              :         {
     868            0 :             return "#Window";
     869              :         }
     870              : 
     871            0 :         const char *gettype() const final
     872              :         {
     873            0 :             return typestr();
     874              :         }
     875              : 
     876            0 :         const char *getname() const final
     877              :         {
     878            0 :             return name.c_str();
     879              :         }
     880              : 
     881              :         void build();
     882              : 
     883            0 :         void hide()
     884              :         {
     885            0 :             if(onhide)
     886              :             {
     887            0 :                 execute(onhide);
     888              :             }
     889            0 :         }
     890              : 
     891            0 :         void show()
     892              :         {
     893            0 :             state |= State_Hidden;
     894            0 :             clearstate(State_HoldMask);
     895            0 :             if(onshow)
     896              :             {
     897            0 :                 execute(onshow);
     898              :             }
     899            0 :         }
     900              : 
     901            0 :         void setup()
     902              :         {
     903            0 :             Object::setup();
     904            0 :             allowinput = eschide = true;
     905            0 :             abovehud = false;
     906            0 :             px = py = pw = ph = 0;
     907            0 :         }
     908              : 
     909            0 :         void layout() final
     910              :         {
     911            0 :             if(state & State_Hidden)
     912              :             {
     913            0 :                 w = h = 0;
     914            0 :                 return;
     915              :             }
     916            0 :             window = this;
     917            0 :             Object::layout();
     918            0 :             window = nullptr;
     919              :         }
     920              : 
     921            0 :         void draw(float sx, float sy) final
     922              :         {
     923            0 :             if(state & State_Hidden)
     924              :             {
     925            0 :                 return;
     926              :             }
     927            0 :             window = this;
     928              : 
     929            0 :             projection();
     930            0 :             hudshader->set();
     931              : 
     932            0 :             glEnable(GL_BLEND);
     933            0 :             blendtype = Blend_Alpha;
     934            0 :             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     935            0 :             gle::colorf(1, 1, 1);
     936              : 
     937            0 :             changed = 0;
     938            0 :             drawing = nullptr;
     939              : 
     940            0 :             Object::draw(sx, sy);
     941              : 
     942            0 :             stopdrawing();
     943              : 
     944            0 :             glDisable(GL_BLEND);
     945              : 
     946            0 :             window = nullptr;
     947              :         }
     948              : 
     949            0 :         void draw()
     950              :         {
     951            0 :             draw(x, y);
     952            0 :         }
     953              : 
     954            0 :         void adjustchildren() final
     955              :         {
     956            0 :             if(state & State_Hidden)
     957              :             {
     958            0 :                 return;
     959              :             }
     960            0 :             window = this;
     961            0 :             Object::adjustchildren();
     962            0 :             window = nullptr;
     963              :         }
     964              : 
     965            0 :         void adjustlayout()
     966              :         {
     967            0 :             float aspect = static_cast<float>(hudw())/hudh();
     968            0 :             ph = std::max(std::max(h, w/aspect), 1.0f);
     969            0 :             pw = aspect*ph;
     970            0 :             Object::adjustlayout(0, 0, pw, ph);
     971            0 :         }
     972              : 
     973              :         #define DOSTATE(flags, func) \
     974              :             void func##children(float cx, float cy, int mask, bool inside, int setflags) final \
     975              :             { \
     976              :                 if(!allowinput || state&State_Hidden || pw <= 0 || ph <= 0) \
     977              :                 { \
     978              :                     return; \
     979              :                 } \
     980              :                 cx = cx*pw + px-x; \
     981              :                 cy = cy*ph + py-y; \
     982              :                 if(!inside || (cx >= 0 && cy >= 0 && cx < w && cy < h)) \
     983              :                 { \
     984              :                     Object::func##children(cx, cy, mask, inside, setflags); \
     985              :                 } \
     986              :             }
     987            0 :         DOSTATES
     988              :         #undef DOSTATE
     989              : 
     990              :         void escrelease(float cx, float cy) final;
     991              : 
     992            0 :         void projection()
     993              :         {
     994            0 :             hudmatrix.ortho(px, px + pw, py + ph, py, -1, 1);
     995            0 :             resethudmatrix();
     996            0 :             sscale = vec2(hudmatrix.a.x, hudmatrix.b.y).mul(0.5f);
     997            0 :             soffset = vec2(hudmatrix.d.x, hudmatrix.d.y).mul(0.5f).add(0.5f);
     998            0 :         }
     999              : 
    1000            0 :         void calcscissor(float x1, float y1, float x2, float y2, int &sx1, int &sy1, int &sx2, int &sy2, bool clip = true) const
    1001              :         {
    1002            0 :             vec2 s1 = vec2(x1, y2).mul(sscale).add(soffset),
    1003            0 :                  s2 = vec2(x2, y1).mul(sscale).add(soffset);
    1004            0 :             sx1 = static_cast<int>(std::floor(s1.x*hudw() + 0.5f));
    1005            0 :             sy1 = static_cast<int>(std::floor(s1.y*hudh() + 0.5f));
    1006            0 :             sx2 = static_cast<int>(std::floor(s2.x*hudw() + 0.5f));
    1007            0 :             sy2 = static_cast<int>(std::floor(s2.y*hudh() + 0.5f));
    1008            0 :             if(clip)
    1009              :             {
    1010            0 :                 sx1 = std::clamp(sx1, 0, hudw());
    1011            0 :                 sy1 = std::clamp(sy1, 0, hudh());
    1012            0 :                 sx2 = std::clamp(sx2, 0, hudw());
    1013            0 :                 sy2 = std::clamp(sy2, 0, hudh());
    1014              :             }
    1015            0 :         }
    1016              : 
    1017            0 :         float calcabovehud() const
    1018              :         {
    1019            0 :             return 1 - (y*sscale.y + soffset.y);
    1020              :         }
    1021              :     };
    1022              : 
    1023              :     static std::unordered_map<std::string, Window *> windows;
    1024              : 
    1025            0 :     void ClipArea::scissor()
    1026              :     {
    1027              :         int sx1, sy1, sx2, sy2;
    1028            0 :         window->calcscissor(x1, y1, x2, y2, sx1, sy1, sx2, sy2);
    1029            0 :         glScissor(sx1, sy1, sx2-sx1, sy2-sy1);
    1030            0 :     }
    1031              : 
    1032              :     struct World final : Object
    1033              :     {
    1034            0 :         static const char *typestr()
    1035              :         {
    1036            0 :             return "#World";
    1037              :         }
    1038              : 
    1039            0 :         const char *gettype() const final
    1040              :         {
    1041            0 :             return typestr();
    1042              :         }
    1043              : 
    1044              :         #define LOOP_WINDOWS(o, body) do { \
    1045              :             for(size_t i = 0; i < children.size(); i++) \
    1046              :             { \
    1047              :                 Window *o = static_cast<Window *>(children[i]); \
    1048              :                 body; \
    1049              :             } \
    1050              :         } while(0)
    1051              :         //note reverse iteration
    1052              :         #define LOOP_WINDOWS_REV(o, body) do { \
    1053              :             for(int i = static_cast<int>(children.size()); --i >=0;) \
    1054              :             { \
    1055              :                 Window *o = static_cast<Window *>(children[i]); \
    1056              :                 body; \
    1057              :             } \
    1058              :         } while(0)
    1059              : 
    1060            0 :         void adjustchildren() final
    1061              :         {
    1062            0 :             LOOP_WINDOWS(w, w->adjustlayout());
    1063            0 :         }
    1064              : 
    1065              :         #define DOSTATE(flags, func) \
    1066              :             void func##children(float cx, float cy, int mask, bool inside, int setflags) final \
    1067              :             { \
    1068              :                 LOOP_WINDOWS_REV(w, \
    1069              :                 { \
    1070              :                     if(((w->state | w->childstate) & mask) != mask) \
    1071              :                     { \
    1072              :                         continue; \
    1073              :                     } \
    1074              :                     w->func##children(cx, cy, mask, inside, setflags); \
    1075              :                     int wflags = (w->state | w->childstate) & (setflags); \
    1076              :                     if(wflags) \
    1077              :                     { \
    1078              :                         childstate |= wflags; \
    1079              :                         break; \
    1080              :                     } \
    1081              :                 }); \
    1082              :             }
    1083            0 :         DOSTATES
    1084              :         #undef DOSTATE
    1085              : 
    1086            0 :         void build()
    1087              :         {
    1088            0 :             reset();
    1089            0 :             setup();
    1090            0 :             LOOP_WINDOWS(w,
    1091              :             {
    1092              :                 w->build();
    1093              :                 if(children.size() <= i )
    1094              :                 {
    1095              :                     break;
    1096              :                 }
    1097              :                 if(children.at(i) != w)
    1098              :                 {
    1099              :                     i--;
    1100              :                 }
    1101              :             });
    1102            0 :             resetstate();
    1103            0 :         }
    1104              : 
    1105            0 :         bool show(Window *w)
    1106              :         {
    1107              :             //if w is not found anywhere
    1108            0 :             if(std::find(children.begin(), children.end(), w) != children.end())
    1109              :             {
    1110            0 :                 return false;
    1111              :             }
    1112            0 :             w->resetchildstate();
    1113            0 :             children.push_back(w);
    1114            0 :             w->show();
    1115            0 :             return true;
    1116              :         }
    1117              : 
    1118            0 :         void hide(Window *w, int index)
    1119              :         {
    1120            0 :             children.erase(children.begin() + index);
    1121            0 :             childstate = 0;
    1122            0 :             for(Object *o : children)
    1123              :             {
    1124            0 :                 childstate |= o->state | o->childstate;
    1125              :             }
    1126            0 :             w->hide();
    1127            0 :         }
    1128              : 
    1129            0 :         bool hide(Window *w)
    1130              :         {
    1131            0 :             if(!w) //check that a window was passed
    1132              :             {
    1133            0 :                 return false;
    1134              :             }
    1135            0 :             if(!children.size()) //check that there are children
    1136              :             {
    1137            0 :                 return false;
    1138              :             }
    1139            0 :             else if(std::find(children.begin(), children.end(), w) != children.end())
    1140              :             {
    1141            0 :                 hide(w, std::distance(children.begin(), std::find(children.begin(), children.end(), w)));
    1142            0 :                 return true;
    1143              :             }
    1144              :             else
    1145              :             {
    1146            0 :                 return false;
    1147              :             }
    1148              :         }
    1149              : 
    1150            0 :         bool hidetop()
    1151              :         {
    1152            0 :             LOOP_WINDOWS_REV(w,
    1153              :             {
    1154              :                 if(w->allowinput && !(w->state & State_Hidden))
    1155              :                 {
    1156              :                     hide(w, i);
    1157              :                     return true;
    1158              :                 }
    1159              :             });
    1160            0 :             return false;
    1161              :         }
    1162              : 
    1163            0 :         int hideall()
    1164              :         {
    1165            0 :             int hidden = 0;
    1166            0 :             LOOP_WINDOWS_REV(w,
    1167              :             {
    1168              :                 hide(w, i);
    1169              :                 hidden++;
    1170              :             });
    1171            0 :             return hidden;
    1172              :         }
    1173              : 
    1174              :         /**
    1175              :          * @brief Returns whether any visible window in this World allows inputs
    1176              :          *
    1177              :          * Does not count hidden windows.
    1178              :          *
    1179              :          * @return true if any window allows inputs, or false if none do
    1180              :          */
    1181            0 :         bool allowinput() const
    1182              :         {
    1183            0 :             LOOP_WINDOWS(w,
    1184              :             {
    1185              :                 if(w->allowinput && !(w->state & State_Hidden))
    1186              :                 {
    1187              :                     return true;
    1188              :                 }
    1189              :             });
    1190            0 :             return false;
    1191              :         }
    1192              : 
    1193            0 :         void draw(float, float) final //note unnamed function parameters
    1194              :         {
    1195            0 :         }
    1196              : 
    1197            0 :         void draw()
    1198              :         {
    1199            0 :             if(children.empty())
    1200              :             {
    1201            0 :                 return;
    1202              :             }
    1203            0 :             LOOP_WINDOWS(w, w->draw());
    1204              :         }
    1205              : 
    1206            0 :         float abovehud()
    1207              :         {
    1208            0 :             float y = 1;
    1209            0 :             LOOP_WINDOWS(w,
    1210              :             {
    1211              :                 if(w->abovehud && !(w->state & State_Hidden))
    1212              :                 {
    1213              :                     y = std::min(y, w->calcabovehud());
    1214              :                 }
    1215              :             });
    1216            0 :             return y;
    1217              :         }
    1218              :     };
    1219              : 
    1220              : #undef LOOP_WINDOWS_REV
    1221              : #undef LOOP_WINDOWS
    1222              : 
    1223              :     static World *world = nullptr;
    1224              : 
    1225            0 :     void Window::escrelease(float, float) //note unnamed function parameters
    1226              :     {
    1227            0 :         if(eschide)
    1228              :         {
    1229            0 :             world->hide(this);
    1230              :         }
    1231            0 :     }
    1232              : 
    1233            0 :     void Window::build()
    1234              :     {
    1235            0 :         reset(world);
    1236            0 :         setup();
    1237            0 :         window = this;
    1238            0 :         buildchildren(contents);
    1239            0 :         window = nullptr;
    1240            0 :     }
    1241              : 
    1242              :     struct HorizontalList final : Object
    1243              :     {
    1244              :         float space, subw;
    1245              : 
    1246            0 :         HorizontalList () : space(), subw() {}
    1247              : 
    1248            0 :         static const char *typestr()
    1249              :         {
    1250            0 :             return "#HorizontalList";
    1251              :         }
    1252              : 
    1253            0 :         const char *gettype() const final
    1254              :         {
    1255            0 :             return typestr();
    1256              :         }
    1257              : 
    1258            0 :         void setup(float space_ = 0)
    1259              :         {
    1260            0 :             Object::setup();
    1261            0 :             space = space_;
    1262            0 :         }
    1263              : 
    1264            0 :         uchar childalign() const final
    1265              :         {
    1266            0 :             return Align_VCenter;
    1267              :         }
    1268              : 
    1269            0 :         void layout() final
    1270              :         {
    1271            0 :             subw = h = 0;
    1272            0 :             for(Object *o : children)
    1273              :             {
    1274            0 :                 o->x = subw;
    1275            0 :                 o->y = 0;
    1276            0 :                 o->layout();
    1277            0 :                 subw += o->w;
    1278            0 :                 h = std::max(h, o->y + o->h);
    1279              :             }
    1280            0 :             w = subw + space*std::max(static_cast<int>(children.size()) - 1, 0);
    1281            0 :         }
    1282              : 
    1283            0 :         void adjustchildren() final
    1284              :         {
    1285            0 :             if(children.empty())
    1286              :             {
    1287            0 :                 return;
    1288              :             }
    1289            0 :             float offset = 0,
    1290            0 :                   sx = 0;
    1291            0 :             const float cspace = (w - subw) / std::max(static_cast<int>(children.size()) - 1, 1),
    1292            0 :                         cstep = (w - subw) / children.size();
    1293            0 :             for(Object *o : children)
    1294              :             {
    1295            0 :                 o->x = offset;
    1296            0 :                 offset += o->w + cspace;
    1297            0 :                 float sw = o->w + cstep;
    1298            0 :                 o->adjustlayout(sx, 0, sw, h);
    1299            0 :                 sx += sw;
    1300              :             }
    1301              :         }
    1302              :     };
    1303              : 
    1304              :     struct VerticalList final : Object
    1305              :     {
    1306              :         float space, subh;
    1307              : 
    1308            0 :         VerticalList() : space(), subh() {}
    1309              : 
    1310            0 :         static const char *typestr()
    1311              :         {
    1312            0 :             return "#VerticalList";
    1313              :         }
    1314              : 
    1315            0 :         const char *gettype() const final
    1316              :         {
    1317            0 :             return typestr();
    1318              :         }
    1319              : 
    1320            0 :         void setup(float space_ = 0)
    1321              :         {
    1322            0 :             Object::setup();
    1323            0 :             space = space_;
    1324            0 :         }
    1325              : 
    1326            0 :         uchar childalign() const final
    1327              :         {
    1328            0 :             return Align_HCenter;
    1329              :         }
    1330              : 
    1331            0 :         void layout() final
    1332              :         {
    1333            0 :             w = subh = 0;
    1334            0 :             for(Object *o : children)
    1335              :             {
    1336            0 :                 o->x = 0;
    1337            0 :                 o->y = subh;
    1338            0 :                 o->layout();
    1339            0 :                 subh += o->h;
    1340            0 :                 w = std::max(w, o->x + o->w);
    1341              :             }
    1342            0 :             h = subh + space*std::max(static_cast<int>(children.size()) - 1, 0);
    1343            0 :         }
    1344              : 
    1345            0 :         void adjustchildren() final
    1346              :         {
    1347            0 :             if(children.empty())
    1348              :             {
    1349            0 :                 return;
    1350              :             }
    1351              : 
    1352            0 :             float offset = 0,
    1353            0 :                   sy     = 0,
    1354            0 :                   rspace = (h - subh) / std::max(static_cast<int>(children.size()) - 1, 1),
    1355            0 :                   rstep = (h - subh) / children.size();
    1356            0 :             for(Object *o : children)
    1357              :             {
    1358            0 :                 o->y = offset;
    1359            0 :                 offset += o->h + rspace;
    1360            0 :                 float sh = o->h + rstep;
    1361            0 :                 o->adjustlayout(0, sy, w, sh);
    1362            0 :                 sy += sh;
    1363              :             }
    1364              :         }
    1365              :     };
    1366              : 
    1367              :     struct Grid final : Object
    1368              :     {
    1369              :         int columns;
    1370              :         float spacew, spaceh, subw, subh;
    1371              :         std::vector<float> widths, heights;
    1372              : 
    1373            0 :         Grid() : columns(), spacew(), spaceh(), subw(), subh() {}
    1374              : 
    1375            0 :         static const char *typestr()
    1376              :         {
    1377            0 :             return "#Grid";
    1378              :         }
    1379              : 
    1380            0 :         const char *gettype() const final
    1381              :         {
    1382            0 :             return typestr();
    1383              :         }
    1384              : 
    1385            0 :         void setup(int columns_, float spacew_ = 0, float spaceh_ = 0)
    1386              :         {
    1387            0 :             Object::setup();
    1388            0 :             columns = columns_;
    1389            0 :             spacew = spacew_;
    1390            0 :             spaceh = spaceh_;
    1391            0 :         }
    1392              : 
    1393            0 :         uchar childalign() const final
    1394              :         {
    1395            0 :             return 0;
    1396              :         }
    1397              : 
    1398            0 :         void layout() final
    1399              :         {
    1400            0 :             widths.clear();
    1401            0 :             heights.clear();
    1402              :             {
    1403            0 :                 size_t column = 0,
    1404            0 :                        row = 0;
    1405            0 :                 for(Object *o : children)
    1406              :                 {
    1407            0 :                     o->layout();
    1408            0 :                     if(column >= widths.size())
    1409              :                     {
    1410            0 :                         widths.push_back(o->w);
    1411              :                     }
    1412            0 :                     else if(o->w > widths[column])
    1413              :                     {
    1414            0 :                         widths[column] = o->w;
    1415              :                     }
    1416            0 :                     if(row >= heights.size())
    1417              :                     {
    1418            0 :                         heights.push_back(o->h);
    1419              :                     }
    1420            0 :                     else if(o->h > heights[row])
    1421              :                     {
    1422            0 :                         heights[row] = o->h;
    1423              :                     }
    1424            0 :                     column = (column + 1) % columns;
    1425            0 :                     if(!column)
    1426              :                     {
    1427            0 :                         row++;
    1428              :                     }
    1429              :                 }
    1430              :             }
    1431            0 :             subw = subh = 0;
    1432            0 :             for(const float &i : widths)
    1433              :             {
    1434            0 :                 subw += i;
    1435              :             }
    1436            0 :             for(const float &i : heights)
    1437              :             {
    1438            0 :                 subh += i;
    1439              :             }
    1440            0 :             w = subw + spacew*std::max(static_cast<int>(widths.size()) - 1, 0);
    1441            0 :             h = subh + spaceh*std::max(static_cast<int>(heights.size()) - 1, 0);
    1442            0 :         }
    1443              : 
    1444            0 :         void adjustchildren() final
    1445              :         {
    1446            0 :             if(children.empty())
    1447              :             {
    1448            0 :                 return;
    1449              :             }
    1450            0 :             int row = 0,
    1451            0 :                 column = 0;
    1452            0 :             float offsety = 0,
    1453            0 :                   sy = 0,
    1454            0 :                   offsetx = 0,
    1455            0 :                   sx = 0,
    1456            0 :                   cspace = (w - subw) / std::max(static_cast<int>(widths.size()) - 1, 1),
    1457            0 :                   cstep = (w - subw) / widths.size(),
    1458            0 :                   rspace = (h - subh) / std::max(static_cast<int>(heights.size()) - 1, 1),
    1459            0 :                   rstep = (h - subh) / heights.size();
    1460            0 :             for(Object *o : children)
    1461              :             {
    1462            0 :                 o->x = offsetx;
    1463            0 :                 o->y = offsety;
    1464            0 :                 o->adjustlayout(sx, sy, widths[column] + cstep, heights[row] + rstep);
    1465            0 :                 offsetx += widths[column] + cspace;
    1466            0 :                 sx += widths[column] + cstep;
    1467            0 :                 column = (column + 1) % columns;
    1468            0 :                 if(!column)
    1469              :                 {
    1470            0 :                     offsetx = sx = 0;
    1471            0 :                     offsety += heights[row] + rspace;
    1472            0 :                     sy += heights[row] + rstep;
    1473            0 :                     row++;
    1474              :                 }
    1475              :             }
    1476              :         }
    1477              :     };
    1478              : 
    1479              :     struct TableHeader : Object
    1480              :     {
    1481            0 :         TableHeader() : columns(-1) {}
    1482              : 
    1483            0 :         static const char *typestr()
    1484              :         {
    1485            0 :             return "#TableHeader";
    1486              :         }
    1487              : 
    1488            0 :         const char *gettype() const override
    1489              :         {
    1490            0 :             return typestr();
    1491              :         }
    1492              : 
    1493            0 :         uchar childalign() const final
    1494              :         {
    1495            0 :             return columns < 0 ? Align_VCenter : Align_HCenter | Align_VCenter;
    1496              :         }
    1497              : 
    1498            0 :         int childcolumns() const final
    1499              :         {
    1500            0 :             return columns;
    1501              :         }
    1502              : 
    1503            0 :         void buildchildren(const uint *columndata, const uint *contents)
    1504              :         {
    1505            0 :             Object *oldparent = buildparent;
    1506            0 :             int oldchild = buildchild;
    1507            0 :             buildparent = this;
    1508            0 :             buildchild = 0;
    1509            0 :             executeret(columndata);
    1510            0 :             if(columns != buildchild)
    1511              :             {
    1512            0 :                 while(static_cast<int>(children.size()) > buildchild)
    1513              :                 {
    1514            0 :                     children.pop_back();
    1515              :                 }
    1516              :             }
    1517            0 :             columns = buildchild;
    1518            0 :             if((*contents&Code_OpMask) != Code_Exit)
    1519              :             {
    1520            0 :                 executeret(contents);
    1521              :             }
    1522            0 :             while(static_cast<int>(children.size()) > buildchild)
    1523              :             {
    1524            0 :                 children.pop_back();
    1525              :             }
    1526            0 :             buildparent = oldparent;
    1527            0 :             buildchild = oldchild;
    1528            0 :             resetstate();
    1529            0 :         }
    1530              : 
    1531            0 :         void adjustchildren() final
    1532              :         {
    1533            0 :             LOOP_CHILD_RANGE(columns, static_cast<int>(children.size()), o, o->adjustlayout(0, 0, w, h));
    1534            0 :         }
    1535              : 
    1536            0 :         void draw(float sx, float sy) final
    1537              :         {
    1538            0 :             LOOP_CHILD_RANGE(columns, static_cast<int>(children.size()), o,
    1539              :             {
    1540              :                 if(!isfullyclipped(sx + o->x, sy + o->y, o->w, o->h))
    1541              :                 {
    1542              :                     o->draw(sx + o->x, sy + o->y);
    1543              :                 }
    1544              :             });
    1545            0 :             LOOP_CHILD_RANGE(0, columns, o,
    1546              :             {
    1547              :                 if(!isfullyclipped(sx + o->x, sy + o->y, o->w, o->h))
    1548              :                 {
    1549              :                     o->draw(sx + o->x, sy + o->y);
    1550              :                 }
    1551              :             });
    1552            0 :         }
    1553              :         private:
    1554              :             int columns;
    1555              :     };
    1556              : 
    1557              :     struct TableRow final: TableHeader
    1558              :     {
    1559            0 :         static const char *typestr()
    1560              :         {
    1561            0 :             return "#TableRow";
    1562              :         }
    1563              : 
    1564            0 :         const char *gettype() const final
    1565              :         {
    1566            0 :             return typestr();
    1567              :         }
    1568              : 
    1569            0 :         bool target(float, float) final //note unnamed function parameters
    1570              :         {
    1571            0 :             return true;
    1572              :         }
    1573              :     };
    1574              : 
    1575              :     struct Table final : Object
    1576              :     {
    1577              :         float spacew, spaceh, subw, subh;
    1578              :         std::vector<float> widths;
    1579            0 :         static const char *typestr()
    1580              :         {
    1581            0 :             return "#Table";
    1582              :         }
    1583              : 
    1584            0 :         const char *gettype() const final
    1585              :         {
    1586            0 :             return typestr();
    1587              :         }
    1588              : 
    1589            0 :         void setup(float spacew_ = 0, float spaceh_ = 0)
    1590              :         {
    1591            0 :             Object::setup();
    1592            0 :             spacew = spacew_;
    1593            0 :             spaceh = spaceh_;
    1594            0 :         }
    1595              : 
    1596            0 :         uchar childalign() const final
    1597              :         {
    1598            0 :             return 0;
    1599              :         }
    1600              : 
    1601            0 :         void layout() final
    1602              :         {
    1603            0 :             widths.clear();
    1604              : 
    1605            0 :             w = subh = 0;
    1606            0 :             for(Object *o : children)
    1607              :             {
    1608            0 :                 o->layout();
    1609            0 :                 int cols = o->childcolumns();
    1610            0 :                 while(static_cast<int>(widths.size()) < cols)
    1611              :                 {
    1612            0 :                     widths.push_back(0);
    1613              :                 }
    1614            0 :                 for(int j = 0; j < cols; ++j)
    1615              :                 {
    1616            0 :                     const Object *c = o->children[j];
    1617            0 :                     if(c->w > widths[j])
    1618              :                     {
    1619            0 :                         widths[j] = c->w;
    1620              :                     }
    1621              :                 }
    1622            0 :                 w = std::max(w, o->w);
    1623            0 :                 subh += o->h;
    1624              :             }
    1625              : 
    1626            0 :             subw = 0;
    1627            0 :             for(const float &i : widths)
    1628              :             {
    1629            0 :                 subw += i;
    1630              :             }
    1631            0 :             w = std::max(w, subw + spacew*std::max(static_cast<int>(widths.size()) - 1, 0));
    1632            0 :             h = subh + spaceh*std::max(static_cast<int>(children.size()) - 1, 0);
    1633            0 :         }
    1634              : 
    1635            0 :         void adjustchildren() final
    1636              :         {
    1637            0 :             if(children.empty())
    1638              :             {
    1639            0 :                 return;
    1640              :             }
    1641            0 :             float offsety = 0,
    1642            0 :                   sy = 0,
    1643            0 :                   cspace = (w - subw) / std::max(static_cast<int>(widths.size()) - 1, 1),
    1644            0 :                   cstep = (w - subw) / widths.size(),
    1645            0 :                   rspace = (h - subh) / std::max(static_cast<int>(children.size()) - 1, 1),
    1646            0 :                   rstep = (h - subh) / children.size();
    1647            0 :             for(Object *o : children)
    1648              :             {
    1649            0 :                 o->x = 0;
    1650            0 :                 o->y = offsety;
    1651            0 :                 o->w = w;
    1652            0 :                 offsety += o->h + rspace;
    1653            0 :                 float sh = o->h + rstep;
    1654            0 :                 o->adjustlayout(0, sy, w, sh);
    1655            0 :                 sy += sh;
    1656              : 
    1657            0 :                 float offsetx = 0;
    1658            0 :                 float sx = 0;
    1659            0 :                 int cols = o->childcolumns();
    1660            0 :                 for(int j = 0; j < cols; ++j)
    1661              :                 {
    1662            0 :                     Object *c = o->children[j];
    1663            0 :                     c->x = offsetx;
    1664            0 :                     offsetx += widths[j] + cspace;
    1665            0 :                     float sw = widths[j] + cstep;
    1666            0 :                     c->adjustlayout(sx, 0, sw, o->h);
    1667            0 :                     sx += sw;
    1668              :                 }
    1669              :             }
    1670              :         }
    1671              :     };
    1672              : 
    1673              :     struct Spacer final : Object
    1674              :     {
    1675              :         float spacew, spaceh;
    1676              : 
    1677            0 :         Spacer() : spacew(), spaceh() {}
    1678              : 
    1679            0 :         void setup(float spacew_, float spaceh_)
    1680              :         {
    1681            0 :             Object::setup();
    1682            0 :             spacew = spacew_;
    1683            0 :             spaceh = spaceh_;
    1684            0 :         }
    1685              : 
    1686            0 :         static const char *typestr()
    1687              :         {
    1688            0 :             return "#Spacer";
    1689              :         }
    1690              : 
    1691            0 :         const char *gettype() const final
    1692              :         {
    1693            0 :             return typestr();
    1694              :         }
    1695              : 
    1696            0 :         void layout() final
    1697              :         {
    1698            0 :             w = spacew;
    1699            0 :             h = spaceh;
    1700            0 :             for(Object *o : children)
    1701              :             {
    1702            0 :                 o->x = spacew;
    1703            0 :                 o->y = spaceh;
    1704            0 :                 o->layout();
    1705            0 :                 w = std::max(w, o->x + o->w);
    1706            0 :                 h = std::max(h, o->y + o->h);
    1707              :             }
    1708            0 :             w += spacew;
    1709            0 :             h += spaceh;
    1710            0 :         }
    1711              : 
    1712            0 :         void adjustchildren() final
    1713              :         {
    1714            0 :             adjustchildrento(spacew, spaceh, w - 2*spacew, h - 2*spaceh);
    1715            0 :         }
    1716              :     };
    1717              : 
    1718              :     struct Offsetter final : Object
    1719              :     {
    1720              :         float offsetx, offsety;
    1721              : 
    1722            0 :         void setup(float offsetx_, float offsety_)
    1723              :         {
    1724            0 :             Object::setup();
    1725            0 :             offsetx = offsetx_;
    1726            0 :             offsety = offsety_;
    1727            0 :         }
    1728              : 
    1729            0 :         static const char *typestr()
    1730              :         {
    1731            0 :             return "#Offsetter";
    1732              :         }
    1733              : 
    1734            0 :         const char *gettype() const final
    1735              :         {
    1736            0 :             return typestr();
    1737              :         }
    1738              : 
    1739            0 :         void layout() final
    1740              :         {
    1741            0 :             Object::layout();
    1742              : 
    1743            0 :             for(Object *o : children)
    1744              :             {
    1745            0 :                 o->x += offsetx;
    1746            0 :                 o->y += offsety;
    1747              :             }
    1748              : 
    1749            0 :             w += offsetx;
    1750            0 :             h += offsety;
    1751            0 :         }
    1752              : 
    1753            0 :         void adjustchildren() final
    1754              :         {
    1755            0 :             adjustchildrento(offsetx, offsety, w - offsetx, h - offsety);
    1756            0 :         }
    1757              :     };
    1758              : 
    1759              :     struct Filler : Object
    1760              :     {
    1761              :         float minw, minh;
    1762              : 
    1763            0 :         void setup(float minw_, float minh_)
    1764              :         {
    1765            0 :             Object::setup();
    1766            0 :             minw = minw_;
    1767            0 :             minh = minh_;
    1768            0 :         }
    1769              : 
    1770            0 :         static const char *typestr()
    1771              :         {
    1772            0 :             return "#Filler";
    1773              :         }
    1774              : 
    1775            0 :         const char *gettype() const override
    1776              :         {
    1777            0 :             return typestr();
    1778              :         }
    1779              : 
    1780            0 :         void layout() final
    1781              :         {
    1782            0 :             Object::layout();
    1783            0 :             w = std::max(w, minw);
    1784            0 :             h = std::max(h, minh);
    1785            0 :         }
    1786              :     };
    1787              : 
    1788              :     struct Target : Filler
    1789              :     {
    1790            0 :         static const char *typestr()
    1791              :         {
    1792            0 :             return "#Target";
    1793              :         }
    1794              : 
    1795            0 :         const char *gettype() const override
    1796              :         {
    1797            0 :             return typestr();
    1798              :         }
    1799            0 :         bool target(float, float) final //note unnamed function parameters
    1800              :         {
    1801            0 :             return true;
    1802              :         }
    1803              :     };
    1804              : 
    1805              :     struct Color final
    1806              :     {
    1807              :         uchar r, g, b, a;
    1808              : 
    1809            0 :         Color() {}
    1810              : 
    1811              :         //converts an int color to components
    1812            4 :         Color(uint color) : r((color>>16)&0xFF), g((color>>8)&0xFF), b(color&0xFF), a(color>>24 ? color>>24 : 0xFF) {}
    1813              : 
    1814              :         //converts an int color w/o alpha and alpha channel to components
    1815              :         Color(uint color, uchar alpha) : r((color>>16)&0xFF), g((color>>8)&0xFF), b(color&0xFF), a(alpha) {}
    1816              : 
    1817              :         //assigns components normally
    1818            4 :         Color(uchar red, uchar green, uchar blue, uchar alpha = 255) : r(red), g(green), b(blue), a(alpha) {}
    1819              : 
    1820            0 :         void init()
    1821              :         {
    1822            0 :             gle::colorub(r, g, b, a);
    1823            0 :         }
    1824              : 
    1825            0 :         void attrib()
    1826              :         {
    1827            0 :             gle::attribub(r, g, b, a);
    1828            0 :         }
    1829              : 
    1830            0 :         static void def()
    1831              :         {
    1832            0 :             gle::defcolor(4, GL_UNSIGNED_BYTE);
    1833            0 :         }
    1834              :     };
    1835              : 
    1836              : #undef LOOP_CHILDREN_REV
    1837              : #undef LOOP_CHILD_RANGE
    1838              : 
    1839              :     struct FillColor : Target
    1840              :     {
    1841              :         enum
    1842              :         {
    1843              :             SOLID = 0,
    1844              :             MODULATE
    1845              :         };
    1846              : 
    1847              :         int type;
    1848              :         Color color;
    1849              : 
    1850            0 :         void setup(int type_, const Color &color_, float minw_ = 0, float minh_ = 0)
    1851              :         {
    1852            0 :             Target::setup(minw_, minh_);
    1853            0 :             type = type_;
    1854            0 :             color = color_;
    1855            0 :         }
    1856              : 
    1857            0 :         static const char *typestr()
    1858              :         {
    1859            0 :             return "#FillColor";
    1860              :         }
    1861              : 
    1862            0 :         const char *gettype() const override
    1863              :         {
    1864            0 :             return typestr();
    1865              :         }
    1866              : 
    1867            0 :         void startdraw() const override
    1868              :         {
    1869            0 :             hudnotextureshader->set();
    1870            0 :             gle::defvertex(2);
    1871            0 :         }
    1872              : 
    1873            0 :         void draw(float sx, float sy) override
    1874              :         {
    1875            0 :             changedraw(Change_Shader | Change_Color | Change_Blend);
    1876            0 :             if(type==MODULATE)
    1877              :             {
    1878            0 :                 modblend();
    1879              :             }
    1880              :             else
    1881              :             {
    1882            0 :                 resetblend();
    1883              :             }
    1884            0 :             color.init();
    1885            0 :             gle::begin(GL_TRIANGLE_STRIP);
    1886            0 :             gle::attribf(sx+w, sy);
    1887            0 :             gle::attribf(sx,   sy);
    1888            0 :             gle::attribf(sx+w, sy+h);
    1889            0 :             gle::attribf(sx,   sy+h);
    1890            0 :             gle::end();
    1891              : 
    1892            0 :             Object::draw(sx, sy);
    1893            0 :         }
    1894              :     };
    1895              : 
    1896              :     class Gradient final : public FillColor
    1897              :     {
    1898              :         public:
    1899              :             enum { VERTICAL, HORIZONTAL };
    1900              : 
    1901              :             int dir;
    1902              : 
    1903            0 :             void setup(int type_, int dir_, const Color &color_, const Color &color2_, float minw_ = 0, float minh_ = 0)
    1904              :             {
    1905            0 :                 FillColor::setup(type_, color_, minw_, minh_);
    1906            0 :                 dir = dir_;
    1907            0 :                 color2 = color2_;
    1908            0 :             }
    1909              : 
    1910            0 :             static const char *typestr()
    1911              :             {
    1912            0 :                 return "#Gradient";
    1913              :             }
    1914              : 
    1915              :         protected:
    1916            0 :             const char *gettype() const final
    1917              :             {
    1918            0 :                 return typestr();
    1919              :             }
    1920              : 
    1921            0 :             void startdraw() const final
    1922              :             {
    1923            0 :                 hudnotextureshader->set();
    1924            0 :                 gle::defvertex(2);
    1925            0 :                 Color::def();
    1926            0 :             }
    1927              : 
    1928              :         private:
    1929              :             Color color2;
    1930              : 
    1931            0 :             void draw(float sx, float sy) final
    1932              :             {
    1933            0 :                 changedraw(Change_Shader | Change_Color | Change_Blend);
    1934            0 :                 if(type==MODULATE)
    1935              :                 {
    1936            0 :                     modblend();
    1937              :                 }
    1938              :                 else
    1939              :                 {
    1940            0 :                     resetblend();
    1941              :                 }
    1942            0 :                 gle::begin(GL_TRIANGLE_STRIP);
    1943            0 :                 gle::attribf(sx+w, sy);   (dir == HORIZONTAL ? color2 : color).attrib();
    1944            0 :                 gle::attribf(sx,   sy);   color.attrib();
    1945            0 :                 gle::attribf(sx+w, sy+h); color2.attrib();
    1946            0 :                 gle::attribf(sx,   sy+h); (dir == HORIZONTAL ? color : color2).attrib();
    1947            0 :                 gle::end();
    1948              : 
    1949            0 :                 Object::draw(sx, sy);
    1950            0 :             }
    1951              :     };
    1952              : 
    1953              :     class Line final : public Filler
    1954              :     {
    1955              :         public:
    1956            0 :             void setup(const Color &color_, float minw_ = 0, float minh_ = 0)
    1957              :             {
    1958            0 :                 Filler::setup(minw_, minh_);
    1959            0 :                 color = color_;
    1960            0 :             }
    1961              : 
    1962            0 :             static const char *typestr()
    1963              :             {
    1964            0 :                 return "#Line";
    1965              :             }
    1966              : 
    1967            0 :             const char *gettype() const final
    1968              :             {
    1969            0 :                 return typestr();
    1970              :             }
    1971              : 
    1972              :         protected:
    1973            0 :             void startdraw() const final
    1974              :             {
    1975            0 :                 hudnotextureshader->set();
    1976            0 :                 gle::defvertex(2);
    1977            0 :             }
    1978              : 
    1979            0 :             void draw(float sx, float sy) final
    1980              :             {
    1981            0 :                 changedraw(Change_Shader | Change_Color);
    1982              : 
    1983            0 :                 color.init();
    1984            0 :                 gle::begin(GL_LINES);
    1985            0 :                 gle::attribf(sx,   sy);
    1986            0 :                 gle::attribf(sx+w, sy+h);
    1987            0 :                 gle::end();
    1988              : 
    1989            0 :                 Object::draw(sx, sy);
    1990            0 :             }
    1991              : 
    1992              :         private:
    1993              :             Color color;
    1994              :     };
    1995              : 
    1996              :     class Outline final : public Filler
    1997              :     {
    1998              :         public:
    1999              : 
    2000            0 :             void setup(const Color &color_, float minw_ = 0, float minh_ = 0)
    2001              :             {
    2002            0 :                 Filler::setup(minw_, minh_);
    2003            0 :                 color = color_;
    2004            0 :             }
    2005              : 
    2006            0 :             const char *gettype() const final
    2007              :             {
    2008            0 :                 return typestr();
    2009              :             }
    2010              : 
    2011            0 :             static const char *typestr()
    2012              :             {
    2013            0 :                 return "#Outline";
    2014              :             }
    2015              : 
    2016            0 :             void draw(float sx, float sy) final
    2017              :             {
    2018            0 :                 changedraw(Change_Shader | Change_Color);
    2019              : 
    2020            0 :                 color.init();
    2021            0 :                 gle::begin(GL_LINE_LOOP);
    2022            0 :                 gle::attribf(sx,   sy);
    2023            0 :                 gle::attribf(sx+w, sy);
    2024            0 :                 gle::attribf(sx+w, sy+h);
    2025            0 :                 gle::attribf(sx,   sy+h);
    2026            0 :                 gle::end();
    2027              : 
    2028            0 :                 Object::draw(sx, sy);
    2029            0 :             }
    2030              :         protected:
    2031            0 :             void startdraw() const final
    2032              :             {
    2033            0 :                 hudnotextureshader->set();
    2034            0 :                 gle::defvertex(2);
    2035            0 :             }
    2036              :         private:
    2037              :             Color color;
    2038              :     };
    2039              : 
    2040            0 :     static bool checkalphamask(Texture *tex, float x, float y)
    2041              :     {
    2042            0 :         if(!tex->alphamask)
    2043              :         {
    2044            0 :             tex->loadalphamask();
    2045            0 :             if(!tex->alphamask)
    2046              :             {
    2047            0 :                 return true;
    2048              :             }
    2049              :         }
    2050            0 :         int tx = std::clamp(static_cast<int>(x*tex->xs), 0, tex->xs-1),
    2051            0 :             ty = std::clamp(static_cast<int>(y*tex->ys), 0, tex->ys-1);
    2052            0 :         if(tex->alphamask[ty*((tex->xs+7)/8) + tx/8] & (1<<(tx%8)))
    2053              :         {
    2054            0 :             return true;
    2055              :         }
    2056            0 :         return false;
    2057              :     }
    2058              : 
    2059              :     class Image : public Filler
    2060              :     {
    2061              :         public:
    2062            0 :             void setup(Texture *tex_, float minw_ = 0, float minh_ = 0)
    2063              :             {
    2064            0 :                 Filler::setup(minw_, minh_);
    2065            0 :                 tex = tex_;
    2066            0 :             }
    2067              : 
    2068            0 :             const char *gettype() const override
    2069              :             {
    2070            0 :                 return typestr();
    2071              :             }
    2072              : 
    2073            0 :             void draw(float sx, float sy) override
    2074              :             {
    2075            0 :                 if(tex != notexture)
    2076              :                 {
    2077            0 :                     bindtex();
    2078            0 :                     quads(sx, sy, w, h);
    2079              :                 }
    2080              : 
    2081            0 :                 Object::draw(sx, sy);
    2082            0 :             }
    2083              : 
    2084              :         protected:
    2085              :             Texture *tex;
    2086              : 
    2087            0 :             bool target(float cx, float cy) override
    2088              :             {
    2089            0 :                 return !(tex->type&Texture::ALPHA) || checkalphamask(tex, cx/w, cy/h);
    2090              :             }
    2091              : 
    2092            0 :             void bindtex()
    2093              :             {
    2094            0 :                 changedraw();
    2095            0 :                 if(lasttex != tex)
    2096              :                 {
    2097            0 :                     if(lasttex)
    2098              :                     {
    2099            0 :                         gle::end();
    2100              :                     }
    2101            0 :                     lasttex = tex;
    2102            0 :                     glBindTexture(GL_TEXTURE_2D, tex->id);
    2103              :                 }
    2104            0 :             }
    2105              : 
    2106            0 :             void startdraw() const final
    2107              :             {
    2108            0 :                 lasttex = nullptr;
    2109              : 
    2110            0 :                 gle::defvertex(2);
    2111            0 :                 gle::deftexcoord0();
    2112            0 :                 gle::begin(GL_TRIANGLE_STRIP);
    2113            0 :             }
    2114              : 
    2115            0 :             void enddraw() const final
    2116              :             {
    2117            0 :                 gle::end();
    2118            0 :             }
    2119              : 
    2120              :         private:
    2121              :             static Texture *lasttex;
    2122              :     };
    2123              : 
    2124              :     Texture *Image::lasttex = nullptr;
    2125              : 
    2126              :     class CroppedImage final : public Image
    2127              :     {
    2128              :         public:
    2129            0 :             void setup(Texture *tex_, float minw_ = 0, float minh_ = 0, float cropx_ = 0, float cropy_ = 0, float cropw_ = 1, float croph_ = 1)
    2130              :             {
    2131            0 :                 Image::setup(tex_, minw_, minh_);
    2132            0 :                 cropx = cropx_;
    2133            0 :                 cropy = cropy_;
    2134            0 :                 cropw = cropw_;
    2135            0 :                 croph = croph_;
    2136            0 :             }
    2137              : 
    2138            0 :             static const char *typestr()
    2139              :             {
    2140            0 :                 return "#CroppedImage";
    2141              :             }
    2142              : 
    2143              :         private:
    2144            0 :             bool target(float cx, float cy) final
    2145              :             {
    2146            0 :                 return !(tex->type&Texture::ALPHA) || checkalphamask(tex, cropx + cx/w*cropw, cropy + cy/h*croph);
    2147              :             }
    2148              : 
    2149            0 :             void draw(float sx, float sy) final
    2150              :             {
    2151            0 :                 if(tex == notexture)
    2152              :                 {
    2153            0 :                     Object::draw(sx, sy);
    2154            0 :                     return;
    2155              :                 }
    2156              : 
    2157            0 :                 bindtex();
    2158            0 :                 quads(sx, sy, w, h, cropx, cropy, cropw, croph);
    2159              : 
    2160            0 :                 Object::draw(sx, sy);
    2161              :             }
    2162              : 
    2163            0 :             const char *gettype() const final
    2164              :             {
    2165            0 :                 return typestr();
    2166              :             }
    2167              : 
    2168              :             float cropx, cropy, cropw, croph;
    2169              :     };
    2170              : 
    2171              :     struct StretchedImage final : Image
    2172              :     {
    2173            0 :         static const char *typestr()
    2174              :         {
    2175            0 :             return "#StretchedImage";
    2176              :         }
    2177              : 
    2178            0 :         const char *gettype() const final
    2179              :         {
    2180            0 :             return typestr();
    2181              :         }
    2182              : 
    2183            0 :         bool target(float cx, float cy) final
    2184              :         {
    2185            0 :             if(!(tex->type&Texture::ALPHA))
    2186              :             {
    2187            0 :                 return true;
    2188              :             }
    2189              :             float mx, my;
    2190            0 :             if(w <= minw)
    2191              :             {
    2192            0 :                 mx = cx/w;
    2193              :             }
    2194            0 :             else if(cx < minw/2)
    2195              :             {
    2196            0 :                 mx = cx/minw;
    2197              :             }
    2198            0 :             else if(cx >= w - minw/2)
    2199              :             {
    2200            0 :                 mx = 1 - (w - cx) / minw;
    2201              :             }
    2202              :             else
    2203              :             {
    2204            0 :                 mx = 0.5f;
    2205              :             }
    2206            0 :             if(h <= minh)
    2207              :             {
    2208            0 :                 my = cy/h;
    2209              :             }
    2210            0 :             else if(cy < minh/2)
    2211              :             {
    2212            0 :                 my = cy/minh;
    2213              :             }
    2214            0 :             else if(cy >= h - minh/2)
    2215              :             {
    2216            0 :                 my = 1 - (h - cy) / minh;
    2217              :             }
    2218              :             else
    2219              :             {
    2220            0 :                 my = 0.5f;
    2221              :             }
    2222              : 
    2223            0 :             return checkalphamask(tex, mx, my);
    2224              :         }
    2225              : 
    2226            0 :         void draw(float sx, float sy) final
    2227              :         {
    2228            0 :             if(tex == notexture)
    2229              :             {
    2230            0 :                 Object::draw(sx, sy);
    2231            0 :                 return;
    2232              :             }
    2233              : 
    2234            0 :             bindtex();
    2235              : 
    2236            0 :             float splitw = (minw ? std::min(minw, w) : w) / 2,
    2237            0 :                   splith = (minh ? std::min(minh, h) : h) / 2,
    2238            0 :                   vy = sy,
    2239            0 :                   ty = 0;
    2240            0 :             for(int i = 0; i < 3; ++i)
    2241              :             {
    2242            0 :                 float vh = 0,
    2243            0 :                       th = 0;
    2244            0 :                 switch(i)
    2245              :                 {
    2246            0 :                     case 0:
    2247              :                     {
    2248            0 :                         if(splith < h - splith)
    2249              :                         {
    2250            0 :                             vh = splith;
    2251            0 :                             th = 0.5f;
    2252              :                         }
    2253              :                         else
    2254              :                         {
    2255            0 :                             vh = h;
    2256            0 :                             th = 1;
    2257              :                         }
    2258            0 :                         break;
    2259              :                     }
    2260            0 :                     case 1:
    2261              :                     {
    2262            0 :                         vh = h - 2*splith;
    2263            0 :                         th = 0;
    2264            0 :                         break;
    2265              :                     }
    2266            0 :                     case 2:
    2267              :                     {
    2268            0 :                         vh = splith;
    2269            0 :                         th = 0.5f;
    2270            0 :                         break;
    2271              :                     }
    2272              :                 }
    2273            0 :                 float vx = sx,
    2274            0 :                       tx = 0;
    2275            0 :                 for(int j = 0; j < 3; ++j)
    2276              :                 {
    2277            0 :                     float vw = 0,
    2278            0 :                           tw = 0;
    2279            0 :                     switch(j)
    2280              :                     {
    2281            0 :                         case 0:
    2282              :                         {
    2283            0 :                             if(splitw < w - splitw)
    2284              :                             {
    2285            0 :                                 vw = splitw;
    2286            0 :                                 tw = 0.5f;
    2287              :                             }
    2288              :                             else
    2289              :                             {
    2290            0 :                                 vw = w;
    2291            0 :                                 tw = 1;
    2292              :                             }
    2293            0 :                             break;
    2294              :                         }
    2295            0 :                         case 1:
    2296              :                         {
    2297            0 :                             vw = w - 2*splitw;
    2298            0 :                             tw = 0;
    2299            0 :                             break;
    2300              :                         }
    2301            0 :                         case 2:
    2302              :                         {
    2303            0 :                             vw = splitw;
    2304            0 :                             tw = 0.5f;
    2305            0 :                             break;
    2306              :                         }
    2307              :                     }
    2308            0 :                     quads(vx, vy, vw, vh, tx, ty, tw, th);
    2309            0 :                     vx += vw;
    2310            0 :                     tx += tw;
    2311            0 :                     if(tx >= 1)
    2312              :                     {
    2313            0 :                         break;
    2314              :                     }
    2315              :                 }
    2316            0 :                 vy += vh;
    2317            0 :                 ty += th;
    2318            0 :                 if(ty >= 1)
    2319              :                 {
    2320            0 :                     break;
    2321              :                 }
    2322              :             }
    2323              : 
    2324            0 :             Object::draw(sx, sy);
    2325              :         }
    2326              :     };
    2327              : 
    2328              :     struct BorderedImage final : Image
    2329              :     {
    2330              :         float texborder, screenborder;
    2331              : 
    2332            0 :         void setup(Texture *tex_, float texborder_, float screenborder_)
    2333              :         {
    2334            0 :             Image::setup(tex_);
    2335            0 :             texborder = texborder_;
    2336            0 :             screenborder = screenborder_;
    2337            0 :         }
    2338              : 
    2339            0 :         static const char *typestr()
    2340              :         {
    2341            0 :             return "#BorderedImage";
    2342              :         }
    2343              : 
    2344            0 :         const char *gettype() const  final
    2345              :         {
    2346            0 :             return typestr();
    2347              :         }
    2348              : 
    2349            0 :         bool target(float cx, float cy) final
    2350              :         {
    2351            0 :             if(!(tex->type&Texture::ALPHA))
    2352              :             {
    2353            0 :                 return true;
    2354              :             }
    2355              :             float mx, my;
    2356            0 :             if(cx < screenborder)
    2357              :             {
    2358            0 :                 mx = cx/screenborder*texborder;
    2359              :             }
    2360            0 :             else if(cx >= w - screenborder)
    2361              :             {
    2362            0 :                 mx = 1-texborder + (cx - (w - screenborder))/screenborder*texborder;
    2363              :             }
    2364              :             else
    2365              :             {
    2366            0 :                 mx = texborder + (cx - screenborder)/(w - 2*screenborder)*(1 - 2*texborder);
    2367              :             }
    2368            0 :             if(cy < screenborder)
    2369              :             {
    2370            0 :                 my = cy/screenborder*texborder;
    2371              :             }
    2372            0 :             else if(cy >= h - screenborder)
    2373              :             {
    2374            0 :                 my = 1-texborder + (cy - (h - screenborder))/screenborder*texborder;
    2375              :             }
    2376              :             else
    2377              :             {
    2378            0 :                 my = texborder + (cy - screenborder)/(h - 2*screenborder)*(1 - 2*texborder);
    2379              :             }
    2380            0 :             return checkalphamask(tex, mx, my);
    2381              :         }
    2382              : 
    2383            0 :         void draw(float sx, float sy) final
    2384              :         {
    2385            0 :             if(tex == notexture)
    2386              :             {
    2387            0 :                 Object::draw(sx, sy);
    2388            0 :                 return;
    2389              :             }
    2390              : 
    2391            0 :             bindtex();
    2392              : 
    2393            0 :             float vy = sy,
    2394            0 :                   ty = 0;
    2395            0 :             for(int i = 0; i < 3; ++i)
    2396              :             {
    2397            0 :                 float vh = 0,
    2398            0 :                       th = 0;
    2399            0 :                 switch(i)
    2400              :                 {
    2401            0 :                     case 0:
    2402              :                     {
    2403            0 :                         vh = screenborder;
    2404            0 :                         th = texborder;
    2405            0 :                         break;
    2406              :                     }
    2407            0 :                     case 1:
    2408              :                     {
    2409            0 :                         vh = h - 2*screenborder;
    2410            0 :                         th = 1 - 2*texborder;
    2411            0 :                         break;
    2412              :                     }
    2413            0 :                     case 2:
    2414              :                     {
    2415            0 :                         vh = screenborder;
    2416            0 :                         th = texborder;
    2417            0 :                         break;
    2418              :                     }
    2419              :                 }
    2420            0 :                 float vx = sx,
    2421            0 :                       tx = 0;
    2422            0 :                 for(int j = 0; j < 3; ++j)
    2423              :                 {
    2424            0 :                     float vw = 0,
    2425            0 :                           tw = 0;
    2426            0 :                     switch(j)
    2427              :                     {
    2428            0 :                         case 0:
    2429              :                         {
    2430            0 :                             vw = screenborder;
    2431            0 :                             tw = texborder;
    2432            0 :                             break;
    2433              :                         }
    2434            0 :                         case 1:
    2435              :                         {
    2436            0 :                             vw = w - 2*screenborder;
    2437            0 :                             tw = 1 - 2*texborder;
    2438            0 :                             break;
    2439              :                         }
    2440            0 :                         case 2:
    2441              :                         {
    2442            0 :                             vw = screenborder;
    2443            0 :                             tw = texborder;
    2444            0 :                             break;
    2445              :                         }
    2446              :                     }
    2447            0 :                     quads(vx, vy, vw, vh, tx, ty, tw, th);
    2448            0 :                     vx += vw;
    2449            0 :                     tx += tw;
    2450              :                 }
    2451            0 :                 vy += vh;
    2452            0 :                 ty += th;
    2453              :             }
    2454              : 
    2455            0 :             Object::draw(sx, sy);
    2456              :         }
    2457              :     };
    2458              : 
    2459              :     struct TiledImage final : Image
    2460              :     {
    2461              :         float tilew, tileh;
    2462              : 
    2463            0 :         void setup(Texture *tex_, float minw_ = 0, float minh_ = 0, float tilew_ = 0, float tileh_ = 0)
    2464              :         {
    2465            0 :             Image::setup(tex_, minw_, minh_);
    2466            0 :             tilew = tilew_;
    2467            0 :             tileh = tileh_;
    2468            0 :         }
    2469              : 
    2470            0 :         static const char *typestr()
    2471              :         {
    2472            0 :             return "#TiledImage";
    2473              :         }
    2474              : 
    2475            0 :         const char *gettype() const final
    2476              :         {
    2477            0 :             return typestr();
    2478              :         }
    2479              : 
    2480            0 :         bool target(float cx, float cy) final
    2481              :         {
    2482            0 :             if(!(tex->type&Texture::ALPHA))
    2483              :             {
    2484            0 :                 return true;
    2485              :             }
    2486              : 
    2487            0 :             return checkalphamask(tex, std::fmod(cx/tilew, 1), std::fmod(cy/tileh, 1));
    2488              :         }
    2489              : 
    2490            0 :         void draw(float sx, float sy) final
    2491              :         {
    2492            0 :             if(tex == notexture)
    2493              :             {
    2494            0 :                 Object::draw(sx, sy);
    2495            0 :                 return;
    2496              :             }
    2497            0 :             bindtex();
    2498            0 :             if(tex->clamp)
    2499              :             {
    2500            0 :                 for(float dy = 0; dy < h; dy += tileh)
    2501              :                 {
    2502            0 :                     float dh = std::min(tileh, h - dy);
    2503            0 :                     for(float dx = 0; dx < w; dx += tilew)
    2504              :                     {
    2505            0 :                         float dw = std::min(tilew, w - dx);
    2506            0 :                         quads(sx + dx, sy + dy, dw, dh, 0, 0, dw / tilew, dh / tileh);
    2507              :                     }
    2508              :                 }
    2509              :             }
    2510              :             else
    2511              :             {
    2512            0 :                 quads(sx, sy, w, h, 0, 0, w/tilew, h/tileh);
    2513              :             }
    2514            0 :             Object::draw(sx, sy);
    2515              :         }
    2516              :     };
    2517              : 
    2518              :     class Shape : public Filler
    2519              :     {
    2520              :         public:
    2521              :             enum
    2522              :             {
    2523              :                 SOLID = 0,
    2524              :                 OUTLINE,
    2525              :                 MODULATE
    2526              :             };
    2527              : 
    2528            0 :             void setup(const Color &color_, int type_ = SOLID, float minw_ = 0, float minh_ = 0)
    2529              :             {
    2530            0 :                 Filler::setup(minw_, minh_);
    2531              : 
    2532            0 :                 color = color_;
    2533            0 :                 type = type_;
    2534            0 :             }
    2535              : 
    2536              :         protected:
    2537              :             int type;
    2538              :             Color color;
    2539              : 
    2540            0 :             void startdraw() const final
    2541              :             {
    2542            0 :                 hudnotextureshader->set();
    2543            0 :                 gle::defvertex(2);
    2544            0 :             }
    2545              : 
    2546              :     };
    2547              : 
    2548              :     struct Triangle final : Shape
    2549              :     {
    2550              :         vec2 a, b, c;
    2551              : 
    2552            0 :         void setup(const Color &color_, float w = 0, float h = 0, int angle = 0, int type_ = SOLID)
    2553              :         {
    2554            0 :             a = vec2(0, -h*2.0f/3);
    2555            0 :             b = vec2(-w/2, h/3);
    2556            0 :             c = vec2(w/2, h/3);
    2557            0 :             if(angle)
    2558              :             {
    2559            0 :                 vec2 rot = sincosmod360(-angle);
    2560            0 :                 a.rotate_around_z(rot);
    2561            0 :                 b.rotate_around_z(rot);
    2562            0 :                 c.rotate_around_z(rot);
    2563              :             }
    2564            0 :             vec2 bbmin = vec2(a).min(b).min(c);
    2565            0 :             a.sub(bbmin);
    2566            0 :             b.sub(bbmin);
    2567            0 :             c.sub(bbmin);
    2568            0 :             vec2 bbmax = vec2(a).max(b).max(c);
    2569              : 
    2570            0 :             Shape::setup(color_, type_, bbmax.x, bbmax.y);
    2571            0 :         }
    2572              : 
    2573            0 :         static const char *typestr()
    2574              :         {
    2575            0 :             return "#Triangle";
    2576              :         }
    2577              : 
    2578            0 :         const char *gettype() const final
    2579              :         {
    2580            0 :             return typestr();
    2581              :         }
    2582              : 
    2583            0 :         bool target(float cx, float cy) final
    2584              :         {
    2585            0 :             if(type == OUTLINE)
    2586              :             {
    2587            0 :                 return false;
    2588              :             }
    2589            0 :             bool side = vec2(cx, cy).sub(b).cross(vec2(a).sub(b)) < 0;
    2590            0 :             return (vec2(cx, cy).sub(c).cross(vec2(b).sub(c)) < 0) == side &&
    2591            0 :                    (vec2(cx, cy).sub(a).cross(vec2(c).sub(a)) < 0) == side;
    2592              :         }
    2593              : 
    2594            0 :         void draw(float sx, float sy) final
    2595              :         {
    2596            0 :             Object::draw(sx, sy);
    2597              : 
    2598            0 :             changedraw(Change_Shader | Change_Color | Change_Blend);
    2599            0 :             if(type==MODULATE)
    2600              :             {
    2601            0 :                 modblend();
    2602              :             }
    2603              :             else
    2604              :             {
    2605            0 :                 resetblend();
    2606              :             }
    2607            0 :             color.init();
    2608            0 :             gle::begin(type == OUTLINE ? GL_LINE_LOOP : GL_TRIANGLES);
    2609            0 :             gle::attrib(vec2(sx, sy).add(a));
    2610            0 :             gle::attrib(vec2(sx, sy).add(b));
    2611            0 :             gle::attrib(vec2(sx, sy).add(c));
    2612            0 :             gle::end();
    2613            0 :         }
    2614              :     };
    2615              : 
    2616              :     struct Circle final : Shape
    2617              :     {
    2618              :         float radius;
    2619              : 
    2620            0 :         void setup(const Color &color_, float size, int type_ = SOLID)
    2621              :         {
    2622            0 :             Shape::setup(color_, type_, size, size);
    2623              : 
    2624            0 :             radius = size/2;
    2625            0 :         }
    2626              : 
    2627            0 :         static const char *typestr()
    2628              :         {
    2629            0 :             return "#Circle";
    2630              :         }
    2631              : 
    2632            0 :         const char *gettype() const  final
    2633              :         {
    2634            0 :             return typestr();
    2635              :         }
    2636              : 
    2637            0 :         bool target(float cx, float cy) final
    2638              :         {
    2639            0 :             if(type == OUTLINE)
    2640              :             {
    2641            0 :                 return false;
    2642              :             }
    2643            0 :             float r = radius <= 0 ? std::min(w, h)/2 : radius;
    2644            0 :             return vec2(cx, cy).sub(r).squaredlen() <= r*r;
    2645              :         }
    2646              : 
    2647            0 :         void draw(float sx, float sy) final
    2648              :         {
    2649            0 :             Object::draw(sx, sy);
    2650              : 
    2651            0 :             changedraw(Change_Shader | Change_Color | Change_Blend);
    2652            0 :             if(type==MODULATE)
    2653              :             {
    2654            0 :                 modblend();
    2655              :             }
    2656              :             else
    2657              :             {
    2658            0 :                 resetblend();
    2659              :             }
    2660              : 
    2661            0 :             float r = radius <= 0 ? std::min(w, h)/2 : radius;
    2662            0 :             color.init();
    2663            0 :             vec2 center(sx + r, sy + r);
    2664            0 :             if(type == OUTLINE)
    2665              :             {
    2666            0 :                 gle::begin(GL_LINE_LOOP);
    2667            0 :                 for(int angle = 0; angle < 360; angle += 360/15)
    2668            0 :                     gle::attrib(vec2(sincos360[angle]).mul(r).add(center));
    2669            0 :                 gle::end();
    2670              :             }
    2671              :             else
    2672              :             {
    2673            0 :                 gle::begin(GL_TRIANGLE_FAN);
    2674            0 :                 gle::attrib(center);
    2675            0 :                 gle::attribf(center.x + r, center.y);
    2676            0 :                 for(int angle = 360/15; angle < 360; angle += 360/15)
    2677              :                 {
    2678            0 :                     vec2 p = vec2(sincos360[angle]).mul(r).add(center);
    2679            0 :                     gle::attrib(p);
    2680            0 :                     gle::attrib(p);
    2681              :                 }
    2682            0 :                 gle::attribf(center.x + r, center.y);
    2683            0 :                 gle::end();
    2684              :             }
    2685            0 :         }
    2686              :     };
    2687              : 
    2688              :     // default size of text in terms of rows per screenful
    2689              :     static VARP(uitextrows, 1, 24, 200);
    2690              :     static FVAR(uitextscale, 1, 0, 0);
    2691              : 
    2692              :     struct Text : Object
    2693              :     {
    2694              :         public:
    2695              : 
    2696            0 :             void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1)
    2697              :             {
    2698            0 :                 Object::setup();
    2699              : 
    2700            0 :                 scale = scale_;
    2701            0 :                 color = color_;
    2702            0 :                 wrap = wrap_;
    2703            0 :             }
    2704              : 
    2705            0 :             virtual const char *getstr() const
    2706              :             {
    2707            0 :                 return "";
    2708              :             }
    2709              : 
    2710              :         protected:
    2711            0 :             const char *gettype() const override
    2712              :             {
    2713            0 :                 return typestr();
    2714              :             }
    2715              : 
    2716            0 :             void draw(float sx, float sy) final
    2717              :             {
    2718            0 :                 Object::draw(sx, sy);
    2719              : 
    2720            0 :                 changedraw(Change_Shader | Change_Color);
    2721              : 
    2722            0 :                 float oldscale = textscale;
    2723            0 :                 textscale = drawscale();
    2724            0 :                 ttr.fontsize(36);
    2725            0 :                 const float conscalefactor = 0.000666;
    2726            0 :                 pushhudscale(conscalefactor);
    2727            0 :                 ttr.renderttf(getstr(), {color.r, color.g, color.b, color.a}, sx*1500, sy*1500, scale*33);
    2728            0 :                 pophudmatrix();
    2729              :                 //draw_text(getstr(), sx/textscale, sy/textscale, 0, color.g, color.b, color.a, -1, wrap >= 0 ? static_cast<int>(wrap/textscale) : -1);
    2730              : 
    2731            0 :                 textscale = oldscale;
    2732            0 :             }
    2733              : 
    2734            0 :             void layout() final
    2735              :             {
    2736            0 :                 Object::layout();
    2737              : 
    2738            0 :                 float k = drawscale(), tw, th;
    2739            0 :                 ttr.ttfbounds(getstr(), tw, th, 42);
    2740            0 :                 w = std::max(w, tw*k);
    2741            0 :                 h = std::max(h, th*k);
    2742            0 :             }
    2743              : 
    2744              :         private:
    2745              :             float scale, wrap;
    2746              :             Color color;
    2747              : 
    2748            0 :             static const char *typestr()
    2749              :             {
    2750            0 :                 return "#Text";
    2751              :             }
    2752              : 
    2753            0 :             float drawscale() const
    2754              :             {
    2755            0 :                 return scale / FONTH;
    2756              :             }
    2757              : 
    2758              :     };
    2759              : 
    2760              :     struct TextString final : Text
    2761              :     {
    2762              :         std::string str;
    2763              : 
    2764            0 :         TextString() : str("")
    2765              :         {
    2766            0 :         }
    2767              : 
    2768            0 :         ~TextString()
    2769            0 :         {
    2770            0 :         }
    2771              : 
    2772            0 :         void setup(const char *str_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1)
    2773              :         {
    2774            0 :             Text::setup(scale_, color_, wrap_);
    2775              : 
    2776            0 :             str = std::string(str_);
    2777            0 :         }
    2778              : 
    2779            0 :         static const char *typestr()
    2780              :         {
    2781            0 :             return "#TextString";
    2782              :         }
    2783              : 
    2784            0 :         const char *gettype() const final
    2785              :         {
    2786            0 :             return typestr();
    2787              :         }
    2788              : 
    2789            0 :         const char *getstr() const final
    2790              :         {
    2791            0 :             return str.c_str();
    2792              :         }
    2793              :     };
    2794              : 
    2795              :     struct TextInt final : Text
    2796              :     {
    2797              :         int val;
    2798              :         std::array<char, 20> str;
    2799              : 
    2800            0 :         TextInt() : val(0) { str[0] = '0'; str[1] = '\0'; }
    2801              : 
    2802            0 :         void setup(int val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1)
    2803              :         {
    2804            0 :             Text::setup(scale_, color_, wrap_);
    2805              : 
    2806            0 :             if(val != val_)
    2807              :             {
    2808            0 :                 val = val_;
    2809            0 :                 intformat(str.data(), val, str.size());
    2810              :             }
    2811            0 :         }
    2812              : 
    2813            0 :         static const char *typestr()
    2814              :         {
    2815            0 :             return "#TextInt";
    2816              :         }
    2817              : 
    2818            0 :         const char *gettype() const final
    2819              :         {
    2820            0 :             return typestr();
    2821              :         }
    2822              : 
    2823            0 :         const char *getstr() const final
    2824              :         {
    2825            0 :             return str.data();
    2826              :         }
    2827              :     };
    2828              : 
    2829              :     struct TextFloat final : Text
    2830              :     {
    2831              :         float val;
    2832              :         std::array<char, 20> str;
    2833              : 
    2834            0 :         TextFloat() : val(0) { std::memcpy(str.data(), "0.0", 4); }
    2835              : 
    2836            0 :         void setup(float val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1)
    2837              :         {
    2838            0 :             Text::setup(scale_, color_, wrap_);
    2839              : 
    2840            0 :             if(val != val_)
    2841              :             {
    2842            0 :                 val = val_;
    2843            0 :                 floatformat(str.data(), val, str.size());
    2844              :             }
    2845            0 :         }
    2846              : 
    2847            0 :         static const char *typestr()
    2848              :         {
    2849            0 :             return "#TextFloat";
    2850              :         }
    2851              : 
    2852            0 :         const char *gettype() const final
    2853              :         {
    2854            0 :             return typestr();
    2855              :         }
    2856              : 
    2857            0 :         const char *getstr() const final
    2858              :         {
    2859            0 :             return str.data();
    2860              :         }
    2861              :     };
    2862              : 
    2863              :     struct Font final : Object
    2864              :     {
    2865              :         ::Font *font;
    2866              : 
    2867            0 :         Font() : font(nullptr) {}
    2868              : 
    2869            0 :         void setup()
    2870              :         {
    2871            0 :             Object::setup();
    2872            0 :         }
    2873              : 
    2874            0 :         void layout() final
    2875              :         {
    2876            0 :             pushfont();
    2877            0 :             setfont(font);
    2878            0 :             Object::layout();
    2879            0 :             popfont();
    2880            0 :         }
    2881              : 
    2882            0 :         void draw(float sx, float sy) final
    2883              :         {
    2884            0 :             pushfont();
    2885            0 :             setfont(font);
    2886            0 :             Object::draw(sx, sy);
    2887            0 :             popfont();
    2888            0 :         }
    2889              : 
    2890            0 :         void buildchildren(const uint *contents)
    2891              :         {
    2892            0 :             pushfont();
    2893            0 :             setfont(font);
    2894            0 :             Object::buildchildren(contents);
    2895            0 :             popfont();
    2896            0 :         }
    2897              : 
    2898              :         #define DOSTATE(flags, func) \
    2899              :             void func##children(float cx, float cy, int mask, bool inside, int setflags) final \
    2900              :             { \
    2901              :                 pushfont(); \
    2902              :                 setfont(font); \
    2903              :                 Object::func##children(cx, cy, mask, inside, setflags); \
    2904              :                 popfont(); \
    2905              :             }
    2906            0 :         DOSTATES
    2907              :         #undef DOSTATE
    2908              : 
    2909            0 :         bool rawkey(int code, bool isdown) final
    2910              :         {
    2911            0 :             pushfont();
    2912            0 :             setfont(font);
    2913            0 :             bool result = Object::rawkey(code, isdown);
    2914            0 :             popfont();
    2915            0 :             return result;
    2916              :         }
    2917              : 
    2918            0 :         bool key(int code, bool isdown) final
    2919              :         {
    2920            0 :             pushfont();
    2921            0 :             setfont(font);
    2922            0 :             bool result = Object::key(code, isdown);
    2923            0 :             popfont();
    2924            0 :             return result;
    2925              :         }
    2926              : 
    2927            0 :         bool textinput(const char *str, int len) final
    2928              :         {
    2929            0 :             pushfont();
    2930            0 :             setfont(font);
    2931            0 :             bool result = Object::textinput(str, len);
    2932            0 :             popfont();
    2933            0 :             return result;
    2934              :         }
    2935              :     };
    2936              : 
    2937              :     static float uicontextscale = 0;
    2938              : 
    2939            1 :     static void uicontextscalecmd()
    2940              :     {
    2941            1 :         floatret(FONTH*uicontextscale);
    2942            1 :     }
    2943              : 
    2944              :     struct Console final : Filler
    2945              :     {
    2946            0 :         void setup(float minw_ = 0, float minh_ = 0)
    2947              :         {
    2948            0 :             Filler::setup(minw_, minh_);
    2949            0 :         }
    2950              : 
    2951            0 :         static const char *typestr()
    2952              :         {
    2953            0 :             return "#Console";
    2954              :         }
    2955              : 
    2956            0 :         const char *gettype() const final
    2957              :         {
    2958            0 :             return typestr();
    2959              :         }
    2960              : 
    2961            0 :         float drawscale() const
    2962              :         {
    2963            0 :             return uicontextscale;
    2964              :         }
    2965              : 
    2966            0 :         void draw(float sx, float sy) final
    2967              :         {
    2968            0 :             Object::draw(sx, sy);
    2969              : 
    2970            0 :             changedraw(Change_Shader | Change_Color);
    2971              : 
    2972            0 :             float k = drawscale();
    2973            0 :             pushhudtranslate(sx, sy, k);
    2974            0 :             renderfullconsole(w/k, h/k);
    2975            0 :             pophudmatrix();
    2976            0 :         }
    2977              :     };
    2978              : 
    2979              :     class Clipper : public Object
    2980              :     {
    2981              :         public:
    2982              :             float virtw, virth;
    2983              : 
    2984            0 :             void setup(float clipw_ = 0, float cliph_ = 0)
    2985              :             {
    2986            0 :                 Object::setup();
    2987            0 :                 clipw = clipw_;
    2988            0 :                 cliph = cliph_;
    2989            0 :                 virtw = virth = 0;
    2990            0 :             }
    2991              : 
    2992            0 :             static const char *typestr()
    2993              :             {
    2994            0 :                 return "#Clipper";
    2995              :             }
    2996              : 
    2997            0 :             void layout() override
    2998              :             {
    2999            0 :                 Object::layout();
    3000              : 
    3001            0 :                 virtw = w;
    3002            0 :                 virth = h;
    3003            0 :                 if(clipw)
    3004              :                 {
    3005            0 :                     w = std::min(w, clipw);
    3006              :                 }
    3007            0 :                 if(cliph)
    3008              :                 {
    3009            0 :                     h = std::min(h, cliph);
    3010              :                 }
    3011            0 :             }
    3012              : 
    3013            0 :             void draw(float sx, float sy) override
    3014              :             {
    3015            0 :                 if((clipw && virtw > clipw) || (cliph && virth > cliph))
    3016              :                 {
    3017            0 :                     stopdrawing();
    3018            0 :                     pushclip(sx, sy, w, h);
    3019            0 :                     Object::draw(sx, sy);
    3020            0 :                     stopdrawing();
    3021            0 :                     popclip();
    3022              :                 }
    3023              :                 else
    3024              :                 {
    3025            0 :                     Object::draw(sx, sy);
    3026              :                 }
    3027            0 :             }
    3028              : 
    3029              :         protected:
    3030              :             float clipw, cliph;
    3031              : 
    3032            0 :             const char *gettype() const override
    3033              :             {
    3034            0 :                 return typestr();
    3035              :             }
    3036              : 
    3037            0 :             void adjustchildren() final
    3038              :             {
    3039            0 :                 adjustchildrento(0, 0, virtw, virth);
    3040            0 :             }
    3041              : 
    3042              :     };
    3043              : 
    3044              :     struct Scroller final : Clipper
    3045              :     {
    3046              :         float offsetx, offsety;
    3047              : 
    3048            0 :         Scroller() : offsetx(0), offsety(0) {}
    3049              : 
    3050            0 :         void setup(float clipw_ = 0, float cliph_ = 0)
    3051              :         {
    3052            0 :             Clipper::setup(clipw_, cliph_);
    3053            0 :         }
    3054              : 
    3055            0 :         static const char *typestr()
    3056              :         {
    3057            0 :             return "#Scroller";
    3058              :         }
    3059              : 
    3060            0 :         const char *gettype() const final
    3061              :         {
    3062            0 :             return typestr();
    3063              :         }
    3064              : 
    3065            0 :         void layout() final
    3066              :         {
    3067            0 :             Clipper::layout();
    3068            0 :             offsetx = std::min(offsetx, hlimit());
    3069            0 :             offsety = std::min(offsety, vlimit());
    3070            0 :         }
    3071              : 
    3072              :         #define DOSTATE(flags, func) \
    3073              :             void func##children(float cx, float cy, int mask, bool inside, int setflags) final \
    3074              :             { \
    3075              :                 cx += offsetx; \
    3076              :                 cy += offsety; \
    3077              :                 if(cx < virtw && cy < virth) \
    3078              :                 { \
    3079              :                     Clipper::func##children(cx, cy, mask, inside, setflags); \
    3080              :                 } \
    3081              :             }
    3082            0 :         DOSTATES
    3083              :         #undef DOSTATE
    3084              : 
    3085            0 :         void draw(float sx, float sy) final
    3086              :         {
    3087            0 :             if((clipw && virtw > clipw) || (cliph && virth > cliph))
    3088              :             {
    3089            0 :                 stopdrawing();
    3090            0 :                 pushclip(sx, sy, w, h);
    3091            0 :                 Object::draw(sx - offsetx, sy - offsety);
    3092            0 :                 stopdrawing();
    3093            0 :                 popclip();
    3094              :             }
    3095              :             else
    3096              :             {
    3097            0 :                 Object::draw(sx, sy);
    3098              :             }
    3099            0 :         }
    3100              : 
    3101            0 :         float hlimit() const
    3102              :         {
    3103            0 :             return std::max(virtw - w, 0.0f);
    3104              :         }
    3105              : 
    3106            0 :         float vlimit() const
    3107              :         {
    3108            0 :             return std::max(virth - h, 0.0f);
    3109              :         }
    3110              : 
    3111            0 :         float hoffset() const
    3112              :         {
    3113            0 :             return offsetx / std::max(virtw, w);
    3114              :         }
    3115              : 
    3116            0 :         float voffset() const
    3117              :         {
    3118            0 :             return offsety / std::max(virth, h);
    3119              :         }
    3120              : 
    3121            0 :         float hscale() const
    3122              :         {
    3123            0 :             return w / std::max(virtw, w);
    3124              :         }
    3125              : 
    3126            0 :         float vscale() const
    3127              :         {
    3128            0 :             return h / std::max(virth, h);
    3129              :         }
    3130              : 
    3131            0 :         void addhscroll(float hscroll)
    3132              :         {
    3133            0 :             sethscroll(offsetx + hscroll);
    3134            0 :         }
    3135              : 
    3136            0 :         void addvscroll(float vscroll)
    3137              :         {
    3138            0 :             setvscroll(offsety + vscroll);
    3139            0 :         }
    3140              : 
    3141            0 :         void sethscroll(float hscroll)
    3142              :         {
    3143            0 :             offsetx = std::clamp(hscroll, 0.0f, hlimit());
    3144            0 :         }
    3145              : 
    3146            0 :         void setvscroll(float vscroll)
    3147              :         {
    3148            0 :             offsety = std::clamp(vscroll, 0.0f, vlimit());
    3149            0 :         }
    3150              : 
    3151              :         void scrollup(float cx, float cy) final;
    3152              : 
    3153              :         void scrolldown(float cx, float cy) final;
    3154              :     };
    3155              : 
    3156              :     class ScrollButton final : public Object
    3157              :     {
    3158              :         public:
    3159            0 :             static const char *typestr()
    3160              :             {
    3161            0 :                 return "#ScrollButton";
    3162              :             }
    3163              : 
    3164              :         protected:
    3165            0 :             const char *gettype() const final
    3166              :             {
    3167            0 :                 return typestr();
    3168              :             }
    3169              :     };
    3170              : 
    3171              :     class ScrollBar : public Object
    3172              :     {
    3173              : 
    3174              :         public:
    3175            0 :             ScrollBar() : offsetx(0), offsety(0) {}
    3176              : 
    3177            0 :             static const char *typestr()
    3178              :             {
    3179            0 :                 return "#ScrollBar";
    3180              :             }
    3181              : 
    3182            0 :             void hold(float cx, float cy) final
    3183              :             {
    3184            0 :                 ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
    3185            0 :                 if(button && button->haschildstate(State_Hold))
    3186              :                 {
    3187            0 :                     movebutton(button, offsetx, offsety, cx - button->x, cy - button->y);
    3188              :                 }
    3189            0 :             }
    3190              : 
    3191            0 :             void press(float cx, float cy) final
    3192              :             {
    3193            0 :                 ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
    3194            0 :                 if(button && button->haschildstate(State_Press))
    3195              :                 {
    3196            0 :                     offsetx = cx - button->x;
    3197            0 :                     offsety = cy - button->y;
    3198              :                 }
    3199              :                 else
    3200              :                 {
    3201            0 :                     scrollto(cx, cy, true);
    3202              :                 }
    3203            0 :             }
    3204              : 
    3205            0 :             void arrowscroll(float dir)
    3206              :             {
    3207            0 :                 addscroll(dir*curtime/1000.0f);
    3208            0 :             }
    3209              :             void wheelscroll(float step);
    3210            0 :             virtual int wheelscrolldirection() const
    3211              :             {
    3212            0 :                 return 1;
    3213              :             }
    3214              : 
    3215              :         protected:
    3216            0 :             const char *gettype() const override
    3217              :             {
    3218            0 :                 return typestr();
    3219              :             }
    3220              : 
    3221            0 :             const char *gettypename() const final
    3222              :             {
    3223            0 :                 return typestr();
    3224              :             }
    3225              : 
    3226            0 :             bool target(float, float) final //note unnamed function parameters
    3227              :             {
    3228            0 :                 return true;
    3229              :             }
    3230              : 
    3231            0 :             virtual void scrollto(float, float, bool) {} //note unnamed function parameters
    3232              :             virtual void movebutton(Object *o, float fromx, float fromy, float tox, float toy) = 0;
    3233              :             virtual void addscroll(Scroller *scroller, float dir) = 0;
    3234              : 
    3235              :         private:
    3236              :             float offsetx, offsety;
    3237              : 
    3238            0 :             void addscroll(float dir)
    3239              :             {
    3240            0 :                 Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
    3241            0 :                 if(scroller)
    3242              :                 {
    3243            0 :                     addscroll(scroller, dir);
    3244              :                 }
    3245            0 :             }
    3246              :     };
    3247              : 
    3248            0 :     void Scroller::scrollup(float, float) //note unnamed function parameters
    3249              :     {
    3250            0 :         ScrollBar *scrollbar = static_cast<ScrollBar *>(findsibling(ScrollBar::typestr()));
    3251            0 :         if(scrollbar)
    3252              :         {
    3253            0 :             scrollbar->wheelscroll(-scrollbar->wheelscrolldirection());
    3254              :         }
    3255            0 :     }
    3256              : 
    3257            0 :     void Scroller::scrolldown(float, float) //note unnamed function parameters
    3258              :     {
    3259            0 :         ScrollBar *scrollbar = static_cast<ScrollBar *>(findsibling(ScrollBar::typestr()));
    3260            0 :         if(scrollbar)
    3261              :         {
    3262            0 :             scrollbar->wheelscroll(scrollbar->wheelscrolldirection());
    3263              :         }
    3264            0 :     }
    3265              : 
    3266              :     struct ScrollArrow : Object
    3267              :     {
    3268              :         float arrowspeed;
    3269              : 
    3270            0 :         void setup(float arrowspeed_ = 0)
    3271              :         {
    3272            0 :             Object::setup();
    3273            0 :             arrowspeed = arrowspeed_;
    3274            0 :         }
    3275              : 
    3276            0 :         static const char *typestr()
    3277              :         {
    3278            0 :             return "#ScrollArrow";
    3279              :         }
    3280              : 
    3281            0 :         const char *gettype() const final
    3282              :         {
    3283            0 :             return typestr();
    3284              :         }
    3285              : 
    3286            0 :         void hold(float, float) final //note unnamed function parameters
    3287              :         {
    3288            0 :             ScrollBar *scrollbar = static_cast<ScrollBar *>(findsibling(ScrollBar::typestr()));
    3289            0 :             if(scrollbar)
    3290              :             {
    3291            0 :                 scrollbar->arrowscroll(arrowspeed);
    3292              :             }
    3293            0 :         }
    3294              :     };
    3295              : 
    3296              :     static VARP(uiscrollsteptime, 0, 50, 1000);
    3297              : 
    3298            0 :     void ScrollBar::wheelscroll(float step)
    3299              :     {
    3300            0 :         ScrollArrow *arrow = static_cast<ScrollArrow *>(findsibling(ScrollArrow::typestr()));
    3301            0 :         if(arrow)
    3302              :         {
    3303            0 :             addscroll(arrow->arrowspeed*step*uiscrollsteptime/1000.0f);
    3304              :         }
    3305            0 :     }
    3306              : 
    3307              :     struct HorizontalScrollBar final : ScrollBar
    3308              :     {
    3309            0 :         static const char *typestr()
    3310              :         {
    3311            0 :             return "#HorizontalScrollBar";
    3312              :         }
    3313              : 
    3314            0 :         const char *gettype() const final
    3315              :         {
    3316            0 :             return typestr();
    3317              :         }
    3318              : 
    3319            0 :         void addscroll(Scroller *scroller, float dir) final
    3320              :         {
    3321            0 :             scroller->addhscroll(dir);
    3322            0 :         }
    3323              : 
    3324            0 :         void scrollto(float cx, float, bool closest = false) final //note unnamed function parameter
    3325              :         {
    3326            0 :             Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
    3327            0 :             if(!scroller)
    3328              :             {
    3329            0 :                 return;
    3330              :             }
    3331            0 :             ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
    3332            0 :             if(!button)
    3333              :             {
    3334            0 :                 return;
    3335              :             }
    3336            0 :             float bscale = (w - button->w) / (1 - scroller->hscale()),
    3337            0 :                   offset = bscale > 1e-3f ? (closest && cx >= button->x + button->w ? cx - button->w : cx)/bscale : 0;
    3338            0 :             scroller->sethscroll(offset*scroller->virtw);
    3339              :         }
    3340              : 
    3341            0 :         void adjustchildren() final
    3342              :         {
    3343            0 :             Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
    3344            0 :             if(!scroller)
    3345              :             {
    3346            0 :                 return;
    3347              :             }
    3348            0 :             ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
    3349            0 :             if(!button)
    3350              :             {
    3351            0 :                 return;
    3352              :             }
    3353            0 :             float bw = w*scroller->hscale();
    3354            0 :             button->w = std::max(button->w, bw);
    3355            0 :             float bscale = scroller->hscale() < 1 ? (w - button->w) / (1 - scroller->hscale()) : 1;
    3356            0 :             button->x = scroller->hoffset()*bscale;
    3357            0 :             button->adjust &= ~Align_HMask;
    3358              : 
    3359            0 :             ScrollBar::adjustchildren();
    3360              :         }
    3361              : 
    3362            0 :         void movebutton(Object *o, float fromx, float, float tox, float toy) final //note unnamed function parameter
    3363              :         {
    3364            0 :             scrollto(o->x + tox - fromx, o->y + toy);
    3365            0 :         }
    3366              :     };
    3367              : 
    3368              :     struct VerticalScrollBar final : ScrollBar
    3369              :     {
    3370            0 :         static const char *typestr()
    3371              :         {
    3372            0 :             return "#VerticalScrollBar";
    3373              :         }
    3374              : 
    3375            0 :         const char *gettype() const final
    3376              :         {
    3377            0 :             return typestr();
    3378              :         }
    3379              : 
    3380            0 :         void addscroll(Scroller *scroller, float dir) final
    3381              :         {
    3382            0 :             scroller->addvscroll(dir);
    3383            0 :         }
    3384              : 
    3385            0 :         void scrollto(float, float cy, bool closest = false) final //note unnamed function parameter
    3386              :         {
    3387            0 :             Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
    3388            0 :             if(!scroller)
    3389              :             {
    3390            0 :                 return;
    3391              :             }
    3392            0 :             ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
    3393            0 :             if(!button)
    3394              :             {
    3395            0 :                 return;
    3396              :             }
    3397            0 :             float bscale = (h - button->h) / (1 - scroller->vscale()),
    3398            0 :                   offset = bscale > 1e-3f ? (closest && cy >= button->y + button->h ? cy - button->h : cy)/bscale : 0;
    3399            0 :             scroller->setvscroll(offset*scroller->virth);
    3400              :         }
    3401              : 
    3402            0 :         void adjustchildren() final
    3403              :         {
    3404            0 :             Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
    3405            0 :             if(!scroller)
    3406              :             {
    3407            0 :                 return;
    3408              :             }
    3409            0 :             ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
    3410            0 :             if(!button)
    3411              :             {
    3412            0 :                 return;
    3413              :             }
    3414            0 :             float bh = h*scroller->vscale();
    3415            0 :             button->h = std::max(button->h, bh);
    3416            0 :             float bscale = scroller->vscale() < 1 ? (h - button->h) / (1 - scroller->vscale()) : 1;
    3417            0 :             button->y = scroller->voffset()*bscale;
    3418            0 :             button->adjust &= ~Align_VMask;
    3419              : 
    3420            0 :             ScrollBar::adjustchildren();
    3421              :         }
    3422              : 
    3423            0 :         void movebutton(Object *o, float, float fromy, float tox, float toy) final //note unnamed function parameter
    3424              :         {
    3425            0 :             scrollto(o->x + tox, o->y + toy - fromy);
    3426            0 :         }
    3427              : 
    3428            0 :         int wheelscrolldirection() const final
    3429              :         {
    3430            0 :             return -1;
    3431              :         }
    3432              :     };
    3433              : 
    3434              :     struct SliderButton final : Object
    3435              :     {
    3436            0 :         static const char *typestr()
    3437              :         {
    3438            0 :             return "#SliderButton";
    3439              :         }
    3440              : 
    3441            0 :         const char *gettype() const final
    3442              :         {
    3443            0 :             return typestr();
    3444              :         }
    3445              :     };
    3446              : 
    3447            0 :     static double getfval(ident *id, double val = 0)
    3448              :     {
    3449            0 :         switch(id->type)
    3450              :         {
    3451            0 :             case Id_Var:
    3452              :             {
    3453            0 :                 val = *id->val.storage.i;
    3454            0 :                 break;
    3455              :             }
    3456            0 :             case Id_FloatVar:
    3457              :             {
    3458            0 :                 val = *id->val.storage.f;
    3459            0 :                 break;
    3460              :             }
    3461            0 :             case Id_StringVar:
    3462              :             {
    3463            0 :                 val = parsenumber(*id->val.storage.s);
    3464            0 :                 break;
    3465              :             }
    3466            0 :             case Id_Alias:
    3467              :             {
    3468            0 :                 val = id->getnumber();
    3469            0 :                 break;
    3470              :             }
    3471            0 :             case Id_Command:
    3472              :             {
    3473              :                 tagval t;
    3474            0 :                 executeret(id, nullptr, 0, true, t);
    3475            0 :                 val = t.getnumber();
    3476            0 :                 t.cleanup();
    3477            0 :                 break;
    3478              :             }
    3479              :         }
    3480            0 :         return val;
    3481              :     }
    3482              : 
    3483            0 :     static void setfval(ident *id, double val, uint *onchange = nullptr)
    3484              :     {
    3485            0 :         switch(id->type)
    3486              :         {
    3487            0 :             case Id_Var:
    3488              :             {
    3489            0 :                 setvarchecked(id, static_cast<int>(std::clamp(val, double(INT_MIN), double(INT_MAX))));
    3490            0 :                 break;
    3491              :             }
    3492            0 :             case Id_FloatVar:
    3493              :             {
    3494            0 :                 setfvarchecked(id, val);
    3495            0 :                 break;
    3496              :             }
    3497            0 :             case Id_StringVar:
    3498              :             {
    3499            0 :                 setsvarchecked(id, numberstr(val));
    3500            0 :                 break;
    3501              :             }
    3502            0 :             case Id_Alias:
    3503              :             {
    3504            0 :                 alias(id->name, numberstr(val));
    3505            0 :                 break;
    3506              :             }
    3507            0 :             case Id_Command:
    3508              :             {
    3509              :                 tagval t;
    3510            0 :                 t.setnumber(val);
    3511            0 :                 execute(id, &t, 1);
    3512            0 :                 break;
    3513              :             }
    3514              :         }
    3515            0 :         if(onchange && (*onchange&Code_OpMask) != Code_Exit)
    3516              :         {
    3517            0 :             execute(onchange);
    3518              :         }
    3519            0 :     }
    3520              : 
    3521              :     struct Slider : Object
    3522              :     {
    3523            0 :         Slider() : val(0), vmin(0), vmax(0), vstep(0), id(nullptr), changed(false) {}
    3524              : 
    3525            0 :         void setup(ident *id_, double vmin_ = 0, double vmax_ = 0, double vstep_ = 1, uint *onchange = nullptr)
    3526              :         {
    3527            0 :             Object::setup();
    3528            0 :             if(!vmin_ && !vmax_)
    3529              :             {
    3530            0 :                 switch(id_->type)
    3531              :                 {
    3532            0 :                     case Id_Var:
    3533              :                     {
    3534            0 :                         vmin_ = id_->val.i.min;
    3535            0 :                         vmax_ = id_->val.i.max;
    3536            0 :                         break;
    3537              :                     }
    3538            0 :                     case Id_FloatVar:
    3539              :                     {
    3540            0 :                         vmin_ = id_->val.f.min;
    3541            0 :                         vmax_ = id_->val.f.max;
    3542            0 :                         break;
    3543              :                     }
    3544              :                 }
    3545              :             }
    3546            0 :             if(id != id_)
    3547              :             {
    3548            0 :                 changed = false;
    3549              :             }
    3550            0 :             id = id_;
    3551            0 :             vmin = vmin_;
    3552            0 :             vmax = vmax_;
    3553            0 :             vstep = vstep_ > 0 ? vstep_ : 1;
    3554            0 :             if(changed)
    3555              :             {
    3556            0 :                 setfval(id, val, onchange);
    3557            0 :                 changed = false;
    3558              :             }
    3559              :             else
    3560              :             {
    3561            0 :                 val = getfval(id, vmin);
    3562              :             }
    3563            0 :         }
    3564              : 
    3565            0 :         static const char *typestr()
    3566              :         {
    3567            0 :             return "#Slider";
    3568              :         }
    3569              : 
    3570            0 :         const char *gettype() const override
    3571              :         {
    3572            0 :             return typestr();
    3573              :         }
    3574              : 
    3575            0 :         const char *gettypename() const final
    3576              :         {
    3577            0 :             return typestr();
    3578              :         }
    3579              : 
    3580            0 :         bool target(float, float) final //note unnamed function parameters
    3581              :         {
    3582            0 :             return true;
    3583              :         }
    3584              : 
    3585            0 :         void arrowscroll(double dir)
    3586              :         {
    3587            0 :             double newval = val + dir*vstep;
    3588            0 :             newval += vstep * (newval < 0 ? -0.5 : 0.5);
    3589            0 :             newval -= std::fmod(newval, vstep);
    3590            0 :             newval = std::clamp(newval, std::min(vmin, vmax), std::max(vmin, vmax));
    3591            0 :             if(val != newval)
    3592              :             {
    3593            0 :                 changeval(newval);
    3594              :             }
    3595            0 :         }
    3596              : 
    3597              :         void wheelscroll(float step);
    3598            0 :         virtual int wheelscrolldirection() const
    3599              :         {
    3600            0 :             return 1;
    3601              :         }
    3602              : 
    3603            0 :         void scrollup(float, float) final //note unnamed function parameters
    3604              :         {
    3605            0 :             wheelscroll(-wheelscrolldirection());
    3606            0 :         }
    3607              : 
    3608            0 :         void scrolldown(float, float) final //note unnamed function parameters
    3609              :         {
    3610            0 :             wheelscroll(wheelscrolldirection());
    3611            0 :         }
    3612              : 
    3613            0 :         virtual void scrollto(float, float) {} //note unnamed function parameters
    3614              : 
    3615            0 :         void hold(float cx, float cy) final
    3616              :         {
    3617            0 :             scrollto(cx, cy);
    3618            0 :         }
    3619              : 
    3620            0 :         void changeval(double newval)
    3621              :         {
    3622            0 :             val = newval;
    3623            0 :             changed = true;
    3624            0 :         }
    3625              : 
    3626              :         protected:
    3627              :             double val, vmin, vmax, vstep;
    3628              : 
    3629              :         private:
    3630              :             ident *id;
    3631              :             bool changed;
    3632              :     };
    3633              : 
    3634              :     static VARP(uislidersteptime, 0, 50, 1000);
    3635              : 
    3636              :     struct SliderArrow final : Object
    3637              :     {
    3638              :         double stepdir;
    3639              :         int laststep;
    3640              : 
    3641            0 :         SliderArrow() : laststep(0) {}
    3642              : 
    3643            0 :         void setup(double dir_ = 0)
    3644              :         {
    3645            0 :             Object::setup();
    3646            0 :             stepdir = dir_;
    3647            0 :         }
    3648              : 
    3649            0 :         static const char *typestr()
    3650              :         {
    3651            0 :             return "#SliderArrow";
    3652              :         }
    3653              : 
    3654            0 :         const char *gettype() const final
    3655              :         {
    3656            0 :             return typestr();
    3657              :         }
    3658              : 
    3659            0 :         void press(float, float) final //note unnamed function parameters
    3660              :         {
    3661            0 :             laststep = totalmillis + 2*uislidersteptime;
    3662              : 
    3663            0 :             Slider *slider = static_cast<Slider *>(findsibling(Slider::typestr()));
    3664            0 :             if(slider)
    3665              :             {
    3666            0 :                 slider->arrowscroll(stepdir);
    3667              :             }
    3668            0 :         }
    3669              : 
    3670            0 :         void hold(float, float) final //note unnamed function parameters
    3671              :         {
    3672            0 :             if(totalmillis < laststep + uislidersteptime)
    3673              :             {
    3674            0 :                 return;
    3675              :             }
    3676            0 :             laststep = totalmillis;
    3677              : 
    3678            0 :             Slider *slider = static_cast<Slider *>(findsibling(Slider::typestr()));
    3679            0 :             if(slider)
    3680              :             {
    3681            0 :                 slider->arrowscroll(stepdir);
    3682              :             }
    3683              :         }
    3684              :     };
    3685              : 
    3686            0 :     void Slider::wheelscroll(float step)
    3687              :     {
    3688            0 :         SliderArrow *arrow = static_cast<SliderArrow *>(findsibling(SliderArrow::typestr()));
    3689            0 :         if(arrow)
    3690              :         {
    3691            0 :             step *= arrow->stepdir;
    3692              :         }
    3693            0 :         arrowscroll(step);
    3694            0 :     }
    3695              : 
    3696              :     struct HorizontalSlider final : Slider
    3697              :     {
    3698            0 :         static const char *typestr()
    3699              :         {
    3700            0 :             return "#HorizontalSlider";
    3701              :         }
    3702              : 
    3703            0 :         const char *gettype() const final
    3704              :         {
    3705            0 :             return typestr();
    3706              :         }
    3707              : 
    3708            0 :         void scrollto(float cx, float) final //note unnamed function parameter
    3709              :         {
    3710            0 :             SliderButton *button = static_cast<SliderButton *>(find(SliderButton::typestr(), false));
    3711            0 :             if(!button)
    3712              :             {
    3713            0 :                 return;
    3714              :             }
    3715            0 :             float offset = w > button->w ? std::clamp((cx - button->w/2)/(w - button->w), 0.0f, 1.0f) : 0.0f;
    3716            0 :             int step = static_cast<int>((val - vmin) / vstep),
    3717            0 :                 bstep = static_cast<int>(offset * (vmax - vmin) / vstep);
    3718            0 :             if(step != bstep)
    3719              :             {
    3720            0 :                 changeval(bstep * vstep + vmin);
    3721              :             }
    3722              :         }
    3723              : 
    3724            0 :         void adjustchildren() final
    3725              :         {
    3726            0 :             SliderButton *button = static_cast<SliderButton *>(find(SliderButton::typestr(), false));
    3727            0 :             if(!button)
    3728              :             {
    3729            0 :                 return;
    3730              :             }
    3731            0 :             int step = static_cast<int>((val - vmin) / vstep),
    3732            0 :                 bstep = static_cast<int>(button->x / (w - button->w) * (vmax - vmin) / vstep);
    3733            0 :             if(step != bstep)
    3734              :             {
    3735            0 :                 button->x = (w - button->w) * step * vstep / (vmax - vmin);
    3736              :             }
    3737            0 :             button->adjust &= ~Align_HMask;
    3738              : 
    3739            0 :             Slider::adjustchildren();
    3740              :         }
    3741              :     };
    3742              : 
    3743              :     struct VerticalSlider final : Slider
    3744              :     {
    3745            0 :         static const char *typestr()
    3746              :         {
    3747            0 :             return "#VerticalSlider";
    3748              :         }
    3749              : 
    3750            0 :         const char *gettype() const final
    3751              :         {
    3752            0 :             return typestr();
    3753              :         }
    3754              : 
    3755            0 :         void scrollto(float, float cy) final //note unnamed function parameter
    3756              :         {
    3757            0 :             SliderButton *button = static_cast<SliderButton *>(find(SliderButton::typestr(), false));
    3758            0 :             if(!button)
    3759              :             {
    3760            0 :                 return;
    3761              :             }
    3762            0 :             float offset = h > button->h ? std::clamp((cy - button->h/2)/(h - button->h), 0.0f, 1.0f) : 0.0f;
    3763            0 :             int step = static_cast<int>((val - vmin) / vstep),
    3764            0 :                 bstep = static_cast<int>(offset * (vmax - vmin) / vstep);
    3765            0 :             if(step != bstep)
    3766              :             {
    3767            0 :                 changeval(bstep * vstep + vmin);
    3768              :             }
    3769              :         }
    3770              : 
    3771            0 :         void adjustchildren() final
    3772              :         {
    3773            0 :             SliderButton *button = static_cast<SliderButton *>(find(SliderButton::typestr(), false));
    3774            0 :             if(!button)
    3775              :             {
    3776            0 :                 return;
    3777              :             }
    3778            0 :             int step = static_cast<int>((val - vmin) / vstep),
    3779            0 :                 bstep = static_cast<int>(button->y / (h - button->h) * (vmax - vmin) / vstep);
    3780            0 :             if(step != bstep)
    3781              :             {
    3782            0 :                 button->y = (h - button->h) * step * vstep / (vmax - vmin);
    3783              :             }
    3784            0 :             button->adjust &= ~Align_VMask;
    3785              : 
    3786            0 :             Slider::adjustchildren();
    3787              :         }
    3788              : 
    3789            0 :         int wheelscrolldirection() const final
    3790              :         {
    3791            0 :             return -1;
    3792              :         }
    3793              :     };
    3794              : 
    3795              :     struct TextEditor : Object
    3796              :     {
    3797              :         static TextEditor *focus;
    3798              : 
    3799            0 :         TextEditor() : edit(nullptr), keyfilter("") {}
    3800              : 
    3801            0 :         void setup(const char *name, int length, int height, float scale_ = 1, const char *initval = nullptr, int mode = Editor_Used, const char *keyfilter_ = nullptr)
    3802              :         {
    3803            0 :             Object::setup();
    3804            0 :             Editor *edit_ = useeditor(name, mode, false, initval);
    3805            0 :             if(edit_ != edit)
    3806              :             {
    3807            0 :                 if(edit)
    3808              :                 {
    3809            0 :                     clearfocus();
    3810              :                 }
    3811            0 :                 edit = edit_;
    3812              :             }
    3813            0 :             else if(isfocus() && !hasstate(State_Hover))
    3814              :             {
    3815            0 :                 commit();
    3816              :             }
    3817            0 :             if(initval && edit->mode == Editor_Focused && !isfocus())
    3818              :             {
    3819            0 :                 edit->init(initval);
    3820              :             }
    3821            0 :             edit->active = true;
    3822            0 :             edit->linewrap = length < 0;
    3823            0 :             edit->maxx = edit->linewrap ? -1 : length;
    3824            0 :             edit->maxy = height <= 0 ? 1 : -1;
    3825            0 :             edit->pixelwidth = std::abs(length)*fontwidth();
    3826            0 :             if(edit->linewrap && edit->maxy == 1)
    3827              :             {
    3828            0 :                 edit->updateheight();
    3829              :             }
    3830              :             else
    3831              :             {
    3832            0 :                 edit->pixelheight = FONTH*std::max(height, 1);
    3833              :             }
    3834            0 :             scale = scale_;
    3835            0 :             if(keyfilter_)
    3836              :             {
    3837            0 :                 keyfilter = std::string(keyfilter_);
    3838              :             }
    3839              :             else
    3840              :             {
    3841            0 :                 keyfilter = std::string();
    3842              :             }
    3843            0 :         }
    3844            0 :         ~TextEditor()
    3845            0 :         {
    3846            0 :             clearfocus();
    3847            0 :             keyfilter = std::string();
    3848            0 :         }
    3849              : 
    3850            0 :         static void setfocus(TextEditor *e)
    3851              :         {
    3852            0 :             if(focus == e)
    3853              :             {
    3854            0 :                 return;
    3855              :             }
    3856            0 :             focus = e;
    3857            0 :             bool allowtextinput = focus!=nullptr && focus->allowtextinput();
    3858            0 :             ::textinput(allowtextinput, TextInput_GUI);
    3859            0 :             ::keyrepeat(allowtextinput, KeyRepeat_GUI);
    3860              :         }
    3861            0 :         void setfocus()
    3862              :         {
    3863            0 :             setfocus(this);
    3864            0 :         }
    3865              : 
    3866            0 :         void clearfocus()
    3867              :         {
    3868            0 :             if(focus == this)
    3869              :             {
    3870            0 :                 setfocus(nullptr);
    3871              :             }
    3872            0 :         }
    3873              : 
    3874            0 :         bool isfocus() const
    3875              :         {
    3876            0 :             return focus == this;
    3877              :         }
    3878              : 
    3879            0 :         static const char *typestr()
    3880              :         {
    3881            0 :             return "#TextEditor";
    3882              :         }
    3883              : 
    3884            0 :         const char *gettype() const override
    3885              :         {
    3886            0 :             return typestr();
    3887              :         }
    3888              : 
    3889            0 :         bool target(float, float) final//note unnamed function parameters
    3890              :         {
    3891            0 :             return true;
    3892              :         }
    3893              : 
    3894            0 :         float drawscale() const
    3895              :         {
    3896            0 :             return scale / FONTH;
    3897              :         }
    3898              : 
    3899            0 :         void draw(float sx, float sy) final
    3900              :         {
    3901            0 :             changedraw(Change_Shader | Change_Color);
    3902              : 
    3903            0 :             edit->rendered = true;
    3904              : 
    3905            0 :             float k = drawscale();
    3906            0 :             pushhudtranslate(sx, sy, k);
    3907              : 
    3908            0 :             edit->draw(fontwidth()/2, 0, 0xFFFFFF);
    3909              : 
    3910            0 :             pophudmatrix();
    3911              : 
    3912            0 :             Object::draw(sx, sy);
    3913            0 :         }
    3914              : 
    3915            0 :         void layout() final
    3916              :         {
    3917            0 :             Object::layout();
    3918              : 
    3919            0 :             float k = drawscale();
    3920            0 :             w = std::max(w, (edit->pixelwidth + fontwidth())*k);
    3921            0 :             h = std::max(h, edit->pixelheight*k);
    3922            0 :         }
    3923              : 
    3924            0 :         virtual void resetmark(float cx, float cy)
    3925              :         {
    3926            0 :             edit->mark(false);
    3927            0 :             offsetx = cx;
    3928            0 :             offsety = cy;
    3929            0 :         }
    3930              : 
    3931            0 :         void press(float cx, float cy) final
    3932              :         {
    3933            0 :             setfocus();
    3934            0 :             resetmark(cx, cy);
    3935            0 :         }
    3936              : 
    3937            0 :         void hold(float cx, float cy) final
    3938              :         {
    3939            0 :             if(isfocus())
    3940              :             {
    3941            0 :                 float k = drawscale();
    3942            0 :                 bool dragged = std::max(std::fabs(cx - offsetx), std::fabs(cy - offsety)) > (FONTH/8.0f)*k;
    3943            0 :                 edit->hit(static_cast<int>(std::floor(cx/k - fontwidth()/2)), static_cast<int>(std::floor(cy/k)), dragged);
    3944              :             }
    3945            0 :         }
    3946              : 
    3947            0 :         void scrollup(float, float) final //note unnamed function parameters
    3948              :         {
    3949            0 :             edit->scrollup();
    3950            0 :         }
    3951              : 
    3952            0 :         void scrolldown(float, float) final //note unnamed function parameters
    3953              :         {
    3954            0 :             edit->scrolldown();
    3955            0 :         }
    3956              : 
    3957            0 :         virtual void cancel()
    3958              :         {
    3959            0 :             clearfocus();
    3960            0 :         }
    3961              : 
    3962            0 :         virtual void commit()
    3963              :         {
    3964            0 :             clearfocus();
    3965            0 :         }
    3966              : 
    3967            0 :         bool key(int code, bool isdown) final
    3968              :         {
    3969            0 :             if(Object::key(code, isdown))
    3970              :             {
    3971            0 :                 return true;
    3972              :             }
    3973            0 :             if(!isfocus())
    3974              :             {
    3975            0 :                 return false;
    3976              :             }
    3977            0 :             switch(code)
    3978              :             {
    3979            0 :                 case SDLK_ESCAPE:
    3980              :                 {
    3981            0 :                     if(isdown)
    3982              :                     {
    3983            0 :                         cancel();
    3984              :                     }
    3985            0 :                     return true;
    3986              :                 }
    3987            0 :                 case SDLK_RETURN:
    3988              :                 case SDLK_TAB:
    3989              :                 {
    3990            0 :                     if(edit->maxy != 1)
    3991              :                     {
    3992            0 :                         break;
    3993              :                     }
    3994              :                     [[fallthrough]];
    3995              :                 }
    3996              :                 case SDLK_KP_ENTER:
    3997              :                 {
    3998            0 :                     if(isdown)
    3999              :                     {
    4000            0 :                         commit();
    4001              :                     }
    4002            0 :                     return true;
    4003              :                 }
    4004              :             }
    4005            0 :             if(isdown)
    4006              :             {
    4007            0 :                 edit->key(code);
    4008              :             }
    4009            0 :             return true;
    4010              :         }
    4011              : 
    4012            0 :         virtual bool allowtextinput() const
    4013              :         {
    4014            0 :             return true;
    4015              :         }
    4016              : 
    4017            0 :         bool textinput(const char *str, int len) final
    4018              :         {
    4019            0 :             if(Object::textinput(str, len))
    4020              :             {
    4021            0 :                 return true;
    4022              :             }
    4023            0 :             if(!isfocus() || !allowtextinput())
    4024              :             {
    4025            0 :                 return false;
    4026              :             }
    4027            0 :             if(keyfilter.empty())
    4028              :             {
    4029            0 :                 edit->input(str, len);
    4030              :             }
    4031              :             else
    4032              :             {
    4033            0 :                 while(len > 0)
    4034              :                 {
    4035            0 :                     int accept = std::min(len, static_cast<int>(std::strspn(str, keyfilter.c_str())));
    4036            0 :                     if(accept > 0)
    4037              :                     {
    4038            0 :                         edit->input(str, accept);
    4039              :                     }
    4040            0 :                     str += accept + 1;
    4041            0 :                     len -= accept + 1;
    4042            0 :                     if(len <= 0)
    4043              :                     {
    4044            0 :                         break;
    4045              :                     }
    4046            0 :                     int reject = static_cast<int>(std::strcspn(str, keyfilter.c_str()));
    4047            0 :                     str += reject;
    4048            0 :                     str -= reject;
    4049              :                 }
    4050              :             }
    4051            0 :             return true;
    4052              :         }
    4053              :         protected:
    4054              :             Editor *edit;
    4055              :         private:
    4056              :             std::string keyfilter;
    4057              :             float scale, offsetx, offsety;
    4058              : 
    4059              :     };
    4060              : 
    4061              :     TextEditor *TextEditor::focus = nullptr;
    4062              : 
    4063            0 :     static const char *getsval(ident *id, bool &shouldfree, const char *val = "")
    4064              :     {
    4065            0 :         switch(id->type)
    4066              :         {
    4067            0 :             case Id_Var:
    4068              :             {
    4069            0 :                 val = intstr(*id->val.storage.i);
    4070            0 :                 break;
    4071              :             }
    4072            0 :             case Id_FloatVar:
    4073              :             {
    4074            0 :                 val = floatstr(*id->val.storage.f);
    4075            0 :                 break;
    4076              :             }
    4077            0 :             case Id_StringVar:
    4078              :             {
    4079            0 :                 val = *id->val.storage.s;
    4080            0 :                 break;
    4081              :             }
    4082            0 :             case Id_Alias:
    4083              :             {
    4084            0 :                 val = id->getstr();
    4085            0 :                 break;
    4086              :             }
    4087            0 :             case Id_Command:
    4088              :             {
    4089            0 :                 val = executestr(id, nullptr, 0, true);
    4090            0 :                 shouldfree = true;
    4091            0 :                 break;
    4092              :             }
    4093              :         }
    4094            0 :         return val;
    4095              :     }
    4096              : 
    4097            0 :     static void setsval(ident *id, const char *val, uint *onchange = nullptr)
    4098              :     {
    4099            0 :         switch(id->type)
    4100              :         {
    4101            0 :             case Id_Var:
    4102              :             {
    4103            0 :                 setvarchecked(id, parseint(val));
    4104            0 :                 break;
    4105              :             }
    4106            0 :             case Id_FloatVar:
    4107              :             {
    4108            0 :                 setfvarchecked(id, parsefloat(val));
    4109            0 :                 break;
    4110              :             }
    4111            0 :             case Id_StringVar:
    4112              :             {
    4113            0 :                 setsvarchecked(id, val);
    4114            0 :                 break;
    4115              :             }
    4116            0 :             case Id_Alias:
    4117              :             {
    4118            0 :                 alias(id->name, val);
    4119            0 :                 break;
    4120              :             }
    4121            0 :             case Id_Command:
    4122              :             {
    4123              :                 tagval t;
    4124            0 :                 t.setstr(newstring(val));
    4125            0 :                 execute(id, &t, 1);
    4126            0 :                 break;
    4127              :             }
    4128              :         }
    4129            0 :         if(onchange && (*onchange&Code_OpMask) != Code_Exit)
    4130              :         {
    4131            0 :             execute(onchange);
    4132              :         }
    4133            0 :     }
    4134              : 
    4135              :     struct Field : TextEditor
    4136              :     {
    4137              :         ident *id;
    4138              :         bool changed;
    4139              : 
    4140            0 :         Field() : id(nullptr), changed(false) {}
    4141              : 
    4142            0 :         void setup(ident *id_, int length, uint *onchange, float newscale = 1, const char *keyfilter_ = nullptr)
    4143              :         {
    4144            0 :             if(isfocus() && !hasstate(State_Hover))
    4145              :             {
    4146            0 :                 commit();
    4147              :             }
    4148            0 :             if(changed)
    4149              :             {
    4150            0 :                 if(id == id_)
    4151              :                 {
    4152            0 :                     setsval(id, edit->lines[0].text, onchange);
    4153              :                 }
    4154            0 :                 changed = false;
    4155              :             }
    4156            0 :             bool shouldfree = false;
    4157            0 :             const char *initval = id != id_ || !isfocus() ? getsval(id_, shouldfree) : nullptr;
    4158            0 :             TextEditor::setup(id_->name, length, 0, newscale, initval, Editor_Focused, keyfilter_);
    4159            0 :             if(shouldfree)
    4160              :             {
    4161            0 :                 delete[] initval;
    4162              :             }
    4163            0 :             id = id_;
    4164            0 :         }
    4165              : 
    4166            0 :         static const char *typestr()
    4167              :         {
    4168            0 :             return "#Field";
    4169              :         }
    4170              : 
    4171            0 :         const char *gettype() const override
    4172              :         {
    4173            0 :             return typestr();
    4174              :         }
    4175              : 
    4176            0 :         void commit() final
    4177              :         {
    4178            0 :             TextEditor::commit();
    4179            0 :             changed = true;
    4180            0 :         }
    4181              : 
    4182            0 :         void cancel() final
    4183              :         {
    4184            0 :             TextEditor::cancel();
    4185            0 :             changed = false;
    4186            0 :         }
    4187              :     };
    4188              : 
    4189              :     struct KeyField final : Field
    4190              :     {
    4191            0 :         static const char *typestr()
    4192              :         {
    4193            0 :             return "#KeyField";
    4194              :         }
    4195              : 
    4196            0 :         const char *gettype() const final
    4197              :         {
    4198            0 :             return typestr();
    4199              :         }
    4200              : 
    4201            0 :         void resetmark(float cx, float cy) final
    4202              :         {
    4203            0 :             edit->clear();
    4204            0 :             Field::resetmark(cx, cy);
    4205            0 :         }
    4206              : 
    4207            0 :         void insertkey(int code)
    4208              :         {
    4209            0 :             const char *keyname = getkeyname(code);
    4210            0 :             if(keyname)
    4211              :             {
    4212            0 :                 if(!edit->empty())
    4213              :                 {
    4214            0 :                     edit->insert(" ");
    4215              :                 }
    4216            0 :                 edit->insert(keyname);
    4217              :             }
    4218            0 :         }
    4219              : 
    4220            0 :         bool rawkey(int code, bool isdown) final
    4221              :         {
    4222            0 :             if(Object::rawkey(code, isdown))
    4223              :             {
    4224            0 :                 return true;
    4225              :             }
    4226            0 :             if(!isfocus() || !isdown)
    4227              :             {
    4228            0 :                 return false;
    4229              :             }
    4230            0 :             if(code == SDLK_ESCAPE)
    4231              :             {
    4232            0 :                 commit();
    4233              :             }
    4234              :             else
    4235              :             {
    4236            0 :                 insertkey(code);
    4237              :             }
    4238            0 :             return true;
    4239              :         }
    4240              : 
    4241            0 :         bool allowtextinput() const final
    4242              :         {
    4243            0 :             return false;
    4244              :         }
    4245              :     };
    4246              : 
    4247              :     struct Preview : Target
    4248              :     {
    4249            0 :         void startdraw() const final
    4250              :         {
    4251            0 :             glDisable(GL_BLEND);
    4252              : 
    4253            0 :             if(clipstack.size())
    4254              :             {
    4255            0 :                 glDisable(GL_SCISSOR_TEST);
    4256              :             }
    4257            0 :         }
    4258              : 
    4259            0 :         void enddraw() const final
    4260              :         {
    4261            0 :             glEnable(GL_BLEND);
    4262              : 
    4263            0 :             if(clipstack.size())
    4264              :             {
    4265            0 :                 glEnable(GL_SCISSOR_TEST);
    4266              :             }
    4267            0 :         }
    4268              :     };
    4269              : 
    4270              :     struct ModelPreview final : Preview
    4271              :     {
    4272              :         std::string name;
    4273              :         int anim;
    4274              : 
    4275            0 :         ModelPreview() : name("") {}
    4276            0 :         ~ModelPreview() {}
    4277              : 
    4278            0 :         void setup(const char *name_, const char *animspec, float minw_, float minh_)
    4279              :         {
    4280            0 :             Preview::setup(minw_, minh_);
    4281            0 :             name = std::string(name_);
    4282              : 
    4283            0 :             anim = Anim_All;
    4284            0 :             if(animspec[0])
    4285              :             {
    4286            0 :                 if(isdigit(animspec[0]))
    4287              :                 {
    4288            0 :                     anim = parseint(animspec);
    4289            0 :                     if(anim >= 0)
    4290              :                     {
    4291            0 :                         anim %= Anim_Index;
    4292              :                     }
    4293              :                     else
    4294              :                     {
    4295            0 :                         anim = Anim_All;
    4296              :                     }
    4297              :                 }
    4298              :                 else
    4299              :                 {
    4300            0 :                     std::vector<size_t> anims = findanims(animspec);
    4301            0 :                     if(anims.size())
    4302              :                     {
    4303            0 :                         anim = anims[0];
    4304              :                     }
    4305            0 :                 }
    4306              :             }
    4307            0 :             anim |= Anim_Loop;
    4308            0 :         }
    4309              : 
    4310            0 :         static const char *typestr()
    4311              :         {
    4312            0 :             return "#ModelPreview";
    4313              :         }
    4314              : 
    4315            0 :         const char *gettype() const final
    4316              :         {
    4317            0 :             return typestr();
    4318              :         }
    4319              : 
    4320            0 :         void draw(float sx, float sy) final
    4321              :         {
    4322            0 :             Object::draw(sx, sy);
    4323              : 
    4324            0 :             changedraw(Change_Shader);
    4325              : 
    4326              :             int sx1, sy1, sx2, sy2;
    4327            0 :             window->calcscissor(sx, sy, sx+w, sy+h, sx1, sy1, sx2, sy2, false);
    4328            0 :             modelpreview.start(sx1, sy1, sx2-sx1, sy2-sy1, false, clipstack.size() > 0);
    4329            0 :             model *m = loadmodel(name);
    4330            0 :             if(m)
    4331              :             {
    4332            0 :                 vec center, radius;
    4333            0 :                 m->boundbox(center, radius);
    4334              :                 float yaw;
    4335            0 :                 vec o = calcmodelpreviewpos(radius, yaw).sub(center);
    4336            0 :                 rendermodel(name, anim, o, yaw, 0, 0, 0, nullptr, nullptr, 0);
    4337              :             }
    4338            0 :             if(clipstack.size())
    4339              :             {
    4340            0 :                 clipstack.back().scissor();
    4341              :             }
    4342            0 :             modelpreview.end();
    4343            0 :         }
    4344              :     };
    4345              : 
    4346              :     class PrefabPreview final : public Preview
    4347              :     {
    4348              :         public:
    4349            0 :             PrefabPreview()
    4350            0 :             {
    4351            0 :             }
    4352              : 
    4353            0 :             ~PrefabPreview()
    4354            0 :             {
    4355            0 :             }
    4356              : 
    4357            0 :             void setup(const char *name_, int color_, float minw_, float minh_)
    4358              :             {
    4359            0 :                 Preview::setup(minw_, minh_);
    4360            0 :                 name = std::string(name_);
    4361            0 :                 color = vec::hexcolor(color_);
    4362            0 :             }
    4363              : 
    4364            0 :             static const char *typestr()
    4365              :             {
    4366            0 :                 return "#PrefabPreview";
    4367              :             }
    4368              : 
    4369            0 :             const char *gettype() const final
    4370              :             {
    4371            0 :                 return typestr();
    4372              :             }
    4373              : 
    4374            0 :             void draw(float sx, float sy) final
    4375              :             {
    4376            0 :                 Object::draw(sx, sy);
    4377            0 :                 changedraw(Change_Shader);
    4378              :                 int sx1, sy1, sx2, sy2;
    4379            0 :                 window->calcscissor(sx, sy, sx+w, sy+h, sx1, sy1, sx2, sy2, false);
    4380            0 :                 modelpreview.start(sx1, sy1, sx2-sx1, sy2-sy1, false, clipstack.size() > 0);
    4381            0 :                 previewprefab(name.c_str(), color);
    4382            0 :                 if(clipstack.size())
    4383              :                 {
    4384            0 :                     clipstack.back().scissor();
    4385              :                 }
    4386            0 :                 modelpreview.end();
    4387            0 :             }
    4388              :         private:
    4389              :             std::string name;
    4390              :             vec color;
    4391              :     };
    4392              : 
    4393              :     static VARP(uislotviewtime, 0, 25, 1000);
    4394              :     static int lastthumbnail = 0;
    4395              : 
    4396              :     class SlotViewer : public Target
    4397              :     {
    4398              :         public:
    4399            0 :             void setup(int index_, float minw_ = 0, float minh_ = 0)
    4400              :             {
    4401            0 :                 Target::setup(minw_, minh_);
    4402            0 :                 index = index_;
    4403            0 :             }
    4404              : 
    4405            0 :             static const char *typestr()
    4406              :             {
    4407            0 :                 return "#SlotViewer";
    4408              :             }
    4409              : 
    4410            0 :             const char *gettype() const override
    4411              :             {
    4412            0 :                 return typestr();
    4413              :             }
    4414              : 
    4415            0 :             void draw(float sx, float sy) override
    4416              :             {
    4417            0 :                 Slot &slot = lookupslot(index, false);
    4418            0 :                 previewslot(slot, *slot.variants, sx, sy);
    4419              : 
    4420            0 :                 Object::draw(sx, sy);
    4421            0 :             }
    4422              : 
    4423              :         protected:
    4424              :             int index;
    4425              : 
    4426            0 :             void previewslot(Slot &slot, VSlot &vslot, float sx, float sy)
    4427              :             {
    4428            0 :                 if(slot.sts.empty())
    4429              :                 {
    4430            0 :                     return;
    4431              :                 }
    4432            0 :                 Texture *t = nullptr,
    4433            0 :                         *glowtex = nullptr;
    4434            0 :                 if(slot.loaded)
    4435              :                 {
    4436            0 :                     t = slot.sts[0].t;
    4437            0 :                     if(t == notexture)
    4438              :                     {
    4439            0 :                         return;
    4440              :                     }
    4441            0 :                     Slot &subslot = *vslot.slot;
    4442            0 :                     if(subslot.texmask&(1 << Tex_Glow))
    4443              :                     {
    4444            0 :                         for(size_t i = 0; i < subslot.sts.size(); i++)
    4445              :                         {
    4446            0 :                             if(subslot.sts[i].type == Tex_Glow)
    4447              :                             {
    4448            0 :                                 glowtex = subslot.sts[i].t;
    4449            0 :                                 break;
    4450              :                             }
    4451              :                         }
    4452              :                     }
    4453              :                 }
    4454              :                 else
    4455              :                 {
    4456            0 :                     if(!slot.thumbnail)
    4457              :                     {
    4458            0 :                         if(totalmillis - lastthumbnail < uislotviewtime)
    4459              :                         {
    4460            0 :                             return;
    4461              :                         }
    4462            0 :                         slot.loadthumbnail();
    4463            0 :                         lastthumbnail = totalmillis;
    4464              :                     }
    4465            0 :                     if(slot.thumbnail != notexture)
    4466              :                     {
    4467            0 :                         t = slot.thumbnail;
    4468              :                     }
    4469              :                     else
    4470              :                     {
    4471            0 :                         return;
    4472              :                     }
    4473              :                 }
    4474              : 
    4475            0 :                 changedraw(Change_Shader | Change_Color);
    4476              : 
    4477            0 :                 SETSHADER(hudrgb);
    4478            0 :                 std::array<vec2,4> tc = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) };
    4479            0 :                 int xoff = vslot.offset.x(),
    4480            0 :                     yoff = vslot.offset.y();
    4481            0 :                 if(vslot.rotation)
    4482              :                 {
    4483            0 :                     const TexRotation &r = texrotations[vslot.rotation];
    4484            0 :                     if(r.swapxy)
    4485              :                     {
    4486            0 :                         std::swap(xoff, yoff);
    4487            0 :                         for(int k = 0; k < 4; ++k)
    4488              :                         {
    4489            0 :                             std::swap(tc[k].x, tc[k].y);
    4490              :                         }
    4491              :                     }
    4492            0 :                     if(r.flipx)
    4493              :                     {
    4494            0 :                         xoff *= -1;
    4495            0 :                         for(int k = 0; k < 4; ++k)
    4496              :                         {
    4497            0 :                             tc[k].x *= -1;
    4498              :                         }
    4499              :                     }
    4500            0 :                     if(r.flipy)
    4501              :                     {
    4502            0 :                         yoff *= -1;
    4503            0 :                         for(int k = 0; k < 4; ++k)
    4504              :                         {
    4505            0 :                             tc[k].y *= -1;
    4506              :                         }
    4507              :                     }
    4508              :                 }
    4509            0 :                 float xt = std::min(1.0f, t->xs/static_cast<float>(t->ys)),
    4510            0 :                       yt = std::min(1.0f, t->ys/static_cast<float>(t->xs));
    4511            0 :                 for(int k = 0; k < 4; ++k)
    4512              :                 {
    4513            0 :                     tc[k].x = tc[k].x/xt - static_cast<float>(xoff)/t->xs;
    4514            0 :                     tc[k].y = tc[k].y/yt - static_cast<float>(yoff)/t->ys;
    4515              :                 }
    4516            0 :                 glBindTexture(GL_TEXTURE_2D, t->id);
    4517            0 :                 if(slot.loaded)
    4518              :                 {
    4519            0 :                     gle::color(vslot.colorscale);
    4520              :                 }
    4521              :                 else
    4522              :                 {
    4523            0 :                     gle::colorf(1, 1, 1);
    4524              :                 }
    4525            0 :                 quad(sx, sy, w, h, tc);
    4526            0 :                 if(glowtex)
    4527              :                 {
    4528            0 :                     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    4529            0 :                     glBindTexture(GL_TEXTURE_2D, glowtex->id);
    4530            0 :                     gle::color(vslot.glowcolor);
    4531            0 :                     quad(sx, sy, w, h, tc);
    4532            0 :                     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    4533              :                 }
    4534              :             }
    4535              :     };
    4536              : 
    4537              :     class VSlotViewer final : public SlotViewer
    4538              :     {
    4539              :         public:
    4540            0 :             static const char *typestr()
    4541              :             {
    4542            0 :                 return "#VSlotViewer";
    4543              :             }
    4544              : 
    4545            0 :             const char *gettype() const final
    4546              :             {
    4547            0 :                 return typestr();
    4548              :             }
    4549              : 
    4550            0 :             void draw(float sx, float sy) final
    4551              :             {
    4552            0 :                 VSlot &vslot = lookupvslot(index, false);
    4553            0 :                 previewslot(*vslot.slot, vslot, sx, sy);
    4554              : 
    4555            0 :                 Object::draw(sx, sy);
    4556            0 :             }
    4557              :     };
    4558              : 
    4559              :     //new ui command
    4560            1 :     static void newui(const char *name, const char *contents, const char *onshow, const char *onhide)
    4561              :     {
    4562            1 :         std::unordered_map<std::string, Window *>::const_iterator itr = windows.find(name);
    4563            1 :         if(itr != windows.end())
    4564              :         {
    4565            0 :             if((*itr).second == UI::window)
    4566              :             {
    4567            0 :                 return;
    4568              :             }
    4569            0 :             world->hide((*itr).second);
    4570            0 :             windows.erase(itr);
    4571            0 :             delete window;
    4572              :         }
    4573            3 :         windows[name] = new Window(name, contents, onshow, onhide);
    4574              :     }
    4575              : 
    4576              :     //command
    4577            1 :     static void uiallowinput(const int *val)
    4578              :     {
    4579            1 :         if(window)
    4580              :         {
    4581            0 :             if(*val >= 0)
    4582              :             {
    4583            0 :                 window->allowinput = *val!=0;
    4584              :             }
    4585            0 :             intret(window->allowinput ? 1 : 0);
    4586              :         }
    4587            1 :     }
    4588              : 
    4589              :     //command
    4590            1 :     void uieschide (const int *val)
    4591              :     {
    4592            1 :         if(window)
    4593              :         {
    4594            0 :             if(*val >= 0)
    4595              :             {
    4596            0 :                 window->eschide = *val!=0;
    4597            0 :                 intret(window->eschide ? 1 : 0);
    4598              :             }
    4599              :         }
    4600            1 :     }
    4601              : 
    4602              :     //used in iengine
    4603            3 :     bool showui(const char *name)
    4604              :     {
    4605            3 :         if(!world)
    4606              :         {
    4607            3 :             return false;
    4608              :         }
    4609            0 :         std::unordered_map<std::string, Window *>::const_iterator itr = windows.find(name);
    4610            0 :         return (itr != windows.end()) && world->show((*itr).second);
    4611              :     }
    4612              : 
    4613              :     //used in iengine
    4614            2 :     bool hideui(const char *name)
    4615              :     {
    4616            2 :         if(!world)
    4617              :         {
    4618            2 :             return false;
    4619              :         }
    4620            0 :         if(!name)
    4621              :         {
    4622            0 :             return world->hideall() > 0;
    4623              :         }
    4624            0 :         std::unordered_map<std::string, Window *>::const_iterator itr = windows.find(name);
    4625            0 :         return (itr != windows.end()) && world->hide((*itr).second);
    4626              :     }
    4627              : 
    4628              :     //used in console
    4629            2 :     bool toggleui(const char *name)
    4630              :     {
    4631            2 :         if(showui(name))
    4632              :         {
    4633            0 :             return true;
    4634              :         }
    4635            2 :         hideui(name);
    4636            2 :         return false;
    4637              :     }
    4638              : 
    4639              :     //used in iengine
    4640            1 :     void holdui(const char *name, bool on)
    4641              :     {
    4642            1 :         if(!world)
    4643              :         {
    4644            1 :             return;
    4645              :         }
    4646            0 :         if(on)
    4647              :         {
    4648            0 :             showui(name);
    4649              :         }
    4650              :         else
    4651              :         {
    4652            0 :             hideui(name);
    4653              :         }
    4654              :     }
    4655              : 
    4656              :     //used in iengine
    4657            3 :     bool uivisible(const char *name)
    4658              :     {
    4659            3 :         if(!name)
    4660              :         {
    4661            0 :             return world->children.size() > 0;
    4662              :         }
    4663            3 :         std::unordered_map<std::string, Window *>::const_iterator itr = windows.find(name);
    4664            3 :         if(itr != windows.end() && (*itr).second)
    4665              :         {
    4666            0 :             return std::find(world->children.begin(), world->children.end(), (*itr).second) != world->children.end();
    4667              :         }
    4668            3 :         return false;
    4669              :     }
    4670              : 
    4671           26 :     static void ifstateval(bool state, tagval * t, tagval * f)
    4672              :     {
    4673           26 :         if(state)
    4674              :         {
    4675            0 :             if(t->type == Value_Null)
    4676              :             {
    4677            0 :                 intret(1);
    4678              :             }
    4679              :             else
    4680              :             {
    4681            0 :                 result(*t);
    4682              :             }
    4683              :         }
    4684           26 :         else if(f->type == Value_Null)
    4685              :         {
    4686           26 :             intret(0);
    4687              :         }
    4688              :         else
    4689              :         {
    4690            0 :             result(*f);
    4691              :         }
    4692           26 :     }
    4693              : 
    4694            0 :     static float parsepixeloffset(const tagval *t, int size)
    4695              :     {
    4696            0 :         switch(t->type)
    4697              :         {
    4698            0 :             case Value_Integer:
    4699              :             {
    4700            0 :                 return t->i;
    4701              :             }
    4702            0 :             case Value_Float:
    4703              :             {
    4704            0 :                 return t->f;
    4705              :             }
    4706            0 :             case Value_Null:
    4707              :             {
    4708            0 :                 return 0;
    4709              :             }
    4710            0 :             default:
    4711              :             {
    4712            0 :                 const char *s = t->getstr();
    4713              :                 char *end;
    4714            0 :                 float val = std::strtod(s, &end);
    4715            0 :                 return *end == 'p' ? val/size : val;
    4716              :             }
    4717              :         }
    4718              :     }
    4719              : 
    4720            8 :     static void buildtext(tagval &t, float scale, float scalemod, const Color &color, float wrap, const uint *children)
    4721              :     {
    4722            8 :         if(scale <= 0)
    4723              :         {
    4724            8 :             scale = 1;
    4725              :         }
    4726            8 :         scale *= scalemod;
    4727            8 :         switch(t.type)
    4728              :         {
    4729            0 :             case Value_Integer:
    4730              :             {
    4731            0 :                 BUILD(TextInt, o, o->setup(t.i, scale, color, wrap), children);
    4732            0 :                 break;
    4733              :             }
    4734            0 :             case Value_Float:
    4735              :             {
    4736            0 :                 BUILD(TextFloat, o, o->setup(t.f, scale, color, wrap), children);
    4737            0 :                 break;
    4738              :             }
    4739            0 :             case Value_CString:
    4740              :             case Value_Macro:
    4741              :             case Value_String:
    4742              :             {
    4743            0 :                 if(t.s[0])
    4744              :                 {
    4745            0 :                     BUILD(TextString, o, o->setup(t.s, scale, color, wrap), children);
    4746            0 :                     break;
    4747              :                 }
    4748              :                 [[fallthrough]];
    4749              :             }
    4750              :             default:
    4751              :             {
    4752            8 :                 BUILD(Object, o, o->setup(), children);
    4753            8 :                 break;
    4754              :             }
    4755              :         }
    4756            8 :     }
    4757              : 
    4758            1 :     void inituicmds()
    4759              :     {
    4760              : 
    4761            1 :         static auto showuicmd = [] (const char * name)
    4762              :         {
    4763            1 :             if(!world)
    4764              :             {
    4765            1 :                 intret(-1);
    4766            1 :                 return;
    4767              :             }
    4768            0 :             intret(showui(name) ? 1 : 0);
    4769              :         };
    4770              : 
    4771            1 :         static auto hideuicmd = [] (const char * name)
    4772              :         {
    4773            1 :             if(!world)
    4774              :             {
    4775            1 :                 intret(-1);
    4776            1 :                 return;
    4777              :             }
    4778            0 :             intret(hideui(name) ? 1 : 0);
    4779              :         };
    4780              : 
    4781            1 :         static auto hidetopuicmd = [] ()
    4782              :         {
    4783            1 :             if(!world)
    4784              :             {
    4785            1 :                 intret(-1);
    4786            1 :                 return;
    4787              :             }
    4788            0 :             intret(world->hidetop() ? 1 : 0);
    4789              :         };
    4790              : 
    4791            1 :         static auto hidealluicmd = [] ()
    4792              :         {
    4793            1 :             if(!world)
    4794              :             {
    4795            1 :                 intret(-1);
    4796            1 :                 return;
    4797              :             }
    4798            0 :             intret(world->hideall());
    4799              :         };
    4800              : 
    4801            1 :         static auto toggleuicmd = [] (const char * name)
    4802              :         {
    4803              : 
    4804            1 :             intret(toggleui(name) ? 1 : 0);
    4805            1 :         };
    4806              : 
    4807            1 :         static auto holduicmd = [] (const char * name, const int * down)
    4808              :         {
    4809            1 :             holdui(name, *down!=0);
    4810            1 :         };
    4811              : 
    4812            1 :         static auto uivisiblecmd = [] (const char * name)
    4813              :         {
    4814            1 :             intret(uivisible(name) ? 1 : 0);
    4815            1 :         };
    4816              : 
    4817            1 :         addcommand("showui",        reinterpret_cast<identfun>(+showuicmd),   "s",    Id_Command);
    4818            1 :         addcommand("hideui",        reinterpret_cast<identfun>(+hideuicmd),   "s",    Id_Command);
    4819            1 :         addcommand("hidetopui",     reinterpret_cast<identfun>(+hidetopuicmd),"",     Id_Command);
    4820            1 :         addcommand("hideallui",     reinterpret_cast<identfun>(+hidealluicmd),"",     Id_Command);
    4821            1 :         addcommand("toggleui",      reinterpret_cast<identfun>(+toggleuicmd), "s",    Id_Command);
    4822            1 :         addcommand("holdui",        reinterpret_cast<identfun>(+holduicmd),   "sD",   Id_Command);
    4823            1 :         addcommand("uivisible",     reinterpret_cast<identfun>(+uivisiblecmd),"s",    Id_Command);
    4824              : 
    4825            1 :         static auto uinamecmd = [] ()
    4826              :         {
    4827            1 :             if(window)
    4828              :             {
    4829            0 :                 result(window->name.c_str());
    4830              :             }
    4831            1 :         };
    4832            1 :         addcommand("uiname",        reinterpret_cast<identfun>(+uinamecmd),   "",     Id_Command);
    4833              : 
    4834              :         #define DOSTATE(flags, func) \
    4835              :             addcommand("ui" #func, reinterpret_cast<identfun>(+[] (const uint *t, const uint *f) \
    4836              :             { \
    4837              :                 executeret(buildparent && buildparent->haschildstate(flags) ? t : f);\
    4838              :             }), "ee", Id_Command); \
    4839              :             addcommand("ui" #func "?", reinterpret_cast<identfun>(+[] (tagval *t, tagval *f) \
    4840              :             { \
    4841              :                 ifstateval(buildparent && buildparent->haschildstate(flags), t, f);\
    4842              :             }), "tt", Id_Command); \
    4843              :             addcommand("ui" #func "+", reinterpret_cast<identfun>(+[] (const uint *t, const uint *f) \
    4844              :             { \
    4845              :                 executeret(buildparent && static_cast<int>(buildparent->children.size()) > buildchild && buildparent->children[buildchild]->haschildstate(flags) ? t : f); \
    4846              :             }), "ee", Id_Command); \
    4847              :             addcommand("ui" #func "+?", reinterpret_cast<identfun>(+[] (tagval *t, tagval *f) \
    4848              :             { \
    4849              :                 ifstateval(buildparent && static_cast<int>(buildparent->children.size()) > buildchild && buildparent->children[buildchild]->haschildstate(flags), t, f); \
    4850              :             }), "tt", Id_Command);
    4851           49 :         DOSTATES
    4852              :         #undef DOSTATE
    4853              : 
    4854            1 :         addcommand("uifocus", reinterpret_cast<identfun>(+[] (const uint *t, const uint *f)
    4855              :         {
    4856            1 :             executeret(buildparent && TextEditor::focus == buildparent ? t : f);
    4857            1 :         }), "ee", Id_Command);
    4858            1 :         addcommand("uifocus?", reinterpret_cast<identfun>(+[] (tagval *t, tagval *f)
    4859              :         {
    4860            1 :             ifstateval(buildparent && TextEditor::focus == buildparent, t, f);
    4861            1 :         }), "tt", Id_Command);
    4862            1 :         addcommand("uifocus+", reinterpret_cast<identfun>(+[] (const uint *t, const uint *f)
    4863              :         {
    4864            1 :             executeret(buildparent && static_cast<int>(buildparent->children.size()) > buildchild && TextEditor::focus == buildparent->children[buildchild] ? t : f);
    4865            1 :         }), "ee", Id_Command);
    4866            1 :         addcommand("uifocus+?", reinterpret_cast<identfun>(+[] (tagval *t, tagval *f)
    4867              :         {
    4868            1 :             ifstateval(buildparent && static_cast<int>(buildparent->children.size()) > buildchild && TextEditor::focus == buildparent->children[buildchild], t, f);
    4869            1 :         }), "tt", Id_Command);
    4870            1 :         addcommand("uialign", reinterpret_cast<identfun>(+[] (const int *xalign, const int *yalign)
    4871              :         {
    4872            1 :             if(buildparent)
    4873              :             {
    4874            0 :                 buildparent->setalign(*xalign, *yalign);
    4875              :             }
    4876            1 :         }), "ii", Id_Command);
    4877            1 :         addcommand("uialign-", reinterpret_cast<identfun>(+[] (const int *xalign, const int *yalign)
    4878              :         {
    4879            1 :             if(buildparent && buildchild > 0)
    4880              :             {
    4881            0 :                 buildparent->children[buildchild-1]->setalign(*xalign, *yalign);
    4882              :             }
    4883            1 :         }), "ii", Id_Command);
    4884            1 :         addcommand("uialign*", reinterpret_cast<identfun>(+[] (const int *xalign, const int *yalign)
    4885              :         {
    4886            1 :             if(buildparent)
    4887              :             {
    4888            0 :                 for(int i = 0; i < buildchild; ++i)
    4889              :                 {
    4890            0 :                     buildparent->children[i]->setalign(*xalign, *yalign);
    4891              :                 }
    4892              :             }
    4893            1 :         }), "ii", Id_Command);
    4894            1 :         addcommand("uiclamp", reinterpret_cast<identfun>(+[] (const int *left, const int *right, const int *top, const int *bottom)
    4895              :         {
    4896            1 :             if(buildparent)
    4897              :             {
    4898            0 :                 buildparent->setclamp(*left, *right, *top, *bottom);
    4899              :             }
    4900            1 :         }), "iiii", Id_Command);
    4901            1 :         addcommand("uiclamp-", reinterpret_cast<identfun>(+[] (const int *left, const int *right, const int *top, const int *bottom)
    4902              :         {
    4903            1 :             if(buildparent && buildchild > 0)
    4904              :             {
    4905            0 :                 buildparent->children[buildchild-1]->setclamp(*left, *right, *top, *bottom);
    4906              :             }
    4907            1 :         }), "iiii", Id_Command);
    4908            1 :         addcommand("uiclamp*", reinterpret_cast<identfun>(+[] (const int *left, const int *right, const int *top, const int *bottom)
    4909              :         {
    4910            1 :             if(buildparent)
    4911              :             {
    4912            0 :                 for(int i = 0; i < buildchild; ++i)
    4913              :                 {
    4914            0 :                     buildparent->children[i]->setclamp(*left, *right, *top, *bottom);
    4915              :                 }
    4916              :             }
    4917            1 :         }), "iiii", Id_Command);
    4918            1 :         addcommand("uigroup", reinterpret_cast<identfun>(+[] (const uint *children)
    4919              :         {
    4920            1 :             BUILD(Object, o, o->setup(), children);
    4921            1 :         }), "e", Id_Command);
    4922            1 :         addcommand("uihlist", reinterpret_cast<identfun>(+[] (const float *space, const uint *children)
    4923              :         {
    4924            1 :             BUILD(HorizontalList, o, o->setup(*space), children);
    4925            1 :         }), "fe", Id_Command);
    4926            1 :         addcommand("uivlist", reinterpret_cast<identfun>(+[] (const float *space, const uint *children)
    4927              :         {
    4928            1 :             BUILD(VerticalList, o, o->setup(*space), children);
    4929            1 :         }), "fe", Id_Command);
    4930            1 :         addcommand("uilist", reinterpret_cast<identfun>(+[] (const float *space, const uint *children)
    4931              :         {
    4932            1 :             for(Object *parent = buildparent; parent && !parent->istype<VerticalList>(); parent = parent->parent)
    4933              :             {
    4934            0 :                 if(parent->istype<HorizontalList>())
    4935              :                 {
    4936            0 :                     BUILD(VerticalList, o, o->setup(*space), children); return;
    4937              :                 }
    4938              :             }
    4939            1 :             BUILD(HorizontalList, o, o->setup(*space), children);
    4940            1 :         }), "fe", Id_Command);
    4941            1 :         addcommand("uigrid", reinterpret_cast<identfun>(+[] (const int *columns, const float *spacew, const float *spaceh, const uint *children)
    4942              :         {
    4943            1 :             BUILD(Grid, o, o->setup(*columns, *spacew, *spaceh), children);
    4944            1 :         }), "iffe", Id_Command);
    4945              : 
    4946              :         #define BUILDCOLUMNS(type, o, setup, columndata, contents) do { \
    4947              :             if(buildparent) \
    4948              :             { \
    4949              :                 type *o = buildparent->buildtype<type>(); \
    4950              :                 setup; \
    4951              :                 o->buildchildren(columndata, contents); \
    4952              :             } \
    4953              :         } while(0)
    4954              : 
    4955            1 :         addcommand("uitableheader", reinterpret_cast<identfun>(+[] (const uint *columndata, const uint *children)
    4956              :         {
    4957            1 :             BUILDCOLUMNS(TableHeader, o, o->setup(), columndata, children);
    4958            1 :         }), "ee", Id_Command);
    4959            1 :         addcommand("uitablerow", reinterpret_cast<identfun>(+[] (const uint *columndata, const uint *children)
    4960              :         {
    4961            1 :             BUILDCOLUMNS(TableRow, o, o->setup(), columndata, children);
    4962            1 :         }), "ee", Id_Command);
    4963              : 
    4964              :         #undef BUILDCOLUMNS
    4965              : 
    4966            1 :         addcommand("uitable", reinterpret_cast<identfun>(+[] (const float *spacew, const float *spaceh, const uint *children)
    4967              :         {
    4968            1 :             BUILD(Table, o, o->setup(*spacew, *spaceh), children);
    4969            1 :         }), "ffe", Id_Command);
    4970            1 :         addcommand("uispace", reinterpret_cast<identfun>(+[] (const float *spacew, const float *spaceh, const uint *children)
    4971              :         {
    4972            1 :             BUILD(Spacer, o, o->setup(*spacew, *spaceh), children);
    4973            1 :         }), "ffe", Id_Command);
    4974            1 :         addcommand("uioffset", reinterpret_cast<identfun>(+[] (const float *offsetx, const float *offsety, const uint *children)
    4975              :         {
    4976            1 :             BUILD(Offsetter, o, o->setup(*offsetx, *offsety), children);
    4977            1 :         }), "ffe", Id_Command);
    4978            1 :         addcommand("uifill", reinterpret_cast<identfun>(+[] (const float *minw, const float *minh, const uint *children)
    4979              :         {
    4980            1 :             BUILD(Filler, o, o->setup(*minw, *minh), children);
    4981            1 :         }), "ffe", Id_Command);
    4982            1 :         addcommand("uitarget", reinterpret_cast<identfun>(+[] (const float *minw, const float *minh, const uint *children)
    4983              :         {
    4984            1 :             BUILD(Target, o, o->setup(*minw, *minh), children);
    4985            1 :         }), "ffe", Id_Command);
    4986            1 :         addcommand("uiclip", reinterpret_cast<identfun>(+[] (const float *clipw, const float *cliph, const uint *children)
    4987              :         {
    4988            1 :             BUILD(Clipper, o, o->setup(*clipw, *cliph), children);
    4989            1 :         }), "ffe", Id_Command);
    4990            2 :         addcommand("uiscroll", reinterpret_cast<identfun>(+[] (const float *clipw, const float *cliph, const uint *children) { BUILD(Scroller, o, o->setup(*clipw, *cliph), children); }), "ffe", Id_Command);
    4991            2 :         addcommand("uihscrolloffset", reinterpret_cast<identfun>(+[] () { if(buildparent && buildparent->istype<Scroller>()) { const Scroller *scroller = static_cast<const Scroller *>(buildparent); floatret(scroller->offsetx);} }), "", Id_Command);
    4992            2 :         addcommand("uivscrolloffset", reinterpret_cast<identfun>(+[] () { if(buildparent && buildparent->istype<Scroller>()) { const Scroller *scroller = static_cast<const Scroller *>(buildparent); floatret(scroller->offsety);} }), "", Id_Command);
    4993            2 :         addcommand("uihscrollbar", reinterpret_cast<identfun>(+[] (const uint *children) { BUILD(HorizontalScrollBar, o, o->setup(), children); }), "e", Id_Command);
    4994            2 :         addcommand("uivscrollbar", reinterpret_cast<identfun>(+[] (const uint *children) { BUILD(VerticalScrollBar, o, o->setup(), children); }), "e", Id_Command);
    4995            2 :         addcommand("uiscrollarrow", reinterpret_cast<identfun>(+[] (const float *dir, const uint *children) { BUILD(ScrollArrow, o, o->setup(*dir), children); }), "fe", Id_Command);
    4996            2 :         addcommand("uiscrollbutton", reinterpret_cast<identfun>(+[] (const uint *children) { BUILD(ScrollButton, o, o->setup(), children); }), "e", Id_Command);
    4997            2 :         addcommand("uihslider", reinterpret_cast<identfun>(+[] (ident *var, float *vmin, float *vmax, float *vstep, uint *onchange, uint *children) { BUILD(HorizontalSlider, o, o->setup(var, *vmin, *vmax, *vstep, onchange), children); }), "rfffee", Id_Command);
    4998            2 :         addcommand("uivslider", reinterpret_cast<identfun>(+[] (ident *var, float *vmin, float *vmax, float *vstep, uint *onchange, uint *children) { BUILD(VerticalSlider, o, o->setup(var, *vmin, *vmax, *vstep, onchange), children); }), "rfffee", Id_Command);
    4999            2 :         addcommand("uisliderarrow", reinterpret_cast<identfun>(+[] (float *dir, uint *children) { BUILD(SliderArrow, o, o->setup(*dir), children); }), "fe", Id_Command);
    5000            2 :         addcommand("uisliderbutton", reinterpret_cast<identfun>(+[] (uint *children) { BUILD(SliderButton, o, o->setup(), children); }), "e", Id_Command);
    5001            2 :         addcommand("uicolor", reinterpret_cast<identfun>(+[] (int *c, float *minw, float *minh, uint *children) { BUILD(FillColor, o, o->setup(FillColor::SOLID, Color(*c), *minw, *minh), children); }), "iffe", Id_Command);
    5002            2 :         addcommand("uimodcolor", reinterpret_cast<identfun>(+[] (int *c, float *minw, float *minh, uint *children) { BUILD(FillColor, o, o->setup(FillColor::MODULATE, Color(*c), *minw, *minh), children); }), "iffe", Id_Command);
    5003            2 :         addcommand("uivgradient", reinterpret_cast<identfun>(+[] (int *c, int *c2, float *minw, float *minh, uint *children) { BUILD(Gradient, o, o->setup(Gradient::SOLID, Gradient::VERTICAL, Color(*c), Color(*c2), *minw, *minh), children); }), "iiffe", Id_Command);
    5004            2 :         addcommand("uimodvgradient", reinterpret_cast<identfun>(+[] (int *c, int *c2, float *minw, float *minh, uint *children) { BUILD(Gradient, o, o->setup(Gradient::MODULATE, Gradient::VERTICAL, Color(*c), Color(*c2), *minw, *minh), children); }), "iiffe", Id_Command);
    5005            2 :         addcommand("uihgradient", reinterpret_cast<identfun>(+[] (int *c, int *c2, float *minw, float *minh, uint *children) { BUILD(Gradient, o, o->setup(Gradient::SOLID, Gradient::HORIZONTAL, Color(*c), Color(*c2), *minw, *minh), children); }), "iiffe", Id_Command);
    5006            2 :         addcommand("uimodhgradient", reinterpret_cast<identfun>(+[] (int *c, int *c2, float *minw, float *minh, uint *children) { BUILD(Gradient, o, o->setup(Gradient::MODULATE, Gradient::HORIZONTAL, Color(*c), Color(*c2), *minw, *minh), children); }), "iiffe", Id_Command);
    5007            2 :         addcommand("uioutline", reinterpret_cast<identfun>(+[] (int *c, float *minw, float *minh, uint *children) { BUILD(Outline, o, o->setup(Color(*c), *minw, *minh), children); }), "iffe", Id_Command);
    5008            2 :         addcommand("uiline", reinterpret_cast<identfun>(+[] (int *c, float *minw, float *minh, uint *children) { BUILD(Line, o, o->setup(Color(*c), *minw, *minh), children); }), "iffe", Id_Command);
    5009            2 :         addcommand("uitriangle", reinterpret_cast<identfun>(+[] (int *c, float *minw, float *minh, int *angle, uint *children) { BUILD(Triangle, o, o->setup(Color(*c), *minw, *minh, *angle, Triangle::SOLID), children); }), "iffie", Id_Command);
    5010            2 :         addcommand("uitriangleoutline", reinterpret_cast<identfun>(+[] (int *c, float *minw, float *minh, int *angle, uint *children) { BUILD(Triangle, o, o->setup(Color(*c), *minw, *minh, *angle, Triangle::OUTLINE), children); }), "iffie", Id_Command);
    5011            2 :         addcommand("uimodtriangle", reinterpret_cast<identfun>(+[] (int *c, float *minw, float *minh, int *angle, uint *children) { BUILD(Triangle, o, o->setup(Color(*c), *minw, *minh, *angle, Triangle::MODULATE), children); }), "iffie", Id_Command);
    5012            2 :         addcommand("uicircle", reinterpret_cast<identfun>(+[] (int *c, float *size, uint *children) { BUILD(Circle, o, o->setup(Color(*c), *size, Circle::SOLID), children); }), "ife", Id_Command);
    5013            2 :         addcommand("uicircleoutline", reinterpret_cast<identfun>(+[] (int *c, float *size, uint *children) { BUILD(Circle, o, o->setup(Color(*c), *size, Circle::OUTLINE), children); }), "ife", Id_Command);
    5014            2 :         addcommand("uimodcircle", reinterpret_cast<identfun>(+[] (int *c, float *size, uint *children) { BUILD(Circle, o, o->setup(Color(*c), *size, Circle::MODULATE), children); }), "ife", Id_Command);
    5015            2 :         addcommand("uicolortext", reinterpret_cast<identfun>(+[] (tagval *text, int *c, float *scale, uint *children) { buildtext(*text, *scale, uitextscale, Color(*c), -1, children); }), "tife", Id_Command);
    5016            1 :         addcommand("uitext", reinterpret_cast<identfun>(+[] (tagval *text, const float *scale, const uint *children)
    5017              :         {
    5018            1 :             buildtext(*text, *scale, uitextscale, Color(255, 255, 255), -1, children);
    5019            2 :         }), "tfe", Id_Command);
    5020            2 :         addcommand("uitextfill", reinterpret_cast<identfun>(+[] (float *minw, float *minh, uint *children) { BUILD(Filler, o, o->setup(*minw * uitextscale*0.5f, *minh * uitextscale), children); }), "ffe", Id_Command);
    5021            2 :         addcommand("uiwrapcolortext", reinterpret_cast<identfun>(+[] (tagval *text, float *wrap, int *c, float *scale, uint *children) { buildtext(*text, *scale, uitextscale, Color(*c), *wrap, children); }), "tfife", Id_Command);
    5022            2 :         addcommand("uiwraptext", reinterpret_cast<identfun>(+[] (tagval *text, float *wrap, float *scale, uint *children) { buildtext(*text, *scale, uitextscale, Color(255, 255, 255), *wrap, children); }), "tffe", Id_Command);
    5023            2 :         addcommand("uicolorcontext", reinterpret_cast<identfun>(+[] (tagval *text, int *c, float *scale, uint *children) { buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), -1, children); }), "tife", Id_Command);
    5024            2 :         addcommand("uicontext", reinterpret_cast<identfun>(+[] (tagval *text, float *scale, uint *children) { buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), -1, children); }), "tfe", Id_Command);
    5025            2 :         addcommand("uicontextfill", reinterpret_cast<identfun>(+[] (float *minw, float *minh, uint *children) { BUILD(Filler, o, o->setup(*minw * FONTH*uicontextscale*0.5f, *minh * FONTH*uicontextscale), children); }), "ffe", Id_Command);
    5026            2 :         addcommand("uiwrapcolorcontext", reinterpret_cast<identfun>(+[] (tagval *text, float *wrap, int *c, float *scale, uint *children) { buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), *wrap, children); }), "tfife", Id_Command);
    5027            2 :         addcommand("uiwrapcontext", reinterpret_cast<identfun>(+[] (tagval *text, float *wrap, float *scale, uint *children) { buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), *wrap, children); }), "tffe", Id_Command);
    5028            2 :         addcommand("uitexteditor", reinterpret_cast<identfun>(+[] (char *name, int *length, int *height, float *scale, char *initval, int *mode, uint *children) { BUILD(TextEditor, o, o->setup(name, *length, *height, (*scale <= 0 ? 1 : *scale) * uitextscale, initval, *mode <= 0 ? Editor_Forever : *mode), children); }), "siifsie", Id_Command);
    5029            2 :         addcommand("uifont", reinterpret_cast<identfun>(+[] (uint *children) { BUILD(Font, o, o->setup(), children); }), "e", Id_Command);
    5030            2 :         addcommand("uiabovehud", reinterpret_cast<identfun>(+[] () { if(window) window->abovehud = true; }), "", Id_Command);
    5031            2 :         addcommand("uiconsole", reinterpret_cast<identfun>(+[] (float *minw, float *minh, uint *children) { BUILD(Console, o, o->setup(*minw, *minh), children); }), "ffe", Id_Command);
    5032            2 :         addcommand("uifield", reinterpret_cast<identfun>(+[] (ident *var, int *length, uint *onchange, float *scale, uint *children) { BUILD(Field, o, o->setup(var, *length, onchange, (*scale <= 0 ? 1 : *scale) * uitextscale), children); }), "riefe", Id_Command);
    5033            2 :         addcommand("uikeyfield", reinterpret_cast<identfun>(+[] (ident *var, int *length, uint *onchange, float *scale, uint *children) { BUILD(KeyField, o, o->setup(var, *length, onchange, (*scale <= 0 ? 1 : *scale) * uitextscale), children); }), "riefe", Id_Command);
    5034            2 :         addcommand("uiimage", reinterpret_cast<identfun>(+[] (char *texname, float *minw, float *minh, uint *children) { BUILD(Image, o, o->setup(textureload(texname, 3, true, false), *minw, *minh), children); }), "sffe", Id_Command);
    5035            2 :         addcommand("uistretchedimage", reinterpret_cast<identfun>(+[] (char *texname, float *minw, float *minh, uint *children) { BUILD(StretchedImage, o, o->setup(textureload(texname, 3, true, false), *minw, *minh), children); }), "sffe", Id_Command);
    5036            2 :         addcommand("uicroppedimage", reinterpret_cast<identfun>(+[] (char *texname, float *minw, float *minh, tagval *cropx, tagval *cropy, tagval *cropw, tagval *croph, uint *children) { BUILD(CroppedImage, o, { Texture *tex = textureload(texname, 3, true, false); o->setup(tex, *minw, *minh, parsepixeloffset(cropx, tex->xs), parsepixeloffset(cropy, tex->ys), parsepixeloffset(cropw, tex->xs), parsepixeloffset(croph, tex->ys)); }, children); }), "sfftttte", Id_Command);
    5037            2 :         addcommand("uiborderedimage", reinterpret_cast<identfun>(+[] (char *texname, tagval *texborder, float *screenborder, uint *children) { BUILD(BorderedImage, o, { Texture *tex = textureload(texname, 3, true, false); o->setup(tex, parsepixeloffset(texborder, tex->xs), *screenborder); }, children); }), "stfe", Id_Command);
    5038            2 :         addcommand("uitiledimage", reinterpret_cast<identfun>(+[] (char *texname, float *tilew, float *tileh, float *minw, float *minh, uint *children) { BUILD(TiledImage, o, { Texture *tex = textureload(texname, 0, true, false); o->setup(tex, *minw, *minh, *tilew <= 0 ? 1 : *tilew, *tileh <= 0 ? 1 : *tileh); }, children); }), "sffffe", Id_Command);
    5039            2 :         addcommand("uimodelpreview", reinterpret_cast<identfun>(+[] (char *model, char *animspec, float *minw, float *minh, uint *children) { BUILD(ModelPreview, o, o->setup(model, animspec, *minw, *minh), children); }), "ssffe", Id_Command);
    5040            2 :         addcommand("uiprefabpreview", reinterpret_cast<identfun>(+[] (char *prefab, int *color, float *minw, float *minh, uint *children) { BUILD(PrefabPreview, o, o->setup(prefab, *color, *minw, *minh), children); }), "siffe", Id_Command);
    5041            2 :         addcommand("uislotview", reinterpret_cast<identfun>(+[] (int *index, float *minw, float *minh, uint *children) { BUILD(SlotViewer, o, o->setup(*index, *minw, *minh), children); }), "iffe", Id_Command);
    5042            2 :         addcommand("uivslotview", reinterpret_cast<identfun>(+[] (int *index, float *minw, float *minh, uint *children) { BUILD(VSlotViewer, o, o->setup(*index, *minw, *minh), children); }), "iffe", Id_Command);
    5043              : 
    5044            1 :         addcommand("uicontextscale", reinterpret_cast<identfun>(uicontextscalecmd), "", Id_Command);
    5045            1 :         addcommand("newui", reinterpret_cast<identfun>(newui), "ssss", Id_Command);
    5046            1 :         addcommand("uiallowinput", reinterpret_cast<identfun>(uiallowinput), "b", Id_Command);
    5047            1 :         addcommand("uieschide", reinterpret_cast<identfun>(uieschide), "b", Id_Command);
    5048            1 :     }
    5049              : 
    5050            0 :     bool hascursor()
    5051              :     {
    5052            0 :         return world->allowinput();
    5053              :     }
    5054              : 
    5055            0 :     void getcursorpos(float &x, float &y)
    5056              :     {
    5057            0 :         if(hascursor())
    5058              :         {
    5059            0 :             x = cursorx;
    5060            0 :             y = cursory;
    5061              :         }
    5062              :         else
    5063              :         {
    5064            0 :             x = y = 0.5f;
    5065              :         }
    5066            0 :     }
    5067              : 
    5068            0 :     void resetcursor()
    5069              :     {
    5070            0 :         cursorx = cursory = 0.5f;
    5071            0 :     }
    5072              : 
    5073              :     static FVARP(uisensitivity, 1e-4f, 1, 1e4f);
    5074              : 
    5075            0 :     bool movecursor(int dx, int dy)
    5076              :     {
    5077            0 :         if(!hascursor())
    5078              :         {
    5079            0 :             return false;
    5080              :         }
    5081            0 :         cursorx = std::clamp(cursorx + dx*uisensitivity/hudw(), 0.0f, 1.0f);
    5082            0 :         cursory = std::clamp(cursory + dy*uisensitivity/hudh(), 0.0f, 1.0f);
    5083            0 :         return true;
    5084              :     }
    5085              : 
    5086            0 :     bool keypress(int code, bool isdown)
    5087              :     {
    5088            0 :         if(world->rawkey(code, isdown))
    5089              :         {
    5090            0 :             return true;
    5091              :         }
    5092            0 :         int action = 0,
    5093            0 :             hold = 0;
    5094            0 :         switch(code)
    5095              :         {
    5096            0 :             case Key_Left:
    5097              :             {
    5098            0 :                 action = isdown ? State_Press : State_Release; hold = State_Hold;
    5099            0 :                 break;
    5100              :             }
    5101            0 :             case Key_Middle:
    5102              :             {
    5103            0 :                 action = isdown ? State_AltPress : State_AltRelease; hold = State_AltHold;
    5104            0 :                 break;
    5105              :             }
    5106            0 :             case Key_Right:
    5107              :             {
    5108            0 :                 action = isdown ? State_EscPress : State_EscRelease; hold = State_EscHold;
    5109            0 :                 break;
    5110              :             }
    5111            0 :             case Key_ScrollUp:
    5112              :             {
    5113            0 :                 action = State_ScrollUp;
    5114            0 :                 break;
    5115              :             }
    5116            0 :             case Key_ScrollDown:
    5117              :             {
    5118            0 :                 action = State_ScrollDown;
    5119            0 :                 break;
    5120              :             }
    5121              :         }
    5122            0 :         if(action)
    5123              :         {
    5124            0 :             if(isdown)
    5125              :             {
    5126            0 :                 if(hold)
    5127              :                 {
    5128            0 :                     world->clearstate(hold);
    5129              :                 }
    5130            0 :                 if(world->setstate(action, cursorx, cursory, 0, true, action|hold))
    5131              :                 {
    5132            0 :                     return true;
    5133              :                 }
    5134              :             }
    5135            0 :             else if(hold)
    5136              :             {
    5137            0 :                 if(world->setstate(action, cursorx, cursory, hold, true, action))
    5138              :                 {
    5139            0 :                     world->clearstate(hold);
    5140            0 :                     return true;
    5141              :                 }
    5142            0 :                 world->clearstate(hold);
    5143              :             }
    5144            0 :             return world->allowinput();
    5145              :         }
    5146            0 :         return world->key(code, isdown);
    5147              :     }
    5148              : 
    5149            0 :     bool textinput(const char *str, int len)
    5150              :     {
    5151            0 :         return world->textinput(str, len);
    5152              :     }
    5153              : 
    5154            0 :     void setup()
    5155              :     {
    5156            0 :         world = new World;
    5157            0 :     }
    5158              : 
    5159            0 :     void cleanup()
    5160              :     {
    5161            0 :         world->children.clear();
    5162            0 :         for(auto &[k, i] : windows)
    5163              :         {
    5164            0 :             delete i;
    5165              :         }
    5166            0 :         windows.clear();
    5167            0 :         if(world)
    5168              :         {
    5169            0 :             delete world;
    5170            0 :             world = nullptr;
    5171              :         }
    5172            0 :     }
    5173              : 
    5174            0 :     void calctextscale()
    5175              :     {
    5176            0 :         uitextscale = 1.0f/uitextrows;
    5177              : 
    5178            0 :         int tw = hudw(),
    5179            0 :             th = hudh();
    5180            0 :         if(forceaspect)
    5181              :         {
    5182            0 :             tw = static_cast<int>(std::ceil(th*forceaspect));
    5183              :         }
    5184            0 :         gettextres(tw, th);
    5185            0 :         uicontextscale = conscale/th;
    5186            0 :     }
    5187              : 
    5188            0 :     void update()
    5189              :     {
    5190            0 :         readyeditors();
    5191              : 
    5192            0 :         world->setstate(State_Hover, cursorx, cursory, world->childstate & State_HoldMask);
    5193            0 :         if(world->childstate & State_Hold)
    5194              :         {
    5195            0 :             world->setstate(State_Hold, cursorx, cursory, State_Hold, false);
    5196              :         }
    5197            0 :         if(world->childstate & State_AltHold)
    5198              :         {
    5199            0 :             world->setstate(State_AltHold, cursorx, cursory, State_AltHold, false);
    5200              :         }
    5201            0 :         if(world->childstate & State_EscHold)
    5202              :         {
    5203            0 :             world->setstate(State_EscHold, cursorx, cursory, State_EscHold, false);
    5204              :         }
    5205              : 
    5206            0 :         calctextscale();
    5207            0 :         world->build();
    5208            0 :         flusheditors();
    5209            0 :     }
    5210              : 
    5211            0 :     void render()
    5212              :     {
    5213            0 :         world->layout();
    5214            0 :         world->adjustchildren();
    5215            0 :         world->draw();
    5216            0 :     }
    5217              : 
    5218            0 :     float abovehud()
    5219              :     {
    5220            0 :         return world->abovehud();
    5221              :     }
    5222              : }
        

Generated by: LCOV version 2.0-1