From 7e7bfdac50591ca7b895715df303056f319e94c2 Mon Sep 17 00:00:00 2001 From: Marco Cutecchia Date: Mon, 14 Feb 2022 14:08:24 +0100 Subject: [PATCH] HackStudio: Add a 'Recent Projects' submenu in 'File' --- .../DevTools/HackStudio/HackStudioWidget.cpp | 57 +++++++++++++++++++ .../DevTools/HackStudio/HackStudioWidget.h | 6 ++ 2 files changed, 63 insertions(+) diff --git a/Userland/DevTools/HackStudio/HackStudioWidget.cpp b/Userland/DevTools/HackStudio/HackStudioWidget.cpp index e3e658c1d9..ec6ad5bd52 100644 --- a/Userland/DevTools/HackStudio/HackStudioWidget.cpp +++ b/Userland/DevTools/HackStudio/HackStudioWidget.cpp @@ -23,6 +23,7 @@ #include "ProjectDeclarations.h" #include "TerminalWrapper.h" #include "ToDoEntries.h" +#include #include #include #include @@ -192,6 +193,28 @@ void HackStudioWidget::on_action_tab_change() } } +Vector HackStudioWidget::read_recent_projects() +{ + auto json = Config::read_string("HackStudio", "Global", "RecentProjects"); + AK::JsonParser parser(json); + auto value_or_error = parser.parse(); + if (value_or_error.is_error()) + return {}; + + auto value = value_or_error.release_value(); + if (!value.is_array()) + return {}; + + Vector paths; + for (auto& json_value : value.as_array().values()) { + if (!json_value.is_string()) + return {}; + paths.append(json_value.as_string()); + } + + return paths; +} + void HackStudioWidget::open_project(const String& root_path) { if (warn_unsaved_changes("There are unsaved changes, do you want to save before closing current project?") == ContinueDecision::No) @@ -229,6 +252,15 @@ void HackStudioWidget::open_project(const String& root_path) LexicalPath::relative_path(absolute_old_path, m_project->root_path()), LexicalPath::relative_path(absolute_new_path, m_project->root_path())); }; + + auto recent_projects = read_recent_projects(); + recent_projects.remove_all_matching([&](auto& p) { return p == root_path; }); + recent_projects.insert(0, root_path); + if (recent_projects.size() > recent_projects_history_size) + recent_projects.shrink(recent_projects_history_size); + + Config::write_string("HackStudio", "Global", "RecentProjects", JsonArray(recent_projects).to_string()); + update_recent_projects_submenu(); } Vector HackStudioWidget::selected_file_paths() const @@ -1142,11 +1174,36 @@ void HackStudioWidget::create_project_tab(GUI::Widget& parent) }; } +void HackStudioWidget::update_recent_projects_submenu() +{ + if (!m_recent_projects_submenu) + return; + + m_recent_projects_submenu->remove_all_actions(); + auto recent_projects = read_recent_projects(); + + if (recent_projects.size() <= 1) { + auto empty_action = GUI::Action::create("Empty...", [](auto&) {}); + empty_action->set_enabled(false); + m_recent_projects_submenu->add_action(empty_action); + return; + } + + for (size_t i = 1; i < recent_projects.size(); i++) { + auto project_path = recent_projects[i]; + m_recent_projects_submenu->add_action(GUI::Action::create(recent_projects[i], [this, project_path](auto&) { + open_project(project_path); + })); + } +} + void HackStudioWidget::create_file_menu(GUI::Window& window) { auto& file_menu = window.add_menu("&File"); file_menu.add_action(*m_new_project_action); file_menu.add_action(*m_open_action); + m_recent_projects_submenu = &file_menu.add_submenu("Open Recent"); + update_recent_projects_submenu(); file_menu.add_action(*m_save_action); file_menu.add_action(*m_save_as_action); file_menu.add_separator(); diff --git a/Userland/DevTools/HackStudio/HackStudioWidget.h b/Userland/DevTools/HackStudio/HackStudioWidget.h index 0c2550f1db..de64454a51 100644 --- a/Userland/DevTools/HackStudio/HackStudioWidget.h +++ b/Userland/DevTools/HackStudio/HackStudioWidget.h @@ -73,7 +73,11 @@ public: void for_each_open_file(Function); bool semantic_syntax_highlighting_is_enabled() const; + static Vector read_recent_projects(); + private: + static constexpr size_t recent_projects_history_size = 15; + static String get_full_path_of_serenity_source(const String& file); String get_absolute_path(String const&) const; Vector selected_file_paths() const; @@ -128,6 +132,7 @@ private: void create_toolbar(GUI::Widget& parent); void create_action_tab(GUI::Widget& parent); void create_file_menu(GUI::Window&); + void update_recent_projects_submenu(); void create_project_menu(GUI::Window&); void create_edit_menu(GUI::Window&); void create_build_menu(GUI::Window&); @@ -193,6 +198,7 @@ private: RefPtr m_disassembly_widget; RefPtr m_debugger_thread; RefPtr m_current_editor_in_execution; + RefPtr m_recent_projects_submenu { nullptr }; NonnullRefPtrVector m_new_file_actions; RefPtr m_new_plain_file_action;