1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
// Copyright 2022-2022 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.inner
// SPDX-License-Identifier: MIT
use std::{cell::RefCell, rc::Rc};
use crate::{util::AddOp, ContextMenu, IsMenuItem, MenuId, MenuItemKind, Position};
/// A menu that can be added to a [`Menu`] or another [`Submenu`].
///
/// [`Menu`]: crate::Menu
#[derive(Clone)]
pub struct Submenu {
pub(crate) id: Rc<MenuId>,
pub(crate) inner: Rc<RefCell<crate::platform_impl::MenuChild>>,
}
unsafe impl IsMenuItem for Submenu {
fn kind(&self) -> MenuItemKind {
MenuItemKind::Submenu(self.clone())
}
fn id(&self) -> &MenuId {
self.id()
}
}
impl Submenu {
/// Create a new submenu.
///
/// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic
/// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`.
pub fn new<S: AsRef<str>>(text: S, enabled: bool) -> Self {
let submenu = crate::platform_impl::MenuChild::new_submenu(text.as_ref(), enabled, None);
Self {
id: Rc::new(submenu.id().clone()),
inner: Rc::new(RefCell::new(submenu)),
}
}
/// Create a new submenu with the specified id.
///
/// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic
/// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`.
pub fn with_id<I: Into<MenuId>, S: AsRef<str>>(id: I, text: S, enabled: bool) -> Self {
let id = id.into();
Self {
id: Rc::new(id.clone()),
inner: Rc::new(RefCell::new(crate::platform_impl::MenuChild::new_submenu(
text.as_ref(),
enabled,
Some(id),
))),
}
}
/// Creates a new submenu with given `items`. It calls [`Submenu::new`] and [`Submenu::append_items`] internally.
pub fn with_items<S: AsRef<str>>(
text: S,
enabled: bool,
items: &[&dyn IsMenuItem],
) -> crate::Result<Self> {
let menu = Self::new(text, enabled);
menu.append_items(items)?;
Ok(menu)
}
/// Creates a new submenu with the specified id and given `items`. It calls [`Submenu::new`] and [`Submenu::append_items`] internally.
pub fn with_id_and_items<I: Into<MenuId>, S: AsRef<str>>(
id: I,
text: S,
enabled: bool,
items: &[&dyn IsMenuItem],
) -> crate::Result<Self> {
let menu = Self::with_id(id, text, enabled);
menu.append_items(items)?;
Ok(menu)
}
/// Returns a unique identifier associated with this submenu.
pub fn id(&self) -> &MenuId {
&self.id
}
/// Add a menu item to the end of this menu.
pub fn append(&self, item: &dyn IsMenuItem) -> crate::Result<()> {
self.inner.borrow_mut().add_menu_item(item, AddOp::Append)
}
/// Add menu items to the end of this submenu. It calls [`Submenu::append`] in a loop.
pub fn append_items(&self, items: &[&dyn IsMenuItem]) -> crate::Result<()> {
for item in items {
self.append(*item)?
}
Ok(())
}
/// Add a menu item to the beginning of this submenu.
pub fn prepend(&self, item: &dyn IsMenuItem) -> crate::Result<()> {
self.inner
.borrow_mut()
.add_menu_item(item, AddOp::Insert(0))
}
/// Add menu items to the beginning of this submenu.
/// It calls [`Menu::prepend`](crate::Menu::prepend) on the first element and
/// passes the rest to [`Menu::insert_items`](crate::Menu::insert_items) with position of `1`.
pub fn prepend_items(&self, items: &[&dyn IsMenuItem]) -> crate::Result<()> {
self.insert_items(items, 0)
}
/// Insert a menu item at the specified `postion` in the submenu.
pub fn insert(&self, item: &dyn IsMenuItem, position: usize) -> crate::Result<()> {
self.inner
.borrow_mut()
.add_menu_item(item, AddOp::Insert(position))
}
/// Insert menu items at the specified `postion` in the submenu.
pub fn insert_items(&self, items: &[&dyn IsMenuItem], position: usize) -> crate::Result<()> {
for (i, item) in items.iter().enumerate() {
self.insert(*item, position + i)?
}
Ok(())
}
/// Remove a menu item from this submenu.
pub fn remove(&self, item: &dyn IsMenuItem) -> crate::Result<()> {
self.inner.borrow_mut().remove(item)
}
/// Remove the menu item at the specified position from this submenu.
pub fn remove_at(&self, position: usize) -> Option<MenuItemKind> {
let mut items = self.items();
if items.len() > position {
let item = items.remove(position);
let _ = self.remove(item.as_ref());
Some(item)
} else {
None
}
}
/// Returns a list of menu items that has been added to this submenu.
pub fn items(&self) -> Vec<MenuItemKind> {
self.inner.borrow().items()
}
/// Get the text for this submenu.
pub fn text(&self) -> String {
self.inner.borrow().text()
}
/// Set the text for this submenu. `text` could optionally contain
/// an `&` before a character to assign this character as the mnemonic
/// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`.
pub fn set_text<S: AsRef<str>>(&self, text: S) {
self.inner.borrow_mut().set_text(text.as_ref())
}
/// Get whether this submenu is enabled or not.
pub fn is_enabled(&self) -> bool {
self.inner.borrow().is_enabled()
}
/// Enable or disable this submenu.
pub fn set_enabled(&self, enabled: bool) {
self.inner.borrow_mut().set_enabled(enabled)
}
/// Set this submenu as the Window menu for the application on macOS.
///
/// This will cause macOS to automatically add window-switching items and
/// certain other items to the menu.
#[cfg(target_os = "macos")]
pub fn set_as_windows_menu_for_nsapp(&self) {
self.inner.borrow_mut().set_as_windows_menu_for_nsapp()
}
/// Set this submenu as the Help menu for the application on macOS.
///
/// This will cause macOS to automatically add a search box to the menu.
///
/// If no menu is set as the Help menu, macOS will automatically use any menu
/// which has a title matching the localized word "Help".
#[cfg(target_os = "macos")]
pub fn set_as_help_menu_for_nsapp(&self) {
self.inner.borrow_mut().set_as_help_menu_for_nsapp()
}
}
impl ContextMenu for Submenu {
#[cfg(target_os = "windows")]
fn hpopupmenu(&self) -> windows_sys::Win32::UI::WindowsAndMessaging::HMENU {
self.inner.borrow().hpopupmenu()
}
#[cfg(target_os = "windows")]
fn show_context_menu_for_hwnd(&self, hwnd: isize, position: Option<Position>) {
self.inner
.borrow_mut()
.show_context_menu_for_hwnd(hwnd, position)
}
#[cfg(target_os = "windows")]
fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) {
self.inner.borrow_mut().attach_menu_subclass_for_hwnd(hwnd)
}
#[cfg(target_os = "windows")]
fn detach_menu_subclass_from_hwnd(&self, hwnd: isize) {
self.inner.borrow_mut().detach_menu_subclass_from_hwnd(hwnd)
}
#[cfg(target_os = "linux")]
fn show_context_menu_for_gtk_window(
&self,
w: >k::ApplicationWindow,
position: Option<Position>,
) {
self.inner
.borrow_mut()
.show_context_menu_for_gtk_window(w, position)
}
#[cfg(target_os = "linux")]
fn gtk_context_menu(&self) -> gtk::Menu {
self.inner.borrow_mut().gtk_context_menu()
}
#[cfg(target_os = "macos")]
fn show_context_menu_for_nsview(&self, view: cocoa::base::id, position: Option<Position>) {
self.inner
.borrow_mut()
.show_context_menu_for_nsview(view, position)
}
#[cfg(target_os = "macos")]
fn ns_menu(&self) -> *mut std::ffi::c_void {
self.inner.borrow().ns_menu()
}
}