mirror of
https://github.com/italicsjenga/muda.git
synced 2024-12-23 12:01:31 +11:00
feat: add set_accelerator
(#64)
* feat: add `set_accelerator` closes #63 * unsafe
This commit is contained in:
parent
dfd7b9e437
commit
47ba0b47ed
5
.changes/set-accelerator.md
Normal file
5
.changes/set-accelerator.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"muda": "minor"
|
||||
---
|
||||
|
||||
Add `(MenuItem|CheckMenuItem|IconMenuItem)::set_accelerator` to change or disable the accelerator after creation.
|
|
@ -189,6 +189,8 @@ fn main() {
|
|||
|
||||
if let Ok(event) = menu_channel.try_recv() {
|
||||
if event.id == custom_i_1.id() {
|
||||
custom_i_1
|
||||
.set_accelerator(Some(Accelerator::new(Some(Modifiers::SHIFT), Code::KeyF)));
|
||||
file_m.insert(&MenuItem::new("New Menu Item", true, None), 2);
|
||||
}
|
||||
println!("{event:?}");
|
||||
|
|
|
@ -81,4 +81,9 @@ impl CheckMenuItem {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,4 +75,9 @@ impl IconMenuItem {
|
|||
pub fn set_icon(&self, icon: Option<Icon>) {
|
||||
self.0.set_icon(icon)
|
||||
}
|
||||
|
||||
/// Set this icon menu item accelerator.
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) {
|
||||
self.0.set_accelerator(acccelerator)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,4 +63,9 @@ impl MenuItem {
|
|||
pub fn set_enabled(&self, enabled: bool) {
|
||||
self.0.set_enabled(enabled)
|
||||
}
|
||||
|
||||
/// Set this menu item accelerator.
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) {
|
||||
self.0.set_accelerator(acccelerator)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,6 +99,16 @@ pub fn register_accelerator<M: IsA<gtk::Widget>>(
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
|
|
|
@ -14,7 +14,9 @@ use crate::{
|
|||
util::{AddOp, Counter},
|
||||
MenuEvent, MenuItemType,
|
||||
};
|
||||
use accelerator::{from_gtk_mnemonic, parse_accelerator, register_accelerator, to_gtk_mnemonic};
|
||||
use accelerator::{
|
||||
from_gtk_mnemonic, parse_accelerator, register_accelerator, remove_accelerator, to_gtk_mnemonic,
|
||||
};
|
||||
use gtk::{prelude::*, Orientation};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
|
@ -180,6 +182,20 @@ impl MenuChild {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_accelerator(&mut self, accelerator: Option<Accelerator>) {
|
||||
for items in self.gtk_menu_items.values() {
|
||||
for i in items {
|
||||
if let Some(accel) = self.accelerator {
|
||||
remove_accelerator(i, self.accel_group.as_ref().unwrap(), &accel);
|
||||
}
|
||||
if let Some(accel) = accelerator.as_ref() {
|
||||
register_accelerator(i, self.accel_group.as_ref().unwrap(), accel);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.accelerator = accelerator;
|
||||
}
|
||||
}
|
||||
|
||||
struct InnerMenu {
|
||||
|
@ -818,6 +834,8 @@ impl MenuItem {
|
|||
.sensitive(self_.enabled)
|
||||
.build();
|
||||
|
||||
self_.accel_group = accel_group.cloned();
|
||||
|
||||
if let Some(accelerator) = &self_.accelerator {
|
||||
if let Some(accel_group) = accel_group {
|
||||
register_accelerator(&item, accel_group, accelerator);
|
||||
|
@ -858,6 +876,10 @@ impl MenuItem {
|
|||
pub fn set_enabled(&self, enabled: bool) {
|
||||
self.0.borrow_mut().set_enabled(enabled)
|
||||
}
|
||||
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) {
|
||||
self.0.borrow_mut().set_accelerator(acccelerator)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -1033,6 +1055,8 @@ impl CheckMenuItem {
|
|||
.active(self_.checked)
|
||||
.build();
|
||||
|
||||
self_.accel_group = accel_group.cloned();
|
||||
|
||||
if let Some(accelerator) = &self_.accelerator {
|
||||
if let Some(accel_group) = accel_group {
|
||||
register_accelerator(&item, accel_group, accelerator);
|
||||
|
@ -1111,6 +1135,10 @@ impl CheckMenuItem {
|
|||
pub fn set_checked(&self, checked: bool) {
|
||||
self.0.borrow_mut().set_checked(checked)
|
||||
}
|
||||
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) {
|
||||
self.0.borrow_mut().set_accelerator(acccelerator)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -1152,6 +1180,8 @@ impl IconMenuItem {
|
|||
.map(|i| gtk::Image::from_pixbuf(Some(&i.inner.to_pixbuf(16, 16))))
|
||||
.unwrap_or_else(gtk::Image::default);
|
||||
|
||||
self_.accel_group = accel_group.cloned();
|
||||
|
||||
let label = gtk::AccelLabel::builder()
|
||||
.label(&to_gtk_mnemonic(&self_.text))
|
||||
.use_underline(true)
|
||||
|
@ -1223,6 +1253,10 @@ impl IconMenuItem {
|
|||
pub fn set_icon(&self, icon: Option<Icon>) {
|
||||
self.0.borrow_mut().set_icon(icon)
|
||||
}
|
||||
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) {
|
||||
self.0.borrow_mut().set_accelerator(acccelerator)
|
||||
}
|
||||
}
|
||||
|
||||
impl dyn crate::MenuItemExt + '_ {
|
||||
|
|
|
@ -146,6 +146,34 @@ impl MenuChild {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_accelerator(&mut self, accelerator: Option<Accelerator>) {
|
||||
let key_equivalent = (accelerator)
|
||||
.as_ref()
|
||||
.map(|accel| accel.key_equivalent())
|
||||
.unwrap_or_default();
|
||||
let key_equivalent = unsafe {
|
||||
NSString::alloc(nil)
|
||||
.init_str(key_equivalent.as_str())
|
||||
.autorelease()
|
||||
};
|
||||
|
||||
let modifier_mask = (accelerator)
|
||||
.as_ref()
|
||||
.map(|accel| accel.key_modifier_mask())
|
||||
.unwrap_or_else(NSEventModifierFlags::empty);
|
||||
|
||||
for ns_items in self.ns_menu_items.values() {
|
||||
for &ns_item in ns_items {
|
||||
unsafe {
|
||||
let _: () = msg_send![ns_item, setKeyEquivalent: key_equivalent];
|
||||
ns_item.setKeyEquivalentModifierMask_(modifier_mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.accelerator = accelerator;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -556,6 +584,10 @@ impl MenuItem {
|
|||
pub fn set_enabled(&self, enabled: bool) {
|
||||
self.0.borrow_mut().set_enabled(enabled)
|
||||
}
|
||||
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) {
|
||||
self.0.borrow_mut().set_accelerator(acccelerator)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -719,6 +751,10 @@ impl CheckMenuItem {
|
|||
pub fn set_checked(&self, checked: bool) {
|
||||
self.0.borrow_mut().set_checked(checked)
|
||||
}
|
||||
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) {
|
||||
self.0.borrow_mut().set_accelerator(acccelerator)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -798,6 +834,10 @@ impl IconMenuItem {
|
|||
pub fn set_icon(&self, icon: Option<Icon>) {
|
||||
self.0.borrow_mut().set_icon(icon)
|
||||
}
|
||||
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) {
|
||||
self.0.borrow_mut().set_accelerator(acccelerator)
|
||||
}
|
||||
}
|
||||
|
||||
impl PredfinedMenuItemType {
|
||||
|
|
|
@ -15,7 +15,12 @@ use crate::{
|
|||
util::{AddOp, Counter},
|
||||
MenuEvent, MenuItemType,
|
||||
};
|
||||
use std::{cell::RefCell, fmt::Debug, rc::Rc};
|
||||
use std::{
|
||||
cell::{RefCell, RefMut},
|
||||
collections::HashMap,
|
||||
fmt::Debug,
|
||||
rc::Rc,
|
||||
};
|
||||
use util::{decode_wide, encode_wide, Accel};
|
||||
use windows_sys::Win32::{
|
||||
Foundation::{HWND, LPARAM, LRESULT, POINT, WPARAM},
|
||||
|
@ -39,7 +44,7 @@ use windows_sys::Win32::{
|
|||
const COUNTER_START: u32 = 1000;
|
||||
static COUNTER: Counter = Counter::new_with_start(COUNTER_START);
|
||||
|
||||
type AccelWrapper = (HACCEL, Vec<Accel>);
|
||||
type AccelWrapper = (HACCEL, HashMap<u32, Accel>);
|
||||
|
||||
/// A generic child in a menu
|
||||
///
|
||||
|
@ -51,6 +56,7 @@ struct MenuChild {
|
|||
text: String,
|
||||
enabled: bool,
|
||||
parents_hemnu: Vec<HMENU>,
|
||||
root_menu_haccel_stores: Option<Vec<Rc<RefCell<AccelWrapper>>>>,
|
||||
|
||||
// menu item fields
|
||||
id: u32,
|
||||
|
@ -69,7 +75,6 @@ struct MenuChild {
|
|||
hmenu: HMENU,
|
||||
hpopupmenu: HMENU,
|
||||
children: Option<Vec<Rc<RefCell<MenuChild>>>>,
|
||||
root_menu_haccel: Option<Vec<Rc<RefCell<AccelWrapper>>>>,
|
||||
}
|
||||
|
||||
impl MenuChild {
|
||||
|
@ -104,12 +109,16 @@ impl MenuChild {
|
|||
}
|
||||
|
||||
fn set_text(&mut self, text: &str) {
|
||||
self.text = text.to_string();
|
||||
self.text = if let Some(accelerator) = self.accelerator {
|
||||
format!("{text}\t{}", accelerator)
|
||||
} else {
|
||||
text.to_string()
|
||||
};
|
||||
for parent in &self.parents_hemnu {
|
||||
let mut info: MENUITEMINFOW = unsafe { std::mem::zeroed() };
|
||||
info.cbSize = std::mem::size_of::<MENUITEMINFOW>() as _;
|
||||
info.fMask = MIIM_STRING;
|
||||
info.dwTypeData = encode_wide(text).as_mut_ptr();
|
||||
info.dwTypeData = encode_wide(&self.text).as_mut_ptr();
|
||||
|
||||
unsafe { SetMenuItemInfoW(*parent, self.id(), false.into(), &info) };
|
||||
}
|
||||
|
@ -182,6 +191,21 @@ impl MenuChild {
|
|||
unsafe { SetMenuItemInfoW(*parent, self.id(), false.into(), &info) };
|
||||
}
|
||||
}
|
||||
|
||||
fn set_accelerator(&mut self, accelerator: Option<Accelerator>) {
|
||||
self.accelerator = accelerator;
|
||||
self.set_text(&self.text.clone());
|
||||
|
||||
let haccel_stores = self.root_menu_haccel_stores.as_mut().unwrap();
|
||||
for store in haccel_stores {
|
||||
let mut store = store.borrow_mut();
|
||||
if let Some(accelerator) = self.accelerator {
|
||||
AccelAction::add(&mut store, self.id, &accelerator)
|
||||
} else {
|
||||
AccelAction::remove(&mut store, self.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -189,7 +213,7 @@ pub(crate) struct Menu {
|
|||
hmenu: HMENU,
|
||||
hpopupmenu: HMENU,
|
||||
hwnds: Rc<RefCell<Vec<HWND>>>,
|
||||
haccel: Rc<RefCell<(HACCEL, Vec<Accel>)>>,
|
||||
haccel_store: Rc<RefCell<AccelWrapper>>,
|
||||
children: Rc<RefCell<Vec<Rc<RefCell<MenuChild>>>>>,
|
||||
}
|
||||
|
||||
|
@ -198,7 +222,7 @@ impl Menu {
|
|||
Self {
|
||||
hmenu: unsafe { CreateMenu() },
|
||||
hpopupmenu: unsafe { CreatePopupMenu() },
|
||||
haccel: Rc::new(RefCell::new((0, Vec::new()))),
|
||||
haccel_store: Rc::new(RefCell::new((0, HashMap::new()))),
|
||||
children: Rc::new(RefCell::new(Vec::new())),
|
||||
hwnds: Rc::new(RefCell::new(Vec::new())),
|
||||
}
|
||||
|
@ -212,12 +236,6 @@ impl Menu {
|
|||
let child = &submenu.0 .0;
|
||||
|
||||
flags |= MF_POPUP;
|
||||
child
|
||||
.borrow_mut()
|
||||
.root_menu_haccel
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.push(self.haccel.clone());
|
||||
|
||||
child
|
||||
}
|
||||
|
@ -275,6 +293,15 @@ impl Menu {
|
|||
}
|
||||
.clone();
|
||||
|
||||
{
|
||||
child
|
||||
.borrow_mut()
|
||||
.root_menu_haccel_stores
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.push(self.haccel_store.clone());
|
||||
}
|
||||
|
||||
{
|
||||
let child_ = child.borrow();
|
||||
if !child_.enabled {
|
||||
|
@ -285,15 +312,12 @@ impl Menu {
|
|||
|
||||
if let Some(accelerator) = &child_.accelerator {
|
||||
let accel_str = accelerator.to_string();
|
||||
let accel = accelerator.to_accel(child_.id() as u16);
|
||||
|
||||
text.push('\t');
|
||||
text.push_str(&accel_str);
|
||||
|
||||
let mut haccel = self.haccel.borrow_mut();
|
||||
haccel.1.push(Accel(accel));
|
||||
let accels = haccel.1.clone();
|
||||
update_haccel(&mut haccel.0, accels)
|
||||
let mut haccel_store = self.haccel_store.borrow_mut();
|
||||
AccelAction::add(&mut haccel_store, child_.id(), accelerator);
|
||||
}
|
||||
|
||||
let id = child_.id() as usize;
|
||||
|
@ -461,7 +485,7 @@ impl Menu {
|
|||
}
|
||||
|
||||
pub fn haccel(&self) -> HACCEL {
|
||||
self.haccel.borrow().0
|
||||
self.haccel_store.borrow().0
|
||||
}
|
||||
|
||||
pub fn hpopupmenu(&self) -> HMENU {
|
||||
|
@ -567,7 +591,7 @@ impl Submenu {
|
|||
children: Some(Vec::new()),
|
||||
hmenu: unsafe { CreateMenu() },
|
||||
hpopupmenu: unsafe { CreatePopupMenu() },
|
||||
root_menu_haccel: Some(Vec::new()),
|
||||
root_menu_haccel_stores: Some(Vec::new()),
|
||||
..Default::default()
|
||||
})))
|
||||
}
|
||||
|
@ -589,13 +613,6 @@ impl Submenu {
|
|||
|
||||
flags |= MF_POPUP;
|
||||
|
||||
child
|
||||
.borrow_mut()
|
||||
.root_menu_haccel
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.extend_from_slice(self.0.borrow_mut().root_menu_haccel.as_ref().unwrap());
|
||||
|
||||
child
|
||||
}
|
||||
MenuItemType::Normal => {
|
||||
|
@ -653,6 +670,21 @@ impl Submenu {
|
|||
}
|
||||
.clone();
|
||||
|
||||
{
|
||||
child
|
||||
.borrow_mut()
|
||||
.root_menu_haccel_stores
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.extend_from_slice(
|
||||
self.0
|
||||
.borrow_mut()
|
||||
.root_menu_haccel_stores
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let mut self_ = self.0.borrow_mut();
|
||||
|
||||
|
@ -665,16 +697,13 @@ impl Submenu {
|
|||
|
||||
if let Some(accelerator) = &child_.accelerator {
|
||||
let accel_str = accelerator.to_string();
|
||||
let accel = accelerator.to_accel(child_.id() as u16);
|
||||
|
||||
text.push('\t');
|
||||
text.push_str(&accel_str);
|
||||
|
||||
for root_menu in self_.root_menu_haccel.as_mut().unwrap() {
|
||||
for root_menu in self_.root_menu_haccel_stores.as_mut().unwrap() {
|
||||
let mut haccel = root_menu.borrow_mut();
|
||||
haccel.1.push(Accel(accel));
|
||||
let accels = haccel.1.clone();
|
||||
update_haccel(&mut haccel.0, accels)
|
||||
AccelAction::add(&mut haccel, child_.id(), accelerator);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -895,6 +924,7 @@ impl MenuItem {
|
|||
parents_hemnu: Vec::new(),
|
||||
id: COUNTER.next(),
|
||||
accelerator,
|
||||
root_menu_haccel_stores: Some(Vec::new()),
|
||||
..Default::default()
|
||||
})))
|
||||
}
|
||||
|
@ -918,6 +948,10 @@ impl MenuItem {
|
|||
pub fn set_enabled(&self, enabled: bool) {
|
||||
self.0.borrow_mut().set_enabled(enabled)
|
||||
}
|
||||
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) {
|
||||
self.0.borrow_mut().set_accelerator(acccelerator)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -933,6 +967,7 @@ impl PredefinedMenuItem {
|
|||
id: COUNTER.next(),
|
||||
accelerator: item_type.accelerator(),
|
||||
predefined_item_type: item_type,
|
||||
root_menu_haccel_stores: Some(Vec::new()),
|
||||
..Default::default()
|
||||
})))
|
||||
}
|
||||
|
@ -963,6 +998,7 @@ impl CheckMenuItem {
|
|||
id: COUNTER.next(),
|
||||
accelerator,
|
||||
checked,
|
||||
root_menu_haccel_stores: Some(Vec::new()),
|
||||
..Default::default()
|
||||
})))
|
||||
}
|
||||
|
@ -994,6 +1030,10 @@ impl CheckMenuItem {
|
|||
pub fn set_checked(&self, checked: bool) {
|
||||
self.0.borrow_mut().set_checked(checked)
|
||||
}
|
||||
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) {
|
||||
self.0.borrow_mut().set_accelerator(acccelerator)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -1014,6 +1054,7 @@ impl IconMenuItem {
|
|||
id: COUNTER.next(),
|
||||
accelerator,
|
||||
icon,
|
||||
root_menu_haccel_stores: Some(Vec::new()),
|
||||
..Default::default()
|
||||
})))
|
||||
}
|
||||
|
@ -1041,6 +1082,10 @@ impl IconMenuItem {
|
|||
pub fn set_icon(&self, icon: Option<Icon>) {
|
||||
self.0.borrow_mut().set_icon(icon)
|
||||
}
|
||||
|
||||
pub fn set_accelerator(&self, acccelerator: Option<Accelerator>) {
|
||||
self.0.borrow_mut().set_accelerator(acccelerator)
|
||||
}
|
||||
}
|
||||
|
||||
const MENU_SUBCLASS_ID: usize = 200;
|
||||
|
@ -1183,13 +1228,34 @@ unsafe extern "system" fn menu_subclass_proc(
|
|||
}
|
||||
}
|
||||
|
||||
fn update_haccel(haccel: &mut HMENU, accels: Vec<Accel>) {
|
||||
unsafe {
|
||||
DestroyAcceleratorTable(*haccel);
|
||||
*haccel = CreateAcceleratorTableW(
|
||||
accels.iter().map(|i| i.0).collect::<Vec<_>>().as_ptr(),
|
||||
accels.len() as _,
|
||||
);
|
||||
struct AccelAction;
|
||||
|
||||
impl AccelAction {
|
||||
fn add(haccel_store: &mut RefMut<AccelWrapper>, id: u32, accelerator: &Accelerator) {
|
||||
let accel = accelerator.to_accel(id as _);
|
||||
haccel_store.1.insert(id, Accel(accel));
|
||||
|
||||
Self::update_store(haccel_store)
|
||||
}
|
||||
fn remove(haccel_store: &mut RefMut<AccelWrapper>, id: u32) {
|
||||
haccel_store.1.remove(&id);
|
||||
|
||||
Self::update_store(haccel_store)
|
||||
}
|
||||
|
||||
fn update_store(haccel_store: &mut RefMut<AccelWrapper>) {
|
||||
unsafe {
|
||||
DestroyAcceleratorTable(haccel_store.0);
|
||||
haccel_store.0 = CreateAcceleratorTableW(
|
||||
haccel_store
|
||||
.1
|
||||
.values()
|
||||
.map(|i| i.0)
|
||||
.collect::<Vec<_>>()
|
||||
.as_ptr(),
|
||||
haccel_store.1.len() as _,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue