use super::super as input;
use super::{KeyCode, KeyEvent, KeyModifiers, MissionControlState, PickerViewport};
pub(super) fn classify_theme_picker_key(
key: KeyEvent,
state: &MissionControlState,
viewport: Option<PickerViewport>,
query_visible: bool,
) -> input::InputCommand {
if state.theme_picker_save_pending() {
return input::InputCommand::None;
}
let preview = |id: Option<String>, display: input::DisplayCommand| match id {
Some(id) => {
input::InputCommand::action_with_display(input::InputAction::PreviewTheme(id), display)
}
None => input::InputCommand::display(display),
};
if query_visible && input::keybindings::is_theme_variant_switch_key(key) {
let Some(picker) = state
.modals
.theme_picker
.as_ref()
.filter(|picker| !picker.loading)
else {
return input::InputCommand::None;
};
return preview(
picker.selected_id_after_filter(&picker.query, picker.next_variant()),
input::DisplayCommand::Modal(input::ModalCommand::ThemeSwitchVariant),
);
}
let visible_rows = viewport.map(|viewport| viewport.visible_rows as usize);
match (key.code, key.modifiers) {
(KeyCode::Up, KeyModifiers::NONE) => {
let Some(visible_rows) = visible_rows else {
return input::InputCommand::None;
};
preview(
theme_selected_id_after_move(state, 1, false),
input::DisplayCommand::Modal(input::ModalCommand::ThemeMove {
amount: 1,
down: false,
visible_rows,
}),
)
}
(KeyCode::Down, KeyModifiers::NONE) => {
let Some(visible_rows) = visible_rows else {
return input::InputCommand::None;
};
preview(
theme_selected_id_after_move(state, 1, true),
input::DisplayCommand::Modal(input::ModalCommand::ThemeMove {
amount: 1,
down: true,
visible_rows,
}),
)
}
(KeyCode::PageUp, KeyModifiers::NONE) => {
let Some(visible_rows) = visible_rows else {
return input::InputCommand::None;
};
preview(
theme_selected_id_after_move(state, visible_rows.max(1), false),
input::DisplayCommand::Modal(input::ModalCommand::ThemeMove {
amount: visible_rows.max(1),
down: false,
visible_rows,
}),
)
}
(KeyCode::PageDown, KeyModifiers::NONE) => {
let Some(visible_rows) = visible_rows else {
return input::InputCommand::None;
};
preview(
theme_selected_id_after_move(state, visible_rows.max(1), true),
input::DisplayCommand::Modal(input::ModalCommand::ThemeMove {
amount: visible_rows.max(1),
down: true,
visible_rows,
}),
)
}
(KeyCode::Enter, KeyModifiers::NONE)
if viewport.is_some()
&& state
.modals
.theme_picker
.as_ref()
.is_some_and(|picker| !picker.loading)
&& state.theme_picker_selected_id().is_some() =>
{
input::InputCommand::action(input::InputAction::ConfirmTheme)
}
(KeyCode::Esc, KeyModifiers::NONE) => {
input::InputCommand::action(input::InputAction::CancelTheme)
}
(KeyCode::Backspace, KeyModifiers::NONE) if query_visible => preview(
theme_selected_id_after_edit(state, ThemeQueryEdit::Backspace),
input::DisplayCommand::Modal(input::ModalCommand::ThemeBackspace),
),
(KeyCode::Delete, KeyModifiers::NONE) if query_visible => preview(
theme_selected_id_after_edit(state, ThemeQueryEdit::Delete),
input::DisplayCommand::Modal(input::ModalCommand::ThemeDelete),
),
(KeyCode::Left, KeyModifiers::NONE) if query_visible => input::InputCommand::display(
input::DisplayCommand::Modal(input::ModalCommand::ThemeCursorLeft),
),
(KeyCode::Right, KeyModifiers::NONE) if query_visible => input::InputCommand::display(
input::DisplayCommand::Modal(input::ModalCommand::ThemeCursorRight),
),
(KeyCode::Home, KeyModifiers::NONE) if query_visible => input::InputCommand::display(
input::DisplayCommand::Modal(input::ModalCommand::ThemeCursorHome),
),
(KeyCode::End, KeyModifiers::NONE) if query_visible => input::InputCommand::display(
input::DisplayCommand::Modal(input::ModalCommand::ThemeCursorEnd),
),
(KeyCode::Char(ch), modifiers)
if query_visible && (modifiers.is_empty() || modifiers == KeyModifiers::SHIFT) =>
{
preview(
theme_selected_id_after_edit(state, ThemeQueryEdit::Insert(ch)),
input::DisplayCommand::Modal(input::ModalCommand::ThemeInsertChar(ch)),
)
}
_ => input::InputCommand::None,
}
}
#[derive(Debug, Clone, Copy)]
enum ThemeQueryEdit {
Insert(char),
Backspace,
Delete,
}
fn theme_selected_id_after_move(
state: &MissionControlState,
amount: usize,
down: bool,
) -> Option<String> {
let picker = state.modals.theme_picker.as_ref()?;
if picker.loading || picker.filtered_indices.is_empty() {
return None;
}
let selected = if down {
picker
.selected
.saturating_add(amount)
.min(picker.filtered_indices.len() - 1)
} else {
picker.selected.saturating_sub(amount)
};
picker
.filtered_indices
.get(selected)
.and_then(|index| picker.entries.get(*index))
.map(|entry| entry.id.clone())
}
fn theme_selected_id_after_edit(
state: &MissionControlState,
edit: ThemeQueryEdit,
) -> Option<String> {
use unicode_segmentation::UnicodeSegmentation;
let picker = state.modals.theme_picker.as_ref()?;
if picker.loading {
return None;
}
let mut query = picker.query.clone();
let cursor = picker.query_cursor;
match edit {
ThemeQueryEdit::Insert(ch) => {
query.insert(cursor, ch);
}
ThemeQueryEdit::Backspace => {
if let Some((previous, _)) = query[..cursor].grapheme_indices(true).next_back() {
query.drain(previous..cursor);
}
}
ThemeQueryEdit::Delete => {
if let Some(grapheme) = query[cursor..].graphemes(true).next() {
query.drain(cursor..cursor + grapheme.len());
}
}
}
picker.selected_id_after_filter(&query, picker.active_variant)
}