mirror of
https://github.com/italicsjenga/muda.git
synced 2025-01-11 04:11:32 +11:00
feat: implement NativeMenuItem::About
for windows and linux (#11)
* feat: implement NativeMenuItem::About * linux
This commit is contained in:
parent
421b00f597
commit
8f11761dee
48
src/lib.rs
48
src/lib.rs
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue