Selaa lähdekoodia

HackStudio: Add FindWidget

The find widget appears on Ctrl+F.

It uses the GUI::TextEditor search API to search for text, which also
takes care of highlighting the search results.
Itamar 3 vuotta sitten
vanhempi
commit
d9d299f884

+ 3 - 0
Userland/DevTools/HackStudio/CMakeLists.txt

@@ -10,6 +10,7 @@ add_subdirectory(LanguageClients)
 
 compile_gml(Dialogs/NewProjectDialog.gml Dialogs/NewProjectDialogGML.h new_project_dialog_gml)
 compile_gml(Dialogs/Git/GitCommitDialog.gml Dialogs/Git/GitCommitDialogGML.h git_commit_dialog_gml)
+compile_gml(FindWidget.gml FindWidgetGML.h find_widget_gml)
 
 set(SOURCES
     CodeDocument.cpp
@@ -32,6 +33,8 @@ set(SOURCES
     Editor.cpp
     EditorWrapper.cpp
     FindInFilesWidget.cpp
+    FindWidget.cpp
+    FindWidgetGML.h
     Git/DiffViewer.cpp
     Git/GitFilesModel.cpp
     Git/GitFilesView.cpp

+ 12 - 2
Userland/DevTools/HackStudio/EditorWrapper.cpp

@@ -20,11 +20,13 @@ namespace HackStudio {
 EditorWrapper::EditorWrapper()
 {
     set_layout<GUI::VerticalBoxLayout>();
-
     m_filename_title = untitled_label;
 
     // FIXME: Propagate errors instead of giving up
-    m_editor = MUST(try_add<Editor>());
+    m_editor = MUST(Editor::try_create());
+    m_find_widget = add<FindWidget>(*m_editor);
+
+    add_child(*m_editor);
     m_editor->set_ruler_visible(true);
     m_editor->set_automatic_indentation_enabled(true);
 
@@ -115,4 +117,12 @@ void EditorWrapper::set_debug_mode(bool enabled)
     m_editor->set_debug_mode(enabled);
 }
 
+void EditorWrapper::search_action()
+{
+    if (m_find_widget->visible())
+        m_find_widget->hide();
+    else
+        m_find_widget->show();
+}
+
 }

+ 5 - 0
Userland/DevTools/HackStudio/EditorWrapper.h

@@ -8,6 +8,7 @@
 #pragma once
 
 #include "Debugger/BreakpointCallback.h"
+#include "FindWidget.h"
 #include "Git/GitRepo.h"
 #include "LanguageClient.h"
 #include <AK/Function.h>
@@ -52,6 +53,9 @@ public:
     Function<void()> on_change;
     Function<void(EditorWrapper&)> on_tab_close_request;
 
+    void search_action();
+    FindWidget const& find_widget() const { return *m_find_widget; }
+
 private:
     static constexpr auto untitled_label = "(Untitled)";
 
@@ -62,6 +66,7 @@ private:
     String m_filename;
     String m_filename_title;
     RefPtr<Editor> m_editor;
+    RefPtr<FindWidget> m_find_widget;
 
     Optional<String> m_project_root;
     RefPtr<GitRepo> m_git_repo;

+ 83 - 0
Userland/DevTools/HackStudio/FindWidget.cpp

@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2022, Itamar S. <itamar8910@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "FindWidget.h"
+#include "Editor.h"
+#include <AK/QuickSort.h>
+#include <DevTools/HackStudio/FindWidgetGML.h>
+#include <LibGUI/BoxLayout.h>
+#include <LibGfx/Palette.h>
+
+namespace HackStudio {
+
+FindWidget::FindWidget(NonnullRefPtr<Editor> editor)
+    : m_editor(move(editor))
+{
+    load_from_gml(find_widget_gml);
+    set_fixed_height(widget_height);
+    m_input_field = find_descendant_of_type_named<GUI::TextBox>("input_field");
+    m_index_label = find_descendant_of_type_named<GUI::Label>("index_label");
+    m_next = find_descendant_of_type_named<GUI::Button>("next");
+    m_previous = find_descendant_of_type_named<GUI::Button>("previous");
+
+    VERIFY(m_input_field);
+    VERIFY(m_next);
+    VERIFY(m_previous);
+
+    m_next->on_click = [this](auto) {
+        find_next(GUI::TextEditor::SearchDirection::Forward);
+    };
+    m_previous->on_click = [this](auto) {
+        find_next(GUI::TextEditor::SearchDirection::Backward);
+    };
+
+    m_input_field->on_change = [this]() {
+        m_editor->reset_search_results();
+        find_next(GUI::TextEditor::SearchDirection::Forward);
+    };
+
+    m_input_field->on_return_pressed = [this]() {
+        find_next(GUI::TextEditor::SearchDirection::Forward);
+    };
+}
+
+void FindWidget::show()
+{
+    set_visible(true);
+    set_focus(true);
+    m_input_field->set_focus(true);
+    // Adjust scroll value to smooth the appearance of the FindWidget.
+    m_editor->vertical_scrollbar().set_value(m_editor->vertical_scrollbar().value() + widget_height, GUI::AllowCallback::Yes, GUI::Scrollbar::DoClamp::No);
+    m_visible = !m_visible;
+}
+
+void FindWidget::hide()
+{
+    set_visible(false);
+    set_focus(false);
+    m_visible = !m_visible;
+    m_editor->vertical_scrollbar().set_value(m_editor->vertical_scrollbar().value() - widget_height, GUI::AllowCallback::Yes, GUI::Scrollbar::DoClamp::No);
+    m_editor->set_focus(true);
+    m_editor->reset_search_results();
+}
+
+void FindWidget::find_next(GUI::TextEditor::SearchDirection direction)
+{
+    auto needle = m_input_field->text();
+    if (needle.is_empty()) {
+        m_editor->reset_search_results();
+        m_index_label->set_text(String::empty());
+        return;
+    }
+    auto result = m_editor->find_text(needle, direction, GUI::TextDocument::SearchShouldWrap::Yes, false, false);
+
+    if (result.is_valid())
+        m_index_label->set_text(String::formatted("{}/{}", m_editor->search_result_index().value_or(0) + 1, m_editor->search_results().size()));
+    else
+        m_index_label->set_text(String::empty());
+}
+
+}

+ 32 - 0
Userland/DevTools/HackStudio/FindWidget.gml

@@ -0,0 +1,32 @@
+@GUI::Widget {
+    name: "find_widget"
+    fill_with_background_color: true
+    shrink_to_fit: true
+    visible: false
+    layout: @GUI::HorizontalBoxLayout {
+        margins: [0, 0]
+    }
+
+    @GUI::TextBox {
+        name: "input_field"
+        max_width: 250
+    }
+
+    @GUI::Label {
+        name: "index_label"
+        max_width: 30
+        text: ""
+    }
+
+    @GUI::Button {
+        name: "next"
+        icon: "/res/icons/16x16/go-down.png"
+        max_width: 15
+    }
+
+    @GUI::Button {
+        name: "previous"
+        icon: "/res/icons/16x16/go-up.png"
+        max_width: 15
+    }
+}

+ 44 - 0
Userland/DevTools/HackStudio/FindWidget.h

@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2022, Itamar S. <itamar8910@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include "Editor.h"
+#include <LibGUI/Button.h>
+#include <LibGUI/TextBox.h>
+#include <LibGUI/Widget.h>
+#include <LibGUI/Window.h>
+
+namespace HackStudio {
+
+class Editor;
+class EditorWrapper;
+
+class FindWidget final : public GUI::Widget {
+    C_OBJECT(FindWidget)
+public:
+    ~FindWidget() = default;
+
+    void show();
+    void hide();
+    bool visible() const { return m_visible; }
+
+private:
+    FindWidget(NonnullRefPtr<Editor>);
+
+    void find_next(GUI::TextEditor::SearchDirection);
+
+    static constexpr auto widget_height = 25;
+
+    NonnullRefPtr<Editor> m_editor;
+    RefPtr<GUI::TextBox> m_input_field;
+    RefPtr<GUI::Label> m_index_label;
+    RefPtr<GUI::Button> m_next;
+    RefPtr<GUI::Button> m_previous;
+    bool m_visible { false };
+};
+
+}

+ 6 - 1
Userland/DevTools/HackStudio/HackStudioWidget.cpp

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
- * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
+ * Copyright (c) 2020-2022, Itamar S. <itamar8910@gmail.com>
  * Copyright (c) 2020-2022, the SerenityOS developers.
  *
  * SPDX-License-Identifier: BSD-2-Clause
@@ -1445,6 +1445,11 @@ void HackStudioWidget::create_view_menu(GUI::Window& window)
     create_location_history_actions();
     view_menu.add_action(*m_locations_history_back_action);
     view_menu.add_action(*m_locations_history_forward_action);
+
+    auto search_action = GUI::Action::create("&Search", { Mod_Ctrl, Key_F }, [this](auto&) {
+        current_editor_wrapper().search_action();
+    });
+    view_menu.add_action(search_action);
 }
 
 void HackStudioWidget::create_help_menu(GUI::Window& window)

+ 1 - 1
Userland/DevTools/HackStudio/HackStudioWidget.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
- * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
+ * Copyright (c) 2020-2022, Itamar S. <itamar8910@gmail.com>
  * Copyright (c) 2020-2021, the SerenityOS developers.
  *
  * SPDX-License-Identifier: BSD-2-Clause