|
@@ -0,0 +1,136 @@
|
|
|
+/*
|
|
|
+ * Copyright (c) 2022, Itamar S. <itamar8910@gmail.com>
|
|
|
+ * Copyright (c) 2022, the SerenityOS developers.
|
|
|
+ *
|
|
|
+ * SPDX-License-Identifier: BSD-2-Clause
|
|
|
+ */
|
|
|
+
|
|
|
+#include <LibGUI/Button.h>
|
|
|
+#include <LibGUI/IncrementalSearchBanner.h>
|
|
|
+#include <LibGUI/IncrementalSearchBannerGML.h>
|
|
|
+#include <LibGUI/Label.h>
|
|
|
+#include <LibGUI/Layout.h>
|
|
|
+#include <LibGUI/Painter.h>
|
|
|
+#include <LibGUI/TextBox.h>
|
|
|
+#include <LibGfx/Palette.h>
|
|
|
+
|
|
|
+namespace GUI {
|
|
|
+
|
|
|
+IncrementalSearchBanner::IncrementalSearchBanner(TextEditor& editor)
|
|
|
+ : m_editor(editor)
|
|
|
+{
|
|
|
+ load_from_gml(incremental_search_banner_gml);
|
|
|
+ m_index_label = find_descendant_of_type_named<Label>("index_label");
|
|
|
+
|
|
|
+ m_wrap_search_button = find_descendant_of_type_named<Button>("wrap_search_button");
|
|
|
+ m_wrap_search_button->on_checked = [this](auto is_checked) {
|
|
|
+ m_wrap_search = is_checked
|
|
|
+ ? TextDocument::SearchShouldWrap::Yes
|
|
|
+ : TextDocument::SearchShouldWrap::No;
|
|
|
+ };
|
|
|
+
|
|
|
+ m_match_case_button = find_descendant_of_type_named<Button>("match_case_button");
|
|
|
+ m_match_case_button->on_checked = [this](auto is_checked) {
|
|
|
+ m_match_case = is_checked;
|
|
|
+ m_editor->reset_search_results();
|
|
|
+ search(TextEditor::SearchDirection::Forward);
|
|
|
+ };
|
|
|
+
|
|
|
+ m_close_button = find_descendant_of_type_named<Button>("close_button");
|
|
|
+ m_close_button->set_text("\xE2\x9D\x8C");
|
|
|
+ m_close_button->on_click = [this](auto) {
|
|
|
+ hide();
|
|
|
+ };
|
|
|
+
|
|
|
+ m_next_button = find_descendant_of_type_named<Button>("next_button");
|
|
|
+ m_next_button->on_click = [this](auto) {
|
|
|
+ search(TextEditor::SearchDirection::Forward);
|
|
|
+ };
|
|
|
+
|
|
|
+ m_previous_button = find_descendant_of_type_named<Button>("previous_button");
|
|
|
+ m_previous_button->on_click = [this](auto) {
|
|
|
+ search(TextEditor::SearchDirection::Backward);
|
|
|
+ };
|
|
|
+
|
|
|
+ m_search_textbox = find_descendant_of_type_named<TextBox>("search_textbox");
|
|
|
+ m_search_textbox->on_change = [this]() {
|
|
|
+ m_editor->reset_search_results();
|
|
|
+ search(TextEditor::SearchDirection::Forward);
|
|
|
+ };
|
|
|
+
|
|
|
+ m_search_textbox->on_return_pressed = [this]() {
|
|
|
+ search(TextEditor::SearchDirection::Forward);
|
|
|
+ };
|
|
|
+
|
|
|
+ m_search_textbox->on_shift_return_pressed = [this]() {
|
|
|
+ search(TextEditor::SearchDirection::Backward);
|
|
|
+ };
|
|
|
+
|
|
|
+ m_search_textbox->on_escape_pressed = [this]() {
|
|
|
+ hide();
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+void IncrementalSearchBanner::show()
|
|
|
+{
|
|
|
+ set_visible(true);
|
|
|
+ m_editor->do_layout();
|
|
|
+ m_editor->update_scrollbar_ranges();
|
|
|
+ m_search_textbox->set_focus(true);
|
|
|
+}
|
|
|
+
|
|
|
+void IncrementalSearchBanner::hide()
|
|
|
+{
|
|
|
+ set_visible(false);
|
|
|
+ m_editor->do_layout();
|
|
|
+ m_editor->update_scrollbar_ranges();
|
|
|
+ m_editor->reset_search_results();
|
|
|
+ m_editor->set_focus(true);
|
|
|
+}
|
|
|
+
|
|
|
+void IncrementalSearchBanner::search(TextEditor::SearchDirection direction)
|
|
|
+{
|
|
|
+ auto needle = m_search_textbox->text();
|
|
|
+ if (needle.is_empty()) {
|
|
|
+ m_editor->reset_search_results();
|
|
|
+ m_index_label->set_text(String::empty());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto index = m_editor->search_result_index().value_or(0) + 1;
|
|
|
+ if (m_wrap_search == TextDocument::SearchShouldWrap::No) {
|
|
|
+ auto forward = direction == TextEditor::SearchDirection::Forward;
|
|
|
+ if ((index == m_editor->search_results().size() && forward) || (index == 1 && !forward))
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto result = m_editor->find_text(needle, direction, m_wrap_search, false, m_match_case);
|
|
|
+ index = m_editor->search_result_index().value_or(0) + 1;
|
|
|
+ if (result.is_valid())
|
|
|
+ m_index_label->set_text(String::formatted("{} of {}", index, m_editor->search_results().size()));
|
|
|
+ else
|
|
|
+ m_index_label->set_text(String::empty());
|
|
|
+}
|
|
|
+
|
|
|
+void IncrementalSearchBanner::paint_event(PaintEvent& event)
|
|
|
+{
|
|
|
+ Widget::paint_event(event);
|
|
|
+
|
|
|
+ Painter painter(*this);
|
|
|
+ painter.add_clip_rect(event.rect());
|
|
|
+ painter.draw_line({ 0, rect().bottom() - 1 }, { width(), rect().bottom() - 1 }, palette().threed_shadow1());
|
|
|
+ painter.draw_line({ 0, rect().bottom() }, { width(), rect().bottom() }, palette().threed_shadow2());
|
|
|
+}
|
|
|
+
|
|
|
+Optional<UISize> IncrementalSearchBanner::calculated_min_size() const
|
|
|
+{
|
|
|
+ auto textbox_width = m_search_textbox->effective_min_size().width().as_int();
|
|
|
+ auto textbox_height = m_search_textbox->effective_min_size().height().as_int();
|
|
|
+ auto button_width = m_next_button->effective_min_size().width().as_int();
|
|
|
+ VERIFY(layout());
|
|
|
+ auto margins = layout()->margins();
|
|
|
+ auto spacing = layout()->spacing();
|
|
|
+ return { { margins.left() + textbox_width + spacing + button_width * 2 + margins.right(), textbox_height + margins.top() + margins.bottom() } };
|
|
|
+}
|
|
|
+
|
|
|
+}
|