Bläddra i källkod

HackStudio: Add "Go Back" and "Go Forward" navigation actions

These actions allow the user to move backwards & forwards between
previous project locations they were at.
Itamar 3 år sedan
förälder
incheckning
11832544e5

+ 102 - 4
Userland/DevTools/HackStudio/HackStudioWidget.cpp

@@ -215,6 +215,9 @@ void HackStudioWidget::open_project(const String& root_path)
     }
     for (auto& editor_wrapper : m_all_editor_wrappers)
         editor_wrapper.set_project_root(LexicalPath(m_project->root_path()));
+
+    m_locations_history.clear();
+    m_locations_history_end_index = 0;
 }
 
 Vector<String> HackStudioWidget::selected_file_paths() const
@@ -242,7 +245,6 @@ bool HackStudioWidget::open_file(const String& full_filename, size_t line, size_
     if (full_filename.starts_with(project().root_path())) {
         filename = LexicalPath::relative_path(full_filename, project().root_path());
     }
-    dbgln("HackStudio is opening {}", filename);
     if (Core::File::is_directory(filename) || !Core::File::exists(filename))
         return false;
 
@@ -270,10 +272,10 @@ bool HackStudioWidget::open_file(const String& full_filename, size_t line, size_
                 warnln("Couldn't watch '{}'", filename);
             }
         }
-
         m_open_files_view->model()->invalidate();
     }
 
+    current_editor().on_cursor_change = nullptr; // Disable callback while we're swapping the document.
     current_editor().set_document(const_cast<GUI::TextDocument&>(new_project_file->document()));
     if (new_project_file->could_render_text()) {
         current_editor_wrapper().set_mode_displayable();
@@ -296,7 +298,7 @@ bool HackStudioWidget::open_file(const String& full_filename, size_t line, size_
 
     current_editor().set_focus(true);
 
-    current_editor().on_cursor_change = [this] { update_statusbar(); };
+    current_editor().on_cursor_change = [this] { on_cursor_change(); };
     current_editor_wrapper().on_change = [this] { update_gml_preview(); };
     current_editor().set_cursor(line, column);
     update_gml_preview();
@@ -337,11 +339,22 @@ EditorWrapper& HackStudioWidget::current_editor_wrapper()
     return *m_current_editor_wrapper;
 }
 
+EditorWrapper const& HackStudioWidget::current_editor_wrapper() const
+{
+    VERIFY(m_current_editor_wrapper);
+    return *m_current_editor_wrapper;
+}
+
 GUI::TextEditor& HackStudioWidget::current_editor()
 {
     return current_editor_wrapper().editor();
 }
 
+GUI::TextEditor const& HackStudioWidget::current_editor() const
+{
+    return current_editor_wrapper().editor();
+}
+
 void HackStudioWidget::set_edit_mode(EditMode mode)
 {
     if (mode == EditMode::Text) {
@@ -565,7 +578,7 @@ void HackStudioWidget::add_new_editor(GUI::Widget& parent)
     m_all_editor_wrappers.append(wrapper);
     wrapper->editor().set_focus(true);
     wrapper->set_project_root(LexicalPath(m_project->root_path()));
-    wrapper->editor().on_cursor_change = [this] { update_statusbar(); };
+    wrapper->editor().on_cursor_change = [this] { on_cursor_change(); };
     wrapper->on_change = [this] { update_gml_preview(); };
     set_edit_mode(EditMode::Text);
 }
@@ -1140,6 +1153,12 @@ void HackStudioWidget::create_view_menu(GUI::Window& window)
     view_menu.add_action(*m_remove_current_editor_action);
     view_menu.add_action(*m_add_terminal_action);
     view_menu.add_action(*m_remove_current_terminal_action);
+
+    view_menu.add_separator();
+
+    create_location_history_actions();
+    view_menu.add_action(*m_locations_history_back_action);
+    view_menu.add_action(*m_locations_history_forward_action);
 }
 
 void HackStudioWidget::create_help_menu(GUI::Window& window)
@@ -1276,4 +1295,83 @@ void HackStudioWidget::update_window_title()
     window()->set_title(String::formatted("{} - {} - Hack Studio", m_current_editor_wrapper->filename_label().text(), m_project->name()));
 }
 
+void HackStudioWidget::on_cursor_change()
+{
+    update_statusbar();
+    if (current_editor_wrapper().filename().is_null())
+        return;
+
+    auto current_location = current_project_location();
+
+    if (m_locations_history_end_index != 0) {
+        auto last = m_locations_history[m_locations_history_end_index - 1];
+        if (current_location.filename == last.filename && current_location.line == last.line)
+            return;
+    }
+
+    // Clear "Go Forward" locations
+    VERIFY(m_locations_history_end_index <= m_locations_history.size());
+    m_locations_history.remove(m_locations_history_end_index, m_locations_history.size() - m_locations_history_end_index);
+
+    m_locations_history.append(current_location);
+
+    constexpr size_t max_locations = 30;
+    if (m_locations_history.size() > max_locations)
+        m_locations_history.take_first();
+
+    m_locations_history_end_index = m_locations_history.size();
+
+    update_history_actions();
+}
+
+void HackStudioWidget::create_location_history_actions()
+{
+    m_locations_history_back_action = GUI::Action::create("Go Back", { Mod_Alt | Mod_Shift, Key_Left }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/go-back.png"), [this](auto&) {
+        if (m_locations_history_end_index <= 1)
+            return;
+
+        auto location = m_locations_history[m_locations_history_end_index - 2];
+        --m_locations_history_end_index;
+
+        m_locations_history_disabled = true;
+        open_file(location.filename, location.line, location.column);
+        m_locations_history_disabled = false;
+
+        update_history_actions();
+    });
+
+    m_locations_history_forward_action = GUI::Action::create("Go Forward", { Mod_Alt | Mod_Shift, Key_Right }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/go-forward.png"), [this](auto&) {
+        if (m_locations_history_end_index == m_locations_history.size())
+            return;
+
+        auto location = m_locations_history[m_locations_history_end_index];
+        ++m_locations_history_end_index;
+
+        m_locations_history_disabled = true;
+        open_file(location.filename, location.line, location.column);
+        m_locations_history_disabled = false;
+
+        update_history_actions();
+    });
+    m_locations_history_forward_action->set_enabled(false);
+}
+
+HackStudioWidget::ProjectLocation HackStudioWidget::current_project_location() const
+{
+    return ProjectLocation { current_editor_wrapper().filename(), current_editor().cursor().line(), current_editor().cursor().column() };
+}
+
+void HackStudioWidget::update_history_actions()
+{
+    if (m_locations_history_end_index <= 1)
+        m_locations_history_back_action->set_enabled(false);
+    else
+        m_locations_history_back_action->set_enabled(true);
+
+    if (m_locations_history_end_index == m_locations_history.size())
+        m_locations_history_forward_action->set_enabled(false);
+    else
+        m_locations_history_forward_action->set_enabled(true);
+}
+
 }

+ 21 - 0
Userland/DevTools/HackStudio/HackStudioWidget.h

@@ -41,7 +41,9 @@ public:
     void update_actions();
     Project& project();
     GUI::TextEditor& current_editor();
+    GUI::TextEditor const& current_editor() const;
     EditorWrapper& current_editor_wrapper();
+    EditorWrapper const& current_editor_wrapper() const;
     void set_current_editor_wrapper(RefPtr<EditorWrapper>);
 
     const String& active_file() const { return m_current_editor_wrapper->filename(); }
@@ -93,6 +95,7 @@ private:
     NonnullRefPtr<GUI::Action> create_build_action();
     NonnullRefPtr<GUI::Action> create_run_action();
     NonnullRefPtr<GUI::Action> create_stop_action();
+    void create_location_history_actions();
 
     void add_new_editor(GUI::Widget& parent);
     RefPtr<EditorWrapper> get_editor_of_file(const String& filename);
@@ -128,6 +131,16 @@ private:
     void update_gml_preview();
     void update_tree_view();
     void update_window_title();
+    void on_cursor_change();
+
+    struct ProjectLocation {
+        String filename;
+        size_t line { 0 };
+        size_t column { 0 };
+    };
+
+    ProjectLocation current_project_location() const;
+    void update_history_actions();
 
     NonnullRefPtrVector<EditorWrapper> m_all_editor_wrappers;
     RefPtr<EditorWrapper> m_current_editor_wrapper;
@@ -138,6 +151,12 @@ private:
 
     OwnPtr<Project> m_project;
 
+    Vector<ProjectLocation> m_locations_history;
+    // This index is the boundary between the "Go Back" and "Go Forward" locations.
+    // It always points at one past the current location in the list.
+    size_t m_locations_history_end_index { 0 };
+    bool m_locations_history_disabled { false };
+
     RefPtr<GUI::TreeView> m_project_tree_view;
     RefPtr<GUI::ListView> m_open_files_view;
     RefPtr<GUI::VerticalSplitter> m_right_hand_splitter;
@@ -181,6 +200,8 @@ private:
     RefPtr<GUI::Action> m_debug_action;
     RefPtr<GUI::Action> m_build_action;
     RefPtr<GUI::Action> m_run_action;
+    RefPtr<GUI::Action> m_locations_history_back_action;
+    RefPtr<GUI::Action> m_locations_history_forward_action;
 
     GUI::ActionGroup m_wrapping_mode_actions;
     RefPtr<GUI::Action> m_no_wrapping_action;