mirror of
https://github.com/italicsjenga/muda.git
synced 2024-12-23 12:01:31 +11:00
refactor: add MenuId
type and related functions (#91)
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
parent
dc8e9d41fd
commit
c777f6606a
|
@ -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<P
|
|||
menu.show_context_menu_for_nsview(window.ns_view() as _, position);
|
||||
}
|
||||
|
||||
fn load_icon(path: &std::path::Path) -> 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")
|
||||
}
|
||||
|
|
|
@ -204,7 +204,7 @@ fn show_context_menu(window: &Window, menu: &dyn ContextMenu, position: Option<P
|
|||
menu.show_context_menu_for_nsview(window.ns_view() as _, position);
|
||||
}
|
||||
|
||||
fn load_icon(path: &std::path::Path) -> 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")
|
||||
}
|
||||
|
|
|
@ -266,7 +266,7 @@ fn show_context_menu(window: &Window, menu: &dyn ContextMenu, position: Option<m
|
|||
menu.show_context_menu_for_nsview(window.ns_view() as _, position);
|
||||
}
|
||||
|
||||
fn load_icon(path: &std::path::Path) -> 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")
|
||||
}
|
||||
|
|
|
@ -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<Accelerator>,
|
||||
id: Option<MenuId>,
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MenuId>,
|
||||
acccelerator: Option<Accelerator>,
|
||||
icon: Option<Icon>,
|
||||
native_icon: Option<NativeIcon>,
|
||||
|
@ -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 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(
|
||||
|
|
|
@ -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<MenuId>,
|
||||
acccelerator: Option<Accelerator>,
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MenuId>,
|
||||
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> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<RefCell<crate::platform_impl::MenuChild>>);
|
||||
pub struct CheckMenuItem {
|
||||
pub(crate) id: Rc<MenuId>,
|
||||
pub(crate) inner: Rc<RefCell<crate::platform_impl::MenuChild>>,
|
||||
}
|
||||
|
||||
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<Accelerator>,
|
||||
) -> 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<I: Into<MenuId>, S: AsRef<str>>(
|
||||
id: I,
|
||||
text: S,
|
||||
enabled: bool,
|
||||
checked: bool,
|
||||
acccelerator: Option<Accelerator>,
|
||||
) -> 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<S: AsRef<str>>(&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<Accelerator>) -> 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<RefCell<crate::platform_impl::MenuChild>>);
|
||||
pub struct IconMenuItem {
|
||||
pub(crate) id: Rc<MenuId>,
|
||||
pub(crate) inner: Rc<RefCell<crate::platform_impl::MenuChild>>,
|
||||
}
|
||||
|
||||
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<Icon>,
|
||||
acccelerator: Option<Accelerator>,
|
||||
) -> 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<I: Into<MenuId>, S: AsRef<str>>(
|
||||
id: I,
|
||||
text: S,
|
||||
enabled: bool,
|
||||
icon: Option<Icon>,
|
||||
acccelerator: Option<Accelerator>,
|
||||
) -> 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<NativeIcon>,
|
||||
acccelerator: Option<Accelerator>,
|
||||
) -> Self {
|
||||
Self(Rc::new(RefCell::new(
|
||||
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<I: Into<MenuId>, S: AsRef<str>>(
|
||||
id: I,
|
||||
text: S,
|
||||
enabled: bool,
|
||||
native_icon: Option<NativeIcon>,
|
||||
acccelerator: Option<Accelerator>,
|
||||
) -> 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<S: AsRef<str>>(&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<Accelerator>) -> 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<Icon>) {
|
||||
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<NativeIcon>) {
|
||||
#[cfg(target_os = "macos")]
|
||||
self.0.borrow_mut().set_native_icon(_icon)
|
||||
self.inner.borrow_mut().set_native_icon(_icon)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<RefCell<crate::platform_impl::MenuChild>>);
|
||||
pub struct MenuItem {
|
||||
pub(crate) id: Rc<MenuId>,
|
||||
pub(crate) inner: Rc<RefCell<crate::platform_impl::MenuChild>>,
|
||||
}
|
||||
|
||||
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<S: AsRef<str>>(text: S, enabled: bool, acccelerator: Option<Accelerator>) -> Self {
|
||||
Self(Rc::new(RefCell::new(crate::platform_impl::MenuChild::new(
|
||||
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<I: Into<MenuId>, S: AsRef<str>>(
|
||||
id: I,
|
||||
text: S,
|
||||
enabled: bool,
|
||||
acccelerator: Option<Accelerator>,
|
||||
) -> 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<S: AsRef<str>>(&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<Accelerator>) -> crate::Result<()> {
|
||||
self.0.borrow_mut().set_accelerator(acccelerator)
|
||||
self.inner.borrow_mut().set_accelerator(acccelerator)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<RefCell<crate::platform_impl::MenuChild>>);
|
||||
pub struct PredefinedMenuItem {
|
||||
pub(crate) id: Rc<MenuId>,
|
||||
pub(crate) inner: Rc<RefCell<crate::platform_impl::MenuChild>>,
|
||||
}
|
||||
|
||||
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<S: AsRef<str>>(item: PredefinedMenuItemType, text: Option<S>) -> Self {
|
||||
Self(Rc::new(RefCell::new(
|
||||
crate::platform_impl::MenuChild::new_predefined(
|
||||
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<S: AsRef<str>>(&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())
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<RefCell<crate::platform_impl::MenuChild>>);
|
||||
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 {
|
||||
|
@ -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<S: AsRef<str>>(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<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.
|
||||
|
@ -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<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) -> 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<MenuItemKind> {
|
||||
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<S: AsRef<str>>(&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<Position>) {
|
||||
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<Position>,
|
||||
) {
|
||||
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<Position>) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
81
src/lib.rs
81
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<str> for MenuId {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> 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<String> 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<Self, Self::Err> {
|
||||
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<Option<MenuEventHandler>> = OnceCell::new();
|
|||
|
||||
impl MenuEvent {
|
||||
/// Returns the id of the menu item which triggered this event
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id
|
||||
pub fn id(&self) -> &MenuId {
|
||||
&self.id
|
||||
}
|
||||
|
||||
/// Gets a reference to the event channel's [`MenuEventReceiver`]
|
||||
|
|
98
src/menu.rs
98
src/menu.rs
|
@ -1,15 +1,18 @@
|
|||
// 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 root menu that can be added to a Window on Windows and Linux
|
||||
/// and used as the app global menu on macOS.
|
||||
#[derive(Clone)]
|
||||
pub struct Menu(Rc<RefCell<crate::platform_impl::Menu>>);
|
||||
pub struct Menu {
|
||||
id: MenuId,
|
||||
inner: Rc<RefCell<crate::platform_impl::Menu>>,
|
||||
}
|
||||
|
||||
impl Default for Menu {
|
||||
fn default() -> Self {
|
||||
|
@ -20,7 +23,20 @@ impl Default for Menu {
|
|||
impl Menu {
|
||||
/// Creates a new menu.
|
||||
pub fn new() -> Self {
|
||||
Self(Rc::new(RefCell::new(crate::platform_impl::Menu::new())))
|
||||
let menu = crate::platform_impl::Menu::new(None);
|
||||
Self {
|
||||
id: menu.id().clone(),
|
||||
inner: Rc::new(RefCell::new(menu)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new menu with the specified id.
|
||||
pub fn with_id<I: Into<MenuId>>(id: I) -> Self {
|
||||
let id = id.into();
|
||||
Self {
|
||||
id: id.clone(),
|
||||
inner: Rc::new(RefCell::new(crate::platform_impl::Menu::new(Some(id)))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new menu with given `items`. It calls [`Menu::new`] and [`Menu::append_items`] internally.
|
||||
|
@ -30,9 +46,19 @@ impl Menu {
|
|||
Ok(menu)
|
||||
}
|
||||
|
||||
/// Creates a new menu with the specified id and given `items`. It calls [`Menu::new`] and [`Menu::append_items`] internally.
|
||||
pub fn with_id_and_items<I: Into<MenuId>>(
|
||||
id: I,
|
||||
items: &[&dyn IsMenuItem],
|
||||
) -> crate::Result<Self> {
|
||||
let menu = Self::with_id(id);
|
||||
menu.append_items(items)?;
|
||||
Ok(menu)
|
||||
}
|
||||
|
||||
/// Returns a unique identifier associated with this menu.
|
||||
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.
|
||||
|
@ -43,7 +69,7 @@ impl Menu {
|
|||
///
|
||||
/// [`Submenu`]: crate::Submenu
|
||||
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 menu. It calls [`Menu::append`] in a loop internally.
|
||||
|
@ -69,7 +95,9 @@ impl Menu {
|
|||
///
|
||||
/// [`Submenu`]: crate::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 menu. It calls [`Menu::insert_items`] with position of `0` internally.
|
||||
|
@ -91,7 +119,7 @@ impl Menu {
|
|||
///
|
||||
/// [`Submenu`]: crate::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))
|
||||
}
|
||||
|
@ -113,12 +141,12 @@ impl Menu {
|
|||
|
||||
/// Remove a menu item from this menu.
|
||||
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 menu.
|
||||
pub fn items(&self) -> Vec<MenuItemKind> {
|
||||
self.0.borrow().items()
|
||||
self.inner.borrow().items()
|
||||
}
|
||||
|
||||
/// Adds this menu to a [`gtk::ApplicationWindow`]
|
||||
|
@ -149,7 +177,9 @@ impl Menu {
|
|||
C: gtk::prelude::IsA<gtk::Container>,
|
||||
C: gtk::prelude::IsA<gtk::Box>,
|
||||
{
|
||||
self.0.borrow_mut().init_for_gtk_window(window, container)
|
||||
self.inner
|
||||
.borrow_mut()
|
||||
.init_for_gtk_window(window, container)
|
||||
}
|
||||
|
||||
/// Adds this menu to a win32 window.
|
||||
|
@ -178,7 +208,7 @@ impl Menu {
|
|||
/// ```
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn init_for_hwnd(&self, hwnd: isize) -> crate::Result<()> {
|
||||
self.0.borrow_mut().init_for_hwnd(hwnd)
|
||||
self.inner.borrow_mut().init_for_hwnd(hwnd)
|
||||
}
|
||||
|
||||
/// Returns The [`HACCEL`](windows_sys::Win32::UI::WindowsAndMessaging::HACCEL) associated with this menu
|
||||
|
@ -186,7 +216,7 @@ impl Menu {
|
|||
/// in the event loop to enable accelerators
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn haccel(&self) -> windows_sys::Win32::UI::WindowsAndMessaging::HACCEL {
|
||||
self.0.borrow_mut().haccel()
|
||||
self.inner.borrow_mut().haccel()
|
||||
}
|
||||
|
||||
/// Removes this menu from a [`gtk::ApplicationWindow`]
|
||||
|
@ -196,13 +226,13 @@ impl Menu {
|
|||
W: gtk::prelude::IsA<gtk::ApplicationWindow>,
|
||||
W: gtk::prelude::IsA<gtk::Window>,
|
||||
{
|
||||
self.0.borrow_mut().remove_for_gtk_window(window)
|
||||
self.inner.borrow_mut().remove_for_gtk_window(window)
|
||||
}
|
||||
|
||||
/// Removes this menu from a win32 window
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn remove_for_hwnd(&self, hwnd: isize) -> crate::Result<()> {
|
||||
self.0.borrow_mut().remove_for_hwnd(hwnd)
|
||||
self.inner.borrow_mut().remove_for_hwnd(hwnd)
|
||||
}
|
||||
|
||||
/// Hides this menu from a [`gtk::ApplicationWindow`]
|
||||
|
@ -211,13 +241,13 @@ impl Menu {
|
|||
where
|
||||
W: gtk::prelude::IsA<gtk::ApplicationWindow>,
|
||||
{
|
||||
self.0.borrow_mut().hide_for_gtk_window(window)
|
||||
self.inner.borrow_mut().hide_for_gtk_window(window)
|
||||
}
|
||||
|
||||
/// Hides this menu from a win32 window
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn hide_for_hwnd(&self, hwnd: isize) -> crate::Result<()> {
|
||||
self.0.borrow().hide_for_hwnd(hwnd)
|
||||
self.inner.borrow().hide_for_hwnd(hwnd)
|
||||
}
|
||||
|
||||
/// Shows this menu on a [`gtk::ApplicationWindow`]
|
||||
|
@ -226,13 +256,13 @@ impl Menu {
|
|||
where
|
||||
W: gtk::prelude::IsA<gtk::ApplicationWindow>,
|
||||
{
|
||||
self.0.borrow_mut().show_for_gtk_window(window)
|
||||
self.inner.borrow_mut().show_for_gtk_window(window)
|
||||
}
|
||||
|
||||
/// Shows this menu on a win32 window
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn show_for_hwnd(&self, hwnd: isize) -> crate::Result<()> {
|
||||
self.0.borrow().show_for_hwnd(hwnd)
|
||||
self.inner.borrow().show_for_hwnd(hwnd)
|
||||
}
|
||||
|
||||
/// Returns whether this menu visible on a [`gtk::ApplicationWindow`]
|
||||
|
@ -241,7 +271,7 @@ impl Menu {
|
|||
where
|
||||
W: gtk::prelude::IsA<gtk::ApplicationWindow>,
|
||||
{
|
||||
self.0.borrow().is_visible_on_gtk_window(window)
|
||||
self.inner.borrow().is_visible_on_gtk_window(window)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
|
@ -251,47 +281,49 @@ impl Menu {
|
|||
where
|
||||
W: gtk::prelude::IsA<gtk::ApplicationWindow>,
|
||||
{
|
||||
self.0.borrow().gtk_menubar_for_gtk_window(window)
|
||||
self.inner.borrow().gtk_menubar_for_gtk_window(window)
|
||||
}
|
||||
|
||||
/// Returns whether this menu visible on a on a win32 window
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn is_visible_on_hwnd(&self, hwnd: isize) -> bool {
|
||||
self.0.borrow().is_visible_on_hwnd(hwnd)
|
||||
self.inner.borrow().is_visible_on_hwnd(hwnd)
|
||||
}
|
||||
|
||||
/// Adds this menu to an NSApp.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn init_for_nsapp(&self) {
|
||||
self.0.borrow_mut().init_for_nsapp()
|
||||
self.inner.borrow_mut().init_for_nsapp()
|
||||
}
|
||||
|
||||
/// Removes this menu from an NSApp.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn remove_for_nsapp(&self) {
|
||||
self.0.borrow_mut().remove_for_nsapp()
|
||||
self.inner.borrow_mut().remove_for_nsapp()
|
||||
}
|
||||
}
|
||||
|
||||
impl ContextMenu for Menu {
|
||||
#[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<Position>) {
|
||||
self.0.borrow().show_context_menu_for_hwnd(hwnd, position)
|
||||
self.inner
|
||||
.borrow()
|
||||
.show_context_menu_for_hwnd(hwnd, position)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) {
|
||||
self.0.borrow().attach_menu_subclass_for_hwnd(hwnd)
|
||||
self.inner.borrow().attach_menu_subclass_for_hwnd(hwnd)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn detach_menu_subclass_from_hwnd(&self, hwnd: isize) {
|
||||
self.0.borrow().detach_menu_subclass_from_hwnd(hwnd)
|
||||
self.inner.borrow().detach_menu_subclass_from_hwnd(hwnd)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
|
@ -300,25 +332,25 @@ impl ContextMenu for Menu {
|
|||
window: >k::ApplicationWindow,
|
||||
position: Option<Position>,
|
||||
) {
|
||||
self.0
|
||||
self.inner
|
||||
.borrow_mut()
|
||||
.show_context_menu_for_gtk_window(window, 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<Position>) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::{
|
|||
icon::{Icon, NativeIcon},
|
||||
items::*,
|
||||
util::{AddOp, Counter},
|
||||
IsMenuItem, MenuEvent, MenuItemKind, MenuItemType, Position,
|
||||
IsMenuItem, MenuEvent, MenuId, MenuItemKind, MenuItemType, Position,
|
||||
};
|
||||
use accelerator::{from_gtk_mnemonic, parse_accelerator, to_gtk_mnemonic};
|
||||
use gtk::{prelude::*, Orientation};
|
||||
|
@ -53,7 +53,7 @@ macro_rules! return_if_predefined_item_not_supported {
|
|||
}
|
||||
|
||||
pub struct Menu {
|
||||
id: u32,
|
||||
id: MenuId,
|
||||
children: Vec<Rc<RefCell<MenuChild>>>,
|
||||
gtk_menubars: HashMap<u32, gtk::MenuBar>,
|
||||
accel_group: Option<gtk::AccelGroup>,
|
||||
|
@ -61,9 +61,9 @@ pub struct Menu {
|
|||
}
|
||||
|
||||
impl Menu {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(id: Option<MenuId>) -> Self {
|
||||
Self {
|
||||
id: COUNTER.next(),
|
||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||
children: Vec::new(),
|
||||
gtk_menubars: HashMap::new(),
|
||||
accel_group: None,
|
||||
|
@ -71,8 +71,8 @@ impl Menu {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id
|
||||
pub fn id(&self) -> &MenuId {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn add_menu_item(&mut self, item: &dyn crate::IsMenuItem, op: AddOp) -> crate::Result<()> {
|
||||
|
@ -157,11 +157,12 @@ impl Menu {
|
|||
};
|
||||
|
||||
if let MenuItemKind::Submenu(i) = item.kind() {
|
||||
let gtk_menus = i.0.borrow().gtk_menus.clone();
|
||||
let gtk_menus = i.inner.borrow().gtk_menus.clone();
|
||||
|
||||
for (menu_id, _) in gtk_menus {
|
||||
for item in i.items() {
|
||||
i.0.borrow_mut()
|
||||
i.inner
|
||||
.borrow_mut()
|
||||
.remove_inner(item.as_ref(), false, Some(menu_id))?;
|
||||
}
|
||||
}
|
||||
|
@ -355,7 +356,7 @@ pub struct MenuChild {
|
|||
item_type: MenuItemType,
|
||||
text: String,
|
||||
enabled: bool,
|
||||
id: u32,
|
||||
id: MenuId,
|
||||
|
||||
gtk_menu_items: Rc<RefCell<HashMap<u32, Vec<gtk::MenuItem>>>>,
|
||||
|
||||
|
@ -382,23 +383,28 @@ pub struct MenuChild {
|
|||
|
||||
/// Constructors
|
||||
impl MenuChild {
|
||||
pub fn new(text: &str, enabled: bool, accelerator: Option<Accelerator>) -> Self {
|
||||
pub fn new(
|
||||
text: &str,
|
||||
enabled: bool,
|
||||
accelerator: Option<Accelerator>,
|
||||
id: Option<MenuId>,
|
||||
) -> Self {
|
||||
Self {
|
||||
text: text.to_string(),
|
||||
enabled,
|
||||
accelerator,
|
||||
id: COUNTER.next(),
|
||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||
item_type: MenuItemType::MenuItem,
|
||||
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_submenu(text: &str, enabled: bool) -> Self {
|
||||
pub fn new_submenu(text: &str, enabled: bool, id: Option<MenuId>) -> Self {
|
||||
Self {
|
||||
text: text.to_string(),
|
||||
enabled,
|
||||
id: COUNTER.next(),
|
||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||
children: Some(Vec::new()),
|
||||
item_type: MenuItemType::Submenu,
|
||||
gtk_menu: (COUNTER.next(), None),
|
||||
|
@ -413,7 +419,7 @@ impl MenuChild {
|
|||
text: text.unwrap_or_else(|| item_type.text().to_string()),
|
||||
enabled: true,
|
||||
accelerator: item_type.accelerator(),
|
||||
id: COUNTER.next(),
|
||||
id: MenuId(COUNTER.next().to_string()),
|
||||
item_type: MenuItemType::Predefined,
|
||||
predefined_item_type: item_type,
|
||||
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
||||
|
@ -426,13 +432,14 @@ impl MenuChild {
|
|||
enabled: bool,
|
||||
checked: bool,
|
||||
accelerator: Option<Accelerator>,
|
||||
id: Option<MenuId>,
|
||||
) -> Self {
|
||||
Self {
|
||||
text: text.to_string(),
|
||||
enabled,
|
||||
checked: Rc::new(AtomicBool::new(checked)),
|
||||
accelerator,
|
||||
id: COUNTER.next(),
|
||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||
item_type: MenuItemType::Check,
|
||||
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
||||
is_syncing_checked_state: Rc::new(AtomicBool::new(false)),
|
||||
|
@ -445,13 +452,14 @@ impl MenuChild {
|
|||
enabled: bool,
|
||||
icon: Option<Icon>,
|
||||
accelerator: Option<Accelerator>,
|
||||
id: Option<MenuId>,
|
||||
) -> Self {
|
||||
Self {
|
||||
text: text.to_string(),
|
||||
enabled,
|
||||
icon,
|
||||
accelerator,
|
||||
id: COUNTER.next(),
|
||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||
item_type: MenuItemType::Icon,
|
||||
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
||||
is_syncing_checked_state: Rc::new(AtomicBool::new(false)),
|
||||
|
@ -464,12 +472,13 @@ impl MenuChild {
|
|||
enabled: bool,
|
||||
_native_icon: Option<NativeIcon>,
|
||||
accelerator: Option<Accelerator>,
|
||||
id: Option<MenuId>,
|
||||
) -> Self {
|
||||
Self {
|
||||
text: text.to_string(),
|
||||
enabled,
|
||||
accelerator,
|
||||
id: COUNTER.next(),
|
||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||
item_type: MenuItemType::Icon,
|
||||
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
||||
is_syncing_checked_state: Rc::new(AtomicBool::new(false)),
|
||||
|
@ -484,8 +493,8 @@ impl MenuChild {
|
|||
self.item_type
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id
|
||||
pub fn id(&self) -> &MenuId {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn text(&self) -> String {
|
||||
|
@ -715,11 +724,12 @@ impl MenuChild {
|
|||
};
|
||||
|
||||
if let MenuItemKind::Submenu(i) = item.kind() {
|
||||
let gtk_menus = i.0.borrow().gtk_menus.clone();
|
||||
let gtk_menus = i.inner.borrow().gtk_menus.clone();
|
||||
|
||||
for (menu_id, _) in gtk_menus {
|
||||
for item in i.items() {
|
||||
i.0.borrow_mut()
|
||||
i.inner
|
||||
.borrow_mut()
|
||||
.remove_inner(item.as_ref(), false, Some(menu_id))?;
|
||||
}
|
||||
}
|
||||
|
@ -883,9 +893,9 @@ impl MenuChild {
|
|||
|
||||
register_accel!(self, item, accel_group);
|
||||
|
||||
let id = self.id;
|
||||
let id = self.id.clone();
|
||||
item.connect_activate(move |_| {
|
||||
MenuEvent::send(crate::MenuEvent { id });
|
||||
MenuEvent::send(crate::MenuEvent { id: id.clone() });
|
||||
});
|
||||
|
||||
if add_to_cache {
|
||||
|
@ -1035,7 +1045,7 @@ impl MenuChild {
|
|||
|
||||
register_accel!(self, item, accel_group);
|
||||
|
||||
let id = self.id;
|
||||
let id = self.id.clone();
|
||||
let is_syncing_checked_state = self.is_syncing_checked_state.clone();
|
||||
let checked = self.checked.clone();
|
||||
let store = self.gtk_menu_items.clone();
|
||||
|
@ -1058,7 +1068,7 @@ impl MenuChild {
|
|||
|
||||
is_syncing_checked_state.store(false, Ordering::Release);
|
||||
|
||||
MenuEvent::send(crate::MenuEvent { id });
|
||||
MenuEvent::send(crate::MenuEvent { id: id.clone() });
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1116,9 +1126,9 @@ impl MenuChild {
|
|||
|
||||
register_accel!(self, item, accel_group);
|
||||
|
||||
let id = self.id;
|
||||
let id = self.id.clone();
|
||||
item.connect_activate(move |_| {
|
||||
MenuEvent::send(crate::MenuEvent { id });
|
||||
MenuEvent::send(crate::MenuEvent { id: id.clone() });
|
||||
});
|
||||
|
||||
if add_to_cache {
|
||||
|
|
|
@ -28,7 +28,7 @@ use crate::{
|
|||
icon::{Icon, NativeIcon},
|
||||
items::*,
|
||||
util::{AddOp, Counter},
|
||||
IsMenuItem, LogicalPosition, MenuEvent, MenuItemKind, MenuItemType, Position,
|
||||
IsMenuItem, LogicalPosition, MenuEvent, MenuId, MenuItemKind, MenuItemType, Position,
|
||||
};
|
||||
|
||||
static COUNTER: Counter = Counter::new();
|
||||
|
@ -49,7 +49,7 @@ const NSAboutPanelOptionCopyright: &str = "Copyright";
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Menu {
|
||||
id: u32,
|
||||
id: MenuId,
|
||||
ns_menu: id,
|
||||
children: Rc<RefCell<Vec<Rc<RefCell<MenuChild>>>>>,
|
||||
}
|
||||
|
@ -63,9 +63,9 @@ impl Drop for Menu {
|
|||
}
|
||||
|
||||
impl Menu {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(id: Option<MenuId>) -> Self {
|
||||
Self {
|
||||
id: COUNTER.next(),
|
||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||
ns_menu: unsafe {
|
||||
let ns_menu = NSMenu::new(nil);
|
||||
ns_menu.setAutoenablesItems(NO);
|
||||
|
@ -76,12 +76,12 @@ impl Menu {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id
|
||||
pub fn id(&self) -> &MenuId {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn add_menu_item(&mut self, item: &dyn crate::IsMenuItem, op: AddOp) -> crate::Result<()> {
|
||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(self.id)?;
|
||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(&self.id)?;
|
||||
let child = item.child();
|
||||
|
||||
unsafe {
|
||||
|
@ -103,11 +103,11 @@ impl Menu {
|
|||
pub fn remove(&self, item: &dyn crate::IsMenuItem) -> crate::Result<()> {
|
||||
// get a list of instances of the specified `NSMenuItem` in this menu
|
||||
let child = match item.kind() {
|
||||
MenuItemKind::Submenu(i) => i.0.clone(),
|
||||
MenuItemKind::MenuItem(i) => i.0.clone(),
|
||||
MenuItemKind::Predefined(i) => i.0.clone(),
|
||||
MenuItemKind::Check(i) => i.0.clone(),
|
||||
MenuItemKind::Icon(i) => i.0.clone(),
|
||||
MenuItemKind::Submenu(i) => i.inner.clone(),
|
||||
MenuItemKind::MenuItem(i) => i.inner.clone(),
|
||||
MenuItemKind::Predefined(i) => i.inner.clone(),
|
||||
MenuItemKind::Check(i) => i.inner.clone(),
|
||||
MenuItemKind::Icon(i) => i.inner.clone(),
|
||||
};
|
||||
let mut child_ = child.borrow_mut();
|
||||
if let Some(ns_menu_items) = child_.ns_menu_items.remove(&self.id) {
|
||||
|
@ -160,11 +160,11 @@ impl Menu {
|
|||
pub struct MenuChild {
|
||||
// shared fields between submenus and menu items
|
||||
item_type: MenuItemType,
|
||||
id: u32,
|
||||
id: MenuId,
|
||||
text: String,
|
||||
enabled: bool,
|
||||
|
||||
ns_menu_items: HashMap<u32, Vec<id>>,
|
||||
ns_menu_items: HashMap<MenuId, Vec<id>>,
|
||||
|
||||
// menu item fields
|
||||
accelerator: Option<Accelerator>,
|
||||
|
@ -181,12 +181,12 @@ pub struct MenuChild {
|
|||
|
||||
// submenu fields
|
||||
pub children: Option<Vec<Rc<RefCell<MenuChild>>>>,
|
||||
ns_menus: HashMap<u32, Vec<id>>,
|
||||
ns_menus: HashMap<MenuId, Vec<id>>,
|
||||
ns_menu: NsMenuRef,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NsMenuRef(u32, id);
|
||||
struct NsMenuRef(MenuId, id);
|
||||
|
||||
impl Drop for NsMenuRef {
|
||||
fn drop(&mut self) {
|
||||
|
@ -211,31 +211,37 @@ impl Default for MenuChild {
|
|||
native_icon: Default::default(),
|
||||
children: Default::default(),
|
||||
ns_menus: Default::default(),
|
||||
ns_menu: NsMenuRef(0, 0 as _),
|
||||
ns_menu: NsMenuRef(MenuId(COUNTER.next().to_string()), 0 as _),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructors
|
||||
impl MenuChild {
|
||||
pub fn new(text: &str, enabled: bool, accelerator: Option<Accelerator>) -> Self {
|
||||
pub fn new(
|
||||
text: &str,
|
||||
enabled: bool,
|
||||
accelerator: Option<Accelerator>,
|
||||
id: Option<MenuId>,
|
||||
) -> Self {
|
||||
Self {
|
||||
item_type: MenuItemType::MenuItem,
|
||||
text: strip_mnemonic(text),
|
||||
enabled,
|
||||
id: COUNTER.next(),
|
||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||
accelerator,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_submenu(text: &str, enabled: bool) -> Self {
|
||||
pub fn new_submenu(text: &str, enabled: bool, id: Option<MenuId>) -> Self {
|
||||
Self {
|
||||
item_type: MenuItemType::Submenu,
|
||||
text: strip_mnemonic(text),
|
||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||
enabled,
|
||||
children: Some(Vec::new()),
|
||||
ns_menu: NsMenuRef(COUNTER.next(), unsafe {
|
||||
ns_menu: NsMenuRef(MenuId(COUNTER.next().to_string()), unsafe {
|
||||
let menu = NSMenu::new(nil);
|
||||
let _: () = msg_send![menu, retain];
|
||||
menu
|
||||
|
@ -271,7 +277,7 @@ impl MenuChild {
|
|||
item_type: MenuItemType::Predefined,
|
||||
text,
|
||||
enabled: true,
|
||||
id: COUNTER.next(),
|
||||
id: MenuId(COUNTER.next().to_string()),
|
||||
accelerator,
|
||||
predefined_item_type: item_type,
|
||||
// ns_menu_item,
|
||||
|
@ -284,12 +290,13 @@ impl MenuChild {
|
|||
enabled: bool,
|
||||
checked: bool,
|
||||
accelerator: Option<Accelerator>,
|
||||
id: Option<MenuId>,
|
||||
) -> Self {
|
||||
Self {
|
||||
item_type: MenuItemType::Check,
|
||||
text: text.to_string(),
|
||||
enabled,
|
||||
id: COUNTER.next(),
|
||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||
accelerator,
|
||||
checked,
|
||||
..Default::default()
|
||||
|
@ -301,12 +308,13 @@ impl MenuChild {
|
|||
enabled: bool,
|
||||
icon: Option<Icon>,
|
||||
accelerator: Option<Accelerator>,
|
||||
id: Option<MenuId>,
|
||||
) -> Self {
|
||||
Self {
|
||||
item_type: MenuItemType::Icon,
|
||||
text: text.to_string(),
|
||||
enabled,
|
||||
id: COUNTER.next(),
|
||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||
icon,
|
||||
accelerator,
|
||||
..Default::default()
|
||||
|
@ -318,12 +326,13 @@ impl MenuChild {
|
|||
enabled: bool,
|
||||
native_icon: Option<NativeIcon>,
|
||||
accelerator: Option<Accelerator>,
|
||||
id: Option<MenuId>,
|
||||
) -> Self {
|
||||
Self {
|
||||
item_type: MenuItemType::Icon,
|
||||
text: text.to_string(),
|
||||
enabled,
|
||||
id: COUNTER.next(),
|
||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||
native_icon,
|
||||
accelerator,
|
||||
..Default::default()
|
||||
|
@ -337,8 +346,8 @@ impl MenuChild {
|
|||
self.item_type
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id
|
||||
pub fn id(&self) -> &MenuId {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn text(&self) -> String {
|
||||
|
@ -461,12 +470,12 @@ impl MenuChild {
|
|||
AddOp::Append => {
|
||||
for menus in self.ns_menus.values() {
|
||||
for ns_menu in menus {
|
||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(self.id)?;
|
||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(&self.id)?;
|
||||
ns_menu.addItem_(ns_menu_item);
|
||||
}
|
||||
}
|
||||
|
||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(self.ns_menu.0)?;
|
||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(&self.ns_menu.0)?;
|
||||
self.ns_menu.1.addItem_(ns_menu_item);
|
||||
|
||||
self.children.as_mut().unwrap().push(child);
|
||||
|
@ -474,12 +483,12 @@ impl MenuChild {
|
|||
AddOp::Insert(position) => {
|
||||
for menus in self.ns_menus.values() {
|
||||
for &ns_menu in menus {
|
||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(self.id)?;
|
||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(&self.id)?;
|
||||
let () = msg_send![ns_menu, insertItem: ns_menu_item atIndex: position as NSInteger];
|
||||
}
|
||||
}
|
||||
|
||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(self.ns_menu.0)?;
|
||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(&self.ns_menu.0)?;
|
||||
let () = msg_send![ self.ns_menu.1, insertItem: ns_menu_item atIndex: position as NSInteger];
|
||||
|
||||
self.children.as_mut().unwrap().insert(position, child);
|
||||
|
@ -556,7 +565,7 @@ impl MenuChild {
|
|||
|
||||
/// NSMenuItem item creation methods
|
||||
impl MenuChild {
|
||||
pub fn create_ns_item_for_submenu(&mut self, menu_id: u32) -> crate::Result<id> {
|
||||
pub fn create_ns_item_for_submenu(&mut self, menu_id: &MenuId) -> crate::Result<id> {
|
||||
let ns_menu_item: *mut Object;
|
||||
let ns_submenu: *mut Object;
|
||||
|
||||
|
@ -580,19 +589,19 @@ impl MenuChild {
|
|||
}
|
||||
|
||||
self.ns_menus
|
||||
.entry(menu_id)
|
||||
.entry(menu_id.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(ns_submenu);
|
||||
|
||||
self.ns_menu_items
|
||||
.entry(menu_id)
|
||||
.entry(menu_id.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(ns_menu_item);
|
||||
|
||||
Ok(ns_menu_item)
|
||||
}
|
||||
|
||||
pub fn create_ns_item_for_menu_item(&mut self, menu_id: u32) -> crate::Result<id> {
|
||||
pub fn create_ns_item_for_menu_item(&mut self, menu_id: &MenuId) -> crate::Result<id> {
|
||||
let ns_menu_item = create_ns_menu_item(
|
||||
&self.text,
|
||||
Some(sel!(fireMenuItemAction:)),
|
||||
|
@ -601,7 +610,6 @@ impl MenuChild {
|
|||
|
||||
unsafe {
|
||||
let _: () = msg_send![ns_menu_item, setTarget: ns_menu_item];
|
||||
let _: () = msg_send![ns_menu_item, setTag:self.id()];
|
||||
|
||||
// Store a raw pointer to the `MenuChild` as an instance variable on the native menu item
|
||||
let ptr = Box::into_raw(Box::new(&*self));
|
||||
|
@ -613,14 +621,17 @@ impl MenuChild {
|
|||
}
|
||||
|
||||
self.ns_menu_items
|
||||
.entry(menu_id)
|
||||
.entry(menu_id.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(ns_menu_item);
|
||||
|
||||
Ok(ns_menu_item)
|
||||
}
|
||||
|
||||
pub fn create_ns_item_for_predefined_menu_item(&mut self, menu_id: u32) -> crate::Result<id> {
|
||||
pub fn create_ns_item_for_predefined_menu_item(
|
||||
&mut self,
|
||||
menu_id: &MenuId,
|
||||
) -> crate::Result<id> {
|
||||
let item_type = &self.predefined_item_type;
|
||||
let ns_menu_item = match item_type {
|
||||
PredefinedMenuItemType::Separator => unsafe {
|
||||
|
@ -632,7 +643,6 @@ impl MenuChild {
|
|||
if let PredefinedMenuItemType::About(_) = self.predefined_item_type {
|
||||
unsafe {
|
||||
let _: () = msg_send![ns_menu_item, setTarget: ns_menu_item];
|
||||
let _: () = msg_send![ns_menu_item, setTag:self.id()];
|
||||
|
||||
// Store a raw pointer to the `MenuChild` as an instance variable on the native menu item
|
||||
let ptr = Box::into_raw(Box::new(&*self));
|
||||
|
@ -653,14 +663,14 @@ impl MenuChild {
|
|||
}
|
||||
|
||||
self.ns_menu_items
|
||||
.entry(menu_id)
|
||||
.entry(menu_id.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(ns_menu_item);
|
||||
|
||||
Ok(ns_menu_item)
|
||||
}
|
||||
|
||||
pub fn create_ns_item_for_check_menu_item(&mut self, menu_id: u32) -> crate::Result<id> {
|
||||
pub fn create_ns_item_for_check_menu_item(&mut self, menu_id: &MenuId) -> crate::Result<id> {
|
||||
let ns_menu_item = create_ns_menu_item(
|
||||
&self.text,
|
||||
Some(sel!(fireMenuItemAction:)),
|
||||
|
@ -669,7 +679,6 @@ impl MenuChild {
|
|||
|
||||
unsafe {
|
||||
let _: () = msg_send![ns_menu_item, setTarget: ns_menu_item];
|
||||
let _: () = msg_send![ns_menu_item, setTag:self.id()];
|
||||
|
||||
// Store a raw pointer to the `MenuChild` as an instance variable on the native menu item
|
||||
let ptr = Box::into_raw(Box::new(&*self));
|
||||
|
@ -684,14 +693,14 @@ impl MenuChild {
|
|||
}
|
||||
|
||||
self.ns_menu_items
|
||||
.entry(menu_id)
|
||||
.entry(menu_id.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(ns_menu_item);
|
||||
|
||||
Ok(ns_menu_item)
|
||||
}
|
||||
|
||||
pub fn create_ns_item_for_icon_menu_item(&mut self, menu_id: u32) -> crate::Result<id> {
|
||||
pub fn create_ns_item_for_icon_menu_item(&mut self, menu_id: &MenuId) -> crate::Result<id> {
|
||||
let ns_menu_item = create_ns_menu_item(
|
||||
&self.text,
|
||||
Some(sel!(fireMenuItemAction:)),
|
||||
|
@ -700,7 +709,6 @@ impl MenuChild {
|
|||
|
||||
unsafe {
|
||||
let _: () = msg_send![ns_menu_item, setTarget: ns_menu_item];
|
||||
let _: () = msg_send![ns_menu_item, setTag:self.id()];
|
||||
|
||||
// Store a raw pointer to the `MenuChild` as an instance variable on the native menu item
|
||||
let ptr = Box::into_raw(Box::new(&*self));
|
||||
|
@ -718,14 +726,14 @@ impl MenuChild {
|
|||
}
|
||||
|
||||
self.ns_menu_items
|
||||
.entry(menu_id)
|
||||
.entry(menu_id.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(ns_menu_item);
|
||||
|
||||
Ok(ns_menu_item)
|
||||
}
|
||||
|
||||
fn make_ns_item_for_menu(&mut self, menu_id: u32) -> crate::Result<*mut Object> {
|
||||
fn make_ns_item_for_menu(&mut self, menu_id: &MenuId) -> crate::Result<*mut Object> {
|
||||
match self.item_type {
|
||||
MenuItemType::Submenu => self.create_ns_item_for_submenu(menu_id),
|
||||
MenuItemType::MenuItem => self.create_ns_item_for_menu_item(menu_id),
|
||||
|
@ -763,16 +771,22 @@ impl PredefinedMenuItemType {
|
|||
}
|
||||
|
||||
impl dyn IsMenuItem + '_ {
|
||||
fn make_ns_item_for_menu(&self, menu_id: u32) -> crate::Result<*mut Object> {
|
||||
fn make_ns_item_for_menu(&self, menu_id: &MenuId) -> crate::Result<*mut Object> {
|
||||
match self.kind() {
|
||||
MenuItemKind::Submenu(i) => i.0.borrow_mut().create_ns_item_for_submenu(menu_id),
|
||||
MenuItemKind::MenuItem(i) => i.0.borrow_mut().create_ns_item_for_menu_item(menu_id),
|
||||
MenuItemKind::Predefined(i) => {
|
||||
i.0.borrow_mut()
|
||||
.create_ns_item_for_predefined_menu_item(menu_id)
|
||||
}
|
||||
MenuItemKind::Check(i) => i.0.borrow_mut().create_ns_item_for_check_menu_item(menu_id),
|
||||
MenuItemKind::Icon(i) => i.0.borrow_mut().create_ns_item_for_icon_menu_item(menu_id),
|
||||
MenuItemKind::Submenu(i) => i.inner.borrow_mut().create_ns_item_for_submenu(menu_id),
|
||||
MenuItemKind::MenuItem(i) => i.inner.borrow_mut().create_ns_item_for_menu_item(menu_id),
|
||||
MenuItemKind::Predefined(i) => i
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.create_ns_item_for_predefined_menu_item(menu_id),
|
||||
MenuItemKind::Check(i) => i
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.create_ns_item_for_check_menu_item(menu_id),
|
||||
MenuItemKind::Icon(i) => i
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.create_ns_item_for_icon_menu_item(menu_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -816,8 +830,6 @@ extern "C" fn dealloc_custom_menuitem(this: &Object, _: Sel) {
|
|||
|
||||
extern "C" fn fire_menu_item_click(this: &Object, _: Sel, _item: id) {
|
||||
unsafe {
|
||||
let id: u32 = msg_send![this, tag];
|
||||
|
||||
// Create a reference to the `MenuChild` from the raw pointer
|
||||
// stored as an instance variable on the native menu item
|
||||
let ptr: usize = *this.get_ivar(BLOCK_PTR);
|
||||
|
@ -884,6 +896,7 @@ extern "C" fn fire_menu_item_click(this: &Object, _: Sel, _item: id) {
|
|||
(*item).set_checked(!(*item).is_checked());
|
||||
}
|
||||
|
||||
let id = (*item).id().clone();
|
||||
MenuEvent::send(crate::MenuEvent { id });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,13 +24,12 @@ pub(crate) use self::platform::*;
|
|||
impl dyn IsMenuItem + '_ {
|
||||
fn child(&self) -> Rc<RefCell<MenuChild>> {
|
||||
match self.kind() {
|
||||
MenuItemKind::MenuItem(i) => i.0,
|
||||
MenuItemKind::Submenu(i) => i.0,
|
||||
MenuItemKind::Predefined(i) => i.0,
|
||||
MenuItemKind::Check(i) => i.0,
|
||||
MenuItemKind::Icon(i) => i.0,
|
||||
MenuItemKind::MenuItem(i) => i.inner,
|
||||
MenuItemKind::Submenu(i) => i.inner,
|
||||
MenuItemKind::Predefined(i) => i.inner,
|
||||
MenuItemKind::Check(i) => i.inner,
|
||||
MenuItemKind::Icon(i) => i.inner,
|
||||
}
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,11 +37,41 @@ impl dyn IsMenuItem + '_ {
|
|||
impl MenuChild {
|
||||
fn kind(&self, c: Rc<RefCell<MenuChild>>) -> MenuItemKind {
|
||||
match self.item_type() {
|
||||
MenuItemType::Submenu => MenuItemKind::Submenu(Submenu(c)),
|
||||
MenuItemType::MenuItem => MenuItemKind::MenuItem(MenuItem(c)),
|
||||
MenuItemType::Predefined => MenuItemKind::Predefined(PredefinedMenuItem(c)),
|
||||
MenuItemType::Check => MenuItemKind::Check(CheckMenuItem(c)),
|
||||
MenuItemType::Icon => MenuItemKind::Icon(IconMenuItem(c)),
|
||||
MenuItemType::Submenu => {
|
||||
let id = c.borrow().id().clone();
|
||||
MenuItemKind::Submenu(Submenu {
|
||||
id: Rc::new(id),
|
||||
inner: c,
|
||||
})
|
||||
}
|
||||
MenuItemType::MenuItem => {
|
||||
let id = c.borrow().id().clone();
|
||||
MenuItemKind::MenuItem(MenuItem {
|
||||
id: Rc::new(id),
|
||||
inner: c,
|
||||
})
|
||||
}
|
||||
MenuItemType::Predefined => {
|
||||
let id = c.borrow().id().clone();
|
||||
MenuItemKind::Predefined(PredefinedMenuItem {
|
||||
id: Rc::new(id),
|
||||
inner: c,
|
||||
})
|
||||
}
|
||||
MenuItemType::Check => {
|
||||
let id = c.borrow().id().clone();
|
||||
MenuItemKind::Check(CheckMenuItem {
|
||||
id: Rc::new(id),
|
||||
inner: c,
|
||||
})
|
||||
}
|
||||
MenuItemType::Icon => {
|
||||
let id = c.borrow().id().clone();
|
||||
MenuItemKind::Icon(IconMenuItem {
|
||||
id: Rc::new(id),
|
||||
inner: c,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,21 +90,21 @@ impl MenuItemKind {
|
|||
|
||||
pub(crate) fn child(&self) -> Ref<MenuChild> {
|
||||
match self {
|
||||
MenuItemKind::MenuItem(i) => i.0.borrow(),
|
||||
MenuItemKind::Submenu(i) => i.0.borrow(),
|
||||
MenuItemKind::Predefined(i) => i.0.borrow(),
|
||||
MenuItemKind::Check(i) => i.0.borrow(),
|
||||
MenuItemKind::Icon(i) => i.0.borrow(),
|
||||
MenuItemKind::MenuItem(i) => i.inner.borrow(),
|
||||
MenuItemKind::Submenu(i) => i.inner.borrow(),
|
||||
MenuItemKind::Predefined(i) => i.inner.borrow(),
|
||||
MenuItemKind::Check(i) => i.inner.borrow(),
|
||||
MenuItemKind::Icon(i) => i.inner.borrow(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn child_mut(&self) -> RefMut<MenuChild> {
|
||||
match self {
|
||||
MenuItemKind::MenuItem(i) => i.0.borrow_mut(),
|
||||
MenuItemKind::Submenu(i) => i.0.borrow_mut(),
|
||||
MenuItemKind::Predefined(i) => i.0.borrow_mut(),
|
||||
MenuItemKind::Check(i) => i.0.borrow_mut(),
|
||||
MenuItemKind::Icon(i) => i.0.borrow_mut(),
|
||||
MenuItemKind::MenuItem(i) => i.inner.borrow_mut(),
|
||||
MenuItemKind::Submenu(i) => i.inner.borrow_mut(),
|
||||
MenuItemKind::Predefined(i) => i.inner.borrow_mut(),
|
||||
MenuItemKind::Check(i) => i.inner.borrow_mut(),
|
||||
MenuItemKind::Icon(i) => i.inner.borrow_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::{
|
|||
icon::{Icon, NativeIcon},
|
||||
items::PredefinedMenuItemType,
|
||||
util::{AddOp, Counter},
|
||||
AboutMetadata, IsMenuItem, MenuEvent, MenuItemKind, MenuItemType, Position,
|
||||
AboutMetadata, IsMenuItem, MenuEvent, MenuId, MenuItemKind, MenuItemType, Position,
|
||||
};
|
||||
use std::{
|
||||
cell::{RefCell, RefMut},
|
||||
|
@ -51,15 +51,15 @@ macro_rules! inner_menu_child_and_flags {
|
|||
let child = match $item.kind() {
|
||||
MenuItemKind::Submenu(i) => {
|
||||
flags |= MF_POPUP;
|
||||
i.0.clone()
|
||||
i.inner.clone()
|
||||
}
|
||||
MenuItemKind::MenuItem(i) => {
|
||||
flags |= MF_STRING;
|
||||
i.0.clone()
|
||||
i.inner.clone()
|
||||
}
|
||||
|
||||
MenuItemKind::Predefined(i) => {
|
||||
let child = i.0.clone();
|
||||
let child = i.inner.clone();
|
||||
let child_ = child.borrow();
|
||||
match child_.predefined_item_type {
|
||||
PredefinedMenuItemType::None => return Ok(()),
|
||||
|
@ -74,7 +74,7 @@ macro_rules! inner_menu_child_and_flags {
|
|||
child
|
||||
}
|
||||
MenuItemKind::Check(i) => {
|
||||
let child = i.0.clone();
|
||||
let child = i.inner.clone();
|
||||
flags |= MF_STRING;
|
||||
if child.borrow().checked {
|
||||
flags |= MF_CHECKED;
|
||||
|
@ -83,7 +83,7 @@ macro_rules! inner_menu_child_and_flags {
|
|||
}
|
||||
MenuItemKind::Icon(i) => {
|
||||
flags |= MF_STRING;
|
||||
i.0.clone()
|
||||
i.inner.clone()
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -93,7 +93,7 @@ macro_rules! inner_menu_child_and_flags {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Menu {
|
||||
id: u32,
|
||||
id: MenuId,
|
||||
hmenu: HMENU,
|
||||
hpopupmenu: HMENU,
|
||||
hwnds: Vec<HWND>,
|
||||
|
@ -102,9 +102,9 @@ pub(crate) struct Menu {
|
|||
}
|
||||
|
||||
impl Menu {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(id: Option<MenuId>) -> Self {
|
||||
Self {
|
||||
id: COUNTER.next(),
|
||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||
hmenu: unsafe { CreateMenu() },
|
||||
hpopupmenu: unsafe { CreatePopupMenu() },
|
||||
haccel_store: Rc::new(RefCell::new((0, HashMap::new()))),
|
||||
|
@ -113,8 +113,8 @@ impl Menu {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id
|
||||
pub fn id(&self) -> &MenuId {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn add_menu_item(&mut self, item: &dyn IsMenuItem, op: AddOp) -> crate::Result<()> {
|
||||
|
@ -145,12 +145,12 @@ impl Menu {
|
|||
|
||||
AccelAction::add(
|
||||
&mut self.haccel_store.borrow_mut(),
|
||||
child_.id(),
|
||||
child_.internal_id(),
|
||||
accelerator,
|
||||
)?;
|
||||
}
|
||||
|
||||
let id = child_.id() as usize;
|
||||
let id = child_.internal_id() as usize;
|
||||
|
||||
let text = encode_wide(text);
|
||||
unsafe {
|
||||
|
@ -191,8 +191,8 @@ impl Menu {
|
|||
let info = create_icon_item_info(hbitmap);
|
||||
|
||||
unsafe {
|
||||
SetMenuItemInfoW(self.hmenu, child_.id, false.into(), &info);
|
||||
SetMenuItemInfoW(self.hpopupmenu, child_.id, false.into(), &info);
|
||||
SetMenuItemInfoW(self.hmenu, child_.internal_id, false.into(), &info);
|
||||
SetMenuItemInfoW(self.hpopupmenu, child_.internal_id, false.into(), &info);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -214,9 +214,10 @@ impl Menu {
|
|||
}
|
||||
|
||||
pub fn remove(&mut self, item: &dyn IsMenuItem) -> crate::Result<()> {
|
||||
let id = item.child().borrow().internal_id();
|
||||
unsafe {
|
||||
RemoveMenu(self.hmenu, item.id(), MF_BYCOMMAND);
|
||||
RemoveMenu(self.hpopupmenu, item.id(), MF_BYCOMMAND);
|
||||
RemoveMenu(self.hmenu, id, MF_BYCOMMAND);
|
||||
RemoveMenu(self.hpopupmenu, id, MF_BYCOMMAND);
|
||||
|
||||
for hwnd in &self.hwnds {
|
||||
DrawMenuBar(*hwnd);
|
||||
|
@ -244,7 +245,7 @@ impl Menu {
|
|||
let index = self
|
||||
.children
|
||||
.iter()
|
||||
.position(|e| e.borrow().id() == item.id())
|
||||
.position(|e| e.borrow().internal_id() == id)
|
||||
.ok_or(crate::Error::NotAChildOfThisMenu)?;
|
||||
self.children.remove(index);
|
||||
|
||||
|
@ -373,7 +374,8 @@ pub(crate) struct MenuChild {
|
|||
root_menu_haccel_stores: Option<Vec<Rc<RefCell<AccelWrapper>>>>,
|
||||
|
||||
// menu item fields
|
||||
id: u32,
|
||||
internal_id: u32,
|
||||
id: MenuId,
|
||||
accelerator: Option<Accelerator>,
|
||||
|
||||
// predefined menu item fields
|
||||
|
@ -393,20 +395,28 @@ pub(crate) struct MenuChild {
|
|||
|
||||
/// Constructors
|
||||
impl MenuChild {
|
||||
pub fn new(text: &str, enabled: bool, accelerator: Option<Accelerator>) -> Self {
|
||||
pub fn new(
|
||||
text: &str,
|
||||
enabled: bool,
|
||||
accelerator: Option<Accelerator>,
|
||||
id: Option<MenuId>,
|
||||
) -> Self {
|
||||
let internal_id = COUNTER.next();
|
||||
Self {
|
||||
item_type: MenuItemType::MenuItem,
|
||||
text: text.to_string(),
|
||||
enabled,
|
||||
parents_hemnu: Vec::new(),
|
||||
id: COUNTER.next(),
|
||||
internal_id,
|
||||
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
||||
accelerator,
|
||||
root_menu_haccel_stores: Some(Vec::new()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_submenu(text: &str, enabled: bool) -> Self {
|
||||
pub fn new_submenu(text: &str, enabled: bool, id: Option<MenuId>) -> Self {
|
||||
let internal_id = COUNTER.next();
|
||||
Self {
|
||||
item_type: MenuItemType::Submenu,
|
||||
text: text.to_string(),
|
||||
|
@ -414,6 +424,8 @@ impl MenuChild {
|
|||
parents_hemnu: Vec::new(),
|
||||
children: Some(Vec::new()),
|
||||
hmenu: unsafe { CreateMenu() },
|
||||
internal_id,
|
||||
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
||||
hpopupmenu: unsafe { CreatePopupMenu() },
|
||||
root_menu_haccel_stores: Some(Vec::new()),
|
||||
..Default::default()
|
||||
|
@ -421,12 +433,14 @@ impl MenuChild {
|
|||
}
|
||||
|
||||
pub fn new_predefined(item_type: PredefinedMenuItemType, text: Option<String>) -> Self {
|
||||
let internal_id = COUNTER.next();
|
||||
Self {
|
||||
item_type: MenuItemType::Predefined,
|
||||
text: text.unwrap_or_else(|| item_type.text().to_string()),
|
||||
enabled: true,
|
||||
parents_hemnu: Vec::new(),
|
||||
id: COUNTER.next(),
|
||||
internal_id,
|
||||
id: MenuId(internal_id.to_string()),
|
||||
accelerator: item_type.accelerator(),
|
||||
predefined_item_type: item_type,
|
||||
root_menu_haccel_stores: Some(Vec::new()),
|
||||
|
@ -439,13 +453,16 @@ impl MenuChild {
|
|||
enabled: bool,
|
||||
checked: bool,
|
||||
accelerator: Option<Accelerator>,
|
||||
id: Option<MenuId>,
|
||||
) -> Self {
|
||||
let internal_id = COUNTER.next();
|
||||
Self {
|
||||
item_type: MenuItemType::Check,
|
||||
text: text.to_string(),
|
||||
enabled,
|
||||
parents_hemnu: Vec::new(),
|
||||
id: COUNTER.next(),
|
||||
internal_id,
|
||||
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
||||
accelerator,
|
||||
checked,
|
||||
root_menu_haccel_stores: Some(Vec::new()),
|
||||
|
@ -458,13 +475,16 @@ impl MenuChild {
|
|||
enabled: bool,
|
||||
icon: Option<Icon>,
|
||||
accelerator: Option<Accelerator>,
|
||||
id: Option<MenuId>,
|
||||
) -> Self {
|
||||
let internal_id = COUNTER.next();
|
||||
Self {
|
||||
item_type: MenuItemType::Icon,
|
||||
text: text.to_string(),
|
||||
enabled,
|
||||
parents_hemnu: Vec::new(),
|
||||
id: COUNTER.next(),
|
||||
internal_id,
|
||||
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
||||
accelerator,
|
||||
icon,
|
||||
root_menu_haccel_stores: Some(Vec::new()),
|
||||
|
@ -477,13 +497,16 @@ impl MenuChild {
|
|||
enabled: bool,
|
||||
_native_icon: Option<NativeIcon>,
|
||||
accelerator: Option<Accelerator>,
|
||||
id: Option<MenuId>,
|
||||
) -> Self {
|
||||
let internal_id = COUNTER.next();
|
||||
Self {
|
||||
item_type: MenuItemType::Icon,
|
||||
text: text.to_string(),
|
||||
enabled,
|
||||
parents_hemnu: Vec::new(),
|
||||
id: COUNTER.next(),
|
||||
internal_id,
|
||||
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
||||
accelerator,
|
||||
root_menu_haccel_stores: Some(Vec::new()),
|
||||
..Default::default()
|
||||
|
@ -497,10 +520,14 @@ impl MenuChild {
|
|||
self.item_type
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u32 {
|
||||
pub fn id(&self) -> &MenuId {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn internal_id(&self) -> u32 {
|
||||
match self.item_type() {
|
||||
MenuItemType::Submenu => self.hmenu as u32,
|
||||
_ => self.id,
|
||||
_ => self.internal_id,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -515,12 +542,12 @@ impl MenuChild {
|
|||
info.fMask = MIIM_STRING;
|
||||
info.dwTypeData = label.as_mut_ptr();
|
||||
|
||||
unsafe { GetMenuItemInfoW(*hmenu, self.id(), false.into(), &mut info) };
|
||||
unsafe { GetMenuItemInfoW(*hmenu, self.internal_id(), false.into(), &mut info) };
|
||||
|
||||
info.cch += 1;
|
||||
info.dwTypeData = Vec::with_capacity(info.cch as usize).as_mut_ptr();
|
||||
|
||||
unsafe { GetMenuItemInfoW(*hmenu, self.id(), false.into(), &mut info) };
|
||||
unsafe { GetMenuItemInfoW(*hmenu, self.internal_id(), false.into(), &mut info) };
|
||||
|
||||
let text = decode_wide(info.dwTypeData);
|
||||
text.split('\t').next().unwrap().to_string()
|
||||
|
@ -540,7 +567,7 @@ impl MenuChild {
|
|||
info.fMask = MIIM_STRING;
|
||||
info.dwTypeData = encode_wide(&self.text).as_mut_ptr();
|
||||
|
||||
unsafe { SetMenuItemInfoW(*parent, self.id(), false.into(), &info) };
|
||||
unsafe { SetMenuItemInfoW(*parent, self.internal_id(), false.into(), &info) };
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -552,7 +579,7 @@ impl MenuChild {
|
|||
info.cbSize = std::mem::size_of::<MENUITEMINFOW>() as _;
|
||||
info.fMask = MIIM_STATE;
|
||||
|
||||
unsafe { GetMenuItemInfoW(*hmenu, self.id(), false.into(), &mut info) };
|
||||
unsafe { GetMenuItemInfoW(*hmenu, self.internal_id(), false.into(), &mut info) };
|
||||
|
||||
(info.fState & MFS_DISABLED) == 0
|
||||
})
|
||||
|
@ -565,7 +592,7 @@ impl MenuChild {
|
|||
unsafe {
|
||||
EnableMenuItem(
|
||||
*parent,
|
||||
self.id(),
|
||||
self.internal_id(),
|
||||
if enabled { MF_ENABLED } else { MF_DISABLED },
|
||||
)
|
||||
};
|
||||
|
@ -580,9 +607,9 @@ impl MenuChild {
|
|||
for store in haccel_stores {
|
||||
let mut store = store.borrow_mut();
|
||||
if let Some(accelerator) = self.accelerator {
|
||||
AccelAction::add(&mut store, self.id, &accelerator)?
|
||||
AccelAction::add(&mut store, self.internal_id, &accelerator)?
|
||||
} else {
|
||||
AccelAction::remove(&mut store, self.id)
|
||||
AccelAction::remove(&mut store, self.internal_id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -600,7 +627,7 @@ impl MenuChild {
|
|||
info.cbSize = std::mem::size_of::<MENUITEMINFOW>() as _;
|
||||
info.fMask = MIIM_STATE;
|
||||
|
||||
unsafe { GetMenuItemInfoW(*hmenu, self.id(), false.into(), &mut info) };
|
||||
unsafe { GetMenuItemInfoW(*hmenu, self.internal_id(), false.into(), &mut info) };
|
||||
|
||||
(info.fState & MFS_CHECKED) != 0
|
||||
})
|
||||
|
@ -615,7 +642,7 @@ impl MenuChild {
|
|||
unsafe {
|
||||
WindowsAndMessaging::CheckMenuItem(
|
||||
*parent,
|
||||
self.id(),
|
||||
self.internal_id(),
|
||||
if checked { MF_CHECKED } else { MF_UNCHECKED },
|
||||
)
|
||||
};
|
||||
|
@ -631,7 +658,7 @@ impl MenuChild {
|
|||
let hbitmap = icon.map(|i| unsafe { i.inner.to_hbitmap() }).unwrap_or(0);
|
||||
let info = create_icon_item_info(hbitmap);
|
||||
for parent in &self.parents_hemnu {
|
||||
unsafe { SetMenuItemInfoW(*parent, self.id(), false.into(), &info) };
|
||||
unsafe { SetMenuItemInfoW(*parent, self.internal_id(), false.into(), &info) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -670,11 +697,11 @@ impl MenuChild {
|
|||
|
||||
for root_menu in self.root_menu_haccel_stores.as_mut().unwrap() {
|
||||
let mut haccel = root_menu.borrow_mut();
|
||||
AccelAction::add(&mut haccel, child_.id(), accelerator)?;
|
||||
AccelAction::add(&mut haccel, child_.internal_id(), accelerator)?;
|
||||
}
|
||||
}
|
||||
|
||||
let id = child_.id() as usize;
|
||||
let id = child_.internal_id() as usize;
|
||||
let text = encode_wide(text);
|
||||
unsafe {
|
||||
match op {
|
||||
|
@ -714,8 +741,8 @@ impl MenuChild {
|
|||
let info = create_icon_item_info(hbitmap);
|
||||
|
||||
unsafe {
|
||||
SetMenuItemInfoW(self.hmenu, child_.id, false.into(), &info);
|
||||
SetMenuItemInfoW(self.hpopupmenu, child_.id, false.into(), &info);
|
||||
SetMenuItemInfoW(self.hmenu, child_.internal_id, false.into(), &info);
|
||||
SetMenuItemInfoW(self.hpopupmenu, child_.internal_id, false.into(), &info);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -738,9 +765,10 @@ impl MenuChild {
|
|||
}
|
||||
|
||||
pub fn remove(&mut self, item: &dyn IsMenuItem) -> crate::Result<()> {
|
||||
let id = item.child().borrow().internal_id();
|
||||
unsafe {
|
||||
RemoveMenu(self.hmenu, item.id(), MF_BYCOMMAND);
|
||||
RemoveMenu(self.hpopupmenu, item.id(), MF_BYCOMMAND);
|
||||
RemoveMenu(self.hmenu, id, MF_BYCOMMAND);
|
||||
RemoveMenu(self.hpopupmenu, id, MF_BYCOMMAND);
|
||||
}
|
||||
|
||||
let child = item.child();
|
||||
|
@ -764,7 +792,7 @@ impl MenuChild {
|
|||
let children = self.children.as_mut().unwrap();
|
||||
let index = children
|
||||
.iter()
|
||||
.position(|e| e.borrow().id() == item.id())
|
||||
.position(|e| e.borrow().internal_id() == id)
|
||||
.ok_or(crate::Error::NotAChildOfThisMenu)?;
|
||||
children.remove(index);
|
||||
|
||||
|
@ -813,7 +841,7 @@ impl MenuChild {
|
|||
fn find_by_id(id: u32, children: &Vec<Rc<RefCell<MenuChild>>>) -> Option<Rc<RefCell<MenuChild>>> {
|
||||
for i in children {
|
||||
let item = i.borrow();
|
||||
if item.id() == id {
|
||||
if item.internal_id() == id {
|
||||
return Some(i.clone());
|
||||
}
|
||||
|
||||
|
@ -918,13 +946,15 @@ unsafe extern "system" fn menu_subclass_proc(
|
|||
if let Some(item) = item {
|
||||
ret = 0;
|
||||
|
||||
let mut dispatch = true;
|
||||
let (mut dispatch, mut menu_id) = (true, None);
|
||||
|
||||
{
|
||||
let mut item = item.borrow_mut();
|
||||
|
||||
if item.item_type() == MenuItemType::Predefined {
|
||||
dispatch = false;
|
||||
} else {
|
||||
menu_id.replace(item.id.clone());
|
||||
}
|
||||
|
||||
match item.item_type() {
|
||||
|
@ -966,7 +996,9 @@ unsafe extern "system" fn menu_subclass_proc(
|
|||
}
|
||||
|
||||
if dispatch {
|
||||
MenuEvent::send(MenuEvent { id });
|
||||
MenuEvent::send(MenuEvent {
|
||||
id: menu_id.unwrap(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue