Skip to content

Repository files navigation

muda

Menu Utilities library for Desktop Applications.

Documentation

Platforms supported:

  • Windows
  • macOS
  • Linux/BSD with GTK 3 or GTK 4

Platform-specific notes:

Cargo Features

The Win32 and AppKit backends are always enabled on Windows and macOS, respectively.

  • gtk3: Enables the GTK 3 backend on Linux or BSD platforms. This is enabled by default.
  • gtk4: Enables the GTK 4 backend on Linux or BSD platforms. Use default-features = false when enabling this feature because the default features include gtk3.
  • common-controls-v6: Use TaskDialogIndirect API from ComCtl32.dll v6 on Windows for showing the predefined About menu item dialog.
  • snapshot: Enables thread-safe menu snapshot types and methods, switching shared menu state to thread-safe synchronization.
  • serde: Enables de/serializing the dpi types.

When both gtk3 and gtk4 are enabled, Muda uses the GTK 4 backend and emits a Cargo warning.

Dependencies (Linux/BSD)

The gtk3 feature uses GTK 3 for menus. The gtk4 feature uses GTK 4 for menus.

Be sure to install the packages for the GTK backend you enabled before building:

Arch Linux / Manjaro:

# GTK 3 backend
pacman -S gtk3

# GTK 4 backend
pacman -S gtk4

Debian / Ubuntu:

# GTK 3 backend
sudo apt install libgtk-3-dev

# GTK 4 backend
sudo apt install libgtk-4-dev

Dependencies in FreeBSD

Install these dependencies in order to compile muda. Instructions using pkg:

# GTK 3 backend
pkg install -y rust glib pkgconf gtk3

# GTK 4 backend
pkg install -y rust glib pkgconf gtk4

Example

Create the menu and add your items

let menu = Menu::new();
let menu_item2 = MenuItem::new("Menu item #2", false, None);
let submenu = Submenu::with_items("Submenu Outer", true,&[
  &MenuItem::new("Menu item #1", true, Some(Accelerator::new(Modifiers::ALT, Code::KeyD))),
  &PredefinedMenuItem::separator(),
  &menu_item2,
  &MenuItem::new("Menu item #3", true, None),
  &PredefinedMenuItem::separator(),
  &Submenu::with_items("Submenu Inner", true,&[
    &MenuItem::new("Submenu item #1", true, None),
    &PredefinedMenuItem::separator(),
    &menu_item2,
  ])
]);

Then add your root menu to a window on Windows, GTK 3, or GTK 4 or use it as your global app menu on macOS

// --snip--
#[cfg(target_os = "windows")]
unsafe { menu.init_for_hwnd(window.hwnd() as isize) };
#[cfg(target_os = "linux")]
menu.init_for_gtk_window(&gtk_window, Some(&vertical_gtk_box));
#[cfg(target_os = "macos")]
menu.init_for_nsapp();

Context menus (Popup menus)

You can also use a [Menu] or a [Submenu] to show a context menu.

// --snip--
let position = muda::dpi::PhysicalPosition { x: 100., y: 120. };
#[cfg(target_os = "windows")]
unsafe { menu.show_context_menu_for_hwnd(window.hwnd() as isize, Some(position.into())) };
#[cfg(target_os = "linux")]
menu.show_context_menu_for_gtk_window(&gtk_window, Some(position.into()));
#[cfg(target_os = "macos")]
unsafe { menu.show_context_menu_for_nsview(nsview, Some(position.into())) };

Processing menu events

You can use MenuEvent::receiver to get a reference to the MenuEventReceiver which you can use to listen to events when a menu item is activated

if let Ok(event) = MenuEvent::receiver().try_recv() {
    match event.id {
        _ if event.id == save_item.id() => {
            println!("Save menu item activated");
        },
        _ => {}
    }
}

Note for winit or tao users:

You should use [MenuEvent::set_event_handler] and forward the menu events to the event loop by using EventLoopProxy so that the event loop is awakened on each menu event.

enum UserEvent {
  MenuEvent(muda::MenuEvent)
}

let event_loop = EventLoop::<UserEvent>::with_user_event().build().unwrap();

let proxy = event_loop.create_proxy();
muda::MenuEvent::set_event_handler(Some(move |event| {
    proxy.send_event(UserEvent::MenuEvent(event));
}));

License

Apache-2.0/MIT

About

Menu Utilities for Desktop Applications in Rust.

Topics

Resources

Stars

421 stars

Watchers

10 watching

Forks

Releases

Used by

Contributors

Languages