mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2025-01-09 17:57:11 +01:00
237 lines
8.2 KiB
C++
237 lines
8.2 KiB
C++
#include <json.hpp>
|
|
#include <gui/theme_manager.h>
|
|
#include <imgui_internal.h>
|
|
#include <spdlog/spdlog.h>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
|
|
bool ThemeManager::loadThemesFromDir(std::string path) {
|
|
// // TEST JUST TO DUMP THE ORIGINAL THEME
|
|
// auto& style = ImGui::GetStyle();
|
|
// ImVec4* colors = style.Colors;
|
|
|
|
// printf("\n\n");
|
|
// for (auto [name, id] : IMGUI_COL_IDS) {
|
|
// ImVec4 col = colors[id];
|
|
// uint8_t r = 255 - (col.x * 255.0f);
|
|
// uint8_t g = 255 - (col.y * 255.0f);
|
|
// uint8_t b = 255 - (col.z * 255.0f);
|
|
// uint8_t a = col.w * 255.0f;
|
|
// printf("\"%s\": \"#%02X%02X%02X%02X\",\n", name.c_str(), r, g, b, a);
|
|
// }
|
|
// printf("\n\n");
|
|
|
|
|
|
if (!std::filesystem::is_directory(path)) {
|
|
spdlog::error("Theme directory doesn't exist: {0}", path);
|
|
return false;
|
|
}
|
|
themes.clear();
|
|
for (const auto& file : std::filesystem::directory_iterator(path)) {
|
|
std::string _path = file.path().generic_string();
|
|
if (file.path().extension().generic_string() != ".json") {
|
|
continue;
|
|
}
|
|
loadTheme(_path);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ThemeManager::loadTheme(std::string path) {
|
|
if (!std::filesystem::is_regular_file(path)) {
|
|
spdlog::error("Theme file doesn't exist: {0}", path);
|
|
return false;
|
|
}
|
|
|
|
// Load defaults in theme
|
|
Theme thm;
|
|
thm.author = "--";
|
|
|
|
// Load JSON
|
|
std::ifstream file(path.c_str());
|
|
json data;
|
|
file >> data;
|
|
file.close();
|
|
|
|
// 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"];
|
|
|
|
if (themes.find(name) != themes.end()) {
|
|
spdlog::error("A theme named '{0}' already exists", name);
|
|
return false;
|
|
}
|
|
|
|
// 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; }
|
|
|
|
// Exception for non-imgu colors
|
|
if (param == "WaterfallBackground" || param == "ClearColor") {
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
|
|
bool ThemeManager::applyTheme(std::string name) {
|
|
if (themes.find(name) == themes.end()) {
|
|
spdlog::error("Unknown theme: {0}", 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];
|
|
|
|
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 == "WaterfallBackground") {
|
|
decodeRGBA(val, ret);
|
|
waterfallBg = ImVec4((float)ret[0] / 255.0f, (float)ret[1] / 255.0f, (float)ret[2] / 255.0f, (float)ret[3] / 255.0f);
|
|
continue;
|
|
}
|
|
|
|
if (param == "ClearColor") {
|
|
decodeRGBA(val, ret);
|
|
clearColor = ImVec4((float)ret[0] / 255.0f, (float)ret[1] / 255.0f, (float)ret[2] / 255.0f, (float)ret[3] / 255.0f);
|
|
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;
|
|
}
|
|
|
|
bool ThemeManager::decodeRGBA(std::string str, uint8_t out[4]) {
|
|
if (str[0] != '#' || !std::all_of(str.begin() + 1, str.end(), ::isxdigit) || str.length() != 9) {
|
|
return false;
|
|
}
|
|
out[0] = std::stoi(str.substr(1, 2), NULL, 16);
|
|
out[1] = std::stoi(str.substr(3, 2), NULL, 16);
|
|
out[2] = std::stoi(str.substr(5, 2), NULL, 16);
|
|
out[3] = std::stoi(str.substr(7, 2), NULL, 16);
|
|
return true;
|
|
}
|
|
|
|
std::vector<std::string> ThemeManager::getThemeNames() {
|
|
std::vector<std::string> names;
|
|
for (auto [name, theme] : themes) { names.push_back(name); }
|
|
return names;
|
|
}
|
|
|
|
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 }
|
|
}; |