LCOV - code coverage report
Current view: top level - engine/interface - ui.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 7.5 % 2407 180
Test Date: 2025-02-18 06:21:28 Functions: 20.4 % 710 145

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

Generated by: LCOV version 2.0-1