dlxjj/imradv3
0263
1#pragma once2#include <array>3#include <optional>4#include <imgui.h>5#include <imgui_internal.h>6#include <misc/cpp/imgui_stdlib.h>7#include "stx.h"8#include "utils.h"9#include "binding_property.h"10#include "cppgen.h"11#include "ui_new_field.h"12#include "ui_binding.h"13#include "ui_combo_dlg.h"14 15inline const char* PARENT_STR = "inherit";16inline const uint32_t NONE_COLOR = 0xff800000;17inline const uint32_t NEW_COLOR = 0xff006000;18inline const uint32_t RENAME_COLOR = 0xff004080;19 20inline bool IsHighlighted(const std::string& id)21{22 ImGuiID iid = ImGui::GetID(id.c_str());23 if (ImGui::GetHoveredID() == iid || ImGui::GetActiveID() == iid || ImGui::GetFocusID() == iid)24 return true;25 ImGuiID popupId = ImGui::GetIDWithSeed("##ComboPopup", 0, iid);26 if (ImGui::IsPopupOpen(popupId, 0))27 return true;28 return false;29}30 31enum {32 BindingButton_ReferenceOnly = 0x133};34 35template <class T>36inline bool BindingButton(const char* label, bindable<T>* val, const std::string& type, int flags, UIContext& ctx)37{38 if (val == ctx.setProp)39 {40 //commit dialog request41 ctx.setProp = nullptr;42 *val->access() = ctx.setPropValue;43 return true;44 }45 46 bool bound;47 if constexpr (std::is_same_v<T, std::string> ||48 std::is_same_v<T, std::vector<std::string>> ||49 std::is_same_v<T, color_t>)50 {51 bound = !val->used_variables().empty();52 }53 else if constexpr (std::is_same_v<T, bool>)54 bound = !val->empty(); //the default value is usually empty55 else56 bound = !val->empty() && !val->has_value();57 58 ImGui::PushStyleColor(ImGuiCol_Button, bound ? 0xff0080ff : 0xffc0c0c0);59 float sp = (ImGui::GetFrameHeight() - 12) / 2;60 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + sp);61 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + sp);62 ImGui::PushItemFlag(ImGuiItemFlags_NoNav, true);63 bool pushed = ImGui::Button(("##" + std::string(label)).c_str(), { 12, 12 });64 ImGui::PopItemFlag();65 ImGui::PopStyleColor();66 if (pushed)67 {68 bindingDlg.font = type.find("std::string") != std::string::npos ? ctx.defaultStyleFont : nullptr;69 bindingDlg.codeGen = ctx.codeGen;70 bindingDlg.curArray = ctx.GetCurrentArray();71 bindingDlg.name = label;72 bindingDlg.expr = val->c_str();73 bindingDlg.type = type;74 bindingDlg.forceReference = flags & BindingButton_ReferenceOnly;75 bindingDlg.OpenPopup([&ctx, val](ImRad::ModalResult) {76 ctx.setProp = val;77 ctx.setPropValue = bindingDlg.expr;78 });79 return true;80 }81 82 return false;83}84 85template <class T, class = std::enable_if_t<!std::is_same_v<T, void>>>86inline bool BindingButton(const char* label, bindable<T>* val, int flags, UIContext& ctx)87{88 return BindingButton(label, val, typeid_name<T>(), flags, ctx);89}90 91template <class T, class = std::enable_if_t<!std::is_same_v<T, void>>>92inline bool BindingButton(const char* label, bindable<T>* val, UIContext& ctx)93{94 return BindingButton(label, val, typeid_name<T>(), 0, ctx);95}96 97//---------------------------------------------------------------------------------98 99enum100{101 InputDirectVal_Modified = 0x1,102 InputDirectVal_ShortcutButton = 0x2,103};104 105template <class T>106bool InputDirectVal(direct_val<T, false>* val, int fl, UIContext& ctx)107{108 ImGui::PushFont(!ImRad::IsCurrentItemDisabled() && (fl & InputDirectVal_Modified) ?109 ctx.pgbFont : ctx.pgFont);110 std::string id = "##" + std::to_string((uint64_t)val);111 bool changed = false;112 if constexpr (std::is_same_v<T, int>) {113 bool high = IsHighlighted(id);114 ImGui::PushID(id.c_str());115 high |= IsHighlighted("+") || IsHighlighted("-");116 ImGui::PopID();117 if (ImRad::IsCurrentItemDisabled())118 high = false;119 changed = ImGui::InputInt(id.c_str(), val->access(), high ? 1 : 0);120 }121 else if constexpr (std::is_same_v<T, float>)122 changed = ImGui::InputFloat(id.c_str(), val->access(), 0, 0, "%.f");123 else124 assert(false);125 126 ImGui::PopFont();127 return changed;128}129 130inline bool InputDirectVal(direct_val<dimension_t>* val, int flags, UIContext& ctx)131{132 ImGui::PushFont(!ImRad::IsCurrentItemDisabled() && (flags & InputDirectVal_Modified) ?133 ctx.pgbFont : ctx.pgFont);134 std::string id = "##" + std::to_string((uint64_t)val);135 bool changed = ImGui::InputFloat(id.c_str(), val->access(), 0, 0, "%.0f");136 ImGui::PopFont();137 return changed;138}139 140inline bool InputDirectVal(direct_val<pzdimension_t>* val, UIContext& ctx)141{142 ImGui::PushFont(val->has_value() ? ctx.pgbFont : ctx.pgFont);143 std::string id = "##" + std::to_string((uint64_t)val);144 std::string tmp = val->has_value() ? std::to_string((int)*val->access()) : "";145 std::string hint = ImGui::GetActiveID() == ImGui::GetCurrentWindow()->GetID(id.c_str()) ? "" : PARENT_STR;146 bool changed = ImGui::InputTextWithHint(id.c_str(), hint.c_str(), &tmp, ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll);147 if (changed)148 {149 std::istringstream is(tmp);150 is >> *val->access();151 if (!is && ImGui::IsItemDeactivatedAfterEdit())152 val->clear();153 }154 ImGui::PopFont();155 return changed;156}157 158inline bool InputDirectVal(direct_val<pzdimension2_t>* val, UIContext& ctx)159{160 bool changed = false;161 std::string id = "##" + std::to_string((uint64_t)val);162 ImGui::PushFont(!ImRad::IsCurrentItemDisabled() && val->has_value() ?163 ctx.pgbFont : ctx.pgFont);164 ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth());165 for (int i = 0; i < 2; ++i)166 {167 ImGui::PushID(i);168 if (i)169 ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);170 //bool ch = ImGui::InputFloat2(id.c_str(), (float*)val->access(), "%.0f");171 std::string tmp = val->has_value() ? std::to_string((int)(*val->access())[i]) : "";172 std::string hint = i || ImGui::GetActiveID() == ImGui::GetCurrentWindow()->GetID(id.c_str()) ? "" : PARENT_STR;173 if (ImGui::InputTextWithHint(id.c_str(), hint.c_str(), &tmp, ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll))174 {175 changed = true;176 std::istringstream is(tmp);177 is >> (*val->access())[i];178 if (!is && ImGui::IsItemDeactivatedAfterEdit())179 val->clear();180 }181 ImGui::PopItemWidth();182 ImGui::PopID();183 }184 ImGui::PopFont();185 return changed;186}187 188inline bool InputDirectVal(direct_val<bool>* val, int fl, UIContext& ctx)189{190 std::string id = "##" + std::to_string((uint64_t)val);191 bool val1 = *val;192 193 ImGui::PushStyleColor(ImGuiCol_CheckMark, ImGui::GetStyleColorVec4(194 (fl & InputDirectVal_Modified) ? ImGuiCol_Text : ImGuiCol_TextDisabled));195 196 if ((fl & InputDirectVal_Modified) && !val1) {197 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2);198 ImGui::PushStyleColor(ImGuiCol_Border, ImGui::GetStyleColorVec4(ImGuiCol_Text));199 }200 201 bool changed = ImGui::Checkbox(id.c_str(), val->access());202 203 if ((fl & InputDirectVal_Modified) && !val1) {204 ImGui::PopStyleVar();205 ImGui::PopStyleColor();206 }207 ImGui::PopStyleColor();208 209 return changed;210}211 212inline bool InputDirectVal(direct_val<std::string>* val, int fl, UIContext& ctx)213{214 ImGui::PushFont(215 !IsAscii(*val->access()) ? ctx.defaultStyleFont :216 !ImRad::IsCurrentItemDisabled() && (fl & InputDirectVal_Modified) ? ctx.pgbFont :217 ctx.pgFont218 );219 std::string id = "##" + std::to_string((uint64_t)val);220 bool ch = ImGui::InputText(id.c_str(), val->access(), ImGuiInputTextFlags_CallbackCharFilter, DefaultCharFilter);221 ImGui::PopFont();222 return ch;223}224 225inline bool InputDirectVal(direct_val<shortcut_t>* val, int flags, UIContext& ctx)226{227 if (flags & InputDirectVal_ShortcutButton)228 ImGui::SetNextItemWidth(ImGui::CalcItemWidth() - ImGui::GetFrameHeight());229 230 std::string id = "##" + std::to_string((uint64_t)val);231 ImGui::PushFont(!ImRad::IsCurrentItemDisabled() ? ctx.pgbFont : ctx.pgFont);232 bool changed = ImGui::InputText(id.c_str(), val->access(), ImGuiInputTextFlags_CallbackCharFilter, DefaultCharFilter);233 ImGui::PopFont();234 235 if (flags & InputDirectVal_ShortcutButton)236 {237 std::string buttonId = id + "But";238 std::string popupId = id + "DropDown";239 240 ImGui::SameLine(0, 0);241 if (IsHighlighted(id) || IsHighlighted(buttonId) ||242 ImGui::IsPopupOpen(ImGui::GetID(popupId.c_str()), 0))243 {244 ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyle().Colors[val->flags() ? ImGuiCol_ButtonActive : ImGuiCol_Button]);245 if (ImGui::ArrowButton(buttonId.c_str(), ImGuiDir_Down)) // { ImGui::GetFrameHeight(), ImGui::GetFrameHeight() }))246 {247 ImGui::OpenPopup(popupId.c_str());248 }249 ImGui::PopStyleColor();250 }251 else252 {253 ImGui::InvisibleButton(buttonId.c_str(), { ImGui::GetFrameHeight(), ImGui::GetFrameHeight() });254 }255 256 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 8, 8 });257 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0, 8 });258 if (ImGui::BeginPopup(popupId.c_str()))259 {260 if (ImGui::MenuItem("Global", nullptr, val->flags() & ImGuiInputFlags_RouteGlobal)) {261 changed = true;262 val->set_flags(val->flags() ^ ImGuiInputFlags_RouteGlobal);263 }264 if (ImGui::MenuItem("Repeat", nullptr, val->flags() & ImGuiInputFlags_Repeat)) {265 changed = true;266 val->set_flags(val->flags() ^ ImGuiInputFlags_Repeat);267 }268 ImGui::EndPopup();269 }270 ImGui::PopStyleVar(2);271 }272 273 return changed;274}275 276 277inline bool InputDirectValContextMenu(direct_val<std::string>* val, UIContext& ctx)278{279 bool changed = false;280 ImGui::PushFont(!ImRad::IsCurrentItemDisabled() ? ctx.pgbFont : ctx.pgFont);281 std::string id = "##" + std::to_string((uint64_t)val);282 if (ImGui::BeginCombo(id.c_str(), val->c_str(),283 IsHighlighted(id) ? 0 : ImGuiComboFlags_NoArrowButton))284 {285 ImGui::PopFont();286 ImGui::PushFont(ImGui::GetFont());287 288 if (ImGui::Selectable(" ", val->empty()))289 {290 changed = true;291 *val->access() = "";292 }293 for (const std::string& cm : ctx.contextMenus)294 {295 if (ImGui::Selectable(cm.c_str(), cm == val->c_str()))296 {297 changed = true;298 *val->access() = cm;299 }300 }301 ImGui::EndCombo();302 }303 ImGui::PopFont();304 return changed;305}306 307template <class T>308inline bool InputDirectValEnum(direct_val<T, true>* val, int fl, UIContext& ctx)309{310 bool changed = false;311 std::string id = "##" + std::to_string((uint64_t)val);312 std::string value = val->get_id();313 size_t pre = 0;314 if (!value.compare(0, 5, "ImGui")) {315 pre = value.find("_");316 if (pre != std::string::npos && pre + 1 < value.size())317 ++pre;318 else319 pre = 0;320 }321 ImGui::PushFont(!ImRad::IsCurrentItemDisabled() && (fl & InputDirectVal_Modified) ?322 ctx.pgbFont : ctx.pgFont);323 if (ImGui::BeginCombo(id.c_str(), val->get_id().c_str() + pre,324 !ImRad::IsCurrentItemDisabled() && IsHighlighted(id) ? 0 : ImGuiComboFlags_NoArrowButton))325 {326 ImGui::PopFont();327 ImGui::PushFont(ImGui::GetFont());328 changed = true;329 for (const auto& item : val->get_ids())330 {331 if (!item.first.compare(0, 5, "ImGui")) {332 pre = item.first.find("_");333 if (pre != std::string::npos && pre + 1 < item.first.size())334 ++pre;335 else336 pre = 0;337 }338 if (ImGui::Selectable(item.first.c_str() + pre, *val->access() == item.second))339 *val->access() = item.second;340 }341 ImGui::EndCombo();342 }343 ImGui::PopFont();344 return changed;345}346 347template <class T>348inline int InputDirectValFlags(const char* name, direct_val<T, true>* val, int defVal, UIContext& ctx)349{350 int changed = false;351 ImVec2 pad = ImGui::GetStyle().FramePadding;352 ImGui::Unindent();353 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 0.0f, pad.y });354 //ImGui::PushStyleColor(ImGuiCol_NavCursor, { 0, 0, 0, 0 });355 ImGui::SetNextItemAllowOverlap();356 if (ImGui::TreeNodeEx(name, ImGuiTreeNodeFlags_SpanAllColumns /*| ImGuiTreeNodeFlags_NoNavFocus*/)) {357 ImGui::PopStyleVar();358 ImGui::Indent();359 ImGui::TableNextColumn();360 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { pad.x, 0 }); //row packing361 ImGui::Spacing();362 for (const auto& id : val->get_ids())363 {364 if (id.first == "") {365 ImGui::Separator();366 continue;367 }368 if (defVal != -1) {369 bool different = ((int)*val & id.second) != (defVal & id.second);370 ImGui::PushFont(ImRad::GetFontByName(different ? "imrad.pgb" : "imrad.pg"));371 }372 size_t pre = 0;373 if (!id.first.compare(0, 5, "ImGui")) {374 pre = id.first.find('_');375 if (pre != std::string::npos && pre + 1 < id.first.size())376 ++pre;377 else378 pre = 0;379 }380 ImU64 flags = *val;381 if (ImGui::CheckboxFlags(id.first.c_str() + pre, &flags, id.second)) {382 changed = id.second;383 *val->access() = (int)flags;384 }385 if (defVal != -1)386 ImGui::PopFont();387 }388 ImGui::PopStyleVar();389 ImGui::TreePop();390 ImGui::Unindent();391 }392 else393 {394 ImGui::PopStyleVar();395 ImGui::TableNextColumn();396 std::string label, tip;397 for (const auto& id : val->get_ids())398 {399 if (id.first != "" && (*val & id.second) == id.second)400 {401 size_t pre = 0;402 if (!id.first.compare(0, 5, "ImGui")) {403 size_t i = id.first.find('_');404 if (i != std::string::npos && i + 1 < id.first.size())405 pre = i + 1;406 }407 tip += id.first.substr(pre) + "\n";408 }409 }410 if (tip == "")411 tip = "None";412 ImGui::SetNextItemWidth(-ImGui::GetFrameHeight());413 ImGui::PushFont(!ImRad::IsCurrentItemDisabled() && *val != defVal ?414 ctx.pgbFont : ctx.pgFont);415 //float w = ImGui::GetContentRegionAvail().x;416 ImGui::Text("...");417 ImGui::PopFont();418 if (//ImGui::GetItemRectSize().x > w &&419 ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))420 {421 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 8, 8 });422 ImGui::SetTooltip("%s", tip.c_str());423 ImGui::PopStyleVar();424 }425 }426 ImGui::Indent();427 //ImGui::PopStyleColor();428 return changed;429}430 431//-------------------------------------------------------------------------------432 433enum434{435 InputBindable_StretchButton = 0x1,436 InputBindable_StretchButtonDisabled = InputBindable_StretchButton | 0x2,437 InputBindable_MultilineEdit = 0x4,438 InputBindable_CustomButton = 0x8,439 InputBindable_Modified = 0x10,440 InputBindable_ShowVariables = 0x20,441 InputBindable_ShowNone = 0x40,442};443 444template <class T,445 class = std::enable_if_t<!std::is_same_v<T, dimension_t>> >446inline bool InputBindable(bindable<T>* val, const std::string& type, int flags, UIContext& ctx)447{448 if (val == ctx.setProp)449 {450 //commit dialog request451 ctx.setProp = nullptr;452 *val->access() = ctx.setPropValue;453 return true;454 }455 456 bool changed = false;457 std::string id = "##" + std::to_string((uint64_t)val);458 459 if (!(flags & InputBindable_ShowVariables))460 {461 int fl = ImGuiInputTextFlags_CallbackCharFilter;462 if constexpr (!std::is_same_v<T, std::string>) {463 if (val->has_value())464 fl |= ImGuiInputTextFlags_AutoSelectAll;465 }466 ImGui::PushFont(!ImRad::IsCurrentItemDisabled() && (flags & InputBindable_Modified) ?467 ctx.pgbFont : ctx.pgFont);468 changed = ImGui::InputText(id.c_str(), val->access(), fl, DefaultCharFilter);469 ImGui::PopFont();470 471 /*if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip) &&472 ImGui::CalcTextSize(val->c_str()).x > ImGui::GetItemRectSize().x)473 {474 ImGui::SetItemTooltip(val->c_str());475 }*/476 if (ImGui::IsItemDeactivatedAfterEdit())477 {478 //disallow empty state479 std::string v = Trim(val->c_str());480 if (v.empty())481 *val = {};482 else483 *val->access() = v;484 }485 }486 else487 {488 ImGui::PushFont(!ImRad::IsCurrentItemDisabled() && (flags & InputBindable_Modified) ?489 ctx.pgbFont : ctx.pgFont);490 if (ImGui::BeginCombo(id.c_str(), val->c_str(),491 !ImRad::IsCurrentItemDisabled() && IsHighlighted(id) ? 0 : ImGuiComboFlags_NoArrowButton))492 {493 ImGui::PopFont();494 ImGui::PushFont(ImGui::GetFont());495 496 ImGui::PushStyleColor(ImGuiCol_Text, NONE_COLOR);497 if ((flags & InputBindable_ShowNone) && ImGui::Selectable("None"))498 {499 *val->access() = "";500 changed = true;501 }502 ImGui::PopStyleColor();503 504 ImGui::PushStyleColor(ImGuiCol_Text, RENAME_COLOR);505 if (val->has_single_variable() && ImGui::Selectable("Rename..."))506 {507 auto vars = val->used_variables();508 newFieldPopup.codeGen = ctx.codeGen;509 newFieldPopup.varOldName = vars[0];510 newFieldPopup.mode = NewFieldPopup::RenameField;511 newFieldPopup.OpenPopup([val, root = ctx.root]{512 root->RenameFieldVars(newFieldPopup.varOldName, newFieldPopup.varName);513 });514 }515 ImGui::PopStyleColor();516 517 ImGui::PushStyleColor(ImGuiCol_Text, NEW_COLOR);518 if (ImGui::Selectable("New Variable..."))519 {520 newFieldPopup.varType = type;521 newFieldPopup.codeGen = ctx.codeGen;522 newFieldPopup.mode = NewFieldPopup::NewField;523 newFieldPopup.OpenPopup([&ctx, val] {524 ctx.setProp = val;525 ctx.setPropValue = newFieldPopup.varName;526 });527 }528 ImGui::PopStyleColor();529 530 ImGui::Separator();531 auto vars = ctx.codeGen->GetVarExprs(type, true, ctx.GetCurrentArray());532 /*auto* dc = ImGui::GetCurrentWindow()->DrawList;533 float sp = ImGui::CalcTextSize(" ").x;*/ 534 for (const auto& v : vars)535 {536 if (ImGui::Selectable(v.first.c_str(), v.first == val->c_str()))537 {538 *val->access() = v.first;539 changed = true;540 }541 /*ImVec2 p = ImGui::GetCursorScreenPos();542 p.x += sp;543 p.y -= 0.5f * ImGui::GetTextLineHeight();544 dc->AddCircleFilled(p, 0.2f * ImGui::GetFontSize(), 0xff000000);*/545 }546 547 ImGui::EndCombo();548 }549 ImGui::PopFont();550 }551 552 return changed;553}554 555template <class T,556 class = std::enable_if_t<!std::is_same_v<T, void> && !std::is_same_v<T, dimension_t>> >557 inline bool InputBindable(bindable<T>* val, int flags, UIContext& ctx)558{559 return InputBindable(val, typeid_name<T>(), flags, ctx);560}561 562inline bool InputBindable(bindable<font_name_t>* val, UIContext& ctx)563{564 std::string fn = val->empty() ? PARENT_STR :565 val->has_value() ? val->eval(ctx) : val->c_str();566 bool changed = false;567 std::string id = "##" + std::to_string((uint64_t)val);568 if (ImGui::BeginCombo(id.c_str(), nullptr,569 ImGuiComboFlags_CustomPreview | (IsHighlighted(id) ? 0 : ImGuiComboFlags_NoArrowButton)))570 {571 for (const auto& f : ctx.fontNames)572 {573 if (ImGui::Selectable(f == "" ? " " : f.c_str(), f == fn)) {574 changed = true;575 if (f == "")576 *val->access() = f;577 else578 val->set_font_name(f);579 }580 }581 ImGui::EndCombo();582 }583 if (ImGui::BeginComboPreview())584 {585 ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(586 val->empty() ? ImGuiCol_TextDisabled : ImGuiCol_Text));587 ImGui::PushFont(!ImRad::IsCurrentItemDisabled() && !val->empty() ?588 ctx.pgbFont : ctx.pgFont);589 ImGui::TextUnformatted(fn.c_str());590 ImGui::PopFont();591 ImGui::PopStyleColor();592 ImGui::EndComboPreview();593 }594 return changed;595}596 597inline bool InputBindable(bindable<color_t>* val, int defStyleCol, UIContext& ctx)598{599 static std::string lastColor;600 static int lastStyleClr;601 602 auto nextData = ImGui::GetCurrentContext()->NextItemData; //copy603 int styleClr = val->style_color();604 ImVec4 buttonClr = val->empty() ?605 ctx.style.Colors[defStyleCol] :606 ImGui::ColorConvertU32ToFloat4(val->eval(defStyleCol, ctx));607 std::string id = "##" + std::to_string((uint64_t)val);608 ImGui::PushStyleColor(ImGuiCol_FrameBg, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);609 if (ImGui::ColorButton(id.c_str(), buttonClr, ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview))610 {611 lastColor = val->c_str();612 lastStyleClr = styleClr;613 ImGui::OpenPopup(id.c_str());614 }615 ImGui::PopStyleColor();616 617 ImGui::SameLine();618 ImVec2 sz{ 0, 0 };619 if (nextData.HasFlags & ImGuiNextItemDataFlags_HasWidth)620 {621 sz.x = nextData.Width;622 if (sz.x < 0)623 sz.x += ImGui::GetContentRegionAvail().x;624 }625 std::string clrName = styleClr >= 0 ? ImGui::GetStyleColorName(styleClr) :626 val->empty() ? PARENT_STR :627 val->c_str();628 clrName += id;629 ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(val->empty() ? ImGuiCol_TextDisabled : ImGuiCol_Text));630 ImGui::PushFont(!ImRad::IsCurrentItemDisabled() && !val->empty() ?631 ctx.pgbFont : ctx.pgFont);632 if (ImGui::Selectable(clrName.c_str(), false, 0, sz))633 {634 lastColor = val->c_str();635 lastStyleClr = styleClr;636 ImGui::OpenPopup(id.c_str());637 }638 ImGui::PopFont();639 ImGui::PopStyleColor();640 641 static ImU32 COLORS[]{642 /* 5x5 palette643 IM_COL32(255, 255, 255, 255),644 IM_COL32(246, 246, 194, 255),645 IM_COL32(202, 23, 55, 255),646 IM_COL32(246, 58, 56, 255),647 IM_COL32(162, 34, 31, 255),648 IM_COL32(7, 17, 115, 255),649 IM_COL32(159, 215, 252, 255),650 IM_COL32(15, 63, 25, 255),651 IM_COL32(96, 231, 66, 255),652 IM_COL32(205, 255, 202, 255),653 IM_COL32(246, 253, 85, 255),654 IM_COL32(248, 117, 0, 255),655 IM_COL32(244, 180, 118, 255),656 IM_COL32(104, 102, 90, 255),657 IM_COL32(165, 166, 160, 255),658 IM_COL32(54, 8, 81, 255),659 IM_COL32(148, 35, 213, 255),660 IM_COL32(248, 181, 248, 255),661 IM_COL32(204, 22, 182, 255),662 IM_COL32(219, 190, 2, 255),663 IM_COL32(10, 226, 211, 255),664 IM_COL32(2, 251, 171, 255),665 IM_COL32(143, 105, 69, 255),666 IM_COL32(59, 33, 8, 255),667 IM_COL32(0, 0, 0, 255),*/668 669 //8x5 palette670 IM_COL32(255,255,255,255), //IM_COL32(0,0,0,255),671 IM_COL32(152,51,0,255),672 IM_COL32(51,51,0,255),673 IM_COL32(0,51,0,255),674 IM_COL32(0,51,102,255),675 IM_COL32(0,0,128,255),676 IM_COL32(51,51,153,255),677 IM_COL32(0,0,0,255),678 679 IM_COL32(128,0,0,255),680 IM_COL32(255,102,0,255),681 IM_COL32(128,128,0,255),682 IM_COL32(0,128,0,255),683 IM_COL32(0,128,128,255),684 IM_COL32(0,0,255,255),685 IM_COL32(102,102,153,255),686 IM_COL32(64,64,64,255),687 688 IM_COL32(255,0,0,255),689 IM_COL32(255,153,0,255),690 IM_COL32(153,204,0,255),691 IM_COL32(51,153,102,255),692 IM_COL32(51,204,204,255),693 IM_COL32(51,102,255,255),694 IM_COL32(128,0,128,255),695 IM_COL32(128,128,128,255),696 697 IM_COL32(254,0,255,255),698 IM_COL32(255,203,0,255),699 IM_COL32(255,255,0,255),700 IM_COL32(0,255,0,255),701 IM_COL32(0,255,254,255),702 IM_COL32(0,204,255,255),703 IM_COL32(153,51,102,255),704 IM_COL32(192,192,192,255),705 706 IM_COL32(255,153,204,255),707 IM_COL32(255,204,151,255),708 IM_COL32(255,255,153,255),709 IM_COL32(205,255,205,255),710 IM_COL32(205,255,255,255),711 IM_COL32(153,204,254,255),712 IM_COL32(204,154,255,255),713 IM_COL32(255,255,255,255),714 };715 bool changed = false;716 static std::string lastOpen;717 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 5, 5 });718 ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos() + ImVec2{ ImGui::GetContentRegionAvail().x, 0 }, ImGuiCond_Always, { 1, 0 });719 if (ImGui::BeginPopup(id.c_str(), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNav))720 {721 lastOpen = id.c_str();722 *val->access() = lastColor;723 bool autoSel = ImGui::ColorButton("tooltip", ctx.style.Colors[defStyleCol], ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview);724 bool autoHover = ImGui::IsItemHovered();725 ImGui::SameLine(0, 0);726 ImGui::Selectable(" Automatic", false, ImGuiSelectableFlags_NoPadWithHalfSpacing);727 autoHover = autoHover || ImGui::IsItemHovered();728 autoSel = autoSel || ImGui::IsItemClicked(ImGuiMouseButton_Left);729 if (autoSel)730 {731 changed = true;732 lastColor = "";733 *val = {};734 }735 if (autoHover)736 {737 *val = {};738 }739 ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0);740 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 4, 4 });741 for (int i = 0; i < stx::ssize(COLORS); ++i)742 {743 ImGui::PushID(i);744 ImGui::PushStyleColor(ImGuiCol_Button, COLORS[i]);745 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, COLORS[i]);746 //if (ImGui::ColorButton("##clr", ImGui::ColorConvertU32ToFloat4(COLORS[i]), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview, { 30, 30 }))747 if (ImGui::Button("##clr", { 30, 30 }))748 {749 changed = true;750 *val = i ? COLORS[i] : 0x00ffffff;751 lastColor = *val->access();752 }753 if (ImGui::IsItemHovered())754 {755 *val = i ? COLORS[i] : 0x00ffffff;756 }757 if (!i)758 {759 ImVec2 p1 = ImGui::GetItemRectMin();760 ImVec2 p2 = ImGui::GetItemRectMax();761 ImGui::GetWindowDrawList()->AddLine({ p1.x, p2.y }, { p2.x, p1.y }, 0xff0000ff);762 }763 ImGui::PopStyleColor(2);764 ImGui::PopID();765 if ((i + 1) % 8)766 ImGui::SameLine();767 }768 ImGui::PopStyleVar(2);769 ImGui::Spacing();770 ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);771 int clri = val->style_color();772 std::string clrName = lastStyleClr >= 0 ? ImGui::GetStyleColorName(lastStyleClr) : "Style Color...";773 if (ImGui::BeginCombo("##cmb", clrName.c_str()))774 {775 for (int i = 0; i < ImGuiCol_COUNT; ++i)776 {777 auto cc = ctx.style.Colors[i];778 auto ccc = ImGui::ColorConvertFloat4ToU32(cc);779 ImGui::ColorButton("##color", cc, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoBorder | ImGuiColorEditFlags_AlphaPreview);780 ImGui::SameLine();781 if (ImGui::Selectable(ImGui::GetStyleColorName(i), clri == i))782 {783 changed = true;784 val->set_style_color(i);785 lastColor = *val->access();786 break;787 }788 ImRect r{ ImGui::GetItemRectMin(), ImGui::GetItemRectMax() };789 r.Min.y -= ImGui::GetStyle().ItemSpacing.y;790 if (r.Contains(ImGui::GetMousePos())) //more stable than IsItemHovered791 {792 val->set_style_color(i);793 }794 }795 ImGui::EndCombo();796 }797 if (changed)798 ImGui::CloseCurrentPopup();799 ImGui::EndPopup();800 }801 else if (lastOpen == id.c_str())802 {803 lastOpen = "";804 //return last value if the current color was hovered only805 *val->access() = lastColor;806 }807 ImGui::PopStyleVar();808 809 return changed;810}811 812inline bool InputBindable(bindable<std::string>* val, int flags, UIContext& ctx, std::function<void()> fun = [] {})813{814 std::string id = "##" + std::to_string((uint64_t)val);815 std::string butId = "..." + id;816 bool high = IsHighlighted(id) || IsHighlighted(butId);817 bool hasButton = flags & (InputBindable_MultilineEdit | InputBindable_CustomButton);818 float w = ImGui::CalcItemWidth();819 if (hasButton && high)820 w -= ImGui::GetFrameHeight();821 822 ImGui::SetNextItemWidth(w);823 ImGui::PushFont(824 !IsAscii(*val->access()) ? ctx.defaultStyleFont :825 !ImRad::IsCurrentItemDisabled() ? ctx.pgbFont :826 ctx.pgFont827 );828 bool changed = ImGui::InputText(id.c_str(), val->access(), ImGuiInputTextFlags_CallbackCharFilter, DefaultCharFilter);829 ImGui::PopFont();830 831 if (hasButton && high)832 {833 ImGui::SameLine(0, 0);834 ImGui::PushItemFlag(ImGuiItemFlags_NoNav, true);835 bool isDown = false;836 if (flags & InputBindable_MultilineEdit)837 isDown = val->access()->find('\n') != std::string::npos;838 ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(839 isDown ? ImGuiCol_ButtonActive : ImGuiCol_Button840 ));841 if (ImGui::Button(butId.c_str(), { ImGui::GetFrameHeight(), ImGui::GetFrameHeight() }))842 {843 if (flags & InputBindable_CustomButton)844 {845 fun();846 }847 else if (flags & InputBindable_MultilineEdit)848 {849 comboDlg.title = "Multiline text";850 comboDlg.value = *val->access();851 comboDlg.font = ctx.defaultStyleFont;852 comboDlg.OpenPopup([&ctx, val](ImRad::ModalResult mr) {853 ctx.setProp = val;854 ctx.setPropValue = comboDlg.value;855 });856 }857 }858 ImGui::PopStyleColor();859 ImGui::PopItemFlag();860 }861 862 return changed;863}864 865inline bool InputBindable(bindable<std::vector<std::string>>* val, UIContext& ctx)866{867 bool changed = false;868 std::string id = "##" + std::to_string((uint64_t)val);869 std::string label;870 int nl = (int)stx::count(*val->access(), '\0');871 if (!val->has_single_variable())872 label = "[" + std::to_string(nl) + "]";873 else874 label = val->used_variables()[0];875 label += id;876 877 ImGui::PushFont(!ImRad::IsCurrentItemDisabled() && !val->empty() ?878 ctx.pgbFont : ctx.pgFont);879 /*float w = ImGui::CalcItemWidth();880 ImGui::SetNextItemWidth(w - ImGui::GetFrameHeight());881 882 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);883 ImRad::Selectable(label.c_str(), false, 0, { -2 * ImGui::GetFrameHeight(), 0 });884 ImGui::PopItemFlag();885 ImGui::PopFont();886 887 bool high = IsHighlighted(label) || IsHighlighted(id);888 ImGui::SameLine(0, 0);889 ImGui::SetNextItemWidth(-ImGui::GetFrameHeight());890 ImGui::SetNextWindowSizeConstraints({ w, 0 }, { FLT_MAX, FLT_MAX });891 if (high && ImGui::BeginCombo(id.c_str(), nullptr, ImGuiComboFlags_NoPreview))*/892 if (ImGui::BeginCombo(id.c_str(), label.c_str(),893 IsHighlighted(id) ? 0 : ImGuiComboFlags_NoArrowButton))894 {895 ImGui::PopFont();896 ImGui::PushFont(ImGui::GetFont());897 898 ImGui::PushStyleColor(ImGuiCol_Text, RENAME_COLOR);899 if (ImGui::Selectable("Edit Items..."))900 {901 changed = true;902 std::string tmp = *val->access(); //preserve embeded nulls903 stx::replace(tmp, '\0', '\n');904 if (tmp.size() && tmp.back() == '\n')905 tmp.pop_back();906 comboDlg.title = "Items";907 comboDlg.value = tmp;908 comboDlg.font = ctx.defaultStyleFont;909 comboDlg.OpenPopup([val](ImRad::ModalResult) {910 std::string tmp = comboDlg.value;911 while (tmp.size() && tmp.back() == '\n')912 tmp.pop_back();913 *val->access() = tmp;914 if (!val->has_single_variable()) {915 if (tmp.size() && tmp.back() != '\n')916 tmp.push_back('\n');917 stx::replace(tmp, '\n', '\0');918 *val->access() = tmp;919 }920 });921 }922 ImGui::PopStyleColor();923 924 ImGui::PushStyleColor(ImGuiCol_Text, NEW_COLOR);925 if (ImGui::Selectable("New Variable..."))926 {927 newFieldPopup.varType = "std::vector<std::string>";928 newFieldPopup.codeGen = ctx.codeGen;929 newFieldPopup.mode = NewFieldPopup::NewField;930 newFieldPopup.OpenPopup([val, &ctx] {931 *val->access() = '{' + newFieldPopup.varName + '}';932 //todo933 //ctx.setProp = &items;934 //ctx.setPropValue = '{' + newFieldPopup.varName + '}';935 });936 }937 ImGui::PopStyleColor();938 939 ImGui::Separator();940 const auto& vars = ctx.codeGen->GetVarExprs("std::vector<std::string>", true, ctx.GetCurrentArray());941 for (const auto& v : vars)942 {943 if (ImGui::Selectable(v.first.c_str(), '{' + v.first + '}' == val->c_str()))944 {945 *val->access() = '{' + v.first + '}';946 changed = true;947 }948 }949 ImGui::EndCombo();950 }951 952 ImGui::PopFont();953 return changed;954}955 956inline bool InputBindable(bindable<dimension_t>* val, int flags, UIContext& ctx)957{958 if (val == ctx.setProp)959 {960 //commit dialog request961 ctx.setProp = nullptr;962 if (ctx.setPropValue == "")963 *val = 0;964 else965 *val->access() = ctx.setPropValue;966 return true;967 }968 969 std::string id = "##" + std::to_string((uint64_t)val);970 std::string butId = ICON_FA_LEFT_RIGHT + id;971 if (flags & InputBindable_StretchButton)972 ImGui::SetNextItemWidth(ImGui::CalcItemWidth() - ImGui::GetFrameHeight());973 974 bool stretch = val->stretched();975 ImGui::PushFont(!ImRad::IsCurrentItemDisabled() && (flags & InputBindable_Modified) ?976 ctx.pgbFont : ctx.pgFont);977 int fl = ImGuiInputTextFlags_CallbackCharFilter;978 if (val->has_value())979 fl |= ImGuiInputTextFlags_AutoSelectAll;980 bool changed = ImGui::InputText(id.c_str(), val->access(), fl, DefaultCharFilter);981 /*std::string tmp = *val->access();982 bool active = ImGui::GetFocusID() == ImGui::GetID(id.c_str()) ||983 ImGui::GetActiveID() == ImGui::GetID(id.c_str());984 if (!active)985 tmp = val->to_arg("");986 bool changed = ImGui::InputText(id.c_str(), &tmp, fl, DefaultCharFilter);987 if (changed) {988 if (stretch && tmp.size() && tmp.back() == 'x')989 val->set_from_arg(tmp);990 else991 *val->access() = tmp;992 }*/993 ImGui::PopFont();994 995 //disallow empty state996 if (ImGui::IsItemDeactivatedAfterEdit())997 {998 if (!stretch && val->empty())999 *val = 0;1000 else if (stretch && !val->has_value()) {1001 *val = 1.0f;1002 val->stretch(true);1003 }1004 }1005 1006 if (flags & InputBindable_StretchButton)1007 {1008 ImGui::SameLine(0, 0);1009 if (IsHighlighted(id) || IsHighlighted(butId))1010 {1011 ImGui::PushFont(ImGui::GetDefaultFont());1012 ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyle().Colors[stretch ? ImGuiCol_ButtonActive : ImGuiCol_Button]);1013 ImGui::BeginDisabled((flags & InputBindable_StretchButtonDisabled) == InputBindable_StretchButtonDisabled);1014 if (ImGui::Button(butId.c_str(), { ImGui::GetFrameHeight(), ImGui::GetFrameHeight() }))1015 {1016 changed = true;1017 stretch = !stretch;1018 *val = stretch ? 1.0f : 0.f;1019 val->stretch(stretch);1020 };1021 ImGui::EndDisabled();1022 ImGui::PopStyleColor();1023 ImGui::PopFont();1024 }1025 else1026 {1027 ImGui::InvisibleButton(butId.c_str(), { ImGui::GetFrameHeight(), ImGui::GetFrameHeight() });1028 }1029 }1030 1031 return changed;1032}1033 1034template <class T>1035inline bool InputFieldRef(field_ref<T>* val, const std::string& type, bool allowEmpty, UIContext& ctx)1036{1037 if (val == ctx.setProp)1038 {1039 //commit dialog request1040 ctx.setProp = nullptr;1041 *val->access() = ctx.setPropValue;1042 return true;1043 }1044 1045 bool changed = false;1046 std::string id = "##" + std::to_string((uint64_t)val);1047 ImGui::PushFont(ctx.pgbFont);1048 if (ImGui::BeginCombo(id.c_str(), val->c_str(),1049 !ImRad::IsCurrentItemDisabled() && IsHighlighted(id) ? 0 : ImGuiComboFlags_NoArrowButton))1050 {1051 ImGui::PopFont();1052 ImGui::PushFont(ImGui::GetFont());1053 1054 ImGui::PushStyleColor(ImGuiCol_Text, NONE_COLOR);1055 if (allowEmpty && ImGui::Selectable("None"))1056 {1057 *val->access() = "";1058 changed = true;1059 }1060 ImGui::PopStyleColor();1061 1062 ImGui::PushStyleColor(ImGuiCol_Text, RENAME_COLOR);1063 if (val->has_single_variable() && ImGui::Selectable("Rename..."))1064 {1065 auto vars = val->used_variables();1066 assert(vars.size() == 1);1067 newFieldPopup.codeGen = ctx.codeGen;1068 newFieldPopup.varOldName = vars[0];1069 newFieldPopup.mode = NewFieldPopup::RenameField;1070 newFieldPopup.OpenPopup([val, root=ctx.root] {1071 root->RenameFieldVars(newFieldPopup.varOldName, newFieldPopup.varName);1072 });1073 }1074 ImGui::PopStyleColor();1075 1076 ImGui::PushStyleColor(ImGuiCol_Text, NEW_COLOR);1077 if (ImGui::Selectable("New Variable..."))1078 {1079 newFieldPopup.varType = type;1080 newFieldPopup.codeGen = ctx.codeGen;1081 newFieldPopup.mode = NewFieldPopup::NewField;1082 newFieldPopup.OpenPopup([&ctx, val] {1083 ctx.setProp = val;1084 ctx.setPropValue = newFieldPopup.varName;1085 });1086 }1087 ImGui::PopStyleColor();1088 1089 ImGui::Separator();1090 auto vars = ctx.codeGen->GetVarExprs(type, true, ctx.GetCurrentArray());1091 for (const auto& v : vars)1092 {1093 if (ImGui::Selectable(v.first.c_str(), v.first == val->c_str()))1094 {1095 *val->access() = v.first;1096 changed = true;1097 }1098 }1099 1100 ImGui::EndCombo();1101 }1102 ImGui::PopFont();1103 return changed;1104}1105 1106template <class T>1107inline bool InputFieldRef(field_ref<T>* val, bool allowEmpty, UIContext& ctx)1108{1109 std::string type = typeid_name<T>();1110 return InputFieldRef(val, type, allowEmpty, ctx);1111}1112 1113template <>1114inline bool InputFieldRef(field_ref<void>* val, bool allowEmpty, UIContext& ctx) = delete;1115 1116 1117inline bool InputDataSize(bindable<int>* val, bool allowEmpty, UIContext& ctx)1118{1119 if (val == ctx.setProp)1120 {1121 //commit dialog request1122 ctx.setProp = nullptr;1123 *val->access() = ctx.setPropValue;1124 return true;1125 }1126 1127 bool changed = false;1128 std::string id = "##" + std::to_string((uint64_t)val);1129 ImGui::PushFont(ctx.pgbFont);1130 1131 if (ImGui::BeginCombo(id.c_str(), val->c_str(),1132 IsHighlighted(id) ? 0 : ImGuiComboFlags_NoArrowButton))1133 {1134 ImGui::PopFont();1135 ImGui::PushFont(ImGui::GetFont());1136 1137 ImGui::PushStyleColor(ImGuiCol_Text, NONE_COLOR);1138 if (allowEmpty && ImGui::Selectable("None"))1139 {1140 *val->access() = "";1141 changed = true;1142 }1143 ImGui::PopStyleColor();1144 1145 /* it's confusing to offer new variable here. User can still do it from BindingButton1146 ImGui::PushStyleColor(ImGuiCol_Text, NEW_COLOR);1147 if (ImGui::Selectable("New Variable..."))1148 {1149 newFieldPopup.varType = ""; //allow to create std::vector etc.1150 newFieldPopup.codeGen = ctx.codeGen;1151 newFieldPopup.mode = NewFieldPopup::NewField;1152 newFieldPopup.OpenPopup([&ctx, val] {1153 ctx.setProp = val;1154 if (cpp::is_std_container(newFieldPopup.varType))1155 ctx.setPropValue = newFieldPopup.varName + ".size()";1156 else1157 ctx.setPropValue = newFieldPopup.varName;1158 });1159 }1160 ImGui::PopStyleColor();1161 */1162 1163 ImGui::Separator();1164 const auto& vars = ctx.codeGen->GetVarExprs("int", false);1165 for (const auto& v : vars)1166 {1167 if (ImGui::Selectable(v.first.c_str(), v.first == val->c_str()))1168 {1169 *val->access() = v.first;1170 changed = true;1171 }1172 }1173 1174 ImGui::EndCombo();1175 }1176 ImGui::PopFont();1177 return changed;1178}1179 1180template <class FuncSig>1181inline bool InputEvent(const std::string& name, event<FuncSig>* val, int flags, UIContext& ctx)1182{1183 if (val == ctx.setProp)1184 {1185 //commit dialog request1186 ctx.setProp = nullptr;1187 *val->access() = ctx.setPropValue;1188 return true;1189 }1190 1191 bool changed = false;1192 std::string type = typeid_name<FuncSig>();1193 std::string id = "##" + std::to_string((uint64_t)val);1194 std::string inputId = id + "Input";1195 bool buttonVisible = !ImRad::IsCurrentItemDisabled() && (IsHighlighted(inputId) || IsHighlighted(id));1196 float width = ImGui::CalcItemWidth();1197 if (buttonVisible)1198 width -= ImGui::GetFrameHeight();1199 float realWidth = ImGui::CalcItemSize({ width, 0 }, 0, 0).x;1200 ImGui::SetNextItemWidth(width);