Skip to content

Commit

Permalink
Enable muda macOS menubar on macOS
Browse files Browse the repository at this point in the history
Hide the winit menu bar and provide a fallback if the app doesn't
provide one. Also, activate per window to fix support for multiple
windows.
  • Loading branch information
tronical committed Jan 10, 2025
1 parent c764da4 commit 5acd931
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 16 deletions.
12 changes: 1 addition & 11 deletions internal/backends/winit/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,17 +346,7 @@ impl winit::application::ApplicationHandler<SlintUserEvent> 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, .. } => {
Expand Down
12 changes: 10 additions & 2 deletions internal/backends/winit/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,16 @@ impl BackendBuilder {
/// slint::platform::set_platform(Box::new(backend));
/// ```
pub fn build(self) -> Result<Backend, PlatformError> {
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.

Expand Down
44 changes: 41 additions & 3 deletions internal/backends/winit/muda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use winit::window::Window;

pub struct MudaAdapter {
entries: Vec<i_slint_core::items::MenuEntry>,
menubar: vtable::VBox<MenuVTable>,
menubar: Option<vtable::VBox<MenuVTable>>,
// We need to keep menu alive, otherwise muda segfaults
_menu: muda::Menu,
}
Expand Down Expand Up @@ -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<Self, i_slint_core::api::PlatformError> {
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();
}
}
}
25 changes: 25 additions & 0 deletions internal/backends/winit/winitwindowadapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 5acd931

Please sign in to comment.