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

Add TextEdit option to prevent copying without a selection #90743

Merged
merged 1 commit into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/classes/EditorSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,9 @@
<member name="text_editor/behavior/files/trim_trailing_whitespace_on_save" type="bool" setter="" getter="">
If [code]true[/code], trims trailing whitespace when saving a script. Trailing whitespace refers to tab and space characters placed at the end of lines. Since these serve no practical purpose, they can and should be removed to make version control diffs less noisy.
</member>
<member name="text_editor/behavior/general/empty_selection_clipboard" type="bool" setter="" getter="">
If [code]true[/code], copying or cutting without a selection is performed on all lines with a caret. Otherwise, copy and cut require a selection.
</member>
<member name="text_editor/behavior/indent/auto_indent" type="bool" setter="" getter="">
If [code]true[/code], automatically indents code when pressing the [kbd]Enter[/kbd] key based on blocks above the new line.
</member>
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/TextEdit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1299,6 +1299,9 @@
<member name="editable" type="bool" setter="set_editable" getter="is_editable" default="true" keywords="readonly, disabled, enabled">
If [code]false[/code], existing text cannot be modified and new text cannot be added.
</member>
<member name="empty_selection_clipboard_enabled" type="bool" setter="set_empty_selection_clipboard_enabled" getter="is_empty_selection_clipboard_enabled" default="true">
If [code]true[/code], copying or cutting without a selection is performed on all lines with a caret. Otherwise, copy and cut require a selection.
</member>
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="2" />
<member name="highlight_all_occurrences" type="bool" setter="set_highlight_all_occurrences" getter="is_highlight_all_occurrences_enabled" default="false">
If [code]true[/code], all occurrences of the selected text will be highlighted.
Expand Down
3 changes: 3 additions & 0 deletions editor/code_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,9 @@ void CodeTextEditor::update_editor_settings() {
text_editor->set_draw_spaces(EDITOR_GET("text_editor/appearance/whitespace/draw_spaces"));
text_editor->add_theme_constant_override("line_spacing", EDITOR_GET("text_editor/appearance/whitespace/line_spacing"));

// Behavior: General
text_editor->set_empty_selection_clipboard_enabled(EDITOR_GET("text_editor/behavior/general/empty_selection_clipboard"));

// Behavior: Navigation
text_editor->set_scroll_past_end_of_file_enabled(EDITOR_GET("text_editor/behavior/navigation/scroll_past_end_of_file"));
text_editor->set_smooth_scroll_enabled(EDITOR_GET("text_editor/behavior/navigation/smooth_scrolling"));
Expand Down
3 changes: 3 additions & 0 deletions editor/editor_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,9 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "text_editor/appearance/whitespace/line_spacing", 4, "0,50,1")

// Behavior
// Behavior: General
_initial_set("text_editor/behavior/general/empty_selection_clipboard", true);

// Behavior: Navigation
_initial_set("text_editor/behavior/navigation/move_caret_on_right_click", true);
_initial_set("text_editor/behavior/navigation/scroll_past_end_of_file", false);
Expand Down
3 changes: 3 additions & 0 deletions scene/gui/code_edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,9 @@ void CodeEdit::_cut_internal(int p_caret) {
delete_selection(p_caret);
return;
}
if (!is_empty_selection_clipboard_enabled()) {
return;
}
if (p_caret == -1) {
delete_lines();
} else {
Expand Down
20 changes: 20 additions & 0 deletions scene/gui/text_edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3366,6 +3366,14 @@ bool TextEdit::is_middle_mouse_paste_enabled() const {
return middle_mouse_paste_enabled;
}

void TextEdit::set_empty_selection_clipboard_enabled(bool p_enabled) {
empty_selection_clipboard_enabled = p_enabled;
}

bool TextEdit::is_empty_selection_clipboard_enabled() const {
return empty_selection_clipboard_enabled;
}

// Text manipulation
void TextEdit::clear() {
setting_text = true;
Expand Down Expand Up @@ -6568,6 +6576,9 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_middle_mouse_paste_enabled", "enabled"), &TextEdit::set_middle_mouse_paste_enabled);
ClassDB::bind_method(D_METHOD("is_middle_mouse_paste_enabled"), &TextEdit::is_middle_mouse_paste_enabled);

ClassDB::bind_method(D_METHOD("set_empty_selection_clipboard_enabled", "enabled"), &TextEdit::set_empty_selection_clipboard_enabled);
ClassDB::bind_method(D_METHOD("is_empty_selection_clipboard_enabled"), &TextEdit::is_empty_selection_clipboard_enabled);

// Text manipulation
ClassDB::bind_method(D_METHOD("clear"), &TextEdit::clear);

Expand Down Expand Up @@ -6961,6 +6972,7 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_and_drop_selection_enabled"), "set_drag_and_drop_selection_enabled", "is_drag_and_drop_selection_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "empty_selection_clipboard_enabled"), "set_empty_selection_clipboard_enabled", "is_empty_selection_clipboard_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_mode", PROPERTY_HINT_ENUM, "None,Boundary"), "set_line_wrapping_mode", "get_line_wrapping_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Arbitrary:1,Word:2,Word (Smart):3"), "set_autowrap_mode", "get_autowrap_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indent_wrapped_lines"), "set_indent_wrapped_lines", "is_indent_wrapped_lines");
Expand Down Expand Up @@ -7215,6 +7227,10 @@ void TextEdit::_cut_internal(int p_caret) {
return;
}

