diff --git a/run.bat b/run.bat index f9882fe..98a7349 100644 --- a/run.bat +++ b/run.bat @@ -14,7 +14,9 @@ if %errorlevel%==0 ( set "GENERATOR=" ) -cmake -S . -B build %GENERATOR% -DCMAKE_BUILD_TYPE=Release || exit /b 1 +if not exist build\CMakeCache.txt ( + cmake -S . -B build %GENERATOR% -DCMAKE_BUILD_TYPE=Release || exit /b 1 +) cmake --build build --config Release --parallel || exit /b 1 if exist build\bin\gitree.exe ( diff --git a/src/managers/git_manager.cpp b/src/managers/git_manager.cpp index 6637ca8..01c7e04 100644 --- a/src/managers/git_manager.cpp +++ b/src/managers/git_manager.cpp @@ -148,12 +148,14 @@ namespace std::vector withAuthOverrideArguments(std::vector arguments, const std::optional &auth) { + arguments.insert(arguments.begin(), { + "-c", "credential.interactive=never", + }); if (!auth || auth->username.empty()) return arguments; const std::string header = "http.extraHeader=Authorization: Basic " + encodeBase64(auth->username + ":" + auth->password); - arguments.insert(arguments.begin(), { - "-c", "credential.interactive=never", + arguments.insert(arguments.begin() + 2, { "-c", header, }); return arguments; diff --git a/src/managers/user_data.cpp b/src/managers/user_data.cpp index 7e55af5..af006e2 100644 --- a/src/managers/user_data.cpp +++ b/src/managers/user_data.cpp @@ -118,6 +118,24 @@ namespace return plain; } #endif + + std::string normalizePath(const std::string &path) + { + if (path.empty()) + return {}; + try + { + std::filesystem::path p(path); + std::string result = p.lexically_normal().generic_string(); + if (result.size() > 1 && result.back() == '/') + result.pop_back(); + return result; + } + catch (...) + { + return path; + } + } } UserData::UserData() @@ -200,7 +218,11 @@ void UserData::load() { const char *path = ikv_as_string(ikv_array_get(history, index)); if (path && *path) - recently_closed_.emplace_back(path); + { + std::string normalized = normalizePath(path); + if (std::find(recently_closed_.begin(), recently_closed_.end(), normalized) == recently_closed_.end()) + recently_closed_.emplace_back(std::move(normalized)); + } } } if (const ikv_node_t *recent = object_value(root, "recent_repositories", IKV_ARRAY)) @@ -210,7 +232,11 @@ void UserData::load() { const char *path = ikv_as_string(ikv_array_get(recent, index)); if (path && *path) - recent_repositories_.emplace_back(path); + { + std::string normalized = normalizePath(path); + if (std::find(recent_repositories_.begin(), recent_repositories_.end(), normalized) == recent_repositories_.end()) + recent_repositories_.emplace_back(std::move(normalized)); + } } } if (const ikv_node_t *session = object_value(root, "session", IKV_OBJECT)) @@ -304,7 +330,11 @@ void UserData::load() { const char *path = ikv_as_string(ikv_array_get(history, index)); if (path && *path) - recently_closed_.emplace_back(path); + { + std::string normalized = normalizePath(path); + if (std::find(recently_closed_.begin(), recently_closed_.end(), normalized) == recently_closed_.end()) + recently_closed_.emplace_back(std::move(normalized)); + } } } if (const ikv_node_t *recent = object_value(root, "recent_repositories", IKV_ARRAY)) @@ -314,7 +344,11 @@ void UserData::load() { const char *path = ikv_as_string(ikv_array_get(recent, index)); if (path && *path) - recent_repositories_.emplace_back(path); + { + std::string normalized = normalizePath(path); + if (std::find(recent_repositories_.begin(), recent_repositories_.end(), normalized) == recent_repositories_.end()) + recent_repositories_.emplace_back(std::move(normalized)); + } } } if (const ikv_node_t *session = object_value(root, "session", IKV_OBJECT)) @@ -399,7 +433,11 @@ void UserData::load() while (history >> std::quoted(path)) { if (!path.empty()) - recently_closed_.push_back(path); + { + std::string normalized = normalizePath(path); + if (std::find(recently_closed_.begin(), recently_closed_.end(), normalized) == recently_closed_.end()) + recently_closed_.push_back(std::move(normalized)); + } if (recently_closed_.size() == 100) break; } @@ -408,7 +446,10 @@ void UserData::load() std::ifstream session(directory_ / "session.txt"); session >> active_repository_; while (session >> std::quoted(path)) - open_repositories_.push_back(path); + { + if (!path.empty()) + open_repositories_.push_back(normalizePath(path)); + } if (open_repositories_.empty()) active_repository_ = 0; else @@ -438,8 +479,9 @@ void UserData::addRecentRepository(const std::string &path) { if (path.empty()) return; - std::erase(recent_repositories_, path); - recent_repositories_.insert(recent_repositories_.begin(), path); + const std::string normalized = normalizePath(path); + std::erase(recent_repositories_, normalized); + recent_repositories_.insert(recent_repositories_.begin(), normalized); if (recent_repositories_.size() > 100) recent_repositories_.resize(100); save(); @@ -449,12 +491,13 @@ void UserData::addRecentlyClosed(const std::string &path) { if (path.empty()) return; - std::erase(recent_repositories_, path); - recent_repositories_.insert(recent_repositories_.begin(), path); + const std::string normalized = normalizePath(path); + std::erase(recent_repositories_, normalized); + recent_repositories_.insert(recent_repositories_.begin(), normalized); if (recent_repositories_.size() > 100) recent_repositories_.resize(100); - std::erase(recently_closed_, path); - recently_closed_.insert(recently_closed_.begin(), path); + std::erase(recently_closed_, normalized); + recently_closed_.insert(recently_closed_.begin(), normalized); if (recently_closed_.size() > 100) recently_closed_.resize(100); save(); @@ -472,6 +515,8 @@ std::string UserData::takeRecentlyClosed() void UserData::setRepositorySession(std::vector paths, size_t active_repository) { + for (auto &path : paths) + path = normalizePath(path); open_repositories_ = std::move(paths); active_repository_ = open_repositories_.empty() ? 0 diff --git a/src/ui/gitree_ui.cpp b/src/ui/gitree_ui.cpp index da01862..c4f95e7 100644 --- a/src/ui/gitree_ui.cpp +++ b/src/ui/gitree_ui.cpp @@ -3759,7 +3759,9 @@ void draw_footer() { void draw_new_tab() { ImGui::BeginChild("new_tab", {-1, -ui(28.0f)}, ImGuiChildFlags_None); - const float margin = ui(84.0f); + const float margin = ui(48.0f); + const float content_width = std::min(ui(850.0f), ImGui::GetContentRegionAvail().x - margin * 2.0f); + ImGui::SetCursorPos({margin, ui(28.0f)}); ImGui::PushFont(g_bold_font, 0.0f); ImGui::SetWindowFontScale(1.25f); @@ -3767,20 +3769,20 @@ void draw_new_tab() { ImGui::SetWindowFontScale(1.0f); ImGui::PopFont(); ImGui::SetCursorPosX(margin); - if (ImGui::Button(ICON_TB_FOLDER_OPEN " Open", {ui(92.0f), ui(36.0f)})) + if (ImGui::Button(ICON_TB_FOLDER_OPEN " Open", {ui(112.0f), ui(40.0f)})) pick_and_open_repository(); - ImGui::SameLine(); - if (ImGui::Button(ICON_TB_DOWNLOAD " Clone", {ui(96.0f), ui(36.0f)})) + ImGui::SameLine(0, ui(8.0f)); + if (ImGui::Button(ICON_TB_DOWNLOAD " Clone", {ui(116.0f), ui(40.0f)})) g_clone_popup = true; - ImGui::SameLine(); - if (ImGui::Button(ICON_TB_PLUS " Create", {ui(100.0f), ui(36.0f)})) + ImGui::SameLine(0, ui(8.0f)); + if (ImGui::Button(ICON_TB_PLUS " Create", {ui(120.0f), ui(40.0f)})) g_init_popup = true; ImGui::SetCursorPosX(margin); - ImGui::SetNextItemWidth(std::max(ui(280.0f), ImGui::GetContentRegionAvail().x - margin)); + ImGui::SetNextItemWidth(content_width); ImGui::InputTextWithHint("##repository_filter", ICON_TB_MAGNIFYING_GLASS " Search repositories", g_repository_filter.data(), g_repository_filter.size()); - ImGui::Dummy({0, ui(7.0f)}); + ImGui::Dummy({0, ui(10.0f)}); ImGui::SetCursorPosX(margin); ImGui::PushFont(g_bold_font, 0.0f); ImGui::TextUnformatted("Recent"); @@ -3816,7 +3818,7 @@ void draw_new_tab() { for (const std::string& path : discovered_repositories) add_repository(path); ImGui::SetCursorPosX(margin); - ImGui::BeginChild("repository_list", {-margin, -ui(8.0f)}, ImGuiChildFlags_None); + ImGui::BeginChild("repository_list", {content_width, -ui(8.0f)}, ImGuiChildFlags_None); int visible_index = 0; for (const std::string& repository_path : repositories) { const std::filesystem::path path(repository_path); @@ -3828,16 +3830,17 @@ void draw_new_tab() { continue; ImGui::PushID(visible_index++); const bool clicked = ImGui::Selectable("##repository", false, - ImGuiSelectableFlags_None, {0, ui(22.0f)}); + ImGuiSelectableFlags_None, {0, ui(28.0f)}); const ImVec2 minimum = ImGui::GetItemRectMin(); ImDrawList* draw = ImGui::GetWindowDrawList(); const float row_font_size = ImGui::GetFontSize(); + const float text_y_offset = (ui(28.0f) - row_font_size) * 0.5f; draw->AddText(g_bold_font, row_font_size, - {minimum.x + ui(3.0f), minimum.y + ui(2.0f)}, + {minimum.x + ui(8.0f), minimum.y + text_y_offset}, IM_COL32(45, 192, 238, 255), name.c_str()); const float name_width = g_bold_font->CalcTextSizeA( row_font_size, std::numeric_limits::max(), 0.0f, name.c_str()).x; - draw->AddText({minimum.x + name_width + ui(14.0f), minimum.y + ui(2.0f)}, + draw->AddText({minimum.x + ui(8.0f) + name_width + ui(14.0f), minimum.y + text_y_offset}, IM_COL32(137, 143, 154, 255), repository_path.c_str()); if (clicked) open_repository(repository_path.c_str()); if (ImGui::BeginPopupContextItem()) { @@ -3858,6 +3861,7 @@ void draw_new_tab() { ImGui::EndChild(); } + void draw_app() { ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->WorkPos);