CoolFace
Datasetpublic

dlxjj/imradv3

sourceHugging Faceupdated 8mo agoView on Hugging Face
0likes262downloads
node_standard.cpp6502 linesDownload Raw Back to src
1#include "node_standard.h"2#include "node_container.h"3#include "node_extra.h"4#include "stx.h"5#include "cppgen.h"6#include "binding_input.h"7#include "binding_eval.h"8#include "ui_message_box.h"9#include "ui_combo_dlg.h"10#include <misc/cpp/imgui_stdlib.h>11#include <nfd.h>12#include <algorithm>13#include <array>14 15const std::string TMP_LAST_ITEM_VAR = "tmpLastItem";16 17void toggle(std::vector<UINode*>& c, UINode* val)18{19    auto it = stx::find(c, val);20    if (it == c.end())21        c.push_back(val);22    else23        c.erase(it);24}25 26// for compatibility with ImRAD 0.727std::string ParseShortcutOld(const std::string& line)28{29    std::string sh;30    size_t i = -1;31    while (true)32    {33        size_t j1 = line.find("ImGui::IsKeyPressed(ImGuiKey_", i + 1);34        size_t j2 = line.find("ImGuiMod_", i + 1);35        if (j1 == std::string::npos && j2 == std::string::npos)36            break;37        if (j1 < j2) {38            j1 += 29;39            size_t e = line.find_first_of(",)", j1);40            if (e == std::string::npos)41                break;42            sh += "+";43            sh += line.substr(j1, e - j1);44            i = j1;45        }46        else47        {48            j2 += 9;49            sh += "+";50            size_t end = std::find_if(line.begin() + j2, line.end(), [](char c) {51                return !std::isalpha(c);52                }) - line.begin();53                sh += line.substr(j2, end - j2);54                i = j2;55        }56    }57    if (sh.size())58        sh.erase(sh.begin());59    return sh;60}61 62void TreeNodeProp(const char* name, ImFont* font, const std::string& label, std::function<void()> f)63{64    ImVec2 pad = ImGui::GetStyle().FramePadding;65    ImGui::Unindent();66    ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 0.0f, pad.y });67    ImGui::PushStyleColor(ImGuiCol_NavCursor, { 0, 0, 0, 0 });68    ImGui::SetNextItemAllowOverlap();69    if (ImGui::TreeNodeEx(name, ImGuiTreeNodeFlags_SpanAllColumns)) {70        ImGui::PopStyleVar();71        ImGui::Indent();72        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { pad.x, 0 }); //row packing73        //ImGui::TableNextColumn();74        //ImGui::Spacing();75        f();76        ImGui::PopStyleVar();77        ImGui::TreePop();78        ImGui::Unindent();79    }80    else81    {82        ImGui::PopStyleVar();83        ImGui::TableNextColumn();84        ImGui::SetNextItemWidth(-ImGui::GetFrameHeight());85        ImGui::PushFont(font);86        ImGui::Text("%s", label.c_str());87        ImGui::PopFont();88    }89    ImGui::Indent();90    ImGui::PopStyleColor();91}92 93//1. limits length of lengthy {} expression (like in case it contains ?:)94//2. formats {{, }} into {, }95//3. errors out on incorrect format string96PreparedString PrepareString(std::string_view s)97{98    const int n = 25;99    PreparedString ps;100    ps.error = false;101    ps.pos = ImGui::GetCursorScreenPos();102    ps.pos.y += ImGui::GetCurrentWindow()->DC.CurrLineTextBaseOffset;103    ps.label.reserve(s.size() + 10);104    size_t argFrom = 0;105    for (size_t i = 0; i < s.size(); ++i)106    {107        if (s[i] == '{') {108            ps.label += "{";109            if (i + 1 < s.size() && s[i + 1] == '{')110                ++i;111            else {112                if (argFrom) {113                    ps.error = true;114                    break;115                }116                argFrom = i + 1;117            }118        }119        else if (s[i] == '}') {120            if (!argFrom) {121                if (i + 1 == s.size() || s[i + 1] != '}') {122                    ps.error = true;123                    break;124                }125                ps.label += "}";126                ++i;127            }128            else {129                if (i == argFrom) {130                    ps.error = true;131                    break;132                }133                if (i - argFrom > n) {134                    ps.label += s.substr(argFrom, n - 3);135                    while (ps.label.back() < 0) //strip incomplete unicode char136                        ps.label.pop_back();137                    ps.label += "...}";138                }139                else {140                    ps.label += s.substr(argFrom, i - argFrom);141                    ps.label += "}";142                }143                ps.fmtArgs.push_back({ argFrom - 1, ps.label.size() });144                argFrom = 0;145            }146        }147        else if (!argFrom) {148            ps.label += s[i];149        }150    }151    if (argFrom)152        ps.error = true;153 154    if (ps.error) {155        ps.label = "error";156        ps.fmtArgs.clear();157    }158    return ps;159}160 161ImVec2 IncWrapText(const ImVec2& dpos, const char* s, const char* text_end, float wrap_width, float scale)162{163    ImFont* font = ImGui::GetFont();164    const float line_height = font->FontSize * scale;165    float line_width = dpos.x;166    float text_height = dpos.y;167    const char* word_wrap_eol = NULL;168    const char* real_end = s + strlen(s);169    while (s < text_end)170    {171        // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.172        if (!word_wrap_eol)173        {174            word_wrap_eol = font->CalcWordWrapPositionA(scale, s, real_end, wrap_width - line_width);175        }176 177        if (s >= word_wrap_eol)178        {179            text_height += line_height;180            line_width = 0.0f;181            word_wrap_eol = NULL;182            //Wrapping skips upcoming blanks183            while (s < text_end && ImCharIsBlankA(*s))184                s++;185            if (*s == '\n')186                s++;187            continue;188        }189 190        // Decode and advance source191        const char* prev_s = s;192        unsigned int c = (unsigned int)*s;193        if (c < 0x80)194            s += 1;195        else196            s += ImTextCharFromUtf8(&c, s, text_end);197 198        if (c < 32)199        {200            if (c == '\n')201            {202                text_height += line_height;203                line_width = 0.0f;204                continue;205            }206            if (c == '\r')207                continue;208        }209 210        const float char_width = scale * ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX);211        /*if (line_width + char_width >= max_width)212        {213        s = prev_s;214        break;215        }*/216 217        line_width += char_width;218    }219 220    if (s == word_wrap_eol)221    {222        text_height += line_height;223        line_width = 0.0f;224    }225 226    return { line_width, text_height };227}228 229void DrawTextArgs(const PreparedString& ps, UIContext& ctx, const ImVec2& offset, const ImVec2& size, const ImVec2& align)230{231    if (ctx.beingResized)232        return;233 234    ImVec2 pos = ps.pos;235    uint32_t clr = ctx.colors[UIContext::Color::Selected];236    clr = (clr & 0x00ffffff) | 0xb0000000;237    float wrapPos = ImGui::GetCurrentWindow()->DC.TextWrapPos;238 239    if (wrapPos < 0 || size.x || size.y || offset.x || offset.y)240    {241        if (align.x || align.y) {242            ImVec2 textSize = ImGui::CalcTextSize(ps.label.data(), ps.label.data() + ps.label.size());243            ImVec2 sz = ImGui::CalcItemSize(size, textSize.x, textSize.y);244            ImVec2 dp{ (sz.x - textSize.x) * align.x, (sz.y - textSize.y) * align.y };245            pos += dp;246        }247 248        pos += offset;249        size_t i = 0;250        for (const auto& arg : ps.fmtArgs) {251            pos.x += ImGui::CalcTextSize(ps.label.data() + i, ps.label.data() + arg.first).x;252            ImGui::GetWindowDrawList()->AddText(pos, clr, "{");253            //ImVec2 sz = ImGui::CalcTextSize(ps.label.data() + arg.first, ps.label.data() + arg.second);254            //ImGui::GetWindowDrawList()->AddRectFilled(pos, pos + sz, 0x50808080);255            ImVec2 sz = ImGui::CalcTextSize(ps.label.data() + arg.first, ps.label.data() + arg.second - 1);256            pos.x += sz.x;257            ImGui::GetWindowDrawList()->AddText(pos, clr, "}");258            i = arg.second - 1;259        }260 261        if (ps.error)262            ImGui::GetWindowDrawList()->AddText(pos, clr, ps.label.c_str());263    }264    else265    {266        float wrapWidth = ImGui::CalcWrapWidthForPos(ps.pos, wrapPos);267        const char* text = ps.label.data();268        ImVec2 dp{ 0, 0 };269        for (const auto& arg : ps.fmtArgs)270        {271            const char* text_end = ps.label.data() + arg.first;272            dp = IncWrapText(dp, text, text_end, wrapWidth, ctx.zoomFactor);273            ImGui::GetWindowDrawList()->AddText(pos + dp, clr, "{");274            text = text_end;275            text_end = ps.label.data() + arg.second - 1;276            dp = IncWrapText(dp, text, text_end, wrapWidth, ctx.zoomFactor);277            ImGui::GetWindowDrawList()->AddText(pos + dp, clr, "}");278            text = text_end;279        }280 281        if (ps.error)282            ImGui::GetWindowDrawList()->AddText(pos, clr, ps.label.c_str());283    }284}285 286//----------------------------------------------------287 288UINode::child_iterator::iter::iter()289    : children(), idx()290{}291 292UINode::child_iterator::iter::iter(children_type& ch, bool freePos)293    : children(&ch), freePos(freePos), idx()294{295    while (!end() && !valid())296        ++idx;297}298 299UINode::child_iterator::iter&300UINode::child_iterator::iter::operator++ () {301    if (end())302        return *this;303    ++idx;304    while (!end() && !valid())305        ++idx;306    return *this;307}308 309UINode::child_iterator::iter310UINode::child_iterator::iter::operator++ (int) {311    iter it(*this);312    ++(*this);313    return it;314}315 316bool UINode::child_iterator::iter::operator== (const iter& it) const317{318    if (end() != it.end())319        return false;320    if (!end())321        return idx == it.idx;322    return true;323}324 325bool UINode::child_iterator::iter::operator!= (const iter& it) const326{327    return !(*this == it);328}329 330UINode::child_iterator::children_type::value_type&331UINode::child_iterator::iter::operator* ()332{333    static children_type::value_type dummy;334    if (end())335        return dummy;336    return children->at(idx);337}338 339const UINode::child_iterator::children_type::value_type&340UINode::child_iterator::iter::operator* () const341{342    static children_type::value_type dummy;343    if (end())344        return dummy;345    return children->at(idx);346}347 348size_t UINode::child_iterator::iter::index() const349{350    return idx;351}352 353bool UINode::child_iterator::iter::end() const354{355    return !children || idx >= children->size();356}357 358bool UINode::child_iterator::iter::valid() const359{360    if (end())361        return false;362    bool fp = children->at(idx)->hasPos;363    return freePos == fp;364}365 366UINode::child_iterator::child_iterator(children_type& children, bool freePos)367    : children(children), freePos(freePos)368{}369 370UINode::child_iterator::iter371UINode::child_iterator::begin() const372{373    return iter(children, freePos);374}375 376UINode::child_iterator::iter377UINode::child_iterator::end() const378{379    return iter();380}381 382UINode::child_iterator::operator bool() const383{384    return begin() != end();385}386 387//--------------------------------------------------------------------388 389void UINode::CloneChildrenFrom(const UINode& node, UIContext& ctx)390{391    children.resize(node.children.size());392    for (size_t i = 0; i < node.children.size(); ++i)393        children[i] = node.children[i]->Clone(ctx);394}395 396void UINode::DrawInteriorRect(UIContext& ctx)397{398    size_t level = ctx.parents.size() - 1;399    ImDrawList* dl = ImGui::GetWindowDrawList();400    int snapCount = UIContext::Color::COUNT - UIContext::Color::Snap1;401    dl->AddRect(cached_pos, cached_pos + cached_size, ctx.colors[UIContext::Snap1 + level % snapCount], 0, 0, 3);402}403 404void UINode::DrawSnap(UIContext& ctx)405{406    ctx.snapNextColumn = 0;407    ctx.snapSameLine = false;408    ctx.snapUseNextSpacing = false;409    ctx.snapSetNextSameLine = false;410 411    const float MARGIN = 7;412    assert(ctx.parents.back() == this);413    size_t level = ctx.parents.size() - 1;414    int snapOp = Behavior();415    ImVec2 m = ImGui::GetMousePos();416    ImVec2 d1 = m - cached_pos;417    ImVec2 d2 = cached_pos + cached_size - m;418    float mind = std::min({ d1.x, d1.y, d2.x, d2.y });419 420    //snap interior (first child)421    if ((snapOp & (SnapInterior | SnapItemInterior)) &&422        mind >= 3 && //allow snapping sides with zero border423        !stx::count_if(children, [](const auto& ch) { return ch->Behavior() & SnapSides; }))424    {425        ctx.snapParent = this;426        child_iterator it(children, true);427        if (it)428            ctx.snapIndex = it.begin().index();429        else430            ctx.snapIndex = children.size();431        DrawInteriorRect(ctx);432        return;433    }434 435    if (!level || !(snapOp & SnapSides))436        return;437 438    //snap side439    UINode* parent = ctx.parents[ctx.parents.size() - 2];440    const auto& pchildren = parent->children;441    size_t i = stx::find_if(pchildren, [&](const auto& ch) {442        return ch.get() == this;443        }) - pchildren.begin();444    if (i == pchildren.size())445        return;446    UINode* clip = parent;447    if (clip->Behavior() & SnapGrandparentClip)448        clip = ctx.parents[ctx.parents.size() - 3];449    if (m.x < clip->cached_pos.x ||450        m.y < clip->cached_pos.y ||451        m.x > clip->cached_pos.x + clip->cached_size.x ||452        m.y > clip->cached_pos.y + clip->cached_size.y)453        return;454 455    int ncols = parent->ColumnCount(ctx);456    int col = 0;457    if (ncols > 1)458    {459        for (size_t j = 0; j <= i; ++j)460            col = (col + pchildren[j]->nextColumn) % ncols;461    }462    bool lastItem = i + 1 == pchildren.size();463    ImGuiDir snapDir = ImGuiDir_None;464    //allow to snap in space between widgets too (don't check all coordinates)465    //works well with parent clip466    if (mind > MARGIN)467        snapDir = ImGuiDir_None;468    else if (d1.x == mind && d1.y >= 0 && d2.y >= 0)469        snapDir = ImGuiDir_Left;470    else if (d2.x == mind && d1.y >= 0 && d2.y >= 0)471        snapDir = ImGuiDir_Right;472    else if (d1.y == mind && d1.x >= 0 && d2.x >= 0)473        snapDir = ImGuiDir_Up;474    else if (d2.y == mind && d1.x >= 0 && d2.x >= 0)475        snapDir = ImGuiDir_Down;476    else if (lastItem && d2.x < 0 && d2.y < 0)477        snapDir = ImGuiDir_Down;478 479    if (snapDir == ImGuiDir_None)480    {481        if (ImRect(cached_pos, cached_pos + cached_size).Contains(m))482        {483            //end of search with no result (interpreted by TopWindow)484            //children were already snapped so we can safely end search here485            ctx.snapParent = parent;486            ctx.snapIndex = -1;487        }488        return;489    }490 491    ImVec2 p;492    float w = 0, h = 0;493    //snapRight and snapDown will extend the checked area to the next widget494    switch (snapDir)495    {496    case ImGuiDir_Left:497    {498        p = cached_pos;499        h = cached_size.y;500        auto& ch = pchildren[i];501        //check we are not pointing into widget on left502        const auto* lch = i && (pchildren[i - 1]->Behavior() & SnapSides) ? pchildren[i - 1].get() : nullptr;503        if (ncols > 1 && ch->nextColumn) {504            if (m.x < cached_pos.x)505                return;506        }507        if (ch->sameLine && lch) {508            float xm = (p.x + lch->cached_pos.x + lch->cached_size.x) / 2.f;509            //avg marker510            if (ch->spacing <= 1) {511                p.x = xm;512                h = std::max(h, lch->cached_size.y);513            }514            if (m.x < xm)515                return;516        }517        ctx.snapParent = parent;518        ctx.snapIndex = i;519        ctx.snapNextColumn = pchildren[i]->nextColumn;520        ctx.snapSameLine = pchildren[i]->sameLine;521        ctx.snapUseNextSpacing = true;522        ctx.snapSetNextSameLine = true;523        break;524    }525    case ImGuiDir_Right:526    {527        p = cached_pos + ImVec2(cached_size.x, 0);528        h = cached_size.y;529        //check we are not pointing into widget on right530        const Widget* rch = nullptr;531        if (i + 1 < pchildren.size() && !pchildren[i + 1]->nextColumn && pchildren[i + 1]->sameLine)532            rch = pchildren[i + 1].get();533        if (rch) {534            float xm = (p.x + rch->cached_pos.x) / 2.f;535            //avg marker536            if (rch->spacing <= 1) {537                p.x = xm;538                h = std::max(h, rch->cached_size.y);539            }540            if (m.x > xm)541                return;542        }543        if (!rch && ncols > 1)544        {545            for (size_t j = i + 1; j < pchildren.size(); ++j)546            {547                if (pchildren[j]->nextColumn) {548                    if ((col + pchildren[j]->nextColumn) % ncols > col)549                        rch = pchildren[j].get();550                    break;551                }552            }553            if (rch && m.x >= rch->cached_pos.x)554                return;555        }556        const auto* nch = i + 1 < pchildren.size() ? pchildren[i + 1].get() : nullptr;557        ctx.snapParent = parent;558        ctx.snapIndex = i + 1;559        ctx.snapNextColumn = 0;560        ctx.snapSameLine = true;561        ctx.snapUseNextSpacing = false;562        break;563    }564    case ImGuiDir_Up:565    case ImGuiDir_Down:566    {567        bool down = snapDir == ImGuiDir_Down;568        p = cached_pos;569        if (down)570            p.y += cached_size.y;571        float x2 = p.x + cached_size.x;572        //find range of widgets in the same row and column573        size_t i1 = i, i2 = i;574        for (int j = (int)i - 1; j >= 0; --j)575        {576            if (ncols > 1 && pchildren[j + 1]->nextColumn)577                break;578            if (!pchildren[j + 1]->sameLine)579                break;580            const auto& ch = pchildren[j];581            i1 = j;582            p.x = ch->cached_pos.x;583            if (down)584                p.y = std::max(p.y, ch->cached_pos.y + ch->cached_size.y);585        }586        for (size_t j = i + 1; j < pchildren.size(); ++j)587        {588            const auto& ch = pchildren[j];589            if (ncols > 1 && ch->nextColumn)590                break;591            if (!ch->sameLine)592                break;593            i2 = j;594            x2 = ch->cached_pos.x + ch->cached_size.x;595            if (down)596                p.y = std::max(p.y, ch->cached_pos.y + ch->cached_size.y);597        }598        //find a widget from next/prev column-row599        size_t inr = -1, ipr = -1;600        if (ncols >= 2)601        {602            int nc = ncols - col;603            for (size_t j = i + 1; j < pchildren.size(); ++j)604            {605                nc -= pchildren[j]->nextColumn;606                if (nc <= 0) {607                    inr = j;608                    break;609                }610            }611            nc = ncols;612            for (int j = (int)i; j >= 0; --j)613            {614                if (nc <= 0) {615                    ipr = j;616                    break;617                }618                nc -= pchildren[j]->nextColumn;619            }620        }621        w = x2 - p.x;622        if (down)623        {624            //check m.y not pointing in next column-row625            const Widget* nch = inr < pchildren.size() ? pchildren[inr].get() : nullptr;626            if (nch)627            {628                if (m.y >= nch->cached_pos.y)629                    return;630            }631            //check m.y not pointing in next row632            nch = i2 + 1 < pchildren.size() ? pchildren[i2 + 1].get() : nullptr;633            if (nch && (ncols <= 1 || !nch->nextColumn))634            {635                if (m.y >= (p.y + nch->cached_pos.y) / 2.f)636                    return;637            }638            ctx.snapParent = parent;639            ctx.snapIndex = i2 + 1;640            ctx.snapNextColumn = 0;641            ctx.snapSameLine = false;642            ctx.snapUseNextSpacing = false;643        }644        else645        {646            //check m.y is not pointing in previous column-row647            const Widget* pch = ipr < pchildren.size() ? pchildren[ipr].get() : nullptr;648            if (pch)649            {650                if (m.y <= pch->cached_pos.y + pch->cached_size.y)651                    return;652            }653            //check m.y not pointing in prev row654            pch = i1 > 0 ? pchildren[i1 - 1].get() : nullptr;655            if (pch && (ncols <= 1 || !pchildren[i1]->nextColumn))656            {657                if (m.y <= pch->cached_pos.y + pch->cached_size.y)658                    return;659            }660            ctx.snapParent = parent;661            ctx.snapIndex = i1;662            ctx.snapSameLine = false;663            ctx.snapNextColumn = pchildren[i1]->nextColumn;664            ctx.snapUseNextSpacing = true;665        }666        break;667    }668    default:669        return;670    }671 672    ImDrawList* dl = ImGui::GetWindowDrawList();673    dl->AddLine(p, p + ImVec2(w, h), ctx.colors[UIContext::Snap1 + (level - 1)], 3);674}675 676std::optional<std::pair<UINode*, int>>677UINode::FindChild(const UINode* ch)678{679    if (ch == this)680        return std::pair{ nullptr, 0 };681    for (size_t i = 0; i < children.size(); ++i) {682        const auto& child = children[i];683        if (child.get() == ch)684            return std::pair{ this, (int)i };685        auto tmp = child->FindChild(ch);686        if (tmp)687            return tmp;688    }689    return {};690}691 692std::vector<UINode*>693UINode::FindInRect(const ImRect& r)694{695    std::vector<UINode*> sel;696 697    if (cached_size.x && cached_size.y && //skip contextMenu698        cached_pos.x > r.Min.x &&699        cached_pos.y > r.Min.y &&700        cached_pos.x + cached_size.x < r.Max.x &&701        cached_pos.y + cached_size.y < r.Max.y)702        sel.push_back(this);703 704    for (const auto& child : children) {705        auto chsel = child->FindInRect(r);706        sel.insert(sel.end(), chsel.begin(), chsel.end());707    }708    return sel;709}710 711std::vector<UINode*>712UINode::GetAllChildren()713{714    std::vector<UINode*> chs;715    chs.reserve(children.size() * 2);716    chs.push_back(this);717    for (const auto& child : children) {718        auto vec = child->GetAllChildren();719        chs.insert(chs.end(), vec.begin(), vec.end());720    }721    return chs;722}723 724//this must return exact class id725std::string UINode::GetTypeName()726{727    std::string name = typeid(*this).name();728    //erase "struct"729    auto i = name.rfind(' ');730    if (i != std::string::npos)731        name.erase(0, i + 1);732    //erase encoded name length733    auto it = stx::find_if(name, [](char c) { return isalpha(c); });734    if (it != name.end())735        name.erase(0, it - name.begin());736    return name;737}738 739int GetTotalIndex(UINode* parent, UINode* child)740{741    std::string tname = child->GetTypeName();742    int idx = 0;743    for (const auto& ch : parent->children) {744        if (ch.get() == child)745            return idx;746        idx += GetTotalIndex(ch.get(), child);747        if (ch->GetTypeName() == tname)748            ++idx;749    }750    return idx;751}752 753void UINode::PushError(UIContext& ctx, const std::string& err)754{755    std::string name = GetTypeName();756    if (this != ctx.root) {757        int idx = GetTotalIndex(ctx.parents[0], this);758        name = name + " #" + std::to_string(idx+1);759    }760    ctx.errors.push_back(name + " : " + err);761}762 763std::string UINode::GetParentIndexes(UIContext& ctx)764{765    std::string id;766    UINode* node = ctx.parents.back();767    for (auto it = ++ctx.parents.rbegin(); it != ctx.parents.rend(); ++it) {768        size_t i = stx::find_if((*it)->children, [=](const auto& ch) {769            return ch.get() == node;770            }) - (*it)->children.begin();771            id = std::to_string(i) + id;772            node = *it;773    }774    return id;775}776 777void UINode::ResetLayout()778{779    hbox.clear();780    vbox.clear();781    for (auto& ch : children)782        ch->ResetLayout();783}784 785std::vector<std::string> UINode::UsedFieldVars()786{787    std::vector<std::string> used;788    auto props = Properties();789    for (auto& p : props) {790        if (!p.property)791            continue;792        auto us = p.property->used_variables();793        used.insert(used.end(), us.begin(), us.end());794    }795    for (auto& child : children) {796        auto us = child->UsedFieldVars();797        used.insert(used.end(), us.begin(), us.end());798    }799    stx::sort(used);800    used.erase(stx::unique(used), used.end());801    return used;802}803 804void UINode::RenameFieldVars(const std::string& oldn, const std::string& newn)805{806    for (int i = 0; i < 2; ++i)807    {808        auto props = i ? Events() : Properties();809        for (auto& p : props) {810            if (!p.property)811                continue;812            p.property->rename_variable(oldn, newn);813        }814    }815    for (auto& child : children)816        child->RenameFieldVars(oldn, newn);817}818 819//----------------------------------------------------820 821std::unique_ptr<Widget>822Widget::Create(const std::string& name, UIContext& ctx)823{824    if (name == "Text")825        return std::make_unique<Text>(ctx);826    else if (name == "Selectable")827        return std::make_unique<Selectable>(ctx);828    else if (name == "Button")829        return std::make_unique<Button>(ctx);830    else if (name == "CheckBox")831        return std::make_unique<CheckBox>(ctx);832    else if (name == "RadioButton")833        return std::make_unique<RadioButton>(ctx);834    else if (name == "Input")835        return std::make_unique<Input>(ctx);836    else if (name == "Combo")837        return std::make_unique<Combo>(ctx);838    else if (name == "Slider")839        return std::make_unique<Slider>(ctx);840    else if (name == "ProgressBar")841        return std::make_unique<ProgressBar>(ctx);842    else if (name == "ColorEdit")843        return std::make_unique<ColorEdit>(ctx);844    else if (name == "Image")845        return std::make_unique<Image>(ctx);846    else if (name == "Spacer")847        return std::make_unique<Spacer>(ctx);848    else if (name == "Separator")849        return std::make_unique<Separator>(ctx);850    else if (name == "CustomWidget")851        return std::make_unique<CustomWidget>(ctx);852    else if (name == "Table")853        return std::make_unique<Table>(ctx);854    else if (name == "Child")855        return std::make_unique<Child>(ctx);856    else if (name == "CollapsingHeader")857        return std::make_unique<CollapsingHeader>(ctx);858    else if (name == "TabBar")859        return std::make_unique<TabBar>(ctx);860    else if (name == "TabItem")861        return std::make_unique<TabItem>(ctx);862    else if (name == "TreeNode")863        return std::make_unique<TreeNode>(ctx);864    else if (name == "MenuBar")865        return std::make_unique<MenuBar>(ctx);866    else if (name == "ContextMenu")867        return std::make_unique<ContextMenu>(ctx);868    else if (name == "MenuIt")869        return std::make_unique<MenuIt>(ctx);870    else if (name == "Splitter")871        return std::make_unique<Splitter>(ctx);872    else if (name == "DockSpace")873        return std::make_unique<DockSpace>(ctx);874    else if (name == "DockNode")875        return std::make_unique<DockNode>(ctx);876    else877        return {};878}879 880Widget::Widget()881{882    hasPos.add("None", ImRad::AlignNone);883    hasPos.add("TopLeft", ImRad::AlignLeft | ImRad::AlignTop);884    hasPos.add("TopCenter", ImRad::AlignHCenter | ImRad::AlignTop);885    hasPos.add("TopRight", ImRad::AlignRight | ImRad::AlignTop);886    hasPos.add("LeftCenter", ImRad::AlignLeft | ImRad::AlignVCenter);887    hasPos.add("Center", ImRad::AlignHCenter | ImRad::AlignVCenter);888    hasPos.add("RightCenter", ImRad::AlignRight | ImRad::AlignVCenter);889    hasPos.add("BottomLeft", ImRad::AlignLeft | ImRad::AlignBottom);890    hasPos.add("BottomCenter", ImRad::AlignHCenter | ImRad::AlignBottom);891    hasPos.add("BottomRight", ImRad::AlignRight | ImRad::AlignBottom);892 893    cursor.add$(ImGuiMouseCursor_None);894    cursor.add$(ImGuiMouseCursor_Arrow);895    cursor.add$(ImGuiMouseCursor_TextInput);896    cursor.add$(ImGuiMouseCursor_ResizeAll);897    cursor.add$(ImGuiMouseCursor_ResizeNS);898    cursor.add$(ImGuiMouseCursor_ResizeEW);899    cursor.add$(ImGuiMouseCursor_ResizeNESW);900    cursor.add$(ImGuiMouseCursor_ResizeNWSE);901    cursor.add$(ImGuiMouseCursor_Hand);902    cursor.add$(ImGuiMouseCursor_NotAllowed);903}904 905int Widget::Behavior()906{907    return hasPos ? 0 : SnapSides;908}909 910//detects if widget is leftmost/topmost in its row911//colId, rowId - use as index to parent.hbox/vbox912//any usage of stretched dimension triggers HLayout/VLayout for that row/column913Widget::Layout Widget::GetLayout(UINode* parent)914{915    Layout l;916    l.colId = l.rowId = -1;917    l.index = -1;918 919    if (hasPos || !(Behavior() & SnapSides))920    {921        l.flags |= Layout::Topmost | Layout::Leftmost;922        return l;923    }924 925    bool firstWidget = true;926    bool leftmost = true;927    bool topmost = true;928    bool bottommost = false;929    bool hlay = false;930    bool vlay = false;931    int colId = 0;932    int rowId = 0;933    for (const auto& child : parent->children)934    {935        if (child->hasPos || !(child->Behavior() & SnapSides)) //ignore MenuBar etc.936            continue;937 938        if ((!child->sameLine || child->nextColumn) && !firstWidget)939        {940            if (colId == l.colId)941                l.flags |= vlay * Layout::VLayout;942            if (rowId == l.rowId)943                l.flags |= hlay * Layout::HLayout;944            ++rowId;945            topmost = child->nextColumn;946            leftmost = true;947            bottommost = false;948            hlay = false;949            if (child->nextColumn)950            {951                vlay = false;952                colId += child->nextColumn;953            }954        }955        else if (child->sameLine && !child->nextColumn) {956            leftmost = false;957        }958 959        if (child->size_y.stretched())960            vlay = true;961        if (child->size_x.stretched())962            hlay = true;963 964        if (child.get() == this) {965            l.index = &child - parent->children.data();966            l.colId = colId;967            l.rowId = rowId;968            l.flags |= (leftmost * Layout::Leftmost) | (topmost * Layout::Topmost);969            bottommost = true;970        }971 972        firstWidget = false;973    }974    if (bottommost)975        l.flags |= Layout::Bottommost;976    if (colId == l.colId)977        l.flags |= vlay * Layout::VLayout;978    if (rowId == l.rowId)979        l.flags |= hlay * Layout::HLayout;980 981    return l;982}983 984void Widget::Draw(UIContext& ctx)985{986    UINode* parent = ctx.parents.back();987    Layout l = GetLayout(parent);988    const int defSpacing = (l.flags & Layout::Topmost) ? 0 : 1;989    ctx.stretchSize = { 0, 0 };990 991    if (!hasPos && nextColumn) {992        bool inTable = dynamic_cast<Table*>(parent);993        if (inTable)994            ImRad::TableNextColumn(nextColumn);995        else996            ImRad::NextColumn(nextColumn);997    }998 999    if (hasPos)1000    {1001        ImRect r = parent->Behavior() & SnapItemInterior ?1002            ImRect{ parent->cached_pos, parent->cached_pos + parent->cached_size } :1003            ImRad::GetParentInnerRect();1004        ImVec2 pos{ pos_x.eval_px(ImGuiAxis_X, ctx), pos_y.eval_px(ImGuiAxis_Y, ctx) };1005        if (hasPos & ImRad::AlignLeft)1006            pos.x += r.Min.x;1007        else if (hasPos & ImRad::AlignRight)1008            pos.x += r.Max.x;1009        else if (hasPos & ImRad::AlignHCenter)1010            pos.x += r.GetCenter().x;1011        if (hasPos & ImRad::AlignTop)1012            pos.y += r.Min.y;1013        else if (hasPos & ImRad::AlignBottom)1014            pos.y += r.Max.y;1015        else if (hasPos & ImRad::AlignVCenter)1016            pos.y += r.GetCenter().y;1017        ImGui::SetCursorScreenPos(pos);1018    }1019    else if (l.flags & (Layout::HLayout | Layout::VLayout))1020    {1021        ImRad::VBox* vbox = nullptr;1022        ImRad::HBox *hbox = nullptr;1023 1024        if (l.flags & Layout::VLayout)1025        {1026            if (l.colId >= parent->vbox.size())1027                parent->vbox.resize(l.colId + 1);1028            vbox = &parent->vbox[l.colId];1029            if ((l.flags & Layout::Topmost) && (l.flags & Layout::Leftmost))1030                vbox->BeginLayout();1031            /*if (l.flags & Layout::Leftmost)1032                ImGui::SetCursorPosY(vbox);*/1033        }1034        if ((l.flags & Layout::Leftmost) && (spacing - defSpacing))1035        {1036            ImRad::Spacing(spacing - defSpacing);1037        }1038        if (l.flags & Layout::HLayout)1039        {1040            if (l.rowId >= parent->hbox.size())1041                parent->hbox.resize(l.rowId + 1);1042            hbox = &parent->hbox[l.rowId];1043            if (l.flags & Layout::Leftmost)1044                hbox->BeginLayout();1045            //ImGui::SetCursorPosX(hbox); //currently not needed but may be useful if we upgrade layouts1046        }1047        if (!(l.flags & Layout::Leftmost))1048        {1049            //we need to provide correct spacing and don't use SetCursorX1050            //becausein that case after hbox.Reset() hbox.GetPos() will return CursorPos with1051            //a wrong spacing and it will be used in autosized window contentSize1052            ImGui::SameLine(0, spacing * ImGui::GetStyle().ItemSpacing.x);1053        }1054        //after SameLine was determined1055        if (vbox)1056            ctx.stretchSize.y = vbox->GetSize(); // !(l.flags & Layout::Leftmost));1057        if (hbox)1058            ctx.stretchSize.x = hbox->GetSize();1059    }1060    else1061    {1062        if (sameLine) {1063            ImGui::SameLine(0, spacing * ImGui::GetStyle().ItemSpacing.x);1064        }1065        else {1066            ImRad::Spacing(spacing - defSpacing);1067        }1068        if (indent)1069            ImGui::Indent(indent * ImGui::GetStyle().IndentSpacing / 2);1070    }1071 1072    ImGui::PushID(this);1073    ctx.parents.push_back(this);1074    auto lastHovered = ctx.hovered;1075    auto p1 = ImGui::GetCursorScreenPos();1076 1077    if (style_font.has_value())1078        ImGui::PushFont(ImRad::GetFontByName(style_font.eval(ctx)));1079    if (!style_text.empty())1080        ImGui::PushStyleColor(ImGuiCol_Text, style_text.eval(ImGuiCol_Text, ctx));1081    if (!style_border.empty())1082        ImGui::PushStyleColor(ImGuiCol_Border, style_border.eval(ImGuiCol_Border, ctx));1083    if (!style_frameBg.empty())1084        ImGui::PushStyleColor(ImGuiCol_FrameBg, style_frameBg.eval(ImGuiCol_FrameBg, ctx));1085    if (!style_button.empty())1086        ImGui::PushStyleColor(ImGuiCol_Button, style_button.eval(ImGuiCol_Button, ctx));1087    if (!style_buttonHovered.empty())1088        ImGui::PushStyleColor(ImGuiCol_ButtonHovered, style_buttonHovered.eval(ImGuiCol_ButtonHovered, ctx));1089    if (!style_buttonActive.empty())1090        ImGui::PushStyleColor(ImGuiCol_ButtonActive, style_buttonActive.eval(ImGuiCol_ButtonActive, ctx));1091    if (!style_frameBorderSize.empty())1092        ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, style_frameBorderSize);1093    if (!style_frameRounding.empty())1094        ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, style_frameRounding);1095    if (!style_framePadding.empty())1096        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, style_framePadding);1097 1098    ImGui::BeginDisabled((!disabled.empty() && disabled.eval(ctx)) || (!visible.empty() && !visible.eval(ctx)));1099    ImDrawList* drawList = DoDraw(ctx);1100    ImGui::EndDisabled();1101    CalcSizeEx(p1, ctx);1102 1103    if (!style_text.empty())1104        ImGui::PopStyleColor();1105    if (!style_border.empty())1106        ImGui::PopStyleColor();1107    if (!style_frameBg.empty())1108        ImGui::PopStyleColor();1109    if (!style_button.empty())1110        ImGui::PopStyleColor();1111    if (!style_buttonHovered.empty())1112        ImGui::PopStyleColor();1113    if (!style_buttonActive.empty())1114        ImGui::PopStyleColor();1115    if (!style_frameBorderSize.empty())1116        ImGui::PopStyleVar();1117    if (!style_frameRounding.empty())1118        ImGui::PopStyleVar();1119    if (!style_framePadding.empty())1120        ImGui::PopStyleVar();1121    if (style_font.has_value())1122        ImGui::PopFont();1123 1124    if (!hasPos)1125        HashCombineData(ctx.layoutHash, ImGui::GetItemID());1126    if (l.flags & Layout::VLayout)1127    {1128        auto& vbox = parent->vbox[l.colId];1129        float sizeY = ImRad::VBox::ItemSize;1130        if (Behavior() & HasSizeY) {1131            sizeY = size_y.stretched() ? (float)size_y.value() :1132                size_y.zero() ? ImRad::VBox::ItemSize :1133                size_y.eval_px(ImGuiAxis_Y, ctx);1134            HashCombineData(ctx.layoutHash, sizeY);1135        }1136        if (size_y.stretched()) {1137            if (l.flags & Layout::Leftmost)1138                vbox.AddSize(spacing, ImRad::VBox::Stretch(sizeY));1139            else1140                vbox.UpdateSize(0, ImRad::VBox::Stretch(sizeY));1141        }1142        else {1143            if (l.flags & Layout::Leftmost)1144                vbox.AddSize(spacing, sizeY);1145            else1146                vbox.UpdateSize(0, sizeY);1147        }1148    }1149    if (l.flags & Layout::HLayout)1150    {1151        auto& hbox = parent->hbox[l.rowId];1152        float sizeX = ImRad::HBox::ItemSize;1153        if (Behavior() & HasSizeX) {1154            sizeX = size_x.stretched() ? (float)size_x.value() :1155                size_x.zero() ? ImRad::HBox::ItemSize :1156                size_x.eval_px(ImGuiAxis_X, ctx);1157            HashCombineData(ctx.layoutHash, sizeX);1158        }1159        int sp = (l.flags & Layout::Leftmost) ? 0 : (int)spacing;1160        if (size_x.stretched())1161            hbox.AddSize(sp, ImRad::HBox::Stretch(sizeX));1162        else1163            hbox.AddSize(sp, sizeX);1164    }1165 1166    //doesn't work for open CollapsingHeader etc:1167    //bool hovered1 = ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled);1168    bool hovered = ImGui::IsMouseHoveringRect(cached_pos, cached_pos + cached_size) &&1169        ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows);1170    if ((ctx.mode == UIContext::NormalSelection || ctx.mode == UIContext::SnapInsert) &&1171        hovered && !ImGui::GetTopMostAndVisiblePopupModal())1172    {1173        //prevent changing cursor to e.g. TextInput1174        //but don't override child.Draw SetMouseCursor1175        if (ctx.hovered == lastHovered)1176            ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow);1177    }1178    if (ctx.mode == UIContext::NormalSelection &&1179        hovered && !ImGui::GetTopMostAndVisiblePopupModal())1180    {1181        if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) //this works even for non-items like TabControl etc.1182        {1183            if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl))1184            {1185                if (!stx::count(ctx.selected, ctx.root))1186                    toggle(ctx.selected, this);1187            }1188            else1189                ctx.selected = { this };1190            ImGui::GetIO().MouseReleased[ImGuiMouseButton_Left] = false; //eat event so parent won't get selected1191        }1192        else if (ImGui::IsMouseReleased(ImGuiMouseButton_Right))1193        {1194            if (!stx::count(ctx.selected, this))1195                ctx.selected = { this };1196            ImGui::GetIO().MouseReleased[ImGuiMouseButton_Left] = false; //eat event so parent won't get selected1197        }1198    }1199    bool allowed = !ImGui::GetTopMostAndVisiblePopupModal() &&1200        (ctx.activePopups.empty() || stx::count(ctx.activePopups, ImGui::GetCurrentWindow()));

Showing the first 1,200 of 6502 lines. Download the file for the rest.