From a43091d427801f1da8623121ed8b20131993e23e Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 25 Jul 2022 20:32:14 +0200 Subject: [PATCH] Ported: Add drag and drop to TextEdit, LineEdit, RichTextLabel - ConteZero https://github.com/godotengine/godot/commit/4167e980880174ba5f33f6032bae6f17199a18de --- doc/classes/Control.xml | 6 ++ doc/classes/TextEdit.xml | 7 ++ doc/classes/Viewport.xml | 6 ++ scene/gui/control.cpp | 5 + scene/gui/control.h | 1 + scene/gui/line_edit.cpp | 99 ++++++++++++++++---- scene/gui/line_edit.h | 5 +- scene/gui/rich_text_label.cpp | 63 +++++++++++-- scene/gui/rich_text_label.h | 3 + scene/gui/text_edit.cpp | 170 +++++++++++++++++++++++++++++++++- scene/gui/text_edit.h | 10 ++ scene/main/viewport.cpp | 13 ++- scene/main/viewport.h | 2 + 13 files changed, 362 insertions(+), 28 deletions(-) diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index f33eca989..11833c296 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -432,6 +432,12 @@ + + + + Returns [code]true[/code] if drag operation is successful. + + diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index 8dfcf39aa..0f0218da1 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -325,6 +325,13 @@ Returns if the given line is wrapped. + + + + + Returns whether the mouse is over selection. If [code]edges[/code] is [code]true[/code], the edges are considered part of the selection. + + diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index a432496e1..e5cabc39c 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -114,6 +114,12 @@ Returns [code]true[/code] if there are visible modals on-screen. + + + + Returns [code]true[/code] if the drag operation is successful. + + diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 0329fdfd1..5829f5fd8 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -772,6 +772,10 @@ void Control::set_drag_preview(Control *p_control) { get_viewport()->_gui_set_drag_preview(this, p_control); } +bool Control::is_drag_successful() const { + return is_inside_tree() && get_viewport()->gui_is_drag_successful(); +} + bool Control::is_window_modal_on_top() const { if (!is_inside_tree()) { return false; @@ -2759,6 +2763,7 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("set_drag_forwarding", "target"), &Control::set_drag_forwarding); ClassDB::bind_method(D_METHOD("set_drag_preview", "control"), &Control::set_drag_preview); + ClassDB::bind_method(D_METHOD("is_drag_successful"), &Control::is_drag_successful); ClassDB::bind_method(D_METHOD("warp_mouse", "to_position"), &Control::warp_mouse); diff --git a/scene/gui/control.h b/scene/gui/control.h index 9cd8855b5..dadf44ef6 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -318,6 +318,7 @@ public: virtual void drop_data(const Point2 &p_point, const Variant &p_data); void set_drag_preview(Control *p_control); void force_drag(const Variant &p_data, Control *p_control); + bool is_drag_successful() const; void set_custom_minimum_size(const Size2 &p_custom); Size2 get_custom_minimum_size() const; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 99ddad3a4..d6b599ed7 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -31,6 +31,7 @@ #include "line_edit.h" #include "core/message_queue.h" +#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/print_string.h" @@ -39,6 +40,7 @@ #include "scene/gui/popup_menu.h" #include "scene/gui/shortcut.h" #include "scene/main/timer.h" +#include "scene/main/viewport.h" #ifdef TOOLS_ENABLED #include "editor/editor_scale.h" @@ -83,7 +85,9 @@ void LineEdit::_gui_input(Ref p_event) { return; } - shift_selection_check_pre(b->get_shift()); + if (b->get_shift()) { + shift_selection_check_pre(true); + } set_cursor_at_pixel_pos(b->get_position().x); @@ -128,7 +132,7 @@ void LineEdit::_gui_input(Ref p_event) { deselect(); selection.cursor_start = cursor_pos; selection.creating = true; - } else if (selection.enabled) { + } else if (selection.enabled && !selection.doubleclick) { selection.drag_attempt = true; } } @@ -151,6 +155,10 @@ void LineEdit::_gui_input(Ref p_event) { selection.creating = false; selection.doubleclick = false; + if (!drag_action) { + selection.drag_attempt = false; + } + show_virtual_keyboard(); } @@ -174,6 +182,11 @@ void LineEdit::_gui_input(Ref p_event) { selection_fill_at_cursor(); } } + + if (drag_action && can_drop_data(m->get_position(), get_viewport()->gui_get_drag_data())) { + drag_caret_force_displayed = true; + set_cursor_at_pixel_pos(m->get_position().x); + } } Ref k = p_event; @@ -647,28 +660,50 @@ bool LineEdit::can_drop_data(const Point2 &p_point, const Variant &p_data) const return drop_override; } - return p_data.get_type() == Variant::STRING; + return is_editable() && p_data.get_type() == Variant::STRING; } void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) { Control::drop_data(p_point, p_data); - if (p_data.get_type() == Variant::STRING) { + if (p_data.get_type() == Variant::STRING && is_editable()) { set_cursor_at_pixel_pos(p_point.x); - int selected = selection.end - selection.begin; - - Ref font = get_font("font"); - if (font != nullptr) { - for (int i = selection.begin; i < selection.end; i++) { - cached_width -= font->get_char_size(pass ? secret_character[0] : text[i]).width; - } + int caret_column_tmp = cursor_pos; + bool is_inside_sel = selection.enabled && cursor_pos >= selection.begin && cursor_pos <= selection.end; + if (Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { + is_inside_sel = selection.enabled && cursor_pos > selection.begin && cursor_pos < selection.end; } + if (selection.drag_attempt) { + selection.drag_attempt = false; + if (!is_inside_sel) { + if (!Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { + if (caret_column_tmp > selection.end) { + caret_column_tmp = caret_column_tmp - (selection.end - selection.begin); + } + selection_delete(); + } - text.erase(selection.begin, selected); - - append_at_cursor(p_data); - selection.begin = cursor_pos - selected; - selection.end = cursor_pos; + set_cursor_position(caret_column_tmp); + append_at_cursor(p_data); + } + } else if (selection.enabled && cursor_pos >= selection.begin && cursor_pos <= selection.end) { + caret_column_tmp = selection.begin; + selection_delete(); + set_cursor_position(caret_column_tmp); + append_at_cursor(p_data); + grab_focus(); + } else { + append_at_cursor(p_data); + grab_focus(); + } + select(caret_column_tmp, cursor_pos); + if (!text_changed_dirty) { + if (is_inside_tree()) { + MessageQueue::get_singleton()->push_call(this, "_text_changed"); + } + text_changed_dirty = true; + } + update(); } } @@ -924,7 +959,7 @@ void LineEdit::_notification(int p_what) { } } - if ((char_ofs == cursor_pos || using_placeholder) && draw_caret) { // May be at the end, or placeholder. + if ((char_ofs == cursor_pos || using_placeholder || drag_caret_force_displayed) && draw_caret) { // May be at the end, or placeholder. if (ime_text.length() == 0) { int caret_x_ofs = x_ofs; if (using_placeholder) { @@ -996,6 +1031,25 @@ void LineEdit::_notification(int p_what) { update(); } } break; + case Control::NOTIFICATION_DRAG_BEGIN: { + drag_action = true; + } break; + case Control::NOTIFICATION_DRAG_END: { + if (is_drag_successful()) { + if (selection.drag_attempt) { + selection.drag_attempt = false; + if (is_editable() && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { + selection_delete(); + // } else if (deselect_on_focus_loss_enabled) { + // deselect(); + } + } + } else { + selection.drag_attempt = false; + } + drag_action = false; + drag_caret_force_displayed = false; + } break; } } @@ -1052,6 +1106,9 @@ void LineEdit::undo() { } else if (undo_stack_pos == undo_stack.front()) { return; } + + deselect(); + undo_stack_pos = undo_stack_pos->prev(); TextOperation op = undo_stack_pos->get(); text = op.text; @@ -1070,9 +1127,13 @@ void LineEdit::redo() { if (undo_stack_pos == nullptr) { return; } + if (undo_stack_pos == undo_stack.back()) { return; } + + deselect(); + undo_stack_pos = undo_stack_pos->next(); TextOperation op = undo_stack_pos->get(); text = op.text; @@ -1988,6 +2049,10 @@ LineEdit::LineEdit() { selecting_enabled = true; deselect_on_focus_loss_enabled = true; popup_show = false; + virtual_keyboard_enabled = true; + + drag_action = false; + drag_caret_force_displayed = false; undo_stack_pos = nullptr; _create_undo_state(); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index c9b3cdc22..062f4fc63 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -92,7 +92,10 @@ private: bool shortcut_keys_enabled; - bool virtual_keyboard_enabled = true; + bool virtual_keyboard_enabled; + + bool drag_action; + bool drag_caret_force_displayed; Ref right_icon; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 1cdb1c75b..fcc912155 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -33,6 +33,7 @@ #include "core/math/math_defs.h" #include "core/os/keyboard.h" #include "core/os/os.h" +#include "label.h" #include "scene/gui/scroll_bar.h" #include "scene/scene_string_names.h" @@ -1071,6 +1072,9 @@ void RichTextLabel::_notification(int p_what) { update(); } } break; + case Control::NOTIFICATION_DRAG_END: { + selection.drag_attempt = false; + } break; } } @@ -1153,6 +1157,9 @@ void RichTextLabel::_gui_input(Ref p_event) { Item *item = nullptr; bool outside; + + selection.drag_attempt = false; + _find_click(main, b->get_position(), &item, &line, &outside); if (item) { @@ -1162,13 +1169,18 @@ void RichTextLabel::_gui_input(Ref p_event) { // Erase previous selection. if (selection.active) { - selection.from = nullptr; - selection.from_char = '\0'; - selection.to = nullptr; - selection.to_char = '\0'; - selection.active = false; + if (_is_click_inside_selection()) { + selection.drag_attempt = true; + selection.click = nullptr; + } else { + selection.from = nullptr; + selection.from_char = '\0'; + selection.to = nullptr; + selection.to_char = '\0'; + selection.active = false; - update(); + update(); + } } } } @@ -1178,6 +1190,8 @@ void RichTextLabel::_gui_input(Ref p_event) { Item *item = nullptr; bool outside; + selection.drag_attempt = false; + _find_click(main, b->get_position(), &item, &line, &outside); while (item && item->type != ITEM_TEXT) { @@ -1198,6 +1212,26 @@ void RichTextLabel::_gui_input(Ref p_event) { } } } else if (!b->is_pressed()) { + if (selection.drag_attempt) { + selection.drag_attempt = false; + int line = 0; + Item *item = nullptr; + bool outside; + _find_click(main, b->get_position(), &item, &line, &outside); + selection.click = item; + selection.click_char = line; + if (_is_click_inside_selection()) { + selection.active = false; + selection.from = nullptr; + selection.from_char = '\0'; + selection.to = nullptr; + selection.to_char = '\0'; + selection.active = false; + + update(); + } + } + selection.click = nullptr; if (!b->is_doubleclick() && !scroll_updated) { @@ -2515,6 +2549,22 @@ void RichTextLabel::set_deselect_on_focus_loss_enabled(const bool p_enabled) { } } +Variant RichTextLabel::get_drag_data(const Point2 &p_point) { + if (selection.drag_attempt && selection.enabled) { + String t = get_selected_text(); + Label *l = memnew(Label); + l->set_text(t); + set_drag_preview(l); + return t; + } + + return Variant(); +} + +bool RichTextLabel::_is_click_inside_selection() const { + return (selection.click->index >= selection.from->index && selection.click->index <= selection.to->index && (selection.click->index > selection.from->index || selection.click_char >= selection.from_char) && (selection.click->index < selection.to->index || selection.click_char <= selection.to_char)); +} + bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p_search_previous) { ERR_FAIL_COND_V(!selection.enabled, false); Item *it = main; @@ -3033,6 +3083,7 @@ RichTextLabel::RichTextLabel() { selection.click = nullptr; selection.active = false; selection.enabled = false; + selection.drag_attempt = false; deselect_on_focus_loss_enabled = true; visible_characters = -1; diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 42d62baec..2655fb1ec 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -360,6 +360,7 @@ private: bool active; // anything selected? i.e. from, to, etc. valid? bool enabled; // allow selections? + bool drag_attempt; }; Selection selection; @@ -368,6 +369,7 @@ private: int visible_characters; float percent_visible; + bool _is_click_inside_selection() const; int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos = Point2i(), Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr, int p_char_count = 0); void _find_click(ItemFrame *p_frame, const Point2i &p_click, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr); @@ -468,6 +470,7 @@ public: VScrollBar *get_v_scroll() { return vscroll; } virtual CursorShape get_cursor_shape(const Point2 &p_pos) const; + virtual Variant get_drag_data(const Point2 &p_point); void set_selection_enabled(bool p_enabled); bool is_selection_enabled() const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 77c47cc59..7db037188 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -36,6 +36,7 @@ #include "core/os/os.h" #include "core/project_settings.h" #include "core/script_language.h" +#include "label.h" #include "scene/gui/popup_menu.h" #include "scene/gui/scroll_bar.h" #include "scene/gui/shortcut.h" @@ -436,6 +437,7 @@ void TextEdit::_click_selection_held() { } void TextEdit::_update_selection_mode_pointer() { + selection.drag_attempt = false; dragging_selection = true; Point2 mp = get_local_mouse_position(); @@ -452,6 +454,7 @@ void TextEdit::_update_selection_mode_pointer() { } void TextEdit::_update_selection_mode_word() { + selection.drag_attempt = false; dragging_selection = true; Point2 mp = get_local_mouse_position(); @@ -509,6 +512,7 @@ void TextEdit::_update_selection_mode_word() { } void TextEdit::_update_selection_mode_line() { + selection.drag_attempt = false; dragging_selection = true; Point2 mp = get_local_mouse_position(); @@ -1508,7 +1512,7 @@ void TextEdit::_notification(int p_what) { } } if (ime_text.length() == 0) { - if (draw_caret) { + if (draw_caret || drag_caret_force_displayed) { if (insert_mode) { #ifdef TOOLS_ENABLED int caret_h = (block_caret) ? 4 : 2 * EDSCALE; @@ -1614,7 +1618,7 @@ void TextEdit::_notification(int p_what) { } } if (ime_text.length() == 0) { - if (draw_caret) { + if (draw_caret || drag_caret_force_displayed) { if (insert_mode) { int char_w = cache.font->get_char_size(' ').width; #ifdef TOOLS_ENABLED @@ -1905,6 +1909,37 @@ void TextEdit::_notification(int p_what) { deselect(); } } break; + case Control::NOTIFICATION_DRAG_BEGIN: { + selection.selecting_mode = Selection::MODE_NONE; + drag_action = true; + dragging_minimap = false; + dragging_selection = false; + can_drag_minimap = false; + click_select_held->stop(); + } break; + case Control::NOTIFICATION_DRAG_END: { + if (is_drag_successful()) { + if (selection.drag_attempt) { + selection.drag_attempt = false; + if (!readonly && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { + _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); + cursor_set_line(selection.from_line, false); + cursor_set_column(selection.from_column); + selection.active = false; + selection.selecting_mode = Selection::MODE_NONE; + update(); + } + } + } else { + selection.drag_attempt = false; + } + drag_action = false; + drag_caret_force_displayed = false; + dragging_minimap = false; + dragging_selection = false; + can_drag_minimap = false; + click_select_held->stop(); + } break; } } @@ -2498,6 +2533,7 @@ void TextEdit::_gui_input(const Ref &p_gui_input) { cursor_set_line(row, false, false); cursor_set_column(col); + selection.drag_attempt = false; if (mb->get_shift() && (cursor.column != prev_col || cursor.line != prev_line)) { if (!selection.active) { @@ -2544,6 +2580,9 @@ void TextEdit::_gui_input(const Ref &p_gui_input) { update(); } + } else if (is_mouse_over_selection()) { + selection.selecting_mode = Selection::MODE_NONE; + selection.drag_attempt = true; } else { selection.active = false; selection.selecting_mode = Selection::MODE_POINTER; @@ -2607,6 +2646,10 @@ void TextEdit::_gui_input(const Ref &p_gui_input) { } } else { if (mb->get_button_index() == BUTTON_LEFT) { + if (selection.drag_attempt && selection.selecting_mode == Selection::MODE_NONE && is_mouse_over_selection()) { + selection.active = false; + } + if (mb->get_command() && highlighted_word != String()) { int row, col; _get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col); @@ -2619,6 +2662,10 @@ void TextEdit::_gui_input(const Ref &p_gui_input) { dragging_selection = false; can_drag_minimap = false; click_select_held->stop(); + + if (!drag_action) { + selection.drag_attempt = false; + } } // Notify to show soft keyboard. @@ -2634,6 +2681,7 @@ void TextEdit::_gui_input(const Ref &p_gui_input) { } else { _scroll_down(delta); } + h_scroll->set_value(h_scroll->get_value() + pan_gesture->get_delta().x * 100); if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) { accept_event(); // Accept event if scroll changed. @@ -2688,6 +2736,22 @@ void TextEdit::_gui_input(const Ref &p_gui_input) { } } } + + if (drag_action && can_drop_data(mm->get_position(), get_viewport()->gui_get_drag_data())) { + drag_caret_force_displayed = true; + Point2 mp = get_local_mouse_position(); + int row, col; + _get_mouse_pos(Point2i(mp.x, mp.y), row, col); + cursor_set_line(row, true); + cursor_set_column(col); + if (row <= get_first_visible_line()) { + _scroll_lines_up(); + } else if (row >= get_last_full_visible_line()) { + _scroll_lines_down(); + } + dragging_selection = true; + update(); + } } if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) { @@ -4981,6 +5045,104 @@ void TextEdit::insert_text_at_cursor(const String &p_text) { update(); } +Variant TextEdit::get_drag_data(const Point2 &p_point) { + if (selection.active && selection.drag_attempt) { + String t = get_selection_text(); + Label *l = memnew(Label); + l->set_text(t); + set_drag_preview(l); + return t; + } + + return Variant(); +} + +bool TextEdit::can_drop_data(const Point2 &p_point, const Variant &p_data) const { + bool drop_override = Control::can_drop_data(p_point, p_data); // In case user wants to drop custom data. + if (drop_override) { + return drop_override; + } + + return !readonly && p_data.get_type() == Variant::STRING; +} + +void TextEdit::drop_data(const Point2 &p_point, const Variant &p_data) { + Control::drop_data(p_point, p_data); + + if (p_data.get_type() == Variant::STRING && !readonly) { + Point2 mp = get_local_mouse_position(); + int caret_row_tmp, caret_column_tmp; + _get_mouse_pos(Point2i(mp.x, mp.y), caret_row_tmp, caret_column_tmp); + if (selection.drag_attempt) { + selection.drag_attempt = false; + if (!is_mouse_over_selection(!Input::get_singleton()->is_key_pressed(KEY_CONTROL))) { + begin_complex_operation(); + if (!Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { + if (caret_row_tmp > selection.to_line) { + caret_row_tmp = caret_row_tmp - (selection.to_line - selection.from_line); + } else if (caret_row_tmp == selection.to_line && caret_column_tmp >= selection.to_column) { + caret_column_tmp = caret_column_tmp - (selection.to_column - selection.from_column); + } + + _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); + cursor_set_line(selection.from_line, false); + cursor_set_column(selection.from_column); + selection.active = false; + selection.selecting_mode = Selection::MODE_NONE; + } else { + deselect(); + } + + cursor_set_line(caret_row_tmp, true, false); + cursor_set_column(caret_column_tmp); + insert_text_at_cursor(p_data); + end_complex_operation(); + } + } else if (is_mouse_over_selection()) { + begin_complex_operation(); + caret_row_tmp = selection.from_line; + caret_column_tmp = selection.from_column; + + _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); + cursor_set_line(selection.from_line, false); + cursor_set_column(selection.from_column); + selection.active = false; + selection.selecting_mode = Selection::MODE_NONE; + + cursor_set_line(caret_row_tmp, true, false); + cursor_set_column(caret_column_tmp); + insert_text_at_cursor(p_data); + end_complex_operation(); + grab_focus(); + } else { + deselect(); + cursor_set_line(caret_row_tmp, true, false); + cursor_set_column(caret_column_tmp); + insert_text_at_cursor(p_data); + grab_focus(); + } + + if (caret_row_tmp != cursor.line || caret_column_tmp != cursor.column) { + select(caret_row_tmp, caret_column_tmp, cursor.line, cursor.column); + } + } +} + +bool TextEdit::is_mouse_over_selection(bool p_edges) const { + if (!selection.active) { + return false; + } + Point2 mp = get_local_mouse_position(); + int row, col; + _get_mouse_pos(Point2i(mp.x, mp.y), row, col); + if (p_edges) { + if ((row == selection.from_line && col == selection.from_column) || (row == selection.to_line && col == selection.to_column)) { + return true; + } + } + return (row >= selection.from_line && row <= selection.to_line && (row > selection.from_line || col > selection.from_column) && (row < selection.to_line || col < selection.to_column)); +} + Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { if (highlighted_word != String()) { return CURSOR_POINTING_HAND; @@ -7305,6 +7467,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_selection_to_line"), &TextEdit::get_selection_to_line); ClassDB::bind_method(D_METHOD("get_selection_to_column"), &TextEdit::get_selection_to_column); ClassDB::bind_method(D_METHOD("get_selection_text"), &TextEdit::get_selection_text); + ClassDB::bind_method(D_METHOD("is_mouse_over_selection", "edges"), &TextEdit::is_mouse_over_selection); ClassDB::bind_method(D_METHOD("get_word_under_cursor"), &TextEdit::get_word_under_cursor); ClassDB::bind_method(D_METHOD("search", "key", "flags", "from_line", "from_column"), &TextEdit::_search_bind); @@ -7459,6 +7622,9 @@ TextEdit::TextEdit() { cache.info_gutter_width = 0; set_default_cursor_shape(CURSOR_IBEAM); + drag_action = false; + drag_caret_force_displayed = false; + indent_size = 4; text.set_indent_size(indent_size); text.clear(); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 24ee62b1e..d124eba6a 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -192,6 +192,8 @@ private: int to_line, to_column; bool shiftclick_left; + bool drag_attempt; + Selection() { selecting_mode = MODE_NONE; selecting_line = 0; @@ -206,6 +208,7 @@ private: to_line = 0; to_column = 0; shiftclick_left = false; + drag_attempt = false; } } selection; @@ -475,6 +478,9 @@ private: int get_char_pos_for(int p_px, String p_str) const; int get_column_x_offset(int p_char, String p_str) const; + bool drag_action; + bool drag_caret_force_displayed; + void adjust_viewport_to_cursor(); void _scroll_moved(double); void _update_scrollbars(); @@ -589,6 +595,9 @@ public: }; virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const; + virtual Variant get_drag_data(const Point2 &p_point); + virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const; + virtual void drop_data(const Point2 &p_point, const Variant &p_data); void _get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const; void _get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const; @@ -725,6 +734,7 @@ public: int get_selection_to_line() const; int get_selection_to_column() const; String get_selection_text() const; + bool is_mouse_over_selection(bool p_edges = true) const; String get_word_under_cursor() const; String get_word_at_pos(const Vector2 &p_pos) const; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 354a536d9..f0fc08cf7 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -186,6 +186,7 @@ public: Viewport::GUI::GUI() { dragging = false; + drag_successful = false; mouse_focus = nullptr; mouse_click_grabber = nullptr; mouse_focus_mask = 0; @@ -1975,8 +1976,10 @@ void Viewport::_gui_input_event(Ref p_event) { if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == BUTTON_LEFT) { //alternate drop use (when using force_drag(), as proposed by #5342 + gui.drag_successful = false; + if (gui.mouse_focus) { - _gui_drop(gui.mouse_focus, pos, false); + gui.drag_successful = _gui_drop(gui.mouse_focus, pos, false); } gui.drag_data = Variant(); @@ -1994,11 +1997,12 @@ void Viewport::_gui_input_event(Ref p_event) { _gui_cancel_tooltip(); } else { if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == BUTTON_LEFT) { + gui.drag_successful = false; if (gui.mouse_over) { Size2 pos = mpos; pos = gui.focus_inv_xform.xform(pos); - _gui_drop(gui.mouse_over, pos, false); + gui.drag_successful = _gui_drop(gui.mouse_over, pos, false); } Control *drag_preview = _gui_get_drag_preview(); @@ -3135,6 +3139,10 @@ bool Viewport::gui_is_dragging() const { return gui.dragging; } +bool Viewport::gui_is_drag_successful() const { + return gui.drag_successful; +} + void Viewport::set_input_as_handled() { _drop_physics_mouseover(); if (handle_input_locally) { @@ -3265,6 +3273,7 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("gui_has_modal_stack"), &Viewport::gui_has_modal_stack); ClassDB::bind_method(D_METHOD("gui_get_drag_data"), &Viewport::gui_get_drag_data); ClassDB::bind_method(D_METHOD("gui_is_dragging"), &Viewport::gui_is_dragging); + ClassDB::bind_method(D_METHOD("gui_is_drag_successful"), &Viewport::gui_is_drag_successful); ClassDB::bind_method(D_METHOD("get_modal_stack_top"), &Viewport::get_modal_stack_top); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index c9b74c922..59aceb4b6 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -322,6 +322,7 @@ private: List roots; int canvas_sort_index; //for sorting items with canvas as root bool dragging; + bool drag_successful; GUI(); } gui; @@ -579,6 +580,7 @@ public: bool is_handling_input_locally() const; bool gui_is_dragging() const; + bool gui_is_drag_successful() const; Viewport(); ~Viewport();