Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix drag and drop on LineEdit #54339

Merged
merged 1 commit into from
Nov 22, 2021

Conversation

ConteZero
Copy link
Contributor

@ConteZero ConteZero commented Oct 28, 2021

LineEdit contains the code to handle drag and drop but it never gets executed, I don't know if it's excluded on purpose or it's a bug.
I have improved the existing code to make it possible to cut and paste the selection (the current code only makes a copy).
I don't know if this is the best way to implement this functionality, but from my tests it works and with some tweaks it could be used for the TextEdit node as well.

Bugsquad edit: Fixes #54744.

@ConteZero ConteZero requested review from a team as code owners October 28, 2021 07:14
@akien-mga akien-mga added this to the 4.0 milestone Oct 28, 2021
@ConteZero ConteZero force-pushed the line_edit_drag_and_drop branch from ea7742a to 696583c Compare October 30, 2021 13:29
@KoBeWi
Copy link
Member

KoBeWi commented Oct 31, 2021

Nice feature, I'd definitely like to see it in TextEdit too.

Dropping doesn't properly support undo/redo though:
avYh133jW5

@ConteZero ConteZero force-pushed the line_edit_drag_and_drop branch from 696583c to 9593d15 Compare October 31, 2021 18:07
@ConteZero
Copy link
Contributor Author

Nice feature, I'd definitely like to see it in TextEdit too.

Dropping doesn't properly support undo/redo though

Yes, you are right, I force-pushed a change that should fix undo/redo.

scene/gui/line_edit.cpp Outdated Show resolved Hide resolved
scene/gui/line_edit.cpp Outdated Show resolved Hide resolved
@ConteZero ConteZero force-pushed the line_edit_drag_and_drop branch from 9593d15 to 7a04245 Compare October 31, 2021 20:59
scene/gui/line_edit.cpp Outdated Show resolved Hide resolved
scene/gui/line_edit.cpp Outdated Show resolved Hide resolved
Copy link
Member

@KoBeWi KoBeWi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems ok, but core changes need additional review.

btw, the unreported issue fixed by this PR is actually a regression, because drag and drop works in 3.x (except undo/redo is broken).

@ConteZero ConteZero force-pushed the line_edit_drag_and_drop branch from 7a04245 to b9c65aa Compare November 1, 2021 08:41
@ConteZero
Copy link
Contributor Author

Seems ok, but core changes need additional review.

btw, the unreported issue fixed by this PR is actually a regression, because drag and drop works in 3.x (except undo/redo is broken).

Are you sure? I've tested in 3.3.4 and drag and drop in lineedit does not work for me, and this is confirmed by looking at the code, the shift_selection_check_pre that causes the deselect is still there so the condition

} else if (selection.enabled) {
	selection.drag_attempt = true;
}

is never executed.

@ConteZero
Copy link
Contributor Author

I've added some lines of code to LineEdit::drop_data to handle the case where deselect_on_focus_loss_enabled is false.
I've tried to mimic the behavior of classic cut and paste, so if you drag the selection in a different lineedit that have already a text selected, then the grabbed text will replace the one in the existing selection.

@KoBeWi
Copy link
Member

KoBeWi commented Nov 1, 2021

Are you sure?

8SgLrJpUbz
3.4 rc1

@ConteZero
Copy link
Contributor Author

Well, I think we can call it the "Schrödinger's Drag and Drop", it works and doesn't work at the same time.
If you do a double or triple click and you keep the left mouse button pressed on your last click then you can grab the selection.
If you don not keep the left mouse button pressed or if you try to only select some letters of a word than the grab operation does not start.
In your example try to drag only a part of the word "test", like "st" for example.

