Improve theme preferences and make editor static

This commit is contained in:
Zachary Hall 2023-07-10 19:27:55 -07:00
parent 01edf59446
commit 9c6f69203d
3 changed files with 79 additions and 48 deletions

View file

@ -17,6 +17,7 @@
#include <string>
#include <SDL.h>
#include <SDL_image.h>
#include <filesystem>
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <SDL_opengles2.h>
#else
@ -214,6 +215,18 @@ int main(int, char**)
show_demo_window = config["demo_window"].asBool();
}
stream.close();
}
if (is_empty(Theme::themeDir)) {
path lightPath = Theme::themeDir / "light.json";
path darkPath = Theme::themeDir / "dark.json";
if (!exists(lightPath)) {
Theme light(false);
light.Save(lightPath);
}
if (!exists(darkPath)) {
Theme dark(true);
dark.Save(darkPath);
}
}
}
#ifdef __EMSCRIPTEN__
@ -312,23 +325,44 @@ int main(int, char**)
if (prefs_window) {
ImVec2 min_size;
min_size.x = ImGui::GetFontSize() * 18;
min_size.y = (ImGui::GetStyle().FramePadding.y * 5) + (ImGui::GetFontSize() * 5);
min_size.y = (ImGui::GetFrameHeightWithSpacing() * 8) + (ImGui::GetFontSize());
ImVec2 max_size;
max_size.x = 99999997952; // The compiler says that if this were just 9s it would be turned into this anyways.
max_size.y = min_size.y;
max_size.y = 99999997952;
ImGui::SetNextWindowSizeConstraints(min_size, max_size);
ImGui::Begin("Preferences...", &prefs_window);
{
if (ImGui::Button(ICON_FK_MAGIC "Theme Editor")) {
if (ImGui::Button(ICON_FK_MAGIC "Theme Editor", ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), 0))) {
theme_editor = true;
}
static char filter[1024] = {0};
ImGui::Text("Filter:"); ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x);
ImGui::InputText("##FilterInput", filter, 1024);
ImGui::Text("Select a theme...");
if (ImGui::BeginListBox("##Themes", ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), -ImGui::GetTextLineHeightWithSpacing() - ImGui::GetStyle().WindowPadding.y))) {
for (auto themePath : Theme::availableThemes) {
if (themePath.stem().string().starts_with(filter)) {
const bool is_selected = themePath == theme->file_path;
if (ImGui::Selectable(themePath.stem().c_str(), is_selected)) {
delete theme;
theme = new Theme(themePath);
break;
}
if (is_selected) {
ImGui::SetItemDefaultFocus();
}
}
}
ImGui::EndListBox();
}
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - (ImGui::GetStyle().FramePadding.x * 4));
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::ShowEditor(&theme_editor, theme);
}
theme->Apply(accent_color);
fileDialog.Display();

View file

