use crate::action::ActionManager;
use crate::action::UndoManager;
use crate::compat::{lock, Mutex, String};
use crate::event::Event;
use crate::shortcut::Key;
use crate::shortcut::Modifiers;
use crate::shortcut::PlatformShortcutStyle;
use crate::shortcut::Shortcut;
use alloc::sync::Arc;
pub struct ActionRouter<'a> {
shortcut_mgr: &'a mut crate::shortcut::ShortcutManager,
action_mgr: &'a mut ActionManager,
}
impl<'a> ActionRouter<'a> {
pub fn new(
shortcut_mgr: &'a mut crate::shortcut::ShortcutManager,
action_mgr: &'a mut ActionManager,
) -> Self {
Self { shortcut_mgr, action_mgr }
}
pub fn connect(
&mut self,
action_id: impl Into<String>,
shortcut: Shortcut,
text: impl Into<String>,
) -> bool {
let action_id = action_id.into();
if !self.action_mgr.register_action(action_id.clone(), text) {
return false;
}
let existing = self.shortcut_mgr.get_action(&shortcut).cloned();
if !self.shortcut_mgr.register(action_id.clone(), shortcut.clone(), "") {
if let Some(existing) = existing {
self.action_mgr.report_shortcut_conflict(action_id.clone(), existing);
}
self.action_mgr.unregister_action(&action_id);
return false;
}
if self.action_mgr.bind_shortcut_type(&shortcut, action_id.clone()) {
self.assert_tables_reconciled();
true
} else {
self.shortcut_mgr.unregister(&action_id);
self.action_mgr.unregister_action(&action_id);
false
}
}
pub fn shortcut_reconciliation(
&self,
) -> crate::compat::Vec<crate::action::ShortcutDisagreement> {
self.action_mgr.shortcut_reconciliation(self.shortcut_mgr)
}
#[cfg(debug_assertions)]
fn assert_tables_reconciled(&self) {
let disagreements = self.shortcut_reconciliation();
debug_assert!(
disagreements.is_empty(),
"ActionRouter left the action and shortcut tables out of sync: {disagreements:?}"
);
}
#[cfg(not(debug_assertions))]
fn assert_tables_reconciled(&self) {}
pub fn connect_callback<F>(
&mut self,
action_id: impl Into<String>,
shortcut: Shortcut,
text: impl Into<String>,
callback: F,
) -> bool
where
F: FnMut() + Send + Sync + 'static,
{
let action_id = action_id.into();
if !self.connect(action_id.clone(), shortcut, text) {
return false;
}
self.action_mgr
.action(&action_id)
.map(|action| {
action.connect_triggered(callback);
true
})
.unwrap_or(false)
}
pub fn connect_undo_redo<U, R>(&mut self, undo: U, redo: R) -> bool
where
U: FnMut() + Send + Sync + 'static,
R: FnMut() + Send + Sync + 'static,
{
let undo_ok = self.connect_callback("undo", Shortcut::primary(Key::Z), "Undo", undo);
let redo_ok = self.connect_callback("redo", Self::redo_shortcut(), "Redo", redo);
undo_ok && redo_ok
}
pub fn connect_undo_redo_stack(&mut self, stack: Arc<Mutex<UndoManager>>) -> bool {
let for_undo = Arc::clone(&stack);
let for_redo = Arc::clone(&stack);
self.connect_undo_redo(
move || {
lock(&for_undo).undo();
},
move || {
lock(&for_redo).redo();
},
)
}
fn redo_shortcut() -> Shortcut {
match crate::platform::platform_facts().shortcut_style() {
PlatformShortcutStyle::Mac => Shortcut::primary_shift(Key::Z),
PlatformShortcutStyle::Desktop => Shortcut::primary(Key::Y),
}
}
pub fn bind_shortcut(&mut self, action_id: impl Into<String>, shortcut: Shortcut) -> bool {
let action_id = action_id.into();
if self.action_mgr.action(&action_id).is_none() {
return false;
}
let existing = self.shortcut_mgr.get_action(&shortcut).cloned();
if !self.shortcut_mgr.register(action_id.clone(), shortcut.clone(), "") {
if let Some(existing) = existing {
self.action_mgr.report_shortcut_conflict(action_id.clone(), existing);
}
return false;
}
if self.action_mgr.bind_shortcut_type(&shortcut, action_id.clone()) {
self.assert_tables_reconciled();
return true;
}
self.shortcut_mgr.unregister(&action_id);
false
}
pub fn route_action_id(&mut self, action_id: &str) -> bool {
self.action_mgr.trigger_action(action_id)
}
pub fn dispatch_key(&mut self, key: Key, modifiers: Modifiers) -> Option<String> {
let action_id = self.shortcut_mgr.dispatch_shortcut(key, modifiers)?;
self.action_mgr.trigger_action(&action_id);
Some(action_id)
}
pub fn dispatch_event(&mut self, event: &Event) -> bool {
let Event::KeyPress { key, modifiers } = event else {
return false;
};
let Some(key) = Key::from_key_code(*key) else {
return false;
};
self.dispatch_key(key, Modifiers::from_event_bits(*modifiers)).is_some()
}
}