LCOV - code coverage report
Current view: top level - engine/interface - ui.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 180 2405 7.5 %
Date: 2024-11-22 05:07:59 Functions: 145 710 20.4 %

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

Generated by: LCOV version 1.14