Explorar el Código

Merge pull request #60170 from ConteZero/context_menu

Rémi Verschelde hace 3 años
padre
commit
d9cb39c2e8
Se han modificado 3 ficheros con 160 adiciones y 5 borrados
  1. 19 0
      doc/classes/RichTextLabel.xml
  2. 116 5
      scene/gui/rich_text_label.cpp
  3. 25 0
      scene/gui/rich_text_label.h

+ 19 - 0
doc/classes/RichTextLabel.xml

@@ -94,6 +94,13 @@
 				Returns the vertical offset of the line found at the provided index.
 			</description>
 		</method>
+		<method name="get_menu" qualifiers="const">
+			<return type="PopupMenu" />
+			<description>
+				Returns the [PopupMenu] of this [RichTextLabel]. By default, this menu is displayed when right-clicking on the [RichTextLabel].
+				[b]Warning:[/b] This is a required internal node, removing and freeing it may cause a crash. If you wish to hide it or any of its children, use their [member Window.visible] property.
+			</description>
+		</method>
 		<method name="get_paragraph_count" qualifiers="const">
 			<return type="int" />
 			<description>
@@ -163,6 +170,12 @@
 				Installs a custom effect. [code]effect[/code] should be a valid [RichTextEffect].
 			</description>
 		</method>
+		<method name="is_menu_visible" qualifiers="const">
+			<return type="bool" />
+			<description>
+				Returns whether the menu is visible. Use this instead of [code]get_menu().visible[/code] to improve performance (so the creation of the menu is avoided).
+			</description>
+		</method>
 		<method name="newline">
 			<return type="void" />
 			<description>
@@ -433,6 +446,9 @@
 			If [code]true[/code], the label uses BBCode formatting.
 		</member>
 		<member name="clip_contents" type="bool" setter="set_clip_contents" getter="is_clipping_contents" overrides="Control" default="true" />
+		<member name="context_menu_enabled" type="bool" setter="set_context_menu_enabled" getter="is_context_menu_enabled" default="false">
+			If [code]true[/code], a right-click displays the context menu.
+		</member>
 		<member name="custom_effects" type="Array" setter="set_effects" getter="get_effects" default="[]">
 			The currently installed custom effects. This is an array of [RichTextEffect]s.
 			To add a custom effect, it's more convenient to use [method install_effect].
@@ -469,6 +485,9 @@
 		<member name="selection_enabled" type="bool" setter="set_selection_enabled" getter="is_selection_enabled" default="false">
 			If [code]true[/code], the label allows text selection.
 		</member>
+		<member name="shortcut_keys_enabled" type="bool" setter="set_shortcut_keys_enabled" getter="is_shortcut_keys_enabled" default="true">
+			If [code]true[/code], shortcut keys for context menu items are enabled, even if the context menu is disabled.
+		</member>
 		<member name="structured_text_bidi_override" type="int" setter="set_structured_text_bidi_override" getter="get_structured_text_bidi_override" enum="Control.StructuredTextParser" default="0">
 			Set BiDi algorithm override for the structured text.
 		</member>

+ 116 - 5
scene/gui/rich_text_label.cpp

@@ -30,6 +30,7 @@
 
 #include "rich_text_label.h"
 
+#include "core/input/input_map.h"
 #include "core/math/math_defs.h"
 #include "core/os/keyboard.h"
 #include "core/os/os.h"
@@ -1868,6 +1869,13 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
 				vscroll->set_value(vscroll->get_value() + vscroll->get_page() * b->get_factor() * 0.5 / 8);
 			}
 		}
+		if (b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) {
+			_generate_context_menu();
+			menu->set_position(get_screen_position() + b->get_position());
+			menu->reset_size();
+			menu->popup();
+			grab_focus();
+		}
 	}
 
 	Ref<InputEventPanGesture> pan_gesture = p_event;
@@ -1909,12 +1917,24 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
 				vscroll->set_value(vscroll->get_max());
 				handled = true;
 			}
-			if (k->is_action("ui_text_select_all")) {
-				select_all();
-				handled = true;
+			if (is_shortcut_keys_enabled()) {
+				if (k->is_action("ui_text_select_all")) {
+					select_all();
+					handled = true;
+				}
+				if (k->is_action("ui_copy")) {
+					selection_copy();
+					handled = true;
+				}
 			}
-			if (k->is_action("ui_copy")) {
-				selection_copy();
+			if (k->is_action("ui_menu", true)) {
+				if (context_menu_enabled) {
+					_generate_context_menu();
+					menu->set_position(get_screen_position());
+					menu->reset_size();
+					menu->popup();
+					menu->grab_focus();
+				}
 				handled = true;
 			}
 
@@ -4175,6 +4195,32 @@ String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p
 	return text;
 }
 
+void RichTextLabel::set_context_menu_enabled(bool p_enabled) {
+	context_menu_enabled = p_enabled;
+}
+
+bool RichTextLabel::is_context_menu_enabled() const {
+	return context_menu_enabled;
+}
+
+void RichTextLabel::set_shortcut_keys_enabled(bool p_enabled) {
+	shortcut_keys_enabled = p_enabled;
+}
+
+bool RichTextLabel::is_shortcut_keys_enabled() const {
+	return shortcut_keys_enabled;
+}
+
+// Context menu.
+PopupMenu *RichTextLabel::get_menu() const {
+	const_cast<RichTextLabel *>(this)->_generate_context_menu();
+	return menu;
+}
+
+bool RichTextLabel::is_menu_visible() const {
+	return menu && menu->is_visible();
+}
+
 String RichTextLabel::get_selected_text() const {
 	if (!selection.active || !selection.enabled) {
 		return "";
@@ -4527,6 +4573,12 @@ void RichTextLabel::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_selection_enabled", "enabled"), &RichTextLabel::set_selection_enabled);
 	ClassDB::bind_method(D_METHOD("is_selection_enabled"), &RichTextLabel::is_selection_enabled);
 
+	ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enabled"), &RichTextLabel::set_context_menu_enabled);
+	ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &RichTextLabel::is_context_menu_enabled);
+
+	ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enabled"), &RichTextLabel::set_shortcut_keys_enabled);
+	ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &RichTextLabel::is_shortcut_keys_enabled);
+
 	ClassDB::bind_method(D_METHOD("set_deselect_on_focus_loss_enabled", "enable"), &RichTextLabel::set_deselect_on_focus_loss_enabled);
 	ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &RichTextLabel::is_deselect_on_focus_loss_enabled);
 
