diff --git a/lib/external/imgui/include/TextEditor.h b/lib/external/imgui/include/TextEditor.h index 55dc0269d..61fa48618 100644 --- a/lib/external/imgui/include/TextEditor.h +++ b/lib/external/imgui/include/TextEditor.h @@ -188,8 +188,8 @@ public: void SetLanguageDefinition(const LanguageDefinition& aLanguageDef); const LanguageDefinition& GetLanguageDefinition() const { return mLanguageDefinition; } - const Palette& GetPalette() const { return mPaletteBase; } - void SetPalette(const Palette& aValue); + static const Palette& GetPalette() { return sPaletteBase; } + static void SetPalette(const Palette& aValue); void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = aMarkers; } void SetBreakpoints(const Breakpoints& aMarkers) { mBreakpoints = aMarkers; } @@ -372,7 +372,7 @@ private: bool mIgnoreImGuiChild; bool mShowWhitespaces; - Palette mPaletteBase; + static Palette sPaletteBase; Palette mPalette; LanguageDefinition mLanguageDefinition; RegexList mRegexList; diff --git a/lib/external/imgui/source/TextEditor.cpp b/lib/external/imgui/source/TextEditor.cpp index f7e1c8cd0..02595a9c8 100644 --- a/lib/external/imgui/source/TextEditor.cpp +++ b/lib/external/imgui/source/TextEditor.cpp @@ -21,9 +21,10 @@ bool equals(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Bi return first1 == last1 && first2 == last2; } +TextEditor::Palette TextEditor::sPaletteBase = TextEditor::GetDarkPalette(); + TextEditor::TextEditor() : mLineSpacing(1.0f), mUndoIndex(0), mTabSize(4), mOverwrite(false), mReadOnly(false), mWithinRender(false), mScrollToCursor(false), mScrollToTop(false), mTextChanged(false), mColorizerEnabled(true), mTextStart(20.0f), mLeftMargin(10), mCursorPositionChanged(false), mColorRangeMin(0), mColorRangeMax(0), mSelectionMode(SelectionMode::Normal), mCheckComments(true), mLastClick(-1.0f), mHandleKeyboardInputs(true), mHandleMouseInputs(true), mIgnoreImGuiChild(false), mShowWhitespaces(true), mStartTime(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) { - SetPalette(GetDarkPalette()); SetLanguageDefinition(LanguageDefinition::HLSL()); mLines.push_back(Line()); } @@ -42,7 +43,7 @@ void TextEditor::SetLanguageDefinition(const LanguageDefinition &aLanguageDef) { } void TextEditor::SetPalette(const Palette &aValue) { - mPaletteBase = aValue; + sPaletteBase = aValue; } std::string TextEditor::GetText(const Coordinates &aStart, const Coordinates &aEnd) const { @@ -740,7 +741,7 @@ void TextEditor::Render() { /* Update palette with the current alpha from style */ for (int i = 0; i < (int)PaletteIndex::Max; ++i) { - auto color = ImGui::ColorConvertU32ToFloat4(mPaletteBase[i]); + auto color = ImGui::ColorConvertU32ToFloat4(sPaletteBase[i]); color.w *= ImGui::GetStyle().Alpha; mPalette[i] = ImGui::ColorConvertFloat4ToU32(color); } diff --git a/lib/libimhex/CMakeLists.txt b/lib/libimhex/CMakeLists.txt index b4fe35d55..28dea2dd2 100644 --- a/lib/libimhex/CMakeLists.txt +++ b/lib/libimhex/CMakeLists.txt @@ -116,6 +116,7 @@ set(LIBIMHEX_SOURCES source/api/plugin_manager.cpp source/api/localization.cpp source/api/project_file_manager.cpp + source/api/theme_manager.cpp source/data_processor/attribute.cpp source/data_processor/link.cpp diff --git a/lib/libimhex/include/hex/api/event.hpp b/lib/libimhex/include/hex/api/event.hpp index 91afe8f38..3ce63dcba 100644 --- a/lib/libimhex/include/hex/api/event.hpp +++ b/lib/libimhex/include/hex/api/event.hpp @@ -128,7 +128,7 @@ namespace hex { EVENT_DEF(RequestCloseImHex, bool); EVENT_DEF(RequestRestartImHex); EVENT_DEF(RequestOpenFile, std::fs::path); - EVENT_DEF(RequestChangeTheme, u32); + EVENT_DEF(RequestChangeTheme, std::string); EVENT_DEF(RequestOpenPopup, std::string); EVENT_DEF(RequestCreateProvider, std::string, bool, hex::prv::Provider **); diff --git a/lib/libimhex/include/hex/api/imhex_api.hpp b/lib/libimhex/include/hex/api/imhex_api.hpp index 290d8f258..ba4ec5a3d 100644 --- a/lib/libimhex/include/hex/api/imhex_api.hpp +++ b/lib/libimhex/include/hex/api/imhex_api.hpp @@ -191,12 +191,6 @@ namespace hex { char **envp; }; - enum class Theme { - Dark = 1, - Light = 2, - Classic = 3 - }; - const ProgramArguments &getProgramArguments(); std::optional getProgramArgument(int index); @@ -218,9 +212,6 @@ namespace hex { const std::filesystem::path &getCustomFontPath(); float getFontSize(); - void setTheme(Theme theme); - Theme getTheme(); - void enableSystemThemeDetection(bool enabled); bool usesSystemThemeDetection(); diff --git a/lib/libimhex/include/hex/api/theme_manager.hpp b/lib/libimhex/include/hex/api/theme_manager.hpp new file mode 100644 index 000000000..438f24e5a --- /dev/null +++ b/lib/libimhex/include/hex/api/theme_manager.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +namespace hex::api { + + class ThemeManager { + public: + constexpr static auto NativeTheme = "Native"; + + static void changeTheme(std::string name); + + static void addTheme(const std::string &content); + static void addThemeHandler(const std::string &name, const std::function &handler); + static void addStyleHandler(const std::string &name, const std::function &handler); + + static std::vector getThemeNames(); + static const std::string &getThemeImagePostfix(); + + static std::optional parseColorString(const std::string &colorString); + private: + ThemeManager() = default; + + static std::map s_themes; + static std::map> s_themeHandlers, s_styleHandlers; + static std::string s_imagePostfix; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/helpers/fs.hpp b/lib/libimhex/include/hex/helpers/fs.hpp index 6b5c504cc..31621e676 100644 --- a/lib/libimhex/include/hex/helpers/fs.hpp +++ b/lib/libimhex/include/hex/helpers/fs.hpp @@ -101,6 +101,7 @@ namespace hex::fs { Recent, Scripts, Inspectors, + Themes, END }; diff --git a/lib/libimhex/source/api/imhex_api.cpp b/lib/libimhex/source/api/imhex_api.cpp index 0704f806d..d7415676a 100644 --- a/lib/libimhex/source/api/imhex_api.cpp +++ b/lib/libimhex/source/api/imhex_api.cpp @@ -480,19 +480,8 @@ namespace hex { } - static Theme s_theme; static bool s_systemThemeDetection; - void setTheme(Theme theme) { - s_theme = theme; - - EventManager::post(); - } - - Theme getTheme() { - return s_theme; - } - void enableSystemThemeDetection(bool enabled) { s_systemThemeDetection = enabled; diff --git a/lib/libimhex/source/api/theme_manager.cpp b/lib/libimhex/source/api/theme_manager.cpp new file mode 100644 index 000000000..4b6ddbe91 --- /dev/null +++ b/lib/libimhex/source/api/theme_manager.cpp @@ -0,0 +1,118 @@ +#include + +#include +#include + +#include + +namespace hex::api { + + std::map ThemeManager::s_themes; + std::map> ThemeManager::s_themeHandlers, ThemeManager::s_styleHandlers; + std::string ThemeManager::s_imagePostfix; + + void ThemeManager::addThemeHandler(const std::string &name, const std::function &handler) { + s_themeHandlers[name] = handler; + } + + void ThemeManager::addStyleHandler(const std::string &name, const std::function &handler) { + s_styleHandlers[name] = handler; + } + + 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.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; + } + + return ImColor(hex::changeEndianess(color, std::endian::big)); + } + + void ThemeManager::changeTheme(std::string name) { + if (!s_themes.contains(name)) { + if (s_themes.empty()) { + hex::log::error("Theme '{}' does not exist and no other themes are available!", name); + 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()) { + 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; + } + + for (const auto &[key, value] : content.items()) + s_themeHandlers[type](key, value.get()); + } + } + + if (theme.contains("styles")) { + for (const auto&[key, value] : theme["styles"].items()) { + if (!s_styleHandlers.contains(key)) { + log::warn("No style handler found for '{}'", key); + continue; + } + + s_styleHandlers[key](name, value.get()); + } + } + + if (theme.contains("image_postfix")) { + if (theme["image_postfix"].is_string()) { + s_imagePostfix = theme["image_postfix"].get(); + } else { + hex::log::error("Theme '{}' has invalid image postfix!", name); + } + } + } + + const std::string &ThemeManager::getThemeImagePostfix() { + return s_imagePostfix; + } + + std::vector ThemeManager::getThemeNames() { + std::vector themeNames; + for (const auto &[name, theme] : s_themes) + themeNames.push_back(name); + + return themeNames; + } + +} \ No newline at end of file diff --git a/lib/libimhex/source/helpers/fs.cpp b/lib/libimhex/source/helpers/fs.cpp index ab6e30d5e..e54348464 100644 --- a/lib/libimhex/source/helpers/fs.cpp +++ b/lib/libimhex/source/helpers/fs.cpp @@ -253,6 +253,9 @@ namespace hex::fs { case ImHexPath::Inspectors: result = appendPath(getDefaultPaths(ImHexPath::Scripts), "inspectors"); break; + case ImHexPath::Themes: + result = appendPath(getDataPaths(), "themes"); + break; } if (!listNonExisting) { diff --git a/main/source/main.cpp b/main/source/main.cpp index 0ee0e2682..b497847d7 100644 --- a/main/source/main.cpp +++ b/main/source/main.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "window.hpp" diff --git a/main/source/window/linux_window.cpp b/main/source/window/linux_window.cpp index 2fd9512e0..ff7442031 100644 --- a/main/source/window/linux_window.cpp +++ b/main/source/window/linux_window.cpp @@ -43,7 +43,7 @@ namespace hex { auto exitCode = WEXITSTATUS(pclose(pipe)); if (exitCode != 0) return; - EventManager::post(hex::containsIgnoreCase(result, "light") ? 2 : 1); + EventManager::post(hex::containsIgnoreCase(result, "light") ? "Light" : "Dark"); }); if (themeFollowSystem) diff --git a/main/source/window/macos_window.cpp b/main/source/window/macos_window.cpp index 8e3e9320b..36db83b52 100644 --- a/main/source/window/macos_window.cpp +++ b/main/source/window/macos_window.cpp @@ -30,9 +30,9 @@ namespace hex { if (!themeFollowSystem) return; if (!isMacosSystemDarkModeEnabled()) - EventManager::post(2); + EventManager::post("Light"); else - EventManager::post(1); + EventManager::post("Dark"); }); if (themeFollowSystem) diff --git a/main/source/window/window.cpp b/main/source/window/window.cpp index 746283c2e..7f0cb703d 100644 --- a/main/source/window/window.cpp +++ b/main/source/window/window.cpp @@ -106,7 +106,7 @@ namespace hex { this->setupNativeWindow(); // Initialize default theme - EventManager::post(1); + EventManager::post("Dark"); EventManager::subscribe(this, [this](bool noQuestions) { glfwSetWindowShouldClose(this->m_window, GLFW_TRUE); @@ -688,6 +688,8 @@ namespace hex { style.Alpha = 1.0F; style.WindowRounding = 0.0F; + ImNodes::GetStyle().Flags = ImNodesStyleFlags_NodeOutline | ImNodesStyleFlags_GridLines; + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_NavEnableKeyboard; io.ConfigWindowsMoveFromTitleBarOnly = true; io.FontGlobalScale = 1.0F; diff --git a/plugins/builtin/CMakeLists.txt b/plugins/builtin/CMakeLists.txt index 7ec49be4b..f8486ebf6 100644 --- a/plugins/builtin/CMakeLists.txt +++ b/plugins/builtin/CMakeLists.txt @@ -25,6 +25,7 @@ add_library(${PROJECT_NAME} SHARED source/content/hashes.cpp source/content/shortcuts.cpp source/content/global_actions.cpp + source/content/themes.cpp source/content/providers/file_provider.cpp source/content/providers/gdb_provider.cpp diff --git a/plugins/builtin/romfs/lang/de_DE.json b/plugins/builtin/romfs/lang/de_DE.json index e115c1a0b..df4e8335f 100644 --- a/plugins/builtin/romfs/lang/de_DE.json +++ b/plugins/builtin/romfs/lang/de_DE.json @@ -372,10 +372,6 @@ "hex.builtin.setting.imhex.recent_files": "Zuletzt geöffnete Dateien", "hex.builtin.setting.interface": "Aussehen", "hex.builtin.setting.interface.color": "Farbdesign", - "hex.builtin.setting.interface.color.classic": "Klassisch", - "hex.builtin.setting.interface.color.dark": "Dunkel", - "hex.builtin.setting.interface.color.light": "Hell", - "hex.builtin.setting.interface.color.system": "System", "hex.builtin.setting.interface.fps": "FPS Limit", "hex.builtin.setting.interface.fps.unlocked": "Unbegrenzt", "hex.builtin.setting.interface.language": "Sprache", diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index d260f3016..8ddd765fc 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -373,10 +373,6 @@ "hex.builtin.setting.imhex.recent_files": "Recent Files", "hex.builtin.setting.interface": "Interface", "hex.builtin.setting.interface.color": "Color theme", - "hex.builtin.setting.interface.color.classic": "Classic", - "hex.builtin.setting.interface.color.dark": "Dark", - "hex.builtin.setting.interface.color.light": "Light", - "hex.builtin.setting.interface.color.system": "System", "hex.builtin.setting.interface.fps": "FPS Limit", "hex.builtin.setting.interface.fps.unlocked": "Unlocked", "hex.builtin.setting.interface.language": "Language", diff --git a/plugins/builtin/romfs/lang/it_IT.json b/plugins/builtin/romfs/lang/it_IT.json index 1beafbac2..33b306b16 100644 --- a/plugins/builtin/romfs/lang/it_IT.json +++ b/plugins/builtin/romfs/lang/it_IT.json @@ -371,10 +371,6 @@ "hex.builtin.setting.imhex.recent_files": "File recenti", "hex.builtin.setting.interface": "Interfaccia", "hex.builtin.setting.interface.color": "Colore del Tema", - "hex.builtin.setting.interface.color.classic": "Classico", - "hex.builtin.setting.interface.color.dark": "Scuro", - "hex.builtin.setting.interface.color.light": "Chiaro", - "hex.builtin.setting.interface.color.system": "Sistema", "hex.builtin.setting.interface.fps": "Limite FPS", "hex.builtin.setting.interface.fps.unlocked": "Unblocca", "hex.builtin.setting.interface.language": "Lingua", diff --git a/plugins/builtin/romfs/lang/ja_JP.json b/plugins/builtin/romfs/lang/ja_JP.json index a5cca651a..ba53aeade 100644 --- a/plugins/builtin/romfs/lang/ja_JP.json +++ b/plugins/builtin/romfs/lang/ja_JP.json @@ -371,10 +371,6 @@ "hex.builtin.setting.imhex.recent_files": "最近開いたファイル", "hex.builtin.setting.interface": "UI", "hex.builtin.setting.interface.color": "カラーテーマ", - "hex.builtin.setting.interface.color.classic": "クラシック", - "hex.builtin.setting.interface.color.dark": "ダーク", - "hex.builtin.setting.interface.color.light": "ライト", - "hex.builtin.setting.interface.color.system": "システム設定に従う", "hex.builtin.setting.interface.fps": "FPS制限", "hex.builtin.setting.interface.fps.unlocked": "無制限", "hex.builtin.setting.interface.language": "言語", diff --git a/plugins/builtin/romfs/lang/ko_KR.json b/plugins/builtin/romfs/lang/ko_KR.json index bf6fcc10d..4c0a22d6e 100644 --- a/plugins/builtin/romfs/lang/ko_KR.json +++ b/plugins/builtin/romfs/lang/ko_KR.json @@ -371,10 +371,6 @@ "hex.builtin.setting.imhex.recent_files": "최근 파일", "hex.builtin.setting.interface": "인터페이스", "hex.builtin.setting.interface.color": "색상 테마", - "hex.builtin.setting.interface.color.classic": "클래식", - "hex.builtin.setting.interface.color.dark": "다크", - "hex.builtin.setting.interface.color.light": "라이트", - "hex.builtin.setting.interface.color.system": "시스템", "hex.builtin.setting.interface.fps": "FPS 제한", "hex.builtin.setting.interface.fps.unlocked": "제한 없음", "hex.builtin.setting.interface.language": "언어", diff --git a/plugins/builtin/romfs/lang/pt_BR.json b/plugins/builtin/romfs/lang/pt_BR.json index d92e8bb51..6137ea36a 100644 --- a/plugins/builtin/romfs/lang/pt_BR.json +++ b/plugins/builtin/romfs/lang/pt_BR.json @@ -371,10 +371,6 @@ "hex.builtin.setting.imhex.recent_files": "Arquivos Recentes", "hex.builtin.setting.interface": "Interface", "hex.builtin.setting.interface.color": "Color theme", - "hex.builtin.setting.interface.color.classic": "Classico", - "hex.builtin.setting.interface.color.dark": "Escuro", - "hex.builtin.setting.interface.color.light": "Claro", - "hex.builtin.setting.interface.color.system": "Sistema", "hex.builtin.setting.interface.fps": "FPS Limit", "hex.builtin.setting.interface.fps.unlocked": "Destravado", "hex.builtin.setting.interface.language": "Idioma", diff --git a/plugins/builtin/romfs/lang/zh_CN.json b/plugins/builtin/romfs/lang/zh_CN.json index 924024935..022b47416 100644 --- a/plugins/builtin/romfs/lang/zh_CN.json +++ b/plugins/builtin/romfs/lang/zh_CN.json @@ -370,11 +370,6 @@ "hex.builtin.setting.imhex": "ImHex", "hex.builtin.setting.imhex.recent_files": "最近文件", "hex.builtin.setting.interface": "界面", - "hex.builtin.setting.interface.color": "颜色主题", - "hex.builtin.setting.interface.color.classic": "经典", - "hex.builtin.setting.interface.color.dark": "暗", - "hex.builtin.setting.interface.color.light": "亮", - "hex.builtin.setting.interface.color.system": "跟随系统", "hex.builtin.setting.interface.fps": "FPS 限制", "hex.builtin.setting.interface.fps.unlocked": "无限制", "hex.builtin.setting.interface.language": "语言", diff --git a/plugins/builtin/romfs/lang/zh_TW.json b/plugins/builtin/romfs/lang/zh_TW.json index 7b179aa9c..2232c534c 100644 --- a/plugins/builtin/romfs/lang/zh_TW.json +++ b/plugins/builtin/romfs/lang/zh_TW.json @@ -371,10 +371,6 @@ "hex.builtin.setting.imhex.recent_files": "近期檔案", "hex.builtin.setting.interface": "介面", "hex.builtin.setting.interface.color": "顏色主題", - "hex.builtin.setting.interface.color.classic": "經典", - "hex.builtin.setting.interface.color.dark": "暗色", - "hex.builtin.setting.interface.color.light": "亮色", - "hex.builtin.setting.interface.color.system": "系統", "hex.builtin.setting.interface.fps": "FPS 限制", "hex.builtin.setting.interface.fps.unlocked": "解鎖", "hex.builtin.setting.interface.language": "語言", diff --git a/plugins/builtin/romfs/themes/classic.json b/plugins/builtin/romfs/themes/classic.json new file mode 100644 index 000000000..51fcc8af2 --- /dev/null +++ b/plugins/builtin/romfs/themes/classic.json @@ -0,0 +1,153 @@ +{ + "name": "Classic", + "image_postfix": "_dark", + "colors": { + "imgui": { + "border": "#7F7F7F7F", + "border-shadow": "#00000000", + "button": "#59669B9E", + "button-active": "#7589CCFF", + "button-hovered": "#667AB5C9", + "check-mark": "#E5E5E57F", + "child-background": "#00000000", + "docking-empty-background": "#000000D8", + "docking-preview": "#6666E550", + "drag-drop-target": "#FFFF00E5", + "frame-background": "#6D6D6D63", + "frame-background-active": "#6B68A3AF", + "frame-background-hovered": "#7777AF66", + "header": "#6666E572", + "header-active": "#8787DDCC", + "header-hovered": "#7272E5CC", + "menu-bar-background": "#66668CCC", + "modal-window-dim-background": "#33333359", + "nav-highlight": "#7272E5CC", + "nav-windowing-background": "#CCCCCC33", + "nav-windowing-highlight": "#FFFFFFB2", + "plot-histogram": "#E5B200FF", + "plot-histogram-hovered": "#FF9900FF", + "plot-lines": "#FFFFFFFF", + "plot-lines-hovered": "#E5B200FF", + "popup-background": "#1C1C23EA", + "resize-grip": "#FFFFFF19", + "resize-grip-active": "#C6D1FFE5", + "resize-grip-hovered": "#C6D1FF99", + "scrollbar-background": "#333F4C99", + "scrollbar-grab": "#6666CC4C", + "scrollbar-grab-active": "#6863CC99", + "scrollbar-grab-hovered": "#6666CC66", + "separator": "#7F7F7F99", + "separator-active": "#B2B2E5FF", + "separator-hovered": "#9999B2FF", + "slider-grab": "#FFFFFF4C", + "slider-grab-active": "#6863CC99", + "tab": "#5555AEC8", + "tab-active": "#6767B9D6", + "tab-hovered": "#7272E5CC", + "tab-unfocused": "#484891D1", + "tab-unfocused-active": "#5959A6D5", + "table-border-light": "#424247FF", + "table-border-strong": "#4F4F72FF", + "table-header-background": "#444460FF", + "table-row-background": "#00000000", + "table-row-background-alt": "#FFFFFF11", + "text": "#E5E5E5FF", + "text-disabled": "#999999FF", + "text-selected-background": "#0000FF59", + "title-background": "#66668CCC", + "title-background-active": "#66668CCC", + "title-background-collapse": "#66668CCC", + "window-background": "#000000D8" + }, + "implot": { + "axis-bg": "#00000001", + "axis-bg-active": "#00000001", + "axis-bg-hovered": "#00000001", + "axis-grid": "#E5E5E53F", + "axis-text": "#E5E5E5FF", + "axis-tick": "#00000001", + "crosshairs": "#7F7F7FBF", + "error-bar": "#E5E5E5FF", + "fill": "#00000001", + "frame-bg": "#6D6D6D63", + "inlay-text": "#E5E5E5FF", + "legend-bg": "#1C1C23EA", + "legend-border": "#7F7F7F7F", + "legend-text": "#E5E5E5FF", + "line": "#00000001", + "marker-fill": "#00000001", + "marker-outline": "#00000001", + "plot-bg": "#00000059", + "plot-border": "#7F7F7F7F", + "selection": "#F7F763FF", + "title-text": "#E5E5E5FF" + }, + "imnodes": { + "box-selector": "#00000000", + "box-selector-outline": "#00E50000", + "grid-background": "#00000000", + "grid-line": "#00000000", + "grid-line-primary": "#00000000", + "link": "#06539BFF", + "link-hovered": "#672A78FF", + "link-selected": "#DBB377FF", + "mini-map-background": "#00000000", + "mini-map-background-hovered": "#0000FB90", + "mini-map-canvas": "#00000000", + "mini-map-canvas-outline": "#00000000", + "mini-map-link": "#00000000", + "mini-map-link-selected": "#00000000", + "mini-map-node-background": "#00000000", + "mini-map-node-background-hovered": "#00000000", + "mini-map-node-background-selected": "#00000000", + "mini-map-node-outline": "#00000000", + "mini-map-outline": "#00000000", + "mini-map-outline-hovered": "#00000000", + "node-background": "#282850FF", + "node-background-hovered": "#3C3C64FF", + "node-background-selected": "#505078FF", + "node-outline": "#E6E6E6FF", + "pin": "#4DC69BFF", + "pin-hovered": "#00000000", + "title-bar": "#E74C3CFF", + "title-bar-hovered": "#F1C40FFF", + "title-bar-selected": "#388B42FF" + }, + "imhex": { + "desc-button": "#282850FF", + "desc-button-active": "#505078FF", + "desc-button-hovered": "#3C3C64FF", + "highlight": "#4DC69BFF", + "toolbar-blue": "#06539BFF", + "toolbar-brown": "#DBB377FF", + "toolbar-gray": "#E6E6E6FF", + "toolbar-green": "#388B42FF", + "toolbar-purple": "#672A78FF", + "toolbar-red": "#E74C3CFF", + "toolbar-yellow": "#F1C40FFF" + }, + "text-editor": { + "background": "#000080FF", + "breakpoint": "#0080FF80", + "char-literal": "#008080FF", + "comment": "#808080FF", + "current-line-edge": "#00000040", + "current-line-fill": "#00000040", + "current-line-fill-inactive": "#80808040", + "cursor": "#FF8000FF", + "default": "#FFFF00FF", + "error-marker": "#FF0000A0", + "identifier": "#FFFF00FF", + "keyword": "#00FFFFFF", + "known-identifier": "#FFFFFFFF", + "line-number": "#008080FF", + "multi-line-comment": "#404040FF", + "number": "#00FF00FF", + "preproc-identifier": "#FF00FFFF", + "preprocessor": "#008000FF", + "punctuation": "#FFFFFFFF", + "selection": "#00FFFF80", + "string": "#008080FF" + } + } +} \ No newline at end of file diff --git a/plugins/builtin/romfs/themes/dark.json b/plugins/builtin/romfs/themes/dark.json new file mode 100644 index 000000000..9169d679e --- /dev/null +++ b/plugins/builtin/romfs/themes/dark.json @@ -0,0 +1,153 @@ +{ + "name": "Dark", + "image_postfix": "_dark", + "colors": { + "imgui": { + "border": "#6D6D7F7F", + "border-shadow": "#00000000", + "button": "#4296F966", + "button-active": "#0F87F9FF", + "button-hovered": "#4296F9FF", + "check-mark": "#4296F9FF", + "child-background": "#00000000", + "docking-empty-background": "#0F0F0FEF", + "docking-preview": "#4296F9B2", + "drag-drop-target": "#FFFF00E5", + "frame-background": "#28497A89", + "frame-background-active": "#4296F9AA", + "frame-background-hovered": "#4296F966", + "header": "#4296F94F", + "header-active": "#4296F9FF", + "header-hovered": "#4296F9CC", + "menu-bar-background": "#232323FF", + "modal-window-dim-background": "#CCCCCC59", + "nav-highlight": "#4296F9FF", + "nav-windowing-background": "#CCCCCC33", + "nav-windowing-highlight": "#FFFFFFB2", + "plot-histogram": "#E5B200FF", + "plot-histogram-hovered": "#FF9900FF", + "plot-lines": "#9B9B9BFF", + "plot-lines-hovered": "#FF6D59FF", + "popup-background": "#141414EF", + "resize-grip": "#4296F933", + "resize-grip-active": "#4296F9F2", + "resize-grip-hovered": "#4296F9AA", + "scrollbar-background": "#05050587", + "scrollbar-grab": "#4F4F4FFF", + "scrollbar-grab-active": "#828282FF", + "scrollbar-grab-hovered": "#686868FF", + "separator": "#6D6D7F7F", + "separator-active": "#1966BFFF", + "separator-hovered": "#1966BFC6", + "slider-grab": "#3D84E0FF", + "slider-grab-active": "#4296F9FF", + "tab": "#2D5993DB", + "tab-active": "#3268ADFF", + "tab-hovered": "#4296F9CC", + "tab-unfocused": "#111A25F7", + "tab-unfocused-active": "#22426CFF", + "table-border-light": "#3A3A3FFF", + "table-border-strong": "#4F4F59FF", + "table-header-background": "#303033FF", + "table-row-background": "#00000000", + "table-row-background-alt": "#FFFFFF0F", + "text": "#FFFFFFFF", + "text-disabled": "#7F7F7FFF", + "text-selected-background": "#4296F959", + "title-background": "#232323FF", + "title-background-active": "#232323FF", + "title-background-collapse": "#232323FF", + "window-background": "#0F0F0FEF" + }, + "implot": { + "axis-bg": "#00000001", + "axis-bg-active": "#00000001", + "axis-bg-hovered": "#00000001", + "axis-grid": "#FFFFFF3F", + "axis-text": "#FFFFFFFF", + "axis-tick": "#00000001", + "crosshairs": "#FFFFFF7F", + "error-bar": "#00000001", + "fill": "#00000001", + "frame-bg": "#FFFFFF11", + "inlay-text": "#FFFFFFFF", + "legend-bg": "#141414EF", + "legend-border": "#6D6D7F7F", + "legend-text": "#FFFFFFFF", + "line": "#00000001", + "marker-fill": "#00000001", + "marker-outline": "#00000001", + "plot-bg": "#0000007F", + "plot-border": "#6D6D7F7F", + "selection": "#FF9900FF", + "title-text": "#FFFFFFFF" + }, + "imnodes": { + "box-selector": "#AB000000", + "box-selector-outline": "#00E20000", + "grid-background": "#42009900", + "grid-line": "#63008100", + "grid-line-primary": "#EA005B00", + "link": "#06539BFF", + "link-hovered": "#672A78FF", + "link-selected": "#DBB377FF", + "mini-map-background": "#69001B00", + "mini-map-background-hovered": "#7100B5A6", + "mini-map-canvas": "#00000000", + "mini-map-canvas-outline": "#00000000", + "mini-map-link": "#00000000", + "mini-map-link-selected": "#00000000", + "mini-map-node-background": "#00000000", + "mini-map-node-background-hovered": "#00000000", + "mini-map-node-background-selected": "#00000000", + "mini-map-node-outline": "#00000000", + "mini-map-outline": "#00000000", + "mini-map-outline-hovered": "#00000000", + "node-background": "#282850FF", + "node-background-hovered": "#3C3C64FF", + "node-background-selected": "#505078FF", + "node-outline": "#E6E6E6FF", + "pin": "#4DC69BFF", + "pin-hovered": "#00000000", + "title-bar": "#E74C3CFF", + "title-bar-hovered": "#F1C40FFF", + "title-bar-selected": "#388B42FF" + }, + "imhex": { + "desc-button": "#141414FF", + "desc-button-active": "#3C3C3CFF", + "desc-button-hovered": "#282828FF", + "highlight": "#4DC69BFF", + "toolbar-blue": "#06539BFF", + "toolbar-brown": "#DBB377FF", + "toolbar-gray": "#E6E6E6FF", + "toolbar-green": "#388B42FF", + "toolbar-purple": "#672A78FF", + "toolbar-red": "#E74C3CFF", + "toolbar-yellow": "#F1C40FFF" + }, + "text-editor": { + "background": "#101010FF", + "breakpoint": "#0080F040", + "char-literal": "#E0A070FF", + "comment": "#206020FF", + "current-line-edge": "#A0A0A040", + "current-line-fill": "#00000040", + "current-line-fill-inactive": "#80808040", + "cursor": "#E0E0E0FF", + "default": "#7F7F7FFF", + "error-marker": "#FF200080", + "identifier": "#AAAAAAFF", + "keyword": "#569CD6FF", + "known-identifier": "#4DC69BFF", + "line-number": "#007070FF", + "multi-line-comment": "#206040FF", + "number": "#00FF00FF", + "preproc-identifier": "#A040C0FF", + "preprocessor": "#808040FF", + "punctuation": "#FFFFFFFF", + "selection": "#2060A080", + "string": "#E07070FF" + } + } +} \ No newline at end of file diff --git a/plugins/builtin/romfs/themes/light.json b/plugins/builtin/romfs/themes/light.json new file mode 100644 index 000000000..7b6cb8fb1 --- /dev/null +++ b/plugins/builtin/romfs/themes/light.json @@ -0,0 +1,153 @@ +{ + "name": "Light", + "image_postfix": "_light", + "colors": { + "imgui": { + "border": "#0000004C", + "border-shadow": "#00000000", + "button": "#4296F966", + "button-active": "#0F87F9FF", + "button-hovered": "#4296F9FF", + "check-mark": "#4296F9FF", + "child-background": "#00000000", + "docking-empty-background": "#EFEFEFFF", + "docking-preview": "#4296F937", + "drag-drop-target": "#4296F9F2", + "frame-background": "#FFFFFFFF", + "frame-background-active": "#4296F9AA", + "frame-background-hovered": "#4296F966", + "header": "#4296F94F", + "header-active": "#4296F9FF", + "header-hovered": "#4296F9CC", + "menu-bar-background": "#DBDBDBFF", + "modal-window-dim-background": "#33333359", + "nav-highlight": "#4296F9CC", + "nav-windowing-background": "#33333333", + "nav-windowing-highlight": "#B2B2B2B2", + "plot-histogram": "#E5B200FF", + "plot-histogram-hovered": "#FF7200FF", + "plot-lines": "#636363FF", + "plot-lines-hovered": "#FF6D59FF", + "popup-background": "#FFFFFFF9", + "resize-grip": "#5959592B", + "resize-grip-active": "#4296F9F2", + "resize-grip-hovered": "#4296F9AA", + "scrollbar-background": "#F9F9F987", + "scrollbar-grab": "#AFAFAFCC", + "scrollbar-grab-active": "#7C7C7CFF", + "scrollbar-grab-hovered": "#7C7C7CCC", + "separator": "#6363639E", + "separator-active": "#2370CCFF", + "separator-hovered": "#2370CCC6", + "slider-grab": "#4296F9C6", + "slider-grab-active": "#7589CC99", + "tab": "#C2CBD5ED", + "tab-active": "#97B9E1FF", + "tab-hovered": "#4296F9CC", + "tab-unfocused": "#EAECEEFB", + "tab-unfocused-active": "#BDD1E9FF", + "table-border-light": "#ADADBCFF", + "table-border-strong": "#9191A3FF", + "table-header-background": "#C6DDF9FF", + "table-row-background": "#00000000", + "table-row-background-alt": "#4C4C4C16", + "text": "#000000FF", + "text-disabled": "#999999FF", + "text-selected-background": "#4296F959", + "title-background": "#DBDBDBFF", + "title-background-active": "#DBDBDBFF", + "title-background-collapse": "#DBDBDBFF", + "window-background": "#EFEFEFFF" + }, + "implot": { + "axis-bg": "#00000001", + "axis-bg-active": "#00000001", + "axis-bg-hovered": "#00000001", + "axis-grid": "#FFFFFFFF", + "axis-text": "#000000FF", + "axis-tick": "#0000003F", + "crosshairs": "#0000007F", + "error-bar": "#00000001", + "fill": "#00000001", + "frame-bg": "#FFFFFFFF", + "inlay-text": "#000000FF", + "legend-bg": "#FFFFFFF9", + "legend-border": "#D1D1D1CC", + "legend-text": "#000000FF", + "line": "#00000001", + "marker-fill": "#00000001", + "marker-outline": "#00000001", + "plot-bg": "#6B91FF21", + "plot-border": "#00000000", + "selection": "#D1A307FF", + "title-text": "#000000FF" + }, + "imnodes": { + "box-selector": "#00000000", + "box-selector-outline": "#00E20000", + "grid-background": "#00000000", + "grid-line": "#00000000", + "grid-line-primary": "#00000000", + "link": "#06539BFF", + "link-hovered": "#672A78FF", + "link-selected": "#DBB377FF", + "mini-map-background": "#00000000", + "mini-map-background-hovered": "#0000B00C", + "mini-map-canvas": "#00000000", + "mini-map-canvas-outline": "#00000000", + "mini-map-link": "#00000000", + "mini-map-link-selected": "#00000000", + "mini-map-node-background": "#00000000", + "mini-map-node-background-hovered": "#00000000", + "mini-map-node-background-selected": "#00000000", + "mini-map-node-outline": "#00000000", + "mini-map-outline": "#00000000", + "mini-map-outline-hovered": "#00000000", + "node-background": "#282850FF", + "node-background-hovered": "#3C3C64FF", + "node-background-selected": "#505078FF", + "node-outline": "#E6E6E6FF", + "pin": "#4DC69BFF", + "pin-hovered": "#00000000", + "title-bar": "#E74C3CFF", + "title-bar-hovered": "#F1C40FFF", + "title-bar-selected": "#388B42FF" + }, + "imhex": { + "desc-button": "#E6E6E6FF", + "desc-button-active": "#BEBEBEFF", + "desc-button-hovered": "#D2D2D2FF", + "highlight": "#299770FF", + "toolbar-blue": "#06539BFF", + "toolbar-brown": "#DBB377FF", + "toolbar-gray": "#191919FF", + "toolbar-green": "#388B42FF", + "toolbar-purple": "#672A78FF", + "toolbar-red": "#E74C3CFF", + "toolbar-yellow": "#F1C40FFF" + }, + "text-editor": { + "background": "#FFFFFFFF", + "breakpoint": "#0080F080", + "char-literal": "#704030FF", + "comment": "#205020FF", + "current-line-edge": "#00000040", + "current-line-fill": "#00000040", + "current-line-fill-inactive": "#80808040", + "cursor": "#000000FF", + "default": "#7F7F7FFF", + "error-marker": "#FF1000A0", + "identifier": "#404040FF", + "keyword": "#060CFFFF", + "known-identifier": "#106060FF", + "line-number": "#005050FF", + "multi-line-comment": "#205040FF", + "number": "#008000FF", + "preproc-identifier": "#A040C0FF", + "preprocessor": "#606040FF", + "punctuation": "#000000FF", + "selection": "#00006080", + "string": "#A02020FF" + } + } +} \ No newline at end of file diff --git a/plugins/builtin/source/content/settings_entries.cpp b/plugins/builtin/source/content/settings_entries.cpp index 46e1941ab..fb76f7da9 100644 --- a/plugins/builtin/source/content/settings_entries.cpp +++ b/plugins/builtin/source/content/settings_entries.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -97,27 +98,34 @@ namespace hex::plugin::builtin { /* Interface */ - ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", 0, [](auto name, nlohmann::json &setting) { - static int selection = static_cast(setting); + ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", "Dark", [](auto name, nlohmann::json &setting) { + static auto selection = static_cast(setting); - const char *themes[] = { - "hex.builtin.setting.interface.color.system"_lang, - "hex.builtin.setting.interface.color.dark"_lang, - "hex.builtin.setting.interface.color.light"_lang, - "hex.builtin.setting.interface.color.classic"_lang - }; + const auto themeNames = hex::api::ThemeManager::getThemeNames(); + bool changed = false; - if (ImGui::Combo(name.data(), &selection, themes, IM_ARRAYSIZE(themes))) { - setting = selection; + if (ImGui::BeginCombo(name.data(), selection.c_str())) { + if (ImGui::Selectable(api::ThemeManager::NativeTheme, selection == api::ThemeManager::NativeTheme)) { + selection = api::ThemeManager::NativeTheme; + setting = selection; + ImHexApi::System::enableSystemThemeDetection(true); + changed = true; + } - ImHexApi::System::enableSystemThemeDetection(selection == 0); - if (selection != 0) - ImHexApi::System::setTheme(static_cast(selection)); + for (const auto &themeName : themeNames) { + if (ImGui::Selectable(themeName.c_str(), selection == themeName)) { + selection = themeName; + setting = selection; + ImHexApi::System::enableSystemThemeDetection(false); + api::ThemeManager::changeTheme(selection); + changed = true; + } + } - return true; + ImGui::EndCombo(); } - return false; + return changed; }); ContentRegistry::Settings::add( @@ -569,10 +577,14 @@ namespace hex::plugin::builtin { } static void loadThemeSettings() { - auto theme = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", static_cast(ImHexApi::System::Theme::Dark)); + auto theme = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", api::ThemeManager::NativeTheme); - ImHexApi::System::enableSystemThemeDetection(theme == 0); - ImHexApi::System::setTheme(static_cast(theme)); + if (theme == api::ThemeManager::NativeTheme) + ImHexApi::System::enableSystemThemeDetection(true); + else { + ImHexApi::System::enableSystemThemeDetection(false); + api::ThemeManager::changeTheme(theme); + } } static void loadFoldersSettings() { diff --git a/plugins/builtin/source/content/themes.cpp b/plugins/builtin/source/content/themes.cpp new file mode 100644 index 000000000..fc28e6fbe --- /dev/null +++ b/plugins/builtin/source/content/themes.cpp @@ -0,0 +1,228 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace hex::plugin::builtin { + + void registerThemeHandlers() { + api::ThemeManager::addThemeHandler("imgui", [](const std::string &key, const std::string &value) { + const static std::map ColorMap = { + { "text", ImGuiCol_Text }, + { "text-disabled", ImGuiCol_TextDisabled }, + { "window-background", ImGuiCol_WindowBg }, + { "child-background", ImGuiCol_ChildBg }, + { "popup-background", ImGuiCol_PopupBg }, + { "border", ImGuiCol_Border }, + { "border-shadow", ImGuiCol_BorderShadow }, + { "frame-background", ImGuiCol_FrameBg }, + { "frame-background-hovered", ImGuiCol_FrameBgHovered }, + { "frame-background-active", ImGuiCol_FrameBgActive }, + { "title-background", ImGuiCol_TitleBg }, + { "title-background-active", ImGuiCol_TitleBgActive }, + { "title-background-collapse", ImGuiCol_TitleBgCollapsed }, + { "menu-bar-background", ImGuiCol_MenuBarBg }, + { "scrollbar-background", ImGuiCol_ScrollbarBg }, + { "scrollbar-grab", ImGuiCol_ScrollbarGrab }, + { "scrollbar-grab-hovered", ImGuiCol_ScrollbarGrabHovered }, + { "scrollbar-grab-active", ImGuiCol_ScrollbarGrabActive }, + { "check-mark", ImGuiCol_CheckMark }, + { "slider-grab", ImGuiCol_SliderGrab }, + { "slider-grab-active", ImGuiCol_SliderGrabActive }, + { "button", ImGuiCol_Button }, + { "button-hovered", ImGuiCol_ButtonHovered }, + { "button-active", ImGuiCol_ButtonActive }, + { "header", ImGuiCol_Header }, + { "header-hovered", ImGuiCol_HeaderHovered }, + { "header-active", ImGuiCol_HeaderActive }, + { "separator", ImGuiCol_Separator }, + { "separator-hovered", ImGuiCol_SeparatorHovered }, + { "separator-active", ImGuiCol_SeparatorActive }, + { "resize-grip", ImGuiCol_ResizeGrip }, + { "resize-grip-hovered", ImGuiCol_ResizeGripHovered }, + { "resize-grip-active", ImGuiCol_ResizeGripActive }, + { "tab", ImGuiCol_Tab }, + { "tab-hovered", ImGuiCol_TabHovered }, + { "tab-active", ImGuiCol_TabActive }, + { "tab-unfocused", ImGuiCol_TabUnfocused }, + { "tab-unfocused-active", ImGuiCol_TabUnfocusedActive }, + { "docking-preview", ImGuiCol_DockingPreview }, + { "docking-empty-background", ImGuiCol_DockingEmptyBg }, + { "plot-lines", ImGuiCol_PlotLines }, + { "plot-lines-hovered", ImGuiCol_PlotLinesHovered }, + { "plot-histogram", ImGuiCol_PlotHistogram }, + { "plot-histogram-hovered", ImGuiCol_PlotHistogramHovered }, + { "table-header-background", ImGuiCol_TableHeaderBg }, + { "table-border-strong", ImGuiCol_TableBorderStrong }, + { "table-border-light", ImGuiCol_TableBorderLight }, + { "table-row-background", ImGuiCol_TableRowBg }, + { "table-row-background-alt", ImGuiCol_TableRowBgAlt }, + { "text-selected-background", ImGuiCol_TextSelectedBg }, + { "drag-drop-target", ImGuiCol_DragDropTarget }, + { "nav-highlight", ImGuiCol_NavHighlight }, + { "nav-windowing-highlight", ImGuiCol_NavWindowingHighlight }, + { "nav-windowing-background", ImGuiCol_NavWindowingDimBg }, + { "modal-window-dim-background", ImGuiCol_ModalWindowDimBg } + }; + + auto color = api::ThemeManager::parseColorString(value); + auto colors = ImGui::GetStyle().Colors; + + if (ColorMap.contains(key) && color.has_value()) + colors[ColorMap.at(key)] = color->Value; + }); + + api::ThemeManager::addThemeHandler("implot", [](const std::string &key, const std::string &value) { + const static std::map ColorMap = { + { "line", ImPlotCol_Line }, + { "fill", ImPlotCol_Fill }, + { "marker-outline", ImPlotCol_MarkerOutline }, + { "marker-fill", ImPlotCol_MarkerFill }, + { "error-bar", ImPlotCol_ErrorBar }, + { "frame-bg", ImPlotCol_FrameBg }, + { "plot-bg", ImPlotCol_PlotBg }, + { "plot-border", ImPlotCol_PlotBorder }, + { "legend-bg", ImPlotCol_LegendBg }, + { "legend-border", ImPlotCol_LegendBorder }, + { "legend-text", ImPlotCol_LegendText }, + { "title-text", ImPlotCol_TitleText }, + { "inlay-text", ImPlotCol_InlayText }, + { "axis-text", ImPlotCol_AxisText }, + { "axis-grid", ImPlotCol_AxisGrid }, + { "axis-tick", ImPlotCol_AxisTick }, + { "axis-bg", ImPlotCol_AxisBg }, + { "axis-bg-hovered", ImPlotCol_AxisBgHovered }, + { "axis-bg-active", ImPlotCol_AxisBgActive }, + { "selection", ImPlotCol_Selection }, + { "crosshairs", ImPlotCol_Crosshairs } + }; + + auto color = api::ThemeManager::parseColorString(value); + auto &colors = ImPlot::GetStyle().Colors; + + if (ColorMap.contains(key) && color.has_value()) + colors[ColorMap.at(key)] = color->Value; + }); + + api::ThemeManager::addThemeHandler("imnodes", [](const std::string &key, const std::string &value) { + const static std::map ColorMap = { + { "node-background", ImNodesCol_NodeBackground }, + { "node-background-hovered", ImNodesCol_NodeBackgroundHovered }, + { "node-background-selected", ImNodesCol_NodeBackgroundSelected }, + { "node-outline", ImNodesCol_NodeOutline }, + { "title-bar", ImNodesCol_TitleBar }, + { "title-bar-hovered", ImNodesCol_TitleBarHovered }, + { "title-bar-selected", ImNodesCol_TitleBarSelected }, + { "link", ImNodesCol_Link }, + { "link-hovered", ImNodesCol_LinkHovered }, + { "link-selected", ImNodesCol_LinkSelected }, + { "pin", ImNodesCol_Pin }, + { "pin-hovered", ImNodesCol_PinHovered }, + { "box-selector", ImNodesCol_BoxSelector }, + { "box-selector-outline", ImNodesCol_BoxSelectorOutline }, + { "grid-background", ImNodesCol_GridBackground }, + { "grid-line", ImNodesCol_GridLine }, + { "grid-line-primary", ImNodesCol_GridLinePrimary }, + { "mini-map-background", ImNodesCol_MiniMapBackground }, + { "mini-map-background-hovered", ImNodesCol_MiniMapBackgroundHovered }, + { "mini-map-outline", ImNodesCol_MiniMapOutline }, + { "mini-map-outline-hovered", ImNodesCol_MiniMapOutlineHovered }, + { "mini-map-node-background", ImNodesCol_MiniMapNodeBackground }, + { "mini-map-node-background-hovered", ImNodesCol_MiniMapNodeBackgroundHovered }, + { "mini-map-node-background-selected", ImNodesCol_MiniMapNodeBackgroundSelected }, + { "mini-map-node-outline", ImNodesCol_MiniMapNodeOutline }, + { "mini-map-link", ImNodesCol_MiniMapLink }, + { "mini-map-link-selected", ImNodesCol_MiniMapLinkSelected }, + { "mini-map-canvas", ImNodesCol_MiniMapCanvas }, + { "mini-map-canvas-outline", ImNodesCol_MiniMapCanvasOutline }, + }; + + auto color = api::ThemeManager::parseColorString(value); + auto &colors = ImNodes::GetStyle().Colors; + + if (ColorMap.contains(key) && color.has_value()) + colors[ColorMap.at(key)] = *color; + }); + + api::ThemeManager::addThemeHandler("imhex", [](const std::string &key, const std::string &value) { + const static std::map ColorMap = { + { "desc-button", ImGuiCustomCol_DescButton }, + { "desc-button-hovered", ImGuiCustomCol_DescButtonHovered }, + { "desc-button-active", ImGuiCustomCol_DescButtonActive }, + { "toolbar-gray", ImGuiCustomCol_ToolbarGray }, + { "toolbar-red", ImGuiCustomCol_ToolbarRed }, + { "toolbar-yellow", ImGuiCustomCol_ToolbarYellow }, + { "toolbar-green", ImGuiCustomCol_ToolbarGreen }, + { "toolbar-blue", ImGuiCustomCol_ToolbarBlue }, + { "toolbar-purple", ImGuiCustomCol_ToolbarPurple }, + { "toolbar-brown", ImGuiCustomCol_ToolbarBrown }, + { "highlight", ImGuiCustomCol_Highlight } + }; + + auto color = api::ThemeManager::parseColorString(value); + auto &colors = static_cast(GImGui->IO.UserData)->Colors; + + if (ColorMap.contains(key) && color.has_value()) + colors[ColorMap.at(key)] = color->Value; + }); + + api::ThemeManager::addThemeHandler("text-editor", [](const std::string &key, const std::string &value) { + using enum TextEditor::PaletteIndex; + const static std::map ColorMap = { + { "default", Default }, + { "keyword", Keyword }, + { "number", Number }, + { "string", String }, + { "char-literal", CharLiteral }, + { "punctuation", Punctuation }, + { "preprocessor", Preprocessor }, + { "identifier", Identifier }, + { "known-identifier", KnownIdentifier }, + { "preproc-identifier", PreprocIdentifier }, + { "comment", Comment }, + { "multi-line-comment", MultiLineComment }, + { "background", Background }, + { "cursor", Cursor }, + { "selection", Selection }, + { "error-marker", ErrorMarker }, + { "breakpoint", Breakpoint }, + { "line-number", LineNumber }, + { "current-line-fill", CurrentLineFill }, + { "current-line-fill-inactive", CurrentLineFillInactive }, + { "current-line-edge", CurrentLineEdge } + }; + + auto color = api::ThemeManager::parseColorString(value); + auto colors = TextEditor::GetPalette(); + + if (ColorMap.contains(key) && color.has_value()) + colors[size_t(ColorMap.at(key))] = ImU32(*color); + + TextEditor::SetPalette(colors); + }); + } + + void registerThemes() { + // Load built-in themes + for (const auto &theme : romfs::list("themes")) { + api::ThemeManager::addTheme(std::string(romfs::get(theme).string())); + } + + // Load user themes + for (const auto &themeFolder : fs::getDefaultPaths(fs::ImHexPath::Themes)) { + for (const auto &theme : std::fs::directory_iterator(themeFolder)) { + if (theme.is_regular_file()) + api::ThemeManager::addTheme(fs::File(theme.path(), fs::File::Mode::Read).readString()); + } + } + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/views/view_data_processor.cpp b/plugins/builtin/source/content/views/view_data_processor.cpp index 87e618b6f..32a764cd7 100644 --- a/plugins/builtin/source/content/views/view_data_processor.cpp +++ b/plugins/builtin/source/content/views/view_data_processor.cpp @@ -15,23 +15,6 @@ namespace hex::plugin::builtin { ViewDataProcessor::ViewDataProcessor() : View("hex.builtin.view.data_processor.name") { - EventManager::subscribe(this, [](u32 theme) { - switch (theme) { - default: - case 1: /* Dark theme */ - ImNodes::StyleColorsDark(); - break; - case 2: /* Light theme */ - ImNodes::StyleColorsLight(); - break; - case 3: /* Classic theme */ - ImNodes::StyleColorsClassic(); - break; - } - - ImNodes::GetStyle().Flags = ImNodesStyleFlags_NodeOutline | ImNodesStyleFlags_GridLines; - }); - ProjectFile::registerPerProviderHandler({ .basePath = "data_processor.json", .required = false, diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 5972c54e5..b6318fd1a 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -828,21 +828,6 @@ namespace hex::plugin::builtin { this->m_textEditor.SetTextLines(lines); } }); - - EventManager::subscribe(this, [this](u32 theme) { - switch (theme) { - default: - case 1: /* Dark theme */ - this->m_textEditor.SetPalette(TextEditor::GetDarkPalette()); - break; - case 2: /* Light theme */ - this->m_textEditor.SetPalette(TextEditor::GetLightPalette()); - break; - case 3: /* Classic theme */ - this->m_textEditor.SetPalette(TextEditor::GetRetroBluePalette()); - break; - } - }); } static void createNestedMenu(const std::vector &menus, const std::function &function) { diff --git a/plugins/builtin/source/content/welcome_screen.cpp b/plugins/builtin/source/content/welcome_screen.cpp index 953c6810a..eadf1fb2e 100644 --- a/plugins/builtin/source/content/welcome_screen.cpp +++ b/plugins/builtin/source/content/welcome_screen.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -419,12 +420,14 @@ namespace hex::plugin::builtin { { auto theme = ContentRegistry::Settings::getSetting("hex.builtin.setting.interface", "hex.builtin.setting.interface.color"); - if (theme.is_number()) { - static int lastTheme = 0; + if (theme.is_string()) { + if (theme != api::ThemeManager::NativeTheme) { + static std::string lastTheme; - if (const int thisTheme = theme.get(); thisTheme != lastTheme) { - EventManager::post(thisTheme); - lastTheme = thisTheme; + if (const auto thisTheme = theme.get(); thisTheme != lastTheme) { + EventManager::post(thisTheme); + lastTheme = thisTheme; + } } } } @@ -448,51 +451,16 @@ namespace hex::plugin::builtin { } }); - (void)EventManager::subscribe([](u32 theme) { + (void)EventManager::subscribe([](const std::string &theme) { auto changeTexture = [&](const std::string &path) { auto textureData = romfs::get(path); return ImGui::Texture(reinterpret_cast(textureData.data()), textureData.size()); }; - switch (theme) { - default: - case 1: /* Dark theme */ - { - ImGui::StyleColorsDark(); - ImGui::StyleCustomColorsDark(); - ImPlot::StyleColorsDark(); - s_bannerTexture = changeTexture("banner_dark.png"); - s_backdropTexture = changeTexture("backdrop_dark.png"); - - break; - } - case 2: /* Light theme */ - { - ImGui::StyleColorsLight(); - ImGui::StyleCustomColorsLight(); - ImPlot::StyleColorsLight(); - s_bannerTexture = changeTexture("banner_light.png"); - s_backdropTexture = changeTexture("backdrop_light.png"); - - break; - } - case 3: /* Classic theme */ - { - ImGui::StyleColorsClassic(); - ImGui::StyleCustomColorsClassic(); - ImPlot::StyleColorsClassic(); - s_bannerTexture = changeTexture("banner_dark.png"); - s_backdropTexture = changeTexture("backdrop_dark.png"); - - break; - } - } - - ImGui::GetStyle().Colors[ImGuiCol_DockingEmptyBg] = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; - ImGui::GetStyle().Colors[ImGuiCol_TitleBg] = ImGui::GetStyle().Colors[ImGuiCol_MenuBarBg]; - ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive] = ImGui::GetStyle().Colors[ImGuiCol_MenuBarBg]; - ImGui::GetStyle().Colors[ImGuiCol_TitleBgCollapsed] = ImGui::GetStyle().Colors[ImGuiCol_MenuBarBg]; + api::ThemeManager::changeTheme(theme); + s_bannerTexture = changeTexture(hex::format("banner{}.png", api::ThemeManager::getThemeImagePostfix())); + s_backdropTexture = changeTexture(hex::format("backdrop{}.png", api::ThemeManager::getThemeImagePostfix())); if (!s_bannerTexture.isValid()) { log::error("Failed to load banner texture!"); diff --git a/plugins/builtin/source/plugin_builtin.cpp b/plugins/builtin/source/plugin_builtin.cpp index e87735e0d..283765aa7 100644 --- a/plugins/builtin/source/plugin_builtin.cpp +++ b/plugins/builtin/source/plugin_builtin.cpp @@ -26,6 +26,8 @@ namespace hex::plugin::builtin { void createWelcomeScreen(); void registerViews(); void registerShortcuts(); + void registerThemeHandlers(); + void registerThemes(); void addFooterItems(); void addToolbarItems(); @@ -59,6 +61,8 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") { createWelcomeScreen(); registerViews(); registerShortcuts(); + registerThemeHandlers(); + registerThemes(); addFooterItems(); addToolbarItems(); diff --git a/plugins/windows/source/plugin_windows.cpp b/plugins/windows/source/plugin_windows.cpp index cd3257775..7a140dce2 100644 --- a/plugins/windows/source/plugin_windows.cpp +++ b/plugins/windows/source/plugin_windows.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -20,7 +21,7 @@ namespace hex::plugin::windows { static void detectSystemTheme() { // Setup system theme change detector EventManager::subscribe([] { - bool themeFollowSystem = ContentRegistry::Settings::getSetting("hex.builtin.setting.interface", "hex.builtin.setting.interface.color") == 0; + bool themeFollowSystem = ContentRegistry::Settings::getSetting("hex.builtin.setting.interface", "hex.builtin.setting.interface.color") == api::ThemeManager::NativeTheme; if (!themeFollowSystem) return; @@ -31,7 +32,7 @@ static void detectSystemTheme() { auto error = RegQueryValueEx(hkey, "AppsUseLightTheme", nullptr, nullptr, reinterpret_cast(&value), &size); if (error == ERROR_SUCCESS) { - EventManager::post(value == 0 ? 1 : 2); + EventManager::post(value == 0 ? "Dark" : "Light"); } else { ImHexApi::System::impl::setBorderlessWindowMode(false); } @@ -41,7 +42,7 @@ static void detectSystemTheme() { }); EventManager::subscribe([=] { - bool themeFollowSystem = ContentRegistry::Settings::getSetting("hex.builtin.setting.interface", "hex.builtin.setting.interface.color") == 0; + bool themeFollowSystem = ContentRegistry::Settings::getSetting("hex.builtin.setting.interface", "hex.builtin.setting.interface.color") == api::ThemeManager::NativeTheme; if (themeFollowSystem) EventManager::post();