if (!empty_selection_clipboard_enabled) {
return;
}
Comment on lines +7230 to +7232
Copy link
Contributor

Choose a reason for hiding this comment

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

This is probably needed in CodeEdit::_cut_internal as well so it doesn't delete empty lines that aren't copied.


// Remove full lines.
begin_complex_operation();
begin_multicaret_edit();
Expand Down Expand Up @@ -7245,6 +7261,10 @@ void TextEdit::_copy_internal(int p_caret) {
return;
}

if (!empty_selection_clipboard_enabled) {
return;
}

// Copy full lines.
StringBuilder clipboard;
Vector<Point2i> line_ranges;
Expand Down
4 changes: 4 additions & 0 deletions scene/gui/text_edit.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ class TextEdit : public Control {
bool shortcut_keys_enabled = true;
bool virtual_keyboard_enabled = true;
bool middle_mouse_paste_enabled = true;
bool empty_selection_clipboard_enabled = true;

// Overridable actions.
String cut_copy_line = "";
Expand Down Expand Up @@ -770,6 +771,9 @@ class TextEdit : public Control {
void set_middle_mouse_paste_enabled(bool p_enabled);
bool is_middle_mouse_paste_enabled() const;

void set_empty_selection_clipboard_enabled(bool p_enabled);
bool is_empty_selection_clipboard_enabled() const;

// Text manipulation
void clear();

Expand Down
25 changes: 25 additions & 0 deletions tests/scene/test_code_edit.h
Original file line number Diff line number Diff line change
Expand Up @@ -4779,6 +4779,31 @@ TEST_CASE("[SceneTree][CodeEdit] text manipulation") {
CHECK(code_edit->get_caret_column(3) == 0);
}

SUBCASE("[SceneTree][CodeEdit] cut when empty selection clipboard disabled") {
DisplayServerMock *DS = (DisplayServerMock *)(DisplayServer::get_singleton());
code_edit->set_empty_selection_clipboard_enabled(false);
DS->clipboard_set("");

code_edit->set_text("this is\nsome\n");
code_edit->set_caret_line(0);
code_edit->set_caret_column(6);
MessageQueue::get_singleton()->flush();
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");

code_edit->cut();
MessageQueue::get_singleton()->flush();
CHECK(DS->clipboard_get() == "");
CHECK(code_edit->get_text() == "this is\nsome\n");
CHECK(code_edit->get_caret_line() == 0);
CHECK(code_edit->get_caret_column() == 6);
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
}

SUBCASE("[SceneTree][CodeEdit] new line") {
// Add a new line.
code_edit->set_text("test new line");
Expand Down
48 changes: 48 additions & 0 deletions tests/scene/test_text_edit.h
Original file line number Diff line number Diff line change
Expand Up @@ -3585,6 +3585,54 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK_FALSE("lines_edited_from");
}

SUBCASE("[TextEdit] cut when empty selection clipboard disabled") {
text_edit->set_empty_selection_clipboard_enabled(false);
DS->clipboard_set("");

text_edit->set_text("this is\nsome\n");
text_edit->set_caret_line(0);
text_edit->set_caret_column(6);
MessageQueue::get_singleton()->flush();
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");

text_edit->cut();
MessageQueue::get_singleton()->flush();
CHECK(DS->clipboard_get() == "");
CHECK(text_edit->get_text() == "this is\nsome\n");
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 6);
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
}

SUBCASE("[TextEdit] copy when empty selection clipboard disabled") {
text_edit->set_empty_selection_clipboard_enabled(false);
DS->clipboard_set("");

text_edit->set_text("this is\nsome\n");
text_edit->set_caret_line(0);
text_edit->set_caret_column(6);
MessageQueue::get_singleton()->flush();
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");

text_edit->copy();
MessageQueue::get_singleton()->flush();
CHECK(DS->clipboard_get() == "");
CHECK(text_edit->get_text() == "this is\nsome\n");
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 6);
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
}

SIGNAL_UNWATCH(text_edit, "text_set");
SIGNAL_UNWATCH(text_edit, "text_changed");
SIGNAL_UNWATCH(text_edit, "lines_edited_from");
Expand Down
Loading