Add basic custom themes
This commit is contained in:
parent
6870834dc9
commit
4f9f62ee8e
5 changed files with 476 additions and 77 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -2,3 +2,5 @@ assets/*.h
|
|||
build*
|
||||
.vscode
|
||||
subprojects/*/
|
||||
.cache
|
||||
compile_commands.json
|
99
main.cpp
99
main.cpp
|
@ -3,6 +3,7 @@
|
|||
#include "imgui_impl_opengl3.h"
|
||||
#include "imfilebrowser.h"
|
||||
#include "playback.h"
|
||||
#include "theme.h"
|
||||
#include "icon.h"
|
||||
#include "IconsForkAwesome.h"
|
||||
#include <iostream>
|
||||
|
@ -29,62 +30,7 @@ using namespace std::filesystem;
|
|||
using namespace std::numbers;
|
||||
using std::string;
|
||||
static float accent_color = 280.0;
|
||||
float GetHue(ImVec4 rgba){
|
||||
float r = rgba.x, g = rgba.y, b = rgba.z;
|
||||
if (r == g && g == b) {
|
||||
return -1.0;
|
||||
}
|
||||
float hue;
|
||||
if ((r >= g) && (g >= b)) {
|
||||
hue = 60.0 * (g-b)/(r-b);
|
||||
} else if ((g > r) && (r >= b)) {
|
||||
hue = 60.0 * (2.0 - ((r-b)/(g-b)));
|
||||
} else if ((g >= b) && (b > r)) {
|
||||
hue = 60.0 * (2.0 + ((b-r)/(g-r)));
|
||||
} else if ((b > g) && (g > r)) {
|
||||
hue = 60.0 * (4.0 - ((g-r)/(b-r)));
|
||||
} else if ((b > r) && (r >= g)) {
|
||||
hue = 60.0 * (4.0 - ((r-g)/(b-g)));
|
||||
} else if ((r >= b) && (b > g)) {
|
||||
hue = 60.0 * (6.0 - ((b-g)/(r-g)));
|
||||
} else {
|
||||
hue = -1.0;
|
||||
}
|
||||
return hue;
|
||||
}
|
||||
void change_accent_color(ImVec4 &color, float hue) {
|
||||
ImVec4 in = color;
|
||||
float Target = hue;
|
||||
float Current = GetHue(in);
|
||||
if (Current < 0.0f) {
|
||||
return;
|
||||
}
|
||||
float H = 360-Target+Current;
|
||||
float U = cos(H*pi/180.0);
|
||||
float W = sin(H*pi/180.0);
|
||||
ImVec4 out = in;
|
||||
out.x = (.299+.701*U+.168*W)*in.x
|
||||
+ (.587-.587*U+.330*W)*in.y
|
||||
+ (.114-.114*U-.497*W)*in.z;
|
||||
out.y = (.299-.299*U-.328*W)*in.x
|
||||
+ (.587+.413*U+.035*W)*in.y
|
||||
+ (.114-.114*U+.292*W)*in.z;
|
||||
out.z = (.299-.3*U+1.25*W)*in.x
|
||||
+ (.587-.588*U-1.05*W)*in.y
|
||||
+ (.114+.886*U-.203*W)*in.z;
|
||||
color = out;
|
||||
}
|
||||
|
||||
void UpdateStyle(bool dark) {
|
||||
if (dark) {
|
||||
ImGui::StyleColorsDark();
|
||||
} else {
|
||||
ImGui::StyleColorsLight();
|
||||
}
|
||||
for (auto& color : ImGui::GetStyle().Colors) {
|
||||
change_accent_color(color, accent_color);
|
||||
}
|
||||
}
|
||||
string PadZeros(string input, size_t required_length) {
|
||||
return std::string(required_length - std::min(required_length, input.length()), '0') + input;
|
||||
}
|
||||
|
@ -135,6 +81,7 @@ int main(int, char**)
|
|||
}
|
||||
IMG_Init(IMG_INIT_PNG|IMG_INIT_WEBP);
|
||||
const char* prefPath = SDL_GetPrefPath("Catmeow72", NAME);
|
||||
Theme::prefPath = prefPath;
|
||||
|
||||
// Decide GL+GLSL versions
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
|
@ -196,20 +143,8 @@ int main(int, char**)
|
|||
//io.ConfigViewportsNoAutoMerge = true;
|
||||
//io.ConfigViewportsNoTaskBarIcon = true;
|
||||
|
||||
// Setup Dear ImGui style
|
||||
UpdateStyle(true);
|
||||
//ImGui::StyleColorsLight();
|
||||
|
||||
// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
||||
{
|
||||
style.WindowRounding = 0.0f;
|
||||
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
|
||||
}
|
||||
style.FrameRounding = 12;
|
||||
style.WindowRounding = 12;
|
||||
|
||||
// Setup Platform/Renderer backends
|
||||
ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
|
||||
ImGui_ImplOpenGL3_Init(glsl_version);
|
||||
|
@ -255,8 +190,9 @@ int main(int, char**)
|
|||
float position = 0.0;
|
||||
// Main loop
|
||||
bool done = false;
|
||||
bool dark_mode = true;
|
||||
Theme *theme = new Theme(false);
|
||||
bool prefs_window = false;
|
||||
bool theme_editor = false;
|
||||
bool stopped = true;
|
||||
{
|
||||
Json::Value config;
|
||||
|
@ -264,8 +200,12 @@ int main(int, char**)
|
|||
stream.open(path(prefPath) / "config.json");
|
||||
if (stream.is_open()) {
|
||||
stream >> config;
|
||||
if (config.isMember("dark_mode")) {
|
||||
dark_mode = config["dark_mode"].asBool();\
|
||||
if (config.isMember("theme_name")) {
|
||||
path themePath = theme->themeDir / config["theme_name"].asString();
|
||||
if (exists(themePath)) {
|
||||
delete theme;
|
||||
theme = new Theme(themePath);
|
||||
}
|
||||
}
|
||||
if (config.isMember("accent_color")) {
|
||||
accent_color = config["accent_color"].asFloat();
|
||||
|
@ -274,7 +214,6 @@ int main(int, char**)
|
|||
show_demo_window = config["demo_window"].asBool();
|
||||
}
|
||||
stream.close();
|
||||
UpdateStyle(dark_mode);
|
||||
}
|
||||
}
|
||||
#ifdef __EMSCRIPTEN__
|
||||
|
@ -381,16 +320,18 @@ int main(int, char**)
|
|||
ImGui::SetNextWindowSizeConstraints(min_size, max_size);
|
||||
ImGui::Begin("Preferences...", &prefs_window);
|
||||
{
|
||||
if (ImGui::Checkbox(ICON_FK_MOON "Dark Mode", &dark_mode)) {
|
||||
UpdateStyle(dark_mode);
|
||||
if (ImGui::Button(ICON_FK_MAGIC "Theme Editor")) {
|
||||
theme_editor = true;
|
||||
}
|
||||
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - (ImGui::GetStyle().FramePadding.x * 4));
|
||||
if (ImGui::SliderFloat("##AccentColor", &accent_color, 0.0, 360.0, "UI hue: %.0f°", ImGuiSliderFlags_NoRoundToFormat)) {
|
||||
UpdateStyle(dark_mode);
|
||||
}
|
||||
ImGui::SliderFloat("##AccentColor", &accent_color, 0.0, 360.0, "UI hue: %.0f°", ImGuiSliderFlags_NoRoundToFormat);
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
if (theme_editor) {
|
||||
theme->ShowEditor(&theme_editor, theme);
|
||||
}
|
||||
theme->Apply(accent_color);
|
||||
fileDialog.Display();
|
||||
|
||||
if (fileDialog.HasSelected()) {
|
||||
|
@ -441,7 +382,11 @@ int main(int, char**)
|
|||
Json::Value config;
|
||||
std::ofstream stream;
|
||||
stream.open(path(prefPath) / "config.json");
|
||||
config["dark_mode"] = dark_mode;
|
||||
path themePath(theme->file_path);
|
||||
themePath = themePath.filename();
|
||||
if (!themePath.empty()) {
|
||||
config["theme_name"] = themePath.filename().string();
|
||||
}
|
||||
config["accent_color"] = accent_color;
|
||||
config["demo_window"] = show_demo_window;
|
||||
stream << config;
|
||||
|
|
|
@ -19,6 +19,7 @@ deps = [
|
|||
srcs = [
|
||||
'main.cpp',
|
||||
'playback.cpp',
|
||||
'theme.cpp',
|
||||
'imgui/imgui.cpp',
|
||||
'imgui/imgui_widgets.cpp',
|
||||
'imgui/imgui_tables.cpp',
|
||||
|
|
421
theme.cpp
Normal file
421
theme.cpp
Normal file
|
@ -0,0 +1,421 @@
|
|||
#include "theme.h"
|
||||
#include "imgui.h"
|
||||
#include "json/value.h"
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
#include <numbers>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <json/json.h>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace std::filesystem;
|
||||
using namespace std::numbers;
|
||||
const char* Theme::prefPath = NULL;
|
||||
bool Theme::ShowEditor(bool* open, Theme* &theme) {
|
||||
ImGui::Begin("Theme Editor", open);
|
||||
ImGuiStyle *ref = &style;
|
||||
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f);
|
||||
if (ImGui::Button("Create light")) {
|
||||
delete theme;
|
||||
theme = new Theme(false);
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::End();
|
||||
return true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Create dark")) {
|
||||
delete theme;
|
||||
theme = new Theme(true);
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::End();
|
||||
return true;
|
||||
}
|
||||
if (ImGui::Button("Import...")) {
|
||||
importDialog.SetTitle("Import theme...");
|
||||
importDialog.SetTypeFilters({ ".json"});
|
||||
std::string userdir = std::getenv(
|
||||
#ifdef _WIN32
|
||||
"UserProfile"
|
||||
#else
|
||||
"HOME"
|
||||
#endif
|
||||
);
|
||||
importDialog.SetPwd(userdir);
|
||||
importDialog.Open();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Export...")) {
|
||||
exportDialog = ImGui::FileBrowser(ImGuiFileBrowserFlags_EnterNewFilename|ImGuiFileBrowserFlags_CreateNewDir);
|
||||
exportDialog.SetTitle("Export theme...");
|
||||
exportDialog.SetTypeFilters({ ".json"});
|
||||
std::string userdir = std::getenv(
|
||||
#ifdef _WIN32
|
||||
"UserProfile"
|
||||
#else
|
||||
"HOME"
|
||||
#endif
|
||||
);
|
||||
exportDialog.SetPwd(userdir);
|
||||
exportDialog.Open();
|
||||
}
|
||||
importDialog.Display();
|
||||
exportDialog.Display();
|
||||
if (!file_path.empty()) {
|
||||
if (ImGui::Button("Revert")) {
|
||||
string file_path_backup = file_path;
|
||||
delete theme;
|
||||
theme = new Theme(file_path_backup);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::End();
|
||||
return true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Save")) {
|
||||
Save(file_path);
|
||||
}
|
||||
}
|
||||
if (ImGui::Button("Load...")) {
|
||||
loadOpen = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Save as...")) {
|
||||
saveAsOpen = true;
|
||||
}
|
||||
|
||||
// Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)
|
||||
if (ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"))
|
||||
style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding
|
||||
{ bool border = (style.WindowBorderSize > 0.0f); if (ImGui::Checkbox("WindowBorder", &border)) { style.WindowBorderSize = border ? 1.0f : 0.0f; } }
|
||||
ImGui::SameLine();
|
||||
{ bool border = (style.FrameBorderSize > 0.0f); if (ImGui::Checkbox("FrameBorder", &border)) { style.FrameBorderSize = border ? 1.0f : 0.0f; } }
|
||||
ImGui::SameLine();
|
||||
{ bool border = (style.PopupBorderSize > 0.0f); if (ImGui::Checkbox("PopupBorder", &border)) { style.PopupBorderSize = border ? 1.0f : 0.0f; } }
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None))
|
||||
{
|
||||
if (ImGui::BeginTabItem("Sizes"))
|
||||
{
|
||||
|
||||
ImGui::SeparatorText("Borders");
|
||||
ImGui::SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f");
|
||||
ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f");
|
||||
ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f");
|
||||
ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f");
|
||||
ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f");
|
||||
|
||||
ImGui::SeparatorText("Rounding");
|
||||
ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f");
|
||||
ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f");
|
||||
ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f");
|
||||
ImGui::SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f");
|
||||
ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
|
||||
ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f");
|
||||
ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f");
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Colors"))
|
||||
{
|
||||
|
||||
static ImGuiTextFilter filter;
|
||||
filter.Draw("Filter colors", ImGui::GetFontSize() * 16);
|
||||
|
||||
static ImGuiColorEditFlags alpha_flags = 0;
|
||||
if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; }
|
||||
|
||||
ImGui::BeginChild("##colors", ImVec2(0, 0), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened);
|
||||
ImGui::PushItemWidth(-160);
|
||||
for (int i = 0; i < ImGuiCol_COUNT; i++)
|
||||
{
|
||||
const char* name = ImGui::GetStyleColorName(i);
|
||||
if (!filter.PassFilter(name))
|
||||
continue;
|
||||
ImGui::PushID(i);
|
||||
ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags | ImGuiColorEditFlags_DisplayHSV);
|
||||
if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0)
|
||||
{
|
||||
// Tips: in a real user application, you may want to merge and use an icon font into the main font,
|
||||
// so instead of "Save"/"Revert" you'd use icons!
|
||||
// Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient!
|
||||
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Save")) { ref->Colors[i] = style.Colors[i]; }
|
||||
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Revert")) { style.Colors[i] = ref->Colors[i]; }
|
||||
}
|
||||
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
|
||||
ImGui::TextUnformatted(name);
|
||||
bool hueEnabled = HueEnabledColors.contains(i);
|
||||
if (ImGui::Checkbox("Match accent color hue", &hueEnabled)) {
|
||||
if (hueEnabled) {
|
||||
HueEnabledColors.insert(i);
|
||||
} else {
|
||||
HueEnabledColors.erase(i);
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::End();
|
||||
if (importDialog.HasSelected()) {
|
||||
path selected_path = importDialog.GetSelected();
|
||||
path filename = selected_path.filename();
|
||||
copy_file(selected_path, themeDir / filename);
|
||||
availableThemes.insert(filename);
|
||||
}
|
||||
if (exportDialog.HasSelected()) {
|
||||
path selected_path = importDialog.GetSelected();
|
||||
Save(selected_path);
|
||||
}
|
||||
if (loadOpen) {
|
||||
ImGui::OpenPopup("Load...");
|
||||
}
|
||||
if (ImGui::BeginPopupModal("Load...", &loadOpen)) {
|
||||
static path selectedThemePath;
|
||||
static char filter[1024] = {0};
|
||||
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f));
|
||||
ImGui::InputText("Filter: ", filter, 1024);
|
||||
ImGui::Text("Available themes...");
|
||||
if (ImGui::BeginListBox("##Themes", ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), -ImGui::GetTextLineHeightWithSpacing() - ImGui::GetStyle().WindowPadding.y))) {
|
||||
for (auto themePath : availableThemes) {
|
||||
if (themePath.stem().string().starts_with(filter)) {
|
||||
const bool is_selected = themePath == selectedThemePath;
|
||||
if (ImGui::Selectable(themePath.stem().c_str(), is_selected)) {
|
||||
selectedThemePath = themePath;
|
||||
}
|
||||
if (is_selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
if (ImGui::Button("Load")) {
|
||||
if (!selectedThemePath.empty()) {
|
||||
filter[0] = '\0';
|
||||
loadOpen = false;
|
||||
delete theme;
|
||||
theme = new Theme(selectedThemePath);
|
||||
selectedThemePath = path();
|
||||
ImGui::EndPopup();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
selectedThemePath = path();
|
||||
filter[0] = '\0';
|
||||
loadOpen = false;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
if (saveAsOpen) {
|
||||
ImGui::OpenPopup("Save as...");
|
||||
}
|
||||
if (ImGui::BeginPopupModal("Save as...", &saveAsOpen)) {
|
||||
static char selectedThemeName[1024] = {0};
|
||||
static char filter[1024] = {0};
|
||||
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f));
|
||||
ImGui::InputText("Filter: ", filter, 1024);
|
||||
ImGui::Text("Available themes...");
|
||||
if (ImGui::BeginListBox("##Themes", ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), -ImGui::GetFrameHeightWithSpacing() - ImGui::GetTextLineHeightWithSpacing() - ImGui::GetStyle().WindowPadding.y))) {
|
||||
for (auto themePath : availableThemes) {
|
||||
if (themePath.stem().string().starts_with(filter)) {
|
||||
const bool is_selected = strcmp(themePath.stem().c_str(), selectedThemeName) == 0;
|
||||
if (ImGui::Selectable(themePath.stem().c_str(), is_selected)) {
|
||||
strncpy(selectedThemeName, themePath.stem().c_str(), 1024);
|
||||
}
|
||||
if (is_selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f));
|
||||
ImGui::InputText("Theme name: ", selectedThemeName, 1024);
|
||||
if (ImGui::Button("Save")) {
|
||||
path selectedThemePath(selectedThemeName);
|
||||
if (!selectedThemePath.empty() && !selectedThemePath.is_absolute()) {
|
||||
selectedThemeName[0] = '\0'; // This empties the string by taking advantage of C strings.
|
||||
filter[0] = '\0';
|
||||
saveAsOpen = false;
|
||||
Save(themeDir / selectedThemePath.replace_extension(".json"));
|
||||
file_path = selectedThemePath;
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
selectedThemeName[0] = '\0'; // Same as above
|
||||
filter[0] = '\0';
|
||||
saveAsOpen = false;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
ImVec4 change_accent_color(ImVec4 in, float hue) {
|
||||
if (in.x == in.y && in.y == in.z) {
|
||||
return in;
|
||||
}
|
||||
ImVec4 hsv = in;
|
||||
ImVec4 out = in;
|
||||
ImGui::ColorConvertRGBtoHSV(in.x, in.y, in.z, hsv.x, hsv.y, hsv.z);
|
||||
hsv.x = hue / 360.0f;
|
||||
ImGui::ColorConvertHSVtoRGB(hsv.x, hsv.y, hsv.z, out.x, out.y, out.z);
|
||||
return out;
|
||||
}
|
||||
void Theme::Apply(float hue) {
|
||||
ImGuiStyle& actual_style = ImGui::GetStyle();
|
||||
actual_style = style;
|
||||
for (int i = 0; i < ImGuiCol_COUNT; i++)
|
||||
{
|
||||
if (HueEnabledColors.contains(i)) {
|
||||
actual_style.Colors[i] = change_accent_color(style.Colors[i], hue);
|
||||
}
|
||||
}
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
||||
{
|
||||
actual_style.WindowRounding = 0.0f;
|
||||
actual_style.Colors[ImGuiCol_WindowBg].w = 1.0f;
|
||||
}
|
||||
}
|
||||
void Theme::Save(string path) {
|
||||
{
|
||||
Json::Value config;
|
||||
std::ofstream stream;
|
||||
stream.open(path);
|
||||
{
|
||||
Json::Value rounding;
|
||||
rounding["Frame"] = style.FrameRounding;
|
||||
rounding["Window"] = style.WindowRounding;
|
||||
rounding["Child"] = style.ChildRounding;
|
||||
rounding["Popup"] = style.PopupRounding;
|
||||
rounding["Scrollbar"] = style.ScrollbarRounding;
|
||||
rounding["Grab"] = style.GrabRounding;
|
||||
rounding["Tab"] = style.TabRounding;
|
||||
config["rounding"] = rounding;
|
||||
}
|
||||
{
|
||||
Json::Value borders;
|
||||
borders["Frame"] = style.FrameBorderSize;
|
||||
borders["Window"] = style.WindowBorderSize;
|
||||
borders["Child"] = style.ChildBorderSize;
|
||||
borders["Popup"] = style.PopupBorderSize;
|
||||
borders["Tab"] = style.TabBorderSize;
|
||||
config["borders"] = borders;
|
||||
}
|
||||
{
|
||||
Json::Value colors;
|
||||
for (int i = 0; i < ImGuiCol_COUNT; i++)
|
||||
{
|
||||
const char* name = ImGui::GetStyleColorName(i);
|
||||
ImVec4 color = style.Colors[i];
|
||||
Json::Value colorValue;
|
||||
colorValue["r"] = color.x;
|
||||
colorValue["g"] = color.y;
|
||||
colorValue["b"] = color.z;
|
||||
colorValue["a"] = color.w;
|
||||
colorValue["ConvertToAccent"] = HueEnabledColors.contains(i);
|
||||
colors[name] = colorValue;
|
||||
}
|
||||
config["colors"] = colors;
|
||||
}
|
||||
stream << config;
|
||||
stream.close();
|
||||
}
|
||||
updateAvailableThemes();
|
||||
}
|
||||
void Theme::updateAvailableThemes() {
|
||||
availableThemes.clear();
|
||||
for (auto const& dir_entry : directory_iterator(themeDir)) {
|
||||
if (dir_entry.is_regular_file()) {
|
||||
if (dir_entry.path().extension().string() == ".json") {
|
||||
availableThemes.insert(dir_entry.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Theme::Theme() {
|
||||
if (prefPath == NULL) {
|
||||
throw std::exception();
|
||||
}
|
||||
themeDir = path(prefPath) / path("themes");
|
||||
create_directories(themeDir);
|
||||
updateAvailableThemes();
|
||||
}
|
||||
|
||||
Theme::Theme(bool dark) : Theme() {
|
||||
if (dark) {
|
||||
ImGui::StyleColorsDark(&style);
|
||||
} else {
|
||||
ImGui::StyleColorsLight(&style);
|
||||
style.FrameBorderSize = 1;
|
||||
}
|
||||
for (int i = 0; i < ImGuiCol_COUNT; i++)
|
||||
{
|
||||
HueEnabledColors.insert(i);
|
||||
}
|
||||
style.FrameRounding = 12;
|
||||
style.GrabRounding = 12;
|
||||
style.WindowRounding = 12;
|
||||
}
|
||||
Theme::Theme(string path) : Theme() {
|
||||
Json::Value config;
|
||||
std::ifstream stream;
|
||||
stream.open(path);
|
||||
if (stream.is_open()) {
|
||||
stream >> config;
|
||||
if (config.isMember("rounding")) {
|
||||
Json::Value rounding = config["rounding"];
|
||||
style.FrameRounding = rounding["Frame"].asFloat();
|
||||
style.WindowRounding = rounding["Window"].asFloat();
|
||||
style.ChildRounding = rounding["Child"].asFloat();
|
||||
style.PopupRounding = rounding["Popup"].asFloat();
|
||||
style.ScrollbarRounding = rounding["Scrollbar"].asFloat();
|
||||
style.GrabRounding = rounding["Grab"].asFloat();
|
||||
style.TabRounding = rounding["Tab"].asFloat();
|
||||
}
|
||||
if (config.isMember("borders")) {
|
||||
Json::Value borders = config["borders"];
|
||||
style.FrameBorderSize = borders["Frame"].asFloat();
|
||||
style.WindowBorderSize = borders["Window"].asFloat();
|
||||
style.ChildBorderSize = borders["Child"].asFloat();
|
||||
style.PopupBorderSize = borders["Popup"].asFloat();
|
||||
style.TabBorderSize = borders["Tab"].asFloat();
|
||||
}
|
||||
if (config.isMember("colors")) {
|
||||
Json::Value colors = config["colors"];
|
||||
for (int i = 0; i < ImGuiCol_COUNT; i++)
|
||||
{
|
||||
const char* name = ImGui::GetStyleColorName(i);
|
||||
if (colors.isMember(name)) {
|
||||
Json::Value colorValue = colors[name];
|
||||
ImVec4 color = ImVec4(colorValue["r"].asFloat(), colorValue["g"].asFloat(), colorValue["b"].asFloat(), colorValue["a"].asFloat());
|
||||
bool hueShifted = colorValue["ConvertToAccent"].asBool();
|
||||
style.Colors[i] = color;
|
||||
if (hueShifted) {
|
||||
HueEnabledColors.insert(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stream.close();
|
||||
}
|
||||
file_path = path;
|
||||
}
|
30
theme.h
Normal file
30
theme.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
#include "imgui.h"
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include "imfilebrowser.h"
|
||||
#include <filesystem>
|
||||
using std::string;
|
||||
using namespace std::filesystem;
|
||||
|
||||
class Theme {
|
||||
ImGuiStyle style;
|
||||
ImGui::FileBrowser importDialog;
|
||||
ImGui::FileBrowser exportDialog;
|
||||
bool loadOpen = false;
|
||||
bool saveAsOpen = false;
|
||||
std::set<path> availableThemes;
|
||||
void updateAvailableThemes();
|
||||
|
||||
public:
|
||||
path themeDir;
|
||||
static const char* prefPath;
|
||||
string file_path;
|
||||
std::set<int> HueEnabledColors;
|
||||
bool ShowEditor(bool *open, Theme* &theme);
|
||||
void Apply(float hue);
|
||||
void Save(string path);
|
||||
Theme();
|
||||
Theme(bool dark);
|
||||
Theme(string path);
|
||||
};
|
Loading…
Reference in a new issue