1use crate::*;
2use alux_ext::ext;
3use rsynko_manager::*;
4
5pub type ManagerKeyBinding<Manager> = KeyBinding<
7 <Manager as ManagerSorts>::Id,
8 <Manager as ManagerSorts>::Source,
9 <Manager as ManagerSorts>::Format,
10 <Manager as ManagerSorts>::Change,
11>;
12
13pub struct KeyBinding<Id, Source, Format, Change> {
18 pub action: Option<ManagerAction>,
20 pub keys: Vec<Keystroke>,
22 pub intent: ManagerIntentOp<Id, Source, Format, Change>,
24}
25
26impl<Id, Source, Format, Change> KeyBinding<Id, Source, Format, Change> {
27 fn action(
29 action: ManagerAction,
30 keys: impl IntoIterator<Item = Key>,
31 intent: ManagerIntentOp<Id, Source, Format, Change>,
32 ) -> Self {
33 Self { action: Some(action), keys: keys.into_iter().map(Keystroke::plain).collect(), intent }
34 }
35
36 fn ungated(keys: impl IntoIterator<Item = Key>, intent: ManagerIntentOp<Id, Source, Format, Change>) -> Self {
38 Self { action: None, keys: keys.into_iter().map(Keystroke::plain).collect(), intent }
39 }
40
41 fn binds(&self, stroke: Keystroke) -> bool {
43 self.keys.contains(&stroke)
44 }
45}
46
47#[ext(name = KeyBindingExt)]
49pub impl<This> This
50where
51 This: NavigationStateAlg,
52{
53 fn page_bindings(&self) -> impl Iterator<Item = ManagerKeyBinding<This>> {
58 let bindings = match self.page() {
59 ManagerPage::Collection => vec![
60 KeyBinding::action(
61 ManagerAction::Previous,
62 [Key::Up, Key::Character('k')],
63 ManagerIntentOp::SelectPrevious {},
64 ),
65 KeyBinding::action(
66 ManagerAction::Next,
67 [Key::Down, Key::Character('j')],
68 ManagerIntentOp::SelectNext {},
69 ),
70 KeyBinding::action(ManagerAction::Activate, [Key::Enter], ManagerIntentOp::OpenSelected {}),
71 KeyBinding::action(
72 ManagerAction::AddSources,
73 [Key::Character('a')],
74 ManagerIntentOp::OpenAddSources {},
75 ),
76 KeyBinding::action(ManagerAction::Space, [Key::Character(' ')], ManagerIntentOp::ApplySelectedSpace {}),
77 KeyBinding::action(ManagerAction::Remove, [Key::Delete], ManagerIntentOp::RemoveSelected {}),
78 ],
79 ManagerPage::Details(_) => vec![
80 KeyBinding::action(
81 ManagerAction::Previous,
82 [Key::Up, Key::Character('k')],
83 ManagerIntentOp::SelectPreviousDetail {},
84 ),
85 KeyBinding::action(
86 ManagerAction::Next,
87 [Key::Down, Key::Character('j')],
88 ManagerIntentOp::SelectNextDetail {},
89 ),
90 KeyBinding::action(ManagerAction::Activate, [Key::Enter], ManagerIntentOp::ActivateDetail {}),
91 KeyBinding::action(ManagerAction::Space, [Key::Character(' ')], ManagerIntentOp::ApplySelectedSpace {}),
92 KeyBinding::action(ManagerAction::Back, [Key::Escape, Key::Backspace], ManagerIntentOp::Back {}),
93 ],
94 ManagerPage::Formats(_) => vec![
95 KeyBinding::action(
96 ManagerAction::Previous,
97 [Key::Up, Key::Character('k')],
98 ManagerIntentOp::SelectPreviousFormat {},
99 ),
100 KeyBinding::action(
101 ManagerAction::Next,
102 [Key::Down, Key::Character('j')],
103 ManagerIntentOp::SelectNextFormat {},
104 ),
105 KeyBinding::action(ManagerAction::Activate, [Key::Enter], ManagerIntentOp::Back {}),
107 KeyBinding::action(ManagerAction::Back, [Key::Escape, Key::Backspace], ManagerIntentOp::Back {}),
108 ],
109 ManagerPage::Log(_) | ManagerPage::Report(_) | ManagerPage::Command(_) => {
110 vec![KeyBinding::action(ManagerAction::Back, [Key::Escape, Key::Enter], ManagerIntentOp::Back {})]
111 }
112 ManagerPage::AddSources => editor_bindings(ManagerIntentOp::SubmitDraft {}),
113 ManagerPage::Output(_) => editor_bindings(ManagerIntentOp::SubmitOutput {}),
114 ManagerPage::Input(_) => editor_bindings(ManagerIntentOp::SubmitInput {}),
115 };
116 bindings.into_iter()
117 }
118
119 fn keystroke_meaning(&self, stroke: Keystroke) -> Option<ManagerKeyBinding<This>> {
124 if stroke == EXIT_KEYSTROKE {
125 return Some(KeyBinding::action(
126 ManagerAction::Exit,
127 [EXIT_KEYSTROKE.key],
128 ManagerIntentOp::SafeExitRequested {},
129 ));
130 }
131 if stroke.modified() {
132 return None;
133 }
134 self.page_bindings().find(|binding| binding.binds(stroke)).or_else(|| self.typed_meaning(stroke.key.typed()?))
135 }
136
137 fn typed_meaning(&self, typed: char) -> Option<ManagerKeyBinding<This>> {
139 self.page_accepts_text().then(|| {
140 KeyBinding::ungated([Key::Character(typed)], ManagerIntentOp::InsertText { text: typed.to_string() })
141 })
142 }
143
144 fn paste_meaning(&self, text: &str) -> Option<ManagerKeyBinding<This>> {
146 self.page_accepts_text().then(|| KeyBinding::ungated([], ManagerIntentOp::InsertText { text: text.to_owned() }))
147 }
148
149 fn page_accepts_text(&self) -> bool {
151 matches!(self.page(), ManagerPage::AddSources | ManagerPage::Output(_) | ManagerPage::Input(_))
152 }
153
154 fn action_keys(&self, action: ManagerAction) -> impl Iterator<Item = Keystroke> {
156 if action == ManagerAction::Exit {
157 return vec![EXIT_KEYSTROKE].into_iter();
158 }
159 self.page_bindings()
160 .find(|binding| binding.action == Some(action))
161 .map(|binding| binding.keys)
162 .unwrap_or_default()
163 .into_iter()
164 }
165}
166
167#[ext(name = KeyInputExt)]
169pub impl<This> This
170where
171 This: NavigationStateAlg
172 + QueueCatalogAlg
173 + QueueAppendAlg
174 + QueueRemoveAlg
175 + QueuePauseAlg
176 + QueueDuplicateAlg
177 + QueueFormatEditAlg
178 + FormatCatalogStateAlg
179 + QueueDryRunAlg
180 + RehearsalStateAlg
181 + TransferStateAlg
182 + DetailSelectionAlg
183 + DraftStateAlg
184 + OutputDraftAlg
185 + InputDraftAlg
186 + QueueOutputAlg
187 + QueueSourceAlg
188 + SourceMetadataAlg
189 + TextEditorStateAlg
190 + ManagerStatusAlg
191 + SafeExitAlg
192 + SourceRequestAlg
193 + SubmissionAlg
194 + MediaOptionsAlg
195 + OutputChoiceAlg,
196 This::Entry: QueueEntryAlg + RequestOptionsAlg,
197 This::Id: Copy + Eq,
198{
199 fn apply_keystroke(&mut self, stroke: Keystroke) -> bool {
204 self.keystroke_meaning(stroke).is_some_and(|binding| self.apply_binding(binding))
205 }
206
207 fn apply_paste(&mut self, text: &str) -> bool {
209 self.paste_meaning(text).is_some_and(|binding| self.apply_binding(binding))
210 }
211
212 fn apply_binding(&mut self, binding: ManagerKeyBinding<This>) -> bool {
214 let enabled = binding.action.is_none_or(|action| self.action_availability(action).is_enabled());
215 if enabled {
216 self.apply_manager_event(binding.intent);
217 }
218 enabled
219 }
220}
221
222fn editor_bindings<Id, Source, Format, Change>(
224 submit: ManagerIntentOp<Id, Source, Format, Change>,
225) -> Vec<KeyBinding<Id, Source, Format, Change>> {
226 vec![
227 KeyBinding::action(ManagerAction::Previous, [Key::Left], ManagerIntentOp::MoveCursorLeft {}),
228 KeyBinding::action(ManagerAction::Next, [Key::Right], ManagerIntentOp::MoveCursorRight {}),
229 KeyBinding::action(ManagerAction::Activate, [Key::Enter], submit),
230 KeyBinding::action(ManagerAction::Back, [Key::Escape], ManagerIntentOp::Back {}),
231 KeyBinding::ungated([Key::Backspace], ManagerIntentOp::DeleteBeforeCursor {}),
232 KeyBinding::ungated([Key::Delete], ManagerIntentOp::DeleteAtCursor {}),
233 KeyBinding::ungated([Key::Home], ManagerIntentOp::MoveCursorHome {}),
234 KeyBinding::ungated([Key::End], ManagerIntentOp::MoveCursorEnd {}),
235 ]
236}