use crate::*;
use alux_ext::ext;
use rsynko_manager::*;
pub struct MenuItem {
pub action: ManagerAction,
pub keys: Vec<Keystroke>,
pub verb: String,
pub availability: ActionAvailability,
}
impl MenuItem {
#[must_use]
pub fn label(&self) -> String {
let keys = self.keys.iter().map(|key| key.label()).collect::<String>();
format!("[{keys}] {}", self.verb)
}
}
#[ext(name = MenuPresentationExt)]
pub impl<This> This
where
This: NavigationStateAlg
+ QueueCatalogAlg
+ DraftStateAlg
+ OutputDraftAlg
+ InputDraftAlg
+ TextEditorStateAlg
+ DetailSelectionAlg,
This::Entry: QueueEntryAlg + RequestOptionsAlg,
This::Id: Copy + Eq,
{
fn menu_items(&self) -> impl Iterator<Item = MenuItem> {
let page = self.page();
let items = match page {
ManagerPage::Collection => vec![
self.cursor_item("Select"),
self.menu_item(ManagerAction::Activate, "Details"),
self.menu_item(ManagerAction::AddSources, "Add"),
self.menu_item(ManagerAction::Space, self.space_verb()),
self.menu_item(ManagerAction::Remove, "Remove"),
],
ManagerPage::Details(_) => vec![
self.cursor_item("Select field or action"),
self.menu_item(
ManagerAction::Activate,
if self.selected_detail_control().is_none() { "Close details" } else { "Activate" },
),
self.menu_item(ManagerAction::Space, self.space_verb()),
self.menu_item(ManagerAction::Back, COLLECTION),
],
ManagerPage::Formats(id) => vec![
self.cursor_item(self.choice_verb(id)),
self.menu_item(ManagerAction::Activate, "Accept"),
self.menu_item(ManagerAction::Back, "Details"),
],
ManagerPage::Log(_) | ManagerPage::Report(_) | ManagerPage::Command(_) => {
vec![self.menu_item(ManagerAction::Back, "Details")]
}
ManagerPage::AddSources => vec![
self.menu_item(ManagerAction::Previous, "Move"),
self.menu_item(ManagerAction::Next, "Move"),
self.menu_item(ManagerAction::Activate, "Add sources"),
self.menu_item(ManagerAction::Back, COLLECTION),
],
ManagerPage::Output(_) | ManagerPage::Input(_) => vec![
self.menu_item(ManagerAction::Previous, "Move"),
self.menu_item(ManagerAction::Next, "Move"),
self.menu_item(ManagerAction::Activate, "Save"),
self.menu_item(ManagerAction::Back, "Details"),
],
};
items.into_iter().chain([self.menu_item(ManagerAction::Exit, "Quit")])
}
fn menu_item(&self, action: ManagerAction, verb: impl Into<String>) -> MenuItem {
MenuItem {
action,
keys: self.action_keys(action).take(1).collect(),
verb: verb.into(),
availability: self.action_availability(action),
}
}
fn cursor_item(&self, verb: impl Into<String>) -> MenuItem {
let keys = self
.action_keys(ManagerAction::Previous)
.take(1)
.chain(self.action_keys(ManagerAction::Next).take(1))
.collect();
MenuItem {
action: ManagerAction::Next,
keys,
verb: verb.into(),
availability: self.action_availability(ManagerAction::Next),
}
}
fn choice_verb(&self, id: This::Id) -> &'static str {
let chooses_media = self.queue_entry(id).is_some_and(|entry| entry.performer() == Performer::Retrieval);
if chooses_media { "Choose format" } else { "Choose transfer" }
}
fn space_verb(&self) -> String {
let entry = self.selected_queue_id().and_then(|id| self.queue_entry(id));
let Some(entry) = entry else {
return UNSTATED.to_owned();
};
entry
.space_action()
.map_or_else(
|| match entry.phase() {
TransferPhase::Extracting | TransferPhase::Downloading => "Pause",
TransferPhase::Paused => "Resume",
TransferPhase::Ready => "Start",
TransferPhase::Waiting
| TransferPhase::Rehearsing
| TransferPhase::Publishing
| TransferPhase::Complete
| TransferPhase::Failed => UNSTATED,
},
SpaceAction::space_label,
)
.to_owned()
}
}