From 8f11761dee0cf52fdcad8626edf8b61a0ab7c4f1 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Sat, 11 Jun 2022 18:17:39 +0200 Subject: [PATCH] feat: implement `NativeMenuItem::About` for windows and linux (#11) * feat: implement NativeMenuItem::About * linux --- src/lib.rs | 48 ++++++++++----------- src/platform_impl/linux/mod.rs | 40 +++++++++++++++++ src/platform_impl/windows/mod.rs | 74 ++++++++++++++++++++++++++------ 3 files changed, 126 insertions(+), 36 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 369734a..9947a8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,7 +106,7 @@ impl Menu { /// /// ## Platform-specific: /// - /// - **Windows / Linux**: The menu label can containt `&` to indicate which letter should get a generated accelerator. + /// - **Windows / Linux:** The menu label can containt `&` to indicate which letter should get a generated accelerator. /// For example, using `&File` for the File menu would result in the label gets an underline under the `F`, /// and the `&` character is not displayed on menu label. /// Then the menu can be activated by press `Alt+F`. @@ -261,7 +261,7 @@ impl Submenu { /// /// ## Platform-specific: /// - /// - **Windows / Linux**: The menu label can containt `&` to indicate which letter should get a generated accelerator. + /// - **Windows / Linux:** The menu label can containt `&` to indicate which letter should get a generated accelerator. /// For example, using `&File` for the File menu would result in the label gets an underline under the `F`, /// and the `&` character is not displayed on menu label. /// Then the menu can be activated by press `F` when its parent menu is active. @@ -273,7 +273,7 @@ impl Submenu { /// /// ## Platform-specific: /// - /// - **Windows / Linux**: The menu item label can containt `&` to indicate which letter should get a generated accelerator. + /// - **Windows / Linux:** The menu item label can containt `&` to indicate which letter should get a generated accelerator. /// For example, using `&Save` for the save menu item would result in the label gets an underline under the `S`, /// and the `&` character is not displayed on menu item label. /// Then the menu item can be activated by press `S` when its parent menu is active. @@ -331,32 +331,32 @@ pub enum NativeMenuItem { /// /// ## platform-specific: /// - /// - **macOS**: the metadata is ignore. - /// - **Windows**: Not implemented. + /// - **macOS:** the metadata is ignore. + /// - **Windows:** Not implemented. About(String, AboutMetadata), /// A native “hide the app” menu item. /// /// ## platform-specific: /// - /// - **Windows / Linux**: Unsupported. + /// - **Windows / Linux:** Unsupported. Hide, /// A native “hide all other windows" menu item. /// /// ## platform-specific: /// - /// - **Windows / Linux**: Unsupported. + /// - **Windows / Linux:** Unsupported. HideOthers, /// A native "Show all windows for this app" menu item. /// /// ## platform-specific: /// - /// - **Windows / Linux**: Unsupported. + /// - **Windows / Linux:** Unsupported. ShowAll, /// A native "Services" menu item. /// /// ## platform-specific: /// - /// - **Windows / Linux**: Unsupported. + /// - **Windows / Linux:** Unsupported. Services, /// A native "Close current window" menu item. CloseWindow, @@ -366,49 +366,49 @@ pub enum NativeMenuItem { /// /// ## Platform-specific: /// - /// - **macOS**: macOS require this menu item to enable "Copy" keyboard shortcut for your app. - /// - **Linux Wayland**: Not implmeneted. + /// - **macOS:** macOS require this menu item to enable "Copy" keyboard shortcut for your app. + /// - **Linux Wayland:** Not implmeneted. Copy, /// A native "Cut" menu item. /// /// ## Platform-specific: /// - /// - **macOS**: macOS require this menu item to enable "Cut" keyboard shortcut for your app. - /// - **Linux Wayland**: Not implmeneted. + /// - **macOS:** macOS require this menu item to enable "Cut" keyboard shortcut for your app. + /// - **Linux Wayland:** Not implmeneted. Cut, /// A native "Paste" menu item. /// /// ## Platform-specific: /// - /// - **macOS**: macOS require this menu item to enable "Paste" keyboard shortcut for your app. - /// - **Linux Wayland**: Not implmeneted. + /// - **macOS:** macOS require this menu item to enable "Paste" keyboard shortcut for your app. + /// - **Linux Wayland:** Not implmeneted. Paste, /// A native "Undo" menu item. /// /// ## Platform-specific: /// - /// - **macOS**: macOS require this menu item to enable "Undo" keyboard shortcut for your app. - /// - **Windows / Linux**: Unsupported. + /// - **macOS:** macOS require this menu item to enable "Undo" keyboard shortcut for your app. + /// - **Windows / Linux:** Unsupported. Undo, /// A native "Redo" menu item. /// /// ## Platform-specific: /// - /// - **macOS**: macOS require this menu item to enable "Redo" keyboard shortcut for your app. - /// - **Windows / Linux**: Unsupported. + /// - **macOS:** macOS require this menu item to enable "Redo" keyboard shortcut for your app. + /// - **Windows / Linux:** Unsupported. Redo, /// A native "Select All" menu item. /// /// ## Platform-specific: /// - /// - **macOS**: macOS require this menu item to enable "Select All" keyboard shortcut for your app. - /// - **Linux Wayland**: Not implmeneted. + /// - **macOS:** macOS require this menu item to enable "Select All" keyboard shortcut for your app. + /// - **Linux Wayland:** Not implmeneted. SelectAll, /// A native "Enter fullscreen" menu item. /// /// ## platform-specific: /// - /// - **Windows / Linux**: Unsupported. + /// - **Windows / Linux:** Unsupported. EnterFullScreen, /// A native "Minimize current window" menu item. Minimize, @@ -416,7 +416,7 @@ pub enum NativeMenuItem { /// /// ## platform-specific: /// - /// - **Windows / Linux**: Unsupported. + /// - **Windows / Linux:** Unsupported. Zoom, /// Represends a Separator in the menu. Separator, @@ -426,7 +426,7 @@ pub enum NativeMenuItem { /// /// ## Platform-specific /// -/// - **macOS**: The metadata is ignored. +/// - **macOS:** The metadata is ignored. #[derive(PartialEq, Eq, Debug, Clone, Default)] pub struct AboutMetadata { /// The application name. diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index cae75e8..d258644 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -516,6 +516,46 @@ impl NativeMenuItem { item.show(); gtk_menu.append(&item); } + NativeMenuItem::About(app_name, metadata) => { + let app_name = app_name.clone(); + let metadata = metadata.clone(); + let item = gtk::MenuItem::with_label(&format!("About {}", app_name)); + item.connect_activate(move |_| { + let mut builder = gtk::builders::AboutDialogBuilder::new() + .program_name(&app_name) + .modal(true) + .resizable(false); + + if let Some(version) = &metadata.version { + builder = builder.version(version); + } + if let Some(authors) = &metadata.authors { + builder = builder.authors(authors.clone()); + } + if let Some(comments) = &metadata.comments { + builder = builder.comments(comments); + } + if let Some(copyright) = &metadata.copyright { + builder = builder.copyright(copyright); + } + if let Some(license) = &metadata.license { + builder = builder.license(license); + } + if let Some(website) = &metadata.website { + builder = builder.website(website); + } + if let Some(website_label) = &metadata.website_label { + builder = builder.website_label(website_label); + } + let about = builder.build(); + about.run(); + unsafe { + about.destroy(); + } + }); + item.show(); + gtk_menu.append(&item); + } _ => {} } } diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 7258547..df48c73 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -4,7 +4,8 @@ mod accelerator; mod util; use crate::{counter::Counter, NativeMenuItem}; -use std::{cell::RefCell, rc::Rc}; +use once_cell::sync::Lazy; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; use util::{decode_wide, encode_wide, LOWORD}; use windows_sys::Win32::{ Foundation::{HWND, LPARAM, LRESULT, WPARAM}, @@ -13,19 +14,21 @@ use windows_sys::Win32::{ Shell::{DefSubclassProc, RemoveWindowSubclass, SetWindowSubclass}, WindowsAndMessaging::{ AppendMenuW, CloseWindow, CreateAcceleratorTableW, CreateMenu, DrawMenuBar, - EnableMenuItem, GetMenuItemInfoW, PostQuitMessage, SetMenu, SetMenuItemInfoW, - ShowWindow, ACCEL, HACCEL, HMENU, MENUITEMINFOW, MFS_DISABLED, MF_DISABLED, MF_ENABLED, - MF_GRAYED, MF_POPUP, MF_SEPARATOR, MF_STRING, MIIM_STATE, MIIM_STRING, SW_MINIMIZE, - WM_COMMAND, + EnableMenuItem, GetMenuItemInfoW, MessageBoxW, PostQuitMessage, SetMenu, + SetMenuItemInfoW, ShowWindow, ACCEL, HACCEL, HMENU, MB_ICONINFORMATION, MENUITEMINFOW, + MFS_DISABLED, MF_DISABLED, MF_ENABLED, MF_GRAYED, MF_POPUP, MF_SEPARATOR, MF_STRING, + MIIM_STATE, MIIM_STRING, SW_MINIMIZE, WM_COMMAND, }, }, }; use self::accelerator::parse_accelerator; -const COUNTER_START: u64 = 563; -static COUNTER: Counter = Counter::new_with_start(563); -const MENU_SUBCLASS_ID: usize = 232; +const MENU_SUBCLASS_ID: usize = 200; +const COUNTER_START: u64 = 1000; +static COUNTER: Counter = Counter::new_with_start(COUNTER_START); +const ABOUT_COUNTER_START: u64 = 400; +static ABOUT_COUNTER: Counter = Counter::new_with_start(ABOUT_COUNTER_START); struct InnerMenu { hmenu: HMENU, @@ -108,6 +111,8 @@ impl Menu { } } +static mut ABOUT_MENU_ITEMS: Lazy> = Lazy::new(|| HashMap::new()); + #[derive(Clone)] pub struct Submenu { hmenu: HMENU, @@ -232,6 +237,19 @@ impl Submenu { NativeMenuItem::Minimize => ("&Minimize", MF_STRING), NativeMenuItem::CloseWindow => ("Close", MF_STRING), NativeMenuItem::Quit => ("Exit", MF_STRING), + NativeMenuItem::About(ref app_name, _) => { + let id = ABOUT_COUNTER.next(); + unsafe { + AppendMenuW( + self.hmenu, + MF_STRING, + id as _, + encode_wide(format!("About {}", app_name)).as_ptr(), + ); + ABOUT_MENU_ITEMS.insert(id, item); + } + return; + } _ => return, }; unsafe { @@ -332,7 +350,7 @@ unsafe extern "system" fn menu_subclass_proc( let id = LOWORD(wparam as _) as u64; // Custom menu items - if COUNTER_START < id && id < COUNTER.current() { + if COUNTER_START <= id && id <= COUNTER.current() { let _ = crate::MENU_CHANNEL.0.send(crate::MenuEvent { id }); ret = 0; }; @@ -362,6 +380,39 @@ unsafe extern "system" fn menu_subclass_proc( _ if id == NativeMenuItem::Quit.id() => { PostQuitMessage(0); } + _ if ABOUT_MENU_ITEMS.get(&id).is_some() => { + let item = ABOUT_MENU_ITEMS.get(&id).unwrap(); + if let NativeMenuItem::About(app_name, metadata) = item { + MessageBoxW( + hwnd, + encode_wide(format!( + r#" +{} + +version: {} +authors: {} +license: {} +website: {} {} + +{} + +{} + "#, + app_name, + metadata.version.as_deref().unwrap_or_default(), + metadata.authors.as_deref().unwrap_or_default().join(","), + metadata.license.as_deref().unwrap_or_default(), + metadata.website_label.as_deref().unwrap_or_default(), + metadata.website.as_deref().unwrap_or_default(), + metadata.comments.as_deref().unwrap_or_default(), + metadata.copyright.as_deref().unwrap_or_default(), + )) + .as_ptr(), + encode_wide(format!("About {}", &app_name)).as_ptr(), + MB_ICONINFORMATION, + ); + } + } _ => unreachable!(), } } @@ -407,8 +458,7 @@ fn execute_edit_command(command: EditCommand) { inputs[3].Anonymous.ki.wVk = VK_CONTROL; inputs[3].Anonymous.ki.dwFlags = KEYEVENTF_KEYUP; - let ret = SendInput(4, &inputs as *const _, std::mem::size_of::() as _); - dbg!(ret); + SendInput(4, &inputs as *const _, std::mem::size_of::() as _); } } @@ -428,6 +478,6 @@ impl NativeMenuItem { } fn is_id_of_native(id: u64) -> bool { - (301..=308).contains(&id) + (301..=308).contains(&id) || (ABOUT_COUNTER_START <= id && id <= ABOUT_COUNTER.current()) } }