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 {}),
]
}