diff --git a/internal/backends/winit/event_loop.rs b/internal/backends/winit/event_loop.rs index ae8ab114a10..f46b0ef8df7 100644 --- a/internal/backends/winit/event_loop.rs +++ b/internal/backends/winit/event_loop.rs @@ -346,17 +346,7 @@ impl winit::application::ApplicationHandler for EventLoopState { .err(); } WindowEvent::Focused(have_focus) => { - let have_focus = have_focus || window.input_method_focused(); - // We don't render popups as separate windows yet, so treat - // focus to be the same as being active. - if have_focus != runtime_window.active() { - self.loop_error = window - .window() - .try_dispatch_event(corelib::platform::WindowEvent::WindowActiveChanged( - have_focus, - )) - .err(); - } + self.loop_error = window.activation_changed(have_focus).err(); } WindowEvent::KeyboardInput { event, is_synthetic, .. } => { diff --git a/internal/backends/winit/lib.rs b/internal/backends/winit/lib.rs index 4ac162c9015..3dae5c3f358 100644 --- a/internal/backends/winit/lib.rs +++ b/internal/backends/winit/lib.rs @@ -235,8 +235,16 @@ impl BackendBuilder { /// slint::platform::set_platform(Box::new(backend)); /// ``` pub fn build(self) -> Result { - let event_loop_builder = - self.event_loop_builder.unwrap_or_else(winit::event_loop::EventLoop::with_user_event); + let event_loop_builder = self.event_loop_builder.unwrap_or_else(|| { + #[allow(unused_mut)] + let mut default_builder = winit::event_loop::EventLoop::with_user_event(); + #[cfg(all(feature = "muda", target_os = "macos"))] + winit::platform::macos::EventLoopBuilderExtMacOS::with_default_menu( + &mut default_builder, + false, + ); + default_builder + }); // Initialize the winit event loop and propagate errors if for example `DISPLAY` or `WAYLAND_DISPLAY` isn't set. diff --git a/internal/backends/winit/muda.rs b/internal/backends/winit/muda.rs index a4fefb7b72e..5fa26d78a22 100644 --- a/internal/backends/winit/muda.rs +++ b/internal/backends/winit/muda.rs @@ -8,7 +8,7 @@ use winit::window::Window; pub struct MudaAdapter { entries: Vec, - menubar: vtable::VBox, + menubar: Option>, // We need to keep menu alive, otherwise muda segfaults _menu: muda::Menu, } @@ -89,11 +89,49 @@ impl MudaAdapter { menu.init_for_nsapp(); } - Self { entries: map, menubar, _menu: menu } + Self { entries: map, menubar: Some(menubar), _menu: menu } } pub fn invoke(&self, entry_id: usize) { let Some(entry) = &self.entries.get(entry_id) else { return }; - self.menubar.activate(entry); + let Some(menubar) = self.menubar.as_ref() else { return }; + menubar.activate(entry); + } + + #[cfg(target_os = "macos")] + pub fn setup_default_menu() -> Result { + let menu = muda::Menu::new(); + + let app_menu = muda::Submenu::new("App", true); + menu.append(&app_menu) + .and_then(|_| { + app_menu.append_items(&[ + &muda::PredefinedMenuItem::about(None, None), + &muda::PredefinedMenuItem::separator(), + &muda::PredefinedMenuItem::services(None), + &muda::PredefinedMenuItem::separator(), + &muda::PredefinedMenuItem::hide(None), + &muda::PredefinedMenuItem::hide_others(None), + &muda::PredefinedMenuItem::show_all(None), + &muda::PredefinedMenuItem::separator(), + &muda::PredefinedMenuItem::quit(None), + ]) + }) + .map_err(|menu_bar_err| { + i_slint_core::api::PlatformError::Other(menu_bar_err.to_string()) + })?; + + menu.init_for_nsapp(); + + Ok(Self { entries: vec![], menubar: None, _menu: menu }) + } + + #[cfg(target_os = "macos")] + pub fn window_activation_changed(&self, is_active: bool) { + if is_active { + self._menu.init_for_nsapp(); + } else { + self._menu.remove_for_nsapp(); + } } } diff --git a/internal/backends/winit/winitwindowadapter.rs b/internal/backends/winit/winitwindowadapter.rs index 8ea46f55943..cd6ede25e28 100644 --- a/internal/backends/winit/winitwindowadapter.rs +++ b/internal/backends/winit/winitwindowadapter.rs @@ -685,6 +685,31 @@ impl WinitWindowAdapter { }) .ok() } + + pub fn activation_changed(&self, is_active: bool) -> Result<(), PlatformError> { + let have_focus = is_active || self.input_method_focused(); + let slint_window = self.window(); + let runtime_window = WindowInner::from_pub(slint_window); + // We don't render popups as separate windows yet, so treat + // focus to be the same as being active. + if have_focus != runtime_window.active() { + slint_window.try_dispatch_event( + corelib::platform::WindowEvent::WindowActiveChanged(have_focus), + )?; + } + + #[cfg(all(feature = "muda", target_os = "macos"))] + { + if self.muda_adapter.borrow().is_none() { + *self.muda_adapter.borrow_mut() = + Some(crate::muda::MudaAdapter::setup_default_menu()?); + } + + self.muda_adapter.borrow().as_ref().unwrap().window_activation_changed(is_active); + } + + Ok(()) + } } impl WindowAdapter for WinitWindowAdapter {