ashlar_native/menu/mod.rs
1//! A native menu bar described by a plain, serializable [`MenuSpec`].
2//!
3//! The model lives in [`ashlar_command::menu`] (one model for every renderer; `ashlar-ui` draws
4//! the same spec in the window). Its types are re-exported here so `ashlar_native::menu::MenuSpec`
5//! keeps working; new code may use either path.
6//!
7//! The app builds the spec from its state every frame (cheap: plain data), and
8//! [`NativeMenuBar::update`] turns it into the OS menu: it rebuilds only when
9//! [`MenuSpec::structure_hash`] changes (items added or removed, accelerators changed) and
10//! otherwise pushes label / enabled / checked changes in place, only when
11//! [`MenuSpec::state_hash`] changes. Activations arrive as item ids on a channel, with a
12//! [`crate::Waker`] call so an idle app wakes up.
13//!
14//! The spec has no OS or UI types, builds everywhere (the web uses an in-window menu bar drawn
15//! from the same spec) and is tested without creating real menus. The native bar exists on macOS
16//! (the app's menu bar) and Windows (a window's menu bar, see [`NativeMenuBar::attach_window`]);
17//! elsewhere [`NativeMenuBar::install`] returns `Unsupported`.
18
19mod accelerator;
20#[cfg(any(target_os = "macos", target_os = "windows"))]
21mod native;
22#[cfg(any(target_os = "macos", target_os = "windows"))]
23pub mod popup;
24
25#[cfg(any(target_os = "macos", target_os = "windows"))]
26pub use accelerator::to_muda;
27#[allow(deprecated)]
28pub use accelerator::{Accelerator, Modifiers, menu_accelerator};
29pub use ashlar_command::menu::{Item, MAX_DEPTH, MAX_NODES, MAX_TEXT, MenuNode, MenuSpec, Path, Role, Submenu, SubmenuRole, menu_chord};
30#[cfg(any(target_os = "macos", target_os = "windows"))]
31pub use native::NativeMenuBar;
32
33#[cfg(not(any(target_os = "macos", target_os = "windows")))]
34use crate::{NativeError, Result};
35
36/// What [`NativeMenuBar::update`] did.
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum Update {
39 Unchanged,
40 /// Labels / enabled / checked state pushed in place.
41 Patched,
42 /// The native menus were rebuilt.
43 Rebuilt,
44}
45
46/// No native menu bar on this platform: every call reports `Unsupported` (draw the spec in the
47/// window instead).
48#[cfg(not(any(target_os = "macos", target_os = "windows")))]
49pub struct NativeMenuBar(());
50
51#[cfg(not(any(target_os = "macos", target_os = "windows")))]
52impl NativeMenuBar {
53 pub fn install(_spec: &MenuSpec, _waker: crate::Waker) -> Result<Self> {
54 Err(NativeError::Unsupported)
55 }
56 pub fn attach_window(&self, _window: &impl raw_window_handle::HasWindowHandle) -> Result<()> {
57 Err(NativeError::Unsupported)
58 }
59 pub fn update(&mut self, _spec: &MenuSpec) -> Result<Update> {
60 Err(NativeError::Unsupported)
61 }
62 pub fn try_recv(&self) -> Option<String> {
63 None
64 }
65 pub fn drain(&self) -> Vec<String> {
66 vec![]
67 }
68}