Browse Source

Merge pull request #54339 from ConteZero/line_edit_drag_and_drop

Rémi Verschelde 3 years ago
parent
commit
78dbe4e3e4

+ 6 - 0
doc/classes/Control.xml

@@ -654,6 +654,12 @@
 				See [method add_theme_stylebox_override].
 			</description>
 		</method>
+		<method name="is_drag_successful" qualifiers="const">
+			<return type="bool" />
+			<description>
+				Returns [code]true[/code] if drag operation is successful.
+			</description>
+		</method>
 		<method name="is_layout_rtl" qualifiers="const">
 			<return type="bool" />
 			<description>

+ 6 - 0
doc/classes/Viewport.xml

@@ -107,6 +107,12 @@
 				Returns the drag data from the GUI, that was previously returned by [method Control._get_drag_data].
 			</description>
 		</method>
+		<method name="gui_is_drag_successful" qualifiers="const">
+			<return type="bool" />
+			<description>
+				Returns [code]true[/code] if the drag operation is successful.
+			</description>
+		</method>
 		<method name="gui_is_dragging" qualifiers="const">
 			<return type="bool" />
 			<description>

+ 5 - 0
scene/gui/control.cpp

@@ -810,6 +810,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();
+}
+
 void Control::_call_gui_input(const Ref<InputEvent> &p_event) {
 	emit_signal(SceneStringNames::get_singleton()->gui_input, p_event); //signal should be first, so it's possible to override an event (and then accept it)
 	if (!is_inside_tree() || get_viewport()->is_input_handled()) {
@@ -2964,6 +2968,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);
 

+ 1 - 0
scene/gui/control.h

@@ -357,6 +357,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;

+ 69 - 11
scene/gui/line_edit.cpp

@@ -268,7 +268,9 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
 				return;
 			}
 
-			shift_selection_check_pre(b->is_shift_pressed());
+			if (b->is_shift_pressed()) {
+				shift_selection_check_pre(true);
+			}
 
 			set_caret_at_pixel_pos(b->get_position().x);
 
@@ -345,6 +347,9 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
 			}
 			selection.creating = false;
 			selection.double_click = false;
+			if (!drag_action) {
+				selection.drag_attempt = false;
+			}
 
 			show_virtual_keyboard();
 		}
@@ -369,6 +374,11 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
 				selection_fill_at_caret();
 			}
 		}
+
+		if (drag_action && can_drop_data(m->get_position(), get_viewport()->gui_get_drag_data())) {
+			drag_caret_force_displayed = true;
+			set_caret_at_pixel_pos(m->get_position().x);
+		}
 	}
 
 	Ref<InputEventKey> k = p_event;
@@ -569,21 +579,44 @@ 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_caret_at_pixel_pos(p_point.x);
+		int caret_column_tmp = caret_column;
+		if (selection.drag_attempt) {
+			selection.drag_attempt = false;
+			if (caret_column < selection.begin || caret_column > selection.end) {
+				if (caret_column_tmp > selection.end) {
+					caret_column_tmp = caret_column_tmp - (selection.end - selection.begin);
+				}
+				selection_delete();
 
-		text = text.left(selection.begin) + text.substr(selection.end);
-		_shape();
-
-		insert_text_at_caret(p_data);
-		selection.begin = caret_column - (selection.end - selection.begin);
-		selection.end = caret_column;
+				set_caret_column(caret_column_tmp);
+				insert_text_at_caret(p_data);
+			}
+		} else if (selection.enabled && caret_column >= selection.begin && caret_column <= selection.end) {
+			caret_column_tmp = selection.begin;
+			selection_delete();
+			set_caret_column(caret_column_tmp);
+			insert_text_at_caret(p_data);
+			grab_focus();
+		} else {
+			insert_text_at_caret(p_data);
+			grab_focus();
+		}
+		select(caret_column_tmp, caret_column);
+		if (!text_changed_dirty) {
+			if (is_inside_tree()) {
+				MessageQueue::get_singleton()->push_call(this, "_text_changed");
+			}
+			text_changed_dirty = true;
+		}
+		update();
 	}
 }
 
@@ -803,7 +836,7 @@ void LineEdit::_notification(int p_what) {
 
 			// Draw carets.
 			ofs.x = x_ofs + scroll_offset;
-			if (draw_caret) {
+			if (draw_caret || drag_caret_force_displayed) {
 				if (ime_text.length() == 0) {
 					// Normal caret.
 					CaretInfo caret = TS->shaped_text_get_carets(text_rid, caret_column);
@@ -921,7 +954,7 @@ void LineEdit::_notification(int p_what) {
 				DisplayServer::get_singleton()->virtual_keyboard_hide();
 			}
 
-			if (deselect_on_focus_loss_enabled) {
+			if (deselect_on_focus_loss_enabled && !selection.drag_attempt) {
 				deselect();
 			}
 		} break;
@@ -935,6 +968,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()) {
+						selection_delete();
+					} else if (deselect_on_focus_loss_enabled) {
+						deselect();
+					}
+				}
+			} else {
+				selection.drag_attempt = false;
+			}
+			drag_action = false;
+			drag_caret_force_displayed = false;
+		} break;
 	}
 }
 
@@ -999,6 +1051,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;
@@ -1020,6 +1075,9 @@ void LineEdit::redo() {
 	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;

+ 3 - 0
scene/gui/line_edit.h

@@ -129,6 +129,9 @@ private:
 
 	bool middle_mouse_paste_enabled = true;
 
+	bool drag_action = false;
+	bool drag_caret_force_displayed = false;
+
 	Ref<Texture2D> right_icon;
 	bool flat = false;
 

+ 9 - 2
scene/main/viewport.cpp

@@ -1515,8 +1515,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
 
 			if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MouseButton::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();
@@ -1534,8 +1535,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
 			_gui_cancel_tooltip();
 		} else {
 			if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MouseButton::LEFT) {
+				gui.drag_successful = false;
 				if (gui.drag_mouse_over) {
-					_gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, false);
+					gui.drag_successful = _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, false);
 				}
 
 				Control *drag_preview = _gui_get_drag_preview();
@@ -2895,6 +2897,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();
 
@@ -3534,6 +3540,7 @@ void Viewport::_bind_methods() {
 
 	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("set_disable_input", "disable"), &Viewport::set_disable_input);
 	ClassDB::bind_method(D_METHOD("is_input_disabled"), &Viewport::is_input_disabled);

+ 2 - 0
scene/main/viewport.h

@@ -348,6 +348,7 @@ private:
 		List<Control *> roots;
 		int canvas_sort_index = 0; //for sorting items with canvas as root
 		bool dragging = false;
+		bool drag_successful = false;
 		bool embed_subwindows_hint = false;
 		bool embedding_subwindows = false;
 
@@ -556,6 +557,7 @@ public:
 	bool is_handling_input_locally() const;
 
 	bool gui_is_dragging() const;
+	bool gui_is_drag_successful() const;
 
 	Control *gui_find_control(const Point2 &p_global);