use super::*;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum OptionRow {
ShowHidden,
SortKey,
SortDirection,
DirsFirst,
CaseSensitive,
ColumnExt,
ColumnSize,
ColumnModified,
ColumnAttributes,
GitMarks,
DateFormat,
SizeUnits,
ConfirmDelete,
DeleteToTrash,
ConfirmCopyMove,
OnExisting,
Terminal,
Editor,
RememberDirs,
PreviewOnStart,
Language,
Theme,
ThemeBackground,
IconStyle,
Clock,
Cursor,
CursorBlink,
TabWidth,
LineNumbers,
ScrollBars,
HighlightLimit,
LargeFile,
ImageDefault,
ImageBackgrounds,
ImagePrefetch,
}
pub const OPTION_ROWS: [OptionRow; 35] = [
OptionRow::ShowHidden,
OptionRow::SortKey,
OptionRow::SortDirection,
OptionRow::DirsFirst,
OptionRow::CaseSensitive,
OptionRow::ColumnExt,
OptionRow::ColumnSize,
OptionRow::ColumnModified,
OptionRow::ColumnAttributes,
OptionRow::GitMarks,
OptionRow::DateFormat,
OptionRow::SizeUnits,
OptionRow::ConfirmDelete,
OptionRow::DeleteToTrash,
OptionRow::ConfirmCopyMove,
OptionRow::OnExisting,
OptionRow::Terminal,
OptionRow::Editor,
OptionRow::RememberDirs,
OptionRow::PreviewOnStart,
OptionRow::Language,
OptionRow::Theme,
OptionRow::ThemeBackground,
OptionRow::IconStyle,
OptionRow::Clock,
OptionRow::Cursor,
OptionRow::CursorBlink,
OptionRow::TabWidth,
OptionRow::LineNumbers,
OptionRow::ScrollBars,
OptionRow::HighlightLimit,
OptionRow::LargeFile,
OptionRow::ImageDefault,
OptionRow::ImageBackgrounds,
OptionRow::ImagePrefetch,
];
impl OptionRow {
pub fn label(self) -> &'static str {
match self {
OptionRow::ShowHidden => t!("Show hidden files"),
OptionRow::SortKey => t!("Sort by"),
OptionRow::SortDirection => t!("Sort direction"),
OptionRow::DirsFirst => t!("Directories first"),
OptionRow::CaseSensitive => t!("Case-sensitive sort"),
OptionRow::ColumnExt => t!("Ext column"),
OptionRow::ColumnSize => t!("Size column"),
OptionRow::ColumnModified => t!("Modified column"),
OptionRow::ColumnAttributes => t!("Attributes column"),
OptionRow::GitMarks => t!("Git marks"),
OptionRow::DateFormat => t!("Date format"),
OptionRow::SizeUnits => t!("Size units"),
OptionRow::ConfirmDelete => t!("Confirm delete"),
OptionRow::DeleteToTrash => t!("Delete to trash (F8)"),
OptionRow::ConfirmCopyMove => t!("Confirm copy and move"),
OptionRow::OnExisting => t!("When destination exists"),
OptionRow::Terminal => t!("Terminal (F9)"),
OptionRow::Editor => t!("Editor (F4)"),
OptionRow::RememberDirs => t!("Remember directories"),
OptionRow::PreviewOnStart => t!("Preview on startup"),
OptionRow::Language => t!("Language"),
OptionRow::Theme => t!("Theme"),
OptionRow::ThemeBackground => t!("Background"),
OptionRow::IconStyle => t!("Icons"),
OptionRow::Clock => t!("Clock"),
OptionRow::Cursor => t!("Cursor"),
OptionRow::CursorBlink => t!("Cursor blinks"),
OptionRow::TabWidth => t!("Tab width"),
OptionRow::LineNumbers => t!("Line numbers"),
OptionRow::ScrollBars => t!("Scroll bars"),
OptionRow::HighlightLimit => t!("Highlight files up to"),
OptionRow::LargeFile => t!("Ask before opening over"),
OptionRow::ImageDefault => t!("Images open as"),
OptionRow::ImageBackgrounds => t!("Image backgrounds"),
OptionRow::ImagePrefetch => t!("Read pictures ahead"),
}
}
pub fn section(self) -> Option<&'static str> {
match self {
OptionRow::ShowHidden => Some(t!("Panels")),
OptionRow::ConfirmDelete => Some(t!("Behaviour")),
OptionRow::Language => Some(t!("Appearance")),
OptionRow::TabWidth => Some(t!("Viewer and editor")),
_ => None,
}
}
pub fn is_text(self) -> bool {
matches!(self, OptionRow::Terminal | OptionRow::Editor)
}
pub fn affects_listing(self) -> bool {
matches!(
self,
OptionRow::ShowHidden | OptionRow::SortKey | OptionRow::SortDirection | OptionRow::DirsFirst | OptionRow::CaseSensitive | OptionRow::SizeUnits | OptionRow::GitMarks
)
}
}
impl Options {
pub fn value(&self, row: OptionRow) -> String {
let on_off = |on: bool| if on { t!("On") } else { t!("Off") }.to_string();
match row {
OptionRow::ShowHidden => on_off(self.show_hidden),
OptionRow::SortKey => match self.sort_key {
SortKey::Name => t!("Name"),
SortKey::Extension => t!("Extension"),
SortKey::Size => t!("Size"),
SortKey::Modified => t!("Modified"),
}
.to_string(),
OptionRow::SortDirection => if self.sort_descending { t!("Descending") } else { t!("Ascending") }.to_string(),
OptionRow::DirsFirst => on_off(self.dirs_first),
OptionRow::CaseSensitive => on_off(self.case_sensitive),
OptionRow::ColumnExt => on_off(self.column_ext),
OptionRow::ColumnSize => on_off(self.column_size),
OptionRow::ColumnModified => on_off(self.column_modified),
OptionRow::ColumnAttributes => on_off(self.column_attributes),
OptionRow::GitMarks => on_off(self.git_marks),
OptionRow::DateFormat => match self.date_format {
DateFormat::Short => "dd/mm/yy",
DateFormat::Iso => "yyyy-mm-dd",
DateFormat::Relative => t!("Relative"),
}
.to_string(),
OptionRow::SizeUnits => match self.size_units {
SizeUnits::Binary => "KiB (1024)",
SizeUnits::Decimal => "kB (1000)",
}
.to_string(),
OptionRow::ConfirmDelete => on_off(self.confirm_delete),
OptionRow::DeleteToTrash => on_off(self.delete_to_trash),
OptionRow::ConfirmCopyMove => on_off(self.confirm_copy_move),
OptionRow::OnExisting => match self.on_existing {
OnExisting::Ask => t!("Ask"),
OnExisting::Overwrite => t!("Overwrite"),
OnExisting::Refuse => t!("Refuse"),
OnExisting::KeepBoth => t!("Keep both"),
}
.to_string(),
OptionRow::Terminal if self.terminal.is_empty() => t!("Automatic").to_string(),
OptionRow::Terminal => self.terminal.clone(),
OptionRow::Editor if self.editor.is_empty() => t!("Built-in").to_string(),
OptionRow::Editor => self.editor.clone(),
OptionRow::RememberDirs => on_off(self.remember_dirs),
OptionRow::PreviewOnStart => on_off(self.preview_on_start),
OptionRow::Language => match self.language {
Some(language) => language.label().to_string(),
None => t!("Automatic ({language})", language = crate::i18n::detect().label()),
},
OptionRow::Theme => self.theme.label().to_string(),
OptionRow::ThemeBackground => if self.theme_background { t!("Theme") } else { t!("Terminal") }.to_string(),
OptionRow::IconStyle => match self.icon_style {
IconStyle::NerdFont => "Nerd Font",
IconStyle::NerdFontMono => "Nerd Font Mono",
IconStyle::Plain => t!("Plain"),
}
.to_string(),
OptionRow::Clock => match self.clock {
Clock::Hours24 => t!("24-hour"),
Clock::Hours12 => t!("12-hour"),
Clock::Off => t!("Off"),
}
.to_string(),
OptionRow::Cursor => match self.cursor {
CursorShape::Bar => t!("Bar"),
CursorShape::Block => t!("Block"),
CursorShape::Underline => t!("Underline"),
CursorShape::Classic => t!("Classic"),
}
.to_string(),
OptionRow::CursorBlink => on_off(self.cursor_blink),
OptionRow::TabWidth => self.tab_width.to_string(),
OptionRow::LineNumbers => on_off(self.line_numbers),
OptionRow::ScrollBars => {
let (down, along) = self.scroll_bar.thumbs();
format!("{} {down}{along}", self.scroll_bar.label())
}
OptionRow::HighlightLimit => match self.highlight_limit_kib {
0 => t!("Off").to_string(),
kib if kib % 1024 == 0 => format!("{} MiB", kib / 1024),
kib => format!("{} KiB", kib),
},
OptionRow::LargeFile => format!("{} MiB", self.large_file_mib),
OptionRow::ImageDefault => if self.image_fill { t!("Fill") } else { t!("Fit") }.to_string(),
OptionRow::ImageBackgrounds => on_off(self.image_backgrounds),
OptionRow::ImagePrefetch => on_off(self.image_prefetch),
}
}
pub fn text(&self, row: OptionRow) -> &str {
match row {
OptionRow::Editor => &self.editor,
_ => &self.terminal,
}
}
pub fn set_text(&mut self, row: OptionRow, text: String) {
match row {
OptionRow::Editor => self.editor = text,
OptionRow::Terminal => self.terminal = text,
_ => {}
}
}
pub fn cycle(&mut self, row: OptionRow, forward: bool) {
fn step<T: Copy + PartialEq>(values: &[T], current: T, forward: bool) -> T {
let Some(index) = values.iter().position(|&value| value == current) else {
return values[0];
};
let next = if forward { index + 1 } else { index + values.len() - 1 };
values[next % values.len()]
}
match row {
OptionRow::ShowHidden => self.show_hidden = !self.show_hidden,
OptionRow::SortKey => {
let keys = [SortKey::Name, SortKey::Extension, SortKey::Size, SortKey::Modified];
self.sort_key = step(&keys, self.sort_key, forward);
}
OptionRow::SortDirection => self.sort_descending = !self.sort_descending,
OptionRow::DirsFirst => self.dirs_first = !self.dirs_first,
OptionRow::CaseSensitive => self.case_sensitive = !self.case_sensitive,
OptionRow::ColumnExt => self.column_ext = !self.column_ext,
OptionRow::ColumnSize => self.column_size = !self.column_size,
OptionRow::ColumnModified => self.column_modified = !self.column_modified,
OptionRow::ColumnAttributes => self.column_attributes = !self.column_attributes,
OptionRow::GitMarks => self.git_marks = !self.git_marks,
OptionRow::DateFormat => {
let formats = [DateFormat::Short, DateFormat::Iso, DateFormat::Relative];
self.date_format = step(&formats, self.date_format, forward);
}
OptionRow::SizeUnits => self.size_units = if self.size_units == SizeUnits::Binary { SizeUnits::Decimal } else { SizeUnits::Binary },
OptionRow::ConfirmDelete => self.confirm_delete = !self.confirm_delete,
OptionRow::DeleteToTrash => self.delete_to_trash = !self.delete_to_trash,
OptionRow::ConfirmCopyMove => self.confirm_copy_move = !self.confirm_copy_move,
OptionRow::OnExisting => {
let policies = [OnExisting::Ask, OnExisting::Overwrite, OnExisting::KeepBoth, OnExisting::Refuse];
self.on_existing = step(&policies, self.on_existing, forward);
}
OptionRow::RememberDirs => self.remember_dirs = !self.remember_dirs,
OptionRow::PreviewOnStart => self.preview_on_start = !self.preview_on_start,
OptionRow::Language => {
let choices: Vec<Option<crate::i18n::Language>> = std::iter::once(None).chain(crate::i18n::Language::ALL.into_iter().map(Some)).collect();
self.language = step(&choices, self.language, forward);
}
OptionRow::Theme => {
self.theme = step(&Theme::ALL, self.theme, forward);
}
OptionRow::ThemeBackground => {
self.theme_background = !self.theme_background;
}
OptionRow::IconStyle => {
let styles = [IconStyle::NerdFont, IconStyle::NerdFontMono, IconStyle::Plain];
self.icon_style = step(&styles, self.icon_style, forward);
}
OptionRow::Clock => {
let clocks = [Clock::Hours24, Clock::Hours12, Clock::Off];
self.clock = step(&clocks, self.clock, forward);
}
OptionRow::Cursor => {
let shapes = [CursorShape::Bar, CursorShape::Block, CursorShape::Underline, CursorShape::Classic];
self.cursor = step(&shapes, self.cursor, forward);
}
OptionRow::CursorBlink => self.cursor_blink = !self.cursor_blink,
OptionRow::TabWidth => self.tab_width = step(&TAB_WIDTHS, self.tab_width, forward),
OptionRow::LineNumbers => self.line_numbers = !self.line_numbers,
OptionRow::ScrollBars => self.scroll_bar = step(&ScrollBarStyle::ALL, self.scroll_bar, forward),
OptionRow::HighlightLimit => self.highlight_limit_kib = step(&HIGHLIGHT_LIMITS_KIB, self.highlight_limit_kib, forward),
OptionRow::LargeFile => self.large_file_mib = step(&LARGE_FILE_LIMITS_MIB, self.large_file_mib, forward),
OptionRow::ImageDefault => self.image_fill = !self.image_fill,
OptionRow::ImageBackgrounds => self.image_backgrounds = !self.image_backgrounds,
OptionRow::ImagePrefetch => self.image_prefetch = !self.image_prefetch,
OptionRow::Terminal | OptionRow::Editor => {}
}
}
}