Sfoglia il codice sorgente

[macOS] Add methods to modify global and dock menus. Add ability to open multiple editor/project manager instances, recent/favourite project list to project manager dock menu and opened scene list to editor dock menu.

bruvzg 6 anni fa
parent
commit
db6d4352ea

+ 30 - 0
core/bind/core_bind.cpp

@@ -185,10 +185,31 @@ _ResourceSaver::_ResourceSaver() {
 
 /////////////////OS
 
+void _OS::global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta) {
+
+	OS::get_singleton()->global_menu_add_item(p_menu, p_label, p_signal, p_meta);
+}
+
+void _OS::global_menu_add_separator(const String &p_menu) {
+
+	OS::get_singleton()->global_menu_add_separator(p_menu);
+}
+
+void _OS::global_menu_remove_item(const String &p_menu, int p_idx) {
+
+	OS::get_singleton()->global_menu_remove_item(p_menu, p_idx);
+}
+
+void _OS::global_menu_clear(const String &p_menu) {
+
+	OS::get_singleton()->global_menu_clear(p_menu);
+}
+
 Point2 _OS::get_mouse_position() const {
 
 	return OS::get_singleton()->get_mouse_position();
 }
+
 void _OS::set_window_title(const String &p_title) {
 
 	OS::get_singleton()->set_window_title(p_title);
@@ -202,6 +223,7 @@ int _OS::get_mouse_button_state() const {
 String _OS::get_unique_id() const {
 	return OS::get_singleton()->get_unique_id();
 }
+
 bool _OS::has_touchscreen_ui_hint() const {
 
 	return OS::get_singleton()->has_touchscreen_ui_hint();
@@ -211,6 +233,7 @@ void _OS::set_clipboard(const String &p_text) {
 
 	OS::get_singleton()->set_clipboard(p_text);
 }
+
 String _OS::get_clipboard() const {
 
 	return OS::get_singleton()->get_clipboard();
@@ -257,12 +280,14 @@ void _OS::set_video_mode(const Size2 &p_size, bool p_fullscreen, bool p_resizeab
 	vm.resizable = p_resizeable;
 	OS::get_singleton()->set_video_mode(vm, p_screen);
 }
+
 Size2 _OS::get_video_mode(int p_screen) const {
 
 	OS::VideoMode vm;
 	vm = OS::get_singleton()->get_video_mode(p_screen);
 	return Size2(vm.width, vm.height);
 }
+
 bool _OS::is_video_mode_fullscreen(int p_screen) const {
 
 	OS::VideoMode vm;
@@ -1125,6 +1150,11 @@ void _OS::_bind_methods() {
 	//ClassDB::bind_method(D_METHOD("is_video_mode_resizable","screen"),&_OS::is_video_mode_resizable,DEFVAL(0));
 	//ClassDB::bind_method(D_METHOD("get_fullscreen_mode_list","screen"),&_OS::get_fullscreen_mode_list,DEFVAL(0));
 
+	ClassDB::bind_method(D_METHOD("global_menu_add_item", "menu", "label", "id", "meta"), &_OS::global_menu_add_item);
+	ClassDB::bind_method(D_METHOD("global_menu_add_separator", "menu"), &_OS::global_menu_add_separator);
+	ClassDB::bind_method(D_METHOD("global_menu_remove_item", "menu", "idx"), &_OS::global_menu_remove_item);
+	ClassDB::bind_method(D_METHOD("global_menu_clear", "menu"), &_OS::global_menu_clear);
+
 	ClassDB::bind_method(D_METHOD("get_video_driver_count"), &_OS::get_video_driver_count);
 	ClassDB::bind_method(D_METHOD("get_video_driver_name", "driver"), &_OS::get_video_driver_name);
 	ClassDB::bind_method(D_METHOD("get_current_video_driver"), &_OS::get_current_video_driver);

+ 5 - 0
core/bind/core_bind.h

@@ -143,6 +143,11 @@ public:
 		MONTH_DECEMBER
 	};
 
+	void global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta);
+	void global_menu_add_separator(const String &p_menu);
+	void global_menu_remove_item(const String &p_menu, int p_idx);
+	void global_menu_clear(const String &p_menu);
+
 	Point2 get_mouse_position() const;
 	void set_window_title(const String &p_title);
 	int get_mouse_button_state() const;

+ 8 - 0
core/os/main_loop.cpp

@@ -49,6 +49,8 @@ void MainLoop::_bind_methods() {
 	BIND_VMETHOD(MethodInfo("_drop_files", PropertyInfo(Variant::POOL_STRING_ARRAY, "files"), PropertyInfo(Variant::INT, "from_screen")));
 	BIND_VMETHOD(MethodInfo("_finalize"));
 
+	BIND_VMETHOD(MethodInfo("_global_menu_action", PropertyInfo(Variant::NIL, "id"), PropertyInfo(Variant::NIL, "meta")));
+
 	BIND_CONSTANT(NOTIFICATION_WM_MOUSE_ENTER);
 	BIND_CONSTANT(NOTIFICATION_WM_MOUSE_EXIT);
 	BIND_CONSTANT(NOTIFICATION_WM_FOCUS_IN);
@@ -115,6 +117,12 @@ void MainLoop::drop_files(const Vector<String> &p_files, int p_from_screen) {
 		get_script_instance()->call("_drop_files", p_files, p_from_screen);
 }
 
+void MainLoop::global_menu_action(const Variant &p_id, const Variant &p_meta) {
+
+	if (get_script_instance())
+		get_script_instance()->call("_global_menu_action", p_id, p_meta);
+}
+
 void MainLoop::finish() {
 
 	if (get_script_instance()) {

+ 1 - 0
core/os/main_loop.h

@@ -71,6 +71,7 @@ public:
 	virtual void finish();
 
 	virtual void drop_files(const Vector<String> &p_files, int p_from_screen = 0);
+	virtual void global_menu_action(const Variant &p_id, const Variant &p_meta);
 
 	void set_init_script(const Ref<Script> &p_init_script);
 

+ 5 - 0
core/os/os.h

@@ -143,6 +143,11 @@ public:
 
 	static OS *get_singleton();
 
+	virtual void global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta){};
+	virtual void global_menu_add_separator(const String &p_menu){};
+	virtual void global_menu_remove_item(const String &p_menu, int p_idx){};
+	virtual void global_menu_clear(const String &p_menu){};
+
 	void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type = Logger::ERR_ERROR);
 	void print(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
 	void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;

+ 10 - 0
doc/classes/MainLoop.xml

@@ -61,6 +61,16 @@
 				Called before the program exits.
 			</description>
 		</method>
+		<method name="_global_menu_action" qualifiers="virtual">
+			<return type="void">
+			</return>
+			<argument index="0" name="id" type="Variant">
+			</argument>
+			<argument index="1" name="meta" type="Variant">
+			</argument>
+			<description>
+			</description>
+		</method>
 		<method name="_idle" qualifiers="virtual">
 			<return type="bool">
 			</return>

+ 44 - 0
doc/classes/OS.xml

@@ -497,6 +497,50 @@
 				Returns unobscured area of the window where interactive controls should be rendered.
 			</description>
 		</method>
+		<method name="global_menu_add_item">
+			<return type="void">
+			</return>
+			<argument index="0" name="menu" type="String">
+			</argument>
+			<argument index="1" name="label" type="String">
+			</argument>
+			<argument index="2" name="id" type="Variant">
+			</argument>
+			<argument index="3" name="meta" type="Variant">
+			</argument>
+			<description>
+				Add a new item with text "label" to global menu. Use "_dock" menu to add item to the macOS dock icon menu.
+			</description>
+		</method>
+		<method name="global_menu_add_separator">
+			<return type="void">
+			</return>
+			<argument index="0" name="menu" type="String">
+			</argument>
+			<description>
+				Add a separator between items. Separators also occupy an index.
+			</description>
+		</method>
+		<method name="global_menu_clear">
+			<return type="void">
+			</return>
+			<argument index="0" name="menu" type="String">
+			</argument>
+			<description>
+				Clear the global menu, in effect removing all items.
+			</description>
+		</method>
+		<method name="global_menu_remove_item">
+			<return type="void">
+			</return>
+			<argument index="0" name="menu" type="String">
+			</argument>
+			<argument index="1" name="idx" type="int">
+			</argument>
+			<description>
+				Removes the item at index "idx" from the global menu. Note that the indexes of items after the removed item are going to be shifted by one.
+			</description>
+		</method>
 		<method name="has_environment" qualifiers="const">
 			<return type="bool">
 			</return>

+ 9 - 0
doc/classes/SceneTree.xml

@@ -324,6 +324,15 @@
 				Emitted when files are dragged from the OS file manager and dropped in the game window. The arguments are a list of file paths and the identifier of the screen where the drag originated.
 			</description>
 		</signal>
+		<signal name="global_menu_action">
+			<argument index="0" name="id" type="Nil">
+			</argument>
+			<argument index="1" name="meta" type="Nil">
+			</argument>
+			<description>
+				Emitted whenever global menu item is clicked.
+			</description>
+		</signal>
 		<signal name="idle_frame">
 			<description>
 				Emitted immediately before [method Node._process] is called on every node in the [SceneTree].

+ 26 - 0
editor/editor_node.cpp

@@ -135,6 +135,8 @@ void EditorNode::_update_scene_tabs() {
 
 	bool show_rb = EditorSettings::get_singleton()->get("interface/scene_tabs/show_script_button");
 
+	OS::get_singleton()->global_menu_clear("_dock");
+
 	scene_tabs->clear_tabs();
 	Ref<Texture> script_icon = gui_base->get_icon("Script", "EditorIcons");
 	for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
@@ -149,11 +151,16 @@ void EditorNode::_update_scene_tabs() {
 		bool unsaved = (i == current) ? saved_version != editor_data.get_undo_redo().get_version() : editor_data.get_scene_version(i) != 0;
 		scene_tabs->add_tab(editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), icon);
 
+		OS::get_singleton()->global_menu_add_item("_dock", editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), GLOBAL_SCENE, i);
+
 		if (show_rb && editor_data.get_scene_root_script(i).is_valid()) {
 			scene_tabs->set_tab_right_button(i, script_icon);
 		}
 	}
 
+	OS::get_singleton()->global_menu_add_separator("_dock");
+	OS::get_singleton()->global_menu_add_item("_dock", TTR("New Window"), GLOBAL_NEW_WINDOW, Variant());
+
 	scene_tabs->set_current_tab(editor_data.get_edited_scene());
 
 	if (scene_tabs->get_offset_buttons_visible()) {
@@ -290,6 +297,7 @@ void EditorNode::_notification(int p_what) {
 			get_tree()->get_root()->set_as_audio_listener_2d(false);
 			get_tree()->set_auto_accept_quit(false);
 			get_tree()->connect("files_dropped", this, "_dropped_files");
+			get_tree()->connect("global_menu_action", this, "_global_menu_action");
 
 			/* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */
 		} break;
@@ -4941,6 +4949,23 @@ void EditorNode::remove_tool_menu_item(const String &p_name) {
 	}
 }
 
+void EditorNode::_global_menu_action(const Variant &p_id, const Variant &p_meta) {
+
+	int id = (int)p_id;
+	if (id == GLOBAL_NEW_WINDOW) {
+		if (OS::get_singleton()->get_main_loop()) {
+			List<String> args;
+			String exec = OS::get_singleton()->get_executable_path();
+
+			OS::ProcessID pid = 0;
+			OS::get_singleton()->execute(exec, args, false, &pid);
+		}
+	} else if (id == GLOBAL_SCENE) {
+		int idx = (int)p_meta;
+		scene_tabs->set_current_tab(idx);
+	}
+}
+
 void EditorNode::_dropped_files(const Vector<String> &p_files, int p_screen) {
 
 	String to_path = ProjectSettings::get_singleton()->globalize_path(get_filesystem_dock()->get_selected_path());
@@ -5322,6 +5347,7 @@ void EditorNode::_bind_methods() {
 
 	ClassDB::bind_method("_clear_undo_history", &EditorNode::_clear_undo_history);
 	ClassDB::bind_method("_dropped_files", &EditorNode::_dropped_files);
+	ClassDB::bind_method(D_METHOD("_global_menu_action"), &EditorNode::_global_menu_action, DEFVAL(Variant()));
 	ClassDB::bind_method("_toggle_distraction_free_mode", &EditorNode::_toggle_distraction_free_mode);
 	ClassDB::bind_method("edit_item_resource", &EditorNode::edit_item_resource);
 

+ 4 - 0
editor/editor_node.h

@@ -207,6 +207,9 @@ private:
 
 		SET_VIDEO_DRIVER_SAVE_AND_RESTART,
 
+		GLOBAL_NEW_WINDOW,
+		GLOBAL_SCENE,
+
 		IMPORT_PLUGIN_BASE = 100,
 
 		TOOL_MENU_BASE = 1000
@@ -504,6 +507,7 @@ private:
 	void _add_to_recent_scenes(const String &p_scene);
 	void _update_recent_scenes();
 	void _open_recent_scene(int p_idx);
+	void _global_menu_action(const Variant &p_id, const Variant &p_meta);
 	void _dropped_files(const Vector<String> &p_files, int p_screen);
 	void _add_dropped_files_recursive(const Vector<String> &p_files, String to_path);
 	String _recent_scene;

+ 35 - 0
editor/project_manager.cpp

@@ -946,6 +946,11 @@ public:
 	static const char *SIGNAL_SELECTION_CHANGED;
 	static const char *SIGNAL_PROJECT_ASK_OPEN;
 
+	enum MenuOptions {
+		GLOBAL_NEW_WINDOW,
+		GLOBAL_OPEN_PROJECT
+	};
+
 	// Can often be passed by copy
 	struct Item {
 		String project_key;
@@ -1181,6 +1186,7 @@ void ProjectList::load_projects() {
 	_projects.clear();
 	_last_clicked = "";
 	_selected_project_keys.clear();
+	OS::get_singleton()->global_menu_clear("_dock");
 
 	// Load data
 	// TODO Would be nice to change how projects and favourites are stored... it complicates things a bit.
@@ -1218,6 +1224,9 @@ void ProjectList::load_projects() {
 		create_project_item_control(i);
 	}
 
+	OS::get_singleton()->global_menu_add_separator("_dock");
+	OS::get_singleton()->global_menu_add_item("_dock", TTR("New Window"), GLOBAL_NEW_WINDOW, Variant());
+
 	sort_projects();
 
 	set_v_scroll(0);
@@ -1305,6 +1314,7 @@ void ProjectList::create_project_item_control(int p_index) {
 	fpath->set_clip_text(true);
 
 	_scroll_children->add_child(hb);
+	OS::get_singleton()->global_menu_add_item("_dock", item.project_name + " ( " + item.path + " )", GLOBAL_OPEN_PROJECT, Variant(item.path.plus_file("project.godot")));
 	item.control = hb;
 }
 
@@ -1894,6 +1904,29 @@ void ProjectManager::_confirm_update_settings() {
 	_open_selected_projects();
 }
 
+void ProjectManager::_global_menu_action(const Variant &p_id, const Variant &p_meta) {
+
+	int id = (int)p_id;
+	if (id == ProjectList::GLOBAL_NEW_WINDOW) {
+		List<String> args;
+		String exec = OS::get_singleton()->get_executable_path();
+
+		OS::ProcessID pid = 0;
+		OS::get_singleton()->execute(exec, args, false, &pid);
+	} else if (id == ProjectList::GLOBAL_OPEN_PROJECT) {
+		String conf = (String)p_meta;
+
+		if (conf != String()) {
+			List<String> args;
+			args.push_back(conf);
+			String exec = OS::get_singleton()->get_executable_path();
+
+			OS::ProcessID pid = 0;
+			OS::get_singleton()->execute(exec, args, false, &pid);
+		}
+	}
+}
+
 void ProjectManager::_open_selected_projects() {
 
 	const Set<String> &selected_list = _project_list->get_selected_project_keys();
@@ -2236,6 +2269,7 @@ void ProjectManager::_bind_methods() {
 
 	ClassDB::bind_method("_open_selected_projects_ask", &ProjectManager::_open_selected_projects_ask);
 	ClassDB::bind_method("_open_selected_projects", &ProjectManager::_open_selected_projects);
+	ClassDB::bind_method(D_METHOD("_global_menu_action"), &ProjectManager::_global_menu_action, DEFVAL(Variant()));
 	ClassDB::bind_method("_run_project", &ProjectManager::_run_project);
 	ClassDB::bind_method("_run_project_confirm", &ProjectManager::_run_project_confirm);
 	ClassDB::bind_method("_scan_projects", &ProjectManager::_scan_projects);
@@ -2561,6 +2595,7 @@ ProjectManager::ProjectManager() {
 	}
 
 	SceneTree::get_singleton()->connect("files_dropped", this, "_files_dropped");
+	SceneTree::get_singleton()->connect("global_menu_action", this, "_global_menu_action");
 
 	run_error_diag = memnew(AcceptDialog);
 	gui_base->add_child(run_error_diag);

+ 2 - 0
editor/project_manager.h

@@ -43,6 +43,7 @@ class ProjectList;
 class ProjectListFilter;
 
 class ProjectManager : public Control {
+
 	GDCLASS(ProjectManager, Control);
 
 	Button *erase_btn;
@@ -96,6 +97,7 @@ class ProjectManager : public Control {
 	void _restart_confirm();
 	void _exit_dialog();
 	void _scan_begin(const String &p_base);
+	void _global_menu_action(const Variant &p_id, const Variant &p_meta);
 
 	void _confirm_update_settings();
 

+ 25 - 0
platform/osx/os_osx.h

@@ -157,6 +157,26 @@ public:
 	int video_driver_index;
 	virtual int get_current_video_driver() const;
 
+	struct GlobalMenuItem {
+		String label;
+		Variant signal;
+		Variant meta;
+
+		GlobalMenuItem() {
+			//NOP
+		}
+
+		GlobalMenuItem(const String &p_label, const Variant &p_signal, const Variant &p_meta) {
+			label = p_label;
+			signal = p_signal;
+			meta = p_meta;
+		}
+	};
+
+	Map<String, Vector<GlobalMenuItem> > global_menus;
+
+	void _update_global_menu();
+
 protected:
 	virtual void initialize_core();
 	virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
@@ -168,6 +188,11 @@ protected:
 public:
 	static OS_OSX *singleton;
 
+	void global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta);
+	void global_menu_add_separator(const String &p_menu);
+	void global_menu_remove_item(const String &p_menu, int p_idx);
+	void global_menu_clear(const String &p_menu);
+
 	void wm_minimized(bool p_minimized);
 
 	virtual String get_name() const;

+ 93 - 1
platform/osx/os_osx.mm

@@ -204,11 +204,53 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
 	}
 }
 
+- (void)globalMenuCallback:(id)sender {
+
+	if (![sender representedObject])
+		return;
+
+	OS_OSX::GlobalMenuItem *item = (OS_OSX::GlobalMenuItem *)[[sender representedObject] pointerValue];
+
+	if (!item)
+		return;
+
+	OS_OSX::singleton->main_loop->global_menu_action(item->signal, item->meta);
+}
+
+- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
+
+	NSMenu *menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
+
+	Vector<OS_OSX::GlobalMenuItem> &E = OS_OSX::singleton->global_menus["_dock"];
+	for (int i = 0; i < E.size(); i++) {
+		if (E[i].label == String()) {
+			[menu addItem:[NSMenuItem separatorItem]];
+		} else {
+			NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:E[i].label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
+			[menu_item setRepresentedObject:[NSValue valueWithPointer:&(E[i])]];
+		}
+	}
+
+	return menu;
+}
+
 - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
-	// Note: called before main loop init!
+	// Note: may be called called before main loop init!
 	char *utfs = strdup([filename UTF8String]);
 	OS_OSX::singleton->open_with_filename.parse_utf8(utfs);
 	free(utfs);
+
+#ifdef TOOLS_ENABLED
+	// Open new instance
+	if (OS_OSX::singleton->get_main_loop()) {
+		List<String> args;
+		args.push_back(OS_OSX::singleton->open_with_filename);
+		String exec = OS::get_singleton()->get_executable_path();
+
+		OS::ProcessID pid = 0;
+		OS::get_singleton()->execute(exec, args, false, &pid);
+	}
+#endif
 	return YES;
 }
 
@@ -1266,6 +1308,56 @@ inline void sendPanEvent(double dx, double dy, int modifierFlags) {
 
 @end
 
+void OS_OSX::_update_global_menu() {
+
+	NSMenu *main_menu = [NSApp mainMenu];
+
+	for (int i = 1; i < [main_menu numberOfItems]; i++) {
+		[main_menu removeItemAtIndex:i];
+	}
+	for (Map<String, Vector<GlobalMenuItem> >::Element *E = global_menus.front(); E; E = E->next()) {
+		if (E->key() != "_dock") {
+			NSMenu *menu = [[[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:E->key().utf8().get_data()]] autorelease];
+			for (int i = 0; i < E->get().size(); i++) {
+				if (E->get()[i].label == String()) {
+					[menu addItem:[NSMenuItem separatorItem]];
+				} else {
+					NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:E->get()[i].label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
+					[menu_item setRepresentedObject:[NSValue valueWithPointer:&(E->get()[i])]];
+				}
+			}
+			NSMenuItem *menu_item = [main_menu addItemWithTitle:[NSString stringWithUTF8String:E->key().utf8().get_data()] action:nil keyEquivalent:@""];
+			[main_menu setSubmenu:menu forItem:menu_item];
+		}
+	}
+}
+
+void OS_OSX::global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta) {
+
+	global_menus[p_menu].push_back(GlobalMenuItem(p_label, p_signal, p_meta));
+	_update_global_menu();
+}
+
+void OS_OSX::global_menu_add_separator(const String &p_menu) {
+
+	global_menus[p_menu].push_back(GlobalMenuItem());
+	_update_global_menu();
+}
+
+void OS_OSX::global_menu_remove_item(const String &p_menu, int p_idx) {
+
+	ERR_FAIL_INDEX(p_idx, global_menus[p_menu].size());
+
+	global_menus[p_menu].remove(p_idx);
+	_update_global_menu();
+}
+
+void OS_OSX::global_menu_clear(const String &p_menu) {
+
+	global_menus[p_menu].clear();
+	_update_global_menu();
+}
+
 Point2 OS_OSX::get_ime_selection() const {
 
 	return im_selection;

+ 7 - 0
scene/main/scene_tree.cpp

@@ -1673,6 +1673,12 @@ void SceneTree::drop_files(const Vector<String> &p_files, int p_from_screen) {
 	MainLoop::drop_files(p_files, p_from_screen);
 }
 
+void SceneTree::global_menu_action(const Variant &p_id, const Variant &p_meta) {
+
+	emit_signal("global_menu_action", p_id, p_meta);
+	MainLoop::global_menu_action(p_id, p_meta);
+}
+
 Ref<SceneTreeTimer> SceneTree::create_timer(float p_delay_sec, bool p_process_pause) {
 
 	Ref<SceneTreeTimer> stt;
@@ -1894,6 +1900,7 @@ void SceneTree::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("physics_frame"));
 
 	ADD_SIGNAL(MethodInfo("files_dropped", PropertyInfo(Variant::POOL_STRING_ARRAY, "files"), PropertyInfo(Variant::INT, "screen")));
+	ADD_SIGNAL(MethodInfo("global_menu_action", PropertyInfo(Variant::NIL, "id"), PropertyInfo(Variant::NIL, "meta")));
 	ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id")));
 	ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id")));
 	ADD_SIGNAL(MethodInfo("connected_to_server"));

+ 1 - 0
scene/main/scene_tree.h

@@ -407,6 +407,7 @@ public:
 	static SceneTree *get_singleton() { return singleton; }
 
 	void drop_files(const Vector<String> &p_files, int p_from_screen = 0);
+	void global_menu_action(const Variant &p_id, const Variant &p_meta);
 
 	//network API