Overhaul TextEdit selection.
The caret is now a part of the selection.
This commit is contained in:
parent
6118592c6d
commit
154f727c7a
|
@ -58,7 +58,7 @@
|
|||
<method name="add_caret">
|
||||
<return type="int" />
|
||||
<param index="0" name="line" type="int" />
|
||||
<param index="1" name="col" type="int" />
|
||||
<param index="1" name="column" type="int" />
|
||||
<description>
|
||||
Adds a new caret at the given location. Returns the index of the new caret, or [code]-1[/code] if the location is invalid.
|
||||
</description>
|
||||
|
@ -363,6 +363,15 @@
|
|||
[b]Note:[/b] The return value is influenced by [theme_item line_spacing] and [theme_item font_size]. And it will not be less than [code]1[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_line_ranges_from_carets" qualifiers="const">
|
||||
<return type="Vector2i[]" />
|
||||
<param index="0" name="p_only_selections" type="bool" default="false" />
|
||||
<param index="1" name="p_merge_adjacent" type="bool" default="true" />
|
||||
<description>
|
||||
Returns an [Array] of line ranges where [code]x[/code] is the first line and [code]y[/code] is the last line. All lines within these ranges will have a caret on them or be part of a selection. Each line will only be part of one line range, even if it has multiple carets on it.
|
||||
If a selection's end column ([method get_selection_to_column]) is at column [code]0[/code], that line will not be included. If a selection begins on the line after another selection ends and [param p_merge_adjacent] is [code]true[/code], or they begin and end on the same line, one line range will include both selections.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_line_width" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="line" type="int" />
|
||||
|
@ -514,7 +523,17 @@
|
|||
Returns the text inside the selection of a caret, or all the carets if [param caret_index] is its default value [code]-1[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_selection_column" qualifiers="const">
|
||||
<method name="get_selection_at_line_column" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="line" type="int" />
|
||||
<param index="1" name="column" type="int" />
|
||||
<param index="2" name="include_edges" type="bool" default="true" />
|
||||
<description>
|
||||
Returns the caret index of the selection at the given [param line] and [param column], or [code]-1[/code] if there is none.
|
||||
If [param include_edges] is [code]false[/code], the position must be inside the selection and not at either end.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_selection_column" qualifiers="const" is_deprecated="true">
|
||||
<return type="int" />
|
||||
<param index="0" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
|
@ -809,7 +828,7 @@
|
|||
<param index="0" name="text" type="String" />
|
||||
<param index="1" name="flags" type="int" />
|
||||
<param index="2" name="from_line" type="int" />
|
||||
<param index="3" name="from_colum" type="int" />
|
||||
<param index="3" name="from_column" type="int" />
|
||||
<description>
|
||||
Perform a search inside the text. Search flags can be specified in the [enum SearchFlags] enum.
|
||||
In the returned vector, [code]x[/code] is the column, [code]y[/code] is the line. If no results are found, both are equal to [code]-1[/code].
|
||||
|
@ -1049,9 +1068,6 @@
|
|||
<method name="set_selection_mode">
|
||||
<return type="void" />
|
||||
<param index="0" name="mode" type="int" enum="TextEdit.SelectionMode" />
|
||||
<param index="1" name="line" type="int" default="-1" />
|
||||
<param index="2" name="column" type="int" default="-1" />
|
||||
<param index="3" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Sets the current selection mode.
|
||||
</description>
|
||||
|
|
|
@ -173,10 +173,8 @@ bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col)
|
|||
if (pos.x != -1) {
|
||||
if (!preserve_cursor && !is_selection_only()) {
|
||||
text_editor->unfold_line(pos.y);
|
||||
text_editor->set_caret_line(pos.y, false);
|
||||
text_editor->set_caret_column(pos.x + text.length(), false);
|
||||
text_editor->center_viewport_to_caret(0);
|
||||
text_editor->select(pos.y, pos.x, pos.y, pos.x + text.length());
|
||||
text_editor->center_viewport_to_caret(0);
|
||||
|
||||
line_col_changed_for_result = true;
|
||||
}
|
||||
|
@ -216,7 +214,7 @@ void FindReplaceBar::_replace() {
|
|||
text_editor->begin_complex_operation();
|
||||
if (selection_enabled && is_selection_only()) {
|
||||
// Restrict search_current() to selected region.
|
||||
text_editor->set_caret_line(selection_begin.width, false, true, 0, 0);
|
||||
text_editor->set_caret_line(selection_begin.width, false, true, -1, 0);
|
||||
text_editor->set_caret_column(selection_begin.height, true, 0);
|
||||
}
|
||||
|
||||
|
@ -285,10 +283,10 @@ void FindReplaceBar::_replace_all() {
|
|||
text_editor->begin_complex_operation();
|
||||
|
||||
if (selection_enabled && is_selection_only()) {
|
||||
text_editor->set_caret_line(selection_begin.width, false, true, 0, 0);
|
||||
text_editor->set_caret_line(selection_begin.width, false, true, -1, 0);
|
||||
text_editor->set_caret_column(selection_begin.height, true, 0);
|
||||
} else {
|
||||
text_editor->set_caret_line(0, false, true, 0, 0);
|
||||
text_editor->set_caret_line(0, false, true, -1, 0);
|
||||
text_editor->set_caret_column(0, true, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -2008,45 +2008,32 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
|
|||
tx->apply_ime();
|
||||
|
||||
Point2i pos = tx->get_line_column_at_pos(local_pos);
|
||||
int row = pos.y;
|
||||
int col = pos.x;
|
||||
int mouse_line = pos.y;
|
||||
int mouse_column = pos.x;
|
||||
|
||||
tx->set_move_caret_on_right_click_enabled(EDITOR_GET("text_editor/behavior/navigation/move_caret_on_right_click"));
|
||||
int caret_clicked = -1;
|
||||
int selection_clicked = -1;
|
||||
if (tx->is_move_caret_on_right_click_enabled()) {
|
||||
if (tx->has_selection()) {
|
||||
for (int i = 0; i < tx->get_caret_count(); i++) {
|
||||
int from_line = tx->get_selection_from_line(i);
|
||||
int to_line = tx->get_selection_to_line(i);
|
||||
int from_column = tx->get_selection_from_column(i);
|
||||
int to_column = tx->get_selection_to_column(i);
|
||||
|
||||
if (row >= from_line && row <= to_line && (row != from_line || col >= from_column) && (row != to_line || col <= to_column)) {
|
||||
// Right click in one of the selected text
|
||||
caret_clicked = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (caret_clicked < 0) {
|
||||
selection_clicked = tx->get_selection_at_line_column(mouse_line, mouse_column, true);
|
||||
if (selection_clicked < 0) {
|
||||
tx->deselect();
|
||||
tx->remove_secondary_carets();
|
||||
caret_clicked = 0;
|
||||
tx->set_caret_line(row, false, false);
|
||||
tx->set_caret_column(col);
|
||||
selection_clicked = 0;
|
||||
tx->set_caret_line(mouse_line, false, false, -1);
|
||||
tx->set_caret_column(mouse_column);
|
||||
}
|
||||
}
|
||||
|
||||
String word_at_pos = tx->get_word_at_pos(local_pos);
|
||||
if (word_at_pos.is_empty()) {
|
||||
word_at_pos = tx->get_word_under_caret(caret_clicked);
|
||||
word_at_pos = tx->get_word_under_caret(selection_clicked);
|
||||
}
|
||||
if (word_at_pos.is_empty()) {
|
||||
word_at_pos = tx->get_selected_text(caret_clicked);
|
||||
word_at_pos = tx->get_selected_text(selection_clicked);
|
||||
}
|
||||
|
||||
bool has_color = (word_at_pos == "Color");
|
||||
bool foldable = tx->can_fold_line(row) || tx->is_line_folded(row);
|
||||
bool foldable = tx->can_fold_line(mouse_line) || tx->is_line_folded(mouse_line);
|
||||
bool open_docs = false;
|
||||
bool goto_definition = false;
|
||||
|
||||
|
@ -2064,9 +2051,9 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
|
|||
}
|
||||
|
||||
if (has_color) {
|
||||
String line = tx->get_line(row);
|
||||
color_position.x = row;
|
||||
color_position.y = col;
|
||||
String line = tx->get_line(mouse_line);
|
||||
color_position.x = mouse_line;
|
||||
color_position.y = mouse_column;
|
||||
|
||||
int begin = -1;
|
||||
int end = -1;
|
||||
|
@ -2076,7 +2063,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
|
|||
COLOR_NAME, // Color.COLOR_NAME
|
||||
} expression_pattern = NOT_PARSED;
|
||||
|
||||
for (int i = col; i < line.length(); i++) {
|
||||
for (int i = mouse_column; i < line.length(); i++) {
|
||||
if (line[i] == '(') {
|
||||
if (expression_pattern == NOT_PARSED) {
|
||||
begin = i;
|
||||
|
@ -2155,7 +2142,6 @@ void ScriptTextEditor::_color_changed(const Color &p_color) {
|
|||
code_editor->get_text_editor()->begin_complex_operation();
|
||||
code_editor->get_text_editor()->set_line(color_position.x, line_with_replaced_args);
|
||||
code_editor->get_text_editor()->end_complex_operation();
|
||||
code_editor->get_text_editor()->queue_redraw();
|
||||
}
|
||||
|
||||
void ScriptTextEditor::_prepare_edit_menu() {
|
||||
|
|
|
@ -531,7 +531,7 @@ void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
|
|||
}
|
||||
}
|
||||
if (!tx->has_selection()) {
|
||||
tx->set_caret_line(row, true, false);
|
||||
tx->set_caret_line(row, true, false, -1);
|
||||
tx->set_caret_column(col);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1007,7 +1007,7 @@ void TextShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
|
|||
}
|
||||
}
|
||||
if (!tx->has_selection()) {
|
||||
tx->set_caret_line(row, true, false);
|
||||
tx->set_caret_line(row, true, false, -1);
|
||||
tx->set_caret_column(col);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -624,6 +624,18 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
|
|||
return TextEdit::get_cursor_shape(p_pos);
|
||||
}
|
||||
|
||||
void CodeEdit::_unhide_carets() {
|
||||
// Unfold caret and selection origin.
|
||||
for (int i = 0; i < get_caret_count(); i++) {
|
||||
if (_is_line_hidden(get_caret_line(i))) {
|
||||
unfold_line(get_caret_line(i));
|
||||
}
|
||||
if (has_selection(i) && _is_line_hidden(get_selection_origin_line(i))) {
|
||||
unfold_line(get_selection_origin_line(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Text manipulation */
|
||||
|
||||
// Overridable actions
|
||||
|
@ -2846,10 +2858,12 @@ void CodeEdit::_gutter_clicked(int p_line, int p_gutter) {
|
|||
|
||||
if (p_gutter == line_number_gutter) {
|
||||
remove_secondary_carets();
|
||||
set_selection_mode(TextEdit::SelectionMode::SELECTION_MODE_LINE, p_line, 0);
|
||||
select(p_line, 0, p_line + 1, 0);
|
||||
set_caret_line(p_line + 1);
|
||||
set_caret_column(0);
|
||||
set_selection_mode(TextEdit::SelectionMode::SELECTION_MODE_LINE);
|
||||
if (p_line == get_line_count() - 1) {
|
||||
select(p_line, 0, p_line, INT_MAX);
|
||||
} else {
|
||||
select(p_line, 0, p_line + 1, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -309,6 +309,8 @@ protected:
|
|||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
virtual void _unhide_carets() override;
|
||||
|
||||
/* Text manipulation */
|
||||
|
||||
// Overridable actions
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -389,18 +389,12 @@ private:
|
|||
/* Caret. */
|
||||
struct Selection {
|
||||
bool active = false;
|
||||
bool shiftclick_left = false;
|
||||
|
||||
int selecting_line = 0;
|
||||
int selecting_column = 0;
|
||||
int selected_word_beg = 0;
|
||||
int selected_word_end = 0;
|
||||
int selected_word_origin = 0;
|
||||
|
||||
int from_line = 0;
|
||||
int from_column = 0;
|
||||
int to_line = 0;
|
||||
int to_column = 0;
|
||||
int origin_line = 0;
|
||||
int origin_column = 0;
|
||||
int origin_last_fit_x = 0;
|
||||
int word_begin_column = 0;
|
||||
int word_end_column = 0;
|
||||
};
|
||||
|
||||
struct Caret {
|
||||
|
@ -438,12 +432,16 @@ private:
|
|||
bool drag_action = false;
|
||||
bool drag_caret_force_displayed = false;
|
||||
|
||||
void _caret_changed(int p_caret = -1);
|
||||
void _emit_caret_changed();
|
||||
|
||||
void _reset_caret_blink_timer();
|
||||
void _toggle_draw_caret();
|
||||
|
||||
int _get_column_x_offset_for_line(int p_char, int p_line, int p_column) const;
|
||||
bool _is_line_col_in_range(int p_line, int p_column, int p_from_line, int p_from_column, int p_to_line, int p_to_column, bool p_include_edges = true) const;
|
||||
|
||||
void _cancel_drag_and_drop_text();
|
||||
|
||||
/* Selection. */
|
||||
SelectionMode selecting_mode = SelectionMode::SELECTION_MODE_NONE;
|
||||
|
@ -456,18 +454,23 @@ private:
|
|||
|
||||
bool selection_drag_attempt = false;
|
||||
bool dragging_selection = false;
|
||||
int drag_and_drop_origin_caret_index = -1;
|
||||
int drag_caret_index = -1;
|
||||
|
||||
Timer *click_select_held = nullptr;
|
||||
uint64_t last_dblclk = 0;
|
||||
Vector2 last_dblclk_pos;
|
||||
|
||||
void _selection_changed(int p_caret = -1);
|
||||
void _click_selection_held();
|
||||
|
||||
void _update_selection_mode_pointer();
|
||||
void _update_selection_mode_word();
|
||||
void _update_selection_mode_line();
|
||||
void _update_selection_mode_pointer(bool p_initial = false);
|
||||
void _update_selection_mode_word(bool p_initial = false);
|
||||
void _update_selection_mode_line(bool p_initial = false);
|
||||
|
||||
void _pre_shift_selection(int p_caret);
|
||||
void _post_shift_selection(int p_caret);
|
||||
|
||||
bool _selection_contains(int p_caret, int p_line, int p_column, bool p_include_edges = true, bool p_only_selections = true) const;
|
||||
|
||||
/* Line wrapping. */
|
||||
LineWrappingMode line_wrapping_mode = LineWrappingMode::LINE_WRAPPING_NONE;
|
||||
|
@ -599,7 +602,8 @@ private:
|
|||
|
||||
/*** Super internal Core API. Everything builds on it. ***/
|
||||
bool text_changed_dirty = false;
|
||||
void _text_changed_emit();
|
||||
void _text_changed();
|
||||
void _emit_text_changed();
|
||||
|
||||
void _insert_text(int p_line, int p_char, const String &p_text, int *r_end_line = nullptr, int *r_end_char = nullptr);
|
||||
void _remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column);
|
||||
|
@ -659,6 +663,7 @@ protected:
|
|||
bool _is_line_hidden(int p_line) const;
|
||||
|
||||
void _unhide_all_lines();
|
||||
virtual void _unhide_carets();
|
||||
|
||||
// Symbol lookup.
|
||||
String lookup_symbol_word;
|
||||
|
@ -851,7 +856,7 @@ public:
|
|||
void set_multiple_carets_enabled(bool p_enabled);
|
||||
bool is_multiple_carets_enabled() const;
|
||||
|
||||
int add_caret(int p_line, int p_col);
|
||||
int add_caret(int p_line, int p_column);
|
||||
void remove_caret(int p_caret);
|
||||
void remove_secondary_carets();
|
||||
void merge_overlapping_carets();
|
||||
|
@ -884,27 +889,34 @@ public:
|
|||
void set_drag_and_drop_selection_enabled(const bool p_enabled);
|
||||
bool is_drag_and_drop_selection_enabled() const;
|
||||
|
||||
void set_selection_mode(SelectionMode p_mode, int p_line = -1, int p_column = -1, int p_caret = 0);
|
||||
void set_selection_mode(SelectionMode p_mode);
|
||||
SelectionMode get_selection_mode() const;
|
||||
|
||||
void select_all();
|
||||
void select_word_under_caret(int p_caret = -1);
|
||||
void add_selection_for_next_occurrence();
|
||||
void skip_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);
|
||||
void select(int p_origin_line, int p_origin_column, int p_caret_line, int p_caret_column, int p_caret = 0);
|
||||
|
||||
bool has_selection(int p_caret = -1) const;
|
||||
|
||||
String get_selected_text(int p_caret = -1);
|
||||
int get_selection_at_line_column(int p_line, int p_column, bool p_include_edges = true) const;
|
||||
Vector<Point2i> get_line_ranges_from_carets(bool p_only_selections = false, bool p_merge_adjacent = true) const;
|
||||
TypedArray<Vector2i> get_line_ranges_from_carets_typed_array(bool p_only_selections = false, bool p_merge_adjacent = true) const;
|
||||
|
||||
int get_selection_line(int p_caret = 0) const;
|
||||
int get_selection_column(int p_caret = 0) const;
|
||||
void set_selection_origin_line(int p_line, bool p_can_be_hidden = true, int p_wrap_index = -1, int p_caret = 0);
|
||||
void set_selection_origin_column(int p_column, int p_caret = 0);
|
||||
int get_selection_origin_line(int p_caret = 0) const;
|
||||
int get_selection_origin_column(int p_caret = 0) const;
|
||||
|
||||
int get_selection_from_line(int p_caret = 0) const;
|
||||
int get_selection_from_column(int p_caret = 0) const;
|
||||
int get_selection_to_line(int p_caret = 0) const;
|
||||
int get_selection_to_column(int p_caret = 0) const;
|
||||
|
||||
bool is_caret_after_selection_origin(int p_caret = 0) const;
|
||||
|
||||
void deselect(int p_caret = -1);
|
||||
void delete_selection(int p_caret = -1);
|
||||
|
||||
|
|
|
@ -3578,6 +3578,13 @@ bool Viewport::gui_is_drag_successful() const {
|
|||
return gui.drag_successful;
|
||||
}
|
||||
|
||||
void Viewport::gui_cancel_drag() {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
if (gui_is_dragging()) {
|
||||
_perform_drop();
|
||||
}
|
||||
}
|
||||
|
||||
void Viewport::set_input_as_handled() {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
if (!handle_input_locally) {
|
||||
|
|
|
@ -616,6 +616,7 @@ public:
|
|||
|
||||
bool gui_is_dragging() const;
|
||||
bool gui_is_drag_successful() const;
|
||||
void gui_cancel_drag();
|
||||
|
||||
Control *gui_find_control(const Point2 &p_global);
|
||||
|
||||
|
|
Loading…
Reference in New Issue