mirror of
https://github.com/italicsjenga/muda.git
synced 2024-12-23 20:11:29 +11:00
refactor: move Rc<RefCell<MenuChild>>
out of platform_impl (#71)
* refactor: move `Rc<RefCell<MenuChild>>` out of platform_impl * fix doc test
This commit is contained in:
parent
fafab8544c
commit
0000e56974
5
.changes/menu-item-trait-name.md
Normal file
5
.changes/menu-item-trait-name.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"muda": "minor"
|
||||
---
|
||||
|
||||
Changed `MenuItemExt` trait name to `IsMenuItem`
|
|
@ -27,12 +27,12 @@ fn main() {
|
|||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let menu_bar_c = menu_bar.clone();
|
||||
let menu_bar = menu_bar.clone();
|
||||
event_loop_builder.with_msg_hook(move |msg| {
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::{TranslateAcceleratorW, MSG};
|
||||
unsafe {
|
||||
let msg = msg as *const MSG;
|
||||
let translated = TranslateAcceleratorW((*msg).hwnd, menu_bar_c.haccel(), msg);
|
||||
let translated = TranslateAcceleratorW((*msg).hwnd, menu_bar.haccel(), msg);
|
||||
translated == 1
|
||||
}
|
||||
});
|
||||
|
|
|
@ -25,12 +25,12 @@ fn main() {
|
|||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let menu_bar_c = menu_bar.clone();
|
||||
let menu_bar = menu_bar.clone();
|
||||
event_loop_builder.with_msg_hook(move |msg| {
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::{TranslateAcceleratorW, MSG};
|
||||
unsafe {
|
||||
let msg = msg as *const MSG;
|
||||
let translated = TranslateAcceleratorW((*msg).hwnd, menu_bar_c.haccel(), msg);
|
||||
let translated = TranslateAcceleratorW((*msg).hwnd, menu_bar.haccel(), msg);
|
||||
translated == 1
|
||||
}
|
||||
});
|
||||
|
|
|
@ -37,26 +37,54 @@ use std::{borrow::Borrow, hash::Hash, str::FromStr};
|
|||
pub struct Accelerator {
|
||||
pub(crate) mods: Modifiers,
|
||||
pub(crate) key: Code,
|
||||
id: u32,
|
||||
}
|
||||
|
||||
impl Accelerator {
|
||||
/// Creates a new accelerator to define keyboard shortcuts throughout your application.
|
||||
/// Only [`Modifiers::ALT`], [`Modifiers::SHIFT`], [`Modifiers::CONTROL`], and [`Modifiers::META`]/[`Modifiers::SUPER`]
|
||||
/// Only [`Modifiers::ALT`], [`Modifiers::SHIFT`], [`Modifiers::CONTROL`], and [`Modifiers::SUPER`]
|
||||
pub fn new(mods: Option<Modifiers>, key: Code) -> Self {
|
||||
Self {
|
||||
mods: mods.unwrap_or_else(Modifiers::empty),
|
||||
key,
|
||||
let mut mods = mods.unwrap_or_else(Modifiers::empty);
|
||||
if mods.contains(Modifiers::META) {
|
||||
mods.remove(Modifiers::META);
|
||||
mods.insert(Modifiers::SUPER);
|
||||
}
|
||||
let mut accelerator = Self { mods, key, id: 0 };
|
||||
accelerator.generate_hash();
|
||||
accelerator
|
||||
}
|
||||
|
||||
fn generate_hash(&mut self) {
|
||||
let mut str = String::new();
|
||||
if self.mods.contains(Modifiers::SHIFT) {
|
||||
str.push_str("shift+")
|
||||
}
|
||||
if self.mods.contains(Modifiers::CONTROL) {
|
||||
str.push_str("control+")
|
||||
}
|
||||
if self.mods.contains(Modifiers::ALT) {
|
||||
str.push_str("alt+")
|
||||
}
|
||||
if self.mods.contains(Modifiers::SUPER) {
|
||||
str.push_str("super+")
|
||||
}
|
||||
str.push_str(&self.key.to_string());
|
||||
|
||||
let mut s = std::collections::hash_map::DefaultHasher::new();
|
||||
str.hash(&mut s);
|
||||
self.id = std::hash::Hasher::finish(&s) as u32;
|
||||
}
|
||||
|
||||
/// Returns the id associated with this accelerator
|
||||
/// which is a hash of a string representing modifiers and key within this accelerator
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Returns `true` if this [`Code`] and [`Modifiers`] matches this `Accelerator`.
|
||||
pub fn matches(&self, modifiers: impl Borrow<Modifiers>, key: impl Borrow<Code>) -> bool {
|
||||
// Should be a const but const bit_or doesn't work here.
|
||||
let base_mods = Modifiers::SHIFT
|
||||
| Modifiers::CONTROL
|
||||
| Modifiers::ALT
|
||||
| Modifiers::META
|
||||
| Modifiers::SUPER;
|
||||
let base_mods = Modifiers::SHIFT | Modifiers::CONTROL | Modifiers::ALT | Modifiers::SUPER;
|
||||
let modifiers = modifiers.borrow();
|
||||
let key = key.borrow();
|
||||
self.mods == *modifiers & base_mods && self.key == *key
|
||||
|
@ -73,51 +101,36 @@ impl FromStr for Accelerator {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_accelerator(accelerator_string: &str) -> crate::Result<Accelerator> {
|
||||
fn parse_accelerator(accelerator: &str) -> crate::Result<Accelerator> {
|
||||
let tokens = accelerator.split('+').collect::<Vec<&str>>();
|
||||
|
||||
let mut mods = Modifiers::empty();
|
||||
let mut key = Code::Unidentified;
|
||||
let mut key = None;
|
||||
|
||||
let mut split = accelerator_string.split('+');
|
||||
let len = split.clone().count();
|
||||
let parse_key = |token: &str| -> crate::Result<Code> {
|
||||
if let Ok(code) = Code::from_str(token) {
|
||||
match code {
|
||||
Code::Unidentified => Err(crate::Error::AcceleratorParseError(format!(
|
||||
"Couldn't identify \"{}\" as a valid `Code`",
|
||||
token
|
||||
))),
|
||||
_ => Ok(code),
|
||||
match tokens.len() {
|
||||
// single key accelerator
|
||||
1 => {
|
||||
key = Some(parse_key(tokens[0])?);
|
||||
}
|
||||
} else {
|
||||
Err(crate::Error::AcceleratorParseError(format!(
|
||||
"Couldn't identify \"{}\" as a valid `Code`",
|
||||
token
|
||||
)))
|
||||
}
|
||||
};
|
||||
// modifiers and key comobo accelerator
|
||||
_ => {
|
||||
for raw in tokens {
|
||||
let token = raw.trim();
|
||||
|
||||
if len == 1 {
|
||||
let token = split.next().unwrap();
|
||||
key = parse_key(token)?;
|
||||
} else {
|
||||
for raw in accelerator_string.split('+') {
|
||||
let token = raw.trim().to_string();
|
||||
if token.is_empty() {
|
||||
return Err(crate::Error::AcceleratorParseError(
|
||||
"Unexpected empty token while parsing accelerator".into(),
|
||||
));
|
||||
return Err(crate::Error::EmptyAcceleratorToken(accelerator.to_string()));
|
||||
}
|
||||
|
||||
if key != Code::Unidentified {
|
||||
// at this point we already parsed the modifiers and found a main key but
|
||||
// the function received more then one main key or it is not in the right order
|
||||
if key.is_some() {
|
||||
// At this point we have parsed the modifiers and a main key, so by reaching
|
||||
// this code, the function either received more than one main key or
|
||||
// the accelerator is not in the right order
|
||||
// examples:
|
||||
// 1. "Ctrl+Shift+C+A" => only one main key should be allowd.
|
||||
// 2. "Ctrl+C+Shift" => wrong order
|
||||
return Err(crate::Error::AcceleratorParseError(format!(
|
||||
"Unexpected accelerator string format: \"{}\"",
|
||||
accelerator_string
|
||||
)));
|
||||
return Err(crate::Error::UnexpectedAcceleratorFormat(
|
||||
accelerator.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
match token.to_uppercase().as_str() {
|
||||
|
@ -135,89 +148,248 @@ fn parse_accelerator(accelerator_string: &str) -> crate::Result<Accelerator> {
|
|||
}
|
||||
"COMMANDORCONTROL" | "COMMANDORCTRL" | "CMDORCTRL" | "CMDORCONTROL" => {
|
||||
#[cfg(target_os = "macos")]
|
||||
mods.set(Modifiers::META, true);
|
||||
mods.set(Modifiers::SUPER, true);
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
mods.set(Modifiers::CONTROL, true);
|
||||
}
|
||||
_ => {
|
||||
key = parse_key(token.as_str())?;
|
||||
key = Some(parse_key(token)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Accelerator { key, mods })
|
||||
Ok(Accelerator::new(Some(mods), key.unwrap()))
|
||||
}
|
||||
|
||||
fn parse_key(key: &str) -> crate::Result<Code> {
|
||||
use Code::*;
|
||||
match key.to_uppercase().as_str() {
|
||||
"BACKQUOTE" | "`" => Ok(Backquote),
|
||||
"BACKSLASH" | "\\" => Ok(Backslash),
|
||||
"BRACKETLEFT" | "[" => Ok(BracketLeft),
|
||||
"BRACKETRIGHT" | "]" => Ok(BracketRight),
|
||||
"COMMA" | "," => Ok(Comma),
|
||||
"DIGIT0" | "0" => Ok(Digit0),
|
||||
"DIGIT1" | "1" => Ok(Digit1),
|
||||
"DIGIT2" | "2" => Ok(Digit2),
|
||||
"DIGIT3" | "3" => Ok(Digit3),
|
||||
"DIGIT4" | "4" => Ok(Digit4),
|
||||
"DIGIT5" | "5" => Ok(Digit5),
|
||||
"DIGIT6" | "6" => Ok(Digit6),
|
||||
"DIGIT7" | "7" => Ok(Digit7),
|
||||
"DIGIT8" | "8" => Ok(Digit8),
|
||||
"DIGIT9" | "9" => Ok(Digit9),
|
||||
"EQUAL" | "=" => Ok(Equal),
|
||||
"KEYA" | "A" => Ok(KeyA),
|
||||
"KEYB" | "B" => Ok(KeyB),
|
||||
"KEYC" | "C" => Ok(KeyC),
|
||||
"KEYD" | "D" => Ok(KeyD),
|
||||
"KEYE" | "E" => Ok(KeyE),
|
||||
"KEYF" | "F" => Ok(KeyF),
|
||||
"KEYG" | "G" => Ok(KeyG),
|
||||
"KEYH" | "H" => Ok(KeyH),
|
||||
"KEYI" | "I" => Ok(KeyI),
|
||||
"KEYJ" | "J" => Ok(KeyJ),
|
||||
"KEYK" | "K" => Ok(KeyK),
|
||||
"KEYL" | "L" => Ok(KeyL),
|
||||
"KEYM" | "M" => Ok(KeyM),
|
||||
"KEYN" | "N" => Ok(KeyN),
|
||||
"KEYO" | "O" => Ok(KeyO),
|
||||
"KEYP" | "P" => Ok(KeyP),
|
||||
"KEYQ" | "Q" => Ok(KeyQ),
|
||||
"KEYR" | "R" => Ok(KeyR),
|
||||
"KEYS" | "S" => Ok(KeyS),
|
||||
"KEYT" | "T" => Ok(KeyT),
|
||||
"KEYU" | "U" => Ok(KeyU),
|
||||
"KEYV" | "V" => Ok(KeyV),
|
||||
"KEYW" | "W" => Ok(KeyW),
|
||||
"KEYX" | "X" => Ok(KeyX),
|
||||
"KEYY" | "Y" => Ok(KeyY),
|
||||
"KEYZ" | "Z" => Ok(KeyZ),
|
||||
"MINUS" | "-" => Ok(Minus),
|
||||
"PERIOD" | "." => Ok(Period),
|
||||
"QUOTE" | "'" => Ok(Quote),
|
||||
"SEMICOLON" | ";" => Ok(Semicolon),
|
||||
"SLASH" | "/" => Ok(Slash),
|
||||
"BACKSPACE" => Ok(Backspace),
|
||||
"CAPSLOCK" => Ok(CapsLock),
|
||||
"ENTER" => Ok(Enter),
|
||||
"SPACE" => Ok(Space),
|
||||
"TAB" => Ok(Tab),
|
||||
"DELETE" => Ok(Delete),
|
||||
"END" => Ok(End),
|
||||
"HOME" => Ok(Home),
|
||||
"INSERT" => Ok(Insert),
|
||||
"PAGEDOWN" => Ok(PageDown),
|
||||
"PAGEUP" => Ok(PageUp),
|
||||
"PRINTSCREEN" => Ok(PrintScreen),
|
||||
"SCROLLLOCK" => Ok(ScrollLock),
|
||||
"ARROWDOWN" | "DOWN" => Ok(ArrowDown),
|
||||
"ARROWLEFT" | "LEFT" => Ok(ArrowLeft),
|
||||
"ARROWRIGHT" | "RIGHT" => Ok(ArrowRight),
|
||||
"ARROWUP" | "UP" => Ok(ArrowUp),
|
||||
"NUMLOCK" => Ok(NumLock),
|
||||
"NUMPAD0" | "NUM0" => Ok(Numpad0),
|
||||
"NUMPAD1" | "NUM1" => Ok(Numpad1),
|
||||
"NUMPAD2" | "NUM2" => Ok(Numpad2),
|
||||
"NUMPAD3" | "NUM3" => Ok(Numpad3),
|
||||
"NUMPAD4" | "NUM4" => Ok(Numpad4),
|
||||
"NUMPAD5" | "NUM5" => Ok(Numpad5),
|
||||
"NUMPAD6" | "NUM6" => Ok(Numpad6),
|
||||
"NUMPAD7" | "NUM7" => Ok(Numpad7),
|
||||
"NUMPAD8" | "NUM8" => Ok(Numpad8),
|
||||
"NUMPAD9" | "NUM9" => Ok(Numpad9),
|
||||
"NUMPADADD" | "NUMADD" | "NUMPADPLUS" | "NUMPLUS" => Ok(NumpadAdd),
|
||||
"NUMPADDECIMAL" | "NUMDECIMAL" => Ok(NumpadDecimal),
|
||||
"NUMPADDIVIDE" | "NUMDIVIDE" => Ok(NumpadDivide),
|
||||
"NUMPADENTER" | "NUMENTER" => Ok(NumpadEnter),
|
||||
"NUMPADEQUAL" | "NUMEQUAL" => Ok(NumpadEqual),
|
||||
"NUMPADMULTIPLY" | "NUMMULTIPLY" => Ok(NumpadMultiply),
|
||||
"NUMPADSUBTRACT" | "NUMSUBTRACT" => Ok(NumpadSubtract),
|
||||
"ESCAPE" | "ESC" => Ok(Escape),
|
||||
"F1" => Ok(F1),
|
||||
"F2" => Ok(F2),
|
||||
"F3" => Ok(F3),
|
||||
"F4" => Ok(F4),
|
||||
"F5" => Ok(F5),
|
||||
"F6" => Ok(F6),
|
||||
"F7" => Ok(F7),
|
||||
"F8" => Ok(F8),
|
||||
"F9" => Ok(F9),
|
||||
"F10" => Ok(F10),
|
||||
"F11" => Ok(F11),
|
||||
"F12" => Ok(F12),
|
||||
"AUDIOVOLUMEDOWN" | "VOLUMEDOWN" => Ok(AudioVolumeDown),
|
||||
"AUDIOVOLUMEUP" | "VOLUMEUP" => Ok(AudioVolumeUp),
|
||||
"AUDIOVOLUMEMUTE" | "VOLUMEMUTE" => Ok(AudioVolumeMute),
|
||||
"F13" => Ok(F13),
|
||||
"F14" => Ok(F14),
|
||||
"F15" => Ok(F15),
|
||||
"F16" => Ok(F16),
|
||||
"F17" => Ok(F17),
|
||||
"F18" => Ok(F18),
|
||||
"F19" => Ok(F19),
|
||||
"F20" => Ok(F20),
|
||||
"F21" => Ok(F21),
|
||||
"F22" => Ok(F22),
|
||||
"F23" => Ok(F23),
|
||||
"F24" => Ok(F24),
|
||||
|
||||
_ => Err(crate::Error::UnrecognizedAcceleratorCode(key.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_accelerator() {
|
||||
assert_eq!(
|
||||
parse_accelerator("CTRL+KeyX").unwrap(),
|
||||
macro_rules! assert_parse_accelerator {
|
||||
($key:literal, $lrh:expr) => {
|
||||
let r = parse_accelerator($key).unwrap();
|
||||
let l = $lrh;
|
||||
assert_eq!(r.mods, l.mods);
|
||||
assert_eq!(r.key, l.key);
|
||||
};
|
||||
}
|
||||
|
||||
assert_parse_accelerator!(
|
||||
"KeyX",
|
||||
Accelerator {
|
||||
mods: Modifiers::empty(),
|
||||
key: Code::KeyX,
|
||||
id: 0,
|
||||
}
|
||||
);
|
||||
|
||||
assert_parse_accelerator!(
|
||||
"CTRL+KeyX",
|
||||
Accelerator {
|
||||
mods: Modifiers::CONTROL,
|
||||
key: Code::KeyX,
|
||||
id: 0,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_accelerator("SHIFT+KeyC").unwrap(),
|
||||
|
||||
assert_parse_accelerator!(
|
||||
"SHIFT+KeyC",
|
||||
Accelerator {
|
||||
mods: Modifiers::SHIFT,
|
||||
key: Code::KeyC,
|
||||
id: 0,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_accelerator("CTRL+KeyZ").unwrap(),
|
||||
|
||||
assert_parse_accelerator!(
|
||||
"SHIFT+KeyC",
|
||||
Accelerator {
|
||||
mods: Modifiers::CONTROL,
|
||||
key: Code::KeyZ,
|
||||
mods: Modifiers::SHIFT,
|
||||
key: Code::KeyC,
|
||||
id: 0,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_accelerator("super+ctrl+SHIFT+alt+ArrowUp").unwrap(),
|
||||
|
||||
assert_parse_accelerator!(
|
||||
"super+ctrl+SHIFT+alt+ArrowUp",
|
||||
Accelerator {
|
||||
mods: Modifiers::META | Modifiers::CONTROL | Modifiers::SHIFT | Modifiers::ALT,
|
||||
mods: Modifiers::SUPER | Modifiers::CONTROL | Modifiers::SHIFT | Modifiers::ALT,
|
||||
key: Code::ArrowUp,
|
||||
id: 0,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_accelerator("Digit5").unwrap(),
|
||||
assert_parse_accelerator!(
|
||||
"Digit5",
|
||||
Accelerator {
|
||||
mods: Modifiers::empty(),
|
||||
key: Code::Digit5,
|
||||
id: 0,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_accelerator("KeyG").unwrap(),
|
||||
assert_parse_accelerator!(
|
||||
"KeyG",
|
||||
Accelerator {
|
||||
mods: Modifiers::empty(),
|
||||
key: Code::KeyG,
|
||||
id: 0,
|
||||
}
|
||||
);
|
||||
|
||||
let acc = parse_accelerator("+G");
|
||||
assert!(acc.is_err());
|
||||
|
||||
let acc = parse_accelerator("SHGSH+G");
|
||||
assert!(acc.is_err());
|
||||
|
||||
assert_eq!(
|
||||
parse_accelerator("SHiFT+F12").unwrap(),
|
||||
assert_parse_accelerator!(
|
||||
"SHiFT+F12",
|
||||
Accelerator {
|
||||
mods: Modifiers::SHIFT,
|
||||
key: Code::F12,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_accelerator("CmdOrCtrl+Space").unwrap(),
|
||||
Accelerator {
|
||||
#[cfg(target_os = "macos")]
|
||||
mods: Modifiers::META,
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
mods: Modifiers::CONTROL,
|
||||
key: Code::Space,
|
||||
id: 0,
|
||||
}
|
||||
);
|
||||
|
||||
let acc = parse_accelerator("CTRL+");
|
||||
assert!(acc.is_err());
|
||||
assert_parse_accelerator!(
|
||||
"CmdOrCtrl+Space",
|
||||
Accelerator {
|
||||
#[cfg(target_os = "macos")]
|
||||
mods: Modifiers::SUPER,
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
mods: Modifiers::CONTROL,
|
||||
key: Code::Space,
|
||||
id: 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
let h1 = parse_accelerator("Shift+KeyR").unwrap();
|
||||
let h2 = parse_accelerator("Shift+KeyR").unwrap();
|
||||
let h3 = Accelerator::new(Some(Modifiers::SHIFT), Code::KeyR);
|
||||
let h4 = parse_accelerator("Alt+KeyR").unwrap();
|
||||
let h5 = parse_accelerator("Alt+KeyR").unwrap();
|
||||
let h6 = parse_accelerator("KeyR").unwrap();
|
||||
|
||||
assert!(h1 == h2 && h2 == h3 && h3 != h4 && h4 == h5 && h5 != h6);
|
||||
assert!(
|
||||
h1.id() == h2.id()
|
||||
&& h2.id() == h3.id()
|
||||
&& h3.id() != h4.id()
|
||||
&& h4.id() == h5.id()
|
||||
&& h5.id() != h6.id()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -24,8 +24,12 @@ pub enum Error {
|
|||
AlreadyInitialized,
|
||||
#[error("{0}")]
|
||||
AcceleratorParseError(String),
|
||||
#[error("Cannot map {0} to gdk key")]
|
||||
AcceleratorKeyNotSupported(keyboard_types::Code),
|
||||
#[error("Couldn't recognize \"{0}\" as a valid Accelerator Code, if you feel like it should be, please report this to https://github.com/tauri-apps/muda")]
|
||||
UnrecognizedAcceleratorCode(String),
|
||||
#[error("Unexpected empty token while parsing accelerator: \"{0}\"")]
|
||||
EmptyAcceleratorToken(String),
|
||||
#[error("Unexpected accelerator string format: \"{0}\", a accelerator should have the modifiers first and only contain one main key")]
|
||||
UnexpectedAcceleratorFormat(String),
|
||||
}
|
||||
|
||||
/// Convenient type alias of Result type for muda.
|
||||
|
|
121
src/icon.rs
121
src/icon.rs
|
@ -164,3 +164,124 @@ impl Icon {
|
|||
Ok(Icon { inner: win_icon })
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::{accelerator::Accelerator, MenuItemExt, MenuItemType};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{accelerator::Accelerator, IsMenuItem, MenuItemType};
|
||||
|
||||
/// A check menu item inside a [`Menu`] or [`Submenu`]
|
||||
/// and usually contains a text and a check mark or a similar toggle
|
||||
|
@ -11,12 +13,13 @@ use crate::{accelerator::Accelerator, MenuItemExt, MenuItemType};
|
|||
/// [`Menu`]: crate::Menu
|
||||
/// [`Submenu`]: crate::Submenu
|
||||
#[derive(Clone)]
|
||||
pub struct CheckMenuItem(pub(crate) crate::platform_impl::CheckMenuItem);
|
||||
pub struct CheckMenuItem(pub(crate) Rc<RefCell<crate::platform_impl::MenuChild>>);
|
||||
|
||||
unsafe impl MenuItemExt for CheckMenuItem {
|
||||
unsafe impl IsMenuItem for CheckMenuItem {
|
||||
fn type_(&self) -> MenuItemType {
|
||||
MenuItemType::Check
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
|
||||
self
|
||||
}
|
||||
|
@ -30,60 +33,62 @@ impl CheckMenuItem {
|
|||
/// Create a new check menu item.
|
||||
///
|
||||
/// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic
|
||||
/// for this check menu item. To display a `&` without assigning a mnemenonic, use `&&`
|
||||
/// for this check menu item. To display a `&` without assigning a mnemenonic, use `&&`.
|
||||
pub fn new<S: AsRef<str>>(
|
||||
text: S,
|
||||
enabled: bool,
|
||||
checked: bool,
|
||||
acccelerator: Option<Accelerator>,
|
||||
) -> Self {
|
||||
Self(crate::platform_impl::CheckMenuItem::new(
|
||||
Self(Rc::new(RefCell::new(
|
||||
crate::platform_impl::MenuChild::new_check(
|
||||
text.as_ref(),
|
||||
enabled,
|
||||
checked,
|
||||
acccelerator,
|
||||
))
|
||||
),
|
||||
)))
|
||||
}
|
||||
|
||||
/// Returns a unique identifier associated with this submenu.
|
||||
pub fn id(&self) -> u32 {
|
||||
self.0.id()
|
||||
self.0.borrow().id()
|
||||
}
|
||||
|
||||
/// Get the text for this check menu item.
|
||||
pub fn text(&self) -> String {
|
||||
self.0.text()
|
||||
self.0.borrow().text()
|
||||
}
|
||||
|
||||
/// Get the text for this check menu item. `text` could optionally contain
|
||||
/// an `&` before a character to assign this character as the mnemonic
|
||||
/// for this check menu item. To display a `&` without assigning a mnemenonic, use `&&`
|
||||
/// for this check menu item. To display a `&` without assigning a mnemenonic, use `&&`.
|
||||
pub fn set_text<S: AsRef<str>>(&self, text: S) {
|
||||
self.0.set_text(text.as_ref())
|
||||
self.0.borrow_mut().set_text(text.as_ref())
|
||||
}
|
||||
|
||||
/// Get whether this check menu item is enabled or not.
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.0.is_enabled()
|
||||
self.0.borrow().is_enabled()
|
||||
}
|
||||
|
||||
/// Enable or disable this check menu item.
|
||||
pub fn set_enabled(&self, enabled: bool) {
|
||||
self.0.set_enabled(enabled)
|
||||
self.0.borrow_mut().set_enabled(enabled)
|
||||
}
|
||||
|
||||
/// Set this check menu item accelerator.
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) -> crate::Result<()> {
|
||||
self.0.borrow_mut().set_accelerator(acccelerator)
|
||||
}
|
||||
|
||||
/// Get whether this check menu item is checked or not.
|
||||
pub fn is_checked(&self) -> bool {
|
||||
self.0.is_checked()
|
||||
self.0.borrow().is_checked()
|
||||
}
|
||||
|
||||
/// Check or Uncheck this check menu item.
|
||||
pub fn set_checked(&self, checked: bool) {
|
||||
self.0.set_checked(checked)
|
||||
}
|
||||
|
||||
/// Set this check menu item accelerator.
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) {
|
||||
self.0.set_accelerator(acccelerator)
|
||||
self.0.borrow_mut().set_checked(checked)
|
||||
}
|
||||
}
|
|
@ -2,7 +2,13 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::{accelerator::Accelerator, icon::Icon, MenuItemExt, MenuItemType};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
accelerator::Accelerator,
|
||||
icon::{Icon, NativeIcon},
|
||||
IsMenuItem, MenuItemType,
|
||||
};
|
||||
|
||||
/// An icon menu item inside a [`Menu`] or [`Submenu`]
|
||||
/// and usually contains an icon and a text.
|
||||
|
@ -10,12 +16,13 @@ use crate::{accelerator::Accelerator, icon::Icon, MenuItemExt, MenuItemType};
|
|||
/// [`Menu`]: crate::Menu
|
||||
/// [`Submenu`]: crate::Submenu
|
||||
#[derive(Clone)]
|
||||
pub struct IconMenuItem(pub(crate) crate::platform_impl::IconMenuItem);
|
||||
pub struct IconMenuItem(pub(crate) Rc<RefCell<crate::platform_impl::MenuChild>>);
|
||||
|
||||
unsafe impl MenuItemExt for IconMenuItem {
|
||||
unsafe impl IsMenuItem for IconMenuItem {
|
||||
fn type_(&self) -> MenuItemType {
|
||||
MenuItemType::Icon
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
|
||||
self
|
||||
}
|
||||
|
@ -26,58 +33,65 @@ unsafe impl MenuItemExt for IconMenuItem {
|
|||
}
|
||||
|
||||
impl IconMenuItem {
|
||||
/// Create a new check menu item.
|
||||
/// Create a new icon menu item.
|
||||
///
|
||||
/// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic
|
||||
/// for this check menu item. To display a `&` without assigning a mnemenonic, use `&&`
|
||||
/// for this icon menu item. To display a `&` without assigning a mnemenonic, use `&&`.
|
||||
pub fn new<S: AsRef<str>>(
|
||||
text: S,
|
||||
enabled: bool,
|
||||
icon: Option<Icon>,
|
||||
acccelerator: Option<Accelerator>,
|
||||
) -> Self {
|
||||
Self(crate::platform_impl::IconMenuItem::new(
|
||||
text.as_ref(),
|
||||
enabled,
|
||||
icon,
|
||||
acccelerator,
|
||||
))
|
||||
Self(Rc::new(RefCell::new(
|
||||
crate::platform_impl::MenuChild::new_icon(text.as_ref(), enabled, icon, acccelerator),
|
||||
)))
|
||||
}
|
||||
|
||||
/// Returns a unique identifier associated with this submenu.
|
||||
pub fn id(&self) -> u32 {
|
||||
self.0.id()
|
||||
self.0.borrow().id()
|
||||
}
|
||||
|
||||
/// Get the text for this check menu item.
|
||||
pub fn text(&self) -> String {
|
||||
self.0.text()
|
||||
self.0.borrow().text()
|
||||
}
|
||||
|
||||
/// Get the text for this check menu item. `text` could optionally contain
|
||||
/// an `&` before a character to assign this character as the mnemonic
|
||||
/// for this check menu item. To display a `&` without assigning a mnemenonic, use `&&`
|
||||
/// for this check menu item. To display a `&` without assigning a mnemenonic, use `&&`.
|
||||
pub fn set_text<S: AsRef<str>>(&self, text: S) {
|
||||
self.0.set_text(text.as_ref())
|
||||
self.0.borrow_mut().set_text(text.as_ref())
|
||||
}
|
||||
|
||||
/// Get whether this check menu item is enabled or not.
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.0.is_enabled()
|
||||
self.0.borrow().is_enabled()
|
||||
}
|
||||
|
||||
/// Enable or disable this check menu item.
|
||||
pub fn set_enabled(&self, enabled: bool) {
|
||||
self.0.set_enabled(enabled)
|
||||
self.0.borrow_mut().set_enabled(enabled)
|
||||
}
|
||||
|
||||
/// Set this icon menu item accelerator.
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) -> crate::Result<()> {
|
||||
self.0.borrow_mut().set_accelerator(acccelerator)
|
||||
}
|
||||
|
||||
/// Change this menu item icon or remove it.
|
||||
pub fn set_icon(&self, icon: Option<Icon>) {
|
||||
self.0.set_icon(icon)
|
||||
self.0.borrow_mut().set_icon(icon)
|
||||
}
|
||||
|
||||
/// Set this icon menu item accelerator.
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) {
|
||||
self.0.set_accelerator(acccelerator)
|
||||
/// Change this menu item icon to a native image or remove it.
|
||||
///
|
||||
/// ## Platform-specific:
|
||||
///
|
||||
/// - **Windows / Linux**: Unsupported.
|
||||
pub fn set_native_icon(&mut self, _icon: Option<NativeIcon>) {
|
||||
#[cfg(target_os = "macos")]
|
||||
self.0.borrow_mut().set_native_icon(_icon)
|
||||
}
|
||||
}
|
15
src/items/mod.rs
Normal file
15
src/items/mod.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2022-2022 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
mod check;
|
||||
mod icon;
|
||||
mod normal;
|
||||
mod predefined;
|
||||
mod submenu;
|
||||
|
||||
pub use check::*;
|
||||
pub use icon::*;
|
||||
pub use normal::*;
|
||||
pub use predefined::*;
|
||||
pub use submenu::*;
|
|
@ -1,20 +1,19 @@
|
|||
// Copyright 2022-2022 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{accelerator::Accelerator, MenuItemExt, MenuItemType};
|
||||
use crate::{accelerator::Accelerator, IsMenuItem, MenuItemType};
|
||||
|
||||
/// A menu item inside a [`Menu`] or [`Submenu`] and contains only text.
|
||||
///
|
||||
/// [`Menu`]: crate::Menu
|
||||
/// [`Submenu`]: crate::Submenu
|
||||
#[derive(Clone)]
|
||||
pub struct MenuItem(pub(crate) crate::platform_impl::MenuItem);
|
||||
pub struct MenuItem(pub(crate) Rc<RefCell<crate::platform_impl::MenuChild>>);
|
||||
|
||||
unsafe impl MenuItemExt for MenuItem {
|
||||
unsafe impl IsMenuItem for MenuItem {
|
||||
fn type_(&self) -> MenuItemType {
|
||||
MenuItemType::Normal
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
|
||||
self
|
||||
}
|
||||
|
@ -28,44 +27,44 @@ impl MenuItem {
|
|||
/// Create a new menu item.
|
||||
///
|
||||
/// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic
|
||||
/// for this menu item. To display a `&` without assigning a mnemenonic, use `&&`
|
||||
/// for this menu item. To display a `&` without assigning a mnemenonic, use `&&`.
|
||||
pub fn new<S: AsRef<str>>(text: S, enabled: bool, acccelerator: Option<Accelerator>) -> Self {
|
||||
Self(crate::platform_impl::MenuItem::new(
|
||||
Self(Rc::new(RefCell::new(crate::platform_impl::MenuChild::new(
|
||||
text.as_ref(),
|
||||
enabled,
|
||||
acccelerator,
|
||||
))
|
||||
))))
|
||||
}
|
||||
|
||||
/// Returns a unique identifier associated with this menu item.
|
||||
pub fn id(&self) -> u32 {
|
||||
self.0.id()
|
||||
self.0.borrow().id()
|
||||
}
|
||||
|
||||
/// Get the text for this menu item.
|
||||
pub fn text(&self) -> String {
|
||||
self.0.text()
|
||||
self.0.borrow().text()
|
||||
}
|
||||
|
||||
/// Set the text for this menu item. `text` could optionally contain
|
||||
/// an `&` before a character to assign this character as the mnemonic
|
||||
/// for this menu item. To display a `&` without assigning a mnemenonic, use `&&`
|
||||
/// for this menu item. To display a `&` without assigning a mnemenonic, use `&&`.
|
||||
pub fn set_text<S: AsRef<str>>(&self, text: S) {
|
||||
self.0.set_text(text.as_ref())
|
||||
self.0.borrow_mut().set_text(text.as_ref())
|
||||
}
|
||||
|
||||
/// Get whether this menu item is enabled or not.
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.0.is_enabled()
|
||||
self.0.borrow().is_enabled()
|
||||
}
|
||||
|
||||
/// Enable or disable this menu item.
|
||||
pub fn set_enabled(&self, enabled: bool) {
|
||||
self.0.set_enabled(enabled)
|
||||
self.0.borrow_mut().set_enabled(enabled)
|
||||
}
|
||||
|
||||
/// Set this menu item accelerator.
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) {
|
||||
self.0.set_accelerator(acccelerator)
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) -> crate::Result<()> {
|
||||
self.0.borrow_mut().set_accelerator(acccelerator)
|
||||
}
|
||||
}
|
|
@ -2,7 +2,9 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::{accelerator::Accelerator, icon::Icon, MenuItemExt, MenuItemType};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{accelerator::Accelerator, icon::Icon, IsMenuItem, MenuItemType};
|
||||
use keyboard_types::{Code, Modifiers};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
|
@ -11,12 +13,13 @@ pub const CMD_OR_CTRL: Modifiers = Modifiers::META;
|
|||
pub const CMD_OR_CTRL: Modifiers = Modifiers::CONTROL;
|
||||
|
||||
/// A predefined (native) menu item which has a predfined behavior by the OS or by this crate.
|
||||
pub struct PredefinedMenuItem(pub(crate) crate::platform_impl::PredefinedMenuItem);
|
||||
pub struct PredefinedMenuItem(pub(crate) Rc<RefCell<crate::platform_impl::MenuChild>>);
|
||||
|
||||
unsafe impl MenuItemExt for PredefinedMenuItem {
|
||||
unsafe impl IsMenuItem for PredefinedMenuItem {
|
||||
fn type_(&self) -> MenuItemType {
|
||||
MenuItemType::Predefined
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
|
||||
self
|
||||
}
|
||||
|
@ -156,24 +159,26 @@ impl PredefinedMenuItem {
|
|||
}
|
||||
|
||||
fn new<S: AsRef<str>>(item: PredfinedMenuItemType, text: Option<S>) -> Self {
|
||||
Self(crate::platform_impl::PredefinedMenuItem::new(
|
||||
Self(Rc::new(RefCell::new(
|
||||
crate::platform_impl::MenuChild::new_predefined(
|
||||
item,
|
||||
text.map(|t| t.as_ref().to_string()),
|
||||
))
|
||||
),
|
||||
)))
|
||||
}
|
||||
|
||||
fn id(&self) -> u32 {
|
||||
self.0.id()
|
||||
self.0.borrow().id()
|
||||
}
|
||||
|
||||
/// Get the text for this predefined menu item.
|
||||
pub fn text(&self) -> String {
|
||||
self.0.text()
|
||||
self.0.borrow().text()
|
||||
}
|
||||
|
||||
/// Set the text for this predefined menu item.
|
||||
pub fn set_text<S: AsRef<str>>(&self, text: S) {
|
||||
self.0.set_text(text.as_ref())
|
||||
self.0.borrow_mut().set_text(text.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,6 +242,7 @@ pub struct AboutMetadata {
|
|||
}
|
||||
|
||||
impl AboutMetadata {
|
||||
#[allow(unused)]
|
||||
pub(crate) fn full_version(&self) -> Option<String> {
|
||||
Some(format!(
|
||||
"{}{}",
|
|
@ -2,18 +2,21 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::{util::AddOp, ContextMenu, MenuItemExt, MenuItemType};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{util::AddOp, ContextMenu, IsMenuItem, MenuItemType};
|
||||
|
||||
/// A menu that can be added to a [`Menu`] or another [`Submenu`].
|
||||
///
|
||||
/// [`Menu`]: crate::Menu
|
||||
#[derive(Clone)]
|
||||
pub struct Submenu(pub(crate) crate::platform_impl::Submenu);
|
||||
pub struct Submenu(pub(crate) Rc<RefCell<crate::platform_impl::MenuChild>>);
|
||||
|
||||
unsafe impl MenuItemExt for Submenu {
|
||||
unsafe impl IsMenuItem for Submenu {
|
||||
fn type_(&self) -> MenuItemType {
|
||||
MenuItemType::Submenu
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
|
||||
self
|
||||
}
|
||||
|
@ -27,90 +30,101 @@ impl Submenu {
|
|||
/// Create a new submenu.
|
||||
///
|
||||
/// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic
|
||||
/// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`
|
||||
/// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`.
|
||||
pub fn new<S: AsRef<str>>(text: S, enabled: bool) -> Self {
|
||||
Self(crate::platform_impl::Submenu::new(text.as_ref(), enabled))
|
||||
Self(Rc::new(RefCell::new(
|
||||
crate::platform_impl::MenuChild::new_submenu(text.as_ref(), enabled),
|
||||
)))
|
||||
}
|
||||
|
||||
/// Creates a new submenu with given `items`. It calls [`Submenu::new`] and [`Submenu::append_items`] internally.
|
||||
pub fn with_items<S: AsRef<str>>(text: S, enabled: bool, items: &[&dyn MenuItemExt]) -> Self {
|
||||
pub fn with_items<S: AsRef<str>>(
|
||||
text: S,
|
||||
enabled: bool,
|
||||
items: &[&dyn IsMenuItem],
|
||||
) -> crate::Result<Self> {
|
||||
let menu = Self::new(text, enabled);
|
||||
menu.append_items(items);
|
||||
menu
|
||||
menu.append_items(items)?;
|
||||
Ok(menu)
|
||||
}
|
||||
|
||||
/// Returns a unique identifier associated with this submenu.
|
||||
pub fn id(&self) -> u32 {
|
||||
self.0.id()
|
||||
self.0.borrow().id()
|
||||
}
|
||||
|
||||
/// Add a menu item to the end of this menu.
|
||||
pub fn append(&self, item: &dyn MenuItemExt) {
|
||||
self.0.add_menu_item(item, AddOp::Append)
|
||||
pub fn append(&self, item: &dyn IsMenuItem) -> crate::Result<()> {
|
||||
self.0.borrow_mut().add_menu_item(item, AddOp::Append)
|
||||
}
|
||||
|
||||
/// Add menu items to the end of this submenu. It calls [`Submenu::append`] in a loop.
|
||||
pub fn append_items(&self, items: &[&dyn MenuItemExt]) {
|
||||
pub fn append_items(&self, items: &[&dyn IsMenuItem]) -> crate::Result<()> {
|
||||
for item in items {
|
||||
self.append(*item);
|
||||
self.append(*item)?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a menu item to the beginning of this submenu.
|
||||
pub fn prepend(&self, item: &dyn MenuItemExt) {
|
||||
self.0.add_menu_item(item, AddOp::Insert(0))
|
||||
pub fn prepend(&self, item: &dyn IsMenuItem) -> crate::Result<()> {
|
||||
self.0.borrow_mut().add_menu_item(item, AddOp::Insert(0))
|
||||
}
|
||||
|
||||
/// Add menu items to the beginning of this submenu.
|
||||
/// It calls [`Menu::prepend`](crate::Menu::prepend) on the first element and
|
||||
/// passes the rest to [`Menu::insert_items`](crate::Menu::insert_items) with position of `1`.
|
||||
pub fn prepend_items(&self, items: &[&dyn MenuItemExt]) {
|
||||
self.prepend(items[0]);
|
||||
self.insert_items(&items[1..], 1);
|
||||
pub fn prepend_items(&self, items: &[&dyn IsMenuItem]) -> crate::Result<()> {
|
||||
self.insert_items(items, 0)
|
||||
}
|
||||
|
||||
/// Insert a menu item at the specified `postion` in the submenu.
|
||||
pub fn insert(&self, item: &dyn MenuItemExt, position: usize) {
|
||||
self.0.add_menu_item(item, AddOp::Insert(position))
|
||||
pub fn insert(&self, item: &dyn IsMenuItem, position: usize) -> crate::Result<()> {
|
||||
self.0
|
||||
.borrow_mut()
|
||||
.add_menu_item(item, AddOp::Insert(position))
|
||||
}
|
||||
|
||||
/// Insert menu items at the specified `postion` in the submenu.
|
||||
pub fn insert_items(&self, items: &[&dyn MenuItemExt], position: usize) {
|
||||
pub fn insert_items(&self, items: &[&dyn IsMenuItem], position: usize) -> crate::Result<()> {
|
||||
for (i, item) in items.iter().enumerate() {
|
||||
self.insert(*item, position + i)
|
||||
self.insert(*item, position + i)?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a menu item from this submenu.
|
||||
pub fn remove(&self, item: &dyn MenuItemExt) -> crate::Result<()> {
|
||||
self.0.remove(item)
|
||||
pub fn remove(&self, item: &dyn IsMenuItem) -> crate::Result<()> {
|
||||
self.0.borrow_mut().remove(item)
|
||||
}
|
||||
|
||||
/// Returns a list of menu items that has been added to this submenu.
|
||||
pub fn items(&self) -> Vec<Box<dyn MenuItemExt>> {
|
||||
self.0.items()
|
||||
pub fn items(&self) -> Vec<Box<dyn IsMenuItem>> {
|
||||
self.0.borrow().items()
|
||||
}
|
||||
|
||||
/// Get the text for this submenu.
|
||||
pub fn text(&self) -> String {
|
||||
self.0.text()
|
||||
self.0.borrow().text()
|
||||
}
|
||||
|
||||
/// Set the text for this submenu. `text` could optionally contain
|
||||
/// an `&` before a character to assign this character as the mnemonic
|
||||
/// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`
|
||||
/// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`.
|
||||
pub fn set_text<S: AsRef<str>>(&self, text: S) {
|
||||
self.0.set_text(text.as_ref())
|
||||
self.0.borrow_mut().set_text(text.as_ref())
|
||||
}
|
||||
|
||||
/// Get whether this submenu is enabled or not.
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.0.is_enabled()
|
||||
self.0.borrow().is_enabled()
|
||||
}
|
||||
|
||||
/// Enable or disable this submenu.
|
||||
pub fn set_enabled(&self, enabled: bool) {
|
||||
self.0.set_enabled(enabled)
|
||||
self.0.borrow_mut().set_enabled(enabled)
|
||||
}
|
||||
|
||||
/// Set this submenu as the Window menu for the application on macOS.
|
||||
|
@ -119,7 +133,7 @@ impl Submenu {
|
|||
/// certain other items to the menu.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn set_windows_menu_for_nsapp(&self) {
|
||||
self.0.set_windows_menu_for_nsapp()
|
||||
self.0.borrow_mut().set_windows_menu_for_nsapp()
|
||||
}
|
||||
|
||||
/// Set this submenu as the Help menu for the application on macOS.
|
||||
|
@ -130,48 +144,50 @@ impl Submenu {
|
|||
/// which has a title matching the localized word "Help".
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn set_help_menu_for_nsapp(&self) {
|
||||
self.0.set_help_menu_for_nsapp()
|
||||
self.0.borrow_mut().set_help_menu_for_nsapp()
|
||||
}
|
||||
}
|
||||
|
||||
impl ContextMenu for Submenu {
|
||||
#[cfg(target_os = "windows")]
|
||||
fn hpopupmenu(&self) -> windows_sys::Win32::UI::WindowsAndMessaging::HMENU {
|
||||
self.0.hpopupmenu()
|
||||
self.0.borrow().hpopupmenu()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn show_context_menu_for_hwnd(&self, hwnd: isize, x: f64, y: f64) {
|
||||
self.0.show_context_menu_for_hwnd(hwnd, x, y)
|
||||
self.0.borrow_mut().show_context_menu_for_hwnd(hwnd, x, y)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) {
|
||||
self.0.attach_menu_subclass_for_hwnd(hwnd)
|
||||
self.0.borrow_mut().attach_menu_subclass_for_hwnd(hwnd)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn detach_menu_subclass_from_hwnd(&self, hwnd: isize) {
|
||||
self.0.detach_menu_subclass_from_hwnd(hwnd)
|
||||
self.0.borrow_mut().detach_menu_subclass_from_hwnd(hwnd)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn show_context_menu_for_gtk_window(&self, w: >k::ApplicationWindow, x: f64, y: f64) {
|
||||
self.0.show_context_menu_for_gtk_window(w, x, y)
|
||||
self.0
|
||||
.borrow_mut()
|
||||
.show_context_menu_for_gtk_window(w, x, y)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn gtk_context_menu(&self) -> gtk::Menu {
|
||||
self.0.gtk_context_menu()
|
||||
self.0.borrow_mut().gtk_context_menu()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn show_context_menu_for_nsview(&self, view: cocoa::base::id, x: f64, y: f64) {
|
||||
self.0.show_context_menu_for_nsview(view, x, y)
|
||||
self.0.borrow_mut().show_context_menu_for_nsview(view, x, y)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn ns_menu(&self) -> *mut std::ffi::c_void {
|
||||
self.0.ns_menu()
|
||||
self.0.borrow().ns_menu()
|
||||
}
|
||||
}
|
20
src/lib.rs
20
src/lib.rs
|
@ -63,7 +63,7 @@
|
|||
//! &PredefinedMenuItem::separator(),
|
||||
//! &menu_item2,
|
||||
//! ],
|
||||
//! ),
|
||||
//! ).unwrap(),
|
||||
//! ],
|
||||
//! );
|
||||
//! ```
|
||||
|
@ -129,14 +129,10 @@ use crossbeam_channel::{unbounded, Receiver, Sender};
|
|||
use once_cell::sync::{Lazy, OnceCell};
|
||||
|
||||
pub mod accelerator;
|
||||
mod check_menu_item;
|
||||
mod error;
|
||||
mod icon_menu_item;
|
||||
mod items;
|
||||
mod menu;
|
||||
mod menu_item;
|
||||
mod platform_impl;
|
||||
mod predefined;
|
||||
mod submenu;
|
||||
mod util;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
|
@ -144,14 +140,12 @@ mod util;
|
|||
extern crate objc;
|
||||
|
||||
pub use self::error::*;
|
||||
pub use check_menu_item::CheckMenuItem;
|
||||
pub use icon_menu_item::IconMenuItem;
|
||||
pub use items::*;
|
||||
pub use menu::Menu;
|
||||
pub mod icon;
|
||||
pub use menu_item::MenuItem;
|
||||
pub use predefined::{AboutMetadata, PredefinedMenuItem};
|
||||
pub use submenu::Submenu;
|
||||
|
||||
/// An enumeration of all available menu types, useful to match against
|
||||
/// the items return from [`Menu::items`] or [`Submenu::items`]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum MenuItemType {
|
||||
Submenu,
|
||||
|
@ -174,7 +168,7 @@ impl Default for MenuItemType {
|
|||
/// This trait is ONLY meant to be implemented internally.
|
||||
// TODO(amrbashir): first person to replace this trait with an enum while keeping `Menu.append_items`
|
||||
// taking mix of types (`MenuItem`, `CheckMenuItem`, `Submenu`...etc) in the same call, gets a cookie.
|
||||
pub unsafe trait MenuItemExt {
|
||||
pub unsafe trait IsMenuItem {
|
||||
/// Get the type of this menu entry
|
||||
fn type_(&self) -> MenuItemType;
|
||||
|
||||
|
@ -246,7 +240,7 @@ pub trait ContextMenu {
|
|||
}
|
||||
|
||||
/// Describes a menu event emitted when a menu item is activated
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct MenuEvent {
|
||||
/// Id of the menu item which triggered this event
|
||||
pub id: u32,
|
||||
|
|
112
src/menu.rs
112
src/menu.rs
|
@ -2,12 +2,14 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::{util::AddOp, ContextMenu, MenuItemExt};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{util::AddOp, ContextMenu, IsMenuItem};
|
||||
|
||||
/// A root menu that can be added to a Window on Windows and Linux
|
||||
/// and used as the app global menu on macOS.
|
||||
#[derive(Clone)]
|
||||
pub struct Menu(crate::platform_impl::Menu);
|
||||
pub struct Menu(Rc<RefCell<crate::platform_impl::Menu>>);
|
||||
|
||||
impl Default for Menu {
|
||||
fn default() -> Self {
|
||||
|
@ -18,14 +20,19 @@ impl Default for Menu {
|
|||
impl Menu {
|
||||
/// Creates a new menu.
|
||||
pub fn new() -> Self {
|
||||
Self(crate::platform_impl::Menu::new())
|
||||
Self(Rc::new(RefCell::new(crate::platform_impl::Menu::new())))
|
||||
}
|
||||
|
||||
/// Creates a new menu with given `items`. It calls [`Menu::new`] and [`Menu::append_items`] internally.
|
||||
pub fn with_items(items: &[&dyn MenuItemExt]) -> Self {
|
||||
pub fn with_items(items: &[&dyn IsMenuItem]) -> crate::Result<Self> {
|
||||
let menu = Self::new();
|
||||
menu.append_items(items);
|
||||
menu
|
||||
menu.append_items(items)?;
|
||||
Ok(menu)
|
||||
}
|
||||
|
||||
/// Returns a unique identifier associated with this menu.
|
||||
pub fn id(&self) -> u32 {
|
||||
self.0.borrow().id()
|
||||
}
|
||||
|
||||
/// Add a menu item to the end of this menu.
|
||||
|
@ -35,8 +42,8 @@ impl Menu {
|
|||
/// - **macOS:** Only [`Submenu`] can be added to the menu
|
||||
///
|
||||
/// [`Submenu`]: crate::Submenu
|
||||
pub fn append(&self, item: &dyn MenuItemExt) {
|
||||
self.0.add_menu_item(item, AddOp::Append)
|
||||
pub fn append(&self, item: &dyn IsMenuItem) -> crate::Result<()> {
|
||||
self.0.borrow_mut().add_menu_item(item, AddOp::Append)
|
||||
}
|
||||
|
||||
/// Add menu items to the end of this menu. It calls [`Menu::append`] in a loop internally.
|
||||
|
@ -46,10 +53,12 @@ impl Menu {
|
|||
/// - **macOS:** Only [`Submenu`] can be added to the menu
|
||||
///
|
||||
/// [`Submenu`]: crate::Submenu
|
||||
pub fn append_items(&self, items: &[&dyn MenuItemExt]) {
|
||||
pub fn append_items(&self, items: &[&dyn IsMenuItem]) -> crate::Result<()> {
|
||||
for item in items {
|
||||
self.append(*item);
|
||||
self.append(*item)?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a menu item to the beginning of this menu.
|
||||
|
@ -59,8 +68,8 @@ impl Menu {
|
|||
/// - **macOS:** Only [`Submenu`] can be added to the menu
|
||||
///
|
||||
/// [`Submenu`]: crate::Submenu
|
||||
pub fn prepend(&self, item: &dyn MenuItemExt) {
|
||||
self.0.add_menu_item(item, AddOp::Insert(0))
|
||||
pub fn prepend(&self, item: &dyn IsMenuItem) -> crate::Result<()> {
|
||||
self.0.borrow_mut().add_menu_item(item, AddOp::Insert(0))
|
||||
}
|
||||
|
||||
/// Add menu items to the beginning of this menu. It calls [`Menu::insert_items`] with position of `0` internally.
|
||||
|
@ -70,8 +79,8 @@ impl Menu {
|
|||
/// - **macOS:** Only [`Submenu`] can be added to the menu
|
||||
///
|
||||
/// [`Submenu`]: crate::Submenu
|
||||
pub fn prepend_items(&self, items: &[&dyn MenuItemExt]) {
|
||||
self.insert_items(items, 0);
|
||||
pub fn prepend_items(&self, items: &[&dyn IsMenuItem]) -> crate::Result<()> {
|
||||
self.insert_items(items, 0)
|
||||
}
|
||||
|
||||
/// Insert a menu item at the specified `postion` in the menu.
|
||||
|
@ -81,8 +90,10 @@ impl Menu {
|
|||
/// - **macOS:** Only [`Submenu`] can be added to the menu
|
||||
///
|
||||
/// [`Submenu`]: crate::Submenu
|
||||
pub fn insert(&self, item: &dyn MenuItemExt, position: usize) {
|
||||
self.0.add_menu_item(item, AddOp::Insert(position))
|
||||
pub fn insert(&self, item: &dyn IsMenuItem, position: usize) -> crate::Result<()> {
|
||||
self.0
|
||||
.borrow_mut()
|
||||
.add_menu_item(item, AddOp::Insert(position))
|
||||
}
|
||||
|
||||
/// Insert menu items at the specified `postion` in the menu.
|
||||
|
@ -92,20 +103,22 @@ impl Menu {
|
|||
/// - **macOS:** Only [`Submenu`] can be added to the menu
|
||||
///
|
||||
/// [`Submenu`]: crate::Submenu
|
||||
pub fn insert_items(&self, items: &[&dyn MenuItemExt], position: usize) {
|
||||
pub fn insert_items(&self, items: &[&dyn IsMenuItem], position: usize) -> crate::Result<()> {
|
||||
for (i, item) in items.iter().enumerate() {
|
||||
self.insert(*item, position + i)
|
||||
self.insert(*item, position + i)?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a menu item from this menu.
|
||||
pub fn remove(&self, item: &dyn MenuItemExt) -> crate::Result<()> {
|
||||
self.0.remove(item)
|
||||
pub fn remove(&self, item: &dyn IsMenuItem) -> crate::Result<()> {
|
||||
self.0.borrow_mut().remove(item)
|
||||
}
|
||||
|
||||
/// Returns a list of menu items that has been added to this menu.
|
||||
pub fn items(&self) -> Vec<Box<dyn MenuItemExt>> {
|
||||
self.0.items()
|
||||
pub fn items(&self) -> Vec<Box<dyn IsMenuItem>> {
|
||||
self.0.borrow().items()
|
||||
}
|
||||
|
||||
/// Adds this menu to a [`gtk::ApplicationWindow`]
|
||||
|
@ -128,7 +141,7 @@ impl Menu {
|
|||
W: gtk::prelude::IsA<gtk::Container>,
|
||||
W: gtk::prelude::IsA<gtk::Window>,
|
||||
{
|
||||
self.0.init_for_gtk_window(w)
|
||||
self.0.borrow_mut().init_for_gtk_window(w)
|
||||
}
|
||||
|
||||
/// Adds this menu to a win32 window.
|
||||
|
@ -157,7 +170,7 @@ impl Menu {
|
|||
/// ```
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn init_for_hwnd(&self, hwnd: isize) -> crate::Result<()> {
|
||||
self.0.init_for_hwnd(hwnd)
|
||||
self.0.borrow_mut().init_for_hwnd(hwnd)
|
||||
}
|
||||
|
||||
/// Returns The [`HACCEL`](windows_sys::Win32::UI::WindowsAndMessaging::HACCEL) associated with this menu
|
||||
|
@ -165,7 +178,7 @@ impl Menu {
|
|||
/// in the event loop to enable accelerators
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn haccel(&self) -> windows_sys::Win32::UI::WindowsAndMessaging::HACCEL {
|
||||
self.0.haccel()
|
||||
self.0.borrow_mut().haccel()
|
||||
}
|
||||
|
||||
/// Removes this menu from a [`gtk::ApplicationWindow`]
|
||||
|
@ -175,13 +188,13 @@ impl Menu {
|
|||
W: gtk::prelude::IsA<gtk::ApplicationWindow>,
|
||||
W: gtk::prelude::IsA<gtk::Window>,
|
||||
{
|
||||
self.0.remove_for_gtk_window(w)
|
||||
self.0.borrow_mut().remove_for_gtk_window(w)
|
||||
}
|
||||
|
||||
/// Removes this menu from a win32 window
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn remove_for_hwnd(&self, hwnd: isize) -> crate::Result<()> {
|
||||
self.0.remove_for_hwnd(hwnd)
|
||||
self.0.borrow_mut().remove_for_hwnd(hwnd)
|
||||
}
|
||||
|
||||
/// Hides this menu from a [`gtk::ApplicationWindow`]
|
||||
|
@ -190,13 +203,13 @@ impl Menu {
|
|||
where
|
||||
W: gtk::prelude::IsA<gtk::ApplicationWindow>,
|
||||
{
|
||||
self.0.hide_for_gtk_window(w)
|
||||
self.0.borrow_mut().hide_for_gtk_window(w)
|
||||
}
|
||||
|
||||
/// Hides this menu from a win32 window
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn hide_for_hwnd(&self, hwnd: isize) -> crate::Result<()> {
|
||||
self.0.hide_for_hwnd(hwnd)
|
||||
self.0.borrow().hide_for_hwnd(hwnd)
|
||||
}
|
||||
|
||||
/// Shows this menu on a [`gtk::ApplicationWindow`]
|
||||
|
@ -205,66 +218,83 @@ impl Menu {
|
|||
where
|
||||
W: gtk::prelude::IsA<gtk::ApplicationWindow>,
|
||||
{
|
||||
self.0.show_for_gtk_window(w)
|
||||
self.0.borrow_mut().show_for_gtk_window(w)
|
||||
}
|
||||
|
||||
/// Shows this menu on a win32 window
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn show_for_hwnd(&self, hwnd: isize) -> crate::Result<()> {
|
||||
self.0.show_for_hwnd(hwnd)
|
||||
self.0.borrow().show_for_hwnd(hwnd)
|
||||
}
|
||||
|
||||
/// Returns whether this menu visible on a [`gtk::ApplicationWindow`]
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn is_visible_on_gtk_window<W>(&self, w: &W) -> bool
|
||||
where
|
||||
W: gtk::prelude::IsA<gtk::ApplicationWindow>,
|
||||
{
|
||||
self.0.borrow().is_visible_on_gtk_window(w)
|
||||
}
|
||||
|
||||
/// Returns whether this menu visible on a on a win32 window
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn is_visible_on_hwnd(&self, hwnd: isize) -> bool {
|
||||
self.0.borrow().is_visible_on_hwnd(hwnd)
|
||||
}
|
||||
|
||||
/// Adds this menu to an NSApp.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn init_for_nsapp(&self) {
|
||||
self.0.init_for_nsapp()
|
||||
self.0.borrow_mut().init_for_nsapp()
|
||||
}
|
||||
|
||||
/// Removes this menu from an NSApp.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn remove_for_nsapp(&self) {
|
||||
self.0.remove_for_nsapp()
|
||||
self.0.borrow_mut().remove_for_nsapp()
|
||||
}
|
||||
}
|
||||
|
||||
impl ContextMenu for Menu {
|
||||
#[cfg(target_os = "windows")]
|
||||
fn hpopupmenu(&self) -> windows_sys::Win32::UI::WindowsAndMessaging::HMENU {
|
||||
self.0.hpopupmenu()
|
||||
self.0.borrow().hpopupmenu()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn show_context_menu_for_hwnd(&self, hwnd: isize, x: f64, y: f64) {
|
||||
self.0.show_context_menu_for_hwnd(hwnd, x, y)
|
||||
self.0.borrow().show_context_menu_for_hwnd(hwnd, x, y)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) {
|
||||
self.0.attach_menu_subclass_for_hwnd(hwnd)
|
||||
self.0.borrow().attach_menu_subclass_for_hwnd(hwnd)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn detach_menu_subclass_from_hwnd(&self, hwnd: isize) {
|
||||
self.0.detach_menu_subclass_from_hwnd(hwnd)
|
||||
self.0.borrow().detach_menu_subclass_from_hwnd(hwnd)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn show_context_menu_for_gtk_window(&self, w: >k::ApplicationWindow, x: f64, y: f64) {
|
||||
self.0.show_context_menu_for_gtk_window(w, x, y)
|
||||
self.0
|
||||
.borrow_mut()
|
||||
.show_context_menu_for_gtk_window(w, x, y)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn gtk_context_menu(&self) -> gtk::Menu {
|
||||
self.0.gtk_context_menu()
|
||||
self.0.borrow_mut().gtk_context_menu()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn show_context_menu_for_nsview(&self, view: cocoa::base::id, x: f64, y: f64) {
|
||||
self.0.show_context_menu_for_nsview(view, x, y)
|
||||
self.0.borrow_mut().show_context_menu_for_nsview(view, x, y)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn ns_menu(&self) -> *mut std::ffi::c_void {
|
||||
self.0.ns_menu()
|
||||
self.0.borrow().ns_menu()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use gtk::{prelude::*, AccelGroup};
|
||||
use keyboard_types::{Code, Modifiers};
|
||||
|
||||
use crate::accelerator::Accelerator;
|
||||
|
@ -78,7 +77,7 @@ pub fn parse_accelerator(accelerator: &Accelerator) -> crate::Result<(gdk::Modif
|
|||
if let Some(gdk_key) = key_to_raw_key(k) {
|
||||
*gdk_key
|
||||
} else {
|
||||
return Err(crate::Error::AcceleratorKeyNotSupported(*k));
|
||||
return Err(crate::Error::UnrecognizedAcceleratorCode(k.to_string()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -86,29 +85,6 @@ pub fn parse_accelerator(accelerator: &Accelerator) -> crate::Result<(gdk::Modif
|
|||
Ok((modifiers_to_gdk_modifier_type(accelerator.mods), key))
|
||||
}
|
||||
|
||||
pub fn register_accelerator<M: IsA<gtk::Widget>>(
|
||||
item: &M,
|
||||
accel_group: &AccelGroup,
|
||||
accelerator: &Accelerator,
|
||||
) -> Option<(gdk::ModifierType, u32)> {
|
||||
if let Ok((mods, key)) = parse_accelerator(accelerator) {
|
||||
item.add_accelerator("activate", accel_group, key, mods, gtk::AccelFlags::VISIBLE);
|
||||
Some((mods, key))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_accelerator<M: IsA<gtk::Widget>>(
|
||||
item: &M,
|
||||
accel_group: &AccelGroup,
|
||||
accelerator: &Accelerator,
|
||||
) {
|
||||
if let Ok((mods, key)) = parse_accelerator(accelerator) {
|
||||
item.remove_accelerator(accel_group, key, mods);
|
||||
}
|
||||
}
|
||||
|
||||
fn modifiers_to_gdk_modifier_type(modifiers: Modifiers) -> gdk::ModifierType {
|
||||
let mut result = gdk::ModifierType::empty();
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -11,8 +11,8 @@ impl Accelerator {
|
|||
/// Return the string value of this hotkey, without modifiers.
|
||||
///
|
||||
/// Returns the empty string if no key equivalent is known.
|
||||
pub fn key_equivalent(self) -> String {
|
||||
match self.key {
|
||||
pub fn key_equivalent(self) -> crate::Result<String> {
|
||||
Ok(match self.key {
|
||||
Code::KeyA => "a".into(),
|
||||
Code::KeyB => "b".into(),
|
||||
Code::KeyC => "c".into(),
|
||||
|
@ -103,12 +103,8 @@ impl Accelerator {
|
|||
Code::F22 => "\u{F719}".into(),
|
||||
Code::F23 => "\u{F71A}".into(),
|
||||
Code::F24 => "\u{F71B}".into(),
|
||||
_ => {
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("no key equivalent for {:?}", self);
|
||||
"".into()
|
||||
}
|
||||
}
|
||||
key => return Err(crate::Error::UnrecognizedAcceleratorCode(key.to_string())),
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the modifiers of this hotkey, as an NSEventModifierFlags bitflag.
|
||||
|
|
|
@ -43,18 +43,15 @@ impl PlatformIcon {
|
|||
let (width, height) = self.get_size();
|
||||
let icon = self.to_png();
|
||||
|
||||
let (icon_height, icon_width) = match fixed_height {
|
||||
let (icon_width, icon_height) = match fixed_height {
|
||||
Some(fixed_height) => {
|
||||
let icon_height: f64 = fixed_height;
|
||||
let icon_width: f64 = (width as f64) / (height as f64 / icon_height);
|
||||
|
||||
(icon_height, icon_width)
|
||||
(icon_width, icon_height)
|
||||
}
|
||||
|
||||
None => {
|
||||
let (icon_height, icon_width) = self.get_size();
|
||||
(icon_height as f64, icon_width as f64)
|
||||
}
|
||||
None => (width as f64, height as f64),
|
||||
};
|
||||
|
||||
let nsdata = NSData::dataWithBytes_length_(
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -12,4 +12,58 @@ mod platform;
|
|||
#[path = "macos/mod.rs"]
|
||||
mod platform;
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{items::*, IsMenuItem, MenuItemType};
|
||||
|
||||
pub(crate) use self::platform::*;
|
||||
|
||||
impl dyn IsMenuItem + '_ {
|
||||
fn child(&self) -> Rc<RefCell<MenuChild>> {
|
||||
match self.type_() {
|
||||
MenuItemType::Submenu => self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::Submenu>()
|
||||
.unwrap()
|
||||
.0
|
||||
.clone(),
|
||||
MenuItemType::Normal => self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::MenuItem>()
|
||||
.unwrap()
|
||||
.0
|
||||
.clone(),
|
||||
MenuItemType::Predefined => self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::PredefinedMenuItem>()
|
||||
.unwrap()
|
||||
.0
|
||||
.clone(),
|
||||
MenuItemType::Check => self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::CheckMenuItem>()
|
||||
.unwrap()
|
||||
.0
|
||||
.clone(),
|
||||
MenuItemType::Icon => self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::IconMenuItem>()
|
||||
.unwrap()
|
||||
.0
|
||||
.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal utilities
|
||||
impl MenuChild {
|
||||
fn boxed(&self, c: Rc<RefCell<MenuChild>>) -> Box<dyn IsMenuItem> {
|
||||
match self.type_ {
|
||||
MenuItemType::Submenu => Box::new(Submenu(c)),
|
||||
MenuItemType::Normal => Box::new(MenuItem(c)),
|
||||
MenuItemType::Predefined => Box::new(PredefinedMenuItem(c)),
|
||||
MenuItemType::Check => Box::new(CheckMenuItem(c)),
|
||||
MenuItemType::Icon => Box::new(IconMenuItem(c)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::accelerator::Accelerator;
|
|||
|
||||
impl Accelerator {
|
||||
// Convert a hotkey to an accelerator.
|
||||
pub fn to_accel(&self, menu_id: u16) -> ACCEL {
|
||||
pub fn to_accel(&self, menu_id: u16) -> crate::Result<ACCEL> {
|
||||
let mut virt_key = FVIRTKEY;
|
||||
let key_mods: Modifiers = self.mods;
|
||||
if key_mods.contains(Modifiers::CONTROL) {
|
||||
|
@ -27,7 +27,7 @@ impl Accelerator {
|
|||
virt_key |= FSHIFT;
|
||||
}
|
||||
|
||||
let vk_code = key_to_vk(&self.key);
|
||||
let vk_code = key_to_vk(&self.key)?;
|
||||
let mod_code = vk_code >> 8;
|
||||
if mod_code & 0x1 != 0 {
|
||||
virt_key |= FSHIFT;
|
||||
|
@ -40,17 +40,17 @@ impl Accelerator {
|
|||
}
|
||||
let raw_key = vk_code & 0x00ff;
|
||||
|
||||
ACCEL {
|
||||
Ok(ACCEL {
|
||||
fVirt: virt_key,
|
||||
key: raw_key,
|
||||
cmd: menu_id,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// used to build accelerators table from Key
|
||||
fn key_to_vk(key: &Code) -> VIRTUAL_KEY {
|
||||
match key {
|
||||
fn key_to_vk(key: &Code) -> crate::Result<VIRTUAL_KEY> {
|
||||
Ok(match key {
|
||||
Code::KeyA => unsafe { VkKeyScanW('a' as u16) as u16 },
|
||||
Code::KeyB => unsafe { VkKeyScanW('b' as u16) as u16 },
|
||||
Code::KeyC => unsafe { VkKeyScanW('c' as u16) as u16 },
|
||||
|
@ -162,8 +162,8 @@ fn key_to_vk(key: &Code) -> VIRTUAL_KEY {
|
|||
Code::MediaPlayPause => VK_MEDIA_PLAY_PAUSE,
|
||||
Code::LaunchMail => VK_LAUNCH_MAIL,
|
||||
Code::Convert => VK_CONVERT,
|
||||
key => panic!("Unsupported key: {}", key),
|
||||
}
|
||||
key => return Err(crate::Error::UnrecognizedAcceleratorCode(key.to_string())),
|
||||
})
|
||||
}
|
||||
|
||||
impl fmt::Display for Accelerator {
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue