diff --git a/main.cpp b/main.cpp index b5a4a57..7a2840f 100644 --- a/main.cpp +++ b/main.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #if defined(IMGUI_IMPL_OPENGL_ES2) #include #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(); diff --git a/theme.cpp b/theme.cpp index 0884422..d73221f 100644 --- a/theme.cpp +++ b/theme.cpp @@ -12,11 +12,18 @@ using namespace std::filesystem; using namespace std::numbers; const char* Theme::prefPath = NULL; +path Theme::themeDir = path(); +std::set Theme::availableThemes = std::set(); 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(); } diff --git a/theme.h b/theme.h index 26ca388..bc1cb51 100644 --- a/theme.h +++ b/theme.h @@ -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 availableThemes; - void updateAvailableThemes(); public: - path themeDir; + static std::set availableThemes; + static void updateAvailableThemes(); + static path themeDir; static const char* prefPath; string file_path; std::set HueEnabledColors; - bool ShowEditor(bool *open, Theme* &theme); + static bool ShowEditor(bool *open, Theme* &theme); void Apply(float hue); void Save(string path); Theme();