Skip to main content

win_context_menu/
menu_items.rs

1//! Data types representing context menu items and user selections.
2
3/// A single item from a context menu.
4///
5/// Obtained via [`ContextMenu::enumerate`](crate::ContextMenu::enumerate) or
6/// as part of a [`SelectedItem`].
7#[derive(Debug, Clone, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct MenuItem {
10    /// The raw command ID assigned by the shell handler.
11    pub id: u32,
12    /// Display label with accelerator characters (`&`) stripped.
13    pub label: String,
14    /// The verb/command string if available (e.g. `"open"`, `"delete"`,
15    /// `"properties"`). Not all items expose a verb.
16    pub command_string: Option<String>,
17    /// `true` if this item is a visual separator line.
18    pub is_separator: bool,
19    /// `true` if this item is disabled / grayed out.
20    pub is_disabled: bool,
21    /// `true` if this item has a check mark.
22    pub is_checked: bool,
23    /// `true` if this is the default (bold) item.
24    pub is_default: bool,
25    /// Sub-menu items, if this item opens a submenu.
26    pub submenu: Option<Vec<MenuItem>>,
27}
28
29impl MenuItem {
30    pub(crate) fn separator() -> Self {
31        Self {
32            id: 0,
33            label: String::new(),
34            command_string: None,
35            is_separator: true,
36            is_disabled: false,
37            is_checked: false,
38            is_default: false,
39            submenu: None,
40        }
41    }
42}
43
44impl std::fmt::Display for MenuItem {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        if self.is_separator {
47            write!(f, "---separator---")
48        } else {
49            write!(f, "{}", self.label)?;
50            if let Some(ref cmd) = self.command_string {
51                write!(f, " [{}]", cmd)?;
52            }
53            Ok(())
54        }
55    }
56}
57
58/// Boxed closure that executes a context menu command.
59type Invoker = Box<dyn FnOnce(Option<InvokeParams>) -> crate::error::Result<()>>;
60
61/// A menu item that was selected by the user via
62/// [`ContextMenu::show`](crate::ContextMenu::show) or
63/// [`ContextMenu::show_at`](crate::ContextMenu::show_at).
64///
65/// Call [`execute`](SelectedItem::execute) to run the associated shell command,
66/// or inspect [`menu_item`](SelectedItem::menu_item) to decide first.
67pub struct SelectedItem {
68    pub(crate) menu_item: MenuItem,
69    pub(crate) command_id: u32,
70    pub(crate) invoker: Option<Invoker>,
71}
72
73impl SelectedItem {
74    /// Get the menu item metadata (label, verb, flags, etc.).
75    pub fn menu_item(&self) -> &MenuItem {
76        &self.menu_item
77    }
78
79    /// Get the raw command ID that was returned by `TrackPopupMenu`.
80    pub fn command_id(&self) -> u32 {
81        self.command_id
82    }
83
84    /// Execute the selected command with default parameters.
85    ///
86    /// This is equivalent to the user clicking the menu item in Explorer.
87    /// Consumes `self` because a command can only be invoked once.
88    pub fn execute(self) -> crate::error::Result<()> {
89        if let Some(invoker) = self.invoker {
90            invoker(None)
91        } else {
92            Ok(())
93        }
94    }
95
96    /// Execute the selected command with custom parameters.
97    ///
98    /// Use this to override the working directory or window show state.
99    pub fn execute_with(self, params: InvokeParams) -> crate::error::Result<()> {
100        if let Some(invoker) = self.invoker {
101            invoker(Some(params))
102        } else {
103            Ok(())
104        }
105    }
106}
107
108impl std::fmt::Debug for SelectedItem {
109    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110        f.debug_struct("SelectedItem")
111            .field("menu_item", &self.menu_item)
112            .field("command_id", &self.command_id)
113            .finish_non_exhaustive()
114    }
115}
116
117/// Parameters for customizing command invocation.
118///
119/// Pass to [`SelectedItem::execute_with`] to override defaults.
120#[derive(Debug, Clone, Default)]
121pub struct InvokeParams {
122    /// Working directory for the command. Defaults to the item's parent folder.
123    pub directory: Option<String>,
124    /// Window show command (`SW_SHOW`, `SW_HIDE`, etc.). Defaults to
125    /// `SW_SHOWNORMAL`.
126    pub show_cmd: Option<i32>,
127}