feat(session): restore repository tabs and selection

This commit is contained in:
2026-06-18 18:55:34 -05:00
parent 6f27fac9fb
commit cda8f9ae37
3 changed files with 87 additions and 9 deletions

View File

@@ -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';
}

View File

@@ -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;

View File

@@ -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;