From af3268a4be69e2dee72ba867ee54bc36d12712e6 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Wed, 16 Aug 2023 19:06:17 +0300 Subject: [PATCH] refactor(menu,tray): add wrappers (#7622) --- core/tauri/src/app.rs | 8 +- core/tauri/src/menu/builders/icon.rs | 7 +- core/tauri/src/menu/icon.rs | 8 +- core/tauri/src/menu/menu.rs | 4 +- core/tauri/src/menu/mod.rs | 389 ++++++++++++++++++++++++++- core/tauri/src/menu/predefined.rs | 2 +- core/tauri/src/tray.rs | 92 ++++++- 7 files changed, 494 insertions(+), 16 deletions(-) diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index 036ba89cfa54..e6dc32998911 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -1461,16 +1461,16 @@ impl Builder { { // setup menu event handler let proxy = runtime.create_proxy(); - crate::menu::MenuEvent::set_event_handler(Some(move |e| { - let _ = proxy.send_event(EventLoopMessage::MenuEvent(e)); + muda::MenuEvent::set_event_handler(Some(move |e: muda::MenuEvent| { + let _ = proxy.send_event(EventLoopMessage::MenuEvent(e.into())); })); // setup tray event handler #[cfg(feature = "tray-icon")] { let proxy = runtime.create_proxy(); - crate::tray::TrayIconEvent::set_event_handler(Some(move |e| { - let _ = proxy.send_event(EventLoopMessage::TrayIconEvent(e)); + tray_icon::TrayIconEvent::set_event_handler(Some(move |e: tray_icon::TrayIconEvent| { + let _ = proxy.send_event(EventLoopMessage::TrayIconEvent(e.into())); })); } } diff --git a/core/tauri/src/menu/builders/icon.rs b/core/tauri/src/menu/builders/icon.rs index d2b2aa0d54cd..a6a24360c627 100644 --- a/core/tauri/src/menu/builders/icon.rs +++ b/core/tauri/src/menu/builders/icon.rs @@ -2,9 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use muda::{MenuId, NativeIcon}; - -use crate::{menu::IconMenuItem, Icon, Manager, Runtime}; +use crate::{ + menu::{IconMenuItem, MenuId, NativeIcon}, + Icon, Manager, Runtime, +}; /// A builder type for [`IconMenuItem`] pub struct IconMenuItemBuilder { diff --git a/core/tauri/src/menu/icon.rs b/core/tauri/src/menu/icon.rs index 9a2d8b0dda5e..699a0ea9f49e 100644 --- a/core/tauri/src/menu/icon.rs +++ b/core/tauri/src/menu/icon.rs @@ -115,7 +115,7 @@ impl IconMenuItem { let item = muda::IconMenuItem::with_native_icon( text, enabled, - native_icon, + native_icon.map(Into::into), acccelerator.and_then(|s| s.as_ref().parse().ok()), ); Self { @@ -144,7 +144,7 @@ impl IconMenuItem { id, text, enabled, - native_icon, + native_icon.map(Into::into), acccelerator.and_then(|s| s.as_ref().parse().ok()), ); Self { @@ -207,7 +207,9 @@ impl IconMenuItem { /// - **Windows / Linux**: Unsupported. pub fn set_native_icon(&mut self, _icon: Option) -> crate::Result<()> { #[cfg(target_os = "macos")] - return run_main_thread!(self, |mut self_: Self| self_.inner.set_native_icon(_icon)); + return run_main_thread!(self, |mut self_: Self| self_ + .inner + .set_native_icon(_icon.map(Into::into))); #[allow(unreachable_code)] Ok(()) } diff --git a/core/tauri/src/menu/menu.rs b/core/tauri/src/menu/menu.rs index f4752d665c56..a29ecb3300d9 100644 --- a/core/tauri/src/menu/menu.rs +++ b/core/tauri/src/menu/menu.rs @@ -3,11 +3,11 @@ // SPDX-License-Identifier: MIT use super::sealed::ContextMenuBase; -use super::{IsMenuItem, MenuItemKind, PredefinedMenuItem, Submenu}; +use super::{AboutMetadata, IsMenuItem, MenuItemKind, PredefinedMenuItem, Submenu}; use crate::Window; use crate::{run_main_thread, AppHandle, Manager, Position, Runtime}; use muda::ContextMenu; -use muda::{AboutMetadata, MenuId}; +use muda::MenuId; /// Expected submenu id of the Window menu for macOS. pub const WINDOW_SUBMENU_ID: &str = "__tauri_window_menu__"; diff --git a/core/tauri/src/menu/mod.rs b/core/tauri/src/menu/mod.rs index 4f594f9905f8..bdfef026836a 100644 --- a/core/tauri/src/menu/mod.rs +++ b/core/tauri/src/menu/mod.rs @@ -24,8 +24,393 @@ pub use normal::MenuItem; pub use predefined::PredefinedMenuItem; pub use submenu::Submenu; -use crate::Runtime; -pub use muda::{AboutMetadata, MenuEvent, MenuId, NativeIcon}; +use crate::{Icon, Runtime}; +pub use muda::MenuId; + +/// Describes a menu event emitted when a menu item is activated +#[derive(Debug, Clone)] +pub struct MenuEvent { + /// Id of the menu item which triggered this event + pub id: MenuId, +} + +impl MenuEvent { + /// Returns the id of the menu item which triggered this event + pub fn id(&self) -> &MenuId { + &self.id + } +} + +impl From for MenuEvent { + fn from(value: muda::MenuEvent) -> Self { + Self { id: value.id } + } +} + +/// Application metadata for the [`PredefinedMenuItem::about`](crate::PredefinedMenuItem::about). +#[derive(Debug, Clone, Default)] +pub struct AboutMetadata { + /// Sets the application name. + pub name: Option, + /// The application version. + pub version: Option, + /// The short version, e.g. "1.0". + /// + /// ## Platform-specific + /// + /// - **Windows / Linux:** Appended to the end of `version` in parentheses. + pub short_version: Option, + /// The authors of the application. + /// + /// ## Platform-specific + /// + /// - **macOS:** Unsupported. + pub authors: Option>, + /// Application comments. + /// + /// ## Platform-specific + /// + /// - **macOS:** Unsupported. + pub comments: Option, + /// The copyright of the application. + pub copyright: Option, + /// The license of the application. + /// + /// ## Platform-specific + /// + /// - **macOS:** Unsupported. + pub license: Option, + /// The application website. + /// + /// ## Platform-specific + /// + /// - **macOS:** Unsupported. + pub website: Option, + /// The website label. + /// + /// ## Platform-specific + /// + /// - **macOS:** Unsupported. + pub website_label: Option, + /// The credits. + /// + /// ## Platform-specific + /// + /// - **Windows / Linux:** Unsupported. + pub credits: Option, + /// The application icon. + /// + /// ## Platform-specific + /// + /// - **Windows:** Unsupported. + pub icon: Option, +} + +/// A builder type for [`AboutMetadata`]. +#[derive(Clone, Debug, Default)] +pub struct AboutMetadataBuilder(AboutMetadata); + +impl AboutMetadataBuilder { + /// Create a new about metdata builder. + pub fn new() -> Self { + Default::default() + } + + /// Sets the application name. + pub fn name>(mut self, name: Option) -> Self { + self.0.name = name.map(|s| s.into()); + self + } + /// Sets the application version. + pub fn version>(mut self, version: Option) -> Self { + self.0.version = version.map(|s| s.into()); + self + } + /// Sets the short version, e.g. "1.0". + /// + /// ## Platform-specific + /// + /// - **Windows / Linux:** Appended to the end of `version` in parentheses. + pub fn short_version>(mut self, short_version: Option) -> Self { + self.0.short_version = short_version.map(|s| s.into()); + self + } + /// Sets the authors of the application. + /// + /// ## Platform-specific + /// + /// - **macOS:** Unsupported. + pub fn authors(mut self, authors: Option>) -> Self { + self.0.authors = authors; + self + } + /// Application comments. + /// + /// ## Platform-specific + /// + /// - **macOS:** Unsupported. + pub fn comments>(mut self, comments: Option) -> Self { + self.0.comments = comments.map(|s| s.into()); + self + } + /// Sets the copyright of the application. + pub fn copyright>(mut self, copyright: Option) -> Self { + self.0.copyright = copyright.map(|s| s.into()); + self + } + /// Sets the license of the application. + /// + /// ## Platform-specific + /// + /// - **macOS:** Unsupported. + pub fn license>(mut self, license: Option) -> Self { + self.0.license = license.map(|s| s.into()); + self + } + /// Sets the application website. + /// + /// ## Platform-specific + /// + /// - **macOS:** Unsupported. + pub fn website>(mut self, website: Option) -> Self { + self.0.website = website.map(|s| s.into()); + self + } + /// Sets the website label. + /// + /// ## Platform-specific + /// + /// - **macOS:** Unsupported. + pub fn website_label>(mut self, website_label: Option) -> Self { + self.0.website_label = website_label.map(|s| s.into()); + self + } + /// Sets the credits. + /// + /// ## Platform-specific + /// + /// - **Windows / Linux:** Unsupported. + pub fn credits>(mut self, credits: Option) -> Self { + self.0.credits = credits.map(|s| s.into()); + self + } + /// Sets the application icon. + /// + /// ## Platform-specific + /// + /// - **Windows:** Unsupported. + pub fn icon(mut self, icon: Option) -> Self { + self.0.icon = icon; + self + } + + /// Construct the final [`AboutMetadata`] + pub fn build(self) -> AboutMetadata { + self.0 + } +} + +impl From for muda::AboutMetadata { + fn from(value: AboutMetadata) -> Self { + Self { + authors: value.authors, + name: value.name, + version: value.version, + short_version: value.short_version, + comments: value.comments, + copyright: value.copyright, + license: value.license, + website: value.website, + website_label: value.website_label, + credits: value.credits, + icon: value.icon.and_then(|i| i.try_into().ok()), + } + } +} + +/// A native Icon to be used for the menu item +/// +/// ## Platform-specific: +/// +/// - **Windows / Linux**: Unsupported. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum NativeIcon { + /// An add item template image. + Add, + /// Advanced preferences toolbar icon for the preferences window. + Advanced, + /// A Bluetooth template image. + Bluetooth, + /// Bookmarks image suitable for a template. + Bookmarks, + /// A caution image. + Caution, + /// A color panel toolbar icon. + ColorPanel, + /// A column view mode template image. + ColumnView, + /// A computer icon. + Computer, + /// An enter full-screen mode template image. + EnterFullScreen, + /// Permissions for all users. + Everyone, + /// An exit full-screen mode template image. + ExitFullScreen, + /// A cover flow view mode template image. + FlowView, + /// A folder image. + Folder, + /// A burnable folder icon. + FolderBurnable, + /// A smart folder icon. + FolderSmart, + /// A link template image. + FollowLinkFreestanding, + /// A font panel toolbar icon. + FontPanel, + /// A `go back` template image. + GoLeft, + /// A `go forward` template image. + GoRight, + /// Home image suitable for a template. + Home, + /// An iChat Theater template image. + IChatTheater, + /// An icon view mode template image. + IconView, + /// An information toolbar icon. + Info, + /// A template image used to denote invalid data. + InvalidDataFreestanding, + /// A generic left-facing triangle template image. + LeftFacingTriangle, + /// A list view mode template image. + ListView, + /// A locked padlock template image. + LockLocked, + /// An unlocked padlock template image. + LockUnlocked, + /// A horizontal dash, for use in menus. + MenuMixedState, + /// A check mark template image, for use in menus. + MenuOnState, + /// A MobileMe icon. + MobileMe, + /// A drag image for multiple items. + MultipleDocuments, + /// A network icon. + Network, + /// A path button template image. + Path, + /// General preferences toolbar icon for the preferences window. + PreferencesGeneral, + /// A Quick Look template image. + QuickLook, + /// A refresh template image. + RefreshFreestanding, + /// A refresh template image. + Refresh, + /// A remove item template image. + Remove, + /// A reveal contents template image. + RevealFreestanding, + /// A generic right-facing triangle template image. + RightFacingTriangle, + /// A share view template image. + Share, + /// A slideshow template image. + Slideshow, + /// A badge for a `smart` item. + SmartBadge, + /// Small green indicator, similar to iChat’s available image. + StatusAvailable, + /// Small clear indicator. + StatusNone, + /// Small yellow indicator, similar to iChat’s idle image. + StatusPartiallyAvailable, + /// Small red indicator, similar to iChat’s unavailable image. + StatusUnavailable, + /// A stop progress template image. + StopProgressFreestanding, + /// A stop progress button template image. + StopProgress, + /// An image of the empty trash can. + TrashEmpty, + /// An image of the full trash can. + TrashFull, + /// Permissions for a single user. + User, + /// User account toolbar icon for the preferences window. + UserAccounts, + /// Permissions for a group of users. + UserGroup, + /// Permissions for guests. + UserGuest, +} + +impl From for muda::NativeIcon { + fn from(value: NativeIcon) -> Self { + match value { + NativeIcon::Add => muda::NativeIcon::Add, + NativeIcon::Advanced => muda::NativeIcon::Advanced, + NativeIcon::Bluetooth => muda::NativeIcon::Bluetooth, + NativeIcon::Bookmarks => muda::NativeIcon::Bookmarks, + NativeIcon::Caution => muda::NativeIcon::Caution, + NativeIcon::ColorPanel => muda::NativeIcon::ColorPanel, + NativeIcon::ColumnView => muda::NativeIcon::ColumnView, + NativeIcon::Computer => muda::NativeIcon::Computer, + NativeIcon::EnterFullScreen => muda::NativeIcon::EnterFullScreen, + NativeIcon::Everyone => muda::NativeIcon::Everyone, + NativeIcon::ExitFullScreen => muda::NativeIcon::ExitFullScreen, + NativeIcon::FlowView => muda::NativeIcon::FlowView, + NativeIcon::Folder => muda::NativeIcon::Folder, + NativeIcon::FolderBurnable => muda::NativeIcon::FolderBurnable, + NativeIcon::FolderSmart => muda::NativeIcon::FolderSmart, + NativeIcon::FollowLinkFreestanding => muda::NativeIcon::FollowLinkFreestanding, + NativeIcon::FontPanel => muda::NativeIcon::FontPanel, + NativeIcon::GoLeft => muda::NativeIcon::GoLeft, + NativeIcon::GoRight => muda::NativeIcon::GoRight, + NativeIcon::Home => muda::NativeIcon::Home, + NativeIcon::IChatTheater => muda::NativeIcon::IChatTheater, + NativeIcon::IconView => muda::NativeIcon::IconView, + NativeIcon::Info => muda::NativeIcon::Info, + NativeIcon::InvalidDataFreestanding => muda::NativeIcon::InvalidDataFreestanding, + NativeIcon::LeftFacingTriangle => muda::NativeIcon::LeftFacingTriangle, + NativeIcon::ListView => muda::NativeIcon::ListView, + NativeIcon::LockLocked => muda::NativeIcon::LockLocked, + NativeIcon::LockUnlocked => muda::NativeIcon::LockUnlocked, + NativeIcon::MenuMixedState => muda::NativeIcon::MenuMixedState, + NativeIcon::MenuOnState => muda::NativeIcon::MenuOnState, + NativeIcon::MobileMe => muda::NativeIcon::MobileMe, + NativeIcon::MultipleDocuments => muda::NativeIcon::MultipleDocuments, + NativeIcon::Network => muda::NativeIcon::Network, + NativeIcon::Path => muda::NativeIcon::Path, + NativeIcon::PreferencesGeneral => muda::NativeIcon::PreferencesGeneral, + NativeIcon::QuickLook => muda::NativeIcon::QuickLook, + NativeIcon::RefreshFreestanding => muda::NativeIcon::RefreshFreestanding, + NativeIcon::Refresh => muda::NativeIcon::Refresh, + NativeIcon::Remove => muda::NativeIcon::Remove, + NativeIcon::RevealFreestanding => muda::NativeIcon::RevealFreestanding, + NativeIcon::RightFacingTriangle => muda::NativeIcon::RightFacingTriangle, + NativeIcon::Share => muda::NativeIcon::Share, + NativeIcon::Slideshow => muda::NativeIcon::Slideshow, + NativeIcon::SmartBadge => muda::NativeIcon::SmartBadge, + NativeIcon::StatusAvailable => muda::NativeIcon::StatusAvailable, + NativeIcon::StatusNone => muda::NativeIcon::StatusNone, + NativeIcon::StatusPartiallyAvailable => muda::NativeIcon::StatusPartiallyAvailable, + NativeIcon::StatusUnavailable => muda::NativeIcon::StatusUnavailable, + NativeIcon::StopProgressFreestanding => muda::NativeIcon::StopProgressFreestanding, + NativeIcon::StopProgress => muda::NativeIcon::StopProgress, + NativeIcon::TrashEmpty => muda::NativeIcon::TrashEmpty, + NativeIcon::TrashFull => muda::NativeIcon::TrashFull, + NativeIcon::User => muda::NativeIcon::User, + NativeIcon::UserAccounts => muda::NativeIcon::UserAccounts, + NativeIcon::UserGroup => muda::NativeIcon::UserGroup, + NativeIcon::UserGuest => muda::NativeIcon::UserGuest, + } + } +} /// An enumeration of all menu item kinds that could be added to /// a [`Menu`] or [`Submenu`] diff --git a/core/tauri/src/menu/predefined.rs b/core/tauri/src/menu/predefined.rs index 37ba63a78465..44c6aff01d63 100644 --- a/core/tauri/src/menu/predefined.rs +++ b/core/tauri/src/menu/predefined.rs @@ -240,7 +240,7 @@ impl PredefinedMenuItem { text: Option<&str>, metadata: Option, ) -> Self { - let inner = muda::PredefinedMenuItem::about(text, metadata); + let inner = muda::PredefinedMenuItem::about(text, metadata.map(Into::into)); Self { id: inner.id().clone(), inner, diff --git a/core/tauri/src/tray.rs b/core/tauri/src/tray.rs index 77b3e3c1c75d..be6ea773dc54 100644 --- a/core/tauri/src/tray.rs +++ b/core/tauri/src/tray.rs @@ -11,10 +11,100 @@ use crate::menu::ContextMenu; use crate::menu::MenuEvent; use crate::{run_main_thread, AppHandle, Icon, Manager, Runtime}; use std::path::Path; -pub use tray_icon::{ClickType, Rectangle, TrayIconEvent, TrayIconId}; +pub use tray_icon::TrayIconId; // TODO(muda-migration): figure out js events +/// Describes a rectangle including position (x - y axis) and size. +#[derive(Debug, PartialEq, Clone, Copy, Default)] +pub struct Rectangle { + /// The x-coordinate of the upper-left corner of the rectangle. + pub left: f64, + /// The y-coordinate of the upper-left corner of the rectangle. + pub top: f64, + /// The x-coordinate of the lower-right corner of the rectangle. + pub right: f64, + /// The y-coordinate of the lower-right corner of the rectangle. + pub bottom: f64, +} + +/// Describes the click type that triggered this tray icon event. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum ClickType { + /// Left mouse click. + Left, + /// Right mouse click. + Right, + /// Double left mouse click. + Double, +} + +impl Default for ClickType { + fn default() -> Self { + Self::Left + } +} + +/// Describes a tray event emitted when a tray icon is clicked +/// +/// ## Platform-specific: +/// +/// - **Linux**: Unsupported. The event is not emmited even though the icon is shown, +/// the icon will still show a context menu on right click. +#[derive(Debug, Clone, Default)] +pub struct TrayIconEvent { + /// Id of the tray icon which triggered this event. + pub id: TrayIconId, + /// Physical X Position of the click the triggered this event. + pub x: f64, + /// Physical Y Position of the click the triggered this event. + pub y: f64, + /// Position and size of the tray icon + pub icon_rect: Rectangle, + /// The click type that triggered this event. + pub click_type: ClickType, +} + +impl TrayIconEvent { + /// Returns the id of the tray icon which triggered this event. + pub fn id(&self) -> &TrayIconId { + &self.id + } +} + +impl From for Rectangle { + fn from(value: tray_icon::Rectangle) -> Self { + Self { + bottom: value.bottom, + left: value.left, + top: value.top, + right: value.right, + } + } +} + +impl From for ClickType { + fn from(value: tray_icon::ClickType) -> Self { + match value { + tray_icon::ClickType::Left => Self::Left, + tray_icon::ClickType::Right => Self::Right, + tray_icon::ClickType::Double => Self::Double, + } + } +} + +impl From for TrayIconEvent { + fn from(value: tray_icon::TrayIconEvent) -> Self { + Self { + id: value.id, + x: value.x, + y: value.y, + icon_rect: value.icon_rect.into(), + click_type: value.click_type.into(), + } + } +} + /// [`TrayIcon`] builder struct and associated methods. #[derive(Default)] pub struct TrayIconBuilder {