Skip to content

Commit

Permalink
Add drag and drop to TextEdit, LineEdit, RichTextLabel
Browse files Browse the repository at this point in the history
  • Loading branch information
ConteZero committed Mar 14, 2022
1 parent 4f58ec3 commit 4167e98
Show file tree
Hide file tree
Showing 13 changed files with 347 additions and 28 deletions.
6 changes: 6 additions & 0 deletions doc/classes/Control.xml
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,12 @@
See [method add_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="minimum_size_changed">
<return type="void" />
<description>
Expand Down
7 changes: 7 additions & 0 deletions doc/classes/TextEdit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,13 @@
Returns if the given line is wrapped.
</description>
</method>
<method name="is_mouse_over_selection" qualifiers="const">
<return type="bool" />
<argument index="0" name="edges" type="bool" />
<description>
Returns whether the mouse is over selection. If [code]edges[/code] is [code]true[/code], the edges are considered part of the selection.
</description>
</method>
<method name="is_selection_active" qualifiers="const">
<return type="bool" />
<description>
Expand Down
6 changes: 6 additions & 0 deletions doc/classes/Viewport.xml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@
Returns [code]true[/code] if there are visible modals on-screen.
</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>
Expand Down
5 changes: 5 additions & 0 deletions scene/gui/control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,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;
Expand Down Expand Up @@ -2852,6 +2856,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);

Expand Down
1 change: 1 addition & 0 deletions scene/gui/control.h
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ class Control : public CanvasItem {
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;
Expand Down
91 changes: 75 additions & 16 deletions scene/gui/line_edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@
#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"
#include "core/translation.h"
#include "label.h"
#include "scene/main/viewport.h"

#ifdef TOOLS_ENABLED
#include "editor/editor_scale.h"
Expand Down Expand Up @@ -79,7 +81,9 @@ void LineEdit::_gui_input(Ref<InputEvent> 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);

Expand Down Expand Up @@ -124,7 +128,7 @@ void LineEdit::_gui_input(Ref<InputEvent> 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;
}
}
Expand All @@ -146,6 +150,9 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
}
selection.creating = false;
selection.doubleclick = false;
if (!drag_action) {
selection.drag_attempt = false;
}

show_virtual_keyboard();
}
Expand All @@ -170,6 +177,11 @@ void LineEdit::_gui_input(Ref<InputEvent> 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<InputEventKey> k = p_event;
Expand Down Expand Up @@ -641,28 +653,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;
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();
}

Ref<Font> 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;
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();
}

text.erase(selection.begin, selected);

append_at_cursor(p_data);
selection.begin = cursor_pos - selected;
selection.end = cursor_pos;
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();
}
}

Expand Down Expand Up @@ -910,7 +944,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) {
Expand Down Expand Up @@ -977,6 +1011,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;
}
}

Expand Down Expand Up @@ -1033,6 +1086,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;
Expand All @@ -1054,6 +1110,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;
Expand Down
3 changes: 3 additions & 0 deletions scene/gui/line_edit.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ class LineEdit : public Control {

bool virtual_keyboard_enabled = true;

bool drag_action = false;
bool drag_caret_force_displayed = false;

Ref<Texture> right_icon;

struct Selection {
Expand Down
64 changes: 57 additions & 7 deletions scene/gui/rich_text_label.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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/scene_string_names.h"

#ifdef TOOLS_ENABLED
Expand Down Expand Up @@ -1047,6 +1048,9 @@ void RichTextLabel::_notification(int p_what) {
update();
}
}
case Control::NOTIFICATION_DRAG_END: {
selection.drag_attempt = false;
} break;
}
}

Expand Down Expand Up @@ -1129,6 +1133,9 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
Item *item = nullptr;

bool outside;

selection.drag_attempt = false;

_find_click(main, b->get_position(), &item, &line, &outside);

if (item) {
Expand All @@ -1138,13 +1145,18 @@ void RichTextLabel::_gui_input(Ref<InputEvent> 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;

update();
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();
}
}
}
}
Expand All @@ -1154,6 +1166,8 @@ void RichTextLabel::_gui_input(Ref<InputEvent> 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) {
Expand All @@ -1174,6 +1188,25 @@ void RichTextLabel::_gui_input(Ref<InputEvent> 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) {
Expand Down Expand Up @@ -2476,6 +2509,22 @@ void RichTextLabel::set_selection_enabled(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;
Expand Down Expand Up @@ -2985,6 +3034,7 @@ RichTextLabel::RichTextLabel() {
selection.click = nullptr;
selection.active = false;
selection.enabled = false;
selection.drag_attempt = false;

visible_characters = -1;
percent_visible = 1;
Expand Down
3 changes: 3 additions & 0 deletions scene/gui/rich_text_label.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,13 +358,15 @@ class RichTextLabel : public Control {

bool active; // anything selected? i.e. from, to, etc. valid?
bool enabled; // allow selections?
bool drag_attempt;
};

Selection selection;

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<Font> &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);

Expand Down Expand Up @@ -465,6 +467,7 @@ class RichTextLabel : public Control {
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;
Expand Down
Loading

0 comments on commit 4167e98

Please sign in to comment.