Skip to content

Commit ea7d988

Browse files
committed
Merge pull request godotengine#92781 from bruvzg/menu_open_close_imp
[macOS] Improve native menu open/close callbacks.
2 parents 54e09c3 + 1f7bf27 commit ea7d988

File tree

8 files changed

+72
-10
lines changed

8 files changed

+72
-10
lines changed

doc/classes/NativeMenu.xml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,14 @@
473473
[b]Note:[/b] This method is implemented on macOS and Windows.
474474
</description>
475475
</method>
476+
<method name="is_opened" qualifiers="const">
477+
<return type="bool" />
478+
<param index="0" name="rid" type="RID" />
479+
<description>
480+
Returns [code]true[/code] if the menu is currently opened.
481+
[b]Note:[/b] This method is implemented only on macOS.
482+
</description>
483+
</method>
476484
<method name="is_system_menu" qualifiers="const">
477485
<return type="bool" />
478486
<param index="0" name="rid" type="RID" />
@@ -699,6 +707,7 @@
699707
<param index="1" name="callback" type="Callable" />
700708
<description>
701709
Registers callable to emit when the menu is about to show.
710+
[b]Note:[/b] The OS can simulate menu opening to track menu item changes and global shortcuts, in which case the corresponding close callback is not triggered. Use [method is_opened] to check if the menu is currently opened.
702711
[b]Note:[/b] This method is implemented only on macOS.
703712
</description>
704713
</method>
@@ -707,7 +716,7 @@
707716
<param index="0" name="rid" type="RID" />
708717
<param index="1" name="callback" type="Callable" />
709718
<description>
710-
Registers callable to emit when the menu is about to closed.
719+
Registers callable to emit after the menu is closed.
711720
[b]Note:[/b] This method is implemented only on macOS.
712721
</description>
713722
</method>

platform/macos/godot_menu_delegate.mm

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,20 @@ @implementation GodotMenuDelegate
4040
- (void)doNothing:(id)sender {
4141
}
4242

