feat: implement NativeMenuItem::About for windows and linux (#11)

* feat: implement NativeMenuItem::About

* linux
This commit is contained in:
Amr Bashir 2022-06-11 18:17:39 +02:00 committed by GitHub
parent 421b00f597
commit 8f11761dee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 126 additions and 36 deletions

View file

@ -106,7 +106,7 @@ impl Menu {
/// ///
/// ## Platform-specific: /// ## 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`, /// 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. /// and the `&` character is not displayed on menu label.
/// Then the menu can be activated by press `Alt+F`. /// Then the menu can be activated by press `Alt+F`.
@ -261,7 +261,7 @@ impl Submenu {
/// ///
/// ## Platform-specific: /// ## 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`, /// 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. /// and the `&` character is not displayed on menu label.
/// Then the menu can be activated by press `F` when its parent menu is active. /// Then the menu can be activated by press `F` when its parent menu is active.
@ -273,7 +273,7 @@ impl Submenu {
/// ///
/// ## Platform-specific: /// ## 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`, /// 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. /// 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. /// 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: /// ## platform-specific:
/// ///
/// - **macOS**: the metadata is ignore. /// - **macOS:** the metadata is ignore.
/// - **Windows**: Not implemented. /// - **Windows:** Not implemented.
About(String, AboutMetadata), About(String, AboutMetadata),
/// A native “hide the app” menu item. /// A native “hide the app” menu item.
/// ///
/// ## platform-specific: /// ## platform-specific:
/// ///
/// - **Windows / Linux**: Unsupported. /// - **Windows / Linux:** Unsupported.
Hide, Hide,
/// A native “hide all other windows" menu item. /// A native “hide all other windows" menu item.
/// ///
/// ## platform-specific: /// ## platform-specific:
/// ///
/// - **Windows / Linux**: Unsupported. /// - **Windows / Linux:** Unsupported.
HideOthers, HideOthers,
/// A native "Show all windows for this app" menu item. /// A native "Show all windows for this app" menu item.
/// ///
/// ## platform-specific: /// ## platform-specific:
/// ///
/// - **Windows / Linux**: Unsupported. /// - **Windows / Linux:** Unsupported.
ShowAll, ShowAll,
/// A native "Services" menu item. /// A native "Services" menu item.
/// ///
/// ## platform-specific: /// ## platform-specific:
/// ///
/// - **Windows / Linux**: Unsupported. /// - **Windows / Linux:** Unsupported.
Services, Services,
/// A native "Close current window" menu item. /// A native "Close current window" menu item.
CloseWindow, CloseWindow,
@ -366,49 +366,49 @@ pub enum NativeMenuItem {
/// ///
/// ## Platform-specific: /// ## Platform-specific:
/// ///
/// - **macOS**: macOS require this menu item to enable "Copy" keyboard shortcut for your app. /// - **macOS:** macOS require this menu item to enable "Copy" keyboard shortcut for your app.
/// - **Linux Wayland**: Not implmeneted. /// - **Linux Wayland:** Not implmeneted.
Copy, Copy,
/// A native "Cut" menu item. /// A native "Cut" menu item.
/// ///
/// ## Platform-specific: /// ## Platform-specific:
/// ///
/// - **macOS**: macOS require this menu item to enable "Cut" keyboard shortcut for your app. /// - **macOS:** macOS require this menu item to enable "Cut" keyboard shortcut for your app.
/// - **Linux Wayland**: Not implmeneted. /// - **Linux Wayland:** Not implmeneted.
Cut, Cut,
/// A native "Paste" menu item. /// A native "Paste" menu item.
/// ///
/// ## Platform-specific: /// ## Platform-specific:
/// ///
/// - **macOS**: macOS require this menu item to enable "Paste" keyboard shortcut for your app. /// - **macOS:** macOS require this menu item to enable "Paste" keyboard shortcut for your app.
/// - **Linux Wayland**: Not implmeneted. /// - **Linux Wayland:** Not implmeneted.
Paste, Paste,
/// A native "Undo" menu item. /// A native "Undo" menu item.
/// ///
/// ## Platform-specific: /// ## Platform-specific:
/// ///
/// - **macOS**: macOS require this menu item to enable "Undo" keyboard shortcut for your app. /// - **macOS:** macOS require this menu item to enable "Undo" keyboard shortcut for your app.
/// - **Windows / Linux**: Unsupported. /// - **Windows / Linux:** Unsupported.
Undo, Undo,
/// A native "Redo" menu item. /// A native "Redo" menu item.
/// ///
/// ## Platform-specific: /// ## Platform-specific:
/// ///
/// - **macOS**: macOS require this menu item to enable "Redo" keyboard shortcut for your app. /// - **macOS:** macOS require this menu item to enable "Redo" keyboard shortcut for your app.
/// - **Windows / Linux**: Unsupported. /// - **Windows / Linux:** Unsupported.
Redo, Redo,
/// A native "Select All" menu item. /// A native "Select All" menu item.
/// ///
/// ## Platform-specific: /// ## Platform-specific:
/// ///
/// - **macOS**: macOS require this menu item to enable "Select All" keyboard shortcut for your app. /// - **macOS:** macOS require this menu item to enable "Select All" keyboard shortcut for your app.
/// - **Linux Wayland**: Not implmeneted. /// - **Linux Wayland:** Not implmeneted.
SelectAll, SelectAll,
/// A native "Enter fullscreen" menu item. /// A native "Enter fullscreen" menu item.
/// ///
/// ## platform-specific: /// ## platform-specific:
/// ///
/// - **Windows / Linux**: Unsupported. /// - **Windows / Linux:** Unsupported.
EnterFullScreen, EnterFullScreen,
/// A native "Minimize current window" menu item. /// A native "Minimize current window" menu item.
Minimize, Minimize,
@ -416,7 +416,7 @@ pub enum NativeMenuItem {
/// ///
/// ## platform-specific: /// ## platform-specific:
/// ///
/// - **Windows / Linux**: Unsupported. /// - **Windows / Linux:** Unsupported.
Zoom, Zoom,
/// Represends a Separator in the menu. /// Represends a Separator in the menu.
Separator, Separator,
@ -426,7 +426,7 @@ pub enum NativeMenuItem {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **macOS**: The metadata is ignored. /// - **macOS:** The metadata is ignored.
#[derive(PartialEq, Eq, Debug, Clone, Default)] #[derive(PartialEq, Eq, Debug, Clone, Default)]
pub struct AboutMetadata { pub struct AboutMetadata {
/// The application name. /// The application name.

View file

@ -516,6 +516,46 @@ impl NativeMenuItem {
item.show(); item.show();
gtk_menu.append(&item); 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);
}
_ => {} _ => {}
} }
} }

View file

@ -4,7 +4,8 @@ mod accelerator;
mod util; mod util;
use crate::{counter::Counter, NativeMenuItem}; 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 util::{decode_wide, encode_wide, LOWORD};
use windows_sys::Win32::{ use windows_sys::Win32::{
Foundation::{HWND, LPARAM, LRESULT, WPARAM}, Foundation::{HWND, LPARAM, LRESULT, WPARAM},
@ -13,19 +14,21 @@ use windows_sys::Win32::{
Shell::{DefSubclassProc, RemoveWindowSubclass, SetWindowSubclass}, Shell::{DefSubclassProc, RemoveWindowSubclass, SetWindowSubclass},
WindowsAndMessaging::{ WindowsAndMessaging::{
AppendMenuW, CloseWindow, CreateAcceleratorTableW, CreateMenu, DrawMenuBar, AppendMenuW, CloseWindow, CreateAcceleratorTableW, CreateMenu, DrawMenuBar,
EnableMenuItem, GetMenuItemInfoW, PostQuitMessage, SetMenu, SetMenuItemInfoW, EnableMenuItem, GetMenuItemInfoW, MessageBoxW, PostQuitMessage, SetMenu,
ShowWindow, ACCEL, HACCEL, HMENU, MENUITEMINFOW, MFS_DISABLED, MF_DISABLED, MF_ENABLED, SetMenuItemInfoW, ShowWindow, ACCEL, HACCEL, HMENU, MB_ICONINFORMATION, MENUITEMINFOW,
MF_GRAYED, MF_POPUP, MF_SEPARATOR, MF_STRING, MIIM_STATE, MIIM_STRING, SW_MINIMIZE, MFS_DISABLED, MF_DISABLED, MF_ENABLED, MF_GRAYED, MF_POPUP, MF_SEPARATOR, MF_STRING,
WM_COMMAND, MIIM_STATE, MIIM_STRING, SW_MINIMIZE, WM_COMMAND,
}, },
}, },
}; };
use self::accelerator::parse_accelerator; use self::accelerator::parse_accelerator;
const COUNTER_START: u64 = 563; const MENU_SUBCLASS_ID: usize = 200;
static COUNTER: Counter = Counter::new_with_start(563); const COUNTER_START: u64 = 1000;
const MENU_SUBCLASS_ID: usize = 232; 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 { struct InnerMenu {
hmenu: HMENU, hmenu: HMENU,
@ -108,6 +111,8 @@ impl Menu {
} }
} }
static mut ABOUT_MENU_ITEMS: Lazy<HashMap<u64, NativeMenuItem>> = Lazy::new(|| HashMap::new());
#[derive(Clone)] #[derive(Clone)]
pub struct Submenu { pub struct Submenu {
hmenu: HMENU, hmenu: HMENU,
@ -232,6 +237,19 @@ impl Submenu {
NativeMenuItem::Minimize => ("&Minimize", MF_STRING), NativeMenuItem::Minimize => ("&Minimize", MF_STRING),
NativeMenuItem::CloseWindow => ("Close", MF_STRING), NativeMenuItem::CloseWindow => ("Close", MF_STRING),
NativeMenuItem::Quit => ("Exit", 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, _ => return,
}; };
unsafe { unsafe {
@ -332,7 +350,7 @@ unsafe extern "system" fn menu_subclass_proc(
let id = LOWORD(wparam as _) as u64; let id = LOWORD(wparam as _) as u64;
// Custom menu items // 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 }); let _ = crate::MENU_CHANNEL.0.send(crate::MenuEvent { id });
ret = 0; ret = 0;
}; };
@ -362,6 +380,39 @@ unsafe extern "system" fn menu_subclass_proc(
_ if id == NativeMenuItem::Quit.id() => { _ if id == NativeMenuItem::Quit.id() => {
PostQuitMessage(0); 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!(), _ => unreachable!(),
} }
} }
@ -407,8 +458,7 @@ fn execute_edit_command(command: EditCommand) {
inputs[3].Anonymous.ki.wVk = VK_CONTROL; inputs[3].Anonymous.ki.wVk = VK_CONTROL;
inputs[3].Anonymous.ki.dwFlags = KEYEVENTF_KEYUP; inputs[3].Anonymous.ki.dwFlags = KEYEVENTF_KEYUP;
let ret = SendInput(4, &inputs as *const _, std::mem::size_of::<INPUT>() as _); SendInput(4, &inputs as *const _, std::mem::size_of::<INPUT>() as _);
dbg!(ret);
} }
} }
@ -428,6 +478,6 @@ impl NativeMenuItem {
} }
fn is_id_of_native(id: u64) -> bool { fn is_id_of_native(id: u64) -> bool {
(301..=308).contains(&id) (301..=308).contains(&id) || (ABOUT_COUNTER_START <= id && id <= ABOUT_COUNTER.current())
} }
} }