/* * Copyright (c) 2026, Fırat Kızılboğa * * SPDX-License-Identifier: BSD-2-Clause */ #include "FindInPageWidget.h" #include #include #include #include #include #include REGISTER_WIDGET(Browser, FindInPageWidget) namespace Browser { FindInPageWidget::FindInPageWidget() { load_from_gml(find_in_page_widget_gml).release_value_but_fixme_should_propagate_errors(); m_close_button = find_descendant_of_type_named("close_button"); m_previous_button = find_descendant_of_type_named("previous_button"); m_next_button = find_descendant_of_type_named("next_button"); m_search_textbox = find_descendant_of_type_named("search_textbox"); m_match_case_checkbox = find_descendant_of_type_named("match_case_checkbox"); m_result_label = find_descendant_of_type_named("result_label"); m_close_button->set_icon(MUST(Gfx::Bitmap::load_from_uri("resource://icons/16x16/window-close.png"sv))); m_previous_button->set_icon(MUST(Gfx::Bitmap::load_from_uri("resource://icons/16x16/go-up.png"sv))); m_next_button->set_icon(MUST(Gfx::Bitmap::load_from_uri("resource://icons/16x16/go-down.png"sv))); } void FindInPageWidget::initialize(WebView::OutOfProcessWebView& web_view) { m_web_content_view = web_view; m_search_textbox->on_change = [this] { find_text_changed(); notify_search_text_changed(); }; m_search_textbox->on_return_pressed = [this] { m_web_content_view->find_in_page_next_match(); notify_search_text_changed(); }; m_search_textbox->on_shift_return_pressed = [this] { m_web_content_view->find_in_page_previous_match(); notify_search_text_changed(); }; m_next_button->on_click = [this](auto) { m_web_content_view->find_in_page_next_match(); notify_search_text_changed(); }; m_previous_button->on_click = [this](auto) { m_web_content_view->find_in_page_previous_match(); notify_search_text_changed(); }; m_close_button->on_click = [this](auto) { set_visible(false); }; m_match_case_checkbox->on_checked = [this](bool) { if (!m_search_textbox->text().is_empty()) find_text_changed(); }; m_web_content_view->on_find_in_page = [this](size_t current_match_index, Optional const& total_match_count) { update_result_label(current_match_index, total_match_count); }; } void FindInPageWidget::find_text_changed() { auto query = MUST(String::from_byte_string(m_search_textbox->text())); auto case_sensitive = m_match_case_checkbox->is_checked() ? CaseSensitivity::CaseSensitive : CaseSensitivity::CaseInsensitive; m_web_content_view->find_in_page(query, case_sensitive); } void FindInPageWidget::update_result_label(size_t current_match_index, Optional const& total_match_count) { if (total_match_count.has_value()) { if (*total_match_count > 0) { m_result_label->set_text(MUST(String::formatted("{} of {} matches", current_match_index + 1, *total_match_count))); } else { m_result_label->set_text("Phrase not found"_string); } m_result_label->set_visible(true); } else { m_result_label->set_visible(false); } } void FindInPageWidget::notify_search_text_changed() { if (on_search_text_change) { auto text = MUST(String::from_byte_string(m_search_textbox->text())); on_search_text_change(text); } } void FindInPageWidget::show(String const& global_term) { // Priority 1: If there's a selection on the page, use that if (auto selection = m_web_content_view->selected_text_with_whitespace_collapsed(); selection.has_value() && !selection->is_empty()) { m_search_textbox->set_text(*selection); notify_search_text_changed(); } // Priority 2: If the textbox is empty, use the global term else if (m_search_textbox->text().is_empty() && !global_term.is_empty()) { m_search_textbox->set_text(global_term); } // Priority 3: Keep existing text (do nothing) m_search_textbox->select_all(); m_search_textbox->set_focus(true); set_visible(true); } void FindInPageWidget::keydown_event(GUI::KeyEvent& event) { if (event.key() == KeyCode::Key_Escape) { set_visible(false); event.accept(); return; } Widget::keydown_event(event); } }