use std::sync::Arc;
use super::run_item_main_thread;
use super::Submenu;
use super::{sealed::ContextMenuBase, IsMenuItem, MenuItemKind};
use crate::menu::NativeIcon;
use crate::menu::SubmenuInner;
use crate::run_main_thread;
use crate::{AppHandle, Manager, Position, Runtime, Window};
use muda::{ContextMenu, Icon as MudaIcon, MenuId};
impl<R: Runtime> super::ContextMenu for Submenu<R> {
#[cfg(target_os = "windows")]
fn hpopupmenu(&self) -> crate::Result<isize> {
run_item_main_thread!(self, |self_: Self| (*self_.0).as_ref().hpopupmenu())
}
fn popup<T: Runtime>(&self, window: Window<T>) -> crate::Result<()> {
self.popup_inner(window, None::<Position>)
}
fn popup_at<T: Runtime, P: Into<Position>>(
&self,
window: Window<T>,
position: P,
) -> crate::Result<()> {
self.popup_inner(window, Some(position))
}
}
impl<R: Runtime> ContextMenuBase for Submenu<R> {
fn popup_inner<T: Runtime, P: Into<crate::Position>>(
&self,
window: crate::Window<T>,
position: Option<P>,
) -> crate::Result<()> {
let position = position.map(Into::into);
run_item_main_thread!(self, move |self_: Self| {
#[cfg(target_os = "macos")]
if let Ok(view) = window.ns_view() {
unsafe {
self_
.inner()
.show_context_menu_for_nsview(view as _, position);
}
}
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
if let Ok(w) = window.gtk_window() {
self_
.inner()
.show_context_menu_for_gtk_window(w.as_ref(), position);
}
#[cfg(windows)]
if let Ok(hwnd) = window.hwnd() {
unsafe {
self_
.inner()
.show_context_menu_for_hwnd(hwnd.0 as _, position);
}
}
})
}
fn inner_context(&self) -> &dyn muda::ContextMenu {
(*self.0).as_ref()
}
fn inner_context_owned(&self) -> Box<dyn muda::ContextMenu> {
Box::new((*self.0).as_ref().clone())
}
}
impl<R: Runtime> Submenu<R> {
pub fn new<M: Manager<R>, S: AsRef<str>>(
manager: &M,
text: S,
enabled: bool,
) -> crate::Result<Self> {
let handle = manager.app_handle();
let app_handle = handle.clone();
let text = text.as_ref().to_owned();
let submenu = run_main_thread!(handle, || {
let submenu = muda::Submenu::new(text, enabled);
SubmenuInner {
id: submenu.id().clone(),
inner: Some(submenu),
app_handle,
}
})?;
Ok(Self(Arc::new(submenu)))
}
pub fn new_with_icon<M: Manager<R>, S: AsRef<str>>(
manager: &M,
text: S,
enabled: bool,
icon: Option<crate::image::Image<'_>>,
) -> crate::Result<Self> {
let handle = manager.app_handle();
let app_handle = handle.clone();
let text = text.as_ref().to_owned();
let icon_data = icon.map(|i| (i.rgba().to_vec(), i.width(), i.height()));
let submenu = run_main_thread!(handle, || {
let submenu = muda::Submenu::new(text, enabled);
if let Some((rgba, width, height)) = icon_data.clone() {
submenu.set_icon(Some(MudaIcon::from_rgba(rgba, width, height).unwrap()));
}
SubmenuInner {
id: submenu.id().clone(),
inner: Some(submenu),
app_handle,
}
})?;
Ok(Self(Arc::new(submenu)))
}
pub fn new_with_native_icon<M: Manager<R>, S: AsRef<str>>(
manager: &M,
text: S,
enabled: bool,
icon: Option<NativeIcon>,
) -> crate::Result<Self> {
let handle = manager.app_handle();
let app_handle = handle.clone();
let text = text.as_ref().to_owned();
let submenu = run_main_thread!(handle, || {
let submenu = muda::Submenu::new(text, enabled);
if let Some(icon) = icon {
submenu.set_native_icon(Some(icon.into()));
}
SubmenuInner {
id: submenu.id().clone(),
inner: Some(submenu),
app_handle,
}
})?;
Ok(Self(Arc::new(submenu)))
}
pub fn with_id<M: Manager<R>, I: Into<MenuId>, S: AsRef<str>>(
manager: &M,
id: I,
text: S,
enabled: bool,
) -> crate::Result<Self> {
let handle = manager.app_handle();
let app_handle = handle.clone();
let id = id.into();
let text = text.as_ref().to_owned();
let submenu = run_main_thread!(handle, || {
let submenu = muda::Submenu::with_id(id.clone(), text, enabled);
SubmenuInner {
id,
inner: Some(submenu),
app_handle,
}
})?;
Ok(Self(Arc::new(submenu)))
}
pub fn with_id_and_icon<M: Manager<R>, I: Into<MenuId>, S: AsRef<str>>(
manager: &M,
id: I,
text: S,
enabled: bool,
icon: Option<crate::image::Image<'_>>,
) -> crate::Result<Self> {
let handle = manager.app_handle();
let app_handle = handle.clone();
let id = id.into();
let text = text.as_ref().to_owned();
let icon_data = icon.map(|i| (i.rgba().to_vec(), i.width(), i.height()));
let submenu = run_main_thread!(handle, || {
let submenu = muda::Submenu::with_id(id.clone(), text, enabled);
if let Some((rgba, width, height)) = icon_data.clone() {
submenu.set_icon(Some(MudaIcon::from_rgba(rgba, width, height).unwrap()));
}
SubmenuInner {
id,
inner: Some(submenu),
app_handle,
}
})?;
Ok(Self(Arc::new(submenu)))
}
pub fn with_id_and_native_icon<M: Manager<R>, I: Into<MenuId>, S: AsRef<str>>(
manager: &M,
id: I,
text: S,
enabled: bool,
icon: Option<NativeIcon>,
) -> crate::Result<Self> {
let handle = manager.app_handle();
let app_handle = handle.clone();
let id = id.into();
let text = text.as_ref().to_owned();
let submenu = run_main_thread!(handle, || {
let submenu = muda::Submenu::with_id(id.clone(), text, enabled);
if let Some(icon) = icon {
submenu.set_native_icon(Some(icon.into()));
}
SubmenuInner {
id,
inner: Some(submenu),
app_handle,
}
})?;
Ok(Self(Arc::new(submenu)))
}
pub fn with_items<M: Manager<R>, S: AsRef<str>>(
manager: &M,
text: S,
enabled: bool,
items: &[&dyn IsMenuItem<R>],
) -> crate::Result<Self> {
let menu = Self::new(manager, text, enabled)?;
menu.append_items(items)?;
Ok(menu)
}
pub fn with_id_and_items<M: Manager<R>, I: Into<MenuId>, S: AsRef<str>>(
manager: &M,
id: I,
text: S,
enabled: bool,
items: &[&dyn IsMenuItem<R>],
) -> crate::Result<Self> {
let menu = Self::with_id(manager, id, text, enabled)?;
menu.append_items(items)?;
Ok(menu)
}
pub(crate) fn inner(&self) -> &muda::Submenu {
(*self.0).as_ref()
}
pub fn app_handle(&self) -> &AppHandle<R> {
&self.0.app_handle
}
pub fn id(&self) -> &MenuId {
&self.0.id
}
pub fn append(&self, item: &dyn IsMenuItem<R>) -> crate::Result<()> {
let kind = item.kind();
run_item_main_thread!(self, |self_: Self| {
(*self_.0).as_ref().append(kind.inner().inner_muda())
})?
.map_err(Into::into)
}
pub fn append_items(&self, items: &[&dyn IsMenuItem<R>]) -> crate::Result<()> {
for item in items {
self.append(*item)?
}
Ok(())
}
pub fn prepend(&self, item: &dyn IsMenuItem<R>) -> crate::Result<()> {
let kind = item.kind();
run_item_main_thread!(self, |self_: Self| {
(*self_.0).as_ref().prepend(kind.inner().inner_muda())
})?
.map_err(Into::into)
}
pub fn prepend_items(&self, items: &[&dyn IsMenuItem<R>]) -> crate::Result<()> {
self.insert_items(items, 0)
}
pub fn insert(&self, item: &dyn IsMenuItem<R>, position: usize) -> crate::Result<()> {
let kind = item.kind();
run_item_main_thread!(self, |self_: Self| {
(*self_.0)
.as_ref()
.insert(kind.inner().inner_muda(), position)
})?
.map_err(Into::into)
}
pub fn insert_items(&self, items: &[&dyn IsMenuItem<R>], position: usize) -> crate::Result<()> {
for (i, item) in items.iter().enumerate() {
self.insert(*item, position + i)?
}
Ok(())
}
pub fn remove(&self, item: &dyn IsMenuItem<R>) -> crate::Result<()> {
let kind = item.kind();
run_item_main_thread!(self, |self_: Self| {
(*self_.0).as_ref().remove(kind.inner().inner_muda())
})?
.map_err(Into::into)
}
pub fn remove_at(&self, position: usize) -> crate::Result<Option<MenuItemKind<R>>> {
run_item_main_thread!(self, |self_: Self| {
(*self_.0)
.as_ref()
.remove_at(position)
.map(|i| MenuItemKind::from_muda(self_.0.app_handle.clone(), i))
})
}
pub fn get<'a, I>(&self, id: &'a I) -> Option<MenuItemKind<R>>
where
I: ?Sized,
MenuId: PartialEq<&'a I>,
{
self
.items()
.unwrap_or_default()
.into_iter()
.find(|i| i.id() == &id)
}
pub fn items(&self) -> crate::Result<Vec<MenuItemKind<R>>> {
run_item_main_thread!(self, |self_: Self| {
(*self_.0)
.as_ref()
.items()
.into_iter()
.map(|i| MenuItemKind::from_muda(self_.0.app_handle.clone(), i))
.collect::<Vec<_>>()
})
}
pub fn text(&self) -> crate::Result<String> {
run_item_main_thread!(self, |self_: Self| (*self_.0).as_ref().text())
}
pub fn set_text<S: AsRef<str>>(&self, text: S) -> crate::Result<()> {
let text = text.as_ref().to_string();
run_item_main_thread!(self, |self_: Self| (*self_.0).as_ref().set_text(text))
}
pub fn is_enabled(&self) -> crate::Result<bool> {
run_item_main_thread!(self, |self_: Self| (*self_.0).as_ref().is_enabled())
}
pub fn set_enabled(&self, enabled: bool) -> crate::Result<()> {
run_item_main_thread!(self, |self_: Self| (*self_.0).as_ref().set_enabled(enabled))
}
#[cfg(target_os = "macos")]
pub fn set_as_windows_menu_for_nsapp(&self) -> crate::Result<()> {
run_item_main_thread!(self, |self_: Self| {
(*self_.0).as_ref().set_as_windows_menu_for_nsapp()
})?;
Ok(())
}
#[cfg(target_os = "macos")]
pub fn set_as_help_menu_for_nsapp(&self) -> crate::Result<()> {
run_item_main_thread!(self, |self_: Self| {
(*self_.0).as_ref().set_as_help_menu_for_nsapp()
})?;
Ok(())
}
pub fn set_icon(&self, icon: Option<crate::image::Image<'_>>) -> crate::Result<()> {
let icon = match icon {
Some(i) => Some(i.try_into()?),
None => None,
};
run_item_main_thread!(self, |self_: Self| (*self_.0).as_ref().set_icon(icon))
}
pub fn set_native_icon(&self, _icon: Option<NativeIcon>) -> crate::Result<()> {
#[cfg(target_os = "macos")]
return run_item_main_thread!(self, |self_: Self| {
(*self_.0).as_ref().set_native_icon(_icon.map(Into::into))
});
#[allow(unreachable_code)]
Ok(())
}
}