43-
- (void)menuNeedsUpdate:(NSMenu *)menu {
43+
- (void)menuWillOpen:(NSMenu *)menu {
4444
if (NativeMenu::get_singleton()) {
4545
NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton();
4646
nmenu->_menu_open(menu);
4747
}
4848
}
4949

50+
- (void)menuNeedsUpdate:(NSMenu *)menu {
51+
if (NativeMenu::get_singleton()) {
52+
NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton();
53+
nmenu->_menu_need_update(menu);
54+
}
55+
}
56+
5057
- (void)menuDidClose:(NSMenu *)menu {
5158
if (NativeMenu::get_singleton()) {
5259
NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton();

platform/macos/native_menu_macos.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,11 @@ class NativeMenuMacOS : public NativeMenu {
7373
public:
7474
void _register_system_menus(NSMenu *p_main_menu, NSMenu *p_application_menu, NSMenu *p_window_menu, NSMenu *p_help_menu, NSMenu *p_dock_menu);
7575
NSMenu *_get_dock_menu();
76+
77+
void _menu_need_update(NSMenu *p_menu);
7678
void _menu_open(NSMenu *p_menu);
7779
void _menu_close(NSMenu *p_menu);
80+
void _menu_close_cb(const RID &p_rid);
7881

7982
virtual bool has_feature(Feature p_feature) const override;
8083

@@ -98,6 +101,8 @@ class NativeMenuMacOS : public NativeMenu {
98101
virtual void set_minimum_width(const RID &p_rid, float p_width) override;
99102
virtual float get_minimum_width(const RID &p_rid) const override;
100103

104+
virtual bool is_opened(const RID &p_rid) const override;
105+
101106
virtual int add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag = Variant(), int p_index = -1) override;
102107
virtual int add_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
103108
virtual int add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;

platform/macos/native_menu_macos.mm

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,22 @@
9191
if (menu_lookup.has(p_menu)) {
9292
MenuData *md = menus.get_or_null(menu_lookup[p_menu]);
9393
if (md) {
94+
// Note: Set "is_open" flag, but do not call callback, menu items can't be modified during this call and "_menu_need_update" will be called right before it.
9495
md->is_open = true;
96+
}
97+
}
98+
}
99+
100+
void NativeMenuMacOS::_menu_need_update(NSMenu *p_menu) {
101+
if (menu_lookup.has(p_menu)) {
102+
MenuData *md = menus.get_or_null(menu_lookup[p_menu]);
103+
if (md) {
104+
// Note: "is_open" flag is set by "_menu_open", this method is always called before menu is shown, but might be called for the other reasons as well.
95105
if (md->open_cb.is_valid()) {
96106
Variant ret;
97107
Callable::CallError ce;
98108

109+
// Callback is called directly, since it's expected to modify menu items before it's shown.
99110
md->open_cb.callp(nullptr, 0, ret, ce);
100111
if (ce.error != Callable::CallError::CALL_OK) {
101112
ERR_PRINT(vformat("Failed to execute menu open callback: %s.", Variant::get_callable_error_text(md->open_cb, nullptr, 0, ce)));
@@ -110,15 +121,22 @@
110121
MenuData *md = menus.get_or_null(menu_lookup[p_menu]);
111122
if (md) {
112123
md->is_open = false;
113-
if (md->close_cb.is_valid()) {
114-
Variant ret;
115-
Callable::CallError ce;
116124

117-
md->close_cb.callp(nullptr, 0, ret, ce);
118-
if (ce.error != Callable::CallError::CALL_OK) {
119-
ERR_PRINT(vformat("Failed to execute menu close callback: %s.", Variant::get_callable_error_text(md->close_cb, nullptr, 0, ce)));
120-
}
121-
}
125+
// Callback called deferred, since it should not modify menu items during "_menu_close" call.
126+
callable_mp(this, &NativeMenuMacOS::_menu_close_cb).call_deferred(menu_lookup[p_menu]);
127+
}
128+
}
129+
}
130+
131+
void NativeMenuMacOS::_menu_close_cb(const RID &p_rid) {
132+
MenuData *md = menus.get_or_null(p_rid);
133+
if (md->close_cb.is_valid()) {
134+
Variant ret;
135+
Callable::CallError ce;
136+
137+
md->close_cb.callp(nullptr, 0, ret, ce);
138+
if (ce.error != Callable::CallError::CALL_OK) {
139+
ERR_PRINT(vformat("Failed to execute menu close callback: %s.", Variant::get_callable_error_text(md->close_cb, nullptr, 0, ce)));
122140
}
123141
}
124142
}
@@ -328,6 +346,13 @@
328346
return md->menu.minimumWidth * DisplayServer::get_singleton()->screen_get_max_scale();
329347
}
330348

349+
bool NativeMenuMacOS::is_opened(const RID &p_rid) const {
350+
const MenuData *md = menus.get_or_null(p_rid);
351+
ERR_FAIL_NULL_V(md, false);
352+
353+
return md->is_open;
354+
}
355+
331356
int NativeMenuMacOS::add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag, int p_index) {
332357
MenuData *md = menus.get_or_null(p_rid);
333358
MenuData *md_sub = menus.get_or_null(p_submenu_rid);

platform/windows/native_menu_windows.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,11 @@ float NativeMenuWindows::get_minimum_width(const RID &p_rid) const {
231231
return 0.f;
232232
}
233233

234+
bool NativeMenuWindows::is_opened(const RID &p_rid) const {
235+
// Not supported.
236+
return false;
237+
}
238+
234239
int NativeMenuWindows::add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag, int p_index) {
235240
MenuData *md = menus.get_or_null(p_rid);
236241
MenuData *md_sub = menus.get_or_null(p_submenu_rid);

platform/windows/native_menu_windows.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ class NativeMenuWindows : public NativeMenu {
9090
virtual void set_minimum_width(const RID &p_rid, float p_width) override;
9191
virtual float get_minimum_width(const RID &p_rid) const override;
9292

93+
virtual bool is_opened(const RID &p_rid) const override;
94+
9395
virtual int add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag = Variant(), int p_index = -1) override;
9496
virtual int add_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
9597
virtual int add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;

servers/display/native_menu.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ void NativeMenu::_bind_methods() {
5656
ClassDB::bind_method(D_METHOD("set_minimum_width", "rid", "width"), &NativeMenu::set_minimum_width);
5757
ClassDB::bind_method(D_METHOD("get_minimum_width", "rid"), &NativeMenu::get_minimum_width);
5858

59+
ClassDB::bind_method(D_METHOD("is_opened", "rid"), &NativeMenu::is_opened);
60+
5961
ClassDB::bind_method(D_METHOD("add_submenu_item", "rid", "label", "submenu_rid", "tag", "index"), &NativeMenu::add_submenu_item, DEFVAL(Variant()), DEFVAL(-1));
6062
ClassDB::bind_method(D_METHOD("add_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
6163
ClassDB::bind_method(D_METHOD("add_check_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
@@ -200,6 +202,11 @@ Callable NativeMenu::get_popup_close_callback(const RID &p_rid) const {
200202
return Callable();
201203
}
202204

205+
bool NativeMenu::is_opened(const RID &p_rid) const {
206+
WARN_PRINT("Global menus are not supported on this platform.");
207+
return false;
208+
}
209+
203210
void NativeMenu::set_minimum_width(const RID &p_rid, float p_width) {
204211
WARN_PRINT("Global menus are not supported on this platform.");
205212
}

servers/display/native_menu.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ class NativeMenu : public Object {
9090
virtual void set_minimum_width(const RID &p_rid, float p_width);
9191
virtual float get_minimum_width(const RID &p_rid) const;
9292

93+
virtual bool is_opened(const RID &p_rid) const;
94+
9395
virtual int add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag = Variant(), int p_index = -1);
9496
virtual int add_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
9597
virtual int add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);

0 commit comments

Comments
 (0)