@@ -192,6 +192,13 @@ impl App {
cx.borrow_mut().quit();
}
}));
+ foreground_platform.on_validate_menu_command(Box::new({
+ let cx = app.0.clone();
+ move |action| {
+ let cx = cx.borrow_mut();
+ cx.is_action_available(action)
+ }
+ }));
foreground_platform.on_menu_command(Box::new({
let cx = app.0.clone();
move |action| {
@@ -1364,6 +1371,26 @@ impl MutableAppContext {
})
}
+ pub fn is_action_available(&self, action: &dyn Action) -> bool {
+ let action_type = action.as_any().type_id();
+ if let Some(window_id) = self.cx.platform.key_window_id() {
+ if let Some((presenter, _)) = self.presenters_and_platform_windows.get(&window_id) {
+ let dispatch_path = presenter.borrow().dispatch_path(&self.cx);
+ for view_id in dispatch_path {
+ if let Some(view) = self.views.get(&(window_id, view_id)) {
+ let view_type = view.as_any().type_id();
+ if let Some(actions) = self.actions.get(&view_type) {
+ if actions.contains_key(&action_type) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ self.global_actions.contains_key(&action_type)
+ }
+
pub fn dispatch_action_at(&mut self, window_id: usize, view_id: usize, action: &dyn Action) {
let presenter = self
.presenters_and_platform_windows
@@ -73,6 +73,7 @@ pub(crate) trait ForegroundPlatform {
fn run(&self, on_finish_launching: Box<dyn FnOnce() -> ()>);
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 set_menus(&self, menus: Vec<Menu>, matcher: &keymap::Matcher);
fn prompt_for_paths(
&self,
@@ -89,6 +89,10 @@ unsafe fn build_classes() {
sel!(handleGPUIMenuItem:),
handle_menu_item as extern "C" fn(&mut Object, Sel, id),
);
+ decl.add_method(
+ sel!(validateMenuItem:),
+ validate_menu_item as extern "C" fn(&mut Object, Sel, id) -> bool,
+ );
decl.add_method(
sel!(application:openURLs:),
open_urls as extern "C" fn(&mut Object, Sel, id, id),
@@ -107,6 +111,7 @@ pub struct MacForegroundPlatformState {
quit: Option<Box<dyn FnMut()>>,
event: Option<Box<dyn FnMut(crate::Event) -> bool>>,
menu_command: Option<Box<dyn FnMut(&dyn Action)>>,
+ validate_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
open_urls: Option<Box<dyn FnMut(Vec<String>)>>,
finish_launching: Option<Box<dyn FnOnce() -> ()>>,
menu_actions: Vec<Box<dyn Action>>,
@@ -237,6 +242,10 @@ impl platform::ForegroundPlatform for MacForegroundPlatform {
self.0.borrow_mut().menu_command = Some(callback);
}
+ fn on_validate_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
+ self.0.borrow_mut().validate_menu_command = Some(callback);
+ }
+
fn set_menus(&self, menus: Vec<Menu>, keystroke_matcher: &keymap::Matcher) {
unsafe {
let app: id = msg_send![APP_CLASS, sharedApplication];
@@ -738,6 +747,23 @@ extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
}
}
+extern "C" fn validate_menu_item(this: &mut Object, _: Sel, item: id) -> bool {
+ unsafe {
+ let mut result = false;
+ let platform = get_foreground_platform(this);
+ let mut platform = platform.0.borrow_mut();
+ if let Some(mut callback) = platform.validate_menu_command.take() {
+ let tag: NSInteger = msg_send![item, tag];
+ let index = tag as usize;
+ if let Some(action) = platform.menu_actions.get(index) {
+ result = callback(action.as_ref());
+ }
+ platform.validate_menu_command = Some(callback);
+ }
+ result
+ }
+}
+
unsafe fn ns_string(string: &str) -> id {
NSString::alloc(nil).init_str(string).autorelease()
}
@@ -74,6 +74,8 @@ impl super::ForegroundPlatform for ForegroundPlatform {
fn on_menu_command(&self, _: Box<dyn FnMut(&dyn Action)>) {}
+ fn on_validate_menu_command(&self, _: Box<dyn FnMut(&dyn Action) -> bool>) {}
+
fn set_menus(&self, _: Vec<crate::Menu>, _: &keymap::Matcher) {}
fn prompt_for_paths(