mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 09:48:11 +00:00
TextEditor: Implement replacement functionality
This patch adds a new replace widget that cooperates with the find widget, the replace widget takes the input in the find textbox, searches for occurences of that input, and replaces them with the input provied in the replace textbox.
This commit is contained in:
parent
2b20e8e187
commit
ecf58ddd4f
3 changed files with 131 additions and 13 deletions
|
@ -41,15 +41,30 @@ TextEditorWidget::TextEditorWidget()
|
||||||
update_title();
|
update_title();
|
||||||
};
|
};
|
||||||
|
|
||||||
m_find_widget = GWidget::construct(this);
|
m_find_replace_widget = GWidget::construct(this);
|
||||||
|
m_find_replace_widget->set_fill_with_background_color(true);
|
||||||
|
m_find_replace_widget->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
|
||||||
|
m_find_replace_widget->set_preferred_size(0, 48);
|
||||||
|
m_find_replace_widget->set_layout(make<GBoxLayout>(Orientation::Vertical));
|
||||||
|
m_find_replace_widget->layout()->set_margins({ 2, 2, 2, 4 });
|
||||||
|
m_find_replace_widget->set_visible(false);
|
||||||
|
|
||||||
|
m_find_widget = GWidget::construct(m_find_replace_widget);
|
||||||
m_find_widget->set_fill_with_background_color(true);
|
m_find_widget->set_fill_with_background_color(true);
|
||||||
m_find_widget->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
|
m_find_widget->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
|
||||||
m_find_widget->set_preferred_size(0, 22);
|
m_find_widget->set_preferred_size(0, 22);
|
||||||
m_find_widget->set_layout(make<GBoxLayout>(Orientation::Horizontal));
|
m_find_widget->set_layout(make<GBoxLayout>(Orientation::Horizontal));
|
||||||
m_find_widget->layout()->set_margins({ 2, 2, 2, 2 });
|
|
||||||
m_find_widget->set_visible(false);
|
m_find_widget->set_visible(false);
|
||||||
|
|
||||||
|
m_replace_widget = GWidget::construct(m_find_replace_widget);
|
||||||
|
m_replace_widget->set_fill_with_background_color(true);
|
||||||
|
m_replace_widget->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
|
||||||
|
m_replace_widget->set_preferred_size(0, 22);
|
||||||
|
m_replace_widget->set_layout(make<GBoxLayout>(Orientation::Horizontal));
|
||||||
|
m_replace_widget->set_visible(false);
|
||||||
|
|
||||||
m_find_textbox = GTextBox::construct(m_find_widget);
|
m_find_textbox = GTextBox::construct(m_find_widget);
|
||||||
|
m_replace_textbox = GTextBox::construct(m_replace_widget);
|
||||||
|
|
||||||
m_find_next_action = GAction::create("Find next", { Mod_Ctrl, Key_G }, [&](auto&) {
|
m_find_next_action = GAction::create("Find next", { Mod_Ctrl, Key_G }, [&](auto&) {
|
||||||
auto needle = m_find_textbox->text();
|
auto needle = m_find_textbox->text();
|
||||||
|
@ -69,6 +84,7 @@ TextEditorWidget::TextEditorWidget()
|
||||||
GMessageBox::InputType::OK, window());
|
GMessageBox::InputType::OK, window());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
m_find_previous_action = GAction::create("Find previous", { Mod_Ctrl | Mod_Shift, Key_G }, [&](auto&) {
|
m_find_previous_action = GAction::create("Find previous", { Mod_Ctrl | Mod_Shift, Key_G }, [&](auto&) {
|
||||||
auto needle = m_find_textbox->text();
|
auto needle = m_find_textbox->text();
|
||||||
if (needle.is_empty()) {
|
if (needle.is_empty()) {
|
||||||
|
@ -94,14 +110,78 @@ TextEditorWidget::TextEditorWidget()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
m_find_previous_button = GButton::construct("Previous", m_find_widget);
|
m_replace_next_action = GAction::create("Replace next", { Mod_Ctrl, Key_F1 }, [&](auto&) {
|
||||||
|
auto needle = m_find_textbox->text();
|
||||||
|
auto substitute = m_replace_textbox->text();
|
||||||
|
|
||||||
|
if (needle.is_empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto selection_start = m_editor->normalized_selection().start();
|
||||||
|
if (!selection_start.is_valid())
|
||||||
|
selection_start = m_editor->normalized_selection().start();
|
||||||
|
|
||||||
|
auto found_range = m_editor->document().find_next(needle, selection_start);
|
||||||
|
|
||||||
|
if (found_range.is_valid()) {
|
||||||
|
m_editor->set_selection(found_range);
|
||||||
|
m_editor->insert_at_cursor_or_replace_selection(substitute);
|
||||||
|
} else {
|
||||||
|
GMessageBox::show(
|
||||||
|
String::format("Not found: \"%s\"", needle.characters()),
|
||||||
|
"Not found",
|
||||||
|
GMessageBox::Type::Information,
|
||||||
|
GMessageBox::InputType::OK, window());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_replace_previous_action = GAction::create("Replace previous", { Mod_Ctrl | Mod_Shift, Key_F1 }, [&](auto&) {
|
||||||
|
auto needle = m_find_textbox->text();
|
||||||
|
auto substitute = m_replace_textbox->text();
|
||||||
|
if (needle.is_empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto selection_start = m_editor->normalized_selection().start();
|
||||||
|
if (!selection_start.is_valid())
|
||||||
|
selection_start = m_editor->normalized_selection().start();
|
||||||
|
|
||||||
|
auto found_range = m_editor->document().find_previous(needle, selection_start);
|
||||||
|
|
||||||
|
if (found_range.is_valid()) {
|
||||||
|
m_editor->set_selection(found_range);
|
||||||
|
m_editor->insert_at_cursor_or_replace_selection(substitute);
|
||||||
|
} else {
|
||||||
|
GMessageBox::show(
|
||||||
|
String::format("Not found: \"%s\"", needle.characters()),
|
||||||
|
"Not found",
|
||||||
|
GMessageBox::Type::Information,
|
||||||
|
GMessageBox::InputType::OK, window());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_replace_all_action = GAction::create("Replace all", { Mod_Ctrl, Key_F2 }, [&](auto&) {
|
||||||
|
auto needle = m_find_textbox->text();
|
||||||
|
auto substitute = m_replace_textbox->text();
|
||||||
|
if (needle.is_empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
auto found_range = m_editor->document().find_next(needle);
|
||||||
|
while(found_range.is_valid()) {
|
||||||
|
m_editor->set_selection(found_range);
|
||||||
|
m_editor->insert_at_cursor_or_replace_selection(substitute);
|
||||||
|
found_range = m_editor->document().find_next(needle);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_find_previous_button = GButton::construct("Find Previous", m_find_widget);
|
||||||
m_find_previous_button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill);
|
m_find_previous_button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill);
|
||||||
m_find_previous_button->set_preferred_size(64, 0);
|
m_find_previous_button->set_preferred_size(150, 0);
|
||||||
m_find_previous_button->set_action(*m_find_previous_action);
|
m_find_previous_button->set_action(*m_find_previous_action);
|
||||||
|
|
||||||
m_find_next_button = GButton::construct("Next", m_find_widget);
|
m_find_next_button = GButton::construct("Find Next", m_find_widget);
|
||||||
m_find_next_button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill);
|
m_find_next_button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill);
|
||||||
m_find_next_button->set_preferred_size(64, 0);
|
m_find_next_button->set_preferred_size(150, 0);
|
||||||
m_find_next_button->set_action(*m_find_next_action);
|
m_find_next_button->set_action(*m_find_next_action);
|
||||||
|
|
||||||
m_find_textbox->on_return_pressed = [this] {
|
m_find_textbox->on_return_pressed = [this] {
|
||||||
|
@ -109,17 +189,43 @@ TextEditorWidget::TextEditorWidget()
|
||||||
};
|
};
|
||||||
|
|
||||||
m_find_textbox->on_escape_pressed = [this] {
|
m_find_textbox->on_escape_pressed = [this] {
|
||||||
m_find_widget->set_visible(false);
|
m_find_replace_widget->set_visible(false);
|
||||||
m_editor->set_focus(true);
|
m_editor->set_focus(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
m_find_action = GAction::create("Find...", { Mod_Ctrl, Key_F }, load_png("/res/icons/16x16/find.png"), [this](auto&) {
|
m_replace_previous_button = GButton::construct("Replace Previous", m_replace_widget);
|
||||||
|
m_replace_previous_button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill);
|
||||||
|
m_replace_previous_button->set_preferred_size(100, 0);
|
||||||
|
m_replace_previous_button->set_action(*m_replace_previous_action);
|
||||||
|
|
||||||
|
m_replace_next_button = GButton::construct("Replace Next", m_replace_widget);
|
||||||
|
m_replace_next_button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill);
|
||||||
|
m_replace_next_button->set_preferred_size(100, 0);
|
||||||
|
m_replace_next_button->set_action(*m_replace_next_action);
|
||||||
|
|
||||||
|
m_replace_all_button = GButton::construct("Replace ALl", m_replace_widget);
|
||||||
|
m_replace_all_button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill);
|
||||||
|
m_replace_all_button->set_preferred_size(100, 0);
|
||||||
|
m_replace_all_button->set_action(*m_replace_all_action);
|
||||||
|
|
||||||
|
m_replace_textbox->on_return_pressed = [this] {
|
||||||
|
m_replace_next_button->click();
|
||||||
|
};
|
||||||
|
|
||||||
|
m_replace_textbox->on_escape_pressed = [this] {
|
||||||
|
m_find_replace_widget->set_visible(false);
|
||||||
|
m_editor->set_focus(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
m_find_replace_action = GAction::create("Find/Replace..", { Mod_Ctrl, Key_F }, load_png("/res/icons/16x16/find.png"), [this](auto&) {
|
||||||
|
m_find_replace_widget->set_visible(true);
|
||||||
m_find_widget->set_visible(true);
|
m_find_widget->set_visible(true);
|
||||||
|
m_replace_widget->set_visible(true);
|
||||||
m_find_textbox->set_focus(true);
|
m_find_textbox->set_focus(true);
|
||||||
m_find_textbox->select_all();
|
m_find_textbox->select_all();
|
||||||
});
|
});
|
||||||
|
|
||||||
m_editor->add_custom_context_menu_action(*m_find_action);
|
m_editor->add_custom_context_menu_action(*m_find_replace_action);
|
||||||
m_editor->add_custom_context_menu_action(*m_find_next_action);
|
m_editor->add_custom_context_menu_action(*m_find_next_action);
|
||||||
m_editor->add_custom_context_menu_action(*m_find_previous_action);
|
m_editor->add_custom_context_menu_action(*m_find_previous_action);
|
||||||
|
|
||||||
|
@ -223,9 +329,12 @@ TextEditorWidget::TextEditorWidget()
|
||||||
edit_menu->add_action(m_editor->paste_action());
|
edit_menu->add_action(m_editor->paste_action());
|
||||||
edit_menu->add_action(m_editor->delete_action());
|
edit_menu->add_action(m_editor->delete_action());
|
||||||
edit_menu->add_separator();
|
edit_menu->add_separator();
|
||||||
edit_menu->add_action(*m_find_action);
|
edit_menu->add_action(*m_find_replace_action);
|
||||||
edit_menu->add_action(*m_find_next_action);
|
edit_menu->add_action(*m_find_next_action);
|
||||||
edit_menu->add_action(*m_find_previous_action);
|
edit_menu->add_action(*m_find_previous_action);
|
||||||
|
edit_menu->add_action(*m_replace_next_action);
|
||||||
|
edit_menu->add_action(*m_replace_previous_action);
|
||||||
|
edit_menu->add_action(*m_replace_all_action);
|
||||||
menubar->add_menu(move(edit_menu));
|
menubar->add_menu(move(edit_menu));
|
||||||
|
|
||||||
auto font_menu = GMenu::construct("Font");
|
auto font_menu = GMenu::construct("Font");
|
||||||
|
|
|
@ -36,17 +36,26 @@ private:
|
||||||
RefPtr<GAction> m_open_action;
|
RefPtr<GAction> m_open_action;
|
||||||
RefPtr<GAction> m_save_action;
|
RefPtr<GAction> m_save_action;
|
||||||
RefPtr<GAction> m_save_as_action;
|
RefPtr<GAction> m_save_as_action;
|
||||||
RefPtr<GAction> m_find_action;
|
RefPtr<GAction> m_find_replace_action;
|
||||||
RefPtr<GAction> m_line_wrapping_setting_action;
|
RefPtr<GAction> m_line_wrapping_setting_action;
|
||||||
RefPtr<GAction> m_find_next_action;
|
RefPtr<GAction> m_find_next_action;
|
||||||
RefPtr<GAction> m_find_previous_action;
|
RefPtr<GAction> m_find_previous_action;
|
||||||
|
RefPtr<GAction> m_replace_next_action;
|
||||||
|
RefPtr<GAction> m_replace_previous_action;
|
||||||
|
RefPtr<GAction> m_replace_all_action;
|
||||||
|
|
||||||
RefPtr<GStatusBar> m_statusbar;
|
RefPtr<GStatusBar> m_statusbar;
|
||||||
|
|
||||||
RefPtr<GTextBox> m_find_textbox;
|
RefPtr<GTextBox> m_find_textbox;
|
||||||
|
RefPtr<GTextBox> m_replace_textbox;
|
||||||
GButton* m_find_previous_button { nullptr };
|
GButton* m_find_previous_button { nullptr };
|
||||||
GButton* m_find_next_button { nullptr };
|
GButton* m_find_next_button { nullptr };
|
||||||
|
GButton* m_replace_previous_button { nullptr };
|
||||||
|
GButton* m_replace_next_button { nullptr };
|
||||||
|
GButton* m_replace_all_button { nullptr };
|
||||||
|
RefPtr<GWidget> m_find_replace_widget;
|
||||||
RefPtr<GWidget> m_find_widget;
|
RefPtr<GWidget> m_find_widget;
|
||||||
|
RefPtr<GWidget> m_replace_widget;
|
||||||
|
|
||||||
bool m_document_dirty { false };
|
bool m_document_dirty { false };
|
||||||
bool m_document_opening { false };
|
bool m_document_opening { false };
|
||||||
|
|
|
@ -64,8 +64,9 @@ public:
|
||||||
// FIXME: This should take glyph spacing into account, no?
|
// FIXME: This should take glyph spacing into account, no?
|
||||||
int glyph_width() const { return font().glyph_width('x'); }
|
int glyph_width() const { return font().glyph_width('x'); }
|
||||||
|
|
||||||
bool write_to_file(const StringView& path);
|
|
||||||
|
|
||||||
|
void insert_at_cursor_or_replace_selection(const StringView&);
|
||||||
|
bool write_to_file(const StringView& path);
|
||||||
bool has_selection() const { return m_selection.is_valid(); }
|
bool has_selection() const { return m_selection.is_valid(); }
|
||||||
String selected_text() const;
|
String selected_text() const;
|
||||||
void set_selection(const GTextRange&);
|
void set_selection(const GTextRange&);
|
||||||
|
@ -156,7 +157,6 @@ private:
|
||||||
int ruler_width() const;
|
int ruler_width() const;
|
||||||
Rect ruler_content_rect(size_t line) const;
|
Rect ruler_content_rect(size_t line) const;
|
||||||
void toggle_selection_if_needed_for_event(const GKeyEvent&);
|
void toggle_selection_if_needed_for_event(const GKeyEvent&);
|
||||||
void insert_at_cursor_or_replace_selection(const StringView&);
|
|
||||||
void delete_selection();
|
void delete_selection();
|
||||||
void did_update_selection();
|
void did_update_selection();
|
||||||
int content_x_for_position(const GTextPosition&) const;
|
int content_x_for_position(const GTextPosition&) const;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue