SDRPlusPlus/core/src/gui/theme_manager.cpp

237 wiersze
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 }
};