diff options
Diffstat (limited to 'scene/gui')
-rw-r--r-- | scene/gui/popup_menu.cpp | 21 | ||||
-rw-r--r-- | scene/gui/popup_menu.h | 3 | ||||
-rw-r--r-- | scene/gui/rich_text_label.cpp | 132 | ||||
-rw-r--r-- | scene/gui/rich_text_label.h | 25 |
4 files changed, 167 insertions, 14 deletions
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 9fc1fb072c..b01f45e9ab 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -169,7 +169,7 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const { return -1; } -void PopupMenu::_activate_submenu(int p_over) { +void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) { Node *n = get_node(items[p_over].submenu); ERR_FAIL_COND_MSG(!n, "Item subnode does not exist: " + items[p_over].submenu + "."); Popup *submenu_popup = Object::cast_to<Popup>(n); @@ -213,8 +213,10 @@ void PopupMenu::_activate_submenu(int p_over) { return; } + submenu_pum->activated_by_keyboard = p_by_keyboard; + // If not triggered by the mouse, start the popup with its first item selected. - if (submenu_pum->get_item_count() > 0 && Input::get_singleton()->is_action_just_pressed("ui_accept")) { + if (submenu_pum->get_item_count() > 0 && p_by_keyboard) { submenu_pum->set_current_index(0); } @@ -323,14 +325,14 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { set_input_as_handled(); } } else if (p_event->is_action("ui_right") && p_event->is_pressed()) { - if (mouse_over >= 0 && mouse_over < items.size() && !!items[mouse_over].separator && items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { - _activate_submenu(mouse_over); + if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && !items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { + _activate_submenu(mouse_over, true); set_input_as_handled(); } } else if (p_event->is_action("ui_accept") && p_event->is_pressed()) { if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) { if (!items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { - _activate_submenu(mouse_over); + _activate_submenu(mouse_over, true); } else { activate_item(mouse_over); } @@ -396,6 +398,11 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> m = p_event; if (m.is_valid()) { + if (m->get_velocity().is_equal_approx(Vector2())) { + return; + } + activated_by_keyboard = false; + for (const Rect2 &E : autohide_areas) { if (!Rect2(Point2(), get_size()).has_point(m->get_position()) && E.has_point(m->get_position())) { _close_pressed(); @@ -687,7 +694,7 @@ void PopupMenu::_draw_background() { void PopupMenu::_minimum_lifetime_timeout() { close_allowed = true; // If the mouse still isn't in this popup after timer expires, close. - if (!get_visible_rect().has_point(get_mouse_position())) { + if (!activated_by_keyboard && !get_visible_rect().has_point(get_mouse_position())) { _close_pressed(); } } @@ -772,7 +779,7 @@ void PopupMenu::_notification(int p_what) { case NOTIFICATION_INTERNAL_PROCESS: { // Only used when using operating system windows. - if (!is_embedded() && autohide_areas.size()) { + if (!activated_by_keyboard && !is_embedded() && autohide_areas.size()) { Point2 mouse_pos = DisplayServer::get_singleton()->mouse_get_position(); mouse_pos -= get_position(); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 518ba14dae..98d76875cb 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -87,6 +87,7 @@ class PopupMenu : public Popup { }; bool close_allowed = false; + bool activated_by_keyboard = false; Timer *minimum_lifetime_timer = nullptr; Timer *submenu_timer = nullptr; @@ -107,7 +108,7 @@ class PopupMenu : public Popup { void _shape_item(int p_item); virtual void gui_input(const Ref<InputEvent> &p_event); - void _activate_submenu(int p_over); + void _activate_submenu(int p_over, bool p_by_keyboard = false); void _submenu_timeout(); uint64_t popup_time_msec = 0; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 8e26fae8c2..ec13399f82 100644 --- a/scene/gui/rich_text_label.cpp +++ b/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 ""; @@ -4211,7 +4257,7 @@ void RichTextLabel::select_all() { while (it) { if (it->type != ITEM_FRAME) { - if (from_item == nullptr) { + if (!from_item) { from_item = it; } else { to_item = it; @@ -4219,13 +4265,22 @@ void RichTextLabel::select_all() { } it = _get_next_item(it, true); } + if (!from_item || !to_item) { + return; + } ItemFrame *from_frame = nullptr; int from_line = 0; _find_frame(from_item, &from_frame, &from_line); + if (!from_frame) { + return; + } ItemFrame *to_frame = nullptr; int to_line = 0; _find_frame(to_item, &to_frame, &to_line); + if (!to_frame) { + return; + } selection.from_line = from_line; selection.from_frame = from_frame; selection.from_char = 0; @@ -4527,6 +4582,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 +4637,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 +4661,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 +4843,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); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 9ee182c668..856dd52b6e 100644 --- a/scene/gui/rich_text_label.h +++ b/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); |