IncrementalSearchBanner.cpp 4.8 KB

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