#include "UI.hpp" #include "Model/GlobalStates.hpp" #include "Model/Project.hpp" #include "UI/Localization.hpp" #include #include #include #include #include #include namespace fs = std::filesystem; namespace { void ProjectTab_Normal() { auto ls = LocaleStrings::Instance.get(); auto& gs = GlobalStates::GetInstance(); if (ImGui::Button(ls->Close.Get())) { gs.SetCurrentProject(nullptr); return; } ImGui::SameLine(); if (ImGui::Button(ls->OpenActiveProjectInFileSystem.Get())) { // TODO } ImGui::Text("%s%s", ls->ActiveProjectName.Get(), gs.GetCurrentProject()->GetName().c_str()); ImGui::Text("%s%s", ls->ActiveProjectPath.Get(), gs.GetCurrentProject()->GetPathString().c_str()); } void ProjectTab_NoProject() { auto ls = LocaleStrings::Instance.get(); auto& gs = GlobalStates::GetInstance(); bool openedDummy = true; bool openErrorDialog = false; static std::string projectName; static std::string dirName; static fs::path dirPath; static bool dirNameIsValid = false; auto TrySelectPath = [&](fs::path newPath) { if (fs::exists(newPath)) { dirNameIsValid = true; dirName = newPath.string(); dirPath = std::move(newPath); } else { dirNameIsValid = false; } }; if (ImGui::Button(ls->NewProject.Get())) { ImGui::SetNextWindowCentered(); ImGui::OpenPopup(ls->NewProjectDialogTitle.Get()); } // Make it so that the modal dialog has a close button if (ImGui::BeginPopupModal(ls->NewProjectDialogTitle.Get(), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::InputTextWithHint("##ProjectName", ls->NewProjectNameHint.Get(), &projectName); if (ImGui::InputTextWithHint("##ProjectPath", ls->NewProjectPathHint.Get(), &dirName)) { // Changed, validate value TrySelectPath(fs::path(dirName)); } ImGui::SameLine(); if (ImGui::Button("...")) { auto selection = pfd::select_folder(ls->NewProjectPathDialogTitle.Get()).result(); if (!selection.empty()) { TrySelectPath(fs::path(selection)); } } if (projectName.empty()) { ImGui::ErrorMessage("%s", ls->NewProjectEmptyNameError.Get()); } if (!dirNameIsValid) { ImGui::ErrorMessage("%s", ls->NewProjectInvalidPathError.Get()); } ImGui::Spacing(); if (ImGui::Button(ls->DialogConfirm.Get(), !dirNameIsValid || projectName.empty())) { ImGui::CloseCurrentPopup(); gs.SetCurrentProject(std::make_unique(std::move(dirPath), std::move(projectName))); // Dialog just got closed, reset states projectName.clear(); dirName.clear(); dirPath.clear(); dirNameIsValid = false; } ImGui::SameLine(); if (ImGui::Button(ls->DialogCancel.Get())) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } ImGui::SameLine(); if (ImGui::Button(ls->OpenProject.Get())) { auto selection = pfd::open_file(ls->OpenProjectDialogTitle.Get()).result(); if (!selection.empty()) { fs::path path(selection[0]); try { // Project's constructor wants a path to directory containing cplt_project.json gs.SetCurrentProject(std::make_unique(path.parent_path())); openErrorDialog = false; } catch (const std::exception& e) { openErrorDialog = true; } } } // TODO cleanup UI // Recent projects ImGui::Separator(); ImGui::TextUnformatted(ls->RecentProjects.Get()); ImGui::SameLine(); if (ImGui::Button(ls->ClearRecentProjects.Get())) { gs.ClearRecentProjects(); } auto& rp = gs.GetRecentProjects(); // End of vector is the most recently used, so that appending has less overhead size_t toRemoveIdx = rp.size(); if (rp.empty()) { ImGui::TextUnformatted(ls->NoRecentProjectsMessage.Get()); } else { for (auto it = rp.rbegin(); it != rp.rend(); ++it) { auto& [path, recent] = *it; ImGui::TextUnformatted(recent.c_str()); size_t idx = std::distance(it, rp.rend()) - 1; ImGui::PushID(idx); ImGui::SameLine(); if (ImGui::Button(ICON_FA_FOLDER_OPEN)) { try { gs.SetCurrentProject(std::make_unique(path)); openErrorDialog = false; } catch (const std::exception& e) { openErrorDialog = true; } } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", ls->OpenRecentProjectTooltip.Get()); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_TRASH)) { toRemoveIdx = idx; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", ls->DeleteRecentProjectTooltip.Get()); } ImGui::PopID(); } } if (toRemoveIdx != rp.size()) { gs.RemoveRecentProject(toRemoveIdx); } if (openErrorDialog) { ImGui::SetNextWindowCentered(); ImGui::OpenPopup(ls->Error.Get()); } if (ImGui::BeginPopupModal(ls->Error.Get(), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::ErrorMessage("%s", ls->InvalidProjectFormat.Get()); ImGui::EndPopup(); } } } // namespace void UI::MainWindow() { auto ls = LocaleStrings::Instance.get(); auto& gs = GlobalStates::GetInstance(); auto windowSize = ImGui::GetMainViewport()->Size; ImGui::SetNextWindowSize({ windowSize.x, windowSize.y }); ImGui::SetNextWindowPos({ 0, 0 }); ImGui::Begin("##MainWindow", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize); if (ImGui::BeginTabBar("MainWindowTabs")) { if (ImGui::BeginTabItem(ls->SettingsTab.Get())) { UI::SettingsTab(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem(ls->ProjectTab.Get(), nullptr)) { if (gs.HasCurrentProject()) { ProjectTab_Normal(); } else { ProjectTab_NoProject(); } ImGui::EndTabItem(); } if (!gs.HasCurrentProject()) { // No project open, simply skip all project specific tabs goto endTab; } if (ImGui::BeginTabItem(ls->DatabaseViewTab.Get())) { UI::DatabaseViewTab(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem(ls->ItemsTab.Get())) { UI::ItemsTab(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem(ls->WorkflowsTab.Get())) { UI::WorkflowsTab(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem(ls->TemplatesTab.Get())) { UI::TemplatesTab(); ImGui::EndTabItem(); } endTab: ImGui::EndTabBar(); } ImGui::End(); }