mirror of
https://github.com/godotengine/godot.git
synced 2024-11-22 12:12:28 +00:00
Merge pull request #67644 from alfredbaudisch/add-selection-next-occurrence
Add Selection and Caret for Next Occurrence of Selection
This commit is contained in:
commit
0486810697
@ -337,6 +337,7 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
|
||||
{ "ui_text_scroll_down.macos", TTRC("Scroll Down") },
|
||||
{ "ui_text_select_all", TTRC("Select All") },
|
||||
{ "ui_text_select_word_under_caret", TTRC("Select Word Under Caret") },
|
||||
{ "ui_text_add_selection_for_next_occurrence", TTRC("Add Selection for Next Occurrence") },
|
||||
{ "ui_text_toggle_insert_mode", TTRC("Toggle Insert Mode") },
|
||||
{ "ui_text_submit", TTRC("Text Submitted") },
|
||||
{ "ui_graph_duplicate", TTRC("Duplicate Nodes") },
|
||||
@ -641,9 +642,13 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
|
||||
default_builtin_cache.insert("ui_text_select_all", inputs);
|
||||
|
||||
inputs = List<Ref<InputEvent>>();
|
||||
inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL));
|
||||
inputs.push_back(InputEventKey::create_reference(Key::G | KeyModifierMask::ALT));
|
||||
default_builtin_cache.insert("ui_text_select_word_under_caret", inputs);
|
||||
|
||||
inputs = List<Ref<InputEvent>>();
|
||||
inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL));
|
||||
default_builtin_cache.insert("ui_text_add_selection_for_next_occurrence", inputs);
|
||||
|
||||
inputs = List<Ref<InputEvent>>();
|
||||
inputs.push_back(InputEventKey::create_reference(Key::INSERT));
|
||||
default_builtin_cache.insert("ui_text_toggle_insert_mode", inputs);
|
||||
|
@ -830,6 +830,13 @@
|
||||
</member>
|
||||
<member name="input/ui_swap_input_direction" type="Dictionary" setter="" getter="">
|
||||
</member>
|
||||
<member name="input/ui_text_add_selection_for_next_occurrence" type="Dictionary" setter="" getter="">
|
||||
If a selection is currently active with the last caret in text fields, searches for the next occurrence of the selection, adds a caret and selects the next occurrence.
|
||||
If no selection is currently active with the last caret in text fields, selects the word currently under the caret.
|
||||
The action can be performed sequentially for all occurrences of the selection of the last caret and for all existing carets.
|
||||
The viewport is adjusted to the latest newly added caret.
|
||||
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
|
||||
</member>
|
||||
<member name="input/ui_text_backspace" type="Dictionary" setter="" getter="">
|
||||
Default [InputEventAction] to delete the character before the text cursor.
|
||||
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
|
||||
@ -984,7 +991,7 @@
|
||||
</member>
|
||||
<member name="input/ui_text_select_word_under_caret" type="Dictionary" setter="" getter="">
|
||||
If no selection is currently active, selects the word currently under the caret in text fields. If a selection is currently active, deselects the current selection.
|
||||
[b]Note:[/b] Currently, this is only implemented in [TextEdit], not [LineEdit].
|
||||
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
|
||||
</member>
|
||||
<member name="input/ui_text_submit" type="Dictionary" setter="" getter="">
|
||||
Default [InputEventAction] to submit a text field.
|
||||
|
@ -70,6 +70,12 @@
|
||||
Register a new gutter to this [TextEdit]. Use [param at] to have a specific gutter order. A value of [code]-1[/code] appends the gutter to the right.
|
||||
</description>
|
||||
</method>
|
||||
<method name="add_selection_for_next_occurrence">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Adds a selection and a caret for the next occurrence of the current selection. If there is no active selection, selects word under caret.
|
||||
</description>
|
||||
</method>
|
||||
<method name="adjust_carets_after_edit">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret" type="int" />
|
||||
|
@ -2051,7 +2051,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
|
||||
}
|
||||
|
||||
if (is_shortcut_keys_enabled()) {
|
||||
// SELECT ALL, SELECT WORD UNDER CARET, CUT, COPY, PASTE.
|
||||
// SELECT ALL, SELECT WORD UNDER CARET, ADD SELECTION FOR NEXT OCCURRENCE, CUT, COPY, PASTE.
|
||||
if (k->is_action("ui_text_select_all", true)) {
|
||||
select_all();
|
||||
accept_event();
|
||||
@ -2062,6 +2062,11 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
|
||||
accept_event();
|
||||
return;
|
||||
}
|
||||
if (k->is_action("ui_text_add_selection_for_next_occurrence", true)) {
|
||||
add_selection_for_next_occurrence();
|
||||
accept_event();
|
||||
return;
|
||||
}
|
||||
if (k->is_action("ui_cut", true)) {
|
||||
cut();
|
||||
accept_event();
|
||||
@ -4832,6 +4837,45 @@ void TextEdit::select_word_under_caret(int p_caret) {
|
||||
merge_overlapping_carets();
|
||||
}
|
||||
|
||||
void TextEdit::add_selection_for_next_occurrence() {
|
||||
if (!selecting_enabled || !is_multiple_carets_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (text.size() == 1 && text[0].length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Always use the last caret, to correctly search for
|
||||
// the next occurrence that comes after this caret.
|
||||
int caret = get_caret_count() - 1;
|
||||
|
||||
if (!has_selection(caret)) {
|
||||
select_word_under_caret(caret);
|
||||
return;
|
||||
}
|
||||
|
||||
const String &highlighted_text = get_selected_text(caret);
|
||||
int column = get_selection_from_column(caret) + 1;
|
||||
int line = get_caret_line(caret);
|
||||
|
||||
const Point2i next_occurrence = search(highlighted_text, SEARCH_MATCH_CASE, line, column);
|
||||
|
||||
if (next_occurrence.x == -1 || next_occurrence.y == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
int to_column = get_selection_to_column(caret) + 1;
|
||||
int end = next_occurrence.x + (to_column - column);
|
||||
int new_caret = add_caret(next_occurrence.y, end);
|
||||
|
||||
if (new_caret != -1) {
|
||||
select(next_occurrence.y, next_occurrence.x, next_occurrence.y, end, new_caret);
|
||||
adjust_viewport_to_caret(new_caret);
|
||||
merge_overlapping_carets();
|
||||
}
|
||||
}
|
||||
|
||||
void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_to_column, int p_caret) {
|
||||
ERR_FAIL_INDEX(p_caret, carets.size());
|
||||
if (!selecting_enabled) {
|
||||
@ -6000,6 +6044,7 @@ void TextEdit::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("select_all"), &TextEdit::select_all);
|
||||
ClassDB::bind_method(D_METHOD("select_word_under_caret", "caret_index"), &TextEdit::select_word_under_caret, DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("add_selection_for_next_occurrence"), &TextEdit::add_selection_for_next_occurrence);
|
||||
ClassDB::bind_method(D_METHOD("select", "from_line", "from_column", "to_line", "to_column", "caret_index"), &TextEdit::select, DEFVAL(0));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("has_selection", "caret_index"), &TextEdit::has_selection, DEFVAL(-1));
|
||||
|
@ -851,6 +851,7 @@ public:
|
||||
|
||||
void select_all();
|
||||
void select_word_under_caret(int p_caret = -1);
|
||||
void add_selection_for_next_occurrence();
|
||||
void select(int p_from_line, int p_from_column, int p_to_line, int p_to_column, int p_caret = 0);
|
||||
|
||||
bool has_selection(int p_caret = -1) const;
|
||||
|
@ -733,6 +733,46 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
|
||||
SIGNAL_CHECK_FALSE("caret_changed");
|
||||
}
|
||||
|
||||
SUBCASE("[TextEdit] add selection for next occurrence") {
|
||||
text_edit->set_text("\ntest other_test\nrandom test\nword test word");
|
||||
text_edit->set_caret_column(0);
|
||||
text_edit->set_caret_line(1);
|
||||
|
||||
text_edit->select_word_under_caret();
|
||||
CHECK(text_edit->has_selection(0));
|
||||
CHECK(text_edit->get_selected_text(0) == "test");
|
||||
|
||||
text_edit->add_selection_for_next_occurrence();
|
||||
CHECK(text_edit->get_caret_count() == 2);
|
||||
CHECK(text_edit->get_selected_text(1) == "test");
|
||||
CHECK(text_edit->get_selection_from_line(1) == 1);
|
||||
CHECK(text_edit->get_selection_from_column(1) == 13);
|
||||
CHECK(text_edit->get_selection_to_line(1) == 1);
|
||||
CHECK(text_edit->get_selection_to_column(1) == 17);
|
||||
CHECK(text_edit->get_caret_line(1) == 1);
|
||||
CHECK(text_edit->get_caret_column(1) == 17);
|
||||
|
||||
text_edit->add_selection_for_next_occurrence();
|
||||
CHECK(text_edit->get_caret_count() == 3);
|
||||
CHECK(text_edit->get_selected_text(2) == "test");
|
||||
CHECK(text_edit->get_selection_from_line(2) == 2);
|
||||
CHECK(text_edit->get_selection_from_column(2) == 9);
|
||||
CHECK(text_edit->get_selection_to_line(2) == 2);
|
||||
CHECK(text_edit->get_selection_to_column(2) == 13);
|
||||
CHECK(text_edit->get_caret_line(2) == 2);
|
||||
CHECK(text_edit->get_caret_column(2) == 13);
|
||||
|
||||
text_edit->add_selection_for_next_occurrence();
|
||||
CHECK(text_edit->get_caret_count() == 4);
|
||||
CHECK(text_edit->get_selected_text(3) == "test");
|
||||
CHECK(text_edit->get_selection_from_line(3) == 3);
|
||||
CHECK(text_edit->get_selection_from_column(3) == 5);
|
||||
CHECK(text_edit->get_selection_to_line(3) == 3);
|
||||
CHECK(text_edit->get_selection_to_column(3) == 9);
|
||||
CHECK(text_edit->get_caret_line(3) == 3);
|
||||
CHECK(text_edit->get_caret_column(3) == 9);
|
||||
}
|
||||
|
||||
SUBCASE("[TextEdit] deselect on focus loss") {
|
||||
text_edit->set_text("test");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user