looper/theme.cpp

634 lines
30 KiB
C++

#include "theme.h"
#include "imgui.h"
#include "json/value.h"
#include "translation.h"
#include <cmath>
#include <exception>
#include <numbers>
#include <iostream>
#include <fstream>
#include <filesystem>
#include <functional>
#include "IconsForkAwesome.h"
#include "imgui_stdlib.h"
using namespace std::filesystem;
using namespace std::numbers;
const char* Theme::prefPath = NULL;
path Theme::themeDir = path();
std::set<path> Theme::availableThemes = std::set<path>();
std::map<path, ThemeStrings> Theme::themeStrings = std::map<path, ThemeStrings>();
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;
}
bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_width, int window_height) {
static FileBrowser importDialog(false, ImGuiFileBrowserFlags_NoTitleBar|ImGuiFileBrowserFlags_NoMove|ImGuiFileBrowserFlags_NoResize);
static FileBrowser exportDialog(true, ImGuiFileBrowserFlags_NoTitleBar|ImGuiFileBrowserFlags_NoMove|ImGuiFileBrowserFlags_NoResize);
static bool loadOpen = false;
static bool saveAsOpen = false;
ImGui::SetNextWindowDockID(dockid);
ImGui::Begin(_TRI_CTX(ICON_FK_MAGIC, "Window title", "Theme Editor"), open);
ImGuiStyle& style = theme->style;
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f);
ImVec2 buttonSize = ImVec2((ImGui::GetWindowWidth() * 0.50f) - (ImGui::GetStyle().WindowPadding.x) - (ImGui::GetStyle().ItemSpacing.x * 0.5f), 0);
if (ImGui::Button(_TR_CTX("Theme Editor | preset button", "Create light"), buttonSize)) {
delete theme;
theme = new Theme(false);
ImGui::PopItemWidth();
ImGui::End();
return true;
}
ImGui::SameLine();
if (ImGui::Button(_TR_CTX("Theme Editor | preset button", "Create dark"), buttonSize)) {
delete theme;
theme = new Theme(true);
ImGui::PopItemWidth();
ImGui::End();
return true;
}
if (ImGui::Button(_TR_CTX("Theme Editor | import button. Opens the theme import file dialog", "Import..."), buttonSize)) {
importDialog.SetTitle(_TR_CTX("Theme Editor file dialog title", "Import theme..."));
importDialog.SetTypeFilters(_TR_CTX("Theme Editor file dialog filter name", "Theme JSON files"), { ".json"});
std::string userdir = std::getenv(
#ifdef _WIN32
"UserProfile"
#else
"HOME"
#endif
);
importDialog.SetPwd(userdir);
importDialog.Open();
}
ImGui::SameLine();
if (ImGui::Button(_TR_CTX("Theme Editor | export button. Opens the theme export file dialog", "Export..."), buttonSize)) {
exportDialog.SetTitle(_TR_CTX("Theme Editor file dialog title", "Export theme..."));
exportDialog.SetTypeFilters(_TR_CTX("Theme Editor file dialog filter name", "Theme JSON files"), { ".json"});
std::string userdir = std::getenv(
#ifdef _WIN32
"UserProfile"
#else
"HOME"
#endif
);
exportDialog.SetPwd(userdir);
exportDialog.Open();
}
if (importDialog.IsOpened()) {
importDialog.SetWindowSize(window_width, window_height);
importDialog.SetWindowPos(0, 0);
}
if (exportDialog.IsOpened()) {
exportDialog.SetWindowSize(window_width, window_height);
exportDialog.SetWindowPos(0, 0);
}
importDialog.Display();
exportDialog.Display();
if (!theme->file_path.empty()) {
if (ImGui::Button(_TR_CTX("Theme Editor | button. Reverts to saved file.", "Revert"), buttonSize)) {
string file_path_backup = theme->file_path;
delete theme;
theme = new Theme(file_path_backup);
ImGui::PopItemWidth();
ImGui::End();
return true;
}
ImGui::SameLine();
if (ImGui::Button(_TR_CTX("Theme Editor | button. Saves the theme to it's current location. Not shown when it doesn't already have one.", "Save"), buttonSize)) {
theme->Save(theme->file_path);
}
}
if (ImGui::Button(_TR_CTX("Theme Editor | button. Opens the theme loading dialog for themes created or imported by the user.", "Load..."), buttonSize)) {
loadOpen = true;
}
ImGui::SameLine();
if (ImGui::Button(_TR_CTX("Theme Editor | button. Opens the theme saving dialog for themes created or imported by the user.", "Save as..."), buttonSize)) {
saveAsOpen = true;
}
// Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)
if (ImGui::SliderFloat(_TR_CTX("Theme Editor | slider. Simplified frame rounding.", "Frame Rounding"), &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(_TR_CTX("Theme Editor | checkbox", "Window Border"), &border)) { style.WindowBorderSize = border ? 1.0f : 0.0f; } }
ImGui::SameLine();
{ bool border = (style.FrameBorderSize > 0.0f); if (ImGui::Checkbox(_TR_CTX("Theme Editor | checkbox", "Frame Border"), &border)) { style.FrameBorderSize = border ? 1.0f : 0.0f; } }
ImGui::SameLine();
{ bool border = (style.PopupBorderSize > 0.0f); if (ImGui::Checkbox(_TR_CTX("Theme Editor | checkbox", "Popup Border"), &border)) { style.PopupBorderSize = border ? 1.0f : 0.0f; } }
ImGui::Separator();
if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None))
{
if (ImGui::BeginTabItem(_TR_CTX("Theme Editor | Tab label", "Strings")))
{
ImGui::InputText(_TR_CTX("Theme Editor | Strings | Name input label", "Name"), &theme->strings["fallback"].name);
ImGui::InputText(_TR_CTX("Theme Editor | Strings | Description input label", "Description"), &theme->strings["fallback"].description);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem(_TR_CTX("Theme Editor | Tab label", "Sizes")))
{
ImGui::SeparatorText(_TR_CTX("Theme Editor | Sizes | Section label", "Sizing"));
ImGui::SliderFloat2(_TR_CTX("Theme Editor | Sizes | Sizing | label of XY sliders", "Window Padding"), (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2(_TR_CTX("Theme Editor | Sizes | Sizing | label of XY sliders", "Frame Padding"), (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2(_TR_CTX("Theme Editor | Sizes | Sizing | label of XY sliders", "Cell Padding"), (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2(_TR_CTX("Theme Editor | Sizes | Sizing | label of XY sliders", "Item Spacing"), (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2(_TR_CTX("Theme Editor | Sizes | Sizing | label of XY sliders", "Inner Item Spacing"), (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f");
//ImGui::SliderFloat2(_TR_CTX("Theme Editor | Sizes | Sizing | label of XY sliders", "TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f");
//ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Sizing | slider label", "IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Sizing | slider label", "Scrollbar Size"), &style.ScrollbarSize, 1.0f, 20.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Sizing | slider label", "Minimum Grabber Size"), &style.GrabMinSize, 1.0f, 20.0f, "%.0f");
ImGui::SliderFloat2(_TR_CTX("Theme Editor | Sizes | Sizing | label of XY sliders", "Separator Text Padding"), (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f");
ImGui::SeparatorText(_TR_CTX("Theme Editor | Sizes | Section label", "Borders"));
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Borders | slider label", "Window Border Size"), &style.WindowBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Borders | slider label", "Child Border Size"), &style.ChildBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Borders | slider label", "Popup Border Size"), &style.PopupBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Borders | slider label", "Frame Border Size"), &style.FrameBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Borders | slider label", "Tab Border Size"), &style.TabBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Borders | slider label", "Separator Text Border Size"), &style.SeparatorTextBorderSize, 0.0f, 10.0f, "%.0f");
ImGui::SeparatorText(_TR_CTX("Theme Editor | Sizes | Section label", "Rounding"));
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Rounding | slider label", "Window Rounding"), &style.WindowRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Rounding | slider label", "Child Rounding"), &style.ChildRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Rounding | slider label", "Frame Rounding"), &style.FrameRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Rounding | slider label", "Popup Rounding"), &style.PopupRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Rounding | slider label", "Scrollbar Rounding"), &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Rounding | slider label", "Grabber Rounding"), &style.GrabRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat(_TR_CTX("Theme Editor | Sizes | Rounding | slider label", "Tab Rounding"), &style.TabRounding, 0.0f, 12.0f, "%.0f");
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem(_TR_CTX("Theme Editor | Tab label", "Colors")))
{
static ImGuiTextFilter filter;
filter.Draw(_TR_CTX("Theme Editor | Colors | text filter", "Filter colors"), ImGui::GetFontSize() * 16);
static ImGuiColorEditFlags alpha_flags = 0;
if (ImGui::RadioButton(_TR_CTX("Theme Editor | Colors | preview settings radio button", "Opaque"), alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine();
if (ImGui::RadioButton(_TR_CTX("Theme Editor | Colors | preview settings radio button", "Alpha"), alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine();
if (ImGui::RadioButton(_TR_CTX("Theme Editor | Colors | preview settings radio button", "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);
AccentColorizer &colorizer = theme->AccentColorizers[i];
ImGui::Text(_TR_CTX("Theme Editor | Colors | (Any color) | recoloring label", "Match accent color preference: "));
ImGui::Checkbox(_TR_CTX("Theme Editor | Colors | (Any color) | recoloring checkbox | Hue", "H: "), &colorizer.Hue); ImGui::SameLine();
ImGui::Checkbox(_TR_CTX("Theme Editor | Colors | (Any color) | recoloring checkbox | Saturation", "S: "), &colorizer.Saturation); ImGui::SameLine();
ImGui::Checkbox(_TR_CTX("Theme Editor | Colors | (Any color) | recoloring checkbox | Value", "V: "), &colorizer.Value); ImGui::SameLine();
ImGui::Checkbox(_TR_CTX("Theme Editor | Colors | (Any color) | recoloring checkbox | Alpha (opacity)", "A: "), &colorizer.Alpha); ImGui::SameLine();
ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags | ImGuiColorEditFlags_DisplayHSV);
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
ImGui::TextUnformatted(name);
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, theme->themeDir / filename);
Theme::updateAvailableThemes();
importDialog.ClearSelected();
}
if (exportDialog.HasSelected()) {
path selected_path = exportDialog.GetSelected();
theme->Save(selected_path);
exportDialog.ClearSelected();
}
if (loadOpen) {
ImGui::OpenPopup(_TR_CTX("Theme Editor | Custom modal dialog title", "Load..."));
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImVec2(window_width, window_height));
if (ImGui::BeginPopupModal(_TR_CTX("Theme Editor | Custom modal dialog title", "Load..."), nullptr, ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize)) {
static path selectedThemePath;
static string filter = "";
ImGui::Text(_TR_CTX("Theme Editor | Load dialog | Theme selector | filter label", "Filter:")); ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x);
ImGui::InputText("##FilterInput", &filter);
ImGui::Text(_TR_CTX("Theme Editor | Load dialog | Theme selector | label", "Available themes..."));
if (ImGui::BeginListBox("##Themes", ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), -ImGui::GetTextLineHeightWithSpacing() - ImGui::GetStyle().WindowPadding.y))) {
for (auto themePath : Theme::availableThemes) {
string themeStem = themePath.stem().string();
if (themeStem.starts_with(filter)) {
const bool is_selected = themePath == selectedThemePath;
if (ImGui::Selectable((theme->themeStrings[themePath].name + string(" (") + string(themeStem) + string(")")).c_str(), is_selected)) {
selectedThemePath = themePath;
}
if (is_selected) {
ImGui::SetItemDefaultFocus();
}
}
}
ImGui::EndListBox();
}
if (ImGui::Button(_TR_CTX("Theme Editor | Load dialog | Load button", "Load"))) {
if (!selectedThemePath.empty()) {
filter = "";
loadOpen = false;
delete theme;
theme = new Theme(selectedThemePath);
selectedThemePath = path();
ImGui::EndPopup();
return true;
}
}
ImGui::SameLine();
if (ImGui::Button(_TR_CTX("Theme Editor | Load dialog | Cancel button", "Cancel"))) {
selectedThemePath = path();
filter = "";
loadOpen = false;
}
ImGui::EndPopup();
}
}
if (saveAsOpen) {
ImGui::OpenPopup(_TR_CTX("Theme Editor | Custom modal dialog title", "Save as..."));
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImVec2(window_width, window_height));
if (ImGui::BeginPopupModal(_TR_CTX("Theme Editor | Custom modal dialog title", "Save as..."), nullptr, ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize)) {
static string selectedThemeName = "";
static string filter = "";
ImGui::Text(_TR_CTX("Theme Editor | Save as dialog | Theme selector | filter label", "Filter:")); ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x);
ImGui::InputText("##FilterInput", &filter, 1024);
ImGui::Text(_TR_CTX("Theme Editor | Save as dialog | Theme selector | label", "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 : Theme::availableThemes) {
string themeStem = themePath.stem().string();
if (themeStem.starts_with(filter)) {
const bool is_selected = themeStem == selectedThemeName;
if (ImGui::Selectable((theme->themeStrings[themePath].name + string(" (") + string(themeStem) + string(")")).c_str(), is_selected)) {
selectedThemeName = themeStem;
}
if (is_selected) {
ImGui::SetItemDefaultFocus();
}
}
}
ImGui::EndListBox();
}
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f));
ImGui::InputText(_TR_CTX("Theme Editor | Save as dialog | Theme name input label", "Theme name: "), &selectedThemeName);
if (ImGui::Button(_TR_CTX("Theme Editor | Save as dialog | Save button", "Save"))) {
path selectedThemePath(selectedThemeName);
if (!selectedThemePath.empty() && !selectedThemePath.is_absolute()) {
selectedThemeName = "";
filter = "";
saveAsOpen = false;
theme->Save(Theme::themeDir / selectedThemePath.replace_extension(".json"));
theme->file_path = selectedThemePath.generic_string();
}
}
ImGui::SameLine();
if (ImGui::Button(_TR_CTX("Theme Editor | Save as dialog | Cancel button", "Cancel"))) {
selectedThemeName = "";
filter = "";
saveAsOpen = false;
}
ImGui::EndPopup();
}
}
return false;
}
AccentColorizer::AccentColorizer() {
Hue = false;
Saturation = false;
Value = false;
Alpha = false;
}
AccentColorizer::AccentColorizer(Json::Value json) : AccentColorizer() {
if (json.isBool()) {
Hue = json.asBool();
Saturation = Hue;
Value = Hue;
Alpha = Hue;
} else {
Hue = json["hue"].asBool();
Saturation = json["saturation"].asBool();
Value = json["value"].asBool();
Alpha = json["alpha"].asBool();
}
}
Json::Value AccentColorizer::Serialize() {
Json::Value output;
output["hue"] = Hue;
output["saturation"] = Saturation;
output["value"] = Value;
output["alpha"] = Alpha;
return output;
}
void AccentColorizer::Colorize(ImVec4 accent, ImVec4 &color) {
ImVec4 hsv = color;
ImGui::ColorConvertRGBtoHSV(color.x, color.y, color.z, hsv.x, hsv.y, hsv.z);
if (Saturation)
hsv.y = accent.y;
if (Value)
hsv.z = accent.z;
if (Hue)
hsv.x = accent.x;
ImGui::ColorConvertHSVtoRGB(hsv.x, hsv.y, hsv.z, color.x, color.y, color.z);
if (Alpha)
color.w *= accent.w;
}
void Theme::Apply(ImVec4 accent) {
ImGuiStyle& actual_style = ImGui::GetStyle();
actual_style = style;
for (int i = 0; i < ImGuiCol_COUNT; i++)
{
AccentColorizers[i].Colorize(accent, actual_style.Colors[i]);
}
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) {
printf("Saving theme to %s...\n", path.c_str());
{
Json::Value config;
std::ofstream stream;
stream.open(path);
{
Json::Value metadata;
metadata["SchemaVersion"] = 2;
{
Json::Value stringsList;
for (auto kv : strings) {
Json::Value stringsEntryJson;
string language = kv.first;
ThemeStrings stringsEntry = kv.second;
stringsEntryJson["Name"] = stringsEntry.name;
stringsEntryJson["Description"] = stringsEntry.description;
stringsList[language] = stringsEntryJson;
}
metadata["Strings"] = stringsList;
}
config["meta"] = metadata;
}
{
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 sizing;
sizing["FrameX"] = style.FramePadding.x;
sizing["FrameY"] = style.FramePadding.y;
sizing["WindowX"] = style.WindowPadding.x;
sizing["WindowY"] = style.WindowPadding.y;
sizing["CellX"] = style.CellPadding.x;
sizing["CellY"] = style.CellPadding.y;
sizing["SeparatorTextX"] = style.SeparatorTextPadding.x;
sizing["SeparatorTextY"] = style.SeparatorTextPadding.y;
sizing["ItemSpacingX"] = style.ItemSpacing.x;
sizing["ItemSpacingY"] = style.ItemSpacing.y;
sizing["Scrollbar"] = style.ScrollbarSize;
sizing["Grab"] = style.GrabMinSize;
config["sizing"] = sizing;
}
{
Json::Value borders;
borders["Frame"] = style.FrameBorderSize;
borders["Window"] = style.WindowBorderSize;
borders["Child"] = style.ChildBorderSize;
borders["Popup"] = style.PopupBorderSize;
borders["Tab"] = style.TabBorderSize;
borders["Tab"] = style.TabBorderSize;
borders["SeparatorText"] = style.SeparatorTextBorderSize;
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"] = AccentColorizers[i].Serialize();
colors[name] = colorValue;
}
config["colors"] = colors;
}
stream << config;
stream.close();
}
updateAvailableThemes();
}
void Theme::Save(path path) {
Save((string)path.string());
}
void Theme::updateAvailableThemes() {
themeDir = path(prefPath) / path("themes");
create_directories(themeDir);
availableThemes.clear();
themeStrings.clear();
for (auto const& dir_entry : directory_iterator(themeDir)) {
if (dir_entry.is_regular_file()) {
if (dir_entry.path().extension().string() == ".json") {
path path = dir_entry.path();
availableThemes.insert(path);
Json::Value config;
std::ifstream stream;
stream.open(path);
if (stream.is_open()) {
stream >> config;
themeStrings[path] = ThemeStrings(config);
}
}
}
}
}
Theme::Theme() {
if (prefPath == NULL) {
throw std::exception();
}
updateAvailableThemes();
strings["fallback"] = ThemeStrings();
}
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++)
{
ImVec4 color = style.Colors[i];
auto colorizer = AccentColorizer();
if (color.x != color.y || color.y != color.z) {
colorizer.Hue = true;
colorizer.Saturation = true;
colorizer.Alpha = true;
}
AccentColorizers[i] = colorizer;
}
style.FrameRounding = 12;
style.GrabRounding = 12;
style.WindowRounding = 12;
}
ThemeStrings::ThemeStrings() {
name = _TRS_CTX("Theme default strings | name", "A theme");
description = _TRS_CTX("Theme default strings | description", "(No description)");
}
ThemeStrings Theme::GetStrings() {
char *language_c = CURRENT_LANGUAGE;
string language = language_c;
if (strings.contains(language)) {
return strings[language];
} else {
if (!strings.contains("fallback")) {
strings["fallback"] = ThemeStrings();
}
return strings["fallback"];
}
}
ThemeStrings::ThemeStrings(Json::Value config) : ThemeStrings() {
char *language_c = CURRENT_LANGUAGE;
string language = language_c;
if (config.isMember("meta")) {
Json::Value metadata = config["meta"];
//metadata["SchemaVersion"] = 1;
if (metadata.isMember("Strings")) {
Json::Value stringsList = metadata["Strings"];
if (stringsList.isMember(language)) {
Json::Value stringsEntryJson = stringsList[language];
if (stringsEntryJson.isMember("Name")) {
name = stringsEntryJson["Name"].asString();
}
if (stringsEntryJson.isMember("Description")) {
description = stringsEntryJson["Description"].asString();
}
} else if (metadata.isMember("fallback")) {
Json::Value stringsEntryJson = stringsList["fallback"];
if (stringsEntryJson.isMember("Name")) {
name = stringsEntryJson["Name"].asString();
}
if (stringsEntryJson.isMember("Description")) {
description = stringsEntryJson["Description"].asString();
}
}
}
}
}
Theme::Theme(string path) : Theme() {
Json::Value config;
std::ifstream stream;
stream.open(path);
if (stream.is_open()) {
stream >> config;
if (config.isMember("meta")) {
Json::Value metadata = config["meta"];
//metadata["SchemaVersion"] = 1;
if (metadata.isMember("Strings")) {
Json::Value stringsList = metadata["Strings"];
for (string language : stringsList.getMemberNames()) {
Json::Value stringsEntryJson = stringsList[language];
ThemeStrings stringsEntry;
if (stringsEntryJson.isMember("Name")) {
stringsEntry.name = stringsEntryJson["Name"].asString();
}
if (stringsEntryJson.isMember("Description")) {
stringsEntry.description = stringsEntryJson["Description"].asString();
}
strings[language] = stringsEntry;
}
}
}
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("sizing")) {
Json::Value sizing = config["sizing"];
style.FramePadding.x = sizing["FrameX"].asFloat();
style.FramePadding.y = sizing["FrameY"].asFloat();
style.WindowPadding.x = sizing["WindowX"].asFloat();
style.WindowPadding.y = sizing["WindowY"].asFloat();
style.CellPadding.x = sizing["CellX"].asFloat();
style.CellPadding.y = sizing["CellY"].asFloat();
style.SeparatorTextPadding.x = sizing["SeparatorTextX"].asFloat();
style.SeparatorTextPadding.y = sizing["SeparatorTextY"].asFloat();
style.ItemSpacing.x = sizing["ItemSpacingX"].asFloat();
style.ItemSpacing.y = sizing["ItemSpacingY"].asFloat();
style.ScrollbarSize = sizing["Scrollbar"].asFloat();
style.GrabMinSize = sizing["Grab"].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 (borders.isMember("SeparatorText")) {
style.SeparatorTextBorderSize = borders["SeparatorText"].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());
AccentColorizers[i] = AccentColorizer(colorValue["ConvertToAccent"]);
style.Colors[i] = color;
}
}
}
stream.close();
}
file_path = path;
}
Theme::Theme(path path) : Theme(path.string()) {
}