diff --git a/examples/tao.rs b/examples/tao.rs
index 5458a01..8153eed 100644
--- a/examples/tao.rs
+++ b/examples/tao.rs
@@ -72,7 +72,8 @@ fn main() {
menu_bar.append_items(&[&file_m, &edit_m, &window_m]);
- let custom_i_1 = MenuItem::new(
+ let custom_i_1 = MenuItem::with_id(
+ "custom-i-1",
"C&ustom 1",
true,
Some(Accelerator::new(Some(Modifiers::ALT), Code::KeyC)),
@@ -80,16 +81,20 @@ fn main() {
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/icon.png");
let icon = load_icon(std::path::Path::new(path));
- let image_item = IconMenuItem::new(
+ let image_item = IconMenuItem::with_id(
+ "image-custom-1",
"Image custom 1",
true,
Some(icon),
Some(Accelerator::new(Some(Modifiers::CONTROL), Code::KeyC)),
);
- let check_custom_i_1 = CheckMenuItem::new("Check Custom 1", true, true, None);
- let check_custom_i_2 = CheckMenuItem::new("Check Custom 2", false, true, None);
- let check_custom_i_3 = CheckMenuItem::new(
+ let check_custom_i_1 =
+ CheckMenuItem::with_id("check-custom-1", "Check Custom 1", true, true, None);
+ let check_custom_i_2 =
+ CheckMenuItem::with_id("check-custom-2", "Check Custom 2", false, true, None);
+ let check_custom_i_3 = CheckMenuItem::with_id(
+ "check-custom-3",
"Check Custom 3",
true,
true,
@@ -201,7 +206,10 @@ fn main() {
if event.id == custom_i_1.id() {
custom_i_1
.set_accelerator(Some(Accelerator::new(Some(Modifiers::SHIFT), Code::KeyF)));
- file_m.insert(&MenuItem::new("New Menu Item", true, None), 2);
+ file_m.insert(
+ &MenuItem::with_id("new-menu-id", "New Menu Item", true, None),
+ 2,
+ );
}
println!("{event:?}");
}
@@ -218,7 +226,7 @@ fn show_context_menu(window: &Window, menu: &dyn ContextMenu, position: Option
muda::icon::Icon {
+fn load_icon(path: &std::path::Path) -> muda::Icon {
let (icon_rgba, icon_width, icon_height) = {
let image = image::open(path)
.expect("Failed to open icon path")
@@ -227,5 +235,5 @@ fn load_icon(path: &std::path::Path) -> muda::icon::Icon {
let rgba = image.into_raw();
(rgba, width, height)
};
- muda::icon::Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
+ muda::Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
}
diff --git a/examples/winit.rs b/examples/winit.rs
index 664b069..b950066 100644
--- a/examples/winit.rs
+++ b/examples/winit.rs
@@ -204,7 +204,7 @@ fn show_context_menu(window: &Window, menu: &dyn ContextMenu, position: Option
muda::icon::Icon {
+fn load_icon(path: &std::path::Path) -> muda::Icon {
let (icon_rgba, icon_width, icon_height) = {
let image = image::open(path)
.expect("Failed to open icon path")
@@ -213,5 +213,5 @@ fn load_icon(path: &std::path::Path) -> muda::icon::Icon {
let rgba = image.into_raw();
(rgba, width, height)
};
- muda::icon::Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
+ muda::Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
}
diff --git a/examples/wry.rs b/examples/wry.rs
index 93e4e36..c795df4 100644
--- a/examples/wry.rs
+++ b/examples/wry.rs
@@ -266,7 +266,7 @@ fn show_context_menu(window: &Window, menu: &dyn ContextMenu, position: Option muda::icon::Icon {
+fn load_icon(path: &std::path::Path) -> muda::Icon {
let (icon_rgba, icon_width, icon_height) = {
let image = image::open(path)
.expect("Failed to open icon path")
@@ -275,5 +275,5 @@ fn load_icon(path: &std::path::Path) -> muda::icon::Icon {
let rgba = image.into_raw();
(rgba, width, height)
};
- muda::icon::Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
+ muda::Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
}
diff --git a/src/builders/check.rs b/src/builders/check.rs
index 38d0c37..4597082 100644
--- a/src/builders/check.rs
+++ b/src/builders/check.rs
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
-use crate::{accelerator::Accelerator, CheckMenuItem};
+use crate::{accelerator::Accelerator, CheckMenuItem, MenuId};
/// A builder type for [`CheckMenuItem`]
#[derive(Clone, Debug, Default)]
@@ -11,6 +11,7 @@ pub struct CheckMenuItemBuilder {
enabled: bool,
checked: bool,
acccelerator: Option,
+ id: Option,
}
impl CheckMenuItemBuilder {
@@ -18,6 +19,12 @@ impl CheckMenuItemBuilder {
Default::default()
}
+ /// Set the id this check menu item.
+ pub fn id(mut self, id: MenuId) -> Self {
+ self.id.replace(id);
+ self
+ }
+
/// Set the text for this check menu item.
///
/// See [`CheckMenuItem::set_text`] for more info.
@@ -52,6 +59,10 @@ impl CheckMenuItemBuilder {
/// Build this check menu item.
pub fn build(self) -> CheckMenuItem {
- CheckMenuItem::new(self.text, self.enabled, self.checked, self.acccelerator)
+ if let Some(id) = self.id {
+ CheckMenuItem::with_id(id, self.text, self.enabled, self.checked, self.acccelerator)
+ } else {
+ CheckMenuItem::new(self.text, self.enabled, self.checked, self.acccelerator)
+ }
}
}
diff --git a/src/builders/icon.rs b/src/builders/icon.rs
index e8a1dc6..d413da1 100644
--- a/src/builders/icon.rs
+++ b/src/builders/icon.rs
@@ -5,7 +5,7 @@
use crate::{
accelerator::Accelerator,
icon::{Icon, NativeIcon},
- IconMenuItem,
+ IconMenuItem, MenuId,
};
/// A builder type for [`IconMenuItem`]
@@ -13,6 +13,7 @@ use crate::{
pub struct IconMenuItemBuilder {
text: String,
enabled: bool,
+ id: Option,
acccelerator: Option,
icon: Option,
native_icon: Option,
@@ -23,6 +24,12 @@ impl IconMenuItemBuilder {
Default::default()
}
+ /// Set the id this icon menu item.
+ pub fn id(mut self, id: MenuId) -> Self {
+ self.id.replace(id);
+ self
+ }
+
/// Set the text for this icon menu item.
///
/// See [`IconMenuItem::set_text`] for more info.
@@ -65,7 +72,19 @@ impl IconMenuItemBuilder {
/// Build this icon menu item.
pub fn build(self) -> IconMenuItem {
- if self.icon.is_some() {
+ if let Some(id) = self.id {
+ if self.icon.is_some() {
+ IconMenuItem::with_id(id, self.text, self.enabled, self.icon, self.acccelerator)
+ } else {
+ IconMenuItem::with_id_and_native_icon(
+ id,
+ self.text,
+ self.enabled,
+ self.native_icon,
+ self.acccelerator,
+ )
+ }
+ } else if self.icon.is_some() {
IconMenuItem::new(self.text, self.enabled, self.icon, self.acccelerator)
} else {
IconMenuItem::with_native_icon(
diff --git a/src/builders/normal.rs b/src/builders/normal.rs
index 4fc459e..3fde37a 100644
--- a/src/builders/normal.rs
+++ b/src/builders/normal.rs
@@ -2,13 +2,14 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
-use crate::{accelerator::Accelerator, MenuItem};
+use crate::{accelerator::Accelerator, MenuId, MenuItem};
/// A builder type for [`MenuItem`]
#[derive(Clone, Debug, Default)]
pub struct MenuItemBuilder {
text: String,
enabled: bool,
+ id: Option,
acccelerator: Option,
}
@@ -17,6 +18,12 @@ impl MenuItemBuilder {
Default::default()
}
+ /// Set the id this menu item.
+ pub fn id(mut self, id: MenuId) -> Self {
+ self.id.replace(id);
+ self
+ }
+
/// Set the text for this menu item.
///
/// See [`MenuItem::set_text`] for more info.
@@ -45,6 +52,10 @@ impl MenuItemBuilder {
/// Build this menu item.
pub fn build(self) -> MenuItem {
- MenuItem::new(self.text, self.enabled, self.acccelerator)
+ if let Some(id) = self.id {
+ MenuItem::with_id(id, self.text, self.enabled, self.acccelerator)
+ } else {
+ MenuItem::new(self.text, self.enabled, self.acccelerator)
+ }
}
}
diff --git a/src/builders/submenu.rs b/src/builders/submenu.rs
index a3f7214..15a01ea 100644
--- a/src/builders/submenu.rs
+++ b/src/builders/submenu.rs
@@ -2,13 +2,14 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
-use crate::{IsMenuItem, Submenu};
+use crate::{IsMenuItem, MenuId, Submenu};
/// A builder type for [`Submenu`]
#[derive(Clone, Default)]
pub struct SubmenuBuilder<'a> {
text: String,
enabled: bool,
+ id: Option,
items: Vec<&'a dyn IsMenuItem>,
}
@@ -26,6 +27,12 @@ impl<'a> SubmenuBuilder<'a> {
Default::default()
}
+ /// Set the id this submenu.
+ pub fn id(mut self, id: MenuId) -> Self {
+ self.id.replace(id);
+ self
+ }
+
/// Set the text for this submenu.
///
/// See [`Submenu::set_text`] for more info.
@@ -54,6 +61,10 @@ impl<'a> SubmenuBuilder<'a> {
/// Build this menu item.
pub fn build(self) -> crate::Result {
- Submenu::with_items(self.text, self.enabled, &self.items)
+ if let Some(id) = self.id {
+ Submenu::with_id_and_items(id, self.text, self.enabled, &self.items)
+ } else {
+ Submenu::with_items(self.text, self.enabled, &self.items)
+ }
}
}
diff --git a/src/items/check.rs b/src/items/check.rs
index 3bc4d6a..6cd92ea 100644
--- a/src/items/check.rs
+++ b/src/items/check.rs
@@ -1,10 +1,10 @@
// Copyright 2022-2022 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: Apache-2.inner
// SPDX-License-Identifier: MIT
use std::{cell::RefCell, rc::Rc};
-use crate::{accelerator::Accelerator, IsMenuItem, MenuItemKind};
+use crate::{accelerator::Accelerator, IsMenuItem, MenuId, MenuItemKind};
/// A check menu item inside a [`Menu`] or [`Submenu`]
/// and usually contains a text and a check mark or a similar toggle
@@ -13,12 +13,19 @@ use crate::{accelerator::Accelerator, IsMenuItem, MenuItemKind};
/// [`Menu`]: crate::Menu
/// [`Submenu`]: crate::Submenu
#[derive(Clone)]
-pub struct CheckMenuItem(pub(crate) Rc>);
+pub struct CheckMenuItem {
+ pub(crate) id: Rc,
+ pub(crate) inner: Rc>,
+}
unsafe impl IsMenuItem for CheckMenuItem {
fn kind(&self) -> MenuItemKind {
MenuItemKind::Check(self.clone())
}
+
+ fn id(&self) -> &MenuId {
+ self.id()
+ }
}
impl CheckMenuItem {
@@ -32,55 +39,82 @@ impl CheckMenuItem {
checked: bool,
acccelerator: Option,
) -> Self {
- Self(Rc::new(RefCell::new(
- crate::platform_impl::MenuChild::new_check(
+ let item = crate::platform_impl::MenuChild::new_check(
+ text.as_ref(),
+ enabled,
+ checked,
+ acccelerator,
+ None,
+ );
+ Self {
+ id: Rc::new(item.id().clone()),
+ inner: Rc::new(RefCell::new(item)),
+ }
+ }
+
+ /// Create a new check menu item with the specified id.
+ ///
+ /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic
+ /// for this check menu item. To display a `&` without assigning a mnemenonic, use `&&`.
+ pub fn with_id, S: AsRef>(
+ id: I,
+ text: S,
+ enabled: bool,
+ checked: bool,
+ acccelerator: Option,
+ ) -> Self {
+ let id = id.into();
+ Self {
+ id: Rc::new(id.clone()),
+ inner: Rc::new(RefCell::new(crate::platform_impl::MenuChild::new_check(
text.as_ref(),
enabled,
checked,
acccelerator,
- ),
- )))
+ Some(id),
+ ))),
+ }
}
/// Returns a unique identifier associated with this submenu.
- pub fn id(&self) -> u32 {
- self.0.borrow().id()
+ pub fn id(&self) -> &MenuId {
+ &self.id
}
/// Get the text for this check menu item.
pub fn text(&self) -> String {
- self.0.borrow().text()
+ self.inner.borrow().text()
}
/// Set the text for this check menu item. `text` could optionally contain
/// an `&` before a character to assign this character as the mnemonic
/// for this check menu item. To display a `&` without assigning a mnemenonic, use `&&`.
pub fn set_text>(&self, text: S) {
- self.0.borrow_mut().set_text(text.as_ref())
+ self.inner.borrow_mut().set_text(text.as_ref())
}
/// Get whether this check menu item is enabled or not.
pub fn is_enabled(&self) -> bool {
- self.0.borrow().is_enabled()
+ self.inner.borrow().is_enabled()
}
/// Enable or disable this check menu item.
pub fn set_enabled(&self, enabled: bool) {
- self.0.borrow_mut().set_enabled(enabled)
+ self.inner.borrow_mut().set_enabled(enabled)
}
/// Set this check menu item accelerator.
pub fn set_accelerator(&self, acccelerator: Option) -> crate::Result<()> {
- self.0.borrow_mut().set_accelerator(acccelerator)
+ self.inner.borrow_mut().set_accelerator(acccelerator)
}
/// Get whether this check menu item is checked or not.
pub fn is_checked(&self) -> bool {
- self.0.borrow().is_checked()
+ self.inner.borrow().is_checked()
}
/// Check or Uncheck this check menu item.
pub fn set_checked(&self, checked: bool) {
- self.0.borrow_mut().set_checked(checked)
+ self.inner.borrow_mut().set_checked(checked)
}
}
diff --git a/src/items/icon.rs b/src/items/icon.rs
index 0d3a446..222b658 100644
--- a/src/items/icon.rs
+++ b/src/items/icon.rs
@@ -1,5 +1,5 @@
// Copyright 2022-2022 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: Apache-2.inner
// SPDX-License-Identifier: MIT
use std::{cell::RefCell, rc::Rc};
@@ -7,7 +7,7 @@ use std::{cell::RefCell, rc::Rc};
use crate::{
accelerator::Accelerator,
icon::{Icon, NativeIcon},
- IsMenuItem, MenuItemKind,
+ IsMenuItem, MenuId, MenuItemKind,
};
/// An icon menu item inside a [`Menu`] or [`Submenu`]
@@ -16,12 +16,19 @@ use crate::{
/// [`Menu`]: crate::Menu
/// [`Submenu`]: crate::Submenu
#[derive(Clone)]
-pub struct IconMenuItem(pub(crate) Rc>);
+pub struct IconMenuItem {
+ pub(crate) id: Rc,
+ pub(crate) inner: Rc>,
+}
unsafe impl IsMenuItem for IconMenuItem {
fn kind(&self) -> MenuItemKind {
MenuItemKind::Icon(self.clone())
}
+
+ fn id(&self) -> &MenuId {
+ self.id()
+ }
}
impl IconMenuItem {
@@ -35,9 +42,41 @@ impl IconMenuItem {
icon: Option,
acccelerator: Option,
) -> Self {
- Self(Rc::new(RefCell::new(
- crate::platform_impl::MenuChild::new_icon(text.as_ref(), enabled, icon, acccelerator),
- )))
+ let item = crate::platform_impl::MenuChild::new_icon(
+ text.as_ref(),
+ enabled,
+ icon,
+ acccelerator,
+ None,
+ );
+ Self {
+ id: Rc::new(item.id().clone()),
+ inner: Rc::new(RefCell::new(item)),
+ }
+ }
+
+ /// Create a new icon menu item with the specified id.
+ ///
+ /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic
+ /// for this icon menu item. To display a `&` without assigning a mnemenonic, use `&&`.
+ pub fn with_id, S: AsRef>(
+ id: I,
+ text: S,
+ enabled: bool,
+ icon: Option,
+ acccelerator: Option,
+ ) -> Self {
+ let id = id.into();
+ Self {
+ id: Rc::new(id.clone()),
+ inner: Rc::new(RefCell::new(crate::platform_impl::MenuChild::new_icon(
+ text.as_ref(),
+ enabled,
+ icon,
+ acccelerator,
+ Some(id),
+ ))),
+ }
}
/// Create a new icon menu item but with a native icon.
@@ -53,51 +92,83 @@ impl IconMenuItem {
native_icon: Option,
acccelerator: Option,
) -> Self {
- Self(Rc::new(RefCell::new(
- crate::platform_impl::MenuChild::new_native_icon(
- text.as_ref(),
- enabled,
- native_icon,
- acccelerator,
- ),
- )))
+ let item = crate::platform_impl::MenuChild::new_native_icon(
+ text.as_ref(),
+ enabled,
+ native_icon,
+ acccelerator,
+ None,
+ );
+ Self {
+ id: Rc::new(item.id().clone()),
+ inner: Rc::new(RefCell::new(item)),
+ }
+ }
+
+ /// Create a new icon menu item but with the specified id and a native icon.
+ ///
+ /// See [`IconMenuItem::new`] for more info.
+ ///
+ /// ## Platform-specific:
+ ///
+ /// - **Windows / Linux**: Unsupported.
+ pub fn with_id_and_native_icon, S: AsRef>(
+ id: I,
+ text: S,
+ enabled: bool,
+ native_icon: Option,
+ acccelerator: Option,
+ ) -> Self {
+ let id = id.into();
+ Self {
+ id: Rc::new(id.clone()),
+ inner: Rc::new(RefCell::new(
+ crate::platform_impl::MenuChild::new_native_icon(
+ text.as_ref(),
+ enabled,
+ native_icon,
+ acccelerator,
+ Some(id),
+ ),
+ )),
+ }
}
/// Returns a unique identifier associated with this submenu.
- pub fn id(&self) -> u32 {
- self.0.borrow().id()
+ pub fn id(&self) -> &MenuId {
+ &self.id
}
/// Get the text for this check menu item.
pub fn text(&self) -> String {
- self.0.borrow().text()
+ self.inner.borrow().text()
}
/// Set the text for this check menu item. `text` could optionally contain
/// an `&` before a character to assign this character as the mnemonic
/// for this check menu item. To display a `&` without assigning a mnemenonic, use `&&`.
pub fn set_text>(&self, text: S) {
- self.0.borrow_mut().set_text(text.as_ref())
+ self.inner.borrow_mut().set_text(text.as_ref())
}
/// Get whether this check menu item is enabled or not.
pub fn is_enabled(&self) -> bool {
- self.0.borrow().is_enabled()
+ self.inner.borrow().is_enabled()
}
/// Enable or disable this check menu item.
pub fn set_enabled(&self, enabled: bool) {
- self.0.borrow_mut().set_enabled(enabled)
+ self.inner.borrow_mut().set_enabled(enabled)
}
/// Set this icon menu item accelerator.
pub fn set_accelerator(&self, acccelerator: Option) -> crate::Result<()> {
- self.0.borrow_mut().set_accelerator(acccelerator)
+ self.inner.borrow_mut().set_accelerator(acccelerator)
}
/// Change this menu item icon or remove it.
pub fn set_icon(&self, icon: Option) {
- self.0.borrow_mut().set_icon(icon)
+ self.inner.borrow_mut().set_icon(icon)
}
/// Change this menu item icon to a native image or remove it.
@@ -107,6 +178,6 @@ impl IconMenuItem {
/// - **Windows / Linux**: Unsupported.
pub fn set_native_icon(&mut self, _icon: Option) {
#[cfg(target_os = "macos")]
- self.0.borrow_mut().set_native_icon(_icon)
+ self.inner.borrow_mut().set_native_icon(_icon)
}
}
diff --git a/src/items/mod.rs b/src/items/mod.rs
index 4a76aac..43b9a3b 100644
--- a/src/items/mod.rs
+++ b/src/items/mod.rs
@@ -13,3 +13,23 @@ pub use icon::*;
pub use normal::*;
pub use predefined::*;
pub use submenu::*;
+
+#[cfg(test)]
+mod test {
+ use crate::{CheckMenuItem, IconMenuItem, MenuId, MenuItem, Submenu};
+
+ #[test]
+ fn it_returns_same_id() {
+ let id = MenuId("1".into());
+ assert_eq!(id, MenuItem::with_id(id.clone(), "", true, None).id());
+ assert_eq!(id, Submenu::with_id(id.clone(), "", true).id());
+ assert_eq!(
+ id,
+ CheckMenuItem::with_id(id.clone(), "", true, true, None).id()
+ );
+ assert_eq!(
+ id,
+ IconMenuItem::with_id(id.clone(), "", true, None, None).id()
+ );
+ }
+}
diff --git a/src/items/normal.rs b/src/items/normal.rs
index d49f6cc..52a79e8 100644
--- a/src/items/normal.rs
+++ b/src/items/normal.rs
@@ -1,18 +1,25 @@
use std::{cell::RefCell, rc::Rc};
-use crate::{accelerator::Accelerator, IsMenuItem, MenuItemKind};
+use crate::{accelerator::Accelerator, IsMenuItem, MenuId, MenuItemKind};
/// A menu item inside a [`Menu`] or [`Submenu`] and contains only text.
///
/// [`Menu`]: crate::Menu
/// [`Submenu`]: crate::Submenu
#[derive(Clone)]
-pub struct MenuItem(pub(crate) Rc>);
+pub struct MenuItem {
+ pub(crate) id: Rc,
+ pub(crate) inner: Rc>,
+}
unsafe impl IsMenuItem for MenuItem {
fn kind(&self) -> MenuItemKind {
MenuItemKind::MenuItem(self.clone())
}
+
+ fn id(&self) -> &MenuId {
+ self.id()
+ }
}
impl MenuItem {
@@ -21,42 +28,64 @@ impl MenuItem {
/// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic
/// for this menu item. To display a `&` without assigning a mnemenonic, use `&&`.
pub fn new>(text: S, enabled: bool, acccelerator: Option) -> Self {
- Self(Rc::new(RefCell::new(crate::platform_impl::MenuChild::new(
- text.as_ref(),
- enabled,
- acccelerator,
- ))))
+ let item = crate::platform_impl::MenuChild::new(text.as_ref(), enabled, acccelerator, None);
+ Self {
+ id: Rc::new(item.id().clone()),
+ inner: Rc::new(RefCell::new(item)),
+ }
+ }
+
+ /// Create a new menu item with the specified id.
+ ///
+ /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic
+ /// for this menu item. To display a `&` without assigning a mnemenonic, use `&&`.
+ pub fn with_id, S: AsRef>(
+ id: I,
+ text: S,
+ enabled: bool,
+ acccelerator: Option,
+ ) -> Self {
+ let id = id.into();
+ Self {
+ id: Rc::new(id.clone()),
+ inner: Rc::new(RefCell::new(crate::platform_impl::MenuChild::new(
+ text.as_ref(),
+ enabled,
+ acccelerator,
+ Some(id),
+ ))),
+ }
}
/// Returns a unique identifier associated with this menu item.
- pub fn id(&self) -> u32 {
- self.0.borrow().id()
+ pub fn id(&self) -> &MenuId {
+ &self.id
}
/// Set the text for this menu item.
pub fn text(&self) -> String {
- self.0.borrow().text()
+ self.inner.borrow().text()
}
/// Set the text for this menu item. `text` could optionally contain
/// an `&` before a character to assign this character as the mnemonic
/// for this menu item. To display a `&` without assigning a mnemenonic, use `&&`.
pub fn set_text>(&self, text: S) {
- self.0.borrow_mut().set_text(text.as_ref())
+ self.inner.borrow_mut().set_text(text.as_ref())
}
/// Get whether this menu item is enabled or not.
pub fn is_enabled(&self) -> bool {
- self.0.borrow().is_enabled()
+ self.inner.borrow().is_enabled()
}
/// Enable or disable this menu item.
pub fn set_enabled(&self, enabled: bool) {
- self.0.borrow_mut().set_enabled(enabled)
+ self.inner.borrow_mut().set_enabled(enabled)
}
/// Set this menu item accelerator.
pub fn set_accelerator(&self, acccelerator: Option) -> crate::Result<()> {
- self.0.borrow_mut().set_accelerator(acccelerator)
+ self.inner.borrow_mut().set_accelerator(acccelerator)
}
}
diff --git a/src/items/predefined.rs b/src/items/predefined.rs
index 0f16061..af01c0c 100644
--- a/src/items/predefined.rs
+++ b/src/items/predefined.rs
@@ -1,23 +1,30 @@
// Copyright 2022-2022 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: Apache-2.inner
// SPDX-License-Identifier: MIT
use std::{cell::RefCell, rc::Rc};
use crate::{
accelerator::{Accelerator, CMD_OR_CTRL},
- AboutMetadata, IsMenuItem, MenuItemKind,
+ AboutMetadata, IsMenuItem, MenuId, MenuItemKind,
};
use keyboard_types::{Code, Modifiers};
/// A predefined (native) menu item which has a predfined behavior by the OS or by this crate.
#[derive(Clone)]
-pub struct PredefinedMenuItem(pub(crate) Rc>);
+pub struct PredefinedMenuItem {
+ pub(crate) id: Rc,
+ pub(crate) inner: Rc>,
+}
unsafe impl IsMenuItem for PredefinedMenuItem {
fn kind(&self) -> MenuItemKind {
MenuItemKind::Predefined(self.clone())
}
+
+ fn id(&self) -> &MenuId {
+ self.id()
+ }
}
impl PredefinedMenuItem {
@@ -150,26 +157,29 @@ impl PredefinedMenuItem {
}
fn new>(item: PredefinedMenuItemType, text: Option) -> Self {
- Self(Rc::new(RefCell::new(
- crate::platform_impl::MenuChild::new_predefined(
- item,
- text.map(|t| t.as_ref().to_string()),
- ),
- )))
+ let item = crate::platform_impl::MenuChild::new_predefined(
+ item,
+ text.map(|t| t.as_ref().to_string()),
+ );
+ Self {
+ id: Rc::new(item.id().clone()),
+ inner: Rc::new(RefCell::new(item)),
+ }
}
- pub(crate) fn id(&self) -> u32 {
- self.0.borrow().id()
+ /// Returns a unique identifier associated with this predefined menu item.
+ pub fn id(&self) -> &MenuId {
+ &self.id
}
/// Get the text for this predefined menu item.
pub fn text(&self) -> String {
- self.0.borrow().text()
+ self.inner.borrow().text()
}
/// Set the text for this predefined menu item.
pub fn set_text>(&self, text: S) {
- self.0.borrow_mut().set_text(text.as_ref())
+ self.inner.borrow_mut().set_text(text.as_ref())
}
}
@@ -185,21 +195,21 @@ fn test_about_metadata() {
assert_eq!(
AboutMetadata {
- version: Some("Version: 1.0".into()),
+ version: Some("Version: 1.inner".into()),
..Default::default()
}
.full_version(),
- Some("Version: 1.0".into())
+ Some("Version: 1.inner".into())
);
assert_eq!(
AboutMetadata {
- version: Some("Version: 1.0".into()),
+ version: Some("Version: 1.inner".into()),
short_version: Some("Universal".into()),
..Default::default()
}
.full_version(),
- Some("Version: 1.0 (Universal)".into())
+ Some("Version: 1.inner (Universal)".into())
);
}
diff --git a/src/items/submenu.rs b/src/items/submenu.rs
index ba60c5e..3f288fd 100644
--- a/src/items/submenu.rs
+++ b/src/items/submenu.rs
@@ -1,21 +1,28 @@
// Copyright 2022-2022 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: Apache-2.inner
// SPDX-License-Identifier: MIT
use std::{cell::RefCell, rc::Rc};
-use crate::{util::AddOp, ContextMenu, IsMenuItem, MenuItemKind, Position};
+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) Rc>);
+pub struct Submenu {
+ pub(crate) id: Rc,
+ pub(crate) inner: Rc>,
+}
unsafe impl IsMenuItem for Submenu {
fn kind(&self) -> MenuItemKind {
MenuItemKind::Submenu(self.clone())
}
+
+ fn id(&self) -> &MenuId {
+ self.id()
+ }
}
impl Submenu {
@@ -24,9 +31,28 @@ impl 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>(text: S, enabled: bool) -> Self {
- Self(Rc::new(RefCell::new(
- crate::platform_impl::MenuChild::new_submenu(text.as_ref(), enabled),
- )))
+ 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, S: AsRef>(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.
@@ -40,14 +66,26 @@ impl Submenu {
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, S: AsRef>(
+ id: I,
+ text: S,
+ enabled: bool,
+ items: &[&dyn IsMenuItem],
+ ) -> crate::Result {
+ 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) -> u32 {
- self.0.borrow().id()
+ 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.0.borrow_mut().add_menu_item(item, AddOp::Append)
+ 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.
@@ -61,7 +99,9 @@ impl Submenu {
/// Add a menu item to the beginning of this submenu.
pub fn prepend(&self, item: &dyn IsMenuItem) -> crate::Result<()> {
- self.0.borrow_mut().add_menu_item(item, AddOp::Insert(0))
+ self.inner
+ .borrow_mut()
+ .add_menu_item(item, AddOp::Insert(0))
}
/// Add menu items to the beginning of this submenu.
@@ -73,7 +113,7 @@ impl Submenu {
/// Insert a menu item at the specified `postion` in the submenu.
pub fn insert(&self, item: &dyn IsMenuItem, position: usize) -> crate::Result<()> {
- self.0
+ self.inner
.borrow_mut()
.add_menu_item(item, AddOp::Insert(position))
}
@@ -89,34 +129,34 @@ impl Submenu {
/// Remove a menu item from this submenu.
pub fn remove(&self, item: &dyn IsMenuItem) -> crate::Result<()> {
- self.0.borrow_mut().remove(item)
+ self.inner.borrow_mut().remove(item)
}
/// Returns a list of menu items that has been added to this submenu.
pub fn items(&self) -> Vec {
- self.0.borrow().items()
+ self.inner.borrow().items()
}
/// Get the text for this submenu.
pub fn text(&self) -> String {
- self.0.borrow().text()
+ 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>(&self, text: S) {
- self.0.borrow_mut().set_text(text.as_ref())
+ self.inner.borrow_mut().set_text(text.as_ref())
}
/// Get whether this submenu is enabled or not.
pub fn is_enabled(&self) -> bool {
- self.0.borrow().is_enabled()
+ self.inner.borrow().is_enabled()
}
/// Enable or disable this submenu.
pub fn set_enabled(&self, enabled: bool) {
- self.0.borrow_mut().set_enabled(enabled)
+ self.inner.borrow_mut().set_enabled(enabled)
}
// TODO: in a minor release, rename the following two functions to be `set_as_*`
@@ -127,7 +167,7 @@ impl Submenu {
/// certain other items to the menu.
#[cfg(target_os = "macos")]
pub fn set_as_windows_menu_for_nsapp(&self) {
- self.0.borrow_mut().set_as_windows_menu_for_nsapp()
+ self.inner.borrow_mut().set_as_windows_menu_for_nsapp()
}
/// Set this submenu as the Help menu for the application on macOS.
@@ -138,31 +178,31 @@ impl Submenu {
/// which has a title matching the localized word "Help".
#[cfg(target_os = "macos")]
pub fn set_as_help_menu_for_nsapp(&self) {
- self.0.borrow_mut().set_as_help_menu_for_nsapp()
+ 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.0.borrow().hpopupmenu()
+ self.inner.borrow().hpopupmenu()
}
#[cfg(target_os = "windows")]
fn show_context_menu_for_hwnd(&self, hwnd: isize, position: Option) {
- self.0
+ 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.0.borrow_mut().attach_menu_subclass_for_hwnd(hwnd)
+ self.inner.borrow_mut().attach_menu_subclass_for_hwnd(hwnd)
}
#[cfg(target_os = "windows")]
fn detach_menu_subclass_from_hwnd(&self, hwnd: isize) {
- self.0.borrow_mut().detach_menu_subclass_from_hwnd(hwnd)
+ self.inner.borrow_mut().detach_menu_subclass_from_hwnd(hwnd)
}
#[cfg(target_os = "linux")]
@@ -171,25 +211,25 @@ impl ContextMenu for Submenu {
w: >k::ApplicationWindow,
position: Option,
) {
- self.0
+ self.inner
.borrow_mut()
.show_context_menu_for_gtk_window(w, position)
}
#[cfg(target_os = "linux")]
fn gtk_context_menu(&self) -> gtk::Menu {
- self.0.borrow_mut().gtk_context_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) {
- self.0
+ 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.0.borrow().ns_menu()
+ self.inner.borrow().ns_menu()
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 1e76d1f..c6f25f3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -126,14 +126,17 @@
//! }
//! ```
+use std::{convert::Infallible, str::FromStr};
+
use crossbeam_channel::{unbounded, Receiver, Sender};
use once_cell::sync::{Lazy, OnceCell};
mod about_metadata;
pub mod accelerator;
-pub mod builders;
+mod builders;
mod dpi;
mod error;
+mod icon;
mod items;
mod menu;
mod platform_impl;
@@ -144,14 +147,63 @@ mod util;
extern crate objc;
pub use about_metadata::AboutMetadata;
+pub use builders::*;
pub use dpi::*;
pub use error::*;
+pub use icon::{BadIcon, Icon, NativeIcon};
pub use items::*;
pub use menu::Menu;
-pub mod icon;
+
+/// An unique id that is associated with a menu item.
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
+pub struct MenuId(String);
+
+impl AsRef for MenuId {
+ fn as_ref(&self) -> &str {
+ self.0.as_ref()
+ }
+}
+
+impl From for MenuId {
+ fn from(value: String) -> Self {
+ Self(value)
+ }
+}
+
+impl From<&str> for MenuId {
+ fn from(value: &str) -> Self {
+ Self(value.to_string())
+ }
+}
+
+impl PartialEq<&str> for MenuId {
+ fn eq(&self, other: &&str) -> bool {
+ other == &self.0
+ }
+}
+
+impl PartialEq for MenuId {
+ fn eq(&self, other: &String) -> bool {
+ other == &self.0
+ }
+}
+
+impl PartialEq<&MenuId> for MenuId {
+ fn eq(&self, other: &&MenuId) -> bool {
+ other.0 == self.0
+ }
+}
+
+impl FromStr for MenuId {
+ type Err = Infallible;
+
+ fn from_str(s: &str) -> std::result::Result {
+ Ok(Self(s.to_string()))
+ }
+}
/// An enumeration of all available menu types, useful to match against
-/// the items return from [`Menu::items`] or [`Submenu::items`]
+/// the items returned from [`Menu::items`] or [`Submenu::items`]
#[derive(Clone)]
pub enum MenuItemKind {
MenuItem(MenuItem),
@@ -162,17 +214,6 @@ pub enum MenuItemKind {
}
impl MenuItemKind {
- /// Returns the id associated with this menu entry
- fn id(&self) -> u32 {
- match self {
- MenuItemKind::MenuItem(i) => i.id(),
- MenuItemKind::Submenu(i) => i.id(),
- MenuItemKind::Predefined(i) => i.id(),
- MenuItemKind::Check(i) => i.id(),
- MenuItemKind::Icon(i) => i.id(),
- }
- }
-
/// Casts this item to a [`MenuItem`], and returns `None` if it wasn't.
pub fn as_menuitem(&self) -> Option<&MenuItem> {
match self {
@@ -262,9 +303,7 @@ impl MenuItemKind {
pub unsafe trait IsMenuItem {
fn kind(&self) -> MenuItemKind;
- fn id(&self) -> u32 {
- self.kind().id()
- }
+ fn id(&self) -> &MenuId;
}
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
@@ -332,10 +371,10 @@ pub trait ContextMenu {
}
/// Describes a menu event emitted when a menu item is activated
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone)]
pub struct MenuEvent {
/// Id of the menu item which triggered this event
- pub id: u32,
+ pub id: MenuId,
}
/// A reciever that could be used to listen to menu events.
@@ -347,8 +386,8 @@ static MENU_EVENT_HANDLER: OnceCell