瀏覽代碼

Make TextEdit cut, copy and paste overridable

Paulb23 4 年之前
父節點
當前提交
e60900a353
共有 5 個文件被更改,包括 314 次插入224 次删除
  1. 27 5
      doc/classes/TextEdit.xml
  2. 63 60
      scene/gui/code_edit.cpp
  3. 6 3
      scene/gui/code_edit.h
  4. 195 150
      scene/gui/text_edit.cpp
  5. 23 6
      scene/gui/text_edit.h

+ 27 - 5
doc/classes/TextEdit.xml

@@ -13,13 +13,35 @@
 		<method name="_backspace" qualifiers="virtual">
 			<return type="void" />
 			<description>
-				A virtual method that is called whenever backspace is triggered.
+				Overide this method to define what happens when the user presses the backspace key.
+			</description>
+		</method>
+		<method name="_copy" qualifiers="virtual">
+			<return type="void">
+			</return>
+			<description>
+				Overide this method to define what happens when the user performs a copy.
+			</description>
+		</method>
+		<method name="_cut" qualifiers="virtual">
+			<return type="void">
+			</return>
+			<description>
+				Overide this method to define what happens when the user perfroms a cut.
 			</description>
 		</method>
 		<method name="_handle_unicode_input" qualifiers="virtual">
 			<return type="void" />
 			<argument index="0" name="unicode" type="int" />
 			<description>
+				Overide this method to define what happens when the types in the provided key [code]unicode[/code].
+			</description>
+		</method>
+		<method name="_paste" qualifiers="virtual">
+			<return type="void">
+			</return>
+			<description>
+				Overide this method to define what happens when the user perfroms a paste.
 			</description>
 		</method>
 		<method name="add_gutter">
@@ -31,7 +53,7 @@
 		<method name="backspace">
 			<return type="void" />
 			<description>
-				Causes the [TextEdit] to perform a backspace.
+				Called when the user presses the backspace key. Can be overriden with [method _backspace].
 			</description>
 		</method>
 		<method name="center_viewport_to_cursor">
@@ -55,7 +77,7 @@
 		<method name="copy">
 			<return type="void" />
 			<description>
-				Copy's the current text selection.
+				Copy's the current text selection. Can be overriden with [method _copy].
 			</description>
 		</method>
 		<method name="cursor_get_column" qualifiers="const">
@@ -94,7 +116,7 @@
 		<method name="cut">
 			<return type="void" />
 			<description>
-				Cut's the current selection.
+				Cut's the current selection. Can be overriden with [method _cut].
 			</description>
 		</method>
 		<method name="delete_selection">
@@ -351,7 +373,7 @@
 		<method name="paste">
 			<return type="void" />
 			<description>
-				Paste the current selection.
+				Paste the current selection. Can be overriden with [method _paste].
 			</description>
 		</method>
 		<method name="redo">

+ 63 - 60
scene/gui/code_edit.cpp

@@ -544,18 +544,21 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
 	return TextEdit::get_cursor_shape(p_pos);
 }
 
