Disable menu item key equivalents while there are pending keystrokes

This commit is contained in:
Max Brunsfeld 2022-05-20 10:04:43 -07:00
parent 21862faa58
commit b72d97ce78
5 changed files with 43 additions and 5 deletions

View file

@ -192,11 +192,18 @@ impl App {
cx.borrow_mut().quit(); cx.borrow_mut().quit();
} }
})); }));
foreground_platform.on_will_open_menu(Box::new({
let cx = app.0.clone();
move || {
let mut cx = cx.borrow_mut();
cx.keystroke_matcher.clear_pending();
}
}));
foreground_platform.on_validate_menu_command(Box::new({ foreground_platform.on_validate_menu_command(Box::new({
let cx = app.0.clone(); let cx = app.0.clone();
move |action| { move |action| {
let cx = cx.borrow_mut(); let cx = cx.borrow_mut();
cx.is_action_available(action) !cx.keystroke_matcher.has_pending_keystrokes() && cx.is_action_available(action)
} }
})); }));
foreground_platform.on_menu_command(Box::new({ foreground_platform.on_menu_command(Box::new({

View file

@ -123,6 +123,10 @@ impl Matcher {
self.pending.clear(); self.pending.clear();
} }
pub fn has_pending_keystrokes(&self) -> bool {
!self.pending.is_empty()
}
pub fn push_keystroke( pub fn push_keystroke(
&mut self, &mut self,
keystroke: Keystroke, keystroke: Keystroke,

View file

@ -74,6 +74,7 @@ pub(crate) trait ForegroundPlatform {
fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn Action)>); fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn Action)>);
fn on_validate_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>); fn on_validate_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>);
fn on_will_open_menu(&self, callback: Box<dyn FnMut()>);
fn set_menus(&self, menus: Vec<Menu>, matcher: &keymap::Matcher); fn set_menus(&self, menus: Vec<Menu>, matcher: &keymap::Matcher);
fn prompt_for_paths( fn prompt_for_paths(
&self, &self,

View file

@ -93,6 +93,10 @@ unsafe fn build_classes() {
sel!(validateMenuItem:), sel!(validateMenuItem:),
validate_menu_item as extern "C" fn(&mut Object, Sel, id) -> bool, validate_menu_item as extern "C" fn(&mut Object, Sel, id) -> bool,
); );
decl.add_method(
sel!(menuWillOpen:),
menu_will_open as extern "C" fn(&mut Object, Sel, id),
);
decl.add_method( decl.add_method(
sel!(application:openURLs:), sel!(application:openURLs:),
open_urls as extern "C" fn(&mut Object, Sel, id, id), open_urls as extern "C" fn(&mut Object, Sel, id, id),
@ -112,14 +116,21 @@ pub struct MacForegroundPlatformState {
event: Option<Box<dyn FnMut(crate::Event) -> bool>>, event: Option<Box<dyn FnMut(crate::Event) -> bool>>,
menu_command: Option<Box<dyn FnMut(&dyn Action)>>, menu_command: Option<Box<dyn FnMut(&dyn Action)>>,
validate_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>, validate_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
will_open_menu: Option<Box<dyn FnMut()>>,
open_urls: Option<Box<dyn FnMut(Vec<String>)>>, open_urls: Option<Box<dyn FnMut(Vec<String>)>>,
finish_launching: Option<Box<dyn FnOnce() -> ()>>, finish_launching: Option<Box<dyn FnOnce() -> ()>>,
menu_actions: Vec<Box<dyn Action>>, menu_actions: Vec<Box<dyn Action>>,
} }
impl MacForegroundPlatform { impl MacForegroundPlatform {
unsafe fn create_menu_bar(&self, menus: Vec<Menu>, keystroke_matcher: &keymap::Matcher) -> id { unsafe fn create_menu_bar(
&self,
menus: Vec<Menu>,
delegate: id,
keystroke_matcher: &keymap::Matcher,
) -> id {
let menu_bar = NSMenu::new(nil).autorelease(); let menu_bar = NSMenu::new(nil).autorelease();
menu_bar.setDelegate_(delegate);
let mut state = self.0.borrow_mut(); let mut state = self.0.borrow_mut();
state.menu_actions.clear(); state.menu_actions.clear();
@ -130,6 +141,7 @@ impl MacForegroundPlatform {
let menu_name = menu_config.name; let menu_name = menu_config.name;
menu.setTitle_(ns_string(menu_name)); menu.setTitle_(ns_string(menu_name));
menu.setDelegate_(delegate);
for item_config in menu_config.items { for item_config in menu_config.items {
let item; let item;
@ -242,6 +254,10 @@ impl platform::ForegroundPlatform for MacForegroundPlatform {
self.0.borrow_mut().menu_command = Some(callback); self.0.borrow_mut().menu_command = Some(callback);
} }
fn on_will_open_menu(&self, callback: Box<dyn FnMut()>) {
self.0.borrow_mut().will_open_menu = Some(callback);
}
fn on_validate_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) { fn on_validate_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
self.0.borrow_mut().validate_menu_command = Some(callback); self.0.borrow_mut().validate_menu_command = Some(callback);
} }
@ -249,7 +265,7 @@ impl platform::ForegroundPlatform for MacForegroundPlatform {
fn set_menus(&self, menus: Vec<Menu>, keystroke_matcher: &keymap::Matcher) { fn set_menus(&self, menus: Vec<Menu>, keystroke_matcher: &keymap::Matcher) {
unsafe { unsafe {
let app: id = msg_send![APP_CLASS, sharedApplication]; let app: id = msg_send![APP_CLASS, sharedApplication];
app.setMainMenu_(self.create_menu_bar(menus, keystroke_matcher)); app.setMainMenu_(self.create_menu_bar(menus, app.delegate(), keystroke_matcher));
} }
} }
@ -764,6 +780,17 @@ extern "C" fn validate_menu_item(this: &mut Object, _: Sel, item: id) -> bool {
} }
} }
extern "C" fn menu_will_open(this: &mut Object, _: Sel, _: id) {
unsafe {
let platform = get_foreground_platform(this);
let mut platform = platform.0.borrow_mut();
if let Some(mut callback) = platform.will_open_menu.take() {
callback();
platform.will_open_menu = Some(callback);
}
}
}
unsafe fn ns_string(string: &str) -> id { unsafe fn ns_string(string: &str) -> id {
NSString::alloc(nil).init_str(string).autorelease() NSString::alloc(nil).init_str(string).autorelease()
} }

View file

@ -73,9 +73,8 @@ impl super::ForegroundPlatform for ForegroundPlatform {
} }
fn on_menu_command(&self, _: Box<dyn FnMut(&dyn Action)>) {} fn on_menu_command(&self, _: Box<dyn FnMut(&dyn Action)>) {}
fn on_validate_menu_command(&self, _: Box<dyn FnMut(&dyn Action) -> bool>) {} fn on_validate_menu_command(&self, _: Box<dyn FnMut(&dyn Action) -> bool>) {}
fn on_will_open_menu(&self, _: Box<dyn FnMut()>) {}
fn set_menus(&self, _: Vec<crate::Menu>, _: &keymap::Matcher) {} fn set_menus(&self, _: Vec<crate::Menu>, _: &keymap::Matcher) {}
fn prompt_for_paths( fn prompt_for_paths(