Comment on lines 979 to 980
delete_text(selection.begin, selection.end);
deselect();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can call selection_delete()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines +271 to +273
if (b->is_shift_pressed()) {
shift_selection_check_pre(true);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth checking drag_action inside shift_selection_check_pre so we don't have to special case it here?

Copy link
Contributor Author

@ConteZero ConteZero Nov 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't moving the control inside shift_selection_check_pre cause unwanted side effects?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't do as this is the only call that is used for the mouse, though that does raise a good point, we should probably prevent any another input whilst dragging text.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, preventing other input while dragging will avoid unwanted behavior, but I believe this needs to be done in another pull request because it should be done in a generic way, not limited to just Lineedit.

selection.begin = caret_column - selected;
selection.end = caret_column;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems as though the old code tried to persevere the selection, should this behaviour be kept?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've restored the previous behavior and I've also fixed a bug in the undo/redo code by adding a deselect()

set_caret_column(caret_column_tmp);
insert_text_at_caret(p_data);
}
} else if (!deselect_on_focus_loss_enabled && selection.enabled && caret_column >= selection.begin && caret_column <= selection.end) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't selection.enabled tell us if deselect_on_focus_loss_enabled is enabled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@ConteZero ConteZero force-pushed the line_edit_drag_and_drop branch from b9c65aa to 9765e33 Compare November 3, 2021 21:49
Comment on lines 602 to 603
delete_text(selection.begin, selection.end);
deselect();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can call selection_delete.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines 594 to 595
delete_text(selection.begin, selection.end);
deselect();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can call selection_delete.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines 612 to 616
if (selecting_enabled) {
selection.enabled = true;
selection.begin = caret_column_tmp;
selection.end = caret_column;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can call select()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

insert_text_at_caret(p_data);
grab_focus();
} else {
deselect();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can remove this call to deselect as we're immediately selecting afterwords.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines +271 to +273
if (b->is_shift_pressed()) {
shift_selection_check_pre(true);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't do as this is the only call that is used for the mouse, though that does raise a good point, we should probably prevent any another input whilst dragging text.

@ConteZero ConteZero force-pushed the line_edit_drag_and_drop branch from 9765e33 to 272b48a Compare November 8, 2021 07:59
@akien-mga
Copy link
Member

Be sure to check how it behaves with non editable LineEdits too (see #54744).

@ConteZero
Copy link
Contributor Author

Be sure to check how it behaves with non editable LineEdits too (see #54744).

#54744 should be fixed by this this commit.
If this pull request will be accepted my plan is to do a backport to 3.x

@akien-mga akien-mga requested a review from Paulb23 November 8, 2021 23:15
Copy link
Member

@Paulb23 Paulb23 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line edit changes look okay.

However, I'm not sure on the new NOTIFICATIONs, kind of makes NOTIFICATION_DRAG_END redundant. We can also handle quite easily inside of LineEdit rather then pushing up to viewport, though I can see the use case for it.

Would need some other opinions on this.

}

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 (is_editable() && p_data.get_type() == Variant::STRING) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Can invert this.

@akien-mga
Copy link
Member

However, I'm not sure on the new NOTIFICATIONs, kind of makes NOTIFICATION_DRAG_END redundant. We can also handle quite easily inside of LineEdit rather then pushing up to viewport, though I can see the use case for it.

Indeed, I don't see much added value for those notifications, and changing the core like this requires thorougher design review than only ad-hoc LineEdit changes. See https://docs.godotengine.org/en/latest/community/contributing/best_practices_for_engine_contributors.html#prefer-local-solutions

The two new notifications only serve to say "drag end with success" or "drag end with failure", and success or failure is determined by a method implemented in LineEdit. So LineEdit already knows if it's drag is a success or a failure, and can pass this information (e.g. via a member variable) to the existing NOTIFICATION_DRAG_END notification. All the Viewport changes seem unnecessary here.

@ConteZero
Copy link
Contributor Author

The two new notifications only serve to say "drag end with success" or "drag end with failure", and success or failure is determined by a method implemented in LineEdit. So LineEdit already knows if it's drag is a success or a failure, and can pass this information (e.g. via a member variable) to the existing NOTIFICATION_DRAG_END notification. All the Viewport changes seem unnecessary here.

I've not understood what you are suggesting when you talk about a member variable, you mean a member variable of Lineedit?
If this is the case, it will not work when you want to drag and drop from different nodes, as example from two Lineedit nodes, or from Lineedit and Textedit.
Currently the node where the drag operation starts do not know if at the end of the drag the drop is successful or not, this is only known from the node where the drop happens (or from the Viewport that handle the drag and drop at a higher level).
Without this information it is impossible to react accordingly, in case of Lineedit you do not know if you can delete the selected text or not.
Perhaps I'm missing something obvious, but I've not found a way to get this info without passing from Viewport.

@ConteZero
Copy link
Contributor Author

However, I'm not sure on the new NOTIFICATIONs, kind of makes NOTIFICATION_DRAG_END redundant. We can also handle quite easily inside of LineEdit rather then pushing up to viewport, though I can see the use case for it.

As I've tried to explain in the previous comment I don't think it is possible to handle the drag and drop operation without using Viewport.

@ConteZero
Copy link
Contributor Author

I'll try to make it clearer with an example, suppose we have two LineEdit nodes

LineEdit_A contains textA
LineEdit_B contains textB

This is what happens during a drag and drop operation (only relevant method calls are shown):

# select "textA" on LineEdit_A and press Left Mouse Button
LineEdit_A --> selection.drag_attempt = true

# start Mouse Move
Viewport::_gui_input_event
LineEdit_A --> LineEdit::get_drag_data

# move mouse on LineEdit_B and release Left Mouse Button with caret at the end of "textB"
Viewport::_gui_input_event
Viewport::_gui_drop
LineEdit_B --> LineEdit::can_drop_data --> LineEdit::drop_data

# now LineEdit_B contains "textBtextA" but LineEdit_A do not know what to do until it receives the NOTIFICATION_DROP_DATA_SUCCESS
# LineEdit_A only know that the drag operation is started (selection.drag_attempt variable)
# if we send NOTIFICATION_DROP_DATA_SUCCESS from Viewport
# then LineEdit_A can delete "textA" and the drag and drop operation is completed

@ConteZero ConteZero force-pushed the line_edit_drag_and_drop branch from 272b48a to a70834e Compare November 10, 2021 14:36
@Paulb23
Copy link
Member

Paulb23 commented Nov 10, 2021

An alternative solution, which already has a precedent in Viewport is gui.dragging, with a getter gui_is_dragging.

So we could add gui.drag_successful with a getter gui_drag_successful(), and a wrapper in Control to call get_viewport()->gui_drag_successful(). Then check that in LineEdit on NOTIFICATION_DRAG_END.

This way we don't have to touch notifications.

@ConteZero
Copy link
Contributor Author

An alternative solution, which already has a precedent in Viewport is gui.dragging, with a getter gui_is_dragging.

So we could add gui.drag_successful with a getter gui_drag_successful(), and a wrapper in Control to call get_viewport()->gui_drag_successful(). Then check that in LineEdit on NOTIFICATION_DRAG_END.

This way we don't have to touch notifications.

At first I thought about using a getter, but then I chose to create the two new notifications because it seemed simpler to implement and easier for users to understand.

If you think that the getter is a better choice I can modify the code to use it instead of notifications, in the end the thing that interests me most is to have a drag and drop functionality that works (especially for the 3.x branch which is what I use for my projects).

@ConteZero ConteZero force-pushed the line_edit_drag_and_drop branch from a70834e to 8d86ad9 Compare November 15, 2021 11:12
@ConteZero
Copy link
Contributor Author

@Paulb23 I've removed the notifications and added the getter as you suggested

@ConteZero ConteZero force-pushed the line_edit_drag_and_drop branch 2 times, most recently from 413915f to d1b2c75 Compare November 15, 2021 13:14
@ConteZero ConteZero force-pushed the line_edit_drag_and_drop branch from d1b2c75 to 2b1787b Compare November 15, 2021 16:11
Copy link
Member

@akien-mga akien-mga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me as far as Control/Viewport changes are concerned.

@akien-mga akien-mga requested a review from Paulb23 November 18, 2021 12:24
Copy link
Member

@Paulb23 Paulb23 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@akien-mga akien-mga merged commit 78dbe4e into godotengine:master Nov 22, 2021
@akien-mga
Copy link
Member

Thanks!

@ConteZero ConteZero deleted the line_edit_drag_and_drop branch March 15, 2022 08:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

LineEdit.editable = false can be edited with double click.
4 participants