diff options
Diffstat (limited to 'platform')
-rw-r--r-- | platform/linuxbsd/freedesktop_portal_desktop.cpp | 100 | ||||
-rw-r--r-- | platform/linuxbsd/freedesktop_portal_desktop.h | 5 | ||||
-rw-r--r-- | platform/linuxbsd/platform_gl.h | 4 | ||||
-rw-r--r-- | platform/linuxbsd/x11/SCsub | 4 | ||||
-rw-r--r-- | platform/linuxbsd/x11/display_server_x11.cpp | 80 | ||||
-rw-r--r-- | platform/linuxbsd/x11/display_server_x11.h | 2 | ||||
-rw-r--r-- | platform/linuxbsd/x11/gl_manager_x11_egl.cpp | 63 | ||||
-rw-r--r-- | platform/linuxbsd/x11/gl_manager_x11_egl.h | 61 | ||||
-rw-r--r-- | platform/macos/display_server_macos.h | 17 | ||||
-rw-r--r-- | platform/macos/display_server_macos.mm | 551 | ||||
-rw-r--r-- | platform/macos/doc_classes/EditorExportPlatformMacOS.xml | 12 | ||||
-rw-r--r-- | platform/macos/godot_menu_delegate.mm | 28 | ||||
-rw-r--r-- | platform/macos/godot_menu_item.h | 1 | ||||
-rw-r--r-- | platform/web/js/libs/library_godot_javascript_singleton.js | 2 | ||||
-rw-r--r-- | platform/windows/display_server_windows.cpp | 44 |
15 files changed, 759 insertions, 215 deletions
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp index e9f55faf7f..cf4354139a 100644 --- a/platform/linuxbsd/freedesktop_portal_desktop.cpp +++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp @@ -142,36 +142,40 @@ void FreeDesktopPortalDesktop::append_dbus_string(DBusMessageIter *p_iter, const } } -void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters) { +void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filter_names, const Vector<String> &p_filter_exts) { DBusMessageIter dict_iter; DBusMessageIter var_iter; DBusMessageIter arr_iter; const char *filters_key = "filters"; + ERR_FAIL_COND(p_filter_names.size() != p_filter_exts.size()); + dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter); dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &filters_key); dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "a(sa(us))", &var_iter); dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "(sa(us))", &arr_iter); - for (int i = 0; i < p_filters.size(); i++) { - Vector<String> tokens = p_filters[i].split(";"); - if (tokens.size() == 2) { - DBusMessageIter struct_iter; - DBusMessageIter array_iter; - DBusMessageIter array_struct_iter; - dbus_message_iter_open_container(&arr_iter, DBUS_TYPE_STRUCT, nullptr, &struct_iter); - append_dbus_string(&struct_iter, tokens[0]); - - dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(us)", &array_iter); + for (int i = 0; i < p_filter_names.size(); i++) { + DBusMessageIter struct_iter; + DBusMessageIter array_iter; + DBusMessageIter array_struct_iter; + dbus_message_iter_open_container(&arr_iter, DBUS_TYPE_STRUCT, nullptr, &struct_iter); + append_dbus_string(&struct_iter, p_filter_names[i]); + + dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(us)", &array_iter); + String flt = p_filter_exts[i]; + int filter_slice_count = flt.get_slice_count(","); + for (int j = 0; j < filter_slice_count; j++) { dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter); + String str = (flt.get_slice(",", j).strip_edges()); { const unsigned nil = 0; dbus_message_iter_append_basic(&array_struct_iter, DBUS_TYPE_UINT32, &nil); } - append_dbus_string(&array_struct_iter, tokens[1]); + append_dbus_string(&array_struct_iter, str); dbus_message_iter_close_container(&array_iter, &array_struct_iter); - dbus_message_iter_close_container(&struct_iter, &array_iter); - dbus_message_iter_close_container(&arr_iter, &struct_iter); } + dbus_message_iter_close_container(&struct_iter, &array_iter); + dbus_message_iter_close_container(&arr_iter, &struct_iter); } dbus_message_iter_close_container(&var_iter, &arr_iter); dbus_message_iter_close_container(&dict_iter, &var_iter); @@ -219,7 +223,7 @@ void FreeDesktopPortalDesktop::append_dbus_dict_bool(DBusMessageIter *p_iter, co dbus_message_iter_close_container(p_iter, &dict_iter); } -bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls) { +bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_iter, const Vector<String> &p_names, bool &r_cancel, Vector<String> &r_urls, int &r_index) { ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_UINT32, false); dbus_uint32_t resp_code; @@ -243,7 +247,22 @@ bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_it DBusMessageIter var_iter; dbus_message_iter_recurse(&iter, &var_iter); - if (strcmp(key, "uris") == 0) { + if (strcmp(key, "current_filter") == 0) { // (sa(us)) + if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_STRUCT) { + DBusMessageIter struct_iter; + dbus_message_iter_recurse(&var_iter, &struct_iter); + while (dbus_message_iter_get_arg_type(&struct_iter) == DBUS_TYPE_STRING) { + const char *value; + dbus_message_iter_get_basic(&struct_iter, &value); + String name = String::utf8(value); + + r_index = p_names.find(name); + if (!dbus_message_iter_next(&struct_iter)) { + break; + } + } + } + } else if (strcmp(key, "uris") == 0) { // as if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_ARRAY) { DBusMessageIter uri_iter; dbus_message_iter_recurse(&var_iter, &uri_iter); @@ -271,6 +290,30 @@ Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_windo return FAILED; } + ERR_FAIL_INDEX_V(int(p_mode), DisplayServer::FILE_DIALOG_MODE_SAVE_MAX, FAILED); + + Vector<String> filter_names; + Vector<String> filter_exts; + for (int i = 0; i < p_filters.size(); i++) { + Vector<String> tokens = p_filters[i].split(";"); + if (tokens.size() >= 1) { + String flt = tokens[0].strip_edges(); + if (!flt.is_empty()) { + if (tokens.size() == 2) { + filter_exts.push_back(flt); + filter_names.push_back(tokens[1]); + } else { + filter_exts.push_back(flt); + filter_names.push_back(flt); + } + } + } + } + if (filter_names.is_empty()) { + filter_exts.push_back("*.*"); + filter_names.push_back(RTR("All Files")); + } + DBusError err; dbus_error_init(&err); @@ -278,6 +321,7 @@ Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_windo FileDialogData fd; fd.callback = p_callback; fd.prev_focus = p_window_id; + fd.filter_names = filter_names; CryptoCore::RandomGenerator rng; ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator."); @@ -308,16 +352,10 @@ Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_windo // Generate FileChooser message. const char *method = nullptr; - switch (p_mode) { - case DisplayServer::FILE_DIALOG_MODE_SAVE_FILE: { - method = "SaveFile"; - } break; - case DisplayServer::FILE_DIALOG_MODE_OPEN_ANY: - case DisplayServer::FILE_DIALOG_MODE_OPEN_FILE: - case DisplayServer::FILE_DIALOG_MODE_OPEN_DIR: - case DisplayServer::FILE_DIALOG_MODE_OPEN_FILES: { - method = "OpenFile"; - } break; + if (p_mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) { + method = "SaveFile"; + } else { + method = "OpenFile"; } DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_FILE_CHOOSER, method); @@ -334,7 +372,7 @@ Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_windo append_dbus_dict_string(&arr_iter, "handle_token", token); append_dbus_dict_bool(&arr_iter, "multiple", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES); append_dbus_dict_bool(&arr_iter, "directory", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR); - append_dbus_dict_filters(&arr_iter, p_filters); + append_dbus_dict_filters(&arr_iter, filter_names, filter_exts); append_dbus_dict_string(&arr_iter, "current_folder", p_current_directory, true); if (p_mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) { append_dbus_dict_string(&arr_iter, "current_name", p_filename); @@ -409,13 +447,15 @@ void FreeDesktopPortalDesktop::_thread_file_dialog_monitor(void *p_ud) { if (dbus_message_iter_init(msg, &iter)) { bool cancel = false; Vector<String> uris; - file_chooser_parse_response(&iter, cancel, uris); + int index = 0; + file_chooser_parse_response(&iter, fd.filter_names, cancel, uris, index); if (fd.callback.is_valid()) { Variant v_status = !cancel; Variant v_files = uris; - Variant *v_args[2] = { &v_status, &v_files }; - fd.callback.call_deferredp((const Variant **)&v_args, 2); + Variant v_index = index; + Variant *v_args[3] = { &v_status, &v_files, &v_index }; + fd.callback.call_deferredp((const Variant **)&v_args, 3); } if (fd.prev_focus != DisplayServer::INVALID_WINDOW_ID) { callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd.prev_focus); diff --git a/platform/linuxbsd/freedesktop_portal_desktop.h b/platform/linuxbsd/freedesktop_portal_desktop.h index 6ffb3e7b04..503c382207 100644 --- a/platform/linuxbsd/freedesktop_portal_desktop.h +++ b/platform/linuxbsd/freedesktop_portal_desktop.h @@ -49,12 +49,13 @@ private: bool read_setting(const char *p_namespace, const char *p_key, int p_type, void *r_value); static void append_dbus_string(DBusMessageIter *p_iter, const String &p_string); - static void append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters); + static void append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filter_names, const Vector<String> &p_filter_exts); static void append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array = false); static void append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value); - static bool file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls); + static bool file_chooser_parse_response(DBusMessageIter *p_iter, const Vector<String> &p_names, bool &r_cancel, Vector<String> &r_urls, int &r_index); struct FileDialogData { + Vector<String> filter_names; DBusConnection *connection = nullptr; DisplayServer::WindowID prev_focus = DisplayServer::INVALID_WINDOW_ID; Callable callback; diff --git a/platform/linuxbsd/platform_gl.h b/platform/linuxbsd/platform_gl.h index 1c19c4518a..5d4c2d8978 100644 --- a/platform/linuxbsd/platform_gl.h +++ b/platform/linuxbsd/platform_gl.h @@ -35,6 +35,10 @@ #define GL_API_ENABLED // Allow using desktop GL. #endif +#ifndef GLES_API_ENABLED +#define GLES_API_ENABLED // Allow using GLES. +#endif + #include "thirdparty/glad/glad/egl.h" #include "thirdparty/glad/glad/gl.h" diff --git a/platform/linuxbsd/x11/SCsub b/platform/linuxbsd/x11/SCsub index a4890391ce..bbfaaf10d1 100644 --- a/platform/linuxbsd/x11/SCsub +++ b/platform/linuxbsd/x11/SCsub @@ -25,7 +25,9 @@ if env["vulkan"]: if env["opengl3"]: env.Append(CPPDEFINES=["GLAD_GLX_NO_X11"]) - source_files.append(["gl_manager_x11.cpp", "detect_prime_x11.cpp", "#thirdparty/glad/glx.c"]) + source_files.append( + ["gl_manager_x11_egl.cpp", "gl_manager_x11.cpp", "detect_prime_x11.cpp", "#thirdparty/glad/glx.c"] + ) objects = [] diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 678ec340c0..31846c80a2 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -1495,6 +1495,9 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) { if (gl_manager) { gl_manager->window_destroy(p_id); } + if (gl_manager_egl) { + gl_manager_egl->window_destroy(p_id); + } #endif if (wd.xic) { @@ -1534,6 +1537,9 @@ int64_t DisplayServerX11::window_get_native_handle(HandleType p_handle_type, Win if (gl_manager) { return (int64_t)gl_manager->get_glx_context(p_window); } + if (gl_manager_egl) { + return (int64_t)gl_manager_egl->get_context(p_window); + } return 0; } #endif @@ -1711,6 +1717,9 @@ void DisplayServerX11::gl_window_make_current(DisplayServer::WindowID p_window_i if (gl_manager) { gl_manager->window_make_current(p_window_id); } + if (gl_manager_egl) { + gl_manager_egl->window_make_current(p_window_id); + } #endif } @@ -2012,6 +2021,9 @@ void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) { if (gl_manager) { gl_manager->window_resize(p_window, xwa.width, xwa.height); } + if (gl_manager_egl) { + gl_manager_egl->window_resize(p_window, xwa.width, xwa.height); + } #endif } @@ -3721,6 +3733,9 @@ void DisplayServerX11::_window_changed(XEvent *event) { if (gl_manager) { gl_manager->window_resize(window_id, wd.size.width, wd.size.height); } + if (gl_manager_egl) { + gl_manager_egl->window_resize(window_id, wd.size.width, wd.size.height); + } #endif if (!wd.rect_changed_callback.is_null()) { @@ -4855,6 +4870,9 @@ void DisplayServerX11::release_rendering_thread() { if (gl_manager) { gl_manager->release_current(); } + if (gl_manager_egl) { + gl_manager_egl->release_current(); + } #endif } @@ -4863,6 +4881,9 @@ void DisplayServerX11::make_rendering_thread() { if (gl_manager) { gl_manager->make_current(); } + if (gl_manager_egl) { + gl_manager_egl->make_current(); + } #endif } @@ -4871,6 +4892,9 @@ void DisplayServerX11::swap_buffers() { if (gl_manager) { gl_manager->swap_buffers(); } + if (gl_manager_egl) { + gl_manager_egl->swap_buffers(); + } #endif } @@ -5024,6 +5048,9 @@ void DisplayServerX11::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mo if (gl_manager) { gl_manager->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED); } + if (gl_manager_egl) { + gl_manager_egl->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED); + } #endif } @@ -5038,6 +5065,9 @@ DisplayServer::VSyncMode DisplayServerX11::window_get_vsync_mode(WindowID p_wind if (gl_manager) { return gl_manager->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED; } + if (gl_manager_egl) { + return gl_manager_egl->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED; + } #endif return DisplayServer::VSYNC_ENABLED; } @@ -5050,6 +5080,7 @@ Vector<String> DisplayServerX11::get_rendering_drivers_func() { #endif #ifdef GLES3_ENABLED drivers.push_back("opengl3"); + drivers.push_back("opengl3_es"); #endif return drivers; @@ -5092,6 +5123,21 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't acquire visual info from display."); vi_selected = true; } + if (gl_manager_egl) { + XVisualInfo visual_info_template; + int visual_id = gl_manager_egl->display_get_native_visual_id(x11_display); + ERR_FAIL_COND_V_MSG(visual_id < 0, INVALID_WINDOW_ID, "Unable to get a visual id."); + + visual_info_template.visualid = (VisualID)visual_id; + + int number_of_visuals = 0; + XVisualInfo *vi_list = XGetVisualInfo(x11_display, VisualIDMask, &visual_info_template, &number_of_visuals); + ERR_FAIL_COND_V(number_of_visuals <= 0, INVALID_WINDOW_ID); + + visualInfo = vi_list[0]; + + XFree(vi_list); + } #endif if (!vi_selected) { @@ -5364,8 +5410,12 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V if (gl_manager) { Error err = gl_manager->window_create(id, wd.x11_window, x11_display, win_rect.size.width, win_rect.size.height); ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create an OpenGL window"); - window_set_vsync_mode(p_vsync_mode, id); } + if (gl_manager_egl) { + Error err = gl_manager_egl->window_create(id, x11_display, &wd.x11_window, win_rect.size.width, win_rect.size.height); + ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Failed to create an OpenGLES window."); + } + window_set_vsync_mode(p_vsync_mode, id); #endif //set_class_hint(x11_display, wd.x11_window); @@ -5766,7 +5816,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode #endif // Initialize context and rendering device. #if defined(GLES3_ENABLED) - if (rendering_driver == "opengl3") { + if (rendering_driver == "opengl3" || rendering_driver == "opengl3_es") { if (getenv("DRI_PRIME") == nullptr) { int use_prime = -1; @@ -5807,7 +5857,8 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode setenv("DRI_PRIME", "1", 1); } } - + } + if (rendering_driver == "opengl3") { GLManager_X11::ContextType opengl_api_type = GLManager_X11::GLES_3_0_COMPATIBLE; gl_manager = memnew(GLManager_X11(p_resolution, opengl_api_type)); @@ -5820,14 +5871,20 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode } driver_found = true; - if (true) { - RasterizerGLES3::make_current(true); - } else { - memdelete(gl_manager); - gl_manager = nullptr; + RasterizerGLES3::make_current(true); + } + if (rendering_driver == "opengl3_es") { + gl_manager_egl = memnew(GLManagerEGL_X11); + + if (gl_manager_egl->initialize() != OK) { + memdelete(gl_manager_egl); + gl_manager_egl = nullptr; r_error = ERR_UNAVAILABLE; return; } + driver_found = true; + + RasterizerGLES3::make_current(false); } #endif if (!driver_found) { @@ -6044,6 +6101,9 @@ DisplayServerX11::~DisplayServerX11() { if (gl_manager) { gl_manager->window_destroy(E.key); } + if (gl_manager_egl) { + gl_manager_egl->window_destroy(E.key); + } #endif WindowData &wd = E.value; @@ -6094,6 +6154,10 @@ DisplayServerX11::~DisplayServerX11() { memdelete(gl_manager); gl_manager = nullptr; } + if (gl_manager_egl) { + memdelete(gl_manager_egl); + gl_manager_egl = nullptr; + } #endif if (xrandr_handle) { diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index 71beddce76..9706a4aa11 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -54,6 +54,7 @@ #if defined(GLES3_ENABLED) #include "x11/gl_manager_x11.h" +#include "x11/gl_manager_x11_egl.h" #endif #if defined(VULKAN_ENABLED) @@ -138,6 +139,7 @@ class DisplayServerX11 : public DisplayServer { #if defined(GLES3_ENABLED) GLManager_X11 *gl_manager = nullptr; + GLManagerEGL_X11 *gl_manager_egl = nullptr; #endif #if defined(VULKAN_ENABLED) VulkanContextX11 *context_vulkan = nullptr; diff --git a/platform/linuxbsd/x11/gl_manager_x11_egl.cpp b/platform/linuxbsd/x11/gl_manager_x11_egl.cpp new file mode 100644 index 0000000000..544684ebf1 --- /dev/null +++ b/platform/linuxbsd/x11/gl_manager_x11_egl.cpp @@ -0,0 +1,63 @@ +/**************************************************************************/ +/* gl_manager_x11_egl.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/**************************************************************************/ + +#include "gl_manager_x11_egl.h" + +#if defined(X11_ENABLED) && defined(GLES3_ENABLED) + +#include <stdio.h> +#include <stdlib.h> + +const char *GLManagerEGL_X11::_get_platform_extension_name() const { + return "EGL_KHR_platform_x11"; +} + +EGLenum GLManagerEGL_X11::_get_platform_extension_enum() const { + return EGL_PLATFORM_X11_KHR; +} + +Vector<EGLAttrib> GLManagerEGL_X11::_get_platform_display_attributes() const { + return Vector<EGLAttrib>(); +} + +EGLenum GLManagerEGL_X11::_get_platform_api_enum() const { + return EGL_OPENGL_ES_API; +} + +Vector<EGLint> GLManagerEGL_X11::_get_platform_context_attribs() const { + Vector<EGLint> ret; + ret.push_back(EGL_CONTEXT_CLIENT_VERSION); + ret.push_back(3); + ret.push_back(EGL_NONE); + + return ret; +} + +#endif // WINDOWS_ENABLED && GLES3_ENABLED diff --git a/platform/linuxbsd/x11/gl_manager_x11_egl.h b/platform/linuxbsd/x11/gl_manager_x11_egl.h new file mode 100644 index 0000000000..b9c96619c4 --- /dev/null +++ b/platform/linuxbsd/x11/gl_manager_x11_egl.h @@ -0,0 +1,61 @@ +/**************************************************************************/ +/* gl_manager_x11_egl.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/**************************************************************************/ + +#ifndef GL_MANAGER_X11_EGL_H +#define GL_MANAGER_X11_EGL_H + +#if defined(X11_ENABLED) && defined(GLES3_ENABLED) + +#include "core/error/error_list.h" +#include "core/os/os.h" +#include "core/templates/local_vector.h" +#include "drivers/egl/egl_manager.h" +#include "servers/display_server.h" + +#include <X11/Xlib.h> + +class GLManagerEGL_X11 : public EGLManager { +private: + virtual const char *_get_platform_extension_name() const override; + virtual EGLenum _get_platform_extension_enum() const override; + virtual EGLenum _get_platform_api_enum() const override; + virtual Vector<EGLAttrib> _get_platform_display_attributes() const override; + virtual Vector<EGLint> _get_platform_context_attribs() const override; + +public: + void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {} + + GLManagerEGL_X11(){}; + ~GLManagerEGL_X11(){}; +}; + +#endif // X11_ENABLED && GLES3_ENABLED + +#endif // GL_MANAGER_X11_EGL_H diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index 7dbe6a5970..2ca9e493b7 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -139,7 +139,14 @@ private: NSMenu *apple_menu = nullptr; NSMenu *dock_menu = nullptr; - HashMap<String, NSMenu *> submenu; + struct MenuData { + Callable open; + Callable close; + NSMenu *menu = nullptr; + bool is_open = false; + }; + HashMap<String, MenuData> submenu; + HashMap<NSMenu *, String> submenu_inv; struct WarpEvent { NSTimeInterval timestamp; @@ -197,6 +204,7 @@ private: const NSMenu *_get_menu_root(const String &p_menu_root) const; NSMenu *_get_menu_root(const String &p_menu_root); + bool _is_menu_opened(NSMenu *p_menu) const; WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect); void _update_window_style(WindowData p_wd); @@ -223,6 +231,8 @@ private: public: NSMenu *get_dock_menu() const; void menu_callback(id p_sender); + void menu_open(NSMenu *p_menu); + void menu_close(NSMenu *p_menu); bool has_window(WindowID p_window) const; WindowData &get_window(WindowID p_window); @@ -254,6 +264,8 @@ public: virtual bool has_feature(Feature p_feature) const override; virtual String get_name() const override; + virtual void global_menu_set_popup_callbacks(const String &p_menu_root, const Callable &p_open_callback = Callable(), const Callable &p_close_callback = Callable()) override; + virtual int global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index = -1) override; virtual int global_menu_add_item(const String &p_menu_root, 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; virtual int global_menu_add_check_item(const String &p_menu_root, 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; @@ -277,6 +289,7 @@ public: virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const override; virtual Key global_menu_get_item_accelerator(const String &p_menu_root, int p_idx) const override; virtual bool global_menu_is_item_disabled(const String &p_menu_root, int p_idx) const override; + virtual bool global_menu_is_item_hidden(const String &p_menu_root, int p_idx) const override; virtual String global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const override; virtual int global_menu_get_item_state(const String &p_menu_root, int p_idx) const override; virtual int global_menu_get_item_max_states(const String &p_menu_root, int p_idx) const override; @@ -288,11 +301,13 @@ public: virtual void global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable) override; virtual void global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback) override; virtual void global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback) override; + virtual void global_menu_set_item_hover_callbacks(const String &p_menu_root, int p_idx, const Callable &p_callback) override; virtual void global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) override; virtual void global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text) override; virtual void global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu) override; virtual void global_menu_set_item_accelerator(const String &p_menu_root, int p_idx, Key p_keycode) override; virtual void global_menu_set_item_disabled(const String &p_menu_root, int p_idx, bool p_disabled) override; + virtual void global_menu_set_item_hidden(const String &p_menu_root, int p_idx, bool p_hidden) override; virtual void global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip) override; virtual void global_menu_set_item_state(const String &p_menu_root, int p_idx, int p_state) override; virtual void global_menu_set_item_max_states(const String &p_menu_root, int p_idx, int p_max_states) override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index bcd8f5c4e5..67d6f4214f 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -75,7 +75,7 @@ const NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) cons } else { // Submenu. if (submenu.has(p_menu_root)) { - menu = submenu[p_menu_root]; + menu = submenu[p_menu_root].menu; } } if (menu == apple_menu) { @@ -99,9 +99,10 @@ NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) { NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]]; [n_menu setAutoenablesItems:NO]; [n_menu setDelegate:menu_delegate]; - submenu[p_menu_root] = n_menu; + submenu[p_menu_root].menu = n_menu; + submenu_inv[n_menu] = p_menu_root; } - menu = submenu[p_menu_root]; + menu = submenu[p_menu_root].menu; } if (menu == apple_menu) { // Do not allow to change Apple menu. @@ -609,6 +610,30 @@ NSMenu *DisplayServerMacOS::get_dock_menu() const { return dock_menu; } +void DisplayServerMacOS::menu_open(NSMenu *p_menu) { + if (submenu_inv.has(p_menu)) { + MenuData &md = submenu[submenu_inv[p_menu]]; + md.is_open = true; + if (md.open.is_valid()) { + Variant ret; + Callable::CallError ce; + md.open.callp(nullptr, 0, ret, ce); + } + } +} + +void DisplayServerMacOS::menu_close(NSMenu *p_menu) { + if (submenu_inv.has(p_menu)) { + MenuData &md = submenu[submenu_inv[p_menu]]; + md.is_open = false; + if (md.close.is_valid()) { + Variant ret; + Callable::CallError ce; + md.close.callp(nullptr, 0, ret, ce); + } + } +} + void DisplayServerMacOS::menu_callback(id p_sender) { if (![p_sender representedObject]) { return; @@ -816,6 +841,24 @@ bool DisplayServerMacOS::_has_help_menu() const { } } +bool DisplayServerMacOS::_is_menu_opened(NSMenu *p_menu) const { + if (submenu_inv.has(p_menu)) { + const MenuData &md = submenu[submenu_inv[p_menu]]; + if (md.is_open) { + return true; + } + } + for (NSInteger i = (p_menu == [NSApp mainMenu]) ? 1 : 0; i < [p_menu numberOfItems]; i++) { + const NSMenuItem *menu_item = [p_menu itemAtIndex:i]; + if ([menu_item submenu]) { + if (_is_menu_opened([menu_item submenu])) { + return true; + } + } + } + return false; +} + NSMenuItem *DisplayServerMacOS::_menu_add_item(const String &p_menu_root, const String &p_label, Key p_accel, int p_index, int *r_out) { NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { @@ -999,6 +1042,23 @@ int DisplayServerMacOS::global_menu_add_multistate_item(const String &p_menu_roo return out; } +void DisplayServerMacOS::global_menu_set_popup_callbacks(const String &p_menu_root, const Callable &p_open_callback, const Callable &p_close_callback) { + _THREAD_SAFE_METHOD_ + + if (p_menu_root != "" && p_menu_root.to_lower() != "_main" && p_menu_root.to_lower() != "_dock") { + // Submenu. + if (!submenu.has(p_menu_root)) { + NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]]; + [n_menu setAutoenablesItems:NO]; + [n_menu setDelegate:menu_delegate]; + submenu[p_menu_root].menu = n_menu; + submenu_inv[n_menu] = p_menu_root; + } + submenu[p_menu_root].open = p_open_callback; + submenu[p_menu_root].close = p_close_callback; + } +} + int DisplayServerMacOS::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index) { _THREAD_SAFE_METHOD_ @@ -1252,13 +1312,9 @@ String DisplayServerMacOS::global_menu_get_item_submenu(const String &p_menu_roo ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], String()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { - const NSMenu *sub_menu = [menu_item submenu]; - if (sub_menu) { - for (const KeyValue<String, NSMenu *> &E : submenu) { - if (E.value == sub_menu) { - return E.key; - } - } + NSMenu *sub_menu = [menu_item submenu]; + if (sub_menu && submenu_inv.has(sub_menu)) { + return submenu_inv[sub_menu]; } } } @@ -1319,6 +1375,24 @@ bool DisplayServerMacOS::global_menu_is_item_disabled(const String &p_menu_root, return false; } +bool DisplayServerMacOS::global_menu_is_item_hidden(const String &p_menu_root, int p_idx) const { + _THREAD_SAFE_METHOD_ + + const NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + ERR_FAIL_COND_V(p_idx < 0, false); + if (menu == [NSApp mainMenu]) { // Skip Apple menu. + p_idx++; + } + ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false); + const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + return [menu_item isHidden]; + } + } + return false; +} + String DisplayServerMacOS::global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const { _THREAD_SAFE_METHOD_ @@ -1498,6 +1572,25 @@ void DisplayServerMacOS::global_menu_set_item_callback(const String &p_menu_root } } +void DisplayServerMacOS::global_menu_set_item_hover_callbacks(const String &p_menu_root, int p_idx, const Callable &p_callback) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + ERR_FAIL_COND(p_idx < 0); + if (menu == [NSApp mainMenu]) { // Skip Apple menu. + p_idx++; + } + ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + ERR_FAIL_COND(!obj); + obj->hover_callback = p_callback; + } + } +} + void DisplayServerMacOS::global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback) { _THREAD_SAFE_METHOD_ @@ -1557,6 +1650,23 @@ void DisplayServerMacOS::global_menu_set_item_submenu(const String &p_menu_root, _THREAD_SAFE_METHOD_ NSMenu *menu = _get_menu_root(p_menu_root); + if (menu && p_submenu.is_empty()) { + ERR_FAIL_COND(p_idx < 0); + if (menu == [NSApp mainMenu]) { // Skip Apple menu. + p_idx++; + } + ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + if ([menu_item submenu] && _is_menu_opened([menu_item submenu])) { + ERR_PRINT("Can't remove open menu!"); + return; + } + [menu setSubmenu:nil forItem:menu_item]; + } + return; + } + NSMenu *sub_menu = _get_menu_root(p_submenu); if (menu && sub_menu) { if (sub_menu == menu) { @@ -1591,9 +1701,13 @@ void DisplayServerMacOS::global_menu_set_item_accelerator(const String &p_menu_r ERR_FAIL_COND(p_idx >= [menu numberOfItems]); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { - [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_keycode)]; - String keycode = KeyMappingMacOS::keycode_get_native_string(p_keycode & KeyModifierMask::CODE_MASK); - [menu_item setKeyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; + if (p_keycode == Key::NONE) { + [menu_item setKeyEquivalent:@""]; + } else { + [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_keycode)]; + String keycode = KeyMappingMacOS::keycode_get_native_string(p_keycode & KeyModifierMask::CODE_MASK); + [menu_item setKeyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; + } } } } @@ -1615,6 +1729,23 @@ void DisplayServerMacOS::global_menu_set_item_disabled(const String &p_menu_root } } +void DisplayServerMacOS::global_menu_set_item_hidden(const String &p_menu_root, int p_idx, bool p_hidden) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + ERR_FAIL_COND(p_idx < 0); + if (menu == [NSApp mainMenu]) { // Skip Apple menu. + p_idx++; + } + ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + [menu_item setHidden:p_hidden]; + } + } +} + void DisplayServerMacOS::global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip) { _THREAD_SAFE_METHOD_ @@ -1742,6 +1873,11 @@ void DisplayServerMacOS::global_menu_remove_item(const String &p_menu_root, int p_idx++; } ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if ([menu_item submenu] && _is_menu_opened([menu_item submenu])) { + ERR_PRINT("Can't remove open menu!"); + return; + } [menu removeItemAtIndex:p_idx]; } } @@ -1751,6 +1887,10 @@ void DisplayServerMacOS::global_menu_clear(const String &p_menu_root) { NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { + if (_is_menu_opened(menu)) { + ERR_PRINT("Can't remove open menu!"); + return; + } [menu removeAllItems]; // Restore Apple menu. if (menu == [NSApp mainMenu]) { @@ -1758,6 +1898,7 @@ void DisplayServerMacOS::global_menu_clear(const String &p_menu_root) { [menu setSubmenu:apple_menu forItem:menu_item]; } if (submenu.has(p_menu_root)) { + submenu_inv.erase(submenu[p_menu_root].menu); submenu.erase(p_menu_root); } } @@ -1871,179 +2012,275 @@ Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vect return OK; } -Error DisplayServerMacOS::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) { - _THREAD_SAFE_METHOD_ +@interface FileDialogDropdown : NSObject { + NSSavePanel *dialog; + NSMutableArray *allowed_types; + int cur_index; +} + +- (instancetype)initWithDialog:(NSSavePanel *)p_dialog fileTypes:(NSMutableArray *)p_allowed_types; +- (void)popupAction:(id)sender; +- (int)getIndex; + +@end + +@implementation FileDialogDropdown + +- (int)getIndex { + return cur_index; +} + +- (instancetype)initWithDialog:(NSSavePanel *)p_dialog fileTypes:(NSMutableArray *)p_allowed_types { + if ((self = [super init])) { + dialog = p_dialog; + allowed_types = p_allowed_types; + cur_index = 0; + } + return self; +} + +- (void)popupAction:(id)sender { + NSUInteger index = [sender indexOfSelectedItem]; + if (index < [allowed_types count]) { + [dialog setAllowedFileTypes:[allowed_types objectAtIndex:index]]; + cur_index = index; + } else { + [dialog setAllowedFileTypes:@[]]; + cur_index = -1; + } +} + +@end + +FileDialogDropdown *_make_accessory_view(NSSavePanel *p_panel, const Vector<String> &p_filters) { + NSView *group = [[NSView alloc] initWithFrame:NSZeroRect]; + group.translatesAutoresizingMaskIntoConstraints = NO; + + NSTextField *label = [NSTextField labelWithString:[NSString stringWithUTF8String:RTR("Format").utf8().get_data()]]; + label.translatesAutoresizingMaskIntoConstraints = NO; + if (@available(macOS 10.14, *)) { + label.textColor = NSColor.secondaryLabelColor; + } + if (@available(macOS 11.10, *)) { + label.font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; + } + [group addSubview:label]; + + NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO]; + popup.translatesAutoresizingMaskIntoConstraints = NO; - NSString *url = [NSString stringWithUTF8String:p_current_directory.utf8().get_data()]; NSMutableArray *allowed_types = [[NSMutableArray alloc] init]; bool allow_other = false; for (int i = 0; i < p_filters.size(); i++) { Vector<String> tokens = p_filters[i].split(";"); - if (tokens.size() > 0) { - if (tokens[0].strip_edges() == "*.*") { - allow_other = true; - } else { - [allowed_types addObject:[NSString stringWithUTF8String:tokens[0].replace("*.", "").strip_edges().utf8().get_data()]]; + if (tokens.size() >= 1) { + String flt = tokens[0].strip_edges(); + int filter_slice_count = flt.get_slice_count(","); + + NSMutableArray *type_filters = [[NSMutableArray alloc] init]; + for (int j = 0; j < filter_slice_count; j++) { + String str = (flt.get_slice(",", j).strip_edges()); + if (str.strip_edges() == "*.*" || str.strip_edges() == "*") { + allow_other = true; + } else if (!str.is_empty()) { + [type_filters addObject:[NSString stringWithUTF8String:str.replace("*.", "").strip_edges().utf8().get_data()]]; + } + } + + if ([type_filters count] > 0) { + NSString *name_str = [NSString stringWithUTF8String:((tokens.size() == 1) ? tokens[0] : vformat("%s (%s)", tokens[1], tokens[0])).strip_edges().utf8().get_data()]; + [allowed_types addObject:type_filters]; + [popup addItemWithTitle:name_str]; } } } + FileDialogDropdown *handler = [[FileDialogDropdown alloc] initWithDialog:p_panel fileTypes:allowed_types]; + popup.target = handler; + popup.action = @selector(popupAction:); - WindowID prev_focus = last_focused_window; + [group addSubview:popup]; - Callable callback = p_callback; // Make a copy for async completion handler. - switch (p_mode) { - case FILE_DIALOG_MODE_SAVE_FILE: { - NSSavePanel *panel = [NSSavePanel savePanel]; + NSView *view = [[NSView alloc] initWithFrame:NSZeroRect]; + view.translatesAutoresizingMaskIntoConstraints = NO; + [view addSubview:group]; - [panel setDirectoryURL:[NSURL fileURLWithPath:url]]; - if ([allowed_types count]) { - [panel setAllowedFileTypes:allowed_types]; - } - [panel setAllowsOtherFileTypes:allow_other]; - [panel setExtensionHidden:YES]; - [panel setCanSelectHiddenExtension:YES]; - [panel setCanCreateDirectories:YES]; - [panel setShowsHiddenFiles:p_show_hidden]; - if (p_filename != "") { - NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()]; - [panel setNameFieldStringValue:fileurl]; - } + NSMutableArray *constraints = [NSMutableArray array]; + [constraints addObject:[popup.topAnchor constraintEqualToAnchor:group.topAnchor constant:10]]; + [constraints addObject:[label.leadingAnchor constraintEqualToAnchor:group.leadingAnchor constant:10]]; + [constraints addObject:[popup.leadingAnchor constraintEqualToAnchor:label.trailingAnchor constant:10]]; + [constraints addObject:[popup.firstBaselineAnchor constraintEqualToAnchor:label.firstBaselineAnchor]]; + [constraints addObject:[group.trailingAnchor constraintEqualToAnchor:popup.trailingAnchor constant:10]]; + [constraints addObject:[group.bottomAnchor constraintEqualToAnchor:popup.bottomAnchor constant:10]]; + [constraints addObject:[group.topAnchor constraintEqualToAnchor:view.topAnchor]]; + [constraints addObject:[group.centerXAnchor constraintEqualToAnchor:view.centerXAnchor]]; + [constraints addObject:[view.bottomAnchor constraintEqualToAnchor:group.bottomAnchor]]; + [NSLayoutConstraint activateConstraints:constraints]; + + [p_panel setAllowsOtherFileTypes:allow_other]; + if ([allowed_types count] > 0) { + [p_panel setAccessoryView:view]; + [p_panel setAllowedFileTypes:[allowed_types objectAtIndex:0]]; + } + + return handler; +} + +Error DisplayServerMacOS::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED); + + NSString *url = [NSString stringWithUTF8String:p_current_directory.utf8().get_data()]; + FileDialogDropdown *handler = nullptr; - [panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow] - completionHandler:^(NSInteger ret) { - if (ret == NSModalResponseOK) { - // Save bookmark for folder. - if (OS::get_singleton()->is_sandboxed()) { - NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"]; + WindowID prev_focus = last_focused_window; + + Callable callback = p_callback; // Make a copy for async completion handler. + if (p_mode == FILE_DIALOG_MODE_SAVE_FILE) { + NSSavePanel *panel = [NSSavePanel savePanel]; + + [panel setDirectoryURL:[NSURL fileURLWithPath:url]]; + handler = _make_accessory_view(panel, p_filters); + [panel setExtensionHidden:YES]; + [panel setCanSelectHiddenExtension:YES]; + [panel setCanCreateDirectories:YES]; + [panel setShowsHiddenFiles:p_show_hidden]; + if (p_filename != "") { + NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()]; + [panel setNameFieldStringValue:fileurl]; + } + + [panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow] + completionHandler:^(NSInteger ret) { + if (ret == NSModalResponseOK) { + // Save bookmark for folder. + if (OS::get_singleton()->is_sandboxed()) { + NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"]; + bool skip = false; + for (id bookmark in bookmarks) { + NSError *error = nil; + BOOL isStale = NO; + NSURL *exurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]; + if (!error && !isStale && ([[exurl path] compare:[[panel directoryURL] path]] == NSOrderedSame)) { + skip = true; + break; + } + } + if (!skip) { + NSError *error = nil; + NSData *bookmark = [[panel directoryURL] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; + if (!error) { + NSArray *new_bookmarks = [bookmarks arrayByAddingObject:bookmark]; + [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"]; + } + } + } + // Callback. + Vector<String> files; + String url; + url.parse_utf8([[[panel URL] path] UTF8String]); + files.push_back(url); + if (!callback.is_null()) { + Variant v_status = true; + Variant v_files = files; + Variant v_index = [handler getIndex]; + Variant *v_args[3] = { &v_status, &v_files, &v_index }; + Variant ret; + Callable::CallError ce; + callback.callp((const Variant **)&v_args, 3, ret, ce); + } + } else { + if (!callback.is_null()) { + Variant v_status = false; + Variant v_files = Vector<String>(); + Variant v_index = [handler getIndex]; + Variant *v_args[3] = { &v_status, &v_files, &v_index }; + Variant ret; + Callable::CallError ce; + callback.callp((const Variant **)&v_args, 3, ret, ce); + } + } + if (prev_focus != INVALID_WINDOW_ID) { + callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus); + } + }]; + } else { + NSOpenPanel *panel = [NSOpenPanel openPanel]; + + [panel setDirectoryURL:[NSURL fileURLWithPath:url]]; + handler = _make_accessory_view(panel, p_filters); + [panel setExtensionHidden:YES]; + [panel setCanSelectHiddenExtension:YES]; + [panel setCanCreateDirectories:YES]; + [panel setCanChooseFiles:(p_mode != FILE_DIALOG_MODE_OPEN_DIR)]; + [panel setCanChooseDirectories:(p_mode == FILE_DIALOG_MODE_OPEN_DIR || p_mode == FILE_DIALOG_MODE_OPEN_ANY)]; + [panel setShowsHiddenFiles:p_show_hidden]; + if (p_filename != "") { + NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()]; + [panel setNameFieldStringValue:fileurl]; + } + [panel setAllowsMultipleSelection:(p_mode == FILE_DIALOG_MODE_OPEN_FILES)]; + + [panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow] + completionHandler:^(NSInteger ret) { + if (ret == NSModalResponseOK) { + // Save bookmark for folder. + NSArray *urls = [(NSOpenPanel *)panel URLs]; + if (OS::get_singleton()->is_sandboxed()) { + NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"]; + NSMutableArray *new_bookmarks = [bookmarks mutableCopy]; + for (NSUInteger i = 0; i != [urls count]; ++i) { bool skip = false; for (id bookmark in bookmarks) { NSError *error = nil; BOOL isStale = NO; NSURL *exurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]; - if (!error && !isStale && ([[exurl path] compare:[[panel directoryURL] path]] == NSOrderedSame)) { + if (!error && !isStale && ([[exurl path] compare:[[urls objectAtIndex:i] path]] == NSOrderedSame)) { skip = true; break; } } if (!skip) { NSError *error = nil; - NSData *bookmark = [[panel directoryURL] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; + NSData *bookmark = [[urls objectAtIndex:i] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; if (!error) { - NSArray *new_bookmarks = [bookmarks arrayByAddingObject:bookmark]; - [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"]; + [new_bookmarks addObject:bookmark]; } } } - // Callback. - Vector<String> files; + [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"]; + } + // Callback. + Vector<String> files; + for (NSUInteger i = 0; i != [urls count]; ++i) { String url; - url.parse_utf8([[[panel URL] path] UTF8String]); + url.parse_utf8([[[urls objectAtIndex:i] path] UTF8String]); files.push_back(url); - if (!callback.is_null()) { - Variant v_status = true; - Variant v_files = files; - Variant *v_args[2] = { &v_status, &v_files }; - Variant ret; - Callable::CallError ce; - callback.callp((const Variant **)&v_args, 2, ret, ce); - } - } else { - if (!callback.is_null()) { - Variant v_status = false; - Variant v_files = Vector<String>(); - Variant *v_args[2] = { &v_status, &v_files }; - Variant ret; - Callable::CallError ce; - callback.callp((const Variant **)&v_args, 2, ret, ce); - } } - if (prev_focus != INVALID_WINDOW_ID) { - callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus); + if (!callback.is_null()) { + Variant v_status = true; + Variant v_files = files; + Variant v_index = [handler getIndex]; + Variant *v_args[3] = { &v_status, &v_files, &v_index }; + Variant ret; + Callable::CallError ce; + callback.callp((const Variant **)&v_args, 3, ret, ce); } - }]; - } break; - case FILE_DIALOG_MODE_OPEN_ANY: - case FILE_DIALOG_MODE_OPEN_FILE: - case FILE_DIALOG_MODE_OPEN_FILES: - case FILE_DIALOG_MODE_OPEN_DIR: { - NSOpenPanel *panel = [NSOpenPanel openPanel]; - - [panel setDirectoryURL:[NSURL fileURLWithPath:url]]; - if ([allowed_types count]) { - [panel setAllowedFileTypes:allowed_types]; - } - [panel setAllowsOtherFileTypes:allow_other]; - [panel setExtensionHidden:YES]; - [panel setCanSelectHiddenExtension:YES]; - [panel setCanCreateDirectories:YES]; - [panel setCanChooseFiles:(p_mode != FILE_DIALOG_MODE_OPEN_DIR)]; - [panel setCanChooseDirectories:(p_mode == FILE_DIALOG_MODE_OPEN_DIR || p_mode == FILE_DIALOG_MODE_OPEN_ANY)]; - [panel setShowsHiddenFiles:p_show_hidden]; - if (p_filename != "") { - NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()]; - [panel setNameFieldStringValue:fileurl]; - } - [panel setAllowsMultipleSelection:(p_mode == FILE_DIALOG_MODE_OPEN_FILES)]; - - [panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow] - completionHandler:^(NSInteger ret) { - if (ret == NSModalResponseOK) { - // Save bookmark for folder. - NSArray *urls = [(NSOpenPanel *)panel URLs]; - if (OS::get_singleton()->is_sandboxed()) { - NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"]; - NSMutableArray *new_bookmarks = [bookmarks mutableCopy]; - for (NSUInteger i = 0; i != [urls count]; ++i) { - bool skip = false; - for (id bookmark in bookmarks) { - NSError *error = nil; - BOOL isStale = NO; - NSURL *exurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]; - if (!error && !isStale && ([[exurl path] compare:[[urls objectAtIndex:i] path]] == NSOrderedSame)) { - skip = true; - break; - } - } - if (!skip) { - NSError *error = nil; - NSData *bookmark = [[urls objectAtIndex:i] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; - if (!error) { - [new_bookmarks addObject:bookmark]; - } - } - } - [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"]; - } - // Callback. - Vector<String> files; - for (NSUInteger i = 0; i != [urls count]; ++i) { - String url; - url.parse_utf8([[[urls objectAtIndex:i] path] UTF8String]); - files.push_back(url); - } - if (!callback.is_null()) { - Variant v_status = true; - Variant v_files = files; - Variant *v_args[2] = { &v_status, &v_files }; - Variant ret; - Callable::CallError ce; - callback.callp((const Variant **)&v_args, 2, ret, ce); - } - } else { - if (!callback.is_null()) { - Variant v_status = false; - Variant v_files = Vector<String>(); - Variant *v_args[2] = { &v_status, &v_files }; - Variant ret; - Callable::CallError ce; - callback.callp((const Variant **)&v_args, 2, ret, ce); - } - } - if (prev_focus != INVALID_WINDOW_ID) { - callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus); + } else { + if (!callback.is_null()) { + Variant v_status = false; + Variant v_files = Vector<String>(); + Variant v_index = [handler getIndex]; + Variant *v_args[3] = { &v_status, &v_files, &v_index }; + Variant ret; + Callable::CallError ce; + callback.callp((const Variant **)&v_args, 3, ret, ce); } - }]; - } break; + } + if (prev_focus != INVALID_WINDOW_ID) { + callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus); + } + }]; } return OK; @@ -4188,15 +4425,19 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM nsappname = [[NSProcessInfo processInfo] processName]; } + menu_delegate = [[GodotMenuDelegate alloc] init]; + // Setup Dock menu. dock_menu = [[NSMenu alloc] initWithTitle:@"_dock"]; [dock_menu setAutoenablesItems:NO]; + [dock_menu setDelegate:menu_delegate]; // Setup Apple menu. apple_menu = [[NSMenu alloc] initWithTitle:@""]; title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname]; [apple_menu addItemWithTitle:title action:@selector(showAbout:) keyEquivalent:@""]; [apple_menu setAutoenablesItems:NO]; + [apple_menu setDelegate:menu_delegate]; [apple_menu addItem:[NSMenuItem separatorItem]]; @@ -4226,8 +4467,6 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM [main_menu setSubmenu:apple_menu forItem:menu_item]; [main_menu setAutoenablesItems:NO]; - menu_delegate = [[GodotMenuDelegate alloc] init]; - //!!!!!!!!!!!!!!!!!!!!!!!!!! //TODO - do Vulkan and OpenGL support checks, driver selection and fallback rendering_driver = p_rendering_driver; diff --git a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml index 99187fe60e..85fefe65c0 100644 --- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml +++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml @@ -66,7 +66,7 @@ Array of the additional command line arguments passed to the code signing tool. </member> <member name="codesign/entitlements/address_book" type="bool" setter="" getter=""> - Enable to allow access to contacts in the user's address book, if it's enabled you should also provide usage message in the [code]privacy/address_book_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_addressbook]com.apple.security.personal-information.addressbook[/url]. + Enable to allow access to contacts in the user's address book, if it's enabled you should also provide usage message in the [member privacy/address_book_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_addressbook]com.apple.security.personal-information.addressbook[/url]. </member> <member name="codesign/entitlements/allow_dyld_environment_variables" type="bool" setter="" getter=""> Allows app to use dynamic linker environment variables to inject code. If you are using add-ons with dynamic or self-modifying native code, enable them according to the add-on documentation. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_allow-dyld-environment-variables]com.apple.security.cs.allow-dyld-environment-variables[/url]. @@ -115,13 +115,13 @@ Enable to allow app to send Apple events to other apps. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_automation_apple-events]com.apple.security.automation.apple-events[/url]. </member> <member name="codesign/entitlements/audio_input" type="bool" setter="" getter=""> - Enable if you need to use the microphone or other audio input sources, if it's enabled you should also provide usage message in the [code]privacy/microphone_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_audio-input]com.apple.security.device.audio-input[/url]. + Enable if you need to use the microphone or other audio input sources, if it's enabled you should also provide usage message in the [member privacy/microphone_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_audio-input]com.apple.security.device.audio-input[/url]. </member> <member name="codesign/entitlements/calendars" type="bool" setter="" getter=""> - Enable to allow access to the user's calendar, if it's enabled you should also provide usage message in the [code]privacy/calendar_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_calendars]com.apple.security.personal-information.calendars[/url]. + Enable to allow access to the user's calendar, if it's enabled you should also provide usage message in the [member privacy/calendar_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_calendars]com.apple.security.personal-information.calendars[/url]. </member> <member name="codesign/entitlements/camera" type="bool" setter="" getter=""> - Enable if you need to use the camera, if it's enabled you should also provide usage message in the [code]privacy/camera_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_camera]com.apple.security.device.camera[/url]. + Enable if you need to use the camera, if it's enabled you should also provide usage message in the [member privacy/camera_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_camera]com.apple.security.device.camera[/url]. </member> <member name="codesign/entitlements/custom_file" type="String" setter="" getter=""> Custom entitlements [code].plist[/code] file, if specified the rest of entitlements in the export config are ignored. @@ -133,10 +133,10 @@ Allows app to load arbitrary libraries and frameworks (not signed with the same Team ID as the main executable or by Apple). Enable it if you are using GDExtension add-ons or ad-hoc signing, or want to support user-provided external add-ons. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_disable-library-validation]com.apple.security.cs.disable-library-validation[/url]. </member> <member name="codesign/entitlements/location" type="bool" setter="" getter=""> - Enable if you need to use location information from Location Services, if it's enabled you should also provide usage message in the [code]privacy/location_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_location]com.apple.security.personal-information.location[/url]. + Enable if you need to use location information from Location Services, if it's enabled you should also provide usage message in the [member privacy/location_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_location]com.apple.security.personal-information.location[/url]. </member> <member name="codesign/entitlements/photos_library" type="bool" setter="" getter=""> - Enable to allow access to the user's Photos library, if it's enabled you should also provide usage message in the [code]privacy/photos_library_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_photos-library]com.apple.security.personal-information.photos-library[/url]. + Enable to allow access to the user's Photos library, if it's enabled you should also provide usage message in the [member privacy/photos_library_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_photos-library]com.apple.security.personal-information.photos-library[/url]. </member> <member name="codesign/identity" type="String" setter="" getter=""> The "Full Name", "Common Name" or SHA-1 hash of the signing identity used to sign [code].app[/code] bundle. diff --git a/platform/macos/godot_menu_delegate.mm b/platform/macos/godot_menu_delegate.mm index ebfe8b1f6d..1bfcfa7d9e 100644 --- a/platform/macos/godot_menu_delegate.mm +++ b/platform/macos/godot_menu_delegate.mm @@ -39,6 +39,34 @@ - (void)doNothing:(id)sender { } +- (void)menuNeedsUpdate:(NSMenu *)menu { + if (DisplayServer::get_singleton()) { + DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); + ds->menu_open(menu); + } +} + +- (void)menuDidClose:(NSMenu *)menu { + if (DisplayServer::get_singleton()) { + DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); + ds->menu_close(menu); + } +} + +- (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item { + if (item) { + GodotMenuItem *value = [item representedObject]; + if (value && value->hover_callback != Callable()) { + // If custom callback is set, use it. + Variant tag = value->meta; + Variant *tagp = &tag; + Variant ret; + Callable::CallError ce; + value->hover_callback.callp((const Variant **)&tagp, 1, ret, ce); + } + } +} + - (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action { NSString *ev_key = [[event charactersIgnoringModifiers] lowercaseString]; NSUInteger ev_modifiers = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; diff --git a/platform/macos/godot_menu_item.h b/platform/macos/godot_menu_item.h index 8876ceae6a..b816ea1b3a 100644 --- a/platform/macos/godot_menu_item.h +++ b/platform/macos/godot_menu_item.h @@ -46,6 +46,7 @@ enum GlobalMenuCheckType { @public Callable callback; Callable key_callback; + Callable hover_callback; Variant meta; GlobalMenuCheckType checkable_type; int max_states; diff --git a/platform/web/js/libs/library_godot_javascript_singleton.js b/platform/web/js/libs/library_godot_javascript_singleton.js index cbe59230ee..1764c9a026 100644 --- a/platform/web/js/libs/library_godot_javascript_singleton.js +++ b/platform/web/js/libs/library_godot_javascript_singleton.js @@ -210,7 +210,7 @@ const GodotJSWrapper = { // This is safe! JavaScript is single threaded (and using it in threads is not a good idea anyway). GodotJSWrapper.cb_ret = null; const args = Array.from(arguments); - const argsProxy = GodotJSWrapper.MyProxy(args); + const argsProxy = new GodotJSWrapper.MyProxy(args); func(p_ref, argsProxy.get_id(), args.length); argsProxy.unref(); const ret = GodotJSWrapper.cb_ret; diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index ed52c5eb92..ded80ba5f1 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -222,18 +222,37 @@ void DisplayServerWindows::tts_stop() { Error DisplayServerWindows::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) { _THREAD_SAFE_METHOD_ + ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED); + Vector<Char16String> filter_names; Vector<Char16String> filter_exts; for (const String &E : p_filters) { Vector<String> tokens = E.split(";"); - if (tokens.size() == 2) { - filter_exts.push_back(tokens[0].strip_edges().utf16()); - filter_names.push_back(tokens[1].strip_edges().utf16()); - } else if (tokens.size() == 1) { - filter_exts.push_back(tokens[0].strip_edges().utf16()); - filter_names.push_back(tokens[0].strip_edges().utf16()); + if (tokens.size() >= 1) { + String flt = tokens[0].strip_edges(); + int filter_slice_count = flt.get_slice_count(","); + Vector<String> exts; + for (int j = 0; j < filter_slice_count; j++) { + String str = (flt.get_slice(",", j).strip_edges()); + if (!str.is_empty()) { + exts.push_back(str); + } + } + if (!exts.is_empty()) { + String str = String(";").join(exts); + filter_exts.push_back(str.utf16()); + if (tokens.size() == 2) { + filter_names.push_back(tokens[1].strip_edges().utf16()); + } else { + filter_names.push_back(str.utf16()); + } + } } } + if (filter_names.is_empty()) { + filter_exts.push_back(String("*.*").utf16()); + filter_names.push_back(RTR("All Files").utf16()); + } Vector<COMDLG_FILTERSPEC> filters; for (int i = 0; i < filter_names.size(); i++) { @@ -287,6 +306,9 @@ Error DisplayServerWindows::file_dialog_show(const String &p_title, const String } hr = pfd->Show(windows[window_id].hWnd); + UINT index = 0; + pfd->GetFileTypeIndex(&index); + if (SUCCEEDED(hr)) { Vector<String> file_names; @@ -326,19 +348,21 @@ Error DisplayServerWindows::file_dialog_show(const String &p_title, const String if (!p_callback.is_null()) { Variant v_status = true; Variant v_files = file_names; - Variant *v_args[2] = { &v_status, &v_files }; + Variant v_index = index; + Variant *v_args[3] = { &v_status, &v_files, &v_index }; Variant ret; Callable::CallError ce; - p_callback.callp((const Variant **)&v_args, 2, ret, ce); + p_callback.callp((const Variant **)&v_args, 3, ret, ce); } } else { if (!p_callback.is_null()) { Variant v_status = false; Variant v_files = Vector<String>(); - Variant *v_args[2] = { &v_status, &v_files }; + Variant v_index = index; + Variant *v_args[3] = { &v_status, &v_files, &v_index }; Variant ret; Callable::CallError ce; - p_callback.callp((const Variant **)&v_args, 2, ret, ce); + p_callback.callp((const Variant **)&v_args, 3, ret, ce); } } pfd->Release(); |