#include #include #include #include #include #include namespace hex { std::map ThemeManager::s_themes; std::map ThemeManager::s_themeHandlers; std::map ThemeManager::s_styleHandlers; std::string ThemeManager::s_imageTheme; std::string ThemeManager::s_currTheme; void ThemeManager::addThemeHandler(const std::string &name, const ColorMap &colorMap, const std::function &getFunction, const std::function &setFunction) { s_themeHandlers[name] = { colorMap, getFunction, setFunction }; } void ThemeManager::addStyleHandler(const std::string &name, const StyleMap &styleMap) { s_styleHandlers[name] = { styleMap }; } void ThemeManager::addTheme(const std::string &content) { auto theme = nlohmann::json::parse(content); if (theme.contains("name") && theme.contains("colors")) { s_themes[theme["name"].get()] = theme; } else { hex::log::error("Invalid theme file"); } } std::optional ThemeManager::parseColorString(const std::string &colorString) { if (colorString == "auto") return ImVec4(0, 0, 0, -1); if (colorString.length() != 9 || colorString[0] != '#') return std::nullopt; u32 color = 0; for (u32 i = 1; i < 9; i++) { color <<= 4; if (colorString[i] >= '0' && colorString[i] <= '9') color |= colorString[i] - '0'; else if (colorString[i] >= 'A' && colorString[i] <= 'F') color |= colorString[i] - 'A' + 10; else if (colorString[i] >= 'a' && colorString[i] <= 'f') color |= colorString[i] - 'a' + 10; else return std::nullopt; } if (color == 0x00000000) return ImVec4(0, 0, 0, -1); return ImColor(hex::changeEndianess(color, std::endian::big)); } nlohmann::json ThemeManager::exportCurrentTheme(const std::string &name) { nlohmann::json theme = { { "name", name }, { "image_theme", s_imageTheme }, { "colors", {} }, { "styles", {} }, { "base", s_currTheme } }; for (const auto &[type, handler] : s_themeHandlers) { theme["colors"][type] = {}; for (const auto &[key, value] : handler.colorMap) { auto color = handler.getFunction(value); theme["colors"][type][key] = fmt::format("#{:08X}", hex::changeEndianess(u32(color), std::endian::big)); } } for (const auto &[type, handler] : s_styleHandlers) { theme["styles"][type] = {}; for (const auto &[key, style] : handler.styleMap) { if (std::holds_alternative(style.value)) theme["styles"][type][key] = *std::get(style.value); else if (std::holds_alternative(style.value)) { theme["styles"][type][key] = { std::get(style.value)->x, std::get(style.value)->y }; } } } return theme; } void ThemeManager::changeTheme(std::string name) { if (!s_themes.contains(name)) { if (s_themes.empty()) { return; } else { const std::string &defaultTheme = s_themes.begin()->first; hex::log::error("Theme '{}' does not exist, using default theme '{}' instead!", name, defaultTheme); name = defaultTheme; } } const auto &theme = s_themes[name]; if (theme.contains("base")) { if (theme["base"].is_string()) { if (theme["base"] != name) changeTheme(theme["base"].get()); } else { hex::log::error("Theme '{}' has invalid base theme!", name); } } if (theme.contains("colors")) { for (const auto&[type, content] : theme["colors"].items()) { if (!s_themeHandlers.contains(type)) { log::warn("No theme handler found for '{}'", type); continue; } const auto &handler = s_themeHandlers[type]; for (const auto &[key, value] : content.items()) { if (!handler.colorMap.contains(key)) { log::warn("No color found for '{}.{}'", type, key); continue; } auto color = parseColorString(value.get()); if (!color.has_value()) { log::warn("Invalid color '{}' for '{}.{}'", value.get(), type, key); continue; } s_themeHandlers[type].setFunction(s_themeHandlers[type].colorMap.at(key), color.value()); } } } if (theme.contains("styles")) { for (const auto&[type, content] : theme["styles"].items()) { if (!s_styleHandlers.contains(type)) { log::warn("No style handler found for '{}'", type); continue; } auto &handler = s_styleHandlers[type]; for (const auto &[key, value] : content.items()) { if (!handler.styleMap.contains(key)) continue; auto &style = handler.styleMap.at(key); const float scale = style.needsScaling ? 1_scaled : 1.0F; if (value.is_number_float()) { if (auto newValue = std::get_if(&style.value); newValue != nullptr) **newValue = value.get() * scale; else log::warn("Style variable '{}' was of type ImVec2 but a float was expected.", name); } else if (value.is_array() && value.size() == 2 && value[0].is_number_float() && value[1].is_number_float()) { if (auto newValue = std::get_if(&style.value); newValue != nullptr) **newValue = ImVec2(value[0].get() * scale, value[1].get() * scale); else log::warn("Style variable '{}' was of type float but a ImVec2 was expected.", name); } else { hex::log::error("Theme '{}' has invalid style value for '{}.{}'!", name, type, key); } } } } if (theme.contains("image_theme")) { if (theme["image_theme"].is_string()) { s_imageTheme = theme["image_theme"].get(); } else { hex::log::error("Theme '{}' has invalid image theme!", name); s_imageTheme = "dark"; } } s_currTheme = name; EventManager::post(); } const std::string &ThemeManager::getImageTheme() { return s_imageTheme; } std::vector ThemeManager::getThemeNames() { std::vector themeNames; for (const auto &[name, theme] : s_themes) themeNames.push_back(name); return themeNames; } void ThemeManager::reset() { ThemeManager::s_themes.clear(); ThemeManager::s_styleHandlers.clear(); ThemeManager::s_themeHandlers.clear(); ThemeManager::s_imageTheme.clear(); ThemeManager::s_currTheme.clear(); } }