diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index 0f5687f0918..004bbe2286b 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -395,6 +395,12 @@
Returns [code]true[/code] if the specified item's shortcut is disabled.
+
+
+
+ Returns [code]true[/code] if the system native menu is supported and currently used by this [PopupMenu].
+
+
@@ -636,6 +642,7 @@
If [code]true[/code], [MenuBar] will use native menu when supported.
+ [b]Note:[/b] If [PopupMenu] is linked to [StatusIndicator], [MenuBar], or another [PopupMenu] item it can use native menu regardless of this property, use [method is_native_menu] to check it.
Sets the delay time in seconds for the submenu item to popup on mouse hovering. If the popup menu is added as a child of another (acting as a submenu), it will inherit the delay time of the parent menu item.
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index a1a91345ac4..da453919951 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -568,23 +568,7 @@ void DisplayServerMacOS::menu_callback(id p_sender) {
}
GodotMenuItem *value = [p_sender representedObject];
-
if (value) {
- if (value->max_states > 0) {
- value->state++;
- if (value->state >= value->max_states) {
- value->state = 0;
- }
- }
-
- if (value->checkable_type == CHECKABLE_TYPE_CHECK_BOX) {
- if ([p_sender state] == NSControlStateValueOff) {
- [p_sender setState:NSControlStateValueOn];
- } else {
- [p_sender setState:NSControlStateValueOff];
- }
- }
-
if (value->callback.is_valid()) {
MenuCall mc;
mc.tag = value->meta;
diff --git a/platform/macos/godot_menu_item.h b/platform/macos/godot_menu_item.h
index b6e2d41c08d..e1af317259b 100644
--- a/platform/macos/godot_menu_item.h
+++ b/platform/macos/godot_menu_item.h
@@ -52,6 +52,7 @@ enum GlobalMenuCheckType {
Callable hover_callback;
Variant meta;
GlobalMenuCheckType checkable_type;
+ bool checked;
int max_states;
int state;
Ref img;
diff --git a/platform/macos/godot_menu_item.mm b/platform/macos/godot_menu_item.mm
index 30dac9be9be..479542113af 100644
--- a/platform/macos/godot_menu_item.mm
+++ b/platform/macos/godot_menu_item.mm
@@ -31,4 +31,18 @@
#include "godot_menu_item.h"
@implementation GodotMenuItem
+
+- (id)init {
+ self = [super init];
+
+ self->callback = Callable();
+ self->key_callback = Callable();
+ self->checkable_type = GlobalMenuCheckType::CHECKABLE_TYPE_NONE;
+ self->checked = false;
+ self->max_states = 0;
+ self->state = 0;
+
+ return self;
+}
+
@end
diff --git a/platform/macos/native_menu_macos.mm b/platform/macos/native_menu_macos.mm
index 1ae1137ca08..802d58dc269 100644
--- a/platform/macos/native_menu_macos.mm
+++ b/platform/macos/native_menu_macos.mm
@@ -373,12 +373,7 @@ int NativeMenuMacOS::add_submenu_item(const RID &p_rid, const String &p_label, c
menu_item = [md->menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@"" atIndex:p_index];
GodotMenuItem *obj = [[GodotMenuItem alloc] init];
- obj->callback = Callable();
- obj->key_callback = Callable();
obj->meta = p_tag;
- obj->checkable_type = CHECKABLE_TYPE_NONE;
- obj->max_states = 0;
- obj->state = 0;
[menu_item setRepresentedObject:obj];
[md_sub->menu setTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()]];
@@ -417,9 +412,6 @@ int NativeMenuMacOS::add_item(const RID &p_rid, const String &p_label, const Cal
obj->callback = p_callback;
obj->key_callback = p_key_callback;
obj->meta = p_tag;
- obj->checkable_type = CHECKABLE_TYPE_NONE;
- obj->max_states = 0;
- obj->state = 0;
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
[menu_item setRepresentedObject:obj];
}
@@ -438,8 +430,6 @@ int NativeMenuMacOS::add_check_item(const RID &p_rid, const String &p_label, con
obj->key_callback = p_key_callback;
obj->meta = p_tag;
obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX;
- obj->max_states = 0;
- obj->state = 0;
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
[menu_item setRepresentedObject:obj];
}
@@ -457,9 +447,6 @@ int NativeMenuMacOS::add_icon_item(const RID &p_rid, const Ref &p_ico
obj->callback = p_callback;
obj->key_callback = p_key_callback;
obj->meta = p_tag;
- obj->checkable_type = CHECKABLE_TYPE_NONE;
- obj->max_states = 0;
- obj->state = 0;
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
if (ds && p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
obj->img = p_icon->get_image();
@@ -489,8 +476,6 @@ int NativeMenuMacOS::add_icon_check_item(const RID &p_rid, const Ref
obj->key_callback = p_key_callback;
obj->meta = p_tag;
obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX;
- obj->max_states = 0;
- obj->state = 0;
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
if (ds && p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
obj->img = p_icon->get_image();
@@ -520,8 +505,6 @@ int NativeMenuMacOS::add_radio_check_item(const RID &p_rid, const String &p_labe
obj->key_callback = p_key_callback;
obj->meta = p_tag;
obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON;
- obj->max_states = 0;
- obj->state = 0;
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
[menu_item setRepresentedObject:obj];
}
@@ -540,8 +523,6 @@ int NativeMenuMacOS::add_icon_radio_check_item(const RID &p_rid, const Refkey_callback = p_key_callback;
obj->meta = p_tag;
obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON;
- obj->max_states = 0;
- obj->state = 0;
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
if (ds && p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
obj->img = p_icon->get_image();
@@ -570,7 +551,6 @@ int NativeMenuMacOS::add_multistate_item(const RID &p_rid, const String &p_label
obj->callback = p_callback;
obj->key_callback = p_key_callback;
obj->meta = p_tag;
- obj->checkable_type = CHECKABLE_TYPE_NONE;
obj->max_states = p_max_states;
obj->state = p_default_state;
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
@@ -640,7 +620,10 @@ bool NativeMenuMacOS::is_item_checked(const RID &p_rid, int p_idx) const {
ERR_FAIL_COND_V(p_idx >= item_start + item_count, false);
const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx];
if (menu_item) {
- return ([menu_item state] == NSControlStateValueOn);
+ const GodotMenuItem *obj = [menu_item representedObject];
+ if (obj) {
+ return obj->checked;
+ }
}
return false;
}
@@ -958,10 +941,14 @@ void NativeMenuMacOS::set_item_checked(const RID &p_rid, int p_idx, bool p_check
ERR_FAIL_COND(p_idx >= item_start + item_count);
NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx];
if (menu_item) {
- if (p_checked) {
- [menu_item setState:NSControlStateValueOn];
- } else {
- [menu_item setState:NSControlStateValueOff];
+ GodotMenuItem *obj = [menu_item representedObject];
+ if (obj) {
+ obj->checked = p_checked;
+ if (p_checked) {
+ [menu_item setState:NSControlStateValueOn];
+ } else {
+ [menu_item setState:NSControlStateValueOff];
+ }
}
}
}
diff --git a/platform/windows/native_menu_windows.cpp b/platform/windows/native_menu_windows.cpp
index d9dc28e9d90..fde55918e43 100644
--- a/platform/windows/native_menu_windows.cpp
+++ b/platform/windows/native_menu_windows.cpp
@@ -81,22 +81,6 @@ void NativeMenuWindows::_menu_activate(HMENU p_menu, int p_index) const {
if (GetMenuItemInfoW(md->menu, p_index, true, &item)) {
MenuItemData *item_data = (MenuItemData *)item.dwItemData;
if (item_data) {
- if (item_data->max_states > 0) {
- item_data->state++;
- if (item_data->state >= item_data->max_states) {
- item_data->state = 0;
- }
- }
-
- if (item_data->checkable_type == CHECKABLE_TYPE_CHECK_BOX) {
- if ((item.fState & MFS_CHECKED) == MFS_CHECKED) {
- item.fState &= ~MFS_CHECKED;
- } else {
- item.fState |= MFS_CHECKED;
- }
- SetMenuItemInfoW(md->menu, p_index, true, &item);
- }
-
if (item_data->callback.is_valid()) {
Variant ret;
Callable::CallError ce;
@@ -619,9 +603,12 @@ bool NativeMenuWindows::is_item_checked(const RID &p_rid, int p_idx) const {
MENUITEMINFOW item;
ZeroMemory(&item, sizeof(item));
item.cbSize = sizeof(item);
- item.fMask = MIIM_STATE;
+ item.fMask = MIIM_STATE | MIIM_DATA;
if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
- return (item.fState & MFS_CHECKED) == MFS_CHECKED;
+ MenuItemData *item_data = (MenuItemData *)item.dwItemData;
+ if (item_data) {
+ return item_data->checked;
+ }
}
return false;
}
@@ -861,12 +848,16 @@ void NativeMenuWindows::set_item_checked(const RID &p_rid, int p_idx, bool p_che
MENUITEMINFOW item;
ZeroMemory(&item, sizeof(item));
item.cbSize = sizeof(item);
- item.fMask = MIIM_STATE;
+ item.fMask = MIIM_STATE | MIIM_DATA;
if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
- if (p_checked) {
- item.fState |= MFS_CHECKED;
- } else {
- item.fState &= ~MFS_CHECKED;
+ MenuItemData *item_data = (MenuItemData *)item.dwItemData;
+ if (item_data) {
+ item_data->checked = p_checked;
+ if (p_checked) {
+ item.fState |= MFS_CHECKED;
+ } else {
+ item.fState &= ~MFS_CHECKED;
+ }
}
SetMenuItemInfoW(md->menu, p_idx, true, &item);
}
diff --git a/platform/windows/native_menu_windows.h b/platform/windows/native_menu_windows.h
index 5c4aaa52c8b..235a4b332af 100644
--- a/platform/windows/native_menu_windows.h
+++ b/platform/windows/native_menu_windows.h
@@ -51,6 +51,7 @@ class NativeMenuWindows : public NativeMenu {
Callable callback;
Variant meta;
GlobalMenuCheckType checkable_type;
+ bool checked = false;
int max_states = 0;
int state = 0;
Ref img;
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 4f07fdb87b9..7f795ea710f 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -2314,6 +2314,16 @@ bool PopupMenu::is_prefer_native_menu() const {
return prefer_native;
}
+bool PopupMenu::is_native_menu() const {
+#ifdef TOOLS_ENABLED
+ if (is_part_of_edited_scene()) {
+ return false;
+ }
+#endif
+
+ return global_menu.is_valid();
+}
+
bool PopupMenu::activate_item_by_event(const Ref &p_event, bool p_for_global_only) {
ERR_FAIL_COND_V(p_event.is_null(), false);
Key code = Key::NONE;
@@ -2643,6 +2653,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_prefer_native_menu", "enabled"), &PopupMenu::set_prefer_native_menu);
ClassDB::bind_method(D_METHOD("is_prefer_native_menu"), &PopupMenu::is_prefer_native_menu);
+ ClassDB::bind_method(D_METHOD("is_native_menu"), &PopupMenu::is_native_menu);
ClassDB::bind_method(D_METHOD("add_item", "label", "id", "accel"), &PopupMenu::add_item, DEFVAL(-1), DEFVAL(0));
ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_item, DEFVAL(-1), DEFVAL(0));
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index c6eef03aca3..5313dae404f 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -330,6 +330,8 @@ public:
void set_prefer_native_menu(bool p_enabled);
bool is_prefer_native_menu() const;
+ bool is_native_menu() const;
+
void scroll_to_item(int p_idx);
bool activate_item_by_event(const Ref &p_event, bool p_for_global_only = false);