feat(session): restore repository tabs and selection
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
std::filesystem::path roaming_directory() {
|
||||
@@ -49,6 +50,12 @@ void UserData::load() {
|
||||
if (!path.empty()) recently_closed_.push_back(path);
|
||||
if (recently_closed_.size() == 12) break;
|
||||
}
|
||||
|
||||
std::ifstream session(directory_ / "session.txt");
|
||||
session >> active_repository_;
|
||||
while (session >> std::quoted(path)) open_repositories_.push_back(path);
|
||||
if (open_repositories_.empty()) active_repository_ = 0;
|
||||
else active_repository_ = std::min(active_repository_, open_repositories_.size() - 1);
|
||||
}
|
||||
|
||||
void UserData::addRecentlyClosed(const std::string& path) {
|
||||
@@ -59,6 +66,14 @@ void UserData::addRecentlyClosed(const std::string& path) {
|
||||
save();
|
||||
}
|
||||
|
||||
void UserData::setRepositorySession(std::vector<std::string> paths, size_t active_repository) {
|
||||
open_repositories_ = std::move(paths);
|
||||
active_repository_ = open_repositories_.empty()
|
||||
? 0
|
||||
: std::min(active_repository, open_repositories_.size() - 1);
|
||||
save();
|
||||
}
|
||||
|
||||
void UserData::save() const {
|
||||
std::filesystem::create_directories(directory_);
|
||||
std::ofstream settings(directory_ / "settings.ini", std::ios::trunc);
|
||||
@@ -69,4 +84,8 @@ void UserData::save() const {
|
||||
|
||||
std::ofstream history(directory_ / "history.txt", std::ios::trunc);
|
||||
for (const auto& path : recently_closed_) history << std::quoted(path) << '\n';
|
||||
|
||||
std::ofstream session(directory_ / "session.txt", std::ios::trunc);
|
||||
session << active_repository_ << '\n';
|
||||
for (const auto& path : open_repositories_) session << std::quoted(path) << '\n';
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ public:
|
||||
[[nodiscard]] const std::filesystem::path& directory() const { return directory_; }
|
||||
[[nodiscard]] const std::string& imguiIniPath() const { return imgui_ini_path_; }
|
||||
[[nodiscard]] const std::vector<std::string>& recentlyClosed() const { return recently_closed_; }
|
||||
[[nodiscard]] const std::vector<std::string>& openRepositories() const { return open_repositories_; }
|
||||
[[nodiscard]] size_t activeRepository() const { return active_repository_; }
|
||||
[[nodiscard]] float sidebarWidth() const { return sidebar_width_; }
|
||||
[[nodiscard]] float sidebarSectionHeight(size_t index) const { return sidebar_section_heights_.at(index); }
|
||||
[[nodiscard]] int pullMode() const { return pull_mode_; }
|
||||
@@ -21,6 +23,7 @@ public:
|
||||
void setSidebarSectionHeight(size_t index, float height) { sidebar_section_heights_.at(index) = height; }
|
||||
void setPullMode(int mode) { pull_mode_ = mode; save(); }
|
||||
void addRecentlyClosed(const std::string& path);
|
||||
void setRepositorySession(std::vector<std::string> paths, size_t active_repository);
|
||||
void save() const;
|
||||
|
||||
private:
|
||||
@@ -29,6 +32,8 @@ private:
|
||||
std::filesystem::path directory_;
|
||||
std::string imgui_ini_path_;
|
||||
std::vector<std::string> recently_closed_;
|
||||
std::vector<std::string> open_repositories_;
|
||||
size_t active_repository_ = 0;
|
||||
float sidebar_width_ = 230.0f;
|
||||
std::array<float, 4> sidebar_section_heights_ = {110.0f, 220.0f, 90.0f, 150.0f};
|
||||
int pull_mode_ = 1;
|
||||
|
||||
@@ -57,9 +57,18 @@ constexpr const char* ICON_TB_TAG = "\xee\xa4\x80";
|
||||
float ui(float value) { return value * g_ui_scale; }
|
||||
RepositoryView& repo() { return *g_tabs.at(g_active_tab); }
|
||||
|
||||
void create_new_tab() {
|
||||
void persist_repository_session() {
|
||||
if (!g_user_data || g_tabs.empty()) return;
|
||||
std::vector<std::string> paths;
|
||||
paths.reserve(g_tabs.size());
|
||||
for (const auto& tab : g_tabs) paths.push_back(tab->path);
|
||||
g_user_data->setRepositorySession(std::move(paths), g_active_tab);
|
||||
}
|
||||
|
||||
void create_new_tab(bool persist = true) {
|
||||
g_tabs.push_back(std::make_unique<RepositoryView>());
|
||||
g_active_tab = g_tabs.size() - 1;
|
||||
if (persist) persist_repository_session();
|
||||
}
|
||||
|
||||
void close_tab(size_t index) {
|
||||
@@ -69,10 +78,13 @@ void close_tab(size_t index) {
|
||||
if (g_tabs.empty()) create_new_tab();
|
||||
else if (g_active_tab >= g_tabs.size()) g_active_tab = g_tabs.size() - 1;
|
||||
else if (index < g_active_tab) --g_active_tab;
|
||||
persist_repository_session();
|
||||
}
|
||||
|
||||
bool open_repository(const char* path) {
|
||||
return g_git_manager->openRepository(repo(), path, g_notice);
|
||||
const bool opened = g_git_manager->openRepository(repo(), path, g_notice);
|
||||
if (opened) persist_repository_session();
|
||||
return opened;
|
||||
}
|
||||
|
||||
void pick_and_open_repository() {
|
||||
@@ -99,7 +111,9 @@ void pick_and_open_repository() {
|
||||
}
|
||||
|
||||
bool init_repository(const char* path) {
|
||||
return g_git_manager->initializeRepository(repo(), path, g_notice);
|
||||
const bool initialized = g_git_manager->initializeRepository(repo(), path, g_notice);
|
||||
if (initialized) persist_repository_session();
|
||||
return initialized;
|
||||
}
|
||||
|
||||
void apply_style(float scale) {
|
||||
@@ -932,16 +946,34 @@ void draw_app() {
|
||||
}
|
||||
|
||||
size_t tab_to_close = g_tabs.size();
|
||||
size_t tab_move_from = g_tabs.size();
|
||||
size_t tab_move_to = g_tabs.size();
|
||||
bool add_tab = false;
|
||||
if (ImGui::BeginTabBar("repositories", ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable)) {
|
||||
if (ImGui::BeginTabBar("repositories", ImGuiTabBarFlags_AutoSelectNewTabs)) {
|
||||
for (size_t i = 0; i < g_tabs.size(); ++i) {
|
||||
bool open = true;
|
||||
const std::string label = g_tabs[i]->name + "###repo_tab_" + std::to_string(i);
|
||||
const ImGuiTabItemFlags flags = i == g_active_tab ? ImGuiTabItemFlags_SetSelected : ImGuiTabItemFlags_None;
|
||||
const ImGuiTabItemFlags flags = (i == g_active_tab ? ImGuiTabItemFlags_SetSelected : ImGuiTabItemFlags_None) |
|
||||
ImGuiTabItemFlags_NoReorder;
|
||||
if (ImGui::BeginTabItem(label.c_str(), &open, flags)) {
|
||||
g_active_tab = i;
|
||||
if (g_active_tab != i) {
|
||||
g_active_tab = i;
|
||||
persist_repository_session();
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) {
|
||||
ImGui::SetDragDropPayload("GITREE_REPOSITORY_TAB", &i, sizeof(i));
|
||||
ImGui::TextUnformatted(g_tabs[i]->name.c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("GITREE_REPOSITORY_TAB")) {
|
||||
tab_move_from = *static_cast<const size_t*>(payload->Data);
|
||||
tab_move_to = i;
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
if (ImGui::BeginPopupContextItem()) {
|
||||
if (!g_tabs[i]->path.empty() && ImGui::MenuItem(ICON_FA_FOLDER_OPEN " Reveal in file manager")) {
|
||||
std::string error;
|
||||
@@ -959,6 +991,17 @@ void draw_app() {
|
||||
if (ImGui::SmallButton(ICON_FA_PLUS "##new_tab")) add_tab = true;
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
if (tab_move_from < g_tabs.size() && tab_move_to < g_tabs.size() && tab_move_from != tab_move_to) {
|
||||
RepositoryView* active = g_tabs[g_active_tab].get();
|
||||
auto moved = std::move(g_tabs[tab_move_from]);
|
||||
g_tabs.erase(g_tabs.begin() + static_cast<std::ptrdiff_t>(tab_move_from));
|
||||
g_tabs.insert(g_tabs.begin() + static_cast<std::ptrdiff_t>(tab_move_to), std::move(moved));
|
||||
const auto active_tab = std::find_if(g_tabs.begin(), g_tabs.end(), [active](const auto& tab) {
|
||||
return tab.get() == active;
|
||||
});
|
||||
g_active_tab = static_cast<size_t>(std::distance(g_tabs.begin(), active_tab));
|
||||
persist_repository_session();
|
||||
}
|
||||
if (tab_to_close < g_tabs.size()) close_tab(tab_to_close);
|
||||
if (add_tab) create_new_tab();
|
||||
ImGui::Separator();
|
||||
@@ -1060,7 +1103,19 @@ int runGitree(int argc, char** argv) {
|
||||
UserData user_data;
|
||||
g_user_data = &user_data;
|
||||
g_sidebar_width = user_data.sidebarWidth();
|
||||
create_new_tab();
|
||||
|
||||
if (argc > 1) {
|
||||
create_new_tab(false);
|
||||
open_repository(argv[1]);
|
||||
} else if (!user_data.openRepositories().empty()) {
|
||||
for (const std::string& path : user_data.openRepositories()) {
|
||||
g_tabs.push_back(std::make_unique<RepositoryView>());
|
||||
if (!path.empty()) git_manager.openRepository(*g_tabs.back(), path, g_notice);
|
||||
}
|
||||
g_active_tab = std::min(user_data.activeRepository(), g_tabs.size() - 1);
|
||||
} else {
|
||||
create_new_tab(false);
|
||||
}
|
||||
|
||||
WindowManager window_manager;
|
||||
if (!window_manager.create("Gitree", 1500, 900)) return 1;
|
||||
@@ -1076,8 +1131,6 @@ int runGitree(int argc, char** argv) {
|
||||
ImGui_ImplGlfw_InitForOpenGL(window_manager.nativeWindow(), true);
|
||||
ImGui_ImplOpenGL3_Init("#version 330");
|
||||
|
||||
if (argc > 1) open_repository(argv[1]);
|
||||
|
||||
while (!window_manager.shouldClose()) {
|
||||
window_manager.pollEvents();
|
||||
if (window_manager.consumeDpiChange()) load_fonts(window_manager.dpiScale());
|
||||
@@ -1101,6 +1154,7 @@ int runGitree(int argc, char** argv) {
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
user_data.setSidebarWidth(g_sidebar_width);
|
||||
persist_repository_session();
|
||||
user_data.save();
|
||||
ImGui::DestroyContext();
|
||||
g_window_manager = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user