summaryrefslogtreecommitdiff
path: root/scene/gui/popup_menu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui/popup_menu.cpp')
-rw-r--r--scene/gui/popup_menu.cpp154
1 files changed, 115 insertions, 39 deletions
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 18e609c798..436dda41a4 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -398,10 +398,19 @@ 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);
+ }
+ } break;
case NOTIFICATION_TRANSLATION_CHANGED: {
for (int i = 0; i < items.size(); i++) {
- items[i].xl_text = tr(items[i].text);
+ items.write[i].xl_text = tr(items[i].text);
}
minimum_size_changed();
@@ -421,6 +430,8 @@ void PopupMenu::_notification(int p_what) {
Ref<Texture> uncheck[] = { get_icon("unchecked"), get_icon("radio_unchecked") };
Ref<Texture> submenu = get_icon("submenu");
Ref<StyleBox> separator = get_stylebox("separator");
+ Ref<StyleBox> labeled_separator_left = get_stylebox("labeled_separator_left");
+ Ref<StyleBox> labeled_separator_right = get_stylebox("labeled_separator_right");
style->draw(ci, Rect2(Point2(), get_size()));
Point2 ofs = style->get_offset();
@@ -457,10 +468,25 @@ void PopupMenu::_notification(int p_what) {
hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation / 2), Size2(get_size().width - style->get_minimum_size().width + hseparation * 2, h + vseparation)));
}
+ String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text;
+
if (items[i].separator) {
int sep_h = separator->get_center_size().height + separator->get_minimum_size().height;
- separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h)));
+ if (text != String()) {
+ int ss = font->get_string_size(text).width;
+ int center = (get_size().width) / 2;
+ int l = center - ss / 2;
+ int r = center + ss / 2;
+ if (l > item_ofs.x) {
+ labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, l - item_ofs.x), sep_h)));
+ }
+ if (r < get_size().width - style->get_margin(MARGIN_RIGHT)) {
+ labeled_separator_right->draw(ci, Rect2(Point2(r, item_ofs.y + Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, get_size().width - style->get_margin(MARGIN_RIGHT) - r), sep_h)));
+ }
+ } else {
+ separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h)));
+ }
}
if (items[i].checkable_type) {
@@ -480,8 +506,13 @@ void PopupMenu::_notification(int p_what) {
}
item_ofs.y += font->get_ascent();
- String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text;
- if (!items[i].separator) {
+ if (items[i].separator) {
+
+ if (text != String()) {
+ int center = (get_size().width - font->get_string_size(text).width) / 2;
+ font->draw(ci, Point2(center, item_ofs.y + Math::floor((h - font_h) / 2.0)), text, font_color_disabled);
+ }
+ } else {
font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), text, items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color));
}
@@ -493,12 +524,17 @@ void PopupMenu::_notification(int p_what) {
font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), text, i == mouse_over ? font_color_hover : font_color_accel);
}
- items[i]._ofs_cache = ofs.y;
+ items.write[i]._ofs_cache = ofs.y;
ofs.y += h;
}
} break;
+ case MainLoop::NOTIFICATION_WM_FOCUS_OUT: {
+
+ if (hide_on_window_lose_focus)
+ hide();
+ } break;
case NOTIFICATION_MOUSE_ENTER: {
grab_focus();
@@ -591,7 +627,7 @@ void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel
void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p_accel) {
add_check_item(p_label, p_ID, p_accel);
- items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
update();
minimum_size_changed();
}
@@ -599,7 +635,7 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p
void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) {
add_icon_check_item(p_icon, p_label, p_ID, p_accel);
- items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
update();
minimum_size_changed();
}
@@ -671,7 +707,7 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bo
void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
add_check_shortcut(p_shortcut, p_ID, p_global);
- items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
update();
minimum_size_changed();
}
@@ -693,8 +729,8 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
void PopupMenu::set_item_text(int p_idx, const String &p_text) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].text = p_text;
- items[p_idx].xl_text = tr(p_text);
+ items.write[p_idx].text = p_text;
+ items.write[p_idx].xl_text = tr(p_text);
update();
minimum_size_changed();
@@ -702,7 +738,7 @@ void PopupMenu::set_item_text(int p_idx, const String &p_text) {
void PopupMenu::set_item_icon(int p_idx, const Ref<Texture> &p_icon) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].icon = p_icon;
+ items.write[p_idx].icon = p_icon;
update();
minimum_size_changed();
@@ -711,7 +747,7 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].checked = p_checked;
+ items.write[p_idx].checked = p_checked;
update();
minimum_size_changed();
@@ -719,7 +755,7 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
void PopupMenu::set_item_id(int p_idx, int p_ID) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].ID = p_ID;
+ items.write[p_idx].ID = p_ID;
update();
minimum_size_changed();
@@ -728,7 +764,7 @@ void PopupMenu::set_item_id(int p_idx, int p_ID) {
void PopupMenu::set_item_accelerator(int p_idx, uint32_t p_accel) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].accel = p_accel;
+ items.write[p_idx].accel = p_accel;
update();
minimum_size_changed();
@@ -737,7 +773,7 @@ void PopupMenu::set_item_accelerator(int p_idx, uint32_t p_accel) {
void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].metadata = p_meta;
+ items.write[p_idx].metadata = p_meta;
update();
minimum_size_changed();
}
@@ -745,7 +781,7 @@ void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) {
void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].disabled = p_disabled;
+ items.write[p_idx].disabled = p_disabled;
update();
minimum_size_changed();
}
@@ -753,7 +789,7 @@ void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) {
void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].submenu = p_submenu;
+ items.write[p_idx].submenu = p_submenu;
update();
minimum_size_changed();
}
@@ -761,7 +797,7 @@ void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) {
void PopupMenu::toggle_item_checked(int p_idx) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].checked = !items[p_idx].checked;
+ items.write[p_idx].checked = !items[p_idx].checked;
update();
minimum_size_changed();
}
@@ -855,7 +891,7 @@ int PopupMenu::get_item_state(int p_idx) const {
void PopupMenu::set_item_as_separator(int p_idx, bool p_separator) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].separator = p_separator;
+ items.write[p_idx].separator = p_separator;
update();
}
@@ -867,21 +903,21 @@ bool PopupMenu::is_item_separator(int p_idx) const {
void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE;
+ items.write[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE;
update();
}
void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE;
+ items.write[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE;
update();
}
void PopupMenu::set_item_tooltip(int p_idx, const String &p_tooltip) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].tooltip = p_tooltip;
+ items.write[p_idx].tooltip = p_tooltip;
update();
}
@@ -890,8 +926,8 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bo
if (items[p_idx].shortcut.is_valid()) {
_unref_shortcut(items[p_idx].shortcut);
}
- items[p_idx].shortcut = p_shortcut;
- items[p_idx].shortcut_is_global = p_global;
+ items.write[p_idx].shortcut = p_shortcut;
+ items.write[p_idx].shortcut_is_global = p_global;
if (items[p_idx].shortcut.is_valid()) {
_ref_shortcut(items[p_idx].shortcut);
@@ -903,7 +939,7 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bo
void PopupMenu::set_item_h_offset(int p_idx, int p_offset) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].h_ofs = p_offset;
+ items.write[p_idx].h_ofs = p_offset;
update();
minimum_size_changed();
}
@@ -911,14 +947,14 @@ void PopupMenu::set_item_h_offset(int p_idx, int p_offset) {
void PopupMenu::set_item_multistate(int p_idx, int p_state) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].state = p_state;
+ items.write[p_idx].state = p_state;
update();
}
void PopupMenu::set_item_shortcut_disabled(int p_idx, bool p_disabled) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].shortcut_is_disabled = p_disabled;
+ items.write[p_idx].shortcut_is_disabled = p_disabled;
update();
}
@@ -929,9 +965,9 @@ void PopupMenu::toggle_item_multistate(int p_idx) {
return;
}
- ++items[p_idx].state;
- if (items[p_idx].max_states <= items[p_idx].state)
- items[p_idx].state = 0;
+ ++items.write[p_idx].state;
+ if (items.write[p_idx].max_states <= items[p_idx].state)
+ items.write[p_idx].state = 0;
update();
}
@@ -1013,10 +1049,8 @@ void PopupMenu::activate_item(int p_item) {
ERR_FAIL_INDEX(p_item, items.size());
ERR_FAIL_COND(items[p_item].separator);
int id = items[p_item].ID >= 0 ? items[p_item].ID : p_item;
- emit_signal("id_pressed", id);
- emit_signal("index_pressed", p_item);
- //hide all parent PopupMenue's
+ //hide all parent PopupMenus
Node *next = get_parent();
PopupMenu *pop = Object::cast_to<PopupMenu>(next);
while (pop) {
@@ -1040,16 +1074,23 @@ void PopupMenu::activate_item(int p_item) {
// Hides popup by default; unless otherwise specified
// by using set_hide_on_item_selection and set_hide_on_checkable_item_selection
+ bool need_hide = true;
+
if (items[p_item].checkable_type) {
if (!hide_on_checkable_item_selection)
- return;
+ need_hide = false;
} else if (0 < items[p_item].max_states) {
if (!hide_on_multistate_item_selection)
- return;
+ need_hide = false;
} else if (!hide_on_item_selection)
- return;
+ need_hide = false;
- hide();
+ emit_signal("id_pressed", id);
+ emit_signal("index_pressed", p_item);
+
+ if (need_hide) {
+ hide();
+ }
}
void PopupMenu::remove_item(int p_idx) {
@@ -1062,13 +1103,18 @@ void PopupMenu::remove_item(int p_idx) {
items.remove(p_idx);
update();
+ minimum_size_changed();
}
-void PopupMenu::add_separator() {
+void PopupMenu::add_separator(const String &p_text) {
Item sep;
sep.separator = true;
sep.ID = -1;
+ if (p_text != String()) {
+ sep.text = p_text;
+ sep.xl_text = tr(p_text);
+ }
items.push_back(sep);
update();
}
@@ -1201,6 +1247,29 @@ bool PopupMenu::is_hide_on_multistate_item_selection() const {
return hide_on_multistate_item_selection;
}
+void PopupMenu::set_submenu_popup_delay(float p_time) {
+
+ if (p_time <= 0)
+ p_time = 0.01;
+
+ submenu_timer->set_wait_time(p_time);
+}
+
+float PopupMenu::get_submenu_popup_delay() const {
+
+ return submenu_timer->get_wait_time();
+}
+
+void PopupMenu::set_hide_on_window_lose_focus(bool p_enabled) {
+
+ hide_on_window_lose_focus = p_enabled;
+}
+
+bool PopupMenu::is_hide_on_window_lose_focus() const {
+
+ return hide_on_window_lose_focus;
+}
+
String PopupMenu::get_tooltip(const Point2 &p_pos) const {
int over = _get_mouse_over(p_pos);
@@ -1288,7 +1357,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_item", "idx"), &PopupMenu::remove_item);
- ClassDB::bind_method(D_METHOD("add_separator"), &PopupMenu::add_separator);
+ ClassDB::bind_method(D_METHOD("add_separator", "label"), &PopupMenu::add_separator, DEFVAL(String()));
ClassDB::bind_method(D_METHOD("clear"), &PopupMenu::clear);
ClassDB::bind_method(D_METHOD("_set_items"), &PopupMenu::_set_items);
@@ -1303,12 +1372,19 @@ void PopupMenu::_bind_methods() {
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);
+ 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);
+
+ ClassDB::bind_method(D_METHOD("set_hide_on_window_lose_focus", "enable"), &PopupMenu::set_hide_on_window_lose_focus);
+ ClassDB::bind_method(D_METHOD("is_hide_on_window_lose_focus"), &PopupMenu::is_hide_on_window_lose_focus);
+
ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay");
ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "ID")));
ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "ID")));