use crate::*;
use alux_ext::ext;
use rsynko_manager::*;
pub type ManagerKeyBinding<Manager> = KeyBinding<
<Manager as ManagerSorts>::Id,
<Manager as ManagerSorts>::Source,
<Manager as ManagerSorts>::Format,
<Manager as ManagerSorts>::Change,
>;
pub struct KeyBinding<Id, Source, Format, Change> {
pub action: Option<ManagerAction>,
pub keys: Vec<Keystroke>,
pub intent: ManagerIntentOp<Id, Source, Format, Change>,
}
impl<Id, Source, Format, Change> KeyBinding<Id, Source, Format, Change> {
fn action(
action: ManagerAction,
keys: impl IntoIterator<Item = Key>,
intent: ManagerIntentOp<Id, Source, Format, Change>,
) -> Self {
Self { action: Some(action), keys: keys.into_iter().map(Keystroke::plain).collect(), intent }
}
fn ungated(keys: impl IntoIterator<Item = Key>, intent: ManagerIntentOp<Id, Source, Format, Change>) -> Self {
Self { action: None, keys: keys.into_iter().map(Keystroke::plain).collect(), intent }
}
fn binds(&self, stroke: Keystroke) -> bool {
self.keys.contains(&stroke)
}
}
#[ext(name = KeyBindingExt)]
pub impl<This> This
where
This: NavigationStateAlg,
{
fn page_bindings(&self) -> impl Iterator<Item = ManagerKeyBinding<This>> {
let bindings = match self.page() {
ManagerPage::Collection => vec![
KeyBinding::action(
ManagerAction::Previous,
[Key::Up, Key::Character('k')],
ManagerIntentOp::SelectPrevious {},
),
KeyBinding::action(
ManagerAction::Next,
[Key::Down, Key::Character('j')],
ManagerIntentOp::SelectNext {},
),
KeyBinding::action(ManagerAction::Activate, [Key::Enter], ManagerIntentOp::OpenSelected {}),
KeyBinding::action(
ManagerAction::AddSources,
[Key::Character('a')],
ManagerIntentOp::OpenAddSources {},
),
KeyBinding::action(ManagerAction::Space, [Key::Character(' ')], ManagerIntentOp::ApplySelectedSpace {}),
KeyBinding::action(ManagerAction::Remove, [Key::Delete], ManagerIntentOp::RemoveSelected {}),
],
ManagerPage::Details(_) => vec![
KeyBinding::action(
ManagerAction::Previous,
[Key::Up, Key::Character('k')],
ManagerIntentOp::SelectPreviousDetail {},
),
KeyBinding::action(
ManagerAction::Next,
[Key::Down, Key::Character('j')],
ManagerIntentOp::SelectNextDetail {},
),
KeyBinding::action(ManagerAction::Activate, [Key::Enter], ManagerIntentOp::ActivateDetail {}),
KeyBinding::action(ManagerAction::Space, [Key::Character(' ')], ManagerIntentOp::ApplySelectedSpace {}),
KeyBinding::action(ManagerAction::Back, [Key::Escape, Key::Backspace], ManagerIntentOp::Back {}),
],
ManagerPage::Formats(_) => vec![
KeyBinding::action(
ManagerAction::Previous,
[Key::Up, Key::Character('k')],
ManagerIntentOp::SelectPreviousFormat {},
),
KeyBinding::action(
ManagerAction::Next,
[Key::Down, Key::Character('j')],
ManagerIntentOp::SelectNextFormat {},
),
KeyBinding::action(ManagerAction::Activate, [Key::Enter], ManagerIntentOp::Back {}),
KeyBinding::action(ManagerAction::Back, [Key::Escape, Key::Backspace], ManagerIntentOp::Back {}),
],
ManagerPage::Log(_) | ManagerPage::Report(_) | ManagerPage::Command(_) => {
vec![KeyBinding::action(ManagerAction::Back, [Key::Escape, Key::Enter], ManagerIntentOp::Back {})]
}
ManagerPage::AddSources => editor_bindings(ManagerIntentOp::SubmitDraft {}),
ManagerPage::Output(_) => editor_bindings(ManagerIntentOp::SubmitOutput {}),
ManagerPage::Input(_) => editor_bindings(ManagerIntentOp::SubmitInput {}),
};
bindings.into_iter()
}
fn keystroke_meaning(&self, stroke: Keystroke) -> Option<ManagerKeyBinding<This>> {
if stroke == EXIT_KEYSTROKE {
return Some(KeyBinding::action(
ManagerAction::Exit,
[EXIT_KEYSTROKE.key],
ManagerIntentOp::SafeExitRequested {},
));
}
if stroke.modified() {
return None;
}
self.page_bindings().find(|binding| binding.binds(stroke)).or_else(|| self.typed_meaning(stroke.key.typed()?))
}
fn typed_meaning(&self, typed: char) -> Option<ManagerKeyBinding<This>> {
self.page_accepts_text().then(|| {
KeyBinding::ungated([Key::Character(typed)], ManagerIntentOp::InsertText { text: typed.to_string() })
})
}
fn paste_meaning(&self, text: &str) -> Option<ManagerKeyBinding<This>> {
self.page_accepts_text().then(|| KeyBinding::ungated([], ManagerIntentOp::InsertText { text: text.to_owned() }))
}
fn page_accepts_text(&self) -> bool {
matches!(self.page(), ManagerPage::AddSources | ManagerPage::Output(_) | ManagerPage::Input(_))
}
fn action_keys(&self, action: ManagerAction) -> impl Iterator<Item = Keystroke> {
if action == ManagerAction::Exit {
return vec![EXIT_KEYSTROKE].into_iter();
}
self.page_bindings()
.find(|binding| binding.action == Some(action))
.map(|binding| binding.keys)
.unwrap_or_default()
.into_iter()
}
}
#[ext(name = KeyInputExt)]
pub impl<This> This
where
This: NavigationStateAlg
+ QueueCatalogAlg
+ QueueAppendAlg
+ QueueRemoveAlg
+ QueuePauseAlg
+ QueueDuplicateAlg
+ QueueFormatEditAlg
+ FormatCatalogStateAlg
+ QueueDryRunAlg
+ RehearsalStateAlg
+ TransferStateAlg
+ DetailSelectionAlg
+ DraftStateAlg
+ OutputDraftAlg
+ InputDraftAlg
+ QueueOutputAlg
+ QueueSourceAlg
+ SourceMetadataAlg
+ TextEditorStateAlg
+ ManagerStatusAlg
+ SafeExitAlg
+ SourceRequestAlg
+ SubmissionAlg
+ MediaOptionsAlg
+ OutputChoiceAlg,
This::Entry: QueueEntryAlg + RequestOptionsAlg,
This::Id: Copy + Eq,
{
fn apply_keystroke(&mut self, stroke: Keystroke) -> bool {
self.keystroke_meaning(stroke).is_some_and(|binding| self.apply_binding(binding))
}
fn apply_paste(&mut self, text: &str) -> bool {
self.paste_meaning(text).is_some_and(|binding| self.apply_binding(binding))
}
fn apply_binding(&mut self, binding: ManagerKeyBinding<This>) -> bool {
let enabled = binding.action.is_none_or(|action| self.action_availability(action).is_enabled());
if enabled {
self.apply_manager_event(binding.intent);
}
enabled
}
}
fn editor_bindings<Id, Source, Format, Change>(
submit: ManagerIntentOp<Id, Source, Format, Change>,
) -> Vec<KeyBinding<Id, Source, Format, Change>> {
vec![
KeyBinding::action(ManagerAction::Previous, [Key::Left], ManagerIntentOp::MoveCursorLeft {}),
KeyBinding::action(ManagerAction::Next, [Key::Right], ManagerIntentOp::MoveCursorRight {}),
KeyBinding::action(ManagerAction::Activate, [Key::Enter], submit),
KeyBinding::action(ManagerAction::Back, [Key::Escape], ManagerIntentOp::Back {}),
KeyBinding::ungated([Key::Backspace], ManagerIntentOp::DeleteBeforeCursor {}),
KeyBinding::ungated([Key::Delete], ManagerIntentOp::DeleteAtCursor {}),
KeyBinding::ungated([Key::Home], ManagerIntentOp::MoveCursorHome {}),
KeyBinding::ungated([Key::End], ManagerIntentOp::MoveCursorEnd {}),
]
}