dlxjj/imradv3
0263
1#if WIN322#define WIN32_LEAN_AND_MEAN3#include <Windows.h>4#undef min5#undef max6#undef MessageBox7#endif8#if defined(__APPLE__)9#include <mach-o/dyld.h>10#endif11#include <imgui.h>12#include <imgui_internal.h>13#include <misc/cpp/imgui_stdlib.h>14#include <IconsFontAwesome6.h>15//#include <misc/fonts/IconsMaterialDesign.h>16#include <backends/imgui_impl_glfw.h>17#include <backends/imgui_impl_opengl3.h>18#include <iostream>19#include <fstream>20#include <string>21#if defined(IMGUI_IMPL_OPENGL_ES2)22#include <GLES2/gl2.h>23#endif24#include <GLFW/glfw3.h> // Will drag system OpenGL headers25#include <nfd.h>26 27#include "glfw_cursor.h"28#include "node_standard.h"29#include "cppgen.h"30#include "utils.h"31#include "ui_new_field.h"32#include "ui_message_box.h"33#include "ui_error_box.h"34#include "ui_class_wizard.h"35#include "ui_table_cols.h"36#include "ui_about_dlg.h"37#include "ui_binding.h"38#include "ui_combo_dlg.h"39#include "ui_horiz_layout.h"40#include "ui_input_name.h"41#include "ui_settings_dlg.h"42#include "ui_explorer.h"43 44#include "SimpleLayout.h"45 46#include "ImGuiColorTextEdit/TextEditor.h"47#include "nlohmann_json/json.hpp"48#include "MD5/quickdigest5.h"49#include "Constant.h"50#include "global.h"51 52using json = nlohmann::json;53 54//must come last55#define STB_IMAGE_IMPLEMENTATION56#include <stb_image.h>57 58#include <d3d11.h>59 60//#pragma comment(lib, "E:\\huggingface\\ColorTextEditorV2\\build\\bin\\opencv_core4120.lib")61//#pragma comment(lib, "E:\\huggingface\\ColorTextEditorV2\\build\\bin\\opencv_highgui4120.lib")62 63std::filesystem::path g_currPath;64HINSTANCE g_wechatocr_dll = NULL;65typedef const char* (_cdecl* FunctionPtr) (const char* pth_img);66FunctionPtr rec;67 68std::pair<std::filesystem::path, std::filesystem::path> get_img_json_pth(const std::string& m5) {69#ifdef _DEBUG70 // 这是工作目录71 auto pth_img = g_currPath.parent_path().parent_path() / std::filesystem::path("data/img") / std::format("{}.txt", m5);72 auto pth_json = g_currPath.parent_path().parent_path() / std::filesystem::path("data/json") / std::format("{}.json", m5);73#else74 // 这是工作目录75 auto pth_img = g_currPath / std::filesystem::path("data/img") / std::format("{}.txt", m5); // .parent_path().parent_path()76 auto pth_json = g_currPath / std::filesystem::path("data/json") / std::format("{}.json", m5); // .parent_path().parent_path()77 //// 这是可执行文件所在目录 78 //auto pth_img = g_currPath / std::filesystem::path("data/img") / std::format("{}.txt", m5);79 //auto pth_json = g_currPath / std::filesystem::path("data/json") / std::format("{}.json", m5);80#endif // DEBUG81 82 pth_img = pth_img.lexically_normal();83 pth_json = pth_json.lexically_normal();84 85 std::pair<std::filesystem::path, std::filesystem::path> pth_pair{ pth_img, pth_json };86 87 return pth_pair;88}89 90 91std::string readFileToString(const std::string& filePath) {92 std::ifstream file(filePath, std::ios::binary);93 if (!file) return "";94 std::ostringstream buffer;95 buffer << file.rdbuf(); // 将文件流缓冲区内容写入字符串流96 return buffer.str();97}98 99static void glfw_error_callback(int error, const char* description)100{101 std::cerr << "Glfw Error: " << description;102}103 104const float TB_SIZE = 40;105const float TAB_SIZE = 25; //todo examine106const std::string UNTITLED = "Untitled";107const std::string DEFAULT_STYLE = "Dark";108const std::string DEFAULT_UNIT = "px";109const char* INI_FILE_NAME = "imgui.ini";110 111struct File112{113 std::string fname;114 CppGen codeGen;115 std::unique_ptr<TopWindow> rootNode;116 bool modified = false;117 fs::file_time_type time[2];118 std::string styleName;119 std::string unit;120};121 122enum ProgramState { Run, Init, Shutdown };123ProgramState programState;124std::string rootPath;125std::string initErrors, showError;126std::string uiFontName = "Roboto-Regular.ttf";127std::string pgFontName = "Roboto-Regular.ttf";128std::string pgbFontName = "Roboto-Bold.ttf";129float uiFontSize = 19;130float pgFontSize = 20;131UIContext ctx;132std::unique_ptr<Widget> newNode;133std::vector<File> fileTabs;134int activeTab = -1;135std::vector<std::pair<std::string, std::string>> styleNames; //name, path136std::string styleName;137bool reloadStyle = true;138GLFWwindow* window = nullptr;139int addInputCharacter = 0;140std::string lastPropName;141std::string activeButton = "";142std::vector<std::unique_ptr<Widget>> clipboard;143GLFWcursor* curCross = nullptr;144GLFWcursor* curWait = nullptr;145float pgHeight = 0, pgeHeight = 0;146ImRad::IOUserData ioUserData;147 148struct TB_Button149{150 std::string label;151 std::string name;152};153std::vector<std::pair<std::string, std::vector<TB_Button>>> tbButtons{154 { "Standard", {155 { ICON_FA_ARROW_POINTER, "" },156 { ICON_FA_FONT, "Text" },157 { ICON_FA_AUDIO_DESCRIPTION, "Selectable" },158 { ICON_FA_CIRCLE_PLAY, "Button" }, //ICON_FA_SQUARE_PLUS159 { "[a_]", "Input" }, //ICON_FA_CLOSED_CAPTIONING160 { ICON_FA_SQUARE_CHECK, "CheckBox" },161 { ICON_FA_CIRCLE_DOT, "RadioButton" },162 { ICON_FA_SQUARE_CARET_DOWN, "Combo" },163 { ICON_FA_SLIDERS, "Slider" },164 { ICON_FA_CIRCLE_HALF_STROKE, "ColorEdit" },165 { ICON_FA_BATTERY_HALF, "ProgressBar" },166 { ICON_FA_IMAGE, "Image" },167 { ICON_FA_LEFT_RIGHT, "Spacer" },168 { ICON_FA_MINUS, "Separator" },169 { ICON_FA_EXPAND, "CustomWidget" },170 }},171 { "Containers", {172 { ICON_FA_SQUARE_FULL, "Child" },173 { ICON_FA_TABLE_CELLS_LARGE, "Table" },174 { ICON_FA_ARROW_DOWN_WIDE_SHORT, "CollapsingHeader" },175 { ICON_FA_FOLDER_CLOSED, "TabBar" },176 { ICON_FA_SITEMAP, "TreeNode" },177 { ICON_FA_TABLE_COLUMNS, "Splitter" }, //ICON_FA_ARROWS_LEFT_RIGHT_TO_LINE178 //{ ICON_FA_CLAPPERBOARD /*ELLIPSIS*/, "MenuBar" },179 { ICON_FA_TABLE, "DockSpace" },180 { ICON_FA_MESSAGE /*RECEIPT*/, "ContextMenu" },181 }},182};183 184//CancelShutdown identifier is used in winuser.h185void DoCancelShutdown()186{187 if (programState != Shutdown)188 return;189 190 glfwSetWindowShouldClose(window, false);191 programState = Run;192 ImGui::GetIO().IniFilename = INI_FILE_NAME;193}194 195void DoReloadFile()196{197 if (activeTab < 0)198 return;199 auto& tab = fileTabs[activeTab];200 if (tab.fname == "" || !fs::is_regular_file(u8path(tab.fname)))201 return;202 203 std::map<std::string, std::string> params;204 std::string error;205 tab.rootNode = tab.codeGen.Import(tab.fname, params, error);206 auto pit = params.find("style");207 tab.styleName = pit == params.end() ? DEFAULT_STYLE : pit->second;208 pit = params.find("unit");209 tab.unit = pit == params.end() ? DEFAULT_UNIT : pit->second;210 bool styleFound = stx::count_if(styleNames, [&](const auto& st) {211 return st.first == tab.styleName;212 });213 if (!styleFound) {214 error = "Unknown style \"" + tab.styleName + "\" used\n" + error;215 tab.styleName = DEFAULT_STYLE;216 }217 tab.modified = false;218 ctx.mode = UIContext::NormalSelection;219 ctx.selected = { tab.rootNode.get() };220 221 if (error != "" && programState != Shutdown)222 {223 errorBox.title = "Reload";224 errorBox.message = "Errors occured";225 errorBox.error = error;226 errorBox.OpenPopup();227 }228}229 230void ReloadFile()231{232 if (activeTab < 0)233 return;234 auto& tab = fileTabs[activeTab];235 if (tab.fname == "" || !fs::is_regular_file(u8path(tab.fname)))236 return;237 auto time1 = fs::last_write_time(u8path(tab.fname));238 std::error_code err;239 auto time2 = fs::last_write_time(u8path(tab.codeGen.AltFName(tab.fname)), err);240 if (time1 == tab.time[0] && time2 == tab.time[1])241 return;242 tab.time[0] = time1;243 tab.time[1] = time2;244 245 if (programState != Shutdown)246 {247 std::string fn = u8string(u8path(tab.fname).filename());248 messageBox.title = "Reload";249 messageBox.message = "File content of '" + fn + "' has changed. Reload?";250 messageBox.buttons = ImRad::Yes | ImRad::No;251 252 messageBox.OpenPopup([&](ImRad::ModalResult mr) {253 if (mr == ImRad::Yes)254 DoReloadFile();255 });256 }257 else {258 DoReloadFile();259 }260}261 262void ActivateTab(int i)263{264 /*doesn't work when activeTab is closed265 std::string lastStyle;266 if (activeTab >= 0)267 lastStyle = fileTabs[activeTab].styleName;*/268 if (i >= fileTabs.size())269 i = (int)fileTabs.size() - 1;270 if (i < 0) {271 activeTab = -1;272 ctx.selected.clear();273 ctx.codeGen = nullptr;274 return;275 }276 activeTab = i;277 auto& tab = fileTabs[i];278 ctx.selected = { tab.rootNode.get() };279 ctx.codeGen = &tab.codeGen;280 ReloadFile();281 282 if (fileTabs[activeTab].styleName != styleName)283 reloadStyle = true;284}285 286void NewFile(TopWindow::Kind k)287{288 ctx.kind = k;289 auto top = std::make_unique<TopWindow>(ctx);290 File file;291 file.rootNode = std::move(top);292 file.styleName = DEFAULT_STYLE;293 file.unit = k == TopWindow::Activity ? "dp" : DEFAULT_UNIT;294 file.modified = true;295 fileTabs.push_back(std::move(file));296 ActivateTab((int)fileTabs.size() - 1);297}298 299void CopyFileReplace(const std::string& from, const std::string& to, std::vector<std::pair<std::string, std::string>>& repl)300{301 std::ifstream fin(u8path(from));302 if (!fin)303 throw std::runtime_error("can't read from " + from);304 std::ofstream fout(u8path(to));305 if (!fout)306 throw std::runtime_error("can't write to " + to);307 308 std::string line;309 while (std::getline(fin, line))310 {311 size_t i = 0;312 while (true)313 {314 i = line.find("${", i);315 if (i == std::string::npos)316 break;317 size_t j = line.find("}", i);318 if (j == std::string::npos)319 break;320 for (const auto& r : repl)321 if (!line.compare(i + 2, j - i - 2, r.first)) {322 line.replace(line.begin() + i, line.begin() + j + 1, r.second);323 j = i + r.second.size();324 break;325 }326 i = j;327 }328 fout << line << "\n";329 }330}331 332void DoNewTemplate(int type, const std::string& name)333{334 nfdchar_t *outPath = NULL;335 nfdfilteritem_t filterItem[1] = { { (const nfdchar_t *)"Source code", (const nfdchar_t *)"cpp" } };336 nfdresult_t result = NFD_SaveDialog(&outPath, filterItem, 1, nullptr, "main.cpp");337 if (result != NFD_OKAY)338 return;339 fs::path p = u8path(outPath);340 NFD_FreePath(outPath);341 if (!p.has_extension())342 p.replace_extension(".cpp");343 344 try {345 switch (type)346 {347 case 0:348 fs::copy_file(u8path(rootPath + "/template/glfw/main.cpp"), p, fs::copy_options::overwrite_existing);349 break;350 case 1: {351 std::string jni = name;352 stx::replace(jni, '.', '_');353 if (name.find('.') == std::string::npos)354 throw std::runtime_error("invalid package name");355 std::string lib = name.substr(name.rfind('.') + 1);356 357 std::vector<std::pair<std::string, std::string>> repl{358 { "JAVA_PACKAGE", name },359 { "JNI_PACKAGE", jni },360 { "LIB_NAME", lib },361 { "IMRAD_INCLUDE", rootPath + "/include" },362 };363 CopyFileReplace(rootPath + "/template/android/main.cpp", u8string(p), repl);364 CopyFileReplace(rootPath + "/template/android/CMakeLists.txt", u8string(p.replace_filename("CMakeLists.txt")), repl);365 CopyFileReplace(rootPath + "/template/android/MainActivity.java", u8string(p.replace_filename("MainActivity.java")), repl);366 CopyFileReplace(rootPath + "/template/android/AndroidManifest.xml", u8string(p.replace_filename("AndroidManifest.xml")), repl);367 break;368 }369 }370 }371 catch (std::exception& e) {372 messageBox.title = "error";373 messageBox.message = e.what();374 messageBox.buttons = ImRad::Ok;375 messageBox.OpenPopup();376 }377}378 379void NewTemplate(int type)380{381 if (type == 1)382 {383 //android template384 inputName.title = "Java package name";385 inputName.hint = "com.example.test";386 inputName.OpenPopup([type](ImRad::ModalResult mr) {387 DoNewTemplate(type, inputName.name);388 });389 }390 else391 DoNewTemplate(type, "");392}393 394bool DoOpenFile(const std::string& path, std::string* errs = nullptr)395{396 File file;397 file.fname = path;398 if (u8string(u8path(file.fname).extension()).compare(0, 2, ".h")) {399 file.fname = file.codeGen.AltFName(file.fname);400 }401 if (!fs::is_regular_file(u8path(path))) {402 if (errs)403 *errs += "Can't read '" + path + "'\n";404 else {405 messageBox.title = "ImRAD";406 messageBox.message = "Can't read '" + path + "'";407 messageBox.buttons = ImRad::Ok;408 messageBox.OpenPopup();409 }410 return false;411 }412 413 std::error_code err;414 file.time[0] = fs::last_write_time(u8path(file.fname), err);415 file.time[1] = fs::last_write_time(u8path(file.codeGen.AltFName(file.fname)), err);416 std::map<std::string, std::string> params;417 std::string error;418 file.rootNode = file.codeGen.Import(file.fname, params, error);419 auto pit = params.find("style");420 file.styleName = pit == params.end() ? DEFAULT_STYLE : pit->second;421 pit = params.find("unit");422 file.unit = pit == params.end() ? DEFAULT_UNIT : pit->second;423 if (!file.rootNode) {424 if (errs)425 *errs += "Unsuccessful import of '" + path + "'\n";426 else {427 errorBox.title = "CodeGen";428 errorBox.message = "Unsuccessful import because of errors";429 errorBox.error = error;430 errorBox.OpenPopup();431 }432 return false;433 }434 435 bool styleFound = stx::count_if(styleNames, [&](const auto& st) {436 return st.first == file.styleName;437 });438 if (!styleFound) {439 if (errs)440 *errs += "Uknown style \"" + file.styleName + "\" used\n";441 else442 error = "Unknown style \"" + file.styleName + "\" used\n" + error;443 file.styleName = DEFAULT_STYLE;444 }445 446 auto it = stx::find_if(fileTabs, [&](const File& f) { return f.fname == file.fname; });447 if (it == fileTabs.end()) {448 fileTabs.push_back({});449 it = fileTabs.begin() + fileTabs.size() - 1;450 }451 int idx = int(it - fileTabs.begin());452 fileTabs[idx] = std::move(file);453 ActivateTab(idx);454 455 if (error != "") {456 if (errs)457 *errs += error;458 else {459 errorBox.title = "CodeGen";460 errorBox.message = "Import finished with errors";461 errorBox.error = error;462 errorBox.OpenPopup();463 }464 }465 return true;466}467 468void OpenFile()469{470 nfdchar_t *outPath = NULL;471 nfdfilteritem_t filterItem[1] = { { "Headers", "h,hpp" } };472 nfdresult_t result = NFD_OpenDialog(&outPath, filterItem, 1, nullptr);473 if (result != NFD_OKAY)474 return;475 476 ctx.mode = UIContext::NormalSelection;477 ctx.selected.clear();478 auto it = stx::find_if(fileTabs, [&](const File& f) { return f.fname == outPath; });479 if (it != fileTabs.end())480 {481 ActivateTab(int(it - fileTabs.begin()));482 if (it->modified) {483 messageBox.title = "Open File";484 messageBox.message = "Reload and lose unsaved changes?";485 messageBox.buttons = ImRad::Yes | ImRad::No;486 messageBox.OpenPopup([=](ImRad::ModalResult mr) {487 if (mr == ImRad::Yes)488 DoOpenFile(outPath);489 });490 }491 else {492 DoOpenFile(outPath);493 }494 }495 else {496 DoOpenFile(outPath);497 }498 NFD_FreePath(outPath);499}500 501enum {502 CLOSE_ACTIVE = 0x1,503 CLOSE_ALL = 0x2,504 CLOSE_ALL_BUT_PREVIOUS = 0x4,505};506void CloseFile(int flags = CLOSE_ACTIVE);507bool SaveFile(int flags = 0);508bool SaveFileAs(int flags = 0);509 510void DoCloseFile(int flags)511{512 if (activeTab < 0)513 return;514 515 ctx.root = nullptr;516 ctx.selected.clear();517 fileTabs.erase(fileTabs.begin() + activeTab);518 519 if ((flags & CLOSE_ALL_BUT_PREVIOUS) && fileTabs.size() >= 2) {520 ActivateTab(activeTab >= fileTabs.size() ? 0 : activeTab); //cycle from first tab521 CloseFile(flags);522 }523 else if (flags & CLOSE_ALL) {524 ActivateTab(activeTab);525 CloseFile(flags);526 }527 else {528 ActivateTab(activeTab);529 }530}531 532void CloseFile(int flags)533{534 if (activeTab < 0)535 return;536 if (fileTabs[activeTab].modified) {537 messageBox.title = "Confirmation";538 std::string fname = u8string(u8path(fileTabs[activeTab].fname).filename());539 if (fname.empty())540 fname = UNTITLED;541 messageBox.message = "Save changes to " + fname + "?";542 messageBox.buttons = ImRad::Yes | ImRad::No | ImRad::Cancel;543 messageBox.OpenPopup([=](ImRad::ModalResult mr) {544 if (mr == ImRad::Yes)545 SaveFile(flags);546 else if (mr == ImRad::No)547 DoCloseFile(flags);548 else549 DoCancelShutdown();550 });551 }552 else {553 DoCloseFile(flags);554 }555}556 557void DoSaveFile(int flags)558{559 auto& tab = fileTabs[activeTab];560 std::map<std::string, std::string> params{561 { "style", tab.styleName },562 { "unit", tab.unit },563 };564 std::string error;565 if (!tab.codeGen.ExportUpdate(tab.fname, tab.rootNode.get(), params, error))566 {567 DoCancelShutdown();568 errorBox.title = "CodeGen";569 errorBox.message = "Unsuccessful export due to errors";570 errorBox.error = error;571 errorBox.OpenPopup();572 return;573 }574 575 std::error_code err;576 tab.modified = false;577 tab.time[0] = fs::last_write_time(u8path(tab.fname), err);578 tab.time[1] = fs::last_write_time(u8path(tab.codeGen.AltFName(tab.fname)), err);579 if (error != "" && programState != Shutdown)580 {581 errorBox.title = "CodeGen";582 errorBox.message = "Export finished with errors";583 errorBox.error = error;584 errorBox.OpenPopup([=](ImRad::ModalResult) {585 if (flags)586 DoCloseFile(flags);587 });588 return;589 }590 591 if (flags)592 DoCloseFile(flags);593}594 595bool SaveFileAs(int flags)596{597 nfdchar_t *outPath = NULL;598 nfdfilteritem_t filterItem[1] = { { (const nfdchar_t *)"Header File", (const nfdchar_t *)"h,hpp" } };599 nfdresult_t result = NFD_SaveDialog(&outPath, filterItem, 1, nullptr, nullptr);600 if (result != NFD_OKAY) {601 DoCancelShutdown();602 return false;603 }604 auto& tab = fileTabs[activeTab];605 fs::path oldName = u8path(tab.fname);606 fs::path newName = u8path(outPath).replace_extension(".h");607 NFD_FreePath(outPath);608 if (newName == oldName) {609 DoSaveFile(flags);610 return true;611 }612 613 try {614 if (oldName != "") {615 //copy files first so CppGen can parse it and preserve user content as with Save616 fs::copy_file(oldName, newName, fs::copy_options::overwrite_existing);617 fs::copy_file(618 oldName.replace_extension(".cpp"),619 newName.replace_extension(".cpp"),620 fs::copy_options::overwrite_existing);621 }622 else {623 fs::remove(newName);624 fs::remove(newName.replace_extension(".cpp"));625 }626 }627 catch (std::exception& e)628 {629 DoCancelShutdown();630 messageBox.title = "error";631 messageBox.message = e.what();632 messageBox.buttons = ImRad::Ok;633 messageBox.OpenPopup();634 return false;635 }636 637 if (oldName == "") {638 tab.codeGen.SetNamesFromId(u8string(newName.stem()));639 tab.fname = u8string(newName.replace_extension(".h"));640 DoSaveFile(flags);641 }642 else {643 messageBox.title = "codegen";644 messageBox.message = "Rename window class?";645 messageBox.buttons = ImRad::Yes | ImRad::No | ImRad::Cancel;646 messageBox.OpenPopup([&tab,newName,flags](ImRad::ModalResult mr) mutable {647 if (mr == ImRad::ModalResult::Cancel)648 return;649 if (mr == ImRad::ModalResult::Yes)650 tab.codeGen.SetNamesFromId(u8string(newName.stem()));651 tab.fname = u8string(newName.replace_extension(".h"));652 DoSaveFile(flags);653 });654 }655 return true;656}657 658bool SaveFile(int flags)659{660 auto& tab = fileTabs[activeTab];661 if (tab.fname == "") {662 return SaveFileAs(flags);663 }664 else {665 DoSaveFile(flags);666 return true;667 }668}669 670//todo: dangerous because of no reloading671void SaveAll()672{673 int tmp = activeTab;674 for (activeTab = 0; activeTab < fileTabs.size(); ++activeTab)675 {676 if (!SaveFile())677 break;678 }679 activeTab = tmp;680}681 682void ShowCode()683{684 if (activeTab < 0)685 return;686 fs::path path = fs::temp_directory_path() / "imrad-preview.cpp";687 std::ofstream fout(path);688 fout << "// NOTE: This is just a preview of the Draw() method. To see the complete code\n"689 << "// including class definition and event handlers inspect generated .h/cpp files\n\n";690 ctx.ind = "";691 auto* root = fileTabs[activeTab].rootNode.get();692 root->Export(fout, ctx);693 694 if (ctx.errors.size()) {695 fout << "\n// Export finished with errors:\n";696 for (const std::string& e : ctx.errors)697 fout << "// " << e << "\n";698 }699 fout.close();700 ShellExec(u8string(path));701}702 703void NewWidget(const std::string& name)704{705 if (name == "")706 {707 activeButton = "";708 ctx.selected.clear();709 ctx.mode = UIContext::NormalSelection;710 }711 else if (name == "MenuBar")712 {713 if (ctx.root->children.empty() ||714 !dynamic_cast<MenuBar*>(ctx.root->children[0].get()))715 {716 dynamic_cast<TopWindow*>(ctx.root)->flags |= ImGuiWindowFlags_MenuBar;717 ctx.root->children.insert(ctx.root->children.begin(), std::make_unique<MenuBar>(ctx));718 ctx.selected = { ctx.root->children[0]->children[0].get() };719 }720 ctx.mode = UIContext::NormalSelection;721 }722 else if (name == "ContextMenu")723 {724 activeButton = "";725 auto popup = std::make_unique<ContextMenu>(ctx);726 auto item = std::make_unique<MenuIt>(ctx);727 item->label = "Item";728 popup->children.push_back(std::move(item));729 size_t i = 0;730 for (; i < ctx.root->children.size(); ++i)731 {732 if (ctx.root->children[i]->Behavior() & Widget::SnapSides)733 break;734 }735 popup->label = "ContextMenu" + std::to_string(i + 1);736 ctx.root->children.insert(ctx.root->children.begin() + i, std::move(popup));737 ctx.mode = UIContext::NormalSelection;738 ctx.selected = { ctx.root->children[i]->children[0].get() };739 }740 else741 {742 activeButton = name;743 ctx.selected.clear();744 ctx.mode = UIContext::SnapInsert;745 newNode = Widget::Create(name, ctx);746 }747}748 749void StyleColors()750{751 ImGui::StyleColorsLight();752 ImGuiStyle& style = ImGui::GetStyle();753 754 style.Alpha = 1.0f;755 style.FrameRounding = 3.0f;756 style.WindowRounding = 3.f;757 style.Colors[ImGuiCol_Text] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);758 style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.40f, 0.40f, 0.40f, 1.00f);759 style.Colors[ImGuiCol_WindowBg] = ImVec4(0.94f, 0.94f, 0.94f, 0.94f);760 style.Colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);761 style.Colors[ImGuiCol_PopupBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.94f);762 style.Colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.39f);763 style.Colors[ImGuiCol_BorderShadow] = ImVec4(1.00f, 1.00f, 1.00f, 0.10f);764 style.Colors[ImGuiCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.94f);765 style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);766 style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);767 style.Colors[ImGuiCol_TitleBg] = ImVec4(0.96f, 0.96f, 0.96f, 1.00f);768 style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(1.00f, 1.00f, 1.00f, 0.51f);769 style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.82f, 0.82f, 0.82f, 1.00f);770 style.Colors[ImGuiCol_MenuBarBg] = ImVec4(0.86f, 0.86f, 0.86f, 1.00f);771 style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.98f, 0.98f, 0.98f, 0.53f);772 style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.69f, 0.69f, 0.69f, 1.00f);773 style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.59f, 0.59f, 0.59f, 1.00f);774 style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.49f, 0.49f, 0.49f, 1.00f);775 style.Colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);776 style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f);777 style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);778 style.Colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f);779 style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);780 style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);781 style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.78f);782 style.Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.50f);783 style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);784 style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);785 style.Colors[ImGuiCol_Button] = ImVec4(0.59f, 0.59f, 0.59f, 0.50f);786 style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.98f, 0.39f, 0.36f, 1.00f);787 style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.86f, 0.59f, 0.59f, 1.00f);788 style.Colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);789 style.Colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);790 style.Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);791 style.Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);792 style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);793 style.Colors[ImGuiCol_TabSelectedOverline].w = 0;794 style.Colors[ImGuiCol_TabDimmedSelectedOverline].w = 0;795 style.Colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);796 797 float alpha_ = 0.95f;798 if (false) //dark799 {800 for (int i = 0; i < ImGuiCol_COUNT; i++)801 {802 ImVec4& col = style.Colors[i];803 float H, S, V;804 ImGui::ColorConvertRGBtoHSV(col.x, col.y, col.z, H, S, V);805 806 if (S < 0.1f)807 {808 V = 1.0f - V;809 }810 ImGui::ColorConvertHSVtoRGB(H, S, V, col.x, col.y, col.z);811 if (col.w < 1.00f)812 {813 col.w *= alpha_;814 }815 }816 }817 else818 {819 for (int i = 0; i < ImGuiCol_COUNT; i++)820 {821 ImVec4& col = style.Colors[i];822 if (col.w < 1.00f)823 {824 col.x *= alpha_;825 col.y *= alpha_;826 col.z *= alpha_;827 col.w *= alpha_;828 }829 }830 }831 832 //ImRad::SaveStyle(rootPath + "/style/debug.ini");833}834 835void GetStyles()836{837 styleNames = {838 { "Classic", "" },839 { "Light", "" },840 { "Dark", "" }841 };842 for (fs::directory_iterator it(u8path(rootPath + "/style/")); it != fs::directory_iterator(); ++it)843 {844 if (it->path().extension() != ".ini")845 continue;846 styleNames.push_back({ u8string(it->path().stem()), u8string(it->path()) });847 }848}849 850const std::array<ImU32, UIContext::Color::COUNT>&851GetCtxColors(const std::string& styleName)852{853 static const std::array<ImU32, UIContext::Color::COUNT> classic{854 IM_COL32(255, 255, 0, 128),855 IM_COL32(255, 0, 0, 255),856 IM_COL32(128, 128, 255, 255),857 IM_COL32(255, 0, 255, 255),858 IM_COL32(0, 255, 255, 255),859 IM_COL32(0, 255, 0, 255),860 };861 static const std::array<ImU32, UIContext::Color::COUNT> light {862 IM_COL32(0, 0, 255, 128),863 IM_COL32(255, 96, 96, 255),864 IM_COL32(128, 128, 255, 255),865 IM_COL32(255, 0, 255, 255),866 IM_COL32(0, 128, 128, 255),867 IM_COL32(0, 255, 0, 255),868 };869 static const std::array<ImU32, UIContext::Color::COUNT> dark{870 IM_COL32(255, 255, 0, 128),871 IM_COL32(255, 0, 0, 255),872 IM_COL32(128, 128, 255, 255),873 IM_COL32(255, 0, 255, 255),874 IM_COL32(0, 255, 255, 255),875 IM_COL32(0, 255, 0, 255),876 };877 878 if (styleName == "Light")879 return light;880 else if (styleName == "Dark")881 return dark;882 else883 return classic;884}885 886void LoadStyle()887{888 if (!reloadStyle)889 return;890 891 reloadStyle = false;892 auto& io = ImGui::GetIO();893 std::string stylePath = rootPath + "/style/";894 895 glfwSetCursor(window, curWait);896 io.Fonts->Clear();897 898 //reload ImRAD UI first899 StyleColors();900 io.Fonts->AddFontFromFileTTF((stylePath + uiFontName).c_str(), uiFontSize);901 static ImWchar icons_ranges[] = { ICON_MIN_FA, ICON_MAX_16_FA, 0 };902 ImFontConfig cfg;903 cfg.MergeMode = true;904 //icons_config.PixelSnapH = true;905 const float faSize = uiFontSize * 18.f / 20.f;906 io.Fonts->AddFontFromFileTTF((stylePath + FONT_ICON_FILE_NAME_FAR).c_str(), faSize, &cfg, icons_ranges);907 io.Fonts->AddFontFromFileTTF((stylePath + FONT_ICON_FILE_NAME_FAS).c_str(), faSize, &cfg, icons_ranges);908 cfg.MergeMode = false;909 strcpy(cfg.Name, "imrad.H1");910 io.Fonts->AddFontFromFileTTF((stylePath + uiFontName).c_str(), uiFontSize * 1.5f, &cfg);911 strcpy(cfg.Name, "imrad.H3");912 io.Fonts->AddFontFromFileTTF((stylePath + uiFontName).c_str(), uiFontSize * 1.1f, &cfg);913 914 strcpy(cfg.Name, "imrad.pg");915 ctx.pgFont = io.Fonts->AddFontFromFileTTF((stylePath + pgFontName).c_str(), pgFontSize, &cfg);916 strcpy(cfg.Name, "imrad.pgb");917 ctx.pgbFont = io.Fonts->AddFontFromFileTTF((stylePath + pgbFontName).c_str(), pgFontSize, &cfg);918 strcpy(cfg.Name, "imrad.explorer");919 io.Fonts->AddFontFromFileTTF((stylePath + "Roboto-Regular.ttf").c_str(), uiFontSize, &cfg);920 921 cfg.MergeMode = false;922 strcpy(cfg.Name, "simhei");923 ImFont *font = io.Fonts->AddFontFromFileTTF("c:/windows/fonts/simhei.ttf", 23.0f, &cfg, io.Fonts->GetGlyphRangesChineseFull());924 IM_ASSERT(font != nullptr);925 926 // io.FontDefault = font; // 设置默认字体,成功显示中文927 928 //ImGui::PushFont(font);929 930 ctx.defaultStyleFont = nullptr;931 ctx.fontNames.clear();932 stx::fill(ctx.colors, IM_COL32(0, 0, 0, 255));933 ctx.style = ImGuiStyle();934 935 if (activeTab >= 0)936 {937 std::string styleName = fileTabs[activeTab].styleName;938 std::string defaultFont = stylePath + "Roboto-Regular.ttf";939 if (styleName == "Classic")940 {941 ImGui::StyleColorsClassic(&ctx.style);942 ctx.defaultStyleFont = io.Fonts->AddFontFromFileTTF(defaultFont.c_str(), uiFontSize);943 ctx.defaultStyleFont->FallbackChar = '#';944 ctx.fontNames = { "" };945 ctx.colors = GetCtxColors(styleName);946 }947 else if (styleName == "Light")948 {949 ImGui::StyleColorsLight(&ctx.style);950 ctx.defaultStyleFont = io.Fonts->AddFontFromFileTTF(defaultFont.c_str(), uiFontSize);951 ctx.defaultStyleFont->FallbackChar = '#';952 ctx.fontNames = { "" };953 ctx.colors = GetCtxColors(styleName);954 }955 else if (styleName == "Dark")956 {957 ImGui::StyleColorsDark(&ctx.style);958 ctx.defaultStyleFont = io.Fonts->AddFontFromFileTTF(defaultFont.c_str(), uiFontSize);959 ctx.defaultStyleFont->FallbackChar = '#';960 ctx.fontNames = { "" };961 ctx.colors = GetCtxColors(styleName);962 }963 else964 {965 std::map<std::string, ImFont*> fontMap;966 std::map<std::string, std::string> extra;967 try {968 auto it = stx::find_if(styleNames, [&](const auto& s) { return s.first == styleName; });969 ImRad::LoadStyle(it->second, 1.f, &ctx.style, &fontMap, &extra);970 971 ctx.defaultStyleFont = fontMap[""];972 for (const auto& f : fontMap) {973 ctx.fontNames.push_back(f.first);974 }975 for (const auto& ex : extra) {976 if (ex.first.compare(0, 13, "imrad.colors."))977 continue;978 std::istringstream is(ex.second);979 int r, g, b, a;980 is >> r >> g >> b >> a;981 auto clr = IM_COL32(r, g, b, a);982 std::string key = ex.first.substr(13);983 984 #define SET_CLR(a) if (key == #a) ctx.colors[UIContext::a] = clr;985 SET_CLR(Selected);986 SET_CLR(Hovered);987 SET_CLR(Snap1);988 SET_CLR(Snap2);989 SET_CLR(Snap3);990 SET_CLR(Snap4);991 SET_CLR(Snap5);992 #undef SET_CLR993 }994 }995 catch (std::exception& e)996 {997 //can't OpenPopup here, there is no window parent998 showError = e.what();999 }1000 }1001 }1002 ImGui_ImplOpenGL3_DestroyFontsTexture();1003 ImGui_ImplOpenGL3_CreateFontsTexture();1004}1005 1006void DoCloneStyle(const std::string& name)1007{1008 auto FormatClr = [](ImU32 c) {1009 std::ostringstream os;1010 os << (c & 0xff) << " " <<1011 ((c >> 8) & 0xff) << " " <<1012 ((c >> 16) & 0xff) << " " <<1013 ((c >> 24) & 0xff);1014 return os.str();1015 };1016 1017 std::string from = fileTabs[activeTab].styleName;1018 std::string path = rootPath + "/style/" + name + ".ini";1019 try1020 {1021 if (from == "Classic" || from == "Dark" || from == "Light")1022 {1023 ImGuiStyle style;1024 if (from == "Dark")1025 ImGui::StyleColorsDark(&style);1026 else if (from == "Light")1027 ImGui::StyleColorsLight(&style);1028 else1029 ImGui::StyleColorsClassic(&style);1030 1031 std::map<std::string, std::string> extra;1032 const auto& colors = GetCtxColors(from);1033#define SET_CLR(a) extra[std::string("imrad.colors.") + #a] = FormatClr(colors[UIContext::Color::a]);1034 SET_CLR(Selected);1035 SET_CLR(Hovered);1036 SET_CLR(Snap1);1037 SET_CLR(Snap2);1038 SET_CLR(Snap3);1039 SET_CLR(Snap4);1040 SET_CLR(Snap5);1041#undef SET_CLR1042 ImRad::SaveStyle(path, &style, extra);1043 }1044 else1045 {1046 fs::copy_file(u8path(rootPath + "/style/" + from + ".ini"), u8path(path), fs::copy_options::overwrite_existing);1047 }1048 1049 fileTabs[activeTab].styleName = name;1050 if (!stx::count_if(styleNames, [&](const auto& s) { return s.first == name; }))1051 styleNames.push_back({ name, path });1052 1053 messageBox.title = "Style saved";1054 messageBox.message = "New style was saved as '" + path + "'";1055 messageBox.buttons = ImRad::Ok;1056 messageBox.OpenPopup();1057 }1058 catch (std::exception& e)1059 {1060 messageBox.title = "error";1061 messageBox.message = e.what();1062 messageBox.buttons = ImRad::Ok;1063 messageBox.OpenPopup();1064 }1065}1066 1067void CloneStyle()1068{1069 inputName.title = "Clone Style";1070 inputName.hint = "Enter new style name";1071 inputName.OpenPopup([](ImRad::ModalResult mr)1072 {1073 std::string path = rootPath + "/style/" + inputName.name + ".ini";1074 if (fs::exists(u8string(path))) {1075 messageBox.title = "Confirmation";1076 messageBox.message = "Overwrite existing style?";1077 messageBox.buttons = ImRad::Yes | ImRad::No;1078 messageBox.OpenPopup([=](ImRad::ModalResult mr) {1079 if (mr == ImRad::Yes)1080 DoCloneStyle(inputName.name);1081 });1082 }1083 else1084 DoCloneStyle(inputName.name);1085 });1086}1087 1088void DockspaceUI()1089{1090 // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,1091 // because it would be confusing to have two docking targets within each others.1092 ImGuiWindowFlags window_flags = /*ImGuiWindowFlags_MenuBar |*/ ImGuiWindowFlags_NoDocking;1093 ImGuiViewport* viewport = ImGui::GetMainViewport();1094 ImGui::SetNextWindowPos(viewport->Pos + ImVec2(0, TB_SIZE + 0));1095 ImGui::SetNextWindowSize(viewport->Size - ImVec2(0, TB_SIZE + 0));1096 ImGui::SetNextWindowViewport(viewport->ID);1097 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);1098 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);1099 window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;1100 window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;1101 window_flags |= ImGuiWindowFlags_NoBackground;1102 1103 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));1104 ImGui::Begin("DockSpace", nullptr, window_flags);1105 ImGui::PopStyleVar();1106 ImGui::PopStyleVar(2);1107 1108 ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");1109 ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_NoDockingOverCentralNode);1110 if (programState == Init)1111 {1112 ImGui::DockBuilderRemoveNode(dockspace_id); // clear any previous layout1113 ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_DockSpace);1114 ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size);1115 ImGuiID dock_id_right = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, 350.f / viewport->Size.x, nullptr, &dockspace_id);1116 ImGuiID dock_id_left = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 300.f / (viewport->Size.x - 350), nullptr, &dockspace_id);1117 ImGuiID dock_id_right1, dock_id_right2;1118 ImGui::DockBuilderSplitNode(dock_id_right, ImGuiDir_Up, 230.f / (viewport->Size.y - TB_SIZE), &dock_id_right1, &dock_id_right2);1119 float vh = viewport->Size.y - TB_SIZE;1120 ImGuiID dock_id_top = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Up, TAB_SIZE / vh, nullptr, &dockspace_id);1121 1122 ImGui::DockBuilderDockWindow("FileTabs", dock_id_top);1123 ImGui::DockBuilderDockWindow("Hierarchy", dock_id_left);1124 ImGui::DockBuilderDockWindow("Explorer", dock_id_left);1125 ImGui::DockBuilderDockWindow("Widgets", dock_id_right1);1126 ImGui::DockBuilderDockWindow("Properties", dock_id_right2);1127 ImGui::DockBuilderDockWindow("Events", dock_id_right2);1128 ImGui::DockBuilderFinish(dockspace_id);1129 }1130 1131 ImGuiDockNode* cn = ImGui::DockBuilderGetCentralNode(dockspace_id);1132 ctx.designAreaMin = cn->Pos;1133 ctx.designAreaMax = cn->Pos + cn->Size;1134 1135 if (fileTabs.empty())1136 {1137 std::pair<std::string, std::string> help[]{1138 { "Create a New File", "Ctrl+N" },1139 { "Open File", "Ctrl+O" }1140 };1141 float lh = 2 * ImGui::GetTextLineHeight();1142 for (size_t i = 0; i < std::size(help); ++i)1143 {1144 ImVec2 size = ImGui::CalcTextSize((help[i].first + help[i].second).c_str());1145 ImGui::SetCursorScreenPos({1146 viewport->WorkSize.x / 2.5f,1147 (viewport->WorkSize.y - (std::size(help) - 1) * lh) / 2 + i * lh1148 });1149 ImVec4 clr = ImGui::GetStyleColorVec4(ImGuiCol_Text);1150 clr.w = 0.75f;1151 ImGui::PushStyleColor(ImGuiCol_Text, clr);1152 ImGui::Selectable(help[i].first.c_str(), false, 0, { 150, 0 });1153 ImGui::PopStyleColor();1154 clr.w = 1.f;1155 ImGui::PushStyleColor(ImGuiCol_Text, clr);1156 for (size_t c = 0; c < help[i].second.size(); )1157 {1158 bool first = !c;1159 std::string sub;1160 size_t j = help[i].second.find('+', c);1161 if (j != std::string::npos) {1162 sub = help[i].second.substr(c, j - c);1163 c = j + 1;1164 }1165 else {1166 sub = help[i].second.substr(c);1167 c = j;1168 }1169 ImGui::SameLine(0, 0);1170 if (!first) {1171 ImGui::Text(" + ");1172 ImGui::SameLine(0, 0);1173 }1174 ImGui::Selectable(sub.c_str(), true, 0, { ImGui::CalcTextSize(sub.c_str()).x, 0 });1175 }1176 ImGui::PopStyleColor();1177 }1178 }1179 else1180 {1181 float sp = ImGui::GetStyle().ItemSpacing.x;1182 ImGui::SetCursorScreenPos({1183 ctx.designAreaMin.x + sp,1184 ctx.designAreaMax.y - ImGui::GetTextLineHeight() - sp1185 });1186 ImVec4 clr = ImGui::GetStyleColorVec4(ImGuiCol_Text);1187 clr.w = 0.5f;1188 ImGui::PushStyleColor(ImGuiCol_Text, clr);1189 ImGui::Text("Zoom: %d%%", (int)(ctx.zoomFactor * 100));1190 ImGui::PopStyleColor();1191 }1192 1193 ImGui::End();1194}1195 1196void ToolbarUI()1197{1198 ImGuiViewport* viewport = ImGui::GetMainViewport();1199 ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + 0));1200 ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, TB_SIZE));