Browse Source

TextEditor: Replaced 'Find' button with 'Prev' and 'Next' buttons.

Andrew Weller 5 years ago
parent
commit
e75e33eb46

+ 32 - 7
Applications/TextEditor/TextEditorWidget.cpp

@@ -34,14 +34,39 @@ TextEditorWidget::TextEditorWidget()
 
     m_find_textbox = new GTextBox(m_find_widget);
 
-    m_find_button = new GButton("Find", m_find_widget);
-    m_find_button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill);
-    m_find_button->set_preferred_size(100, 0);
+    m_find_prev_button = new GButton("Prev", m_find_widget);
+    m_find_prev_button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill);
+    m_find_prev_button->set_preferred_size(50, 0);
 
-    m_find_button->on_click = [this](auto&) {
+    m_find_next_button = new GButton("Next", m_find_widget);
+    m_find_next_button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill);
+    m_find_next_button->set_preferred_size(50, 0);
+
+    m_find_next_button->on_click = [this](auto&) {
         auto needle = m_find_textbox->text();
-        auto found_range = m_editor->find(needle, m_editor->normalized_selection().end());
-        dbg() << "find(\"" << needle << "\") returned " << found_range;
+        auto found_range = m_editor->find_next(needle, m_editor->normalized_selection().end());
+        dbg() << "find_next(\"" << needle << "\") returned " << found_range;
+        if (found_range.is_valid()) {
+            m_editor->set_selection(found_range);
+        } else {
+            GMessageBox::show(
+                String::format("Not found: \"%s\"", needle.characters()),
+                "Not found",
+                GMessageBox::Type::Information,
+                GMessageBox::InputType::OK, window());
+        }
+    };
+
+    m_find_prev_button->on_click = [this](auto&) {
+        auto needle = m_find_textbox->text();
+
+        auto selection_start = m_editor->normalized_selection().start();
+        if (!selection_start.is_valid())
+            selection_start = m_editor->normalized_selection().end();
+
+        auto found_range = m_editor->find_prev(needle, selection_start);
+        
+        dbg() << "find_prev(\"" << needle << "\") returned " << found_range;
         if (found_range.is_valid()) {
             m_editor->set_selection(found_range);
         } else {
@@ -54,7 +79,7 @@ TextEditorWidget::TextEditorWidget()
     };
 
     m_find_textbox->on_return_pressed = [this] {
-        m_find_button->click();
+        m_find_next_button->click();
     };
 
     m_find_textbox->on_escape_pressed = [this] {

+ 2 - 1
Applications/TextEditor/TextEditorWidget.h

@@ -31,6 +31,7 @@ private:
     RefPtr<GAction> m_find_action;
 
     GTextBox* m_find_textbox { nullptr };
-    GButton* m_find_button { nullptr };
+    GButton* m_find_prev_button { nullptr };
+    GButton* m_find_next_button { nullptr };
     GWidget* m_find_widget { nullptr };
 };

+ 47 - 1
Libraries/LibGUI/GTextEditor.cpp

@@ -1105,7 +1105,23 @@ GTextPosition GTextEditor::next_position_after(const GTextPosition& position, Sh
     return { position.line(), position.column() + 1 };
 }
 
-GTextRange GTextEditor::find(const StringView& needle, const GTextPosition& start)
+GTextPosition GTextEditor::prev_position_before(const GTextPosition& position, ShouldWrapAtStartOfDocument should_wrap)
+{
+    if (position.column() == 0){
+        if (position.line() == 0) {
+            if (should_wrap == ShouldWrapAtStartOfDocument::Yes) {
+                auto& last_line = m_lines[line_count() - 1]; 
+                return { line_count() - 1, last_line.length() };
+            }
+            return {};
+        }
+        auto& prev_line = m_lines[position.line() - 1];
+        return { position.line() - 1, prev_line.length() };
+    }
+    return { position.line(), position.column() - 1 };
+}
+
+GTextRange GTextEditor::find_next(const StringView& needle, const GTextPosition& start)
 {
     if (needle.is_empty())
         return {};
@@ -1135,6 +1151,36 @@ GTextRange GTextEditor::find(const StringView& needle, const GTextPosition& star
     return {};
 }
 
+GTextRange GTextEditor::find_prev(const StringView& needle, const GTextPosition& start)
+{
+    if (needle.is_empty())
+        return {};
+
+    GTextPosition position = start.is_valid() ? start : GTextPosition(0, 0);
+    GTextPosition original_position = position;
+
+    GTextPosition end_of_potential_match;
+    int needle_index = needle.length() - 1;
+
+    do {
+        auto ch = character_at(position);
+        if (ch == needle[needle_index]) {
+            if (needle_index == needle.length() - 1)
+                end_of_potential_match = position;
+            --needle_index;
+            if (needle_index < 0)
+                return { position, next_position_after(end_of_potential_match) };
+        } else {
+            if (needle_index < needle.length() - 1)
+                position = end_of_potential_match;
+            needle_index = needle.length() - 1;
+        }
+        position = prev_position_before(position);
+    } while(position.is_valid() && position != original_position);
+
+    return {};
+}
+
 void GTextEditor::set_selection(const GTextRange& selection)
 {
     if (m_selection == selection)

+ 6 - 2
Libraries/LibGUI/GTextEditor.h

@@ -12,6 +12,7 @@ class GScrollBar;
 class Painter;
 
 enum class ShouldWrapAtEndOfDocument { No = 0, Yes };
+enum class ShouldWrapAtStartOfDocument { No = 0, Yes };
 
 class GTextPosition {
 public:
@@ -126,9 +127,12 @@ public:
 
     bool write_to_file(const StringView& path);
 
-    GTextRange find(const StringView&, const GTextPosition& start = {});
-    GTextPosition next_position_after(const GTextPosition&, ShouldWrapAtEndOfDocument = ShouldWrapAtEndOfDocument::Yes);
+    GTextRange find_next(const StringView&, const GTextPosition& start = {});
+    GTextRange find_prev(const StringView&, const GTextPosition& start = {});
 
+    GTextPosition next_position_after(const GTextPosition&, ShouldWrapAtEndOfDocument = ShouldWrapAtEndOfDocument::Yes);
+    GTextPosition prev_position_before(const GTextPosition&, ShouldWrapAtStartOfDocument = ShouldWrapAtStartOfDocument::Yes);
+    
     bool has_selection() const { return m_selection.is_valid(); }
     String selected_text() const;
     void set_selection(const GTextRange&);