@@ -4576,6 +4628,9 @@ void RichTextLabel::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_effects"), &RichTextLabel::get_effects);
 	ClassDB::bind_method(D_METHOD("install_effect", "effect"), &RichTextLabel::install_effect);
 
+	ClassDB::bind_method(D_METHOD("get_menu"), &RichTextLabel::get_menu);
+	ClassDB::bind_method(D_METHOD("is_menu_visible"), &RichTextLabel::is_menu_visible);
+
 	// Note: set "bbcode_enabled" first, to avoid unnecessary "text" resets.
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode");
 
@@ -4597,6 +4652,9 @@ void RichTextLabel::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
 
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");
+
 	ADD_GROUP("Locale", "");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
@@ -4776,6 +4834,59 @@ Size2 RichTextLabel::get_minimum_size() const {
 	return size;
 }
 
+// Context menu.
+void RichTextLabel::_generate_context_menu() {
+	if (!menu) {
+		menu = memnew(PopupMenu);
+		add_child(menu, false, INTERNAL_MODE_FRONT);
+
+		menu->connect("id_pressed", callable_mp(this, &RichTextLabel::_menu_option));
+	}
+
+	// Reorganize context menu.
+	menu->clear();
+	if (selection.enabled) {
+		menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : Key::NONE);
+		menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE);
+	}
+}
+
+Key RichTextLabel::_get_menu_action_accelerator(const String &p_action) {
+	const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action);
+	if (!events) {
+		return Key::NONE;
+	}
+
+	// Use first event in the list for the accelerator.
+	const List<Ref<InputEvent>>::Element *first_event = events->front();
+	if (!first_event) {
+		return Key::NONE;
+	}
+
+	const Ref<InputEventKey> event = first_event->get();
+	if (event.is_null()) {
+		return Key::NONE;
+	}
+
+	// Use physical keycode if non-zero
+	if (event->get_physical_keycode() != Key::NONE) {
+		return event->get_physical_keycode_with_modifiers();
+	} else {
+		return event->get_keycode_with_modifiers();
+	}
+}
+
+void RichTextLabel::_menu_option(int p_option) {
+	switch (p_option) {
+		case MENU_COPY: {
+			selection_copy();
+		} break;
+		case MENU_SELECT_ALL: {
+			select_all();
+		} break;
+	}
+}
+
 void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item *it_from, Item *it_to, int start, int end, int fbg_flag) {
 	Vector2i fbg_index = Vector2i(end, start);
 	Color last_color = Color(0, 0, 0, 0);

+ 25 - 0
scene/gui/rich_text_label.h

@@ -32,6 +32,7 @@
 #define RICH_TEXT_LABEL_H
 
 #include "rich_text_effect.h"
+#include "scene/gui/popup_menu.h"
 #include "scene/gui/scroll_bar.h"
 #include "scene/resources/text_paragraph.h"
 
@@ -91,6 +92,11 @@ public:
 		VC_GLYPHS_RTL,
 	};
 
+	enum MenuItems {
+		MENU_COPY,
+		MENU_SELECT_ALL,
+	};
+
 protected:
 	void _notification(int p_what);
 	static void _bind_methods();
@@ -420,6 +426,15 @@ private:
 	Selection selection;
 	bool deselect_on_focus_loss_enabled = true;
 
+	bool context_menu_enabled = false;
+	bool shortcut_keys_enabled = true;
+
+	// Context menu.
+	PopupMenu *menu = nullptr;
+	void _generate_context_menu();
+	Key _get_menu_action_accelerator(const String &p_action);
+	void _menu_option(int p_option);
+
 	int visible_characters = -1;
 	float percent_visible = 1.0;
 	VisibleCharactersBehavior visible_chars_behavior = VC_CHARS_BEFORE_SHAPING;
@@ -555,6 +570,12 @@ public:
 	void set_tab_size(int p_spaces);
 	int get_tab_size() const;
 
+	void set_context_menu_enabled(bool p_enabled);
+	bool is_context_menu_enabled() const;
+
+	void set_shortcut_keys_enabled(bool p_enabled);
+	bool is_shortcut_keys_enabled() const;
+
 	void set_fit_content_height(bool p_enabled);
 	bool is_fit_content_height_enabled() const;
 
@@ -590,6 +611,10 @@ public:
 	bool is_deselect_on_focus_loss_enabled() const;
 	void deselect();
 
+	// Context menu.
+	PopupMenu *get_menu() const;
+	bool is_menu_visible() const;
+
 	void parse_bbcode(const String &p_bbcode);
 	void append_text(const String &p_bbcode);