Serenity Operating System
at master 136 lines 4.9 kB view raw
1/* 2 * Copyright (c) 2022, Itamar S. <itamar8910@gmail.com> 3 * Copyright (c) 2022, the SerenityOS developers. 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <LibGUI/Button.h> 9#include <LibGUI/IncrementalSearchBanner.h> 10#include <LibGUI/IncrementalSearchBannerGML.h> 11#include <LibGUI/Label.h> 12#include <LibGUI/Layout.h> 13#include <LibGUI/Painter.h> 14#include <LibGUI/TextBox.h> 15#include <LibGfx/Palette.h> 16 17namespace GUI { 18 19IncrementalSearchBanner::IncrementalSearchBanner(TextEditor& editor) 20 : m_editor(editor) 21{ 22 load_from_gml(incremental_search_banner_gml).release_value_but_fixme_should_propagate_errors(); 23 m_index_label = find_descendant_of_type_named<Label>("incremental_search_banner_index_label"); 24 25 m_wrap_search_button = find_descendant_of_type_named<Button>("incremental_search_banner_wrap_search_button"); 26 m_wrap_search_button->on_checked = [this](auto is_checked) { 27 m_wrap_search = is_checked 28 ? TextDocument::SearchShouldWrap::Yes 29 : TextDocument::SearchShouldWrap::No; 30 }; 31 32 m_match_case_button = find_descendant_of_type_named<Button>("incremental_search_banner_match_case_button"); 33 m_match_case_button->on_checked = [this](auto is_checked) { 34 m_match_case = is_checked; 35 m_editor->reset_search_results(); 36 search(TextEditor::SearchDirection::Forward); 37 }; 38 39 m_close_button = find_descendant_of_type_named<Button>("incremental_search_banner_close_button"); 40 m_close_button->set_text("\xE2\x9D\x8C"_short_string); 41 m_close_button->on_click = [this](auto) { 42 hide(); 43 }; 44 45 m_next_button = find_descendant_of_type_named<Button>("incremental_search_banner_next_button"); 46 m_next_button->on_click = [this](auto) { 47 search(TextEditor::SearchDirection::Forward); 48 }; 49 50 m_previous_button = find_descendant_of_type_named<Button>("incremental_search_banner_previous_button"); 51 m_previous_button->on_click = [this](auto) { 52 search(TextEditor::SearchDirection::Backward); 53 }; 54 55 m_search_textbox = find_descendant_of_type_named<TextBox>("incremental_search_banner_search_textbox"); 56 m_search_textbox->on_change = [this]() { 57 m_editor->reset_search_results(); 58 search(TextEditor::SearchDirection::Forward); 59 }; 60 61 m_search_textbox->on_return_pressed = [this]() { 62 search(TextEditor::SearchDirection::Forward); 63 }; 64 65 m_search_textbox->on_shift_return_pressed = [this]() { 66 search(TextEditor::SearchDirection::Backward); 67 }; 68 69 m_search_textbox->on_escape_pressed = [this]() { 70 hide(); 71 }; 72} 73 74void IncrementalSearchBanner::show() 75{ 76 set_visible(true); 77 m_editor->do_layout(); 78 m_editor->update_scrollbar_ranges(); 79 m_search_textbox->set_focus(true); 80} 81 82void IncrementalSearchBanner::hide() 83{ 84 set_visible(false); 85 m_editor->do_layout(); 86 m_editor->update_scrollbar_ranges(); 87 m_editor->reset_search_results(); 88 m_editor->set_focus(true); 89} 90 91void IncrementalSearchBanner::search(TextEditor::SearchDirection direction) 92{ 93 auto needle = m_search_textbox->text(); 94 if (needle.is_empty()) { 95 m_editor->reset_search_results(); 96 m_index_label->set_text(DeprecatedString::empty()); 97 return; 98 } 99 100 auto index = m_editor->search_result_index().value_or(0) + 1; 101 if (m_wrap_search == TextDocument::SearchShouldWrap::No) { 102 auto forward = direction == TextEditor::SearchDirection::Forward; 103 if ((index == m_editor->search_results().size() && forward) || (index == 1 && !forward)) 104 return; 105 } 106 107 auto result = m_editor->find_text(needle, direction, m_wrap_search, false, m_match_case); 108 index = m_editor->search_result_index().value_or(0) + 1; 109 if (result.is_valid()) 110 m_index_label->set_text(DeprecatedString::formatted("{} of {}", index, m_editor->search_results().size())); 111 else 112 m_index_label->set_text(DeprecatedString::empty()); 113} 114 115void IncrementalSearchBanner::paint_event(PaintEvent& event) 116{ 117 Widget::paint_event(event); 118 119 Painter painter(*this); 120 painter.add_clip_rect(event.rect()); 121 painter.draw_line({ 0, rect().bottom() - 1 }, { width(), rect().bottom() - 1 }, palette().threed_shadow1()); 122 painter.draw_line({ 0, rect().bottom() }, { width(), rect().bottom() }, palette().threed_shadow2()); 123} 124 125Optional<UISize> IncrementalSearchBanner::calculated_min_size() const 126{ 127 auto textbox_width = m_search_textbox->effective_min_size().width().as_int(); 128 auto textbox_height = m_search_textbox->effective_min_size().height().as_int(); 129 auto button_width = m_next_button->effective_min_size().width().as_int(); 130 VERIFY(layout()); 131 auto margins = layout()->margins(); 132 auto spacing = layout()->spacing(); 133 return { { margins.left() + textbox_width + spacing + button_width * 2 + margins.right(), textbox_height + margins.top() + margins.bottom() } }; 134} 135 136}