@ -12,11 +12,18 @@
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>();
bool Theme::ShowEditor(bool* open, Theme* &theme) {
static ImGui::FileBrowser importDialog;
static ImGui::FileBrowser exportDialog;
static bool loadOpen = false;
static bool saveAsOpen = false;
ImGui::Begin("Theme Editor", open);
ImGuiStyle *ref = &style;
ImGuiStyle& style = theme->style;
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f);
if (ImGui::Button("Create light")) {
ImVec2 buttonSize = ImVec2((ImGui::GetWindowWidth() * 0.50f) - (ImGui::GetStyle().WindowPadding.x) - (ImGui::GetStyle().ItemSpacing.x * 0.5f), 0);
if (ImGui::Button("Create light", buttonSize)) {
delete theme;
theme = new Theme(false);
@ -25,7 +32,7 @@ bool Theme::ShowEditor(bool* open, Theme* &theme) {
return true;
}
ImGui::SameLine();
if (ImGui::Button("Create dark")) {
if (ImGui::Button("Create dark", buttonSize)) {
delete theme;
theme = new Theme(true);
@ -33,7 +40,7 @@ bool Theme::ShowEditor(bool* open, Theme* &theme) {
ImGui::End();
return true;
}
if (ImGui::Button("Import...")) {
if (ImGui::Button("Import...", buttonSize)) {
importDialog.SetTitle("Import theme...");
importDialog.SetTypeFilters({ ".json"});
std::string userdir = std::getenv(
@ -47,7 +54,7 @@ bool Theme::ShowEditor(bool* open, Theme* &theme) {
importDialog.Open();
}
ImGui::SameLine();
if (ImGui::Button("Export...")) {
if (ImGui::Button("Export...", buttonSize)) {
exportDialog = ImGui::FileBrowser(ImGuiFileBrowserFlags_EnterNewFilename|ImGuiFileBrowserFlags_CreateNewDir);
exportDialog.SetTitle("Export theme...");
exportDialog.SetTypeFilters({ ".json"});
@ -63,9 +70,9 @@ bool Theme::ShowEditor(bool* open, Theme* &theme) {
}
importDialog.Display();
exportDialog.Display();
if (!file_path.empty()) {
if (ImGui::Button("Revert")) {
string file_path_backup = file_path;
if (!theme->file_path.empty()) {
if (ImGui::Button("Revert", buttonSize)) {
string file_path_backup = theme->file_path;
delete theme;
theme = new Theme(file_path_backup);
ImGui::PopItemWidth();
@ -73,15 +80,15 @@ bool Theme::ShowEditor(bool* open, Theme* &theme) {
return true;
}
ImGui::SameLine();
if (ImGui::Button("Save")) {
Save(file_path);
if (ImGui::Button("Save", buttonSize)) {
theme->Save(theme->file_path);
}
}
if (ImGui::Button("Load...")) {
if (ImGui::Button("Load...", buttonSize)) {
loadOpen = true;
}
ImGui::SameLine();
if (ImGui::Button("Save as...")) {
if (ImGui::Button("Save as...", buttonSize)) {
saveAsOpen = true;
}
@ -140,22 +147,14 @@ bool Theme::ShowEditor(bool* open, Theme* &theme) {
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);
bool hueEnabled = theme->HueEnabledColors.contains(i);
if (ImGui::Checkbox("Match accent color hue", &hueEnabled)) {
if (hueEnabled) {
HueEnabledColors.insert(i);
theme->HueEnabledColors.insert(i);
} else {
HueEnabledColors.erase(i);
theme->HueEnabledColors.erase(i);
}
}
ImGui::PopID();
@ -174,12 +173,12 @@ bool Theme::ShowEditor(bool* open, Theme* &theme) {
if (importDialog.HasSelected()) {
path selected_path = importDialog.GetSelected();
path filename = selected_path.filename();
copy_file(selected_path, themeDir / filename);
availableThemes.insert(filename);
copy_file(selected_path, theme->themeDir / filename);
theme->availableThemes.insert(filename);
}
if (exportDialog.HasSelected()) {
path selected_path = importDialog.GetSelected();
Save(selected_path);
theme->Save(selected_path);
}
if (loadOpen) {
ImGui::OpenPopup("Load...");
@ -187,11 +186,12 @@ bool Theme::ShowEditor(bool* open, Theme* &theme) {
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("Filter:"); ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x);
ImGui::InputText("##FilterInput", 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) {
for (auto themePath : Theme::availableThemes) {
if (themePath.stem().string().starts_with(filter)) {
const bool is_selected = themePath == selectedThemePath;
if (ImGui::Selectable(themePath.stem().c_str(), is_selected)) {
@ -229,11 +229,12 @@ bool Theme::ShowEditor(bool* open, Theme* &theme) {
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("Filter:"); ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x);
ImGui::InputText("##FilterInput", 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) {
for (auto themePath : Theme::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)) {
@ -254,8 +255,8 @@ bool Theme::ShowEditor(bool* open, Theme* &theme) {
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;
theme->Save(Theme::themeDir / selectedThemePath.replace_extension(".json"));
theme->file_path = selectedThemePath;
}
}
ImGui::SameLine();
@ -342,6 +343,8 @@ void Theme::Save(string path) {
updateAvailableThemes();
}
void Theme::updateAvailableThemes() {
themeDir = path(prefPath) / path("themes");
create_directories(themeDir);
availableThemes.clear();
for (auto const& dir_entry : directory_iterator(themeDir)) {
if (dir_entry.is_regular_file()) {
@ -355,8 +358,6 @@ Theme::Theme() {
if (prefPath == NULL) {
throw std::exception();
}
themeDir = path(prefPath) / path("themes");
create_directories(themeDir);
updateAvailableThemes();
}

12
theme.h
View file

@ -9,19 +9,15 @@ 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 std::set<path> availableThemes;
static void updateAvailableThemes();
static path themeDir;
static const char* prefPath;
string file_path;
std::set<int> HueEnabledColors;
bool ShowEditor(bool *open, Theme* &theme);
static bool ShowEditor(bool *open, Theme* &theme);
void Apply(float hue);
void Save(string path);
Theme();