WoW Model Viewer
Your premiere tool for viewing, equipping and animating World of Warcraft models.
Loading...
Searching...
No Matches
ThemeManager.cpp
Go to the documentation of this file.
1#ifdef _WIN32
2#include <windows.h>
3#include <dwmapi.h>
4#pragma comment(lib, "dwmapi.lib")
5#endif
6
7#include "ThemeManager.h"
8
9#include "imgui.h"
10#include <GLFW/glfw3.h>
11#ifdef _WIN32
12#define GLFW_EXPOSE_NATIVE_WIN32
13#include <GLFW/glfw3native.h>
14#endif
15
16namespace ThemeManager
17{
18
19// ---- Module state ---------------------------------------------------------
20static int s_currentTheme = static_cast<int>(UE5);
21
22static const char* s_themeNames[] = {
23 "Dark",
24 "Light",
25 "Unreal Engine 5"
26};
27
28// ---- Title-bar colour (Windows DWM) ---------------------------------------
29static void updateTitleBarColor(Theme theme, [[maybe_unused]] GLFWwindow* window)
30{
31#ifdef _WIN32
32 if (!window)
33 return;
34
35 HWND hwnd = glfwGetWin32Window(window);
36 if (!hwnd)
37 return;
38
39 constexpr DWORD DWMWA_USE_IMMERSIVE_DARK_MODE_VAL = 20;
40 constexpr DWORD DWMWA_CAPTION_COLOR_VAL = 35;
41
42 BOOL useDarkMode = (theme != Light) ? TRUE : FALSE;
43 DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE_VAL, &useDarkMode, sizeof(useDarkMode));
44
45 COLORREF captionColor = RGB(36, 36, 36);
46 switch (theme)
47 {
48 case Dark: captionColor = RGB(50, 50, 55); break;
49 case Light: captionColor = RGB(239, 239, 239); break;
50 case UE5: captionColor = RGB(21, 21, 21); break;
51 default: break;
52 }
53 DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR_VAL, &captionColor, sizeof(captionColor));
54#else
55 (void)theme;
56 (void)window;
57#endif
58}
59
60// ---- Theme application ----------------------------------------------------
61void apply(Theme theme, GLFWwindow* window)
62{
63 ImGuiStyle& style = ImGui::GetStyle();
64
65 auto setCommonStyle = [&](float rounding, float frameBorder) {
66 style.WindowRounding = rounding;
67 style.ChildRounding = rounding;
68 style.FrameRounding = rounding;
69 style.PopupRounding = rounding;
70 style.ScrollbarRounding = rounding;
71 style.GrabRounding = rounding;
72 style.TabRounding = rounding;
73 style.WindowBorderSize = 1.0f;
74 style.FrameBorderSize = frameBorder;
75 style.PopupBorderSize = 1.0f;
76 style.FramePadding = ImVec2(5.0f, 3.0f);
77 style.ItemSpacing = ImVec2(5.0f, 4.0f);
78 style.ItemInnerSpacing = ImVec2(4.0f, 4.0f);
79 style.IndentSpacing = 16.0f;
80 style.ScrollbarSize = 13.0f;
81 style.GrabMinSize = 8.0f;
82 style.SeparatorTextBorderSize = 2.0f;
83 };
84
85 ImVec4* c = style.Colors;
86
87 switch (theme)
88 {
89 case Dark:
90 {
91 ImGui::StyleColorsDark();
92 setCommonStyle(4.0f, 0.0f);
93 style.WindowTitleAlign = ImVec2(0.0f, 0.5f);
94 style.WindowMenuButtonPosition = ImGuiDir_Left;
95 break;
96 }
97 case UE5:
98 {
99 ImGui::StyleColorsDark();
100 setCommonStyle(2.0f, 1.0f);
101 style.WindowTitleAlign = ImVec2(0.5f, 0.5f);
102 style.WindowMenuButtonPosition = ImGuiDir_Right;
103
104 // Palette from UE5 Dark.json export (linear → sRGB via proper transfer function)
105 // Background 0.082 Title 0.082 WindowBorder 0.059
106 // Input 0.059 Recessed 0.102 Panel 0.141
107 // Header 0.184 Dropdown 0.220 Hover 0.341
108 // Hover2 0.502 Foreground 0.753 Primary (0,0.439,0.878)
109 constexpr ImVec4 primary(0.000f, 0.439f, 0.878f, 1.00f); // Select / Highlight
110
111 c[ImGuiCol_WindowBg] = ImVec4(0.141f, 0.141f, 0.141f, 1.00f); // Panel
112 c[ImGuiCol_ChildBg] = ImVec4(0.102f, 0.102f, 0.102f, 1.00f); // Recessed
113 c[ImGuiCol_PopupBg] = ImVec4(0.220f, 0.220f, 0.220f, 1.00f); // Dropdown
114 c[ImGuiCol_MenuBarBg] = ImVec4(0.082f, 0.082f, 0.082f, 1.00f); // Background
115 c[ImGuiCol_Border] = ImVec4(0.059f, 0.059f, 0.059f, 1.00f); // WindowBorder
116 c[ImGuiCol_BorderShadow] = ImVec4(0.000f, 0.000f, 0.000f, 0.00f);
117 c[ImGuiCol_TitleBg] = ImVec4(0.082f, 0.082f, 0.082f, 1.00f); // Title
118 c[ImGuiCol_TitleBgActive] = ImVec4(0.082f, 0.082f, 0.082f, 1.00f); // Title
119 c[ImGuiCol_TitleBgCollapsed] = ImVec4(0.082f, 0.082f, 0.082f, 0.75f);
120 c[ImGuiCol_Text] = ImVec4(0.753f, 0.753f, 0.753f, 1.00f); // Foreground
121 c[ImGuiCol_TextDisabled] = ImVec4(0.400f, 0.400f, 0.400f, 1.00f);
122 c[ImGuiCol_TextSelectedBg] = ImVec4(primary.x, primary.y, primary.z, 0.40f);
123 c[ImGuiCol_FrameBg] = ImVec4(0.059f, 0.059f, 0.059f, 1.00f); // Input
124 c[ImGuiCol_FrameBgHovered] = ImVec4(0.102f, 0.102f, 0.102f, 1.00f); // Recessed
125 c[ImGuiCol_FrameBgActive] = ImVec4(0.141f, 0.141f, 0.141f, 1.00f); // Panel
126 c[ImGuiCol_Button] = ImVec4(0.184f, 0.184f, 0.184f, 1.00f); // Header
127 c[ImGuiCol_ButtonHovered] = ImVec4(0.341f, 0.341f, 0.341f, 1.00f); // Hover
128 c[ImGuiCol_ButtonActive] = ImVec4(primary.x, primary.y, primary.z, 1.00f);
129 c[ImGuiCol_Header] = ImVec4(0.141f, 0.141f, 0.141f, 1.00f); // Panel
130 c[ImGuiCol_HeaderHovered] = ImVec4(primary.x, primary.y, primary.z, 0.50f);
131 c[ImGuiCol_HeaderActive] = ImVec4(primary.x, primary.y, primary.z, 0.70f);
132 c[ImGuiCol_Tab] = ImVec4(0.082f, 0.082f, 0.082f, 1.00f); // Background
133 c[ImGuiCol_TabHovered] = ImVec4(0.184f, 0.184f, 0.184f, 1.00f); // Header
134 c[ImGuiCol_TabSelected] = ImVec4(0.141f, 0.141f, 0.141f, 1.00f); // Panel
135 c[ImGuiCol_TabDimmed] = ImVec4(0.059f, 0.059f, 0.059f, 1.00f); // Input
136 c[ImGuiCol_TabDimmedSelected] = ImVec4(0.102f, 0.102f, 0.102f, 1.00f); // Recessed
137 c[ImGuiCol_ScrollbarBg] = ImVec4(0.059f, 0.059f, 0.059f, 0.50f);
138 c[ImGuiCol_ScrollbarGrab] = ImVec4(0.220f, 0.220f, 0.220f, 1.00f); // Dropdown
139 c[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.341f, 0.341f, 0.341f, 1.00f); // Hover
140 c[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.502f, 0.502f, 0.502f, 1.00f); // Hover2
141 c[ImGuiCol_SliderGrab] = ImVec4(0.298f, 0.298f, 0.298f, 1.00f); // DropdownOutline
142 c[ImGuiCol_SliderGrabActive] = ImVec4(primary.x, primary.y, primary.z, 1.00f);
143 c[ImGuiCol_CheckMark] = ImVec4(primary.x, primary.y, primary.z, 1.00f);
144 c[ImGuiCol_Separator] = ImVec4(0.059f, 0.059f, 0.059f, 1.00f); // WindowBorder
145 c[ImGuiCol_SeparatorHovered] = ImVec4(primary.x, primary.y, primary.z, 0.78f);
146 c[ImGuiCol_SeparatorActive] = ImVec4(primary.x, primary.y, primary.z, 1.00f);
147 c[ImGuiCol_ResizeGrip] = ImVec4(0.220f, 0.220f, 0.220f, 0.25f);
148 c[ImGuiCol_ResizeGripHovered] = ImVec4(primary.x, primary.y, primary.z, 0.67f);
149 c[ImGuiCol_ResizeGripActive] = ImVec4(primary.x, primary.y, primary.z, 0.95f);
150 c[ImGuiCol_DockingPreview] = ImVec4(primary.x, primary.y, primary.z, 0.70f);
151 c[ImGuiCol_DockingEmptyBg] = ImVec4(0.059f, 0.059f, 0.059f, 1.00f); // Input
152 c[ImGuiCol_NavCursor] = ImVec4(primary.x, primary.y, primary.z, 1.00f);
153 c[ImGuiCol_NavWindowingHighlight]= ImVec4(primary.x, primary.y, primary.z, 0.70f);
154 c[ImGuiCol_NavWindowingDimBg] = ImVec4(0.082f, 0.082f, 0.082f, 0.20f);
155 c[ImGuiCol_ModalWindowDimBg] = ImVec4(0.000f, 0.000f, 0.000f, 0.55f);
156 c[ImGuiCol_TableHeaderBg] = ImVec4(0.141f, 0.141f, 0.141f, 1.00f); // Panel
157 c[ImGuiCol_TableBorderStrong] = ImVec4(0.059f, 0.059f, 0.059f, 1.00f); // WindowBorder
158 c[ImGuiCol_TableBorderLight] = ImVec4(0.102f, 0.102f, 0.102f, 1.00f); // Recessed
159 c[ImGuiCol_TableRowBg] = ImVec4(0.000f, 0.000f, 0.000f, 0.00f);
160 c[ImGuiCol_TableRowBgAlt] = ImVec4(1.000f, 1.000f, 1.000f, 0.02f);
161 break;
162 }
163 case Light:
164 {
165 ImGui::StyleColorsLight();
166 setCommonStyle(3.0f, 1.0f);
167 style.WindowTitleAlign = ImVec2(0.0f, 0.5f);
168 style.WindowMenuButtonPosition = ImGuiDir_Left;
169
170 c[ImGuiCol_WindowBg] = ImVec4(0.937f, 0.937f, 0.937f, 1.00f);
171 c[ImGuiCol_ChildBg] = ImVec4(0.961f, 0.961f, 0.961f, 1.00f);
172 c[ImGuiCol_PopupBg] = ImVec4(0.976f, 0.976f, 0.976f, 0.98f);
173 c[ImGuiCol_MenuBarBg] = ImVec4(0.898f, 0.898f, 0.898f, 1.00f);
174 c[ImGuiCol_Border] = ImVec4(0.757f, 0.757f, 0.757f, 1.00f);
175 c[ImGuiCol_BorderShadow] = ImVec4(0.000f, 0.000f, 0.000f, 0.00f);
176 c[ImGuiCol_TitleBg] = ImVec4(0.898f, 0.898f, 0.898f, 1.00f);
177 c[ImGuiCol_TitleBgActive] = ImVec4(0.835f, 0.835f, 0.835f, 1.00f);
178 c[ImGuiCol_TitleBgCollapsed] = ImVec4(0.898f, 0.898f, 0.898f, 0.75f);
179 c[ImGuiCol_Text] = ImVec4(0.133f, 0.133f, 0.133f, 1.00f);
180 c[ImGuiCol_TextDisabled] = ImVec4(0.502f, 0.502f, 0.502f, 1.00f);
181 c[ImGuiCol_TextSelectedBg] = ImVec4(0.184f, 0.525f, 0.945f, 0.35f);
182 c[ImGuiCol_FrameBg] = ImVec4(1.000f, 1.000f, 1.000f, 1.00f);
183 c[ImGuiCol_FrameBgHovered] = ImVec4(0.906f, 0.933f, 0.976f, 1.00f);
184 c[ImGuiCol_FrameBgActive] = ImVec4(0.835f, 0.882f, 0.953f, 1.00f);
185 c[ImGuiCol_Button] = ImVec4(0.859f, 0.859f, 0.859f, 1.00f);
186 c[ImGuiCol_ButtonHovered] = ImVec4(0.184f, 0.525f, 0.945f, 0.60f);
187 c[ImGuiCol_ButtonActive] = ImVec4(0.184f, 0.525f, 0.945f, 1.00f);
188 c[ImGuiCol_Header] = ImVec4(0.898f, 0.898f, 0.898f, 1.00f);
189 c[ImGuiCol_HeaderHovered] = ImVec4(0.184f, 0.525f, 0.945f, 0.40f);
190 c[ImGuiCol_HeaderActive] = ImVec4(0.184f, 0.525f, 0.945f, 0.60f);
191 c[ImGuiCol_Tab] = ImVec4(0.898f, 0.898f, 0.898f, 1.00f);
192 c[ImGuiCol_TabHovered] = ImVec4(0.184f, 0.525f, 0.945f, 0.40f);
193 c[ImGuiCol_TabSelected] = ImVec4(0.961f, 0.961f, 0.961f, 1.00f);
194 c[ImGuiCol_TabDimmed] = ImVec4(0.922f, 0.922f, 0.922f, 1.00f);
195 c[ImGuiCol_TabDimmedSelected] = ImVec4(0.937f, 0.937f, 0.937f, 1.00f);
196 c[ImGuiCol_ScrollbarBg] = ImVec4(0.937f, 0.937f, 0.937f, 0.50f);
197 c[ImGuiCol_ScrollbarGrab] = ImVec4(0.690f, 0.690f, 0.690f, 1.00f);
198 c[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.565f, 0.565f, 0.565f, 1.00f);
199 c[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.439f, 0.439f, 0.439f, 1.00f);
200 c[ImGuiCol_SliderGrab] = ImVec4(0.184f, 0.525f, 0.945f, 0.60f);
201 c[ImGuiCol_SliderGrabActive] = ImVec4(0.184f, 0.525f, 0.945f, 1.00f);
202 c[ImGuiCol_CheckMark] = ImVec4(0.184f, 0.525f, 0.945f, 1.00f);
203 c[ImGuiCol_Separator] = ImVec4(0.757f, 0.757f, 0.757f, 1.00f);
204 c[ImGuiCol_SeparatorHovered] = ImVec4(0.184f, 0.525f, 0.945f, 0.78f);
205 c[ImGuiCol_SeparatorActive] = ImVec4(0.184f, 0.525f, 0.945f, 1.00f);
206 c[ImGuiCol_ResizeGrip] = ImVec4(0.690f, 0.690f, 0.690f, 0.25f);
207 c[ImGuiCol_ResizeGripHovered] = ImVec4(0.184f, 0.525f, 0.945f, 0.67f);
208 c[ImGuiCol_ResizeGripActive] = ImVec4(0.184f, 0.525f, 0.945f, 0.95f);
209 c[ImGuiCol_DockingPreview] = ImVec4(0.184f, 0.525f, 0.945f, 0.70f);
210 c[ImGuiCol_DockingEmptyBg] = ImVec4(0.937f, 0.937f, 0.937f, 1.00f);
211 c[ImGuiCol_NavCursor] = ImVec4(0.184f, 0.525f, 0.945f, 1.00f);
212 c[ImGuiCol_NavWindowingHighlight]= ImVec4(0.184f, 0.525f, 0.945f, 0.70f);
213 c[ImGuiCol_NavWindowingDimBg] = ImVec4(0.800f, 0.800f, 0.800f, 0.20f);
214 c[ImGuiCol_ModalWindowDimBg] = ImVec4(0.200f, 0.200f, 0.200f, 0.35f);
215 c[ImGuiCol_TableHeaderBg] = ImVec4(0.882f, 0.882f, 0.882f, 1.00f);
216 c[ImGuiCol_TableBorderStrong] = ImVec4(0.757f, 0.757f, 0.757f, 1.00f);
217 c[ImGuiCol_TableBorderLight] = ImVec4(0.835f, 0.835f, 0.835f, 1.00f);
218 c[ImGuiCol_TableRowBg] = ImVec4(0.000f, 0.000f, 0.000f, 0.00f);
219 c[ImGuiCol_TableRowBgAlt] = ImVec4(0.000f, 0.000f, 0.000f, 0.03f);
220 break;
221 }
222 default:
223 ImGui::StyleColorsDark();
224 break;
225 }
226
227 s_currentTheme = static_cast<int>(theme);
228 updateTitleBarColor(theme, window);
229}
230
231// ---- Accessors ------------------------------------------------------------
233{
234 return static_cast<Theme>(s_currentTheme);
235}
236
237int& currentThemeRef() noexcept
238{
239 return s_currentTheme;
240}
241
242const char* themeName(int index) noexcept
243{
244 if (index < 0 || index >= static_cast<int>(Count))
245 return "Unknown";
246 return s_themeNames[index];
247}
248
249const char* const* themeNames() noexcept
250{
251 return s_themeNames;
252}
253
254} // namespace ThemeManager
void apply(Theme theme, GLFWwindow *window)
const char * themeName(int index) noexcept
Human-readable name for a theme.
static const char * s_themeNames[]
static int s_currentTheme
const char *const * themeNames() noexcept
Pointer to the contiguous name array (for ImGui::Combo items).
static void updateTitleBarColor(Theme theme, GLFWwindow *window)
int & currentThemeRef() noexcept
Writable reference so ImGui::Combo can bind directly.
Theme currentTheme() noexcept
Currently active theme index.