diff options
Diffstat (limited to 'platform/osx')
-rw-r--r-- | platform/osx/display_server_osx.h | 16 | ||||
-rw-r--r-- | platform/osx/display_server_osx.mm | 170 | ||||
-rw-r--r-- | platform/osx/export/export_plugin.cpp | 5 | ||||
-rw-r--r-- | platform/osx/godot_application_delegate.mm | 4 | ||||
-rw-r--r-- | platform/osx/godot_content_view.mm | 2 | ||||
-rw-r--r-- | platform/osx/godot_window.mm | 4 | ||||
-rw-r--r-- | platform/osx/godot_window_delegate.mm | 16 | ||||
-rw-r--r-- | platform/osx/os_osx.mm | 3 |
8 files changed, 204 insertions, 16 deletions
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h index 2b57983ca7..cc9ac162ea 100644 --- a/platform/osx/display_server_osx.h +++ b/platform/osx/display_server_osx.h @@ -104,8 +104,14 @@ public: bool borderless = false; bool resize_disabled = false; bool no_focus = false; + bool is_popup = false; + + Rect2i parent_safe_rect; }; + List<WindowID> popup_list; + uint64_t time_since_popup = 0; + private: #if defined(GLES3_ENABLED) GLManager_OSX *gl_manager = nullptr; @@ -154,6 +160,7 @@ private: float display_max_scale = 1.f; Point2i origin; bool displays_arrangement_dirty = true; + bool is_resizing = false; CursorShape cursor_shape = CURSOR_ARROW; NSCursor *cursors[CURSOR_MAX]; @@ -197,6 +204,11 @@ public: void push_to_key_event_buffer(const KeyEvent &p_event); void update_im_text(const Point2i &p_selection, const String &p_text); void set_last_focused_window(WindowID p_window); + void mouse_process_popups(bool p_close = false); + void popup_open(WindowID p_window); + void popup_close(WindowID p_window); + void set_is_resizing(bool p_is_resizing); + bool get_is_resizing() const; void window_update(WindowID p_window); void window_destroy(WindowID p_window); @@ -259,6 +271,10 @@ public: virtual void show_window(WindowID p_id) override; virtual void delete_sub_window(WindowID p_id) override; + virtual WindowID window_get_active_popup() const override; + virtual void window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) override; + virtual Rect2i window_get_popup_safe_rect(WindowID p_window) const override; + virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override; virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override; virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override; diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm index b2201eabbc..a4cd8f58bd 100644 --- a/platform/osx/display_server_osx.mm +++ b/platform/osx/display_server_osx.mm @@ -324,27 +324,39 @@ void DisplayServerOSX::_dispatch_input_event(const Ref<InputEvent> &p_event) { Variant ret; Callable::CallError ce; + { + List<WindowID>::Element *E = popup_list.front(); + if (E && Object::cast_to<InputEventKey>(*p_event)) { + // Redirect keyboard input to active popup. + if (windows.has(E->get())) { + Callable callable = windows[E->get()].input_event_callback; + if (callable.is_valid()) { + callable.call((const Variant **)&evp, 1, ret, ce); + } + } + in_dispatch_input_event = false; + return; + } + } + Ref<InputEventFromWindow> event_from_window = p_event; if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) { // Send to a window. if (windows.has(event_from_window->get_window_id())) { Callable callable = windows[event_from_window->get_window_id()].input_event_callback; - if (callable.is_null()) { - return; + if (callable.is_valid()) { + callable.call((const Variant **)&evp, 1, ret, ce); } - callable.call((const Variant **)&evp, 1, ret, ce); } } else { // Send to all windows. - for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { - Callable callable = E->get().input_event_callback; - if (callable.is_null()) { - continue; + for (KeyValue<WindowID, WindowData> &E : windows) { + Callable callable = E.value.input_event_callback; + if (callable.is_valid()) { + callable.call((const Variant **)&evp, 1, ret, ce); } - callable.call((const Variant **)&evp, 1, ret, ce); } } - in_dispatch_input_event = false; } } @@ -513,6 +525,9 @@ DisplayServerOSX::WindowData &DisplayServerOSX::get_window(WindowID p_window) { } void DisplayServerOSX::send_event(NSEvent *p_event) { + if ([p_event type] == NSEventTypeLeftMouseDown || [p_event type] == NSEventTypeRightMouseDown || [p_event type] == NSEventTypeOtherMouseDown) { + mouse_process_popups(); + } // Special case handling of command-period, which is traditionally a special // shortcut in macOS and doesn't arrive at our regular keyDown handler. if ([p_event type] == NSEventTypeKeyDown) { @@ -584,6 +599,14 @@ void DisplayServerOSX::set_last_focused_window(WindowID p_window) { last_focused_window = p_window; } +void DisplayServerOSX::set_is_resizing(bool p_is_resizing) { + is_resizing = p_is_resizing; +} + +bool DisplayServerOSX::get_is_resizing() const { + return is_resizing; +} + void DisplayServerOSX::window_update(WindowID p_window) { #if defined(GLES3_ENABLED) if (gl_manager) { @@ -1366,7 +1389,8 @@ DisplayServer::WindowID DisplayServerOSX::create_sub_window(WindowMode p_mode, V void DisplayServerOSX::show_window(WindowID p_id) { WindowData &wd = windows[p_id]; - if (wd.no_focus) { + popup_open(p_id); + if (wd.no_focus || wd.is_popup) { [wd.window_object orderFront:nil]; } else { [wd.window_object makeKeyAndOrderFront:nil]; @@ -1809,7 +1833,7 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo } _update_window_style(wd); if ([wd.window_object isVisible]) { - if (wd.no_focus) { + if (wd.no_focus || wd.is_popup) { [wd.window_object orderFront:nil]; } else { [wd.window_object makeKeyAndOrderFront:nil]; @@ -1838,6 +1862,11 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo case WINDOW_FLAG_NO_FOCUS: { wd.no_focus = p_enabled; } break; + case WINDOW_FLAG_POPUP: { + ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup."); + ERR_FAIL_COND_MSG([wd.window_object isVisible] && (wd.is_popup != p_enabled), "Pupup flag can't changed while window is opened."); + wd.is_popup = p_enabled; + } break; default: { } } @@ -1869,6 +1898,9 @@ bool DisplayServerOSX::window_get_flag(WindowFlags p_flag, WindowID p_window) co case WINDOW_FLAG_NO_FOCUS: { return wd.no_focus; } break; + case WINDOW_FLAG_POPUP: { + return wd.is_popup; + } break; default: { } } @@ -1888,7 +1920,7 @@ void DisplayServerOSX::window_move_to_foreground(WindowID p_window) { const WindowData &wd = windows[p_window]; [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; - if (wd.no_focus) { + if (wd.no_focus || wd.is_popup) { [wd.window_object orderFront:nil]; } else { [wd.window_object makeKeyAndOrderFront:nil]; @@ -2446,6 +2478,120 @@ void DisplayServerOSX::register_osx_driver() { register_create_function("osx", create_func, get_rendering_drivers_func); } +DisplayServer::WindowID DisplayServerOSX::window_get_active_popup() const { + const List<WindowID>::Element *E = popup_list.back(); + if (E) { + return E->get(); + } else { + return INVALID_WINDOW_ID; + } +} + +void DisplayServerOSX::window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + wd.parent_safe_rect = p_rect; +} + +Rect2i DisplayServerOSX::window_get_popup_safe_rect(WindowID p_window) const { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!windows.has(p_window), Rect2i()); + const WindowData &wd = windows[p_window]; + return wd.parent_safe_rect; +} + +void DisplayServerOSX::popup_open(WindowID p_window) { + WindowData &wd = windows[p_window]; + if (wd.is_popup) { + bool was_empty = popup_list.is_empty(); + // Close all popups, up to current popup parent, or every popup if new window is not transient. + List<WindowID>::Element *E = popup_list.back(); + while (E) { + if (wd.transient_parent != E->get() || wd.transient_parent == INVALID_WINDOW_ID) { + send_window_event(windows[E->get()], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST); + List<WindowID>::Element *F = E->prev(); + popup_list.erase(E); + E = F; + } else { + break; + } + } + + if (was_empty && popup_list.is_empty()) { + // Inform OS that popup was opened, to close other native popups. + [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification" object:@"org.godotengine.godot.popup_window"]; + } + time_since_popup = OS::get_singleton()->get_ticks_msec(); + popup_list.push_back(p_window); + } +} + +void DisplayServerOSX::popup_close(WindowID p_window) { + bool was_empty = popup_list.is_empty(); + List<WindowID>::Element *E = popup_list.find(p_window); + while (E) { + send_window_event(windows[E->get()], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST); + List<WindowID>::Element *F = E->next(); + popup_list.erase(E); + E = F; + } + if (!was_empty && popup_list.is_empty()) { + // Inform OS that all popups are closed. + [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification" object:@"org.godotengine.godot.popup_window"]; + } +} + +void DisplayServerOSX::mouse_process_popups(bool p_close) { + _THREAD_SAFE_METHOD_ + + bool was_empty = popup_list.is_empty(); + if (p_close) { + // Close all popups. + List<WindowID>::Element *E = popup_list.front(); + while (E) { + send_window_event(windows[E->get()], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST); + List<WindowID>::Element *F = E->next(); + popup_list.erase(E); + E = F; + } + if (!was_empty) { + // Inform OS that all popups are closed. + [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification" object:@"org.godotengine.godot.popup_window"]; + } + } else { + uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup; + if (delta < 250) { + return; + } + + Point2i pos = mouse_get_position(); + List<WindowID>::Element *E = popup_list.back(); + while (E) { + // Popup window area. + Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get())); + // Area of the parent window, which responsible for opening sub-menu. + Rect2i safe_rect = window_get_popup_safe_rect(E->get()); + if (win_rect.has_point(pos)) { + break; + } else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) { + break; + } else { + send_window_event(windows[E->get()], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST); + List<WindowID>::Element *F = E->prev(); + popup_list.erase(E); + E = F; + } + } + if (!was_empty && popup_list.is_empty()) { + // Inform OS that all popups are closed. + [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification" object:@"org.godotengine.godot.popup_window"]; + } + } +} + DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); diff --git a/platform/osx/export/export_plugin.cpp b/platform/osx/export/export_plugin.cpp index 24b9bc02a2..4d0fc9add6 100644 --- a/platform/osx/export/export_plugin.cpp +++ b/platform/osx/export/export_plugin.cpp @@ -787,6 +787,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p String fname = tmp_app_path_name + "/Contents/Resources/en.lproj"; tmp_app_dir->make_dir_recursive(fname); FileAccessRef f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE); + f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get("application/config/name").operator String() + "\";"); } for (const String &E : translations) { @@ -795,6 +796,10 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p String fname = tmp_app_path_name + "/Contents/Resources/" + tr->get_locale() + ".lproj"; tmp_app_dir->make_dir_recursive(fname); FileAccessRef f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE); + String prop = "application/config/name_" + tr->get_locale(); + if (ProjectSettings::get_singleton()->has_setting(prop)) { + f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get(prop).operator String() + "\";"); + } } } } diff --git a/platform/osx/godot_application_delegate.mm b/platform/osx/godot_application_delegate.mm index be284ba543..dc82075c44 100644 --- a/platform/osx/godot_application_delegate.mm +++ b/platform/osx/godot_application_delegate.mm @@ -68,6 +68,10 @@ } - (void)applicationDidResignActive:(NSNotification *)notification { + DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); + if (ds) { + ds->mouse_process_popups(true); + } if (OS::get_singleton()->get_main_loop()) { OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT); } diff --git a/platform/osx/godot_content_view.mm b/platform/osx/godot_content_view.mm index 76d9cfb081..e96f0a8098 100644 --- a/platform/osx/godot_content_view.mm +++ b/platform/osx/godot_content_view.mm @@ -281,7 +281,7 @@ } DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - return !wd.no_focus; + return !wd.no_focus && !wd.is_popup; } - (BOOL)acceptsFirstResponder { diff --git a/platform/osx/godot_window.mm b/platform/osx/godot_window.mm index 772a2ddb9f..d43853a94b 100644 --- a/platform/osx/godot_window.mm +++ b/platform/osx/godot_window.mm @@ -52,7 +52,7 @@ } DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - return !wd.no_focus; + return !wd.no_focus && !wd.is_popup; } - (BOOL)canBecomeMainWindow { @@ -63,7 +63,7 @@ } DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - return !wd.no_focus; + return !wd.no_focus && !wd.is_popup; } @end diff --git a/platform/osx/godot_window_delegate.mm b/platform/osx/godot_window_delegate.mm index 1742be987d..9f49a6a4e9 100644 --- a/platform/osx/godot_window_delegate.mm +++ b/platform/osx/godot_window_delegate.mm @@ -54,6 +54,8 @@ return; } + ds->popup_close(window_id); + DisplayServerOSX::WindowData &wd = ds->get_window(window_id); while (wd.transient_children.size()) { ds->window_set_transient(wd.transient_children.front()->get(), DisplayServerOSX::INVALID_WINDOW_ID); @@ -147,6 +149,20 @@ } } +- (void)windowWillStartLiveResize:(NSNotification *)notification { + DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); + if (ds) { + ds->set_is_resizing(true); + } +} + +- (void)windowDidEndLiveResize:(NSNotification *)notification { + DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); + if (ds) { + ds->set_is_resizing(false); + } +} + - (void)windowDidResize:(NSNotification *)notification { DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); if (!ds || !ds->has_window(window_id)) { diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 6700f8fe82..7e0cf9f9cc 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -58,7 +58,8 @@ _FORCE_INLINE_ String OS_OSX::get_framework_executable(const String &p_path) { void OS_OSX::pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context) { // Prevent main loop from sleeping and redraw window during resize / modal popups. - if (get_singleton()->get_main_loop()) { + DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); + if (get_singleton()->get_main_loop() && ds && (get_singleton()->get_render_thread_mode() != RENDER_SEPARATE_THREAD || !ds->get_is_resizing())) { Main::force_redraw(); if (!Main::is_iterating()) { // Avoid cyclic loop. Main::iteration(); |