2014-02-10 01:10:30 +00:00
|
|
|
/*************************************************************************/
|
|
|
|
/* popup_menu.cpp */
|
|
|
|
/*************************************************************************/
|
|
|
|
/* This file is part of: */
|
|
|
|
/* GODOT ENGINE */
|
2017-08-27 12:16:55 +00:00
|
|
|
/* https://godotengine.org */
|
2014-02-10 01:10:30 +00:00
|
|
|
/*************************************************************************/
|
2021-01-01 19:13:46 +00:00
|
|
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
|
|
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
2014-02-10 01:10:30 +00:00
|
|
|
/* */
|
|
|
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
|
|
|
/* a copy of this software and associated documentation files (the */
|
|
|
|
/* "Software"), to deal in the Software without restriction, including */
|
|
|
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
|
|
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
|
|
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
|
|
|
/* the following conditions: */
|
|
|
|
/* */
|
|
|
|
/* The above copyright notice and this permission notice shall be */
|
|
|
|
/* included in all copies or substantial portions of the Software. */
|
|
|
|
/* */
|
|
|
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
|
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
|
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
|
|
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
|
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
|
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
|
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
|
|
/*************************************************************************/
|
2018-01-04 23:50:27 +00:00
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
#include "popup_menu.h"
|
2020-02-21 22:26:13 +00:00
|
|
|
|
2020-04-28 13:19:37 +00:00
|
|
|
#include "core/input/input.h"
|
2018-09-11 16:13:45 +00:00
|
|
|
#include "core/os/keyboard.h"
|
2019-05-11 16:32:53 +00:00
|
|
|
#include "core/os/os.h"
|
2020-11-07 22:33:38 +00:00
|
|
|
#include "core/string/print_string.h"
|
|
|
|
#include "core/string/translation.h"
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-09-03 11:22:16 +00:00
|
|
|
String PopupMenu::_get_accel_text(const Item &p_item) const {
|
|
|
|
if (p_item.shortcut.is_valid()) {
|
|
|
|
return p_item.shortcut->get_as_text();
|
2021-08-13 21:31:57 +00:00
|
|
|
} else if (p_item.accel != Key::NONE) {
|
2020-09-03 11:22:16 +00:00
|
|
|
return keycode_get_string(p_item.accel);
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2016-06-05 00:31:29 +00:00
|
|
|
return String();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
Size2 PopupMenu::_get_contents_minimum_size() const {
|
2021-07-17 21:22:52 +00:00
|
|
|
int vseparation = get_theme_constant(SNAME("vseparation"));
|
|
|
|
int hseparation = get_theme_constant(SNAME("hseparation"));
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2021-07-17 21:22:52 +00:00
|
|
|
Size2 minsize = get_theme_stylebox(SNAME("panel"))->get_minimum_size(); // Accounts for margin in the margin container
|
2020-08-29 12:05:59 +00:00
|
|
|
minsize.x += scroll_container->get_v_scrollbar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2021-02-09 17:24:36 +00:00
|
|
|
float max_w = 0.0;
|
|
|
|
float icon_w = 0.0;
|
2021-07-17 21:22:52 +00:00
|
|
|
int check_w = MAX(get_theme_icon(SNAME("checked"))->get_width(), get_theme_icon(SNAME("radio_checked"))->get_width()) + hseparation;
|
2017-03-05 15:44:50 +00:00
|
|
|
int accel_max_w = 0;
|
2019-03-18 13:10:19 +00:00
|
|
|
bool has_check = false;
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
for (int i = 0; i < items.size(); i++) {
|
2014-02-10 01:10:30 +00:00
|
|
|
Size2 size;
|
2020-08-29 12:05:59 +00:00
|
|
|
|
|
|
|
Size2 icon_size = items[i].get_icon_size();
|
2021-02-16 03:30:57 +00:00
|
|
|
size.height = _get_item_height(i);
|
2020-08-29 12:05:59 +00:00
|
|
|
icon_w = MAX(icon_size.width, icon_w);
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
size.width += items[i].h_ofs;
|
2016-09-17 22:01:11 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (items[i].checkable_type) {
|
2019-03-18 13:10:19 +00:00
|
|
|
has_check = true;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2020-09-03 11:22:16 +00:00
|
|
|
size.width += items[i].text_buf->get_size().x;
|
2021-01-04 08:31:13 +00:00
|
|
|
size.height += vseparation;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-08-13 21:31:57 +00:00
|
|
|
if (items[i].accel != Key::NONE || (items[i].shortcut.is_valid() && items[i].shortcut->has_valid_event())) {
|
2017-03-05 15:44:50 +00:00
|
|
|
int accel_w = hseparation * 2;
|
2020-09-03 11:22:16 +00:00
|
|
|
accel_w += items[i].accel_text_buf->get_size().x;
|
2017-03-05 15:44:50 +00:00
|
|
|
accel_max_w = MAX(accel_w, accel_max_w);
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (items[i].submenu != "") {
|
2021-07-17 21:22:52 +00:00
|
|
|
size.width += get_theme_icon(SNAME("submenu"))->get_width();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2019-03-18 13:10:19 +00:00
|
|
|
|
2019-05-01 05:30:56 +00:00
|
|
|
max_w = MAX(max_w, size.width);
|
2017-05-14 11:42:42 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
minsize.height += size.height;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2021-07-17 21:22:52 +00:00
|
|
|
int item_side_padding = get_theme_constant(SNAME("item_start_padding")) + get_theme_constant(SNAME("item_end_padding"));
|
2021-02-04 13:03:36 +00:00
|
|
|
minsize.width += max_w + icon_w + accel_max_w + item_side_padding;
|
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (has_check) {
|
2019-05-01 05:30:56 +00:00
|
|
|
minsize.width += check_w;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2020-09-06 03:59:40 +00:00
|
|
|
if (is_inside_tree()) {
|
|
|
|
int height_limit = get_usable_parent_rect().size.height;
|
|
|
|
if (minsize.height > height_limit) {
|
|
|
|
minsize.height = height_limit;
|
|
|
|
}
|
2020-08-29 12:05:59 +00:00
|
|
|
}
|
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
return minsize;
|
|
|
|
}
|
|
|
|
|
2021-02-16 03:30:57 +00:00
|
|
|
int PopupMenu::_get_item_height(int p_item) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_item, items.size(), 0);
|
|
|
|
ERR_FAIL_COND_V(p_item < 0, 0);
|
|
|
|
|
|
|
|
int icon_height = items[p_item].get_icon_size().height;
|
|
|
|
if (items[p_item].checkable_type) {
|
2021-07-17 21:22:52 +00:00
|
|
|
icon_height = MAX(icon_height, MAX(get_theme_icon(SNAME("checked"))->get_height(), get_theme_icon(SNAME("radio_checked"))->get_height()));
|
2021-02-16 03:30:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int text_height = items[p_item].text_buf->get_size().height;
|
|
|
|
if (text_height == 0 && !items[p_item].separator) {
|
2021-07-17 21:22:52 +00:00
|
|
|
text_height = get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size")));
|
2021-02-16 03:30:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int separator_height = 0;
|
|
|
|
if (items[p_item].separator) {
|
2021-07-17 21:22:52 +00:00
|
|
|
separator_height = MAX(get_theme_stylebox(SNAME("separator"))->get_minimum_size().height, MAX(get_theme_stylebox(SNAME("labeled_separator_left"))->get_minimum_size().height, get_theme_stylebox(SNAME("labeled_separator_right"))->get_minimum_size().height));
|
2021-02-16 03:30:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return MAX(separator_height, MAX(text_height, icon_height));
|
|
|
|
}
|
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
int PopupMenu::_get_items_total_height() const {
|
2021-07-17 21:22:52 +00:00
|
|
|
int vsep = get_theme_constant(SNAME("vseparation"));
|
2020-08-29 12:05:59 +00:00
|
|
|
|
|
|
|
// Get total height of all items by taking max of icon height and font height
|
|
|
|
int items_total_height = 0;
|
|
|
|
for (int i = 0; i < items.size(); i++) {
|
2021-02-16 03:30:57 +00:00
|
|
|
items_total_height += _get_item_height(i) + vsep;
|
2020-08-29 12:05:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Subtract a separator which is not needed for the last item.
|
|
|
|
return items_total_height - vsep;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PopupMenu::_scroll_to_item(int p_item) {
|
|
|
|
ERR_FAIL_INDEX(p_item, items.size());
|
|
|
|
ERR_FAIL_COND(p_item < 0);
|
|
|
|
|
|
|
|
// Scroll item into view (upwards)
|
|
|
|
if (items[p_item]._ofs_cache < -control->get_position().y) {
|
|
|
|
int amnt_over = items[p_item]._ofs_cache + control->get_position().y;
|
|
|
|
scroll_container->set_v_scroll(scroll_container->get_v_scroll() + amnt_over);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scroll item into view (downwards)
|
|
|
|
if (items[p_item]._ofs_cache + items[p_item]._height_cache > -control->get_position().y + scroll_container->get_size().height) {
|
|
|
|
int amnt_over = items[p_item]._ofs_cache + items[p_item]._height_cache + control->get_position().y - scroll_container->get_size().height;
|
|
|
|
scroll_container->set_v_scroll(scroll_container->get_v_scroll() + amnt_over);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (p_over.x < 0 || p_over.x >= get_size().width) {
|
2014-02-10 01:10:30 +00:00
|
|
|
return -1;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-07-17 21:22:52 +00:00
|
|
|
Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); // Accounts for margin in the margin container
|
2020-08-29 12:05:59 +00:00
|
|
|
|
2021-07-17 21:22:52 +00:00
|
|
|
int vseparation = get_theme_constant(SNAME("vseparation"));
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
Point2 ofs = style->get_offset() + Point2(0, vseparation / 2);
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (ofs.y > p_over.y) {
|
2014-02-10 01:10:30 +00:00
|
|
|
return -1;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
for (int i = 0; i < items.size(); i++) {
|
2021-01-04 08:31:13 +00:00
|
|
|
ofs.y += i > 0 ? vseparation : (float)vseparation / 2;
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2021-02-16 03:30:57 +00:00
|
|
|
ofs.y += _get_item_height(i);
|
2020-08-29 12:05:59 +00:00
|
|
|
|
|
|
|
if (p_over.y - control->get_position().y < ofs.y) {
|
2014-02-10 01:10:30 +00:00
|
|
|
return i;
|
2016-03-08 23:00:52 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-09-07 12:57:50 +00:00
|
|
|
void PopupMenu::_activate_submenu(int p_over) {
|
|
|
|
Node *n = get_node(items[p_over].submenu);
|
|
|
|
ERR_FAIL_COND_MSG(!n, "Item subnode does not exist: " + items[p_over].submenu + ".");
|
2020-08-29 12:05:59 +00:00
|
|
|
Popup *submenu_popup = Object::cast_to<Popup>(n);
|
2020-09-07 12:57:50 +00:00
|
|
|
ERR_FAIL_COND_MSG(!submenu_popup, "Item subnode is not a Popup: " + items[p_over].submenu + ".");
|
2020-08-29 12:05:59 +00:00
|
|
|
if (submenu_popup->is_visible()) {
|
2014-02-10 01:10:30 +00:00
|
|
|
return; //already visible!
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-07-17 21:22:52 +00:00
|
|
|
Ref<StyleBox> style = get_theme_stylebox(SNAME("panel"));
|
|
|
|
int vsep = get_theme_constant(SNAME("vseparation"));
|
2015-11-22 14:53:22 +00:00
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
Point2 this_pos = get_position();
|
|
|
|
Rect2 this_rect(this_pos, get_size());
|
|
|
|
|
|
|
|
float scroll_offset = control->get_position().y;
|
|
|
|
|
2020-09-03 11:22:16 +00:00
|
|
|
Point2 submenu_pos;
|
2020-08-29 12:05:59 +00:00
|
|
|
Size2 submenu_size = submenu_popup->get_size();
|
2020-09-03 11:22:16 +00:00
|
|
|
if (control->is_layout_rtl()) {
|
|
|
|
submenu_pos = this_pos + Point2(-submenu_size.width, items[p_over]._ofs_cache + scroll_offset);
|
|
|
|
} else {
|
|
|
|
submenu_pos = this_pos + Point2(this_rect.size.width, items[p_over]._ofs_cache + scroll_offset);
|
|
|
|
}
|
2020-08-29 12:05:59 +00:00
|
|
|
|
|
|
|
// Fix pos if going outside parent rect
|
2020-09-03 11:22:16 +00:00
|
|
|
if (submenu_pos.x < get_parent_rect().position.x) {
|
|
|
|
submenu_pos.x = this_pos.x + submenu_size.width;
|
|
|
|
}
|
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
if (submenu_pos.x + submenu_size.width > get_parent_rect().size.width) {
|
|
|
|
submenu_pos.x = this_pos.x - submenu_size.width;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2015-11-22 14:53:22 +00:00
|
|
|
|
2020-09-07 12:57:50 +00:00
|
|
|
submenu_popup->set_close_on_parent_focus(false);
|
2020-08-29 12:05:59 +00:00
|
|
|
submenu_popup->set_position(submenu_pos);
|
2021-03-12 13:35:16 +00:00
|
|
|
submenu_popup->set_as_minsize(); // Shrink the popup size to its contents.
|
2020-08-29 12:05:59 +00:00
|
|
|
submenu_popup->popup();
|
|
|
|
|
|
|
|
// Set autohide areas
|
|
|
|
PopupMenu *submenu_pum = Object::cast_to<PopupMenu>(submenu_popup);
|
|
|
|
if (submenu_pum) {
|
2021-08-20 09:49:19 +00:00
|
|
|
submenu_pum->take_mouse_focus();
|
2020-08-29 12:05:59 +00:00
|
|
|
// Make the position of the parent popup relative to submenu popup
|
|
|
|
this_rect.position = this_rect.position - submenu_pum->get_position();
|
|
|
|
|
|
|
|
// Autohide area above the submenu item
|
|
|
|
submenu_pum->clear_autohide_areas();
|
2020-09-07 12:57:50 +00:00
|
|
|
submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2));
|
2020-08-29 12:05:59 +00:00
|
|
|
|
|
|
|
// If there is an area below the submenu item, add an autohide area there.
|
2020-09-07 12:57:50 +00:00
|
|
|
if (items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset <= control->get_size().height) {
|
|
|
|
int from = items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset + vsep / 2 + style->get_offset().height;
|
2020-08-29 12:05:59 +00:00
|
|
|
submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y + from, this_rect.size.x, this_rect.size.y - from));
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PopupMenu::_submenu_timeout() {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (mouse_over == submenu_over) {
|
2014-02-10 01:10:30 +00:00
|
|
|
_activate_submenu(mouse_over);
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-12-06 19:27:21 +00:00
|
|
|
|
|
|
|
submenu_over = -1;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2021-08-22 15:37:22 +00:00
|
|
|
void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
|
2021-04-05 06:52:21 +00:00
|
|
|
ERR_FAIL_COND(p_event.is_null());
|
|
|
|
|
2020-12-09 16:25:00 +00:00
|
|
|
if (p_event->is_action("ui_down") && p_event->is_pressed()) {
|
2017-12-31 03:42:25 +00:00
|
|
|
int search_from = mouse_over + 1;
|
2020-05-14 14:41:43 +00:00
|
|
|
if (search_from >= items.size()) {
|
2017-12-31 03:42:25 +00:00
|
|
|
search_from = 0;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-12-10 12:32:02 +00:00
|
|
|
bool match_found = false;
|
2017-12-31 03:42:25 +00:00
|
|
|
for (int i = search_from; i < items.size(); i++) {
|
|
|
|
if (!items[i].separator && !items[i].disabled) {
|
|
|
|
mouse_over = i;
|
2021-07-17 21:22:52 +00:00
|
|
|
emit_signal(SNAME("id_focused"), i);
|
2020-08-29 12:05:59 +00:00
|
|
|
_scroll_to_item(i);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
set_input_as_handled();
|
2020-12-10 12:32:02 +00:00
|
|
|
match_found = true;
|
2017-12-31 03:42:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-12-10 12:32:02 +00:00
|
|
|
|
|
|
|
if (!match_found) {
|
|
|
|
// If the last item is not selectable, try re-searching from the start.
|
|
|
|
for (int i = 0; i < search_from; i++) {
|
|
|
|
if (!items[i].separator && !items[i].disabled) {
|
|
|
|
mouse_over = i;
|
2021-07-17 21:22:52 +00:00
|
|
|
emit_signal(SNAME("id_focused"), i);
|
2020-12-10 12:32:02 +00:00
|
|
|
_scroll_to_item(i);
|
|
|
|
control->update();
|
|
|
|
set_input_as_handled();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-09 16:25:00 +00:00
|
|
|
} else if (p_event->is_action("ui_up") && p_event->is_pressed()) {
|
2017-12-31 03:42:25 +00:00
|
|
|
int search_from = mouse_over - 1;
|
2020-05-14 14:41:43 +00:00
|
|
|
if (search_from < 0) {
|
2017-12-31 03:42:25 +00:00
|
|
|
search_from = items.size() - 1;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-12-16 22:54:44 +00:00
|
|
|
|
2020-12-10 12:32:02 +00:00
|
|
|
bool match_found = false;
|
2017-12-31 03:42:25 +00:00
|
|
|
for (int i = search_from; i >= 0; i--) {
|
|
|
|
if (!items[i].separator && !items[i].disabled) {
|
|
|
|
mouse_over = i;
|
2021-07-17 21:22:52 +00:00
|
|
|
emit_signal(SNAME("id_focused"), i);
|
2020-08-29 12:05:59 +00:00
|
|
|
_scroll_to_item(i);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
set_input_as_handled();
|
2020-12-10 12:32:02 +00:00
|
|
|
match_found = true;
|
2017-12-31 03:42:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-12-10 12:32:02 +00:00
|
|
|
|
|
|
|
if (!match_found) {
|
|
|
|
// If the first item is not selectable, try re-searching from the end.
|
|
|
|
for (int i = items.size() - 1; i >= search_from; i--) {
|
|
|
|
if (!items[i].separator && !items[i].disabled) {
|
|
|
|
mouse_over = i;
|
2021-07-17 21:22:52 +00:00
|
|
|
emit_signal(SNAME("id_focused"), i);
|
2020-12-10 12:32:02 +00:00
|
|
|
_scroll_to_item(i);
|
|
|
|
control->update();
|
|
|
|
set_input_as_handled();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-12-31 03:42:25 +00:00
|
|
|
} else if (p_event->is_action("ui_left") && p_event->is_pressed()) {
|
|
|
|
Node *n = get_parent();
|
|
|
|
if (n && Object::cast_to<PopupMenu>(n)) {
|
|
|
|
hide();
|
2020-03-12 12:37:40 +00:00
|
|
|
set_input_as_handled();
|
2017-12-31 03:42:25 +00:00
|
|
|
}
|
|
|
|
} 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 != "" && submenu_over != mouse_over) {
|
|
|
|
_activate_submenu(mouse_over);
|
2020-03-12 12:37:40 +00:00
|
|
|
set_input_as_handled();
|
2017-12-31 03:42:25 +00:00
|
|
|
}
|
|
|
|
} 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 != "" && submenu_over != mouse_over) {
|
|
|
|
_activate_submenu(mouse_over);
|
|
|
|
} else {
|
|
|
|
activate_item(mouse_over);
|
|
|
|
}
|
2020-03-12 12:37:40 +00:00
|
|
|
set_input_as_handled();
|
2017-05-20 15:38:03 +00:00
|
|
|
}
|
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
// Make an area which does not include v scrollbar, so that items are not activated when dragging scrollbar.
|
2020-09-01 21:17:46 +00:00
|
|
|
Rect2 item_clickable_area = scroll_container->get_rect();
|
|
|
|
if (scroll_container->get_v_scrollbar()->is_visible_in_tree()) {
|
2020-09-03 11:22:16 +00:00
|
|
|
if (is_layout_rtl()) {
|
|
|
|
item_clickable_area.position.x += scroll_container->get_v_scrollbar()->get_size().width;
|
|
|
|
} else {
|
|
|
|
item_clickable_area.size.width -= scroll_container->get_v_scrollbar()->get_size().width;
|
|
|
|
}
|
2020-09-01 21:17:46 +00:00
|
|
|
}
|
2020-08-29 12:05:59 +00:00
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
Ref<InputEventMouseButton> b = p_event;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
if (b.is_valid()) {
|
2020-09-01 21:17:46 +00:00
|
|
|
if (!item_clickable_area.has_point(b->get_position())) {
|
2017-05-20 15:38:03 +00:00
|
|
|
return;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-08-13 21:31:57 +00:00
|
|
|
MouseButton button_idx = b->get_button_index();
|
2021-08-19 13:54:32 +00:00
|
|
|
if (!b->is_pressed()) {
|
|
|
|
// Activate the item on release of either the left mouse button or
|
|
|
|
// any mouse button held down when the popup was opened.
|
|
|
|
// This allows for opening the popup and triggering an action in a single mouse click.
|
2021-08-13 21:31:57 +00:00
|
|
|
if (button_idx == MouseButton::LEFT || (initial_button_mask & mouse_button_to_mask(button_idx)) != MouseButton::NONE) {
|
2020-08-29 12:05:59 +00:00
|
|
|
bool was_during_grabbed_click = during_grabbed_click;
|
|
|
|
during_grabbed_click = false;
|
2021-08-13 21:31:57 +00:00
|
|
|
initial_button_mask = MouseButton::NONE;
|
2020-08-29 12:05:59 +00:00
|
|
|
|
2020-09-18 18:45:59 +00:00
|
|
|
// Disable clicks under a time threshold to avoid selection right when opening the popup.
|
|
|
|
uint64_t now = OS::get_singleton()->get_ticks_msec();
|
|
|
|
uint64_t diff = now - popup_time_msec;
|
|
|
|
if (diff < 100) {
|
2020-08-29 12:05:59 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-09-18 18:45:59 +00:00
|
|
|
|
|
|
|
int over = _get_mouse_over(b->get_position());
|
2020-08-29 12:05:59 +00:00
|
|
|
if (over < 0) {
|
|
|
|
if (!was_during_grabbed_click) {
|
|
|
|
hide();
|
2018-03-27 21:41:27 +00:00
|
|
|
}
|
2020-08-29 12:05:59 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-03-27 21:41:27 +00:00
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
if (items[over].separator || items[over].disabled) {
|
|
|
|
return;
|
|
|
|
}
|
2018-03-27 21:41:27 +00:00
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
if (items[over].submenu != "") {
|
|
|
|
_activate_submenu(over);
|
|
|
|
return;
|
2017-05-20 15:38:03 +00:00
|
|
|
}
|
2020-08-29 12:05:59 +00:00
|
|
|
activate_item(over);
|
2018-03-27 21:41:27 +00:00
|
|
|
}
|
2017-05-20 15:38:03 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
Ref<InputEventMouseMotion> m = p_event;
|
2015-01-03 19:52:37 +00:00
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
if (m.is_valid()) {
|
2021-07-24 13:46:25 +00:00
|
|
|
for (const Rect2 &E : autohide_areas) {
|
2021-07-16 03:45:57 +00:00
|
|
|
if (!Rect2(Point2(), get_size()).has_point(m->get_position()) && E.has_point(m->get_position())) {
|
2020-03-20 02:32:09 +00:00
|
|
|
_close_pressed();
|
2017-05-20 15:38:03 +00:00
|
|
|
return;
|
2015-11-05 14:22:50 +00:00
|
|
|
}
|
2017-05-20 15:38:03 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-07-23 15:13:01 +00:00
|
|
|
if (!item_clickable_area.has_point(m->get_position())) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-06-03 08:54:24 +00:00
|
|
|
int over = _get_mouse_over(m->get_position());
|
2019-05-09 09:21:49 +00:00
|
|
|
int id = (over < 0 || items[over].separator || items[over].disabled) ? -1 : (items[over].id >= 0 ? items[over].id : over);
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
if (id < 0) {
|
|
|
|
mouse_over = -1;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
2017-05-20 15:38:03 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (items[over].submenu != "" && submenu_over != over) {
|
|
|
|
submenu_over = over;
|
|
|
|
submenu_timer->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (over != mouse_over) {
|
|
|
|
mouse_over = over;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
2017-05-20 15:38:03 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2018-01-06 15:21:27 +00:00
|
|
|
|
2019-05-11 16:32:53 +00:00
|
|
|
Ref<InputEventKey> k = p_event;
|
|
|
|
|
2020-03-30 19:45:56 +00:00
|
|
|
if (allow_search && k.is_valid() && k->get_unicode() && k->is_pressed()) {
|
2019-05-11 16:32:53 +00:00
|
|
|
uint64_t now = OS::get_singleton()->get_ticks_msec();
|
|
|
|
uint64_t diff = now - search_time_msec;
|
|
|
|
uint64_t max_interval = uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000));
|
|
|
|
search_time_msec = now;
|
|
|
|
|
|
|
|
if (diff > max_interval) {
|
|
|
|
search_string = "";
|
|
|
|
}
|
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (String::chr(k->get_unicode()) != search_string) {
|
2019-05-11 16:32:53 +00:00
|
|
|
search_string += String::chr(k->get_unicode());
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2019-05-11 16:32:53 +00:00
|
|
|
|
|
|
|
for (int i = mouse_over + 1; i <= items.size(); i++) {
|
|
|
|
if (i == items.size()) {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (mouse_over <= 0) {
|
2019-05-11 16:32:53 +00:00
|
|
|
break;
|
2020-05-14 14:41:43 +00:00
|
|
|
} else {
|
2019-05-11 16:32:53 +00:00
|
|
|
i = 0;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2019-05-11 16:32:53 +00:00
|
|
|
}
|
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (i == mouse_over) {
|
2019-05-11 16:32:53 +00:00
|
|
|
break;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2019-05-11 16:32:53 +00:00
|
|
|
|
|
|
|
if (items[i].text.findn(search_string) == 0) {
|
|
|
|
mouse_over = i;
|
2021-07-17 21:22:52 +00:00
|
|
|
emit_signal(SNAME("id_focused"), i);
|
2020-08-29 12:05:59 +00:00
|
|
|
_scroll_to_item(i);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
set_input_as_handled();
|
2019-05-11 16:32:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
void PopupMenu::_draw_items() {
|
|
|
|
control->set_custom_minimum_size(Size2(0, _get_items_total_height()));
|
2020-03-12 12:37:40 +00:00
|
|
|
RID ci = control->get_canvas_item();
|
2020-08-29 12:05:59 +00:00
|
|
|
|
|
|
|
Size2 margin_size;
|
2021-07-17 21:22:52 +00:00
|
|
|
margin_size.width = margin_container->get_theme_constant(SNAME("margin_right")) + margin_container->get_theme_constant(SNAME("margin_left"));
|
|
|
|
margin_size.height = margin_container->get_theme_constant(SNAME("margin_top")) + margin_container->get_theme_constant(SNAME("margin_bottom"));
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-02-04 13:03:36 +00:00
|
|
|
// Space between the item content and the sides of popup menu.
|
2021-07-17 21:22:52 +00:00
|
|
|
int item_start_padding = get_theme_constant(SNAME("item_start_padding"));
|
|
|
|
int item_end_padding = get_theme_constant(SNAME("item_end_padding"));
|
2021-02-04 13:03:36 +00:00
|
|
|
|
2020-09-03 11:22:16 +00:00
|
|
|
bool rtl = control->is_layout_rtl();
|
2021-07-17 21:22:52 +00:00
|
|
|
Ref<StyleBox> style = get_theme_stylebox(SNAME("panel"));
|
|
|
|
Ref<StyleBox> hover = get_theme_stylebox(SNAME("hover"));
|
2020-03-12 12:37:40 +00:00
|
|
|
// In Item::checkable_type enum order (less the non-checkable member)
|
2021-07-17 21:22:52 +00:00
|
|
|
Ref<Texture2D> check[] = { get_theme_icon(SNAME("checked")), get_theme_icon(SNAME("radio_checked")) };
|
|
|
|
Ref<Texture2D> uncheck[] = { get_theme_icon(SNAME("unchecked")), get_theme_icon(SNAME("radio_unchecked")) };
|
2020-09-03 11:22:16 +00:00
|
|
|
Ref<Texture2D> submenu;
|
|
|
|
if (rtl) {
|
2021-07-17 21:22:52 +00:00
|
|
|
submenu = get_theme_icon(SNAME("submenu_mirrored"));
|
2020-09-03 11:22:16 +00:00
|
|
|
} else {
|
2021-07-17 21:22:52 +00:00
|
|
|
submenu = get_theme_icon(SNAME("submenu"));
|
2020-09-03 11:22:16 +00:00
|
|
|
}
|
|
|
|
|
2021-07-17 21:22:52 +00:00
|
|
|
Ref<StyleBox> separator = get_theme_stylebox(SNAME("separator"));
|
|
|
|
Ref<StyleBox> labeled_separator_left = get_theme_stylebox(SNAME("labeled_separator_left"));
|
|
|
|
Ref<StyleBox> labeled_separator_right = get_theme_stylebox(SNAME("labeled_separator_right"));
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-07-17 21:22:52 +00:00
|
|
|
int vseparation = get_theme_constant(SNAME("vseparation"));
|
|
|
|
int hseparation = get_theme_constant(SNAME("hseparation"));
|
|
|
|
Color font_color = get_theme_color(SNAME("font_color"));
|
|
|
|
Color font_disabled_color = get_theme_color(SNAME("font_disabled_color"));
|
|
|
|
Color font_accelerator_color = get_theme_color(SNAME("font_accelerator_color"));
|
|
|
|
Color font_hover_color = get_theme_color(SNAME("font_hover_color"));
|
|
|
|
Color font_separator_color = get_theme_color(SNAME("font_separator_color"));
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
float scroll_width = scroll_container->get_v_scrollbar()->is_visible_in_tree() ? scroll_container->get_v_scrollbar()->get_size().width : 0;
|
|
|
|
float display_width = control->get_size().width - scroll_width;
|
|
|
|
|
|
|
|
// Find the widest icon and whether any items have a checkbox, and store the offsets for each.
|
2020-03-12 12:37:40 +00:00
|
|
|
float icon_ofs = 0.0;
|
|
|
|
bool has_check = false;
|
|
|
|
for (int i = 0; i < items.size(); i++) {
|
2020-08-29 12:05:59 +00:00
|
|
|
icon_ofs = MAX(items[i].get_icon_size().width, icon_ofs);
|
2018-07-09 12:16:06 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (items[i].checkable_type) {
|
2020-03-12 12:37:40 +00:00
|
|
|
has_check = true;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2020-03-12 12:37:40 +00:00
|
|
|
}
|
2020-05-14 14:41:43 +00:00
|
|
|
if (icon_ofs > 0.0) {
|
2020-03-12 12:37:40 +00:00
|
|
|
icon_ofs += hseparation;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
float check_ofs = 0.0;
|
2020-05-14 14:41:43 +00:00
|
|
|
if (has_check) {
|
2021-07-17 21:22:52 +00:00
|
|
|
check_ofs = MAX(get_theme_icon(SNAME("checked"))->get_width(), get_theme_icon(SNAME("radio_checked"))->get_width()) + hseparation;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-01-09 19:43:44 +00:00
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
Point2 ofs = Point2();
|
|
|
|
|
|
|
|
// Loop through all items and draw each.
|
2020-03-12 12:37:40 +00:00
|
|
|
for (int i = 0; i < items.size(); i++) {
|
2021-01-04 08:31:13 +00:00
|
|
|
// For the first item only add half a separation. For all other items, add a whole separation to the offset.
|
|
|
|
ofs.y += i > 0 ? vseparation : (float)vseparation / 2;
|
2019-03-18 13:10:19 +00:00
|
|
|
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(i);
|
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
Point2 item_ofs = ofs;
|
|
|
|
Size2 icon_size = items[i].get_icon_size();
|
2021-02-16 03:30:57 +00:00
|
|
|
float h = _get_item_height(i);
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
if (i == mouse_over) {
|
2020-09-03 11:22:16 +00:00
|
|
|
if (rtl) {
|
2021-01-04 08:31:13 +00:00
|
|
|
hover->draw(ci, Rect2(item_ofs + Point2(scroll_width, -vseparation / 2), Size2(display_width, h + vseparation)));
|
2020-09-03 11:22:16 +00:00
|
|
|
} else {
|
2021-01-04 08:31:13 +00:00
|
|
|
hover->draw(ci, Rect2(item_ofs + Point2(0, -vseparation / 2), Size2(display_width, h + vseparation)));
|
2020-09-03 11:22:16 +00:00
|
|
|
}
|
2020-03-12 12:37:40 +00:00
|
|
|
}
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
String text = items[i].xl_text;
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
// Separator
|
2020-03-12 12:37:40 +00:00
|
|
|
item_ofs.x += items[i].h_ofs;
|
|
|
|
if (items[i].separator) {
|
|
|
|
int sep_h = separator->get_center_size().height + separator->get_minimum_size().height;
|
2021-02-16 03:30:57 +00:00
|
|
|
int sep_ofs = Math::floor((h - sep_h) / 2.0);
|
2020-03-12 12:37:40 +00:00
|
|
|
if (text != String()) {
|
2020-09-03 11:22:16 +00:00
|
|
|
int text_size = items[i].text_buf->get_size().width;
|
2020-08-29 12:05:59 +00:00
|
|
|
int text_center = display_width / 2;
|
|
|
|
int text_left = text_center - text_size / 2;
|
|
|
|
int text_right = text_center + text_size / 2;
|
|
|
|
if (text_left > item_ofs.x) {
|
2021-02-16 03:30:57 +00:00
|
|
|
labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, text_left - item_ofs.x), sep_h)));
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2020-08-29 12:05:59 +00:00
|
|
|
if (text_right < display_width) {
|
2021-02-16 03:30:57 +00:00
|
|
|
labeled_separator_right->draw(ci, Rect2(Point2(text_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - text_right), sep_h)));
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2020-03-12 12:37:40 +00:00
|
|
|
} else {
|
2021-02-16 03:30:57 +00:00
|
|
|
separator->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(display_width, sep_h)));
|
2020-03-12 12:37:40 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
Color icon_color(1, 1, 1, items[i].disabled ? 0.5 : 1);
|
2018-07-14 21:15:42 +00:00
|
|
|
|
2021-02-04 13:03:36 +00:00
|
|
|
// For non-separator items, add some padding for the content.
|
|
|
|
item_ofs.x += item_start_padding;
|
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
// Checkboxes
|
2020-03-12 12:37:40 +00:00
|
|
|
if (items[i].checkable_type) {
|
|
|
|
Texture2D *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr();
|
2020-09-03 11:22:16 +00:00
|
|
|
if (rtl) {
|
|
|
|
icon->draw(ci, Size2(control->get_size().width - item_ofs.x - icon->get_width(), item_ofs.y) + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color);
|
|
|
|
} else {
|
|
|
|
icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color);
|
|
|
|
}
|
2020-03-12 12:37:40 +00:00
|
|
|
}
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
// Icon
|
2020-03-12 12:37:40 +00:00
|
|
|
if (!items[i].icon.is_null()) {
|
2020-09-03 11:22:16 +00:00
|
|
|
if (rtl) {
|
|
|
|
items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
|
|
|
|
} else {
|
|
|
|
items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
|
|
|
|
}
|
2020-03-12 12:37:40 +00:00
|
|
|
}
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
// Submenu arrow on right hand side
|
2020-03-12 12:37:40 +00:00
|
|
|
if (items[i].submenu != "") {
|
2020-09-03 11:22:16 +00:00
|
|
|
if (rtl) {
|
2021-02-04 13:03:36 +00:00
|
|
|
submenu->draw(ci, Point2(scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
|
2020-09-03 11:22:16 +00:00
|
|
|
} else {
|
2021-02-04 13:03:36 +00:00
|
|
|
submenu->draw(ci, Point2(display_width - style->get_margin(SIDE_RIGHT) - submenu->get_width() - item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
|
2020-09-03 11:22:16 +00:00
|
|
|
}
|
2020-03-12 12:37:40 +00:00
|
|
|
}
|
2019-03-18 13:10:19 +00:00
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
// Text
|
2021-07-17 21:22:52 +00:00
|
|
|
Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
|
|
|
|
int outline_size = get_theme_constant(SNAME("outline_size"));
|
2020-03-12 12:37:40 +00:00
|
|
|
if (items[i].separator) {
|
|
|
|
if (text != String()) {
|
2020-09-03 11:22:16 +00:00
|
|
|
int center = (display_width - items[i].text_buf->get_size().width) / 2;
|
2020-12-25 21:45:28 +00:00
|
|
|
Vector2 text_pos = Point2(center, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
|
|
|
|
if (outline_size > 0 && font_outline_color.a > 0) {
|
|
|
|
items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
|
|
|
|
}
|
|
|
|
items[i].text_buf->draw(ci, text_pos, font_separator_color);
|
2020-03-12 12:37:40 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
item_ofs.x += icon_ofs + check_ofs;
|
2020-09-03 11:22:16 +00:00
|
|
|
if (rtl) {
|
2020-12-25 21:45:28 +00:00
|
|
|
Vector2 text_pos = Size2(control->get_size().width - items[i].text_buf->get_size().width - item_ofs.x, item_ofs.y) + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
|
|
|
|
if (outline_size > 0 && font_outline_color.a > 0) {
|
|
|
|
items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
|
|
|
|
}
|
|
|
|
items[i].text_buf->draw(ci, text_pos, items[i].disabled ? font_disabled_color : (i == mouse_over ? font_hover_color : font_color));
|
2020-09-03 11:22:16 +00:00
|
|
|
} else {
|
2020-12-25 21:45:28 +00:00
|
|
|
Vector2 text_pos = item_ofs + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
|
|
|
|
if (outline_size > 0 && font_outline_color.a > 0) {
|
|
|
|
items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
|
|
|
|
}
|
|
|
|
items[i].text_buf->draw(ci, text_pos, items[i].disabled ? font_disabled_color : (i == mouse_over ? font_hover_color : font_color));
|
2020-09-03 11:22:16 +00:00
|
|
|
}
|
2020-03-12 12:37:40 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
// Accelerator / Shortcut
|
2021-08-13 21:31:57 +00:00
|
|
|
if (items[i].accel != Key::NONE || (items[i].shortcut.is_valid() && items[i].shortcut->has_valid_event())) {
|
2020-09-03 11:22:16 +00:00
|
|
|
if (rtl) {
|
2021-02-04 13:03:36 +00:00
|
|
|
item_ofs.x = scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding;
|
2020-09-03 11:22:16 +00:00
|
|
|
} else {
|
2021-02-04 13:03:36 +00:00
|
|
|
item_ofs.x = display_width - style->get_margin(SIDE_RIGHT) - items[i].accel_text_buf->get_size().x - item_end_padding;
|
2020-09-03 11:22:16 +00:00
|
|
|
}
|
2020-12-25 21:45:28 +00:00
|
|
|
Vector2 text_pos = item_ofs + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
|
|
|
|
if (outline_size > 0 && font_outline_color.a > 0) {
|
|
|
|
items[i].accel_text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
|
|
|
|
}
|
|
|
|
items[i].accel_text_buf->draw(ci, text_pos, i == mouse_over ? font_hover_color : font_accelerator_color);
|
2020-03-12 12:37:40 +00:00
|
|
|
}
|
2018-07-14 21:15:42 +00:00
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
// Cache the item vertical offset from the first item and the height
|
2020-03-12 12:37:40 +00:00
|
|
|
items.write[i]._ofs_cache = ofs.y;
|
2020-08-29 12:05:59 +00:00
|
|
|
items.write[i]._height_cache = h;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
ofs.y += h;
|
|
|
|
}
|
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-08-29 12:05:59 +00:00
|
|
|
void PopupMenu::_draw_background() {
|
2021-07-17 21:22:52 +00:00
|
|
|
Ref<StyleBox> style = get_theme_stylebox(SNAME("panel"));
|
2020-08-29 12:05:59 +00:00
|
|
|
RID ci2 = margin_container->get_canvas_item();
|
|
|
|
style->draw(ci2, Rect2(Point2(), margin_container->get_size()));
|
|
|
|
}
|
|
|
|
|
2020-09-07 12:57:50 +00:00
|
|
|
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())) {
|
|
|
|
_close_pressed();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PopupMenu::_close_pressed() {
|
|
|
|
// Only apply minimum lifetime to submenus.
|
|
|
|
PopupMenu *parent_pum = Object::cast_to<PopupMenu>(get_parent());
|
|
|
|
if (!parent_pum) {
|
|
|
|
Popup::_close_pressed();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the timer has expired, close. If timer is still running, do nothing.
|
|
|
|
if (close_allowed) {
|
|
|
|
close_allowed = false;
|
|
|
|
Popup::_close_pressed();
|
|
|
|
} else if (minimum_lifetime_timer->is_stopped()) {
|
|
|
|
minimum_lifetime_timer->start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-03 11:22:16 +00:00
|
|
|
void PopupMenu::_shape_item(int p_item) {
|
|
|
|
if (items.write[p_item].dirty) {
|
|
|
|
items.write[p_item].text_buf->clear();
|
|
|
|
|
2021-07-17 21:22:52 +00:00
|
|
|
Ref<Font> font = get_theme_font(SNAME("font"));
|
|
|
|
int font_size = get_theme_font_size(SNAME("font_size"));
|
2020-09-03 11:22:16 +00:00
|
|
|
|
|
|
|
if (items[p_item].text_direction == Control::TEXT_DIRECTION_INHERITED) {
|
|
|
|
items.write[p_item].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
|
|
|
|
} else {
|
|
|
|
items.write[p_item].text_buf->set_direction((TextServer::Direction)items[p_item].text_direction);
|
|
|
|
}
|
|
|
|
items.write[p_item].text_buf->add_string(items.write[p_item].xl_text, font, font_size, items[p_item].opentype_features, (items[p_item].language != "") ? items[p_item].language : TranslationServer::get_singleton()->get_tool_locale());
|
|
|
|
|
|
|
|
items.write[p_item].accel_text_buf->clear();
|
|
|
|
items.write[p_item].accel_text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
|
|
|
|
items.write[p_item].accel_text_buf->add_string(_get_accel_text(items.write[p_item]), font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
|
|
|
|
items.write[p_item].dirty = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
void PopupMenu::_notification(int p_what) {
|
|
|
|
switch (p_what) {
|
|
|
|
case NOTIFICATION_ENTER_TREE: {
|
|
|
|
PopupMenu *pm = Object::cast_to<PopupMenu>(get_parent());
|
|
|
|
if (pm) {
|
|
|
|
// Inherit submenu's popup delay time from parent menu
|
|
|
|
float pm_delay = pm->get_submenu_popup_delay();
|
|
|
|
set_submenu_popup_delay(pm_delay);
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
} break;
|
2020-09-03 11:22:16 +00:00
|
|
|
case NOTIFICATION_THEME_CHANGED:
|
|
|
|
case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
|
2020-03-12 12:37:40 +00:00
|
|
|
case NOTIFICATION_TRANSLATION_CHANGED: {
|
|
|
|
for (int i = 0; i < items.size(); i++) {
|
2021-05-27 17:31:33 +00:00
|
|
|
items.write[i].xl_text = atr(items[i].text);
|
2020-09-03 11:22:16 +00:00
|
|
|
items.write[i].dirty = true;
|
2021-03-20 03:37:09 +00:00
|
|
|
_shape_item(i);
|
2020-03-12 12:37:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
child_controls_changed();
|
|
|
|
control->update();
|
2018-01-02 07:10:49 +00:00
|
|
|
} break;
|
2020-03-12 12:37:40 +00:00
|
|
|
case NOTIFICATION_WM_MOUSE_ENTER: {
|
2020-09-07 12:57:50 +00:00
|
|
|
grab_focus();
|
2014-02-10 01:10:30 +00:00
|
|
|
} break;
|
2020-03-12 12:37:40 +00:00
|
|
|
case NOTIFICATION_WM_MOUSE_EXIT: {
|
2017-12-15 17:17:16 +00:00
|
|
|
if (mouse_over >= 0 && (items[mouse_over].submenu == "" || submenu_over != -1)) {
|
|
|
|
mouse_over = -1;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
2017-12-15 17:17:16 +00:00
|
|
|
}
|
|
|
|
} break;
|
2018-03-27 21:41:27 +00:00
|
|
|
case NOTIFICATION_POST_POPUP: {
|
2020-04-28 13:19:37 +00:00
|
|
|
initial_button_mask = Input::get_singleton()->get_mouse_button_mask();
|
2018-03-27 21:41:27 +00:00
|
|
|
during_grabbed_click = (bool)initial_button_mask;
|
2020-03-14 16:06:39 +00:00
|
|
|
} break;
|
|
|
|
case NOTIFICATION_WM_SIZE_CHANGED: {
|
2020-03-20 02:32:09 +00:00
|
|
|
} break;
|
|
|
|
case NOTIFICATION_INTERNAL_PROCESS: {
|
|
|
|
//only used when using operating system windows
|
2021-07-23 15:13:01 +00:00
|
|
|
if (!is_embedded() && autohide_areas.size()) {
|
2020-03-20 02:32:09 +00:00
|
|
|
Point2 mouse_pos = DisplayServer::get_singleton()->mouse_get_position();
|
|
|
|
mouse_pos -= get_position();
|
|
|
|
|
2021-07-24 13:46:25 +00:00
|
|
|
for (const Rect2 &E : autohide_areas) {
|
2021-07-16 03:45:57 +00:00
|
|
|
if (!Rect2(Point2(), get_size()).has_point(mouse_pos) && E.has_point(mouse_pos)) {
|
2020-03-20 02:32:09 +00:00
|
|
|
_close_pressed();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-27 21:41:27 +00:00
|
|
|
} break;
|
2020-03-12 12:37:40 +00:00
|
|
|
case NOTIFICATION_VISIBILITY_CHANGED: {
|
|
|
|
if (!is_visible()) {
|
|
|
|
if (mouse_over >= 0) {
|
|
|
|
mouse_over = -1;
|
|
|
|
control->update();
|
|
|
|
}
|
2018-09-11 04:44:19 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
for (int i = 0; i < items.size(); i++) {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (items[i].submenu == "") {
|
2020-03-12 12:37:40 +00:00
|
|
|
continue;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-09-11 04:44:19 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
Node *n = get_node(items[i].submenu);
|
2020-05-14 14:41:43 +00:00
|
|
|
if (!n) {
|
2020-03-12 12:37:40 +00:00
|
|
|
continue;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-09-11 04:44:19 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
PopupMenu *pm = Object::cast_to<PopupMenu>(n);
|
2020-05-14 14:41:43 +00:00
|
|
|
if (!pm || !pm->is_visible()) {
|
2020-03-12 12:37:40 +00:00
|
|
|
continue;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-09-11 04:44:19 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
pm->hide();
|
|
|
|
}
|
2020-03-20 02:32:09 +00:00
|
|
|
|
|
|
|
set_process_internal(false);
|
|
|
|
} else {
|
2021-07-23 15:13:01 +00:00
|
|
|
if (!is_embedded()) {
|
2020-03-20 02:32:09 +00:00
|
|
|
set_process_internal(true);
|
|
|
|
}
|
2020-08-29 12:05:59 +00:00
|
|
|
|
|
|
|
// Set margin on the margin container
|
2021-07-17 21:22:52 +00:00
|
|
|
Ref<StyleBox> panel_style = get_theme_stylebox(SNAME("panel"));
|
2020-12-22 16:24:29 +00:00
|
|
|
margin_container->add_theme_constant_override("margin_top", panel_style->get_margin(Side::SIDE_TOP));
|
|
|
|
margin_container->add_theme_constant_override("margin_bottom", panel_style->get_margin(Side::SIDE_BOTTOM));
|
|
|
|
margin_container->add_theme_constant_override("margin_left", panel_style->get_margin(Side::SIDE_LEFT));
|
|
|
|
margin_container->add_theme_constant_override("margin_right", panel_style->get_margin(Side::SIDE_RIGHT));
|
2018-09-11 04:44:19 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
} break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-08 06:28:26 +00:00
|
|
|
/* Methods to add items with or without icon, checkbox, shortcut.
|
|
|
|
* Be sure to keep them in sync when adding new properties in the Item struct.
|
|
|
|
*/
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2019-10-08 07:33:22 +00:00
|
|
|
#define ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel) \
|
|
|
|
item.text = p_label; \
|
2021-05-27 17:31:33 +00:00
|
|
|
item.xl_text = atr(p_label); \
|
2019-10-08 07:33:22 +00:00
|
|
|
item.id = p_id == -1 ? items.size() : p_id; \
|
|
|
|
item.accel = p_accel;
|
|
|
|
|
2021-08-13 21:31:57 +00:00
|
|
|
void PopupMenu::add_item(const String &p_label, int p_id, Key p_accel) {
|
2014-02-10 01:10:30 +00:00
|
|
|
Item item;
|
2019-10-08 07:33:22 +00:00
|
|
|
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
|
2014-02-10 01:10:30 +00:00
|
|
|
items.push_back(item);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(items.size() - 1);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2021-11-03 02:08:58 +00:00
|
|
|
notify_property_list_changed();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2021-08-13 21:31:57 +00:00
|
|
|
void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, Key p_accel) {
|
2014-02-10 01:10:30 +00:00
|
|
|
Item item;
|
2019-10-08 07:33:22 +00:00
|
|
|
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
|
2019-10-08 06:28:26 +00:00
|
|
|
item.icon = p_icon;
|
2014-02-10 01:10:30 +00:00
|
|
|
items.push_back(item);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(items.size() - 1);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2021-11-03 02:08:58 +00:00
|
|
|
notify_property_list_changed();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2021-08-13 21:31:57 +00:00
|
|
|
void PopupMenu::add_check_item(const String &p_label, int p_id, Key p_accel) {
|
2014-02-10 01:10:30 +00:00
|
|
|
Item item;
|
2019-10-08 07:33:22 +00:00
|
|
|
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
|
2018-03-23 20:55:40 +00:00
|
|
|
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
|
2014-02-10 01:10:30 +00:00
|
|
|
items.push_back(item);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(items.size() - 1);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2018-03-23 20:55:40 +00:00
|
|
|
|
2021-08-13 21:31:57 +00:00
|
|
|
void PopupMenu::add_icon_check_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, Key p_accel) {
|
2014-02-10 01:10:30 +00:00
|
|
|
Item item;
|
2019-10-08 07:33:22 +00:00
|
|
|
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
|
2019-10-08 06:28:26 +00:00
|
|
|
item.icon = p_icon;
|
2018-03-23 20:55:40 +00:00
|
|
|
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
|
2014-02-10 01:10:30 +00:00
|
|
|
items.push_back(item);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(items.size() - 1);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2021-08-13 21:31:57 +00:00
|
|
|
void PopupMenu::add_radio_check_item(const String &p_label, int p_id, Key p_accel) {
|
2019-10-08 07:33:22 +00:00
|
|
|
Item item;
|
|
|
|
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
|
|
|
|
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
|
|
|
|
items.push_back(item);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(items.size() - 1);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2018-03-23 20:55:40 +00:00
|
|
|
}
|
|
|
|
|
2021-08-13 21:31:57 +00:00
|
|
|
void PopupMenu::add_icon_radio_check_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, Key p_accel) {
|
2019-10-08 07:33:22 +00:00
|
|
|
Item item;
|
|
|
|
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
|
|
|
|
item.icon = p_icon;
|
|
|
|
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
|
|
|
|
items.push_back(item);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(items.size() - 1);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2018-04-09 17:55:24 +00:00
|
|
|
}
|
|
|
|
|
2021-08-13 21:31:57 +00:00
|
|
|
void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id, Key p_accel) {
|
2019-10-08 06:28:26 +00:00
|
|
|
Item item;
|
2019-10-08 07:33:22 +00:00
|
|
|
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
|
2019-10-08 06:28:26 +00:00
|
|
|
item.max_states = p_max_states;
|
|
|
|
item.state = p_default_state;
|
|
|
|
items.push_back(item);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(items.size() - 1);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2019-10-08 06:28:26 +00:00
|
|
|
}
|
|
|
|
|
2019-10-08 07:33:22 +00:00
|
|
|
#define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global) \
|
2020-09-09 19:53:24 +00:00
|
|
|
ERR_FAIL_COND_MSG(p_shortcut.is_null(), "Cannot add item with invalid Shortcut."); \
|
2019-10-08 07:33:22 +00:00
|
|
|
_ref_shortcut(p_shortcut); \
|
|
|
|
item.text = p_shortcut->get_name(); \
|
2021-05-27 17:31:33 +00:00
|
|
|
item.xl_text = atr(item.text); \
|
2019-10-08 07:33:22 +00:00
|
|
|
item.id = p_id == -1 ? items.size() : p_id; \
|
|
|
|
item.shortcut = p_shortcut; \
|
|
|
|
item.shortcut_is_global = p_global;
|
2016-06-05 00:31:29 +00:00
|
|
|
|
2020-09-09 19:53:24 +00:00
|
|
|
void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
|
2016-06-05 00:31:29 +00:00
|
|
|
Item item;
|
2019-10-08 07:33:22 +00:00
|
|
|
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
|
2016-06-05 00:31:29 +00:00
|
|
|
items.push_back(item);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(items.size() - 1);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2016-06-05 00:31:29 +00:00
|
|
|
}
|
|
|
|
|
2020-09-09 19:53:24 +00:00
|
|
|
void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
|
2016-06-05 00:31:29 +00:00
|
|
|
Item item;
|
2019-10-08 07:33:22 +00:00
|
|
|
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
|
2019-10-08 06:28:26 +00:00
|
|
|
item.icon = p_icon;
|
2016-06-05 00:31:29 +00:00
|
|
|
items.push_back(item);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(items.size() - 1);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2016-06-05 00:31:29 +00:00
|
|
|
}
|
2018-03-23 20:55:40 +00:00
|
|
|
|
2020-09-09 19:53:24 +00:00
|
|
|
void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
|
2016-06-05 00:31:29 +00:00
|
|
|
Item item;
|
2019-10-08 07:33:22 +00:00
|
|
|
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
|
2019-10-08 06:28:26 +00:00
|
|
|
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
|
2016-06-05 00:31:29 +00:00
|
|
|
items.push_back(item);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(items.size() - 1);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2016-06-05 00:31:29 +00:00
|
|
|
}
|
|
|
|
|
2020-09-09 19:53:24 +00:00
|
|
|
void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
|
2016-06-05 00:31:29 +00:00
|
|
|
Item item;
|
2019-10-08 07:33:22 +00:00
|
|
|
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
|
2019-10-08 06:28:26 +00:00
|
|
|
item.icon = p_icon;
|
2019-10-08 07:33:22 +00:00
|
|
|
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
|
2016-06-05 00:31:29 +00:00
|
|
|
items.push_back(item);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(items.size() - 1);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2016-06-05 00:31:29 +00:00
|
|
|
}
|
|
|
|
|
2020-09-09 19:53:24 +00:00
|
|
|
void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
|
2019-10-08 07:33:22 +00:00
|
|
|
Item item;
|
|
|
|
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
|
|
|
|
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
|
|
|
|
items.push_back(item);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(items.size() - 1);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2018-03-23 20:55:40 +00:00
|
|
|
}
|
|
|
|
|
2020-09-09 19:53:24 +00:00
|
|
|
void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
|
2019-10-08 07:33:22 +00:00
|
|
|
Item item;
|
|
|
|
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
|
|
|
|
item.icon = p_icon;
|
|
|
|
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
|
|
|
|
items.push_back(item);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(items.size() - 1);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2019-10-08 06:28:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_id) {
|
2017-11-22 21:29:27 +00:00
|
|
|
Item item;
|
|
|
|
item.text = p_label;
|
2021-05-27 17:31:33 +00:00
|
|
|
item.xl_text = atr(p_label);
|
2019-10-08 07:33:22 +00:00
|
|
|
item.id = p_id == -1 ? items.size() : p_id;
|
2019-10-08 06:28:26 +00:00
|
|
|
item.submenu = p_submenu;
|
2017-11-22 21:29:27 +00:00
|
|
|
items.push_back(item);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(items.size() - 1);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2017-11-22 21:29:27 +00:00
|
|
|
}
|
|
|
|
|
2019-10-08 07:33:22 +00:00
|
|
|
#undef ITEM_SETUP_WITH_ACCEL
|
|
|
|
#undef ITEM_SETUP_WITH_SHORTCUT
|
|
|
|
|
2019-10-08 06:28:26 +00:00
|
|
|
/* Methods to modify existing items. */
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void PopupMenu::set_item_text(int p_idx, const String &p_text) {
|
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].text = p_text;
|
2021-05-27 17:31:33 +00:00
|
|
|
items.write[p_idx].xl_text = atr(p_text);
|
2020-09-03 11:22:16 +00:00
|
|
|
_shape_item(p_idx);
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2020-09-03 11:22:16 +00:00
|
|
|
void PopupMenu::set_item_text_direction(int p_item, Control::TextDirection p_text_direction) {
|
|
|
|
ERR_FAIL_INDEX(p_item, items.size());
|
|
|
|
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
|
|
|
|
if (items[p_item].text_direction != p_text_direction) {
|
|
|
|
items.write[p_item].text_direction = p_text_direction;
|
|
|
|
items.write[p_item].dirty = true;
|
|
|
|
control->update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PopupMenu::clear_item_opentype_features(int p_item) {
|
|
|
|
ERR_FAIL_INDEX(p_item, items.size());
|
|
|
|
items.write[p_item].opentype_features.clear();
|
|
|
|
items.write[p_item].dirty = true;
|
|
|
|
control->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PopupMenu::set_item_opentype_feature(int p_item, const String &p_name, int p_value) {
|
|
|
|
ERR_FAIL_INDEX(p_item, items.size());
|
|
|
|
int32_t tag = TS->name_to_tag(p_name);
|
|
|
|
if (!items[p_item].opentype_features.has(tag) || (int)items[p_item].opentype_features[tag] != p_value) {
|
|
|
|
items.write[p_item].opentype_features[tag] = p_value;
|
|
|
|
items.write[p_item].dirty = true;
|
|
|
|
control->update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PopupMenu::set_item_language(int p_item, const String &p_language) {
|
|
|
|
ERR_FAIL_INDEX(p_item, items.size());
|
|
|
|
if (items[p_item].language != p_language) {
|
|
|
|
items.write[p_item].language = p_language;
|
|
|
|
items.write[p_item].dirty = true;
|
|
|
|
control->update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-11 18:43:37 +00:00
|
|
|
void PopupMenu::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].icon = p_icon;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
|
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].checked = p_checked;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2019-05-09 09:21:49 +00:00
|
|
|
void PopupMenu::set_item_id(int p_idx, int p_id) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2019-05-09 09:21:49 +00:00
|
|
|
items.write[p_idx].id = p_id;
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2021-08-13 21:31:57 +00:00
|
|
|
void PopupMenu::set_item_accelerator(int p_idx, Key p_accel) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].accel = p_accel;
|
2020-09-03 11:22:16 +00:00
|
|
|
items.write[p_idx].dirty = true;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) {
|
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].metadata = p_meta;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) {
|
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].disabled = p_disabled;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) {
|
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].submenu = p_submenu;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2016-10-01 13:50:54 +00:00
|
|
|
void PopupMenu::toggle_item_checked(int p_idx) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].checked = !items[p_idx].checked;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2016-10-01 13:50:54 +00:00
|
|
|
}
|
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
String PopupMenu::get_item_text(int p_idx) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), "");
|
2014-02-10 01:10:30 +00:00
|
|
|
return items[p_idx].text;
|
|
|
|
}
|
2017-07-14 08:45:24 +00:00
|
|
|
|
2020-09-03 11:22:16 +00:00
|
|
|
Control::TextDirection PopupMenu::get_item_text_direction(int p_item) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_item, items.size(), Control::TEXT_DIRECTION_INHERITED);
|
|
|
|
return items[p_item].text_direction;
|
|
|
|
}
|
|
|
|
|
|
|
|
int PopupMenu::get_item_opentype_feature(int p_item, const String &p_name) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_item, items.size(), -1);
|
|
|
|
int32_t tag = TS->name_to_tag(p_name);
|
|
|
|
if (!items[p_item].opentype_features.has(tag)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return items[p_item].opentype_features[tag];
|
|
|
|
}
|
|
|
|
|
|
|
|
String PopupMenu::get_item_language(int p_item) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_item, items.size(), "");
|
|
|
|
return items[p_item].language;
|
|
|
|
}
|
|
|
|
|
2017-07-14 08:45:24 +00:00
|
|
|
int PopupMenu::get_item_idx_from_text(const String &text) const {
|
|
|
|
for (int idx = 0; idx < items.size(); idx++) {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (items[idx].text == text) {
|
2017-07-14 08:45:24 +00:00
|
|
|
return idx;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-07-14 08:45:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-06-11 18:43:37 +00:00
|
|
|
Ref<Texture2D> PopupMenu::get_item_icon(int p_idx) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), Ref<Texture2D>());
|
2014-02-10 01:10:30 +00:00
|
|
|
return items[p_idx].icon;
|
|
|
|
}
|
|
|
|
|
2021-08-13 21:31:57 +00:00
|
|
|
Key PopupMenu::get_item_accelerator(int p_idx) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), Key::NONE);
|
2014-02-10 01:10:30 +00:00
|
|
|
return items[p_idx].accel;
|
|
|
|
}
|
|
|
|
|
|
|
|
Variant PopupMenu::get_item_metadata(int p_idx) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), Variant());
|
2014-02-10 01:10:30 +00:00
|
|
|
return items[p_idx].metadata;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PopupMenu::is_item_disabled(int p_idx) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), false);
|
2014-02-10 01:10:30 +00:00
|
|
|
return items[p_idx].disabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PopupMenu::is_item_checked(int p_idx) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), false);
|
2014-02-10 01:10:30 +00:00
|
|
|
return items[p_idx].checked;
|
|
|
|
}
|
|
|
|
|
2017-08-07 10:17:31 +00:00
|
|
|
int PopupMenu::get_item_id(int p_idx) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), 0);
|
2019-05-09 09:21:49 +00:00
|
|
|
return items[p_idx].id;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2019-05-09 09:21:49 +00:00
|
|
|
int PopupMenu::get_item_index(int p_id) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
for (int i = 0; i < items.size(); i++) {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (items[i].id == p_id) {
|
2014-02-10 01:10:30 +00:00
|
|
|
return i;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
String PopupMenu::get_item_submenu(int p_idx) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), "");
|
2014-02-10 01:10:30 +00:00
|
|
|
return items[p_idx].submenu;
|
|
|
|
}
|
|
|
|
|
|
|
|
String PopupMenu::get_item_tooltip(int p_idx) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), "");
|
2014-02-10 01:10:30 +00:00
|
|
|
return items[p_idx].tooltip;
|
|
|
|
}
|
|
|
|
|
2020-09-09 19:53:24 +00:00
|
|
|
Ref<Shortcut> PopupMenu::get_item_shortcut(int p_idx) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), Ref<Shortcut>());
|
2016-06-05 00:31:29 +00:00
|
|
|
return items[p_idx].shortcut;
|
|
|
|
}
|
|
|
|
|
2017-11-22 21:29:27 +00:00
|
|
|
int PopupMenu::get_item_state(int p_idx) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), -1);
|
|
|
|
return items[p_idx].state;
|
|
|
|
}
|
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
void PopupMenu::set_item_as_separator(int p_idx, bool p_separator) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].separator = p_separator;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool PopupMenu::is_item_separator(int p_idx) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), false);
|
2014-02-10 01:10:30 +00:00
|
|
|
return items[p_idx].separator;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
2018-03-23 20:55:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) {
|
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void PopupMenu::set_item_tooltip(int p_idx, const String &p_tooltip) {
|
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].tooltip = p_tooltip;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2020-09-09 19:53:24 +00:00
|
|
|
void PopupMenu::set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bool p_global) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2016-06-05 00:31:29 +00:00
|
|
|
if (items[p_idx].shortcut.is_valid()) {
|
|
|
|
_unref_shortcut(items[p_idx].shortcut);
|
|
|
|
}
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].shortcut = p_shortcut;
|
|
|
|
items.write[p_idx].shortcut_is_global = p_global;
|
2020-09-03 11:22:16 +00:00
|
|
|
items.write[p_idx].dirty = true;
|
2016-06-05 00:31:29 +00:00
|
|
|
|
|
|
|
if (items[p_idx].shortcut.is_valid()) {
|
|
|
|
_ref_shortcut(items[p_idx].shortcut);
|
|
|
|
}
|
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
2016-06-05 00:31:29 +00:00
|
|
|
}
|
|
|
|
|
2016-09-17 22:01:11 +00:00
|
|
|
void PopupMenu::set_item_h_offset(int p_idx, int p_offset) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].h_ofs = p_offset;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2016-09-17 22:01:11 +00:00
|
|
|
}
|
|
|
|
|
2017-12-12 02:37:37 +00:00
|
|
|
void PopupMenu::set_item_multistate(int p_idx, int p_state) {
|
2017-11-22 21:29:27 +00:00
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].state = p_state;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
2017-11-22 21:29:27 +00:00
|
|
|
}
|
|
|
|
|
2018-06-07 15:46:14 +00:00
|
|
|
void PopupMenu::set_item_shortcut_disabled(int p_idx, bool p_disabled) {
|
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].shortcut_is_disabled = p_disabled;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
2018-06-07 15:46:14 +00:00
|
|
|
}
|
|
|
|
|
2017-12-12 02:37:37 +00:00
|
|
|
void PopupMenu::toggle_item_multistate(int p_idx) {
|
2017-11-22 21:29:27 +00:00
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
|
|
|
if (0 >= items[p_idx].max_states) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-25 01:11:03 +00:00
|
|
|
++items.write[p_idx].state;
|
2020-05-14 14:41:43 +00:00
|
|
|
if (items.write[p_idx].max_states <= items[p_idx].state) {
|
2018-07-25 01:11:03 +00:00
|
|
|
items.write[p_idx].state = 0;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-11-22 21:29:27 +00:00
|
|
|
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
2017-11-22 21:29:27 +00:00
|
|
|
}
|
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
bool PopupMenu::is_item_checkable(int p_idx) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), false);
|
2018-03-23 20:55:40 +00:00
|
|
|
return items[p_idx].checkable_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PopupMenu::is_item_radio_checkable(int p_idx) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), false);
|
|
|
|
return items[p_idx].checkable_type == Item::CHECKABLE_TYPE_RADIO_BUTTON;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2018-06-07 15:46:14 +00:00
|
|
|
bool PopupMenu::is_item_shortcut_disabled(int p_idx) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_idx, items.size(), false);
|
|
|
|
return items[p_idx].shortcut_is_disabled;
|
|
|
|
}
|
|
|
|
|
2020-05-06 20:27:35 +00:00
|
|
|
int PopupMenu::get_current_index() const {
|
|
|
|
return mouse_over;
|
|
|
|
}
|
|
|
|
|
2021-11-03 02:08:58 +00:00
|
|
|
void PopupMenu::set_item_count(int p_count) {
|
|
|
|
ERR_FAIL_COND(p_count < 0);
|
2021-11-05 22:28:04 +00:00
|
|
|
int prev_size = items.size();
|
2021-11-03 02:08:58 +00:00
|
|
|
items.resize(p_count);
|
2021-11-05 22:28:04 +00:00
|
|
|
|
|
|
|
if (prev_size < p_count) {
|
|
|
|
for (int i = prev_size; i < p_count; i++) {
|
|
|
|
items.write[i].id = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 02:08:58 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
|
|
|
notify_property_list_changed();
|
|
|
|
}
|
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
int PopupMenu::get_item_count() const {
|
|
|
|
return items.size();
|
|
|
|
}
|
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only) {
|
2021-08-13 21:31:57 +00:00
|
|
|
Key code = Key::NONE;
|
2017-05-20 15:38:03 +00:00
|
|
|
Ref<InputEventKey> k = p_event;
|
|
|
|
|
|
|
|
if (k.is_valid()) {
|
2018-04-05 17:59:35 +00:00
|
|
|
code = k->get_keycode();
|
2021-08-13 21:31:57 +00:00
|
|
|
if (code == Key::NONE) {
|
2021-06-20 17:12:33 +00:00
|
|
|
code = (Key)k->get_unicode();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2021-04-24 20:33:50 +00:00
|
|
|
if (k->is_ctrl_pressed()) {
|
2021-08-13 21:31:57 +00:00
|
|
|
code |= KeyModifierMask::CTRL;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2021-04-24 20:33:50 +00:00
|
|
|
if (k->is_alt_pressed()) {
|
2021-08-13 21:31:57 +00:00
|
|
|
code |= KeyModifierMask::ALT;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2021-04-24 20:33:50 +00:00
|
|
|
if (k->is_meta_pressed()) {
|
2021-08-13 21:31:57 +00:00
|
|
|
code |= KeyModifierMask::META;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2021-04-24 20:33:50 +00:00
|
|
|
if (k->is_shift_pressed()) {
|
2021-08-13 21:31:57 +00:00
|
|
|
code |= KeyModifierMask::SHIFT;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2016-06-05 00:31:29 +00:00
|
|
|
}
|
|
|
|
|
2018-09-11 04:44:19 +00:00
|
|
|
for (int i = 0; i < items.size(); i++) {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (is_item_disabled(i) || items[i].shortcut_is_disabled) {
|
2016-04-06 12:37:57 +00:00
|
|
|
continue;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-08-03 15:27:45 +00:00
|
|
|
if (items[i].shortcut.is_valid() && items[i].shortcut->matches_event(p_event) && (items[i].shortcut_is_global || !p_for_global_only)) {
|
2016-06-05 00:31:29 +00:00
|
|
|
activate_item(i);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-08-13 21:31:57 +00:00
|
|
|
if (code != Key::NONE && items[i].accel == code) {
|
2016-04-06 12:37:57 +00:00
|
|
|
activate_item(i);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
if (items[i].submenu != "") {
|
|
|
|
Node *n = get_node(items[i].submenu);
|
2020-05-14 14:41:43 +00:00
|
|
|
if (!n) {
|
2016-04-06 12:37:57 +00:00
|
|
|
continue;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2016-04-06 12:37:57 +00:00
|
|
|
|
2017-08-24 20:58:51 +00:00
|
|
|
PopupMenu *pm = Object::cast_to<PopupMenu>(n);
|
2020-05-14 14:41:43 +00:00
|
|
|
if (!pm) {
|
2016-04-06 12:37:57 +00:00
|
|
|
continue;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2016-04-06 12:37:57 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
if (pm->activate_item_by_event(p_event, p_for_global_only)) {
|
2016-04-06 12:37:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2016-04-06 12:37:57 +00:00
|
|
|
return false;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PopupMenu::activate_item(int p_item) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_item, items.size());
|
2014-02-10 01:10:30 +00:00
|
|
|
ERR_FAIL_COND(items[p_item].separator);
|
2019-05-09 09:21:49 +00:00
|
|
|
int id = items[p_item].id >= 0 ? items[p_item].id : p_item;
|
2015-08-12 20:24:21 +00:00
|
|
|
|
2018-08-13 19:55:26 +00:00
|
|
|
//hide all parent PopupMenus
|
2015-08-12 20:24:21 +00:00
|
|
|
Node *next = get_parent();
|
2017-08-24 20:58:51 +00:00
|
|
|
PopupMenu *pop = Object::cast_to<PopupMenu>(next);
|
2015-08-12 20:24:21 +00:00
|
|
|
while (pop) {
|
2017-03-05 15:44:50 +00:00
|
|
|
// We close all parents that are chained together,
|
2016-12-23 14:43:45 +00:00
|
|
|
// with hide_on_item_selection enabled
|
2017-11-22 21:29:27 +00:00
|
|
|
|
2018-03-23 20:55:40 +00:00
|
|
|
if (items[p_item].checkable_type) {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (!hide_on_checkable_item_selection || !pop->is_hide_on_checkable_item_selection()) {
|
2017-11-22 21:29:27 +00:00
|
|
|
break;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-11-22 21:29:27 +00:00
|
|
|
} else if (0 < items[p_item].max_states) {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (!hide_on_multistate_item_selection || !pop->is_hide_on_multistate_item_selection()) {
|
2017-11-22 21:29:27 +00:00
|
|
|
break;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
|
|
|
} else if (!hide_on_item_selection || !pop->is_hide_on_item_selection()) {
|
2016-12-23 14:43:45 +00:00
|
|
|
break;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-11-22 21:29:27 +00:00
|
|
|
|
|
|
|
pop->hide();
|
|
|
|
next = next->get_parent();
|
|
|
|
pop = Object::cast_to<PopupMenu>(next);
|
2016-12-23 14:43:45 +00:00
|
|
|
}
|
2017-11-22 21:29:27 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
// Hides popup by default; unless otherwise specified
|
2017-04-14 11:49:00 +00:00
|
|
|
// by using set_hide_on_item_selection and set_hide_on_checkable_item_selection
|
2017-11-22 21:29:27 +00:00
|
|
|
|
2018-08-16 19:44:18 +00:00
|
|
|
bool need_hide = true;
|
|
|
|
|
2018-03-23 20:55:40 +00:00
|
|
|
if (items[p_item].checkable_type) {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (!hide_on_checkable_item_selection) {
|
2018-08-16 19:44:18 +00:00
|
|
|
need_hide = false;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-11-22 21:29:27 +00:00
|
|
|
} else if (0 < items[p_item].max_states) {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (!hide_on_multistate_item_selection) {
|
2018-08-16 19:44:18 +00:00
|
|
|
need_hide = false;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
|
|
|
} else if (!hide_on_item_selection) {
|
2018-08-16 19:44:18 +00:00
|
|
|
need_hide = false;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-11-22 21:29:27 +00:00
|
|
|
|
2021-07-17 21:22:52 +00:00
|
|
|
emit_signal(SNAME("id_pressed"), id);
|
|
|
|
emit_signal(SNAME("index_pressed"), p_item);
|
2018-08-16 19:44:18 +00:00
|
|
|
|
|
|
|
if (need_hide) {
|
|
|
|
hide();
|
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PopupMenu::remove_item(int p_idx) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_idx, items.size());
|
2016-06-05 00:31:29 +00:00
|
|
|
|
|
|
|
if (items[p_idx].shortcut.is_valid()) {
|
|
|
|
_unref_shortcut(items[p_idx].shortcut);
|
|
|
|
}
|
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
items.remove(p_idx);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2020-12-27 12:18:47 +00:00
|
|
|
void PopupMenu::add_separator(const String &p_text, int p_id) {
|
2014-02-10 01:10:30 +00:00
|
|
|
Item sep;
|
2017-03-05 15:44:50 +00:00
|
|
|
sep.separator = true;
|
2020-12-27 12:18:47 +00:00
|
|
|
sep.id = p_id;
|
2018-07-14 21:15:42 +00:00
|
|
|
if (p_text != String()) {
|
|
|
|
sep.text = p_text;
|
2021-05-27 17:31:33 +00:00
|
|
|
sep.xl_text = atr(p_text);
|
2018-07-14 21:15:42 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
items.push_back(sep);
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void PopupMenu::clear() {
|
|
|
|
for (int i = 0; i < items.size(); i++) {
|
2016-06-05 00:31:29 +00:00
|
|
|
if (items[i].shortcut.is_valid()) {
|
|
|
|
_unref_shortcut(items[i].shortcut);
|
|
|
|
}
|
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
items.clear();
|
2017-03-05 15:44:50 +00:00
|
|
|
mouse_over = -1;
|
2020-03-12 12:37:40 +00:00
|
|
|
control->update();
|
|
|
|
child_controls_changed();
|
2021-11-03 02:08:58 +00:00
|
|
|
notify_property_list_changed();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2016-06-05 00:31:29 +00:00
|
|
|
|
2020-09-09 19:53:24 +00:00
|
|
|
void PopupMenu::_ref_shortcut(Ref<Shortcut> p_sc) {
|
2016-06-05 00:31:29 +00:00
|
|
|
if (!shortcut_refcount.has(p_sc)) {
|
2017-03-05 15:44:50 +00:00
|
|
|
shortcut_refcount[p_sc] = 1;
|
2020-02-21 22:26:13 +00:00
|
|
|
p_sc->connect("changed", callable_mp((CanvasItem *)this, &CanvasItem::update));
|
2016-06-05 00:31:29 +00:00
|
|
|
} else {
|
2017-03-05 15:44:50 +00:00
|
|
|
shortcut_refcount[p_sc] += 1;
|
2016-06-05 00:31:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-09 19:53:24 +00:00
|
|
|
void PopupMenu::_unref_shortcut(Ref<Shortcut> p_sc) {
|
2016-06-05 00:31:29 +00:00
|
|
|
ERR_FAIL_COND(!shortcut_refcount.has(p_sc));
|
|
|
|
shortcut_refcount[p_sc]--;
|
2017-03-05 15:44:50 +00:00
|
|
|
if (shortcut_refcount[p_sc] == 0) {
|
2020-02-21 22:26:13 +00:00
|
|
|
p_sc->disconnect("changed", callable_mp((CanvasItem *)this, &CanvasItem::update));
|
2016-06-05 00:31:29 +00:00
|
|
|
shortcut_refcount.erase(p_sc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-23 14:43:45 +00:00
|
|
|
// Hide on item selection determines whether or not the popup will close after item selection
|
|
|
|
void PopupMenu::set_hide_on_item_selection(bool p_enabled) {
|
2017-03-05 15:44:50 +00:00
|
|
|
hide_on_item_selection = p_enabled;
|
2016-12-23 14:43:45 +00:00
|
|
|
}
|
|
|
|
|
2017-11-22 21:29:27 +00:00
|
|
|
bool PopupMenu::is_hide_on_item_selection() const {
|
2016-12-23 14:43:45 +00:00
|
|
|
return hide_on_item_selection;
|
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-04-14 11:49:00 +00:00
|
|
|
void PopupMenu::set_hide_on_checkable_item_selection(bool p_enabled) {
|
|
|
|
hide_on_checkable_item_selection = p_enabled;
|
|
|
|
}
|
|
|
|
|
2017-11-22 21:29:27 +00:00
|
|
|
bool PopupMenu::is_hide_on_checkable_item_selection() const {
|
2017-04-14 11:49:00 +00:00
|
|
|
return hide_on_checkable_item_selection;
|
|
|
|
}
|
|
|
|
|
2017-12-12 02:37:37 +00:00
|
|
|
void PopupMenu::set_hide_on_multistate_item_selection(bool p_enabled) {
|
|
|
|
hide_on_multistate_item_selection = p_enabled;
|
2017-11-22 21:29:27 +00:00
|
|
|
}
|
|
|
|
|
2017-12-12 02:37:37 +00:00
|
|
|
bool PopupMenu::is_hide_on_multistate_item_selection() const {
|
|
|
|
return hide_on_multistate_item_selection;
|
2017-11-22 21:29:27 +00:00
|
|
|
}
|
|
|
|
|
2018-07-09 12:16:06 +00:00
|
|
|
void PopupMenu::set_submenu_popup_delay(float p_time) {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (p_time <= 0) {
|
2018-07-09 12:16:06 +00:00
|
|
|
p_time = 0.01;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-07-09 12:16:06 +00:00
|
|
|
|
|
|
|
submenu_timer->set_wait_time(p_time);
|
|
|
|
}
|
|
|
|
|
|
|
|
float PopupMenu::get_submenu_popup_delay() const {
|
|
|
|
return submenu_timer->get_wait_time();
|
|
|
|
}
|
|
|
|
|
2019-05-11 16:32:53 +00:00
|
|
|
void PopupMenu::set_allow_search(bool p_allow) {
|
|
|
|
allow_search = p_allow;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PopupMenu::get_allow_search() const {
|
|
|
|
return allow_search;
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
String PopupMenu::get_tooltip(const Point2 &p_pos) const {
|
|
|
|
int over = _get_mouse_over(p_pos);
|
2020-05-14 14:41:43 +00:00
|
|
|
if (over < 0 || over >= items.size()) {
|
2014-02-10 01:10:30 +00:00
|
|
|
return "";
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
return items[over].tooltip;
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void PopupMenu::set_parent_rect(const Rect2 &p_rect) {
|
|
|
|
parent_rect = p_rect;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PopupMenu::get_translatable_strings(List<String> *p_strings) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
for (int i = 0; i < items.size(); i++) {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (items[i].xl_text != "") {
|
2017-01-09 19:43:44 +00:00
|
|
|
p_strings->push_back(items[i].xl_text);
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void PopupMenu::add_autohide_area(const Rect2 &p_area) {
|
2014-02-10 01:10:30 +00:00
|
|
|
autohide_areas.push_back(p_area);
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void PopupMenu::clear_autohide_areas() {
|
2014-02-10 01:10:30 +00:00
|
|
|
autohide_areas.clear();
|
|
|
|
}
|
|
|
|
|
2020-03-20 02:32:09 +00:00
|
|
|
void PopupMenu::take_mouse_focus() {
|
|
|
|
ERR_FAIL_COND(!is_inside_tree());
|
|
|
|
|
|
|
|
if (get_parent()) {
|
|
|
|
get_parent()->get_viewport()->pass_mouse_focus_to(this, control);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 02:08:58 +00:00
|
|
|
bool PopupMenu::_set(const StringName &p_name, const Variant &p_value) {
|
|
|
|
Vector<String> components = String(p_name).split("/", true, 2);
|
|
|
|
if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) {
|
|
|
|
int item_index = components[0].trim_prefix("item_").to_int();
|
|
|
|
String property = components[1];
|
|
|
|
if (property == "text") {
|
|
|
|
set_item_text(item_index, p_value);
|
|
|
|
return true;
|
|
|
|
} else if (property == "icon") {
|
|
|
|
set_item_icon(item_index, p_value);
|
|
|
|
return true;
|
|
|
|
} else if (property == "checkable") {
|
|
|
|
bool radio_checkable = (int)p_value == Item::CHECKABLE_TYPE_RADIO_BUTTON;
|
|
|
|
if (radio_checkable) {
|
|
|
|
set_item_as_radio_checkable(item_index, true);
|
|
|
|
} else {
|
|
|
|
bool checkable = p_value;
|
|
|
|
set_item_as_checkable(item_index, checkable);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
} else if (property == "checked") {
|
|
|
|
set_item_checked(item_index, p_value);
|
|
|
|
return true;
|
|
|
|
} else if (property == "id") {
|
|
|
|
set_item_id(item_index, p_value);
|
|
|
|
return true;
|
|
|
|
} else if (components[1] == "disabled") {
|
|
|
|
set_item_disabled(item_index, p_value);
|
|
|
|
return true;
|
|
|
|
} else if (property == "separator") {
|
|
|
|
set_item_as_separator(item_index, p_value);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifndef DISABLE_DEPRECATED
|
|
|
|
// Compatibility.
|
|
|
|
if (p_name == "items") {
|
|
|
|
Array arr = p_value;
|
|
|
|
ERR_FAIL_COND_V(arr.size() % 10, false);
|
|
|
|
clear();
|
|
|
|
|
|
|
|
for (int i = 0; i < arr.size(); i += 10) {
|
|
|
|
String text = arr[i + 0];
|
|
|
|
Ref<Texture2D> icon = arr[i + 1];
|
|
|
|
// For compatibility, use false/true for no/checkbox and integers for other values
|
|
|
|
bool checkable = arr[i + 2];
|
|
|
|
bool radio_checkable = (int)arr[i + 2] == Item::CHECKABLE_TYPE_RADIO_BUTTON;
|
|
|
|
bool checked = arr[i + 3];
|
|
|
|
bool disabled = arr[i + 4];
|
|
|
|
|
|
|
|
int id = arr[i + 5];
|
|
|
|
int accel = arr[i + 6];
|
|
|
|
Variant meta = arr[i + 7];
|
|
|
|
String subm = arr[i + 8];
|
|
|
|
bool sep = arr[i + 9];
|
|
|
|
|
|
|
|
int idx = get_item_count();
|
|
|
|
add_item(text, id);
|
|
|
|
set_item_icon(idx, icon);
|
|
|
|
if (checkable) {
|
|
|
|
if (radio_checkable) {
|
|
|
|
set_item_as_radio_checkable(idx, true);
|
|
|
|
} else {
|
|
|
|
set_item_as_checkable(idx, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
set_item_checked(idx, checked);
|
|
|
|
set_item_disabled(idx, disabled);
|
|
|
|
set_item_id(idx, id);
|
|
|
|
set_item_metadata(idx, meta);
|
|
|
|
set_item_as_separator(idx, sep);
|
2021-08-13 21:31:57 +00:00
|
|
|
set_item_accelerator(idx, (Key)accel);
|
2021-11-03 02:08:58 +00:00
|
|
|
set_item_submenu(idx, subm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PopupMenu::_get(const StringName &p_name, Variant &r_ret) const {
|
|
|
|
Vector<String> components = String(p_name).split("/", true, 2);
|
|
|
|
if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) {
|
|
|
|
int item_index = components[0].trim_prefix("item_").to_int();
|
|
|
|
String property = components[1];
|
|
|
|
if (property == "text") {
|
|
|
|
r_ret = get_item_text(item_index);
|
|
|
|
return true;
|
|
|
|
} else if (property == "icon") {
|
|
|
|
r_ret = get_item_icon(item_index);
|
|
|
|
return true;
|
|
|
|
} else if (property == "checkable") {
|
|
|
|
r_ret = this->items[item_index].checkable_type;
|
|
|
|
return true;
|
|
|
|
} else if (property == "checked") {
|
|
|
|
r_ret = is_item_checked(item_index);
|
|
|
|
return true;
|
|
|
|
} else if (property == "id") {
|
|
|
|
r_ret = get_item_id(item_index);
|
|
|
|
return true;
|
|
|
|
} else if (components[1] == "disabled") {
|
|
|
|
r_ret = is_item_disabled(item_index);
|
|
|
|
return true;
|
|
|
|
} else if (property == "separator") {
|
|
|
|
r_ret = is_item_separator(item_index);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PopupMenu::_get_property_list(List<PropertyInfo> *p_list) const {
|
|
|
|
for (int i = 0; i < items.size(); i++) {
|
|
|
|
p_list->push_back(PropertyInfo(Variant::STRING, vformat("item_%d/text", i)));
|
|
|
|
|
|
|
|
PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D");
|
|
|
|
pi.usage &= ~(get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
|
|
|
|
p_list->push_back(pi);
|
|
|
|
|
|
|
|
pi = PropertyInfo(Variant::INT, vformat("item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As checkbox,As radio button");
|
|
|
|
pi.usage &= ~(!is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0);
|
|
|
|
p_list->push_back(pi);
|
|
|
|
|
|
|
|
pi = PropertyInfo(Variant::BOOL, vformat("item_%d/checked", i));
|
|
|
|
pi.usage &= ~(!is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0);
|
|
|
|
p_list->push_back(pi);
|
|
|
|
|
2021-11-05 22:28:04 +00:00
|
|
|
pi = PropertyInfo(Variant::INT, vformat("item_%d/id", i), PROPERTY_HINT_RANGE, "0,10,1,or_greater");
|
2021-11-03 02:08:58 +00:00
|
|
|
p_list->push_back(pi);
|
|
|
|
|
|
|
|
pi = PropertyInfo(Variant::BOOL, vformat("item_%d/disabled", i));
|
|
|
|
pi.usage &= ~(!is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0);
|
|
|
|
p_list->push_back(pi);
|
|
|
|
|
|
|
|
pi = PropertyInfo(Variant::BOOL, vformat("item_%d/separator", i));
|
|
|
|
pi.usage &= ~(!is_item_separator(i) ? PROPERTY_USAGE_STORAGE : 0);
|
|
|
|
p_list->push_back(pi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
void PopupMenu::_bind_methods() {
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_item", "label", "id", "accel"), &PopupMenu::add_item, DEFVAL(-1), DEFVAL(0));
|
2019-10-08 06:28:26 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_item, DEFVAL(-1), DEFVAL(0));
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_check_item", "label", "id", "accel"), &PopupMenu::add_check_item, DEFVAL(-1), DEFVAL(0));
|
2019-10-08 06:28:26 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_icon_check_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_check_item, DEFVAL(-1), DEFVAL(0));
|
2018-03-23 20:55:40 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_radio_check_item", "label", "id", "accel"), &PopupMenu::add_radio_check_item, DEFVAL(-1), DEFVAL(0));
|
2019-10-08 06:28:26 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_icon_radio_check_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_radio_check_item, DEFVAL(-1), DEFVAL(0));
|
|
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("add_multistate_item", "label", "max_states", "default_state", "id", "accel"), &PopupMenu::add_multistate_item, DEFVAL(0), DEFVAL(-1), DEFVAL(0));
|
2017-03-05 15:44:50 +00:00
|
|
|
|
2017-08-09 11:19:41 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::add_shortcut, DEFVAL(-1), DEFVAL(false));
|
2019-10-08 06:28:26 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false));
|
2017-08-09 11:19:41 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_check_shortcut, DEFVAL(-1), DEFVAL(false));
|
2019-10-08 06:28:26 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_icon_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_check_shortcut, DEFVAL(-1), DEFVAL(false));
|
2018-03-23 20:55:40 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_radio_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_radio_check_shortcut, DEFVAL(-1), DEFVAL(false));
|
2019-10-08 06:28:26 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_icon_radio_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_radio_check_shortcut, DEFVAL(-1), DEFVAL(false));
|
|
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("add_submenu_item", "label", "submenu", "id"), &PopupMenu::add_submenu_item, DEFVAL(-1));
|
2017-03-05 15:44:50 +00:00
|
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("set_item_text", "idx", "text"), &PopupMenu::set_item_text);
|
2020-09-03 11:22:16 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_item_text_direction", "idx", "direction"), &PopupMenu::set_item_text_direction);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_item_opentype_feature", "idx", "tag", "value"), &PopupMenu::set_item_opentype_feature);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_item_language", "idx", "language"), &PopupMenu::set_item_language);
|
2017-08-09 11:19:41 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "icon"), &PopupMenu::set_item_icon);
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_item_checked", "idx", "checked"), &PopupMenu::set_item_checked);
|
2017-08-07 10:17:31 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_item_id", "idx", "id"), &PopupMenu::set_item_id);
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_item_accelerator", "idx", "accel"), &PopupMenu::set_item_accelerator);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_item_metadata", "idx", "metadata"), &PopupMenu::set_item_metadata);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_item_disabled", "idx", "disabled"), &PopupMenu::set_item_disabled);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_item_submenu", "idx", "submenu"), &PopupMenu::set_item_submenu);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_item_as_separator", "idx", "enable"), &PopupMenu::set_item_as_separator);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_item_as_checkable", "idx", "enable"), &PopupMenu::set_item_as_checkable);
|
2018-03-23 20:55:40 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_item_as_radio_checkable", "idx", "enable"), &PopupMenu::set_item_as_radio_checkable);
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_item_tooltip", "idx", "tooltip"), &PopupMenu::set_item_tooltip);
|
2017-08-09 11:19:41 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_item_shortcut", "idx", "shortcut", "global"), &PopupMenu::set_item_shortcut, DEFVAL(false));
|
2017-12-12 02:37:37 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_item_multistate", "idx", "state"), &PopupMenu::set_item_multistate);
|
2018-06-07 15:46:14 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_item_shortcut_disabled", "idx", "disabled"), &PopupMenu::set_item_shortcut_disabled);
|
2017-03-05 15:44:50 +00:00
|
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("toggle_item_checked", "idx"), &PopupMenu::toggle_item_checked);
|
2017-12-12 02:37:37 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("toggle_item_multistate", "idx"), &PopupMenu::toggle_item_multistate);
|
2017-03-05 15:44:50 +00:00
|
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("get_item_text", "idx"), &PopupMenu::get_item_text);
|
2020-09-03 11:22:16 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_item_text_direction", "idx"), &PopupMenu::get_item_text_direction);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_item_opentype_feature", "idx", "tag"), &PopupMenu::get_item_opentype_feature);
|
|
|
|
ClassDB::bind_method(D_METHOD("clear_item_opentype_features", "idx"), &PopupMenu::clear_item_opentype_features);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_item_language", "idx"), &PopupMenu::get_item_language);
|
2017-08-09 11:19:41 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_item_icon", "idx"), &PopupMenu::get_item_icon);
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("is_item_checked", "idx"), &PopupMenu::is_item_checked);
|
2017-08-07 10:17:31 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_item_id", "idx"), &PopupMenu::get_item_id);
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_item_index", "id"), &PopupMenu::get_item_index);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_item_accelerator", "idx"), &PopupMenu::get_item_accelerator);
|
2017-08-09 11:19:41 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_item_metadata", "idx"), &PopupMenu::get_item_metadata);
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("is_item_disabled", "idx"), &PopupMenu::is_item_disabled);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_item_submenu", "idx"), &PopupMenu::get_item_submenu);
|
|
|
|
ClassDB::bind_method(D_METHOD("is_item_separator", "idx"), &PopupMenu::is_item_separator);
|
|
|
|
ClassDB::bind_method(D_METHOD("is_item_checkable", "idx"), &PopupMenu::is_item_checkable);
|
2018-03-23 20:55:40 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "idx"), &PopupMenu::is_item_radio_checkable);
|
2018-06-07 15:46:14 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("is_item_shortcut_disabled", "idx"), &PopupMenu::is_item_shortcut_disabled);
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &PopupMenu::get_item_tooltip);
|
2017-08-09 11:19:41 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_item_shortcut", "idx"), &PopupMenu::get_item_shortcut);
|
2017-03-05 15:44:50 +00:00
|
|
|
|
2020-05-06 20:27:35 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_current_index"), &PopupMenu::get_current_index);
|
2021-11-04 14:27:23 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_item_count", "count"), &PopupMenu::set_item_count);
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_item_count"), &PopupMenu::get_item_count);
|
|
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("remove_item", "idx"), &PopupMenu::remove_item);
|
|
|
|
|
2020-12-27 12:18:47 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_separator", "label", "id"), &PopupMenu::add_separator, DEFVAL(String()), DEFVAL(-1));
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("clear"), &PopupMenu::clear);
|
|
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("set_hide_on_item_selection", "enable"), &PopupMenu::set_hide_on_item_selection);
|
|
|
|
ClassDB::bind_method(D_METHOD("is_hide_on_item_selection"), &PopupMenu::is_hide_on_item_selection);
|
|
|
|
|
2017-04-14 11:49:00 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_hide_on_checkable_item_selection", "enable"), &PopupMenu::set_hide_on_checkable_item_selection);
|
|
|
|
ClassDB::bind_method(D_METHOD("is_hide_on_checkable_item_selection"), &PopupMenu::is_hide_on_checkable_item_selection);
|
|
|
|
|
2017-12-12 02:37:37 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_hide_on_state_item_selection", "enable"), &PopupMenu::set_hide_on_multistate_item_selection);
|
|
|
|
ClassDB::bind_method(D_METHOD("is_hide_on_state_item_selection"), &PopupMenu::is_hide_on_multistate_item_selection);
|
2017-11-22 21:29:27 +00:00
|
|
|
|
2018-07-09 12:16:06 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_submenu_popup_delay", "seconds"), &PopupMenu::set_submenu_popup_delay);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_submenu_popup_delay"), &PopupMenu::get_submenu_popup_delay);
|
2018-01-02 07:10:49 +00:00
|
|
|
|
2019-05-11 16:32:53 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &PopupMenu::set_allow_search);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_allow_search"), &PopupMenu::get_allow_search);
|
|
|
|
|
2018-11-08 14:30:02 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection");
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection");
|
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay");
|
2019-05-11 16:32:53 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search");
|
2017-03-05 15:44:50 +00:00
|
|
|
|
2021-11-03 02:08:58 +00:00
|
|
|
ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "item_");
|
|
|
|
|
2019-05-09 09:21:49 +00:00
|
|
|
ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "id")));
|
|
|
|
ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "id")));
|
2017-03-05 15:44:50 +00:00
|
|
|
ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index")));
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2016-03-08 23:00:52 +00:00
|
|
|
|
2018-03-27 21:41:27 +00:00
|
|
|
void PopupMenu::popup(const Rect2 &p_bounds) {
|
2017-03-05 15:44:50 +00:00
|
|
|
moved = Vector2();
|
2020-09-18 18:45:59 +00:00
|
|
|
popup_time_msec = OS::get_singleton()->get_ticks_msec();
|
2018-03-27 21:41:27 +00:00
|
|
|
Popup::popup(p_bounds);
|
2015-01-03 19:52:37 +00:00
|
|
|
}
|
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
PopupMenu::PopupMenu() {
|
2020-08-29 12:05:59 +00:00
|
|
|
// Margin Container
|
|
|
|
margin_container = memnew(MarginContainer);
|
2020-12-22 16:24:29 +00:00
|
|
|
margin_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
|
2021-08-25 13:49:30 +00:00
|
|
|
add_child(margin_container, false, INTERNAL_MODE_FRONT);
|
2020-08-29 12:05:59 +00:00
|
|
|
margin_container->connect("draw", callable_mp(this, &PopupMenu::_draw_background));
|
|
|
|
|
|
|
|
// Scroll Container
|
|
|
|
scroll_container = memnew(ScrollContainer);
|
|
|
|
scroll_container->set_clip_contents(true);
|
|
|
|
margin_container->add_child(scroll_container);
|
|
|
|
|
|
|
|
// The control which will display the items
|
2020-03-12 12:37:40 +00:00
|
|
|
control = memnew(Control);
|
2020-08-29 12:05:59 +00:00
|
|
|
control->set_clip_contents(false);
|
2020-12-22 16:24:29 +00:00
|
|
|
control->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
|
2020-08-29 12:05:59 +00:00
|
|
|
control->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
|
|
|
control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
2021-08-25 13:49:30 +00:00
|
|
|
scroll_container->add_child(control, false, INTERNAL_MODE_FRONT);
|
2020-08-29 12:05:59 +00:00
|
|
|
control->connect("draw", callable_mp(this, &PopupMenu::_draw_items));
|
|
|
|
|
2021-08-22 15:37:22 +00:00
|
|
|
connect("window_input", callable_mp(this, &PopupMenu::gui_input));
|
2020-03-12 12:37:40 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
submenu_timer = memnew(Timer);
|
2014-02-10 01:10:30 +00:00
|
|
|
submenu_timer->set_wait_time(0.3);
|
|
|
|
submenu_timer->set_one_shot(true);
|
2020-02-21 17:28:45 +00:00
|
|
|
submenu_timer->connect("timeout", callable_mp(this, &PopupMenu::_submenu_timeout));
|
2021-08-25 13:49:30 +00:00
|
|
|
add_child(submenu_timer, false, INTERNAL_MODE_FRONT);
|
2020-09-07 12:57:50 +00:00
|
|
|
|
|
|
|
minimum_lifetime_timer = memnew(Timer);
|
|
|
|
minimum_lifetime_timer->set_wait_time(0.3);
|
|
|
|
minimum_lifetime_timer->set_one_shot(true);
|
|
|
|
minimum_lifetime_timer->connect("timeout", callable_mp(this, &PopupMenu::_minimum_lifetime_timeout));
|
2021-08-25 13:49:30 +00:00
|
|
|
add_child(minimum_lifetime_timer, false, INTERNAL_MODE_FRONT);
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PopupMenu::~PopupMenu() {
|
|
|
|
}
|