-void CodeEdit::handle_unicode_input(uint32_t p_unicode) {
+/* Text manipulation */
+
+// Overridable actions
+void CodeEdit::_handle_unicode_input(const uint32_t p_unicode) {
 	bool had_selection = is_selection_active();
 	if (had_selection) {
 		begin_complex_operation();
 		delete_selection();
 	}
 
-	// Remove the old character if in insert mode and no selection.
+	/* Remove the old character if in insert mode and no selection. */
 	if (is_insert_mode() && !had_selection) {
 		begin_complex_operation();
 
-		// Make sure we don't try and remove empty space.
+		/* Make sure we don't try and remove empty space. */
 		if (cursor_get_column() < get_line(cursor_get_line()).length()) {
 			_remove_text(cursor_get_line(), cursor_get_column(), cursor_get_line(), cursor_get_column() + 1);
 		}
@@ -596,6 +599,63 @@ void CodeEdit::handle_unicode_input(uint32_t p_unicode) {
 	}
 }
 
+void CodeEdit::_backspace() {
+	if (is_readonly()) {
+		return;
+	}
+
+	int cc = cursor_get_column();
+	int cl = cursor_get_line();
+
+	if (cc == 0 && cl == 0) {
+		return;
+	}
+
+	if (is_selection_active()) {
+		delete_selection();
+		return;
+	}
+
+	if (cl > 0 && is_line_hidden(cl - 1)) {
+		unfold_line(cursor_get_line() - 1);
+	}
+
+	int prev_line = cc ? cl : cl - 1;
+	int prev_column = cc ? (cc - 1) : (get_line(cl - 1).length());
+
+	merge_gutters(cl, prev_line);
+
+	if (auto_brace_completion_enabled && cc > 0) {
+		int idx = _get_auto_brace_pair_open_at_pos(cl, cc);
+		if (idx != -1) {
+			prev_column = cc - auto_brace_completion_pairs[idx].open_key.length();
+
+			if (_get_auto_brace_pair_close_at_pos(cl, cc) == idx) {
+				_remove_text(prev_line, prev_column, cl, cc + auto_brace_completion_pairs[idx].close_key.length());
+			} else {
+				_remove_text(prev_line, prev_column, cl, cc);
+			}
+			cursor_set_line(prev_line, false, true);
+			cursor_set_column(prev_column);
+			return;
+		}
+	}
+
+	/* For space indentation we need to do a simple unindent if there are no chars to the left, acting in the */
+	/* same way as tabs.                                                                                      */
+	if (indent_using_spaces && cc != 0) {
+		if (get_first_non_whitespace_column(cl) > cc) {
+			prev_column = cc - _calculate_spaces_till_next_left_indent(cc);
+			prev_line = cl;
+		}
+	}
+
+	_remove_text(prev_line, prev_column, cl, cc);
+
+	cursor_set_line(prev_line, false, true);
+	cursor_set_column(prev_column);
+}
+
 /* Indent management */
 void CodeEdit::set_indent_size(const int p_size) {
 	ERR_FAIL_COND_MSG(p_size <= 0, "Indend size must be greater than 0.");
@@ -955,63 +1015,6 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
 	end_complex_operation();
 }
 
-void CodeEdit::backspace() {
-	if (is_readonly()) {
-		return;
-	}
-
-	int cc = cursor_get_column();
-	int cl = cursor_get_line();
-
-	if (cc == 0 && cl == 0) {
-		return;
-	}
-
-	if (is_selection_active()) {
-		delete_selection();
-		return;
-	}
-
-	if (cl > 0 && is_line_hidden(cl - 1)) {
-		unfold_line(cursor_get_line() - 1);
-	}
-
-	int prev_line = cc ? cl : cl - 1;
-	int prev_column = cc ? (cc - 1) : (get_line(cl - 1).length());
-
-	merge_gutters(cl, prev_line);
-
-	if (auto_brace_completion_enabled && cc > 0) {
-		int idx = _get_auto_brace_pair_open_at_pos(cl, cc);
-		if (idx != -1) {
-			prev_column = cc - auto_brace_completion_pairs[idx].open_key.length();
-
-			if (_get_auto_brace_pair_close_at_pos(cl, cc) == idx) {
-				_remove_text(prev_line, prev_column, cl, cc + auto_brace_completion_pairs[idx].close_key.length());
-			} else {
-				_remove_text(prev_line, prev_column, cl, cc);
-			}
-			cursor_set_line(prev_line, false, true);
-			cursor_set_column(prev_column);
-			return;
-		}
-	}
-
-	/* For space indentation we need to do a simple unindent if there are no chars to the left, acting in the */
-	/* same way as tabs.                                                                                      */
-	if (indent_using_spaces && cc != 0) {
-		if (get_first_non_whitespace_column(cl) > cc) {
-			prev_column = cc - _calculate_spaces_till_next_left_indent(cc);
-			prev_line = cl;
-		}
-	}
-
-	_remove_text(prev_line, prev_column, cl, cc);
-
-	cursor_set_line(prev_line, false, true);
-	cursor_set_column(prev_column);
-}
-
 /* Auto brace completion */
 void CodeEdit::set_auto_brace_completion_enabled(bool p_enabled) {
 	auto_brace_completion_enabled = p_enabled;

+ 6 - 3
scene/gui/code_edit.h

@@ -239,10 +239,15 @@ protected:
 
 	static void _bind_methods();
 
+	/* Text manipulation */
+
+	// Overridable actions
+	virtual void _handle_unicode_input(const uint32_t p_unicode) override;
+	virtual void _backspace() override;
+
 public:
 	/* General overrides */
 	virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
-	virtual void handle_unicode_input(uint32_t p_unicode) override;
 
 	/* Indent management */
 	void set_indent_size(const int p_size);
@@ -263,8 +268,6 @@ public:
 	void indent_lines();
 	void unindent_lines();
 
-	virtual void backspace() override;
-
 	/* Auto brace completion */
 	void set_auto_brace_completion_enabled(bool p_enabled);
 	bool is_auto_brace_completion_enabled() const;

+ 195 - 150
scene/gui/text_edit.cpp

@@ -1503,40 +1503,6 @@ void TextEdit::_notification(int p_what) {
 	}
 }
 
-void TextEdit::backspace() {
-	ScriptInstance *si = get_script_instance();
-	if (si && si->has_method("_backspace")) {
-		si->call("_backspace");
-		return;
-	}
-
-	if (readonly) {
-		return;
-	}
-
-	if (cursor.column == 0 && cursor.line == 0) {
-		return;
-	}
-
-	if (is_selection_active()) {
-		delete_selection();
-		return;
-	}
-
-	int prev_line = cursor.column ? cursor.line : cursor.line - 1;
-	int prev_column = cursor.column ? (cursor.column - 1) : (text[cursor.line - 1].length());
-
-	merge_gutters(cursor.line, prev_line);
-
-	if (is_line_hidden(cursor.line)) {
-		set_line_as_hidden(prev_line, true);
-	}
-	_remove_text(prev_line, prev_column, cursor.line, cursor.column);
-
-	cursor_set_line(prev_line, false, true);
-	cursor_set_column(prev_column);
-}
-
 void TextEdit::_swap_current_input_direction() {
 	if (input_direction == TEXT_DIRECTION_LTR) {
 		input_direction = TEXT_DIRECTION_RTL;
@@ -1955,37 +1921,6 @@ void TextEdit::_move_cursor_document_end(bool p_select) {
 	}
 }
 
-void TextEdit::handle_unicode_input(uint32_t p_unicode) {
-	ScriptInstance *si = get_script_instance();
-	if (si && si->has_method("_handle_unicode_input")) {
-		si->call("_handle_unicode_input", p_unicode);
-		return;
-	}
-
-	bool had_selection = selection.active;
-	if (had_selection) {
-		begin_complex_operation();
-		delete_selection();
-	}
-
-	// Remove the old character if in insert mode and no selection.
-	if (insert_mode && !had_selection) {
-		begin_complex_operation();
-
-		// Make sure we don't try and remove empty space.
-		if (cursor.column < get_line(cursor.line).length()) {
-			_remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
-		}
-	}
-
-	const char32_t chr[2] = { (char32_t)p_unicode, 0 };
-	insert_text_at_cursor(chr);
-
-	if ((insert_mode && !had_selection) || (had_selection)) {
-		end_complex_operation();
-	}
-}
-
 void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const {
 	float rows = p_mouse.y;
 	rows -= cache.style_normal->get_margin(SIDE_TOP);
@@ -3785,6 +3720,54 @@ void TextEdit::_update_caches() {
 	}
 }
 
+/* Text manipulation */
+
+// Overridable actions
+void TextEdit::handle_unicode_input(const uint32_t p_unicode) {
+	ScriptInstance *si = get_script_instance();
+	if (si && si->has_method("_handle_unicode_input")) {
+		si->call("_handle_unicode_input", p_unicode);
+		return;
+	}
+	_handle_unicode_input(p_unicode);
+}
+
+void TextEdit::backspace() {
+	ScriptInstance *si = get_script_instance();
+	if (si && si->has_method("_backspace")) {
+		si->call("_backspace");
+		return;
+	}
+	_backspace();
+}
+
+void TextEdit::cut() {
+	ScriptInstance *si = get_script_instance();
+	if (si && si->has_method("_cut")) {
+		si->call("_cut");
+		return;
+	}
+	_cut();
+}
+
+void TextEdit::copy() {
+	ScriptInstance *si = get_script_instance();
+	if (si && si->has_method("_copy")) {
+		si->call("_copy");
+		return;
+	}
+	_copy();
+}
+
+void TextEdit::paste() {
+	ScriptInstance *si = get_script_instance();
+	if (si && si->has_method("_paste")) {
+		si->call("_paste");
+		return;
+	}
+	_paste();
+}
+
 /* Syntax Highlighting. */
 Ref<SyntaxHighlighter> TextEdit::get_syntax_highlighter() {
 	return syntax_highlighter;
@@ -4028,84 +4011,6 @@ Color TextEdit::get_line_background_color(int p_line) {
 	return text.get_line_background_color(p_line);
 }
 
-void TextEdit::cut() {
-	if (readonly) {
-		return;
-	}
-
-	if (!selection.active) {
-		String clipboard = text[cursor.line];
-		DisplayServer::get_singleton()->clipboard_set(clipboard);
-		cursor_set_line(cursor.line);
-		cursor_set_column(0);
-
-		if (cursor.line == 0 && get_line_count() > 1) {
-			_remove_text(cursor.line, 0, cursor.line + 1, 0);
-		} else {
-			_remove_text(cursor.line, 0, cursor.line, text[cursor.line].length());
-			backspace();
-			cursor_set_line(cursor.line + 1);
-		}
-
-		update();
-		cut_copy_line = clipboard;
-
-	} else {
-		String clipboard = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
-		DisplayServer::get_singleton()->clipboard_set(clipboard);
-
-		_remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
-		cursor_set_line(selection.from_line, false); // Set afterwards else it causes the view to be offset.
-		cursor_set_column(selection.from_column);
-
-		selection.active = false;
-		selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
-		update();
-		cut_copy_line = "";
-	}
-}
-
-void TextEdit::copy() {
-	if (!selection.active) {
-		if (text[cursor.line].length() != 0) {
-			String clipboard = _base_get_text(cursor.line, 0, cursor.line, text[cursor.line].length());
-			DisplayServer::get_singleton()->clipboard_set(clipboard);
-			cut_copy_line = clipboard;
-		}
-	} else {
-		String clipboard = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
-		DisplayServer::get_singleton()->clipboard_set(clipboard);
-		cut_copy_line = "";
-	}
-}
-
-void TextEdit::paste() {
-	if (readonly) {
-		return;
-	}
-
-	String clipboard = DisplayServer::get_singleton()->clipboard_get();
-
-	begin_complex_operation();
-	if (selection.active) {
-		selection.active = false;
-		selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
-		_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);
-
-	} else if (!cut_copy_line.is_empty() && cut_copy_line == clipboard) {
-		cursor_set_column(0);
-		String ins = "\n";
-		clipboard += ins;
-	}
-
-	insert_text_at_cursor(clipboard);
-	end_complex_operation();
-
-	update();
-}
-
 void TextEdit::select_all() {
 	if (!selecting_enabled) {
 		return;
@@ -5425,13 +5330,6 @@ void TextEdit::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &TextEdit::is_selecting_enabled);
 
 	ClassDB::bind_method(D_METHOD("delete_selection"), &TextEdit::delete_selection);
-	ClassDB::bind_method(D_METHOD("backspace"), &TextEdit::backspace);
-	BIND_VMETHOD(MethodInfo("_backspace"));
-	BIND_VMETHOD(MethodInfo("_handle_unicode_input", PropertyInfo(Variant::INT, "unicode")))
-
-	ClassDB::bind_method(D_METHOD("cut"), &TextEdit::cut);
-	ClassDB::bind_method(D_METHOD("copy"), &TextEdit::copy);
-	ClassDB::bind_method(D_METHOD("paste"), &TextEdit::paste);
 
 	ClassDB::bind_method(D_METHOD("select", "from_line", "from_column", "to_line", "to_column"), &TextEdit::select);
 	ClassDB::bind_method(D_METHOD("select_all"), &TextEdit::select_all);
@@ -5462,6 +5360,23 @@ void TextEdit::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &TextEdit::set_override_selected_font_color);
 	ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &TextEdit::is_overriding_selected_font_color);
 
+	/* Text manipulation */
+
+	// Overridable actions
+	ClassDB::bind_method(D_METHOD("backspace"), &TextEdit::backspace);
+
+	ClassDB::bind_method(D_METHOD("cut"), &TextEdit::cut);
+	ClassDB::bind_method(D_METHOD("copy"), &TextEdit::copy);
+	ClassDB::bind_method(D_METHOD("paste"), &TextEdit::paste);
+
+	BIND_VMETHOD(MethodInfo("_handle_unicode_input", PropertyInfo(Variant::INT, "unicode")))
+	BIND_VMETHOD(MethodInfo("_backspace"));
+
+	BIND_VMETHOD(MethodInfo("_cut"));
+	BIND_VMETHOD(MethodInfo("_copy"));
+	BIND_VMETHOD(MethodInfo("_paste"));
+
+	/* Syntax Highlighting. */
 	ClassDB::bind_method(D_METHOD("set_syntax_highlighter", "syntax_highlighter"), &TextEdit::set_syntax_highlighter);
 	ClassDB::bind_method(D_METHOD("get_syntax_highlighter"), &TextEdit::get_syntax_highlighter);
 
@@ -5679,6 +5594,136 @@ void TextEdit::_ensure_menu() {
 	menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL);
 }
 
+/* Text manipulation */
+
+// Overridable actions
+void TextEdit::_handle_unicode_input(const uint32_t p_unicode) {
+	if (readonly) {
+		return;
+	}
+
+	bool had_selection = is_selection_active();
+	if (had_selection) {
+		begin_complex_operation();
+		delete_selection();
+	}
+
+	/* Remove the old character if in insert mode and no selection. */
+	if (insert_mode && !had_selection) {
+		begin_complex_operation();
+
+		/* Make sure we don't try and remove empty space. */
+		int cl = cursor_get_line();
+		int cc = cursor_get_column();
+		if (cc < get_line(cl).length()) {
+			_remove_text(cl, cc, cl, cc + 1);
+		}
+	}
+
+	const char32_t chr[2] = { (char32_t)p_unicode, 0 };
+	insert_text_at_cursor(chr);
+
+	if ((insert_mode && !had_selection) || (had_selection)) {
+		end_complex_operation();
+	}
+}
+
+void TextEdit::_backspace() {
+	if (readonly) {
+		return;
+	}
+
+	int cc = cursor_get_column();
+	int cl = cursor_get_line();
+
+	if (cc == 0 && cl == 0) {
+		return;
+	}
+
+	if (is_selection_active()) {
+		delete_selection();
+		return;
+	}
+
+	int prev_line = cc ? cl : cl - 1;
+	int prev_column = cc ? (cc - 1) : (text[cl - 1].length());
+
+	merge_gutters(cl, prev_line);
+
+	if (is_line_hidden(cl)) {
+		set_line_as_hidden(prev_line, true);
+	}
+	_remove_text(prev_line, prev_column, cl, cc);
+
+	cursor_set_line(prev_line, false, true);
+	cursor_set_column(prev_column);
+}
+
+void TextEdit::_cut() {
+	if (readonly) {
+		return;
+	}
+
+	if (is_selection_active()) {
+		DisplayServer::get_singleton()->clipboard_set(get_selection_text());
+		delete_selection();
+		cut_copy_line = "";
+		return;
+	}
+
+	int cl = cursor_get_line();
+
+	String clipboard = text[cl];
+	DisplayServer::get_singleton()->clipboard_set(clipboard);
+	cursor_set_line(cl);
+	cursor_set_column(0);
+
+	if (cl == 0 && get_line_count() > 1) {
+		_remove_text(cl, 0, cl + 1, 0);
+	} else {
+		_remove_text(cl, 0, cl, text[cl].length());
+		backspace();
+		cursor_set_line(cursor_get_line() + 1);
+	}
+
+	cut_copy_line = clipboard;
+}
+
+void TextEdit::_copy() {
+	if (is_selection_active()) {
+		DisplayServer::get_singleton()->clipboard_set(get_selection_text());
+		cut_copy_line = "";
+		return;
+	}
+
+	int cl = cursor_get_line();
+	if (text[cl].length() != 0) {
+		String clipboard = _base_get_text(cl, 0, cl, text[cl].length());
+		DisplayServer::get_singleton()->clipboard_set(clipboard);
+		cut_copy_line = clipboard;
+	}
+}
+
+void TextEdit::_paste() {
+	if (readonly) {
+		return;
+	}
+
+	String clipboard = DisplayServer::get_singleton()->clipboard_get();
+
+	begin_complex_operation();
+	if (is_selection_active()) {
+		delete_selection();
+	} else if (!cut_copy_line.is_empty() && cut_copy_line == clipboard) {
+		cursor_set_column(0);
+		String ins = "\n";
+		clipboard += ins;
+	}
+
+	insert_text_at_cursor(clipboard);
+	end_complex_operation();
+}
+
 TextEdit::TextEdit() {
 	clear();
 	set_focus_mode(FOCUS_ALL);

+ 23 - 6
scene/gui/text_edit.h

@@ -173,6 +173,9 @@ private:
 		const Color get_line_background_color(int p_line) const { return text[p_line].background_color; }
 	};
 
+	/* Text manipulation */
+	String cut_copy_line = "";
+
 	struct Cursor {
 		Point2 draw_pos;
 		bool visible = false;
@@ -290,7 +293,6 @@ private:
 	bool scroll_past_end_of_file_enabled = false;
 	bool highlight_current_line = false;
 
-	String cut_copy_line;
 	bool insert_mode = false;
 	bool select_identifiers_enabled = false;
 
@@ -473,7 +475,27 @@ protected:
 
 	void _set_symbol_lookup_word(const String &p_symbol);
 
+	/* Text manipulation */
+
+	// Overridable actions
+	virtual void _handle_unicode_input(const uint32_t p_unicode);
+	virtual void _backspace();
+
+	virtual void _cut();
+	virtual void _copy();
+	virtual void _paste();
+
 public:
+	/* Text manipulation */
+
+	// Overridable actions
+	void handle_unicode_input(const uint32_t p_unicode);
+	void backspace();
+
+	void cut();
+	void copy();
+	void paste();
+
 	/* Syntax Highlighting. */
 	Ref<SyntaxHighlighter> get_syntax_highlighter();
 	void set_syntax_highlighter(Ref<SyntaxHighlighter> p_syntax_highlighter);
@@ -667,11 +689,6 @@ public:
 
 	void delete_selection();
 
-	virtual void handle_unicode_input(uint32_t p_unicode);
-	virtual void backspace();
-	void cut();
-	void copy();
-	void paste();
 	void select_all();
 	void select_word_under_caret();
 	void select(int p_from_line, int p_from_column, int p_to_line, int p_to_column);