mirror of
https://github.com/SerenityOS/serenity.git
synced 2026-11-07 12:38:38 -10:00
Any bitmap load of the style load_from_file("/res/foo") has been
converted to the new pattern load_from_uri("resource://foo").
This will allow for porting more apps to non-serenity targets, as well
as add some flexibility if we want to experiment with other resource
storage formats in the future.
136 lines
4.5 KiB
C++
136 lines
4.5 KiB
C++
/*
|
||
* Copyright (c) 2026, Fırat Kızılboğa <[email protected]>
|
||
*
|
||
* SPDX-License-Identifier: BSD-2-Clause
|
||
*/
|
||
|
||
#include "FindInPageWidget.h"
|
||
#include <Applications/Browser/FindInPageWidgetGML.h>
|
||
#include <LibGUI/Button.h>
|
||
#include <LibGUI/CheckBox.h>
|
||
#include <LibGUI/Label.h>
|
||
#include <LibGUI/TextBox.h>
|
||
#include <LibGfx/Bitmap.h>
|
||
|
||
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<GUI::Button>("close_button");
|
||
m_previous_button = find_descendant_of_type_named<GUI::Button>("previous_button");
|
||
m_next_button = find_descendant_of_type_named<GUI::Button>("next_button");
|
||
m_search_textbox = find_descendant_of_type_named<GUI::TextBox>("search_textbox");
|
||
m_match_case_checkbox = find_descendant_of_type_named<GUI::CheckBox>("match_case_checkbox");
|
||
m_result_label = find_descendant_of_type_named<GUI::Label>("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<size_t> 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<size_t> 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);
|
||
}
|
||
|
||
}
|