Add to TextEdit
END key state support
When the caret is set to the end of a line through `ui_text_caret_line_end` an internal state is kept in order to always keep the caret at the end of each line while is being moved through the different lines of the text. Also add a unit test for `CodeEdit` to check if END key state is kept on folding. Also preventing a SIGEV when `SEND_GUI_ACTION` is called with a non existing action.
This commit is contained in:
parent
db66bd35af
commit
1f27fd59f4
5 changed files with 199 additions and 9 deletions
|
@ -2551,7 +2551,7 @@ void TextEdit::_move_caret_up(bool p_select) {
|
|||
int cur_wrap_index = get_caret_wrap_index(i);
|
||||
if (cur_wrap_index > 0) {
|
||||
set_caret_line(get_caret_line(i), true, false, cur_wrap_index - 1, i);
|
||||
} else if (get_caret_line(i) == 0) {
|
||||
} else if (get_caret_line(i) == 0 && !_is_caret_at_eol(i)) {
|
||||
set_caret_column(0, i == 0, i);
|
||||
} else {
|
||||
int new_line = get_caret_line(i) - get_next_visible_line_offset_from(get_caret_line(i) - 1, -1);
|
||||
|
@ -2577,7 +2577,7 @@ void TextEdit::_move_caret_down(bool p_select) {
|
|||
int cur_wrap_index = get_caret_wrap_index(i);
|
||||
if (cur_wrap_index < get_line_wrap_count(get_caret_line(i))) {
|
||||
set_caret_line(get_caret_line(i), i == 0, false, cur_wrap_index + 1, i);
|
||||
} else if (get_caret_line(i) == get_last_unhidden_line()) {
|
||||
} else if (get_caret_line(i) == get_last_unhidden_line() && !_is_caret_at_eol(i)) {
|
||||
set_caret_column(text[get_caret_line(i)].length());
|
||||
} else {
|
||||
int new_line = get_caret_line(i) + get_next_visible_line_offset_from(CLAMP(get_caret_line(i) + 1, 0, text.size() - 1), 1);
|
||||
|
@ -2639,6 +2639,9 @@ void TextEdit::_move_caret_to_line_end(bool p_select) {
|
|||
} else {
|
||||
set_caret_column(row_end_col, i == 0, i);
|
||||
}
|
||||
|
||||
// Setting this to flag the caret is at the end of line.
|
||||
carets.write[i].last_fit_x = INT_MAX;
|
||||
}
|
||||
merge_overlapping_carets();
|
||||
}
|
||||
|
@ -2841,7 +2844,9 @@ void TextEdit::_move_caret_document_end(bool p_select) {
|
|||
}
|
||||
|
||||
set_caret_line(get_last_unhidden_line(), true, false, -1);
|
||||
set_caret_column(text[get_caret_line()].length());
|
||||
if (!_is_caret_at_eol(0)) {
|
||||
set_caret_column(text[get_caret_line()].length());
|
||||
}
|
||||
}
|
||||
|
||||
bool TextEdit::_clear_carets_and_selection() {
|
||||
|
@ -4896,7 +4901,9 @@ void TextEdit::collapse_carets(int p_from_line, int p_from_column, int p_to_line
|
|||
if (is_caret_in) {
|
||||
// Caret was in the collapsed area.
|
||||
set_caret_line(collapse_line, false, true, -1, i);
|
||||
set_caret_column(collapse_column, false, i);
|
||||
if (!_is_caret_at_eol(i)) {
|
||||
set_caret_column(collapse_column, false, i);
|
||||
}
|
||||
if (is_in_mulitcaret_edit() && get_caret_count() > 1) {
|
||||
multicaret_edit_ignore_carets.insert(i);
|
||||
}
|
||||
|
@ -4909,7 +4916,9 @@ void TextEdit::collapse_carets(int p_from_line, int p_from_column, int p_to_line
|
|||
// Selection was completely encapsulated.
|
||||
deselect(i);
|
||||
set_caret_line(collapse_line, false, true, -1, i);
|
||||
set_caret_column(collapse_column, false, i);
|
||||
if (!_is_caret_at_eol(i)) {
|
||||
set_caret_column(collapse_column, false, i);
|
||||
}
|
||||
if (is_in_mulitcaret_edit() && get_caret_count() > 1) {
|
||||
multicaret_edit_ignore_carets.insert(i);
|
||||
}
|
||||
|
@ -4917,7 +4926,9 @@ void TextEdit::collapse_carets(int p_from_line, int p_from_column, int p_to_line
|
|||
} else if (is_caret_in) {
|
||||
// Only caret was inside.
|
||||
set_caret_line(collapse_line, false, true, -1, i);
|
||||
set_caret_column(collapse_column, false, i);
|
||||
if (!_is_caret_at_eol(i)) {
|
||||
set_caret_column(collapse_column, false, i);
|
||||
}
|
||||
any_collapsed = true;
|
||||
} else if (is_origin_in) {
|
||||
// Only selection origin was inside.
|
||||
|
@ -5116,8 +5127,13 @@ void TextEdit::set_caret_line(int p_line, bool p_adjust_viewport, bool p_can_be_
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// Clamp the column.
|
||||
n_col = MIN(get_caret_column(p_caret), get_line(p_line).length());
|
||||
// Keep the caret at the end of the line.
|
||||
if (_is_caret_at_eol(p_caret)) {
|
||||
n_col = get_line(p_line).length();
|
||||
} else {
|
||||
// Clamp the column.
|
||||
n_col = MIN(get_caret_column(p_caret), get_line(p_line).length());
|
||||
}
|
||||
}
|
||||
caret_moved = (caret_moved || get_caret_column(p_caret) != n_col);
|
||||
carets.write[p_caret].column = n_col;
|
||||
|
@ -7595,7 +7611,7 @@ int TextEdit::_get_char_pos_for_line(int p_px, int p_line, int p_wrap_index) con
|
|||
if (is_layout_rtl()) {
|
||||
p_px = TS->shaped_text_get_size(text_rid).x - p_px + wrap_indent;
|
||||
} else {
|
||||
p_px -= wrap_indent;
|
||||
p_px -= (int)wrap_indent;
|
||||
}
|
||||
int ofs = TS->shaped_text_hit_test_position(text_rid, p_px);
|
||||
if (!caret_mid_grapheme_enabled) {
|
||||
|
@ -7745,6 +7761,10 @@ void TextEdit::_cancel_drag_and_drop_text() {
|
|||
}
|
||||
}
|
||||
|
||||
bool TextEdit::_is_caret_at_eol(int p_caret) {
|
||||
return carets[p_caret].last_fit_x == INT_MAX;
|
||||
}
|
||||
|
||||
/* Selection */
|
||||
void TextEdit::_selection_changed(int p_caret) {
|
||||
if (!selecting_enabled) {
|
||||
|
|
|
@ -461,6 +461,8 @@ private:
|
|||
|
||||
void _cancel_drag_and_drop_text();
|
||||
|
||||
bool _is_caret_at_eol(int p_caret = 0);
|
||||
|
||||
/* Selection. */
|
||||
SelectionMode selecting_mode = SelectionMode::SELECTION_MODE_NONE;
|
||||
|
||||
|
|
|
@ -5674,6 +5674,44 @@ func _ready():
|
|||
memdelete(code_edit);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][CodeEdit] End key state behavior on folded block") {
|
||||
const String text = R"(extends Node
|
||||
|
||||
func some_function(param1, param2, param3):
|
||||
local_const = 5
|
||||
|
||||
if param1 < local_const:
|
||||
print(">" + param1)
|
||||
elif param2 > 5:
|
||||
print(param2)
|
||||
else:
|
||||
print("Fail!")
|
||||
print("No!")
|
||||
|
||||
for i in range(20):
|
||||
print("line longer than for loop")
|
||||
print(i)
|
||||
)";
|
||||
|
||||
CodeEdit *code_edit = memnew(CodeEdit);
|
||||
SceneTree::get_singleton()->get_root()->add_child(code_edit);
|
||||
code_edit->grab_focus();
|
||||
code_edit->set_line_folding_enabled(true);
|
||||
code_edit->set_text(text);
|
||||
code_edit->set_caret_line(15); // print(i)
|
||||
code_edit->set_caret_column(0);
|
||||
|
||||
SEND_GUI_ACTION("ui_text_caret_line_end");
|
||||
code_edit->fold_line(13); // for i in range(20):
|
||||
CHECK(code_edit->get_caret_column() == code_edit->get_line(13).length()); // for i in range(20):
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
CHECK(code_edit->get_caret_column() == code_edit->get_line(10).length()); // print("Fail!")
|
||||
|
||||
memdelete(code_edit);
|
||||
}
|
||||
|
||||
} // namespace TestCodeEdit
|
||||
|
||||
#endif // TEST_CODE_EDIT_H
|
||||
|
|
|
@ -8157,6 +8157,132 @@ TEST_CASE("[SceneTree][TextEdit] gutters") {
|
|||
memdelete(text_edit);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][TextEdit] End Key State") {
|
||||
const String text = R"(extends Node
|
||||
|
||||
func some_function(param1, param2, param3):
|
||||
local_const = 5
|
||||
|
||||
if param1 < local_const:
|
||||
print(">" + param1)
|
||||
elif param2 > 5:
|
||||
print(param2)
|
||||
else:
|
||||
print("Fail!")
|
||||
print("No!")
|
||||
|
||||
for i in range(20):
|
||||
print("line longer than for loop")
|
||||
print(i)
|
||||
)";
|
||||
|
||||
TextEdit *text_edit = memnew(TextEdit);
|
||||
SceneTree::get_singleton()->get_root()->add_child(text_edit);
|
||||
text_edit->grab_focus();
|
||||
text_edit->set_text(text);
|
||||
text_edit->set_caret_line(6); // print(">" + param1)
|
||||
text_edit->set_caret_column(0);
|
||||
|
||||
SUBCASE("End key state") {
|
||||
SEND_GUI_ACTION("ui_text_caret_line_end");
|
||||
CHECK(text_edit->get_caret_column() == text_edit->get_line(6).length()); // print(">" + param1)
|
||||
// Go down to a shorter line.
|
||||
SEND_GUI_ACTION("ui_text_caret_down");
|
||||
CHECK(text_edit->get_caret_column() == text_edit->get_line(7).length()); // elif param2 > 5:
|
||||
// Go up to a longer line.
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
CHECK(text_edit->get_caret_column() == text_edit->get_line(4).length()); // if param1 < local_const:
|
||||
|
||||
// Go up and go through an empty line
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
CHECK(text_edit->get_caret_column() == text_edit->get_line(3).length()); // empty line
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
CHECK(text_edit->get_caret_column() == text_edit->get_line(2).length()); // local_const = 5
|
||||
}
|
||||
|
||||
SUBCASE("Reaching the end of line doesn't set end key state") {
|
||||
text_edit->set_caret_line(11); // print("No!")
|
||||
// print(i) is 14 characters long, so set the caret to the 13th character.
|
||||
text_edit->set_caret_column(13);
|
||||
SEND_GUI_ACTION("ui_text_caret_right");
|
||||
CHECK(text_edit->get_caret_column() == text_edit->get_line(11).length()); // print("No!")
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
CHECK(text_edit->get_caret_column() == 15); // print("Fail!")
|
||||
}
|
||||
|
||||
SUBCASE("Keep end key state even when reach top line") {
|
||||
text_edit->set_caret_line(2); // func some_function(param1, param2, param3):
|
||||
SEND_GUI_ACTION("ui_text_caret_line_end");
|
||||
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
ERR_PRINT_OFF;
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
ERR_PRINT_ON;
|
||||
SEND_GUI_ACTION("ui_text_caret_down");
|
||||
SEND_GUI_ACTION("ui_text_caret_down");
|
||||
CHECK(text_edit->get_caret_column() == text_edit->get_line(2).length()); // func some_function(param1, param2, param3):
|
||||
}
|
||||
|
||||
SUBCASE("Keep end key state even when reach bottom line") {
|
||||
text_edit->set_caret_line(15); // print(i)
|
||||
SEND_GUI_ACTION("ui_text_caret_line_end");
|
||||
|
||||
SEND_GUI_ACTION("ui_text_caret_down"); // Empty line at the end
|
||||
SEND_GUI_ACTION("ui_text_caret_down"); // Empty line at the end
|
||||
SEND_GUI_ACTION("ui_text_caret_up"); // print(i)
|
||||
SEND_GUI_ACTION("ui_text_caret_up"); // print("line longer than for loop")
|
||||
CHECK(text_edit->get_caret_line() == 14); // print("line longer than for loop")
|
||||
CHECK(text_edit->get_caret_column() == text_edit->get_line(14).length()); // print("line longer than for loop")
|
||||
}
|
||||
|
||||
SUBCASE("Clear end key state when go to document start") {
|
||||
text_edit->set_caret_line(9); // else:
|
||||
SEND_GUI_ACTION("ui_text_caret_line_end");
|
||||
CHECK(text_edit->get_caret_column() == text_edit->get_line(9).length()); // else:
|
||||
|
||||
SEND_GUI_ACTION("ui_text_caret_document_start");
|
||||
CHECK(text_edit->get_caret_column() == 0);
|
||||
}
|
||||
|
||||
SUBCASE("Keep end key state when go to document end") {
|
||||
text_edit->set_caret_line(9); // else:
|
||||
SEND_GUI_ACTION("ui_text_caret_line_end");
|
||||
CHECK(text_edit->get_caret_column() == text_edit->get_line(9).length()); // else:
|
||||
|
||||
SEND_GUI_ACTION("ui_text_caret_document_end"); // Empty line at the end
|
||||
SEND_GUI_ACTION("ui_text_caret_up"); // print(i)
|
||||
CHECK(text_edit->get_caret_column() == text_edit->get_line(15).length()); // print(i)
|
||||
}
|
||||
|
||||
SUBCASE("Clear end key state") {
|
||||
text_edit->set_caret_line(13); // for i in range(20):
|
||||
SEND_GUI_ACTION("ui_text_caret_line_end");
|
||||
CHECK(text_edit->get_caret_column() == text_edit->get_line(13).length()); // for i in range(20):
|
||||
SEND_GUI_ACTION("ui_text_caret_left");
|
||||
SEND_GUI_ACTION("ui_text_caret_down");
|
||||
CHECK(text_edit->get_caret_line() == 14); // print("line longer than for loop")
|
||||
CHECK(text_edit->get_caret_column() != text_edit->get_line(14).length()); // print("line longer than for loop")
|
||||
}
|
||||
|
||||
SUBCASE("Moving caret to right clear end key state") {
|
||||
SEND_GUI_ACTION("ui_text_caret_line_end");
|
||||
CHECK(text_edit->get_caret_column() == text_edit->get_line(6).length()); // print(">" + param1)
|
||||
SEND_GUI_ACTION("ui_text_caret_right");
|
||||
// Wraps to next line
|
||||
CHECK(text_edit->get_caret_line() == 7); // elif param2 > 5:
|
||||
CHECK(text_edit->get_caret_column() == 0); // elif param2 > 5:
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
CHECK(text_edit->get_caret_line() == 6); // print(">" + param1)
|
||||
CHECK(text_edit->get_caret_column() == 0); // print(">" + param1)
|
||||
}
|
||||
|
||||
memdelete(text_edit);
|
||||
}
|
||||
|
||||
} // namespace TestTextEdit
|
||||
|
||||
#endif // TEST_TEXT_EDIT_H
|
||||
|
|
|
@ -148,6 +148,10 @@ int register_test_command(String p_command, TestFunc p_function);
|
|||
#define SEND_GUI_ACTION(m_action) \
|
||||
{ \
|
||||
const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(m_action); \
|
||||
if (events == nullptr) { \
|
||||
FAIL("Invalid " #m_action " action"); \
|
||||
return; \
|
||||
} \
|
||||
const List<Ref<InputEvent>>::Element *first_event = events->front(); \
|
||||
Ref<InputEventKey> event = first_event->get()->duplicate(); \
|
||||
event->set_pressed(true); \
|
||||
|
|
Loading…
Reference in a new issue