mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-10-30 00:18:10 +01:00 
			
		
		
		
	Added persistant menu open/close
This commit is contained in:
		| @@ -119,16 +119,31 @@ int sdrpp_main(int argc, char *argv[]) { | ||||
|     defConfig["fullWaterfallUpdate"] = false; | ||||
|     defConfig["max"] = 0.0; | ||||
|     defConfig["maximized"] = false; | ||||
|     defConfig["menuOrder"] = { | ||||
|         "Source", | ||||
|         "Radio", | ||||
|         "Recorder", | ||||
|         "Sinks", | ||||
|         "Audio", | ||||
|         "Scripting", | ||||
|         "Band Plan", | ||||
|         "Display" | ||||
|     }; | ||||
|  | ||||
|     // Menu | ||||
|     defConfig["menuElements"] = json::array(); | ||||
|  | ||||
|     defConfig["menuElements"][0]["name"] = "Source"; | ||||
|     defConfig["menuElements"][0]["open"] = true; | ||||
|  | ||||
|     defConfig["menuElements"][1]["name"] = "Radio"; | ||||
|     defConfig["menuElements"][1]["open"] = true; | ||||
|  | ||||
|     defConfig["menuElements"][2]["name"] = "Recorder"; | ||||
|     defConfig["menuElements"][2]["open"] = true; | ||||
|  | ||||
|     defConfig["menuElements"][3]["name"] = "Sinks"; | ||||
|     defConfig["menuElements"][3]["open"] = true; | ||||
|  | ||||
|     defConfig["menuElements"][4]["name"] = "Scripting"; | ||||
|     defConfig["menuElements"][4]["open"] = false; | ||||
|  | ||||
|     defConfig["menuElements"][5]["name"] = "Band Plan"; | ||||
|     defConfig["menuElements"][5]["open"] = true; | ||||
|  | ||||
|     defConfig["menuElements"][6]["name"] = "Display"; | ||||
|     defConfig["menuElements"][6]["open"] = true; | ||||
|  | ||||
|     defConfig["menuWidth"] = 300; | ||||
|     defConfig["min"] = -120.0; | ||||
|  | ||||
| @@ -169,14 +184,24 @@ int sdrpp_main(int argc, char *argv[]) { | ||||
|     core::configManager.load(defConfig); | ||||
|     core::configManager.enableAutoSave(); | ||||
|  | ||||
|     // Fix config | ||||
|      | ||||
|     core::configManager.aquire(); | ||||
|     // Fix missing elements in config | ||||
|     for (auto const& item : defConfig.items()) { | ||||
|         if (!core::configManager.conf.contains(item.key())) { | ||||
|             spdlog::warn("Missing key in config {0}, repairing", item.key()); | ||||
|             core::configManager.conf[item.key()] = defConfig[item.key()]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Remove unused elements | ||||
|     auto items = core::configManager.conf.items(); | ||||
|     for (auto const& item : items) { | ||||
|         if (!defConfig.contains(item.key())) { | ||||
|             spdlog::warn("Unused key in config {0}, repairing", item.key()); | ||||
|             core::configManager.conf.erase(item.key()); | ||||
|         } | ||||
|     } | ||||
|     core::configManager.release(true); | ||||
|  | ||||
|     // Setup window | ||||
|   | ||||
| @@ -112,11 +112,24 @@ void windowInit() { | ||||
|     credits::init(); | ||||
|  | ||||
|     core::configManager.aquire(); | ||||
|     gui::menu.order = core::configManager.conf["menuOrder"].get<std::vector<std::string>>(); | ||||
|     json menuElements = core::configManager.conf["menuElements"]; | ||||
|     std::string modulesDir = core::configManager.conf["modulesDirectory"]; | ||||
|     std::string resourcesDir = core::configManager.conf["resourcesDirectory"]; | ||||
|     core::configManager.release(); | ||||
|  | ||||
|     // Load menu elements | ||||
|     gui::menu.order.clear(); | ||||
|     for (auto& elem : menuElements) { | ||||
|         if (!elem.contains("name")) { spdlog::error("Menu element is missing name key"); continue; } | ||||
|         if (!elem["name"].is_string()) { spdlog::error("Menu element name isn't a string"); continue; } | ||||
|         if (!elem.contains("open")) { spdlog::error("Menu element is missing open key"); continue; } | ||||
|         if (!elem["open"].is_boolean()) { spdlog::error("Menu element name isn't a string"); continue; } | ||||
|         Menu::MenuOption_t opt; | ||||
|         opt.name = elem["name"]; | ||||
|         opt.open = elem["open"]; | ||||
|         gui::menu.order.push_back(opt); | ||||
|     } | ||||
|  | ||||
|     gui::menu.registerEntry("Source", sourecmenu::draw, NULL); | ||||
|     gui::menu.registerEntry("Sinks", sinkmenu::draw, NULL); | ||||
|     gui::menu.registerEntry("Scripting", scriptingmenu::draw, NULL); | ||||
| @@ -541,7 +554,16 @@ void drawWindow() { | ||||
|         ImGui::BeginChild("Left Column"); | ||||
|         float menuColumnWidth = ImGui::GetContentRegionAvailWidth(); | ||||
|  | ||||
|         gui::menu.draw(); | ||||
|         if (gui::menu.draw()) { | ||||
|             core::configManager.aquire(); | ||||
|             json arr = json::array(); | ||||
|             for (int i = 0; i < gui::menu.order.size(); i++) { | ||||
|                 arr[i]["name"] = gui::menu.order[i].name; | ||||
|                 arr[i]["open"] = gui::menu.order[i].open; | ||||
|             } | ||||
|             core::configManager.conf["menuElements"] = arr; | ||||
|             core::configManager.release(true); | ||||
|         } | ||||
|  | ||||
|         if(ImGui::CollapsingHeader("Debug")) { | ||||
|             ImGui::Text("Frame time: %.3f ms/frame", 1000.0 / ImGui::GetIO().Framerate); | ||||
|   | ||||
| @@ -5,8 +5,6 @@ | ||||
| #include <filesystem> | ||||
| #include <fstream> | ||||
|  | ||||
| using nlohmann::json; | ||||
|  | ||||
| bool ThemeManager::loadThemesFromDir(std::string path) { | ||||
|     if (!std::filesystem::is_directory(path)) { | ||||
|         spdlog::error("Theme directory doesn't exist: {0}", path); | ||||
| @@ -31,6 +29,7 @@ bool ThemeManager::loadTheme(std::string path) { | ||||
|  | ||||
|     // Load defaults in theme | ||||
|     Theme thm; | ||||
|     thm.author = "--"; | ||||
|  | ||||
|     // Load JSON | ||||
|     std::ifstream file(path.c_str()); | ||||
| @@ -38,61 +37,50 @@ bool ThemeManager::loadTheme(std::string path) { | ||||
|     file >> data; | ||||
|     file.close(); | ||||
|  | ||||
|     // Load theme | ||||
|     uint8_t val[4]; | ||||
|     if (data.contains("Text")) {                    if (decodeRGBA(data["Text"], val)) {                    thm.Text = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TextDisabled")) {            if (decodeRGBA(data["TextDisabled"], val)) {            thm.TextDisabled = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("WindowBg")) {                if (decodeRGBA(data["WindowBg"], val)) {                thm.WindowBg = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("ChildBg")) {                 if (decodeRGBA(data["ChildBg"], val)) {                 thm.ChildBg = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("PopupBg")) {                 if (decodeRGBA(data["PopupBg"], val)) {                 thm.PopupBg = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("Border")) {                  if (decodeRGBA(data["Border"], val)) {                  thm.Border = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("BorderShadow")) {            if (decodeRGBA(data["BorderShadow"], val)) {            thm.BorderShadow = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("FrameBg")) {                 if (decodeRGBA(data["FrameBg"], val)) {                 thm.FrameBg = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("FrameBgHovered")) {          if (decodeRGBA(data["FrameBgHovered"], val)) {          thm.FrameBgHovered = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("FrameBgActive")) {           if (decodeRGBA(data["FrameBgActive"], val)) {           thm.FrameBgActive = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TitleBg")) {                 if (decodeRGBA(data["TitleBg"], val)) {                 thm.TitleBg = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TitleBgActive")) {           if (decodeRGBA(data["TitleBgActive"], val)) {           thm.TitleBgActive = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TitleBgCollapsed")) {        if (decodeRGBA(data["TitleBgCollapsed"], val)) {        thm.TitleBgCollapsed = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("MenuBarBg")) {               if (decodeRGBA(data["MenuBarBg"], val)) {               thm.MenuBarBg = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("ScrollbarBg")) {             if (decodeRGBA(data["ScrollbarBg"], val)) {             thm.ScrollbarBg = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("ScrollbarGrab")) {           if (decodeRGBA(data["ScrollbarGrab"], val)) {           thm.ScrollbarGrab = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("ScrollbarGrabHovered")) {    if (decodeRGBA(data["ScrollbarGrabHovered"], val)) {    thm.ScrollbarGrabHovered = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("ScrollbarGrabActive")) {     if (decodeRGBA(data["ScrollbarGrabActive"], val)) {     thm.ScrollbarGrabActive = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("CheckMark")) {               if (decodeRGBA(data["CheckMark"], val)) {               thm.CheckMark = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("SliderGrab")) {              if (decodeRGBA(data["SliderGrab"], val)) {              thm.SliderGrab = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TeSliderGrabActivext")) {    if (decodeRGBA(data["SliderGrabActive"], val)) {        thm.SliderGrabActive = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("Button")) {                  if (decodeRGBA(data["Button"], val)) {                  thm.Button = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("ButtonHovered")) {           if (decodeRGBA(data["ButtonHovered"], val)) {           thm.ButtonHovered = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("ButtonActive")) {            if (decodeRGBA(data["ButtonActive"], val)) {            thm.ButtonActive = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("Header")) {                  if (decodeRGBA(data["Header"], val)) {                  thm.Header = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("HeaderHovered")) {           if (decodeRGBA(data["HeaderHovered"], val)) {           thm.HeaderHovered = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("HeaderActive")) {            if (decodeRGBA(data["HeaderActive"], val)) {            thm.HeaderActive = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("Separator")) {               if (decodeRGBA(data["Separator"], val)) {               thm.Separator = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("SeparatorHovered")) {        if (decodeRGBA(data["SeparatorHovered"], val)) {        thm.SeparatorHovered = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("SeparatorActive")) {         if (decodeRGBA(data["SeparatorActive"], val)) {         thm.SeparatorActive = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("ResizeGrip")) {              if (decodeRGBA(data["ResizeGrip"], val)) {              thm.ResizeGrip = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("ResizeGripHovered")) {       if (decodeRGBA(data["ResizeGripHovered"], val)) {       thm.ResizeGripHovered = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("ResizeGripActive")) {        if (decodeRGBA(data["ResizeGripActive"], val)) {        thm.ResizeGripActive = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("Tab")) {                     if (decodeRGBA(data["Tab"], val)) {                     thm.Tab = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TabHovered")) {              if (decodeRGBA(data["TabHovered"], val)) {              thm.TabHovered = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TabActive")) {               if (decodeRGBA(data["TabActive"], val)) {               thm.TabActive = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TabUnfocused")) {            if (decodeRGBA(data["TabUnfocused"], val)) {            thm.TabUnfocused = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TabUnfocusedActive")) {      if (decodeRGBA(data["TabUnfocusedActive"], val)) {      thm.TabUnfocusedActive = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("PlotLines")) {               if (decodeRGBA(data["PlotLines"], val)) {               thm.PlotLines = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("PlotLinesHovered")) {        if (decodeRGBA(data["PlotLinesHovered"], val)) {        thm.PlotLinesHovered = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("PlotHistogram")) {           if (decodeRGBA(data["PlotHistogram"], val)) {           thm.PlotHistogram = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("PlotHistogramHovered")) {    if (decodeRGBA(data["PlotHistogramHovered"], val)) {    thm.PlotHistogramHovered = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TableHeaderBg")) {           if (decodeRGBA(data["TableHeaderBg"], val)) {           thm.TableHeaderBg = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TableBorderStrong")) {       if (decodeRGBA(data["TableBorderStrong"], val)) {       thm.TableBorderStrong = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TableBorderLight")) {        if (decodeRGBA(data["TableBorderLight"], val)) {        thm.TableBorderLight = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TableRowBg")) {              if (decodeRGBA(data["TableRowBg"], val)) {              thm.TableRowBg = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TableRowBgAlt")) {           if (decodeRGBA(data["TableRowBgAlt"], val)) {           thm.TableRowBgAlt = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("TextSelectedBg")) {          if (decodeRGBA(data["TextSelectedBg"], val)) {          thm.TextSelectedBg = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("DragDropTarget")) {          if (decodeRGBA(data["DragDropTarget"], val)) {          thm.DragDropTarget = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("NavHighlight")) {            if (decodeRGBA(data["NavHighlight"], val)) {            thm.NavHighlight = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("NavWindowingHighlight")) {   if (decodeRGBA(data["NavWindowingHighlight"], val)) {   thm.NavWindowingHighlight = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("NavWindowingDimBg")) {       if (decodeRGBA(data["NavWindowingDimBg"], val)) {       thm.NavWindowingDimBg = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     if (data.contains("ModalWindowDimBg")) {        if (decodeRGBA(data["ModalWindowDimBg"], val)) {        thm.ModalWindowDimBg = ImVec4((float)val[0]/255.0f, (float)val[1]/255.0f, (float)val[2]/255.0f, (float)val[3]/255.0f); } } | ||||
|     // Load theme name | ||||
|     if (!data.contains("name")) { | ||||
|         spdlog::error("Theme {0} is missing the name parameter", path); | ||||
|         return false; | ||||
|     } | ||||
|     if (!data["name"].is_string()) { | ||||
|         spdlog::error("Theme {0} contains invalid name field. Expected string", path); | ||||
|         return false; | ||||
|     } | ||||
|     std::string name = data["name"]; | ||||
|  | ||||
|     // Load theme author if available | ||||
|     if (data.contains("author")) { | ||||
|         if (!data["author"].is_string()) { | ||||
|             spdlog::error("Theme {0} contains invalid author field. Expected string", path); | ||||
|             return false; | ||||
|         } | ||||
|         thm.author = data["author"]; | ||||
|     } | ||||
|  | ||||
|     // Iterate through all parameters and check their contents | ||||
|     std::map<std::string, std::string> params = data; | ||||
|     for (auto const& [param, val] : params) { | ||||
|         if (param == "name" || param == "author") { continue; } | ||||
|  | ||||
|         bool isValid = false; | ||||
|  | ||||
|         // If param is a color, check that it's a valid RGBA hex value | ||||
|         if (IMGUI_COL_IDS.find(param) != IMGUI_COL_IDS.end()) { | ||||
|             if (val[0] != '#' || !std::all_of(val.begin() + 1, val.end(), ::isxdigit) || val.length() != 9) { | ||||
|                 spdlog::error("Theme {0} contains invalid {1} field. Expected hex RGBA color", path, param); | ||||
|                 return false; | ||||
|             } | ||||
|             isValid = true; | ||||
|         } | ||||
|  | ||||
|         if (!isValid) { | ||||
|             spdlog::error("Theme {0} contains unknown {1} field.", path, param); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     thm.data = data; | ||||
|     themes[name] = thm; | ||||
|  | ||||
|     return true; | ||||
| } | ||||
| @@ -103,62 +91,32 @@ bool ThemeManager::applyTheme(std::string name) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     ImGui::StyleColorsDark(); | ||||
|  | ||||
|     auto& style = ImGui::GetStyle(); | ||||
|  | ||||
|     style.WindowRounding = 0.0f; | ||||
|     style.ChildRounding = 0.0f; | ||||
|     style.FrameRounding = 0.0f; | ||||
|     style.GrabRounding = 0.0f; | ||||
|     style.PopupRounding = 0.0f; | ||||
|     style.ScrollbarRounding = 0.0f; | ||||
|  | ||||
|     ImVec4* colors = style.Colors; | ||||
|     Theme thm = themes[name]; | ||||
|     colors[ImGuiCol_Text] = thm.Text; | ||||
|     colors[ImGuiCol_TextDisabled] = thm.TextDisabled; | ||||
|     colors[ImGuiCol_WindowBg] = thm.WindowBg; | ||||
|     colors[ImGuiCol_ChildBg] = thm.ChildBg; | ||||
|     colors[ImGuiCol_PopupBg] = thm.PopupBg; | ||||
|     colors[ImGuiCol_Border] = thm.Border; | ||||
|     colors[ImGuiCol_BorderShadow] = thm.BorderShadow; | ||||
|     colors[ImGuiCol_FrameBg] = thm.FrameBg; | ||||
|     colors[ImGuiCol_FrameBgHovered] = thm.FrameBgHovered; | ||||
|     colors[ImGuiCol_FrameBgActive] = thm.FrameBgActive; | ||||
|     colors[ImGuiCol_TitleBg] = thm.TitleBg; | ||||
|     colors[ImGuiCol_TitleBgActive] = thm.TitleBgActive; | ||||
|     colors[ImGuiCol_TitleBgCollapsed] = thm.TitleBgCollapsed; | ||||
|     colors[ImGuiCol_MenuBarBg] = thm.MenuBarBg; | ||||
|     colors[ImGuiCol_ScrollbarBg] = thm.ScrollbarBg; | ||||
|     colors[ImGuiCol_ScrollbarGrab] = thm.ScrollbarGrab; | ||||
|     colors[ImGuiCol_ScrollbarGrabHovered] = thm.ScrollbarGrabHovered; | ||||
|     colors[ImGuiCol_ScrollbarGrabActive] = thm.ScrollbarGrabActive; | ||||
|     colors[ImGuiCol_CheckMark] = thm.CheckMark; | ||||
|     colors[ImGuiCol_SliderGrab] = thm.SliderGrab; | ||||
|     colors[ImGuiCol_SliderGrabActive] = thm.SliderGrabActive; | ||||
|     colors[ImGuiCol_Button] = thm.Button; | ||||
|     colors[ImGuiCol_ButtonHovered] = thm.ButtonHovered; | ||||
|     colors[ImGuiCol_ButtonActive] = thm.ButtonActive; | ||||
|     colors[ImGuiCol_Header] = thm.Header; | ||||
|     colors[ImGuiCol_HeaderHovered] = thm.HeaderHovered; | ||||
|     colors[ImGuiCol_HeaderActive] = thm.HeaderActive; | ||||
|     colors[ImGuiCol_Separator] = thm.Separator; | ||||
|     colors[ImGuiCol_SeparatorHovered] = thm.SeparatorHovered; | ||||
|     colors[ImGuiCol_SeparatorActive] = thm.SeparatorActive; | ||||
|     colors[ImGuiCol_ResizeGrip] = thm.ResizeGrip; | ||||
|     colors[ImGuiCol_ResizeGripHovered] = thm.ResizeGripHovered; | ||||
|     colors[ImGuiCol_ResizeGripActive] = thm.ResizeGripActive; | ||||
|     colors[ImGuiCol_Tab] = thm.Tab; | ||||
|     colors[ImGuiCol_TabHovered] = thm.TabHovered; | ||||
|     colors[ImGuiCol_TabActive] = thm.TabActive; | ||||
|     colors[ImGuiCol_TabUnfocused] = thm.TabUnfocused; | ||||
|     colors[ImGuiCol_TabUnfocusedActive] = thm.TabUnfocusedActive; | ||||
|     colors[ImGuiCol_PlotLines] = thm.PlotLines; | ||||
|     colors[ImGuiCol_PlotLinesHovered] = thm.PlotLinesHovered; | ||||
|     colors[ImGuiCol_PlotHistogram] = thm.PlotHistogram; | ||||
|     colors[ImGuiCol_PlotHistogramHovered] = thm.PlotHistogramHovered; | ||||
|     colors[ImGuiCol_TableHeaderBg] = thm.TableHeaderBg; | ||||
|     colors[ImGuiCol_TableBorderStrong] = thm.TableBorderStrong; | ||||
|     colors[ImGuiCol_TableBorderLight] = thm.TableBorderLight; | ||||
|     colors[ImGuiCol_TableRowBg] = thm.TableRowBg; | ||||
|     colors[ImGuiCol_TableRowBgAlt] = thm.TableRowBgAlt; | ||||
|     colors[ImGuiCol_TextSelectedBg] = thm.TextSelectedBg; | ||||
|     colors[ImGuiCol_DragDropTarget] = thm.DragDropTarget; | ||||
|     colors[ImGuiCol_NavHighlight] = thm.NavHighlight; | ||||
|     colors[ImGuiCol_NavWindowingHighlight] = thm.NavWindowingHighlight; | ||||
|     colors[ImGuiCol_NavWindowingDimBg] = thm.NavWindowingDimBg; | ||||
|     colors[ImGuiCol_ModalWindowDimBg] = thm.ModalWindowDimBg; | ||||
|      | ||||
|     uint8_t ret[4]; | ||||
|     std::map<std::string, std::string> params = thm.data; | ||||
|     for (auto const& [param, val] : params) { | ||||
|         if (param == "name" || param == "author") { continue; } | ||||
|  | ||||
|         // If param is a color, check that it's a valid RGBA hex value | ||||
|         if (IMGUI_COL_IDS.find(param) != IMGUI_COL_IDS.end()) { | ||||
|             decodeRGBA(val, ret); | ||||
|             colors[IMGUI_COL_IDS[param]] = ImVec4((float)ret[0]/255.0f, (float)ret[1]/255.0f, (float)ret[2]/255.0f, (float)ret[3]/255.0f); | ||||
|             continue; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
| } | ||||
| @@ -172,4 +130,60 @@ bool ThemeManager::decodeRGBA(std::string str, uint8_t out[4]) { | ||||
|     out[2] = std::stoi(str.substr(5, 2), NULL, 16); | ||||
|     out[3] = std::stoi(str.substr(7, 2), NULL, 16); | ||||
|     return true; | ||||
| } | ||||
| } | ||||
|  | ||||
| std::map<std::string, int> ThemeManager::IMGUI_COL_IDS = { | ||||
|     {"Text",                    ImGuiCol_Text}, | ||||
|     {"TextDisabled",            ImGuiCol_TextDisabled}, | ||||
|     {"WindowBg",                ImGuiCol_WindowBg},  | ||||
|     {"ChildBg",                 ImGuiCol_ChildBg}, | ||||
|     {"PopupBg",                 ImGuiCol_PopupBg}, | ||||
|     {"Border",                  ImGuiCol_Border}, | ||||
|     {"BorderShadow",            ImGuiCol_BorderShadow}, | ||||
|     {"FrameBg",                 ImGuiCol_FrameBg}, | ||||
|     {"FrameBgHovered",          ImGuiCol_FrameBgHovered}, | ||||
|     {"FrameBgActive",           ImGuiCol_FrameBgActive}, | ||||
|     {"TitleBg",                 ImGuiCol_TitleBg}, | ||||
|     {"TitleBgActive",           ImGuiCol_TitleBgActive}, | ||||
|     {"TitleBgCollapsed",        ImGuiCol_TitleBgCollapsed}, | ||||
|     {"MenuBarBg",               ImGuiCol_MenuBarBg}, | ||||
|     {"ScrollbarBg",             ImGuiCol_ScrollbarBg}, | ||||
|     {"ScrollbarGrab",           ImGuiCol_ScrollbarGrab}, | ||||
|     {"ScrollbarGrabHovered",    ImGuiCol_ScrollbarGrabHovered}, | ||||
|     {"ScrollbarGrabActive",     ImGuiCol_ScrollbarGrabActive}, | ||||
|     {"CheckMark",               ImGuiCol_CheckMark}, | ||||
|     {"SliderGrab",              ImGuiCol_SliderGrab}, | ||||
|     {"SliderGrabActive",        ImGuiCol_SliderGrabActive}, | ||||
|     {"Button",                  ImGuiCol_Button}, | ||||
|     {"ButtonHovered",           ImGuiCol_ButtonHovered}, | ||||
|     {"ButtonActive",            ImGuiCol_ButtonActive}, | ||||
|     {"Header",                  ImGuiCol_Header}, | ||||
|     {"HeaderHovered",           ImGuiCol_HeaderHovered}, | ||||
|     {"HeaderActive",            ImGuiCol_HeaderActive}, | ||||
|     {"Separator",               ImGuiCol_Separator}, | ||||
|     {"SeparatorHovered",        ImGuiCol_SeparatorHovered}, | ||||
|     {"SeparatorActive",         ImGuiCol_SeparatorActive}, | ||||
|     {"ResizeGrip",              ImGuiCol_ResizeGrip}, | ||||
|     {"ResizeGripHovered",       ImGuiCol_ResizeGripHovered}, | ||||
|     {"ResizeGripActive",        ImGuiCol_ResizeGripActive}, | ||||
|     {"Tab",                     ImGuiCol_Tab}, | ||||
|     {"TabHovered",              ImGuiCol_TabHovered}, | ||||
|     {"TabActive",               ImGuiCol_TabActive}, | ||||
|     {"TabUnfocused",            ImGuiCol_TabUnfocused}, | ||||
|     {"TabUnfocusedActive",      ImGuiCol_TabUnfocusedActive}, | ||||
|     {"PlotLines",               ImGuiCol_PlotLines}, | ||||
|     {"PlotLinesHovered",        ImGuiCol_PlotLinesHovered}, | ||||
|     {"PlotHistogram",           ImGuiCol_PlotHistogram}, | ||||
|     {"PlotHistogramHovered",    ImGuiCol_PlotHistogramHovered}, | ||||
|     {"TableHeaderBg",           ImGuiCol_TableHeaderBg}, | ||||
|     {"TableBorderStrong",       ImGuiCol_TableBorderStrong}, | ||||
|     {"TableBorderLight",        ImGuiCol_TableBorderLight}, | ||||
|     {"TableRowBg",              ImGuiCol_TableRowBg}, | ||||
|     {"TableRowBgAlt",           ImGuiCol_TableRowBgAlt}, | ||||
|     {"TextSelectedBg",          ImGuiCol_TextSelectedBg}, | ||||
|     {"DragDropTarget",          ImGuiCol_DragDropTarget}, | ||||
|     {"NavHighlight",            ImGuiCol_NavHighlight}, | ||||
|     {"NavWindowingHighlight",   ImGuiCol_NavWindowingHighlight}, | ||||
|     {"NavWindowingDimBg",       ImGuiCol_NavWindowingDimBg}, | ||||
|     {"ModalWindowDimBg",        ImGuiCol_ModalWindowDimBg} | ||||
| }; | ||||
| @@ -3,62 +3,13 @@ | ||||
| #include <mutex> | ||||
| #include <imgui.h> | ||||
| #include <vector> | ||||
| #include <json.hpp> | ||||
|  | ||||
| using nlohmann::json; | ||||
|  | ||||
| struct Theme { | ||||
|     std::string author; | ||||
|     ImVec4 Text; | ||||
|     ImVec4 TextDisabled; | ||||
|     ImVec4 WindowBg; | ||||
|     ImVec4 ChildBg; | ||||
|     ImVec4 PopupBg; | ||||
|     ImVec4 Border; | ||||
|     ImVec4 BorderShadow; | ||||
|     ImVec4 FrameBg; | ||||
|     ImVec4 FrameBgHovered; | ||||
|     ImVec4 FrameBgActive; | ||||
|     ImVec4 TitleBg; | ||||
|     ImVec4 TitleBgActive; | ||||
|     ImVec4 TitleBgCollapsed; | ||||
|     ImVec4 MenuBarBg; | ||||
|     ImVec4 ScrollbarBg; | ||||
|     ImVec4 ScrollbarGrab; | ||||
|     ImVec4 ScrollbarGrabHovered; | ||||
|     ImVec4 ScrollbarGrabActive; | ||||
|     ImVec4 CheckMark; | ||||
|     ImVec4 SliderGrab; | ||||
|     ImVec4 SliderGrabActive; | ||||
|     ImVec4 Button; | ||||
|     ImVec4 ButtonHovered; | ||||
|     ImVec4 ButtonActive; | ||||
|     ImVec4 Header; | ||||
|     ImVec4 HeaderHovered; | ||||
|     ImVec4 HeaderActive; | ||||
|     ImVec4 Separator; | ||||
|     ImVec4 SeparatorHovered; | ||||
|     ImVec4 SeparatorActive; | ||||
|     ImVec4 ResizeGrip; | ||||
|     ImVec4 ResizeGripHovered; | ||||
|     ImVec4 ResizeGripActive; | ||||
|     ImVec4 Tab; | ||||
|     ImVec4 TabHovered; | ||||
|     ImVec4 TabActive; | ||||
|     ImVec4 TabUnfocused; | ||||
|     ImVec4 TabUnfocusedActive; | ||||
|     ImVec4 PlotLines; | ||||
|     ImVec4 PlotLinesHovered; | ||||
|     ImVec4 PlotHistogram; | ||||
|     ImVec4 PlotHistogramHovered; | ||||
|     ImVec4 TableHeaderBg; | ||||
|     ImVec4 TableBorderStrong; | ||||
|     ImVec4 TableBorderLight; | ||||
|     ImVec4 TableRowBg; | ||||
|     ImVec4 TableRowBgAlt; | ||||
|     ImVec4 TextSelectedBg; | ||||
|     ImVec4 DragDropTarget; | ||||
|     ImVec4 NavHighlight; | ||||
|     ImVec4 NavWindowingHighlight; | ||||
|     ImVec4 NavWindowingDimBg; | ||||
|     ImVec4 ModalWindowDimBg; | ||||
|     json data; | ||||
| }; | ||||
|  | ||||
| class ThemeManager { | ||||
| @@ -72,6 +23,8 @@ public: | ||||
| private: | ||||
|     static bool decodeRGBA(std::string str, uint8_t out[4]); | ||||
|  | ||||
|     static std::map<std::string, int> IMGUI_COL_IDS; | ||||
|  | ||||
|     std::map<std::string, Theme> themes; | ||||
|  | ||||
| }; | ||||
| @@ -13,7 +13,10 @@ void Menu::registerEntry(std::string name, void (*drawHandler)(void* ctx), void* | ||||
|     item.inst = inst; | ||||
|     items[name] = item; | ||||
|     if (!isInOrderList(name)) { | ||||
|         order.push_back(name); | ||||
|         MenuOption_t opt; | ||||
|         opt.name = name; | ||||
|         opt.open = true; | ||||
|         order.push_back(opt); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -21,35 +24,40 @@ void Menu::removeEntry(std::string name) { | ||||
|     items.erase(name); | ||||
| } | ||||
|  | ||||
| void Menu::draw() { | ||||
|     MenuItem_t item; | ||||
| bool Menu::draw() { | ||||
|     bool changed = false; | ||||
|     float menuWidth = ImGui::GetContentRegionAvailWidth(); | ||||
|     ImGuiWindow* window = ImGui::GetCurrentWindow(); | ||||
|     for (std::string name : order) { | ||||
|         if (items.find(name) == items.end()) { | ||||
|     for (MenuOption_t& opt : order) { | ||||
|         if (items.find(opt.name) == items.end()) { | ||||
|             continue; | ||||
|         } | ||||
|         item = items[name]; | ||||
|         MenuItem_t& item = items[opt.name]; | ||||
|  | ||||
|         ImRect orginalRect = window->WorkRect; | ||||
|         if (item.inst != NULL) { | ||||
|              | ||||
|             window->WorkRect = ImRect(orginalRect.Min, ImVec2(orginalRect.Max.x - ImGui::GetTextLineHeight() - 6, orginalRect.Max.y)); | ||||
|         } | ||||
|  | ||||
|         if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { | ||||
|         if (ImGui::CollapsingHeader(opt.name.c_str(), opt.open ? ImGuiTreeNodeFlags_DefaultOpen : 0)) { | ||||
|             if (item.inst != NULL) { | ||||
|                 window->WorkRect = orginalRect; | ||||
|                 ImVec2 pos = ImGui::GetCursorPos(); | ||||
|                 ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6); | ||||
|                 ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight()); | ||||
|                 bool enabled = item.inst->isEnabled(); | ||||
|                 if (ImGui::Checkbox(("##_menu_checkbox_" + name).c_str(), &enabled)) { | ||||
|                 if (ImGui::Checkbox(("##_menu_checkbox_" + opt.name).c_str(), &enabled)) { | ||||
|                     enabled ? item.inst->enable() : item.inst->disable(); | ||||
|                 } | ||||
|                 ImGui::SetCursorPos(pos); | ||||
|             } | ||||
|  | ||||
|             // Check if the state changed | ||||
|             if (!opt.open) { | ||||
|                 opt.open = true; | ||||
|                 changed = true; | ||||
|             } | ||||
|  | ||||
|             item.drawHandler(item.ctx); | ||||
|             ImGui::Spacing(); | ||||
|         } | ||||
| @@ -59,17 +67,27 @@ void Menu::draw() { | ||||
|             ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6); | ||||
|             ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight()); | ||||
|             bool enabled = item.inst->isEnabled(); | ||||
|             if (ImGui::Checkbox(("##_menu_checkbox_" + name).c_str(), &enabled)) { | ||||
|             if (ImGui::Checkbox(("##_menu_checkbox_" + opt.name).c_str(), &enabled)) { | ||||
|                 enabled ? item.inst->enable() : item.inst->disable(); | ||||
|             } | ||||
|             ImGui::SetCursorPos(pos); | ||||
|  | ||||
|             if (opt.open) { | ||||
|                 opt.open = false; | ||||
|                 changed = true; | ||||
|             } | ||||
|         } | ||||
|         else if (opt.open) { | ||||
|             opt.open = false; | ||||
|             changed = true; | ||||
|         } | ||||
|     } | ||||
|     return changed; | ||||
| } | ||||
|  | ||||
| bool Menu::isInOrderList(std::string name) { | ||||
|     for (std::string _name : order) { | ||||
|         if (_name == name) { | ||||
|     for (MenuOption_t opt : order) { | ||||
|         if (opt.name == name) { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -8,6 +8,11 @@ class Menu { | ||||
| public: | ||||
|     Menu(); | ||||
|  | ||||
|     struct MenuOption_t { | ||||
|         std::string name; | ||||
|         bool open; | ||||
|     }; | ||||
|  | ||||
|     struct MenuItem_t { | ||||
|         void (*drawHandler)(void* ctx); | ||||
|         void* ctx; | ||||
| @@ -16,9 +21,9 @@ public: | ||||
|  | ||||
|     void registerEntry(std::string name, void (*drawHandler)(void* ctx), void* ctx = NULL, ModuleManager::Instance* inst = NULL); | ||||
|     void removeEntry(std::string name); | ||||
|     void draw(); | ||||
|     bool draw(); | ||||
|  | ||||
|     std::vector<std::string> order; | ||||
|     std::vector<MenuOption_t> order; | ||||
|  | ||||
| private: | ||||
|     bool isInOrderList(std::string name); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user