From 322dad2b8ad76316864b38c8aa04aabfb6b2360a Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 28 May 2024 10:40:36 -0600 Subject: [PATCH] Implement view menu, fixes #184 --- i18n/en/cosmic_files.ftl | 5 ++++ src/app.rs | 20 +++++++++++-- src/config.rs | 3 ++ src/key_bind.rs | 3 ++ src/menu.rs | 32 +++++++++++++++++++-- src/tab.rs | 61 +++++++++++++++++++++++++++++++++++++--- 6 files changed, 114 insertions(+), 10 deletions(-) diff --git a/i18n/en/cosmic_files.ftl b/i18n/en/cosmic_files.ftl index 568344e..c77d924 100644 --- a/i18n/en/cosmic_files.ftl +++ b/i18n/en/cosmic_files.ftl @@ -115,8 +115,13 @@ paste = Paste select-all = Select all ## View +zoom-in = Zoom in +default-size = Default size +zoom-out = Zoom out view = View grid-view = Grid view list-view = List view +show-hidden-files = Show hidden files +list-directories-first = List directories first menu-settings = Settings... menu-about = About COSMIC Files... diff --git a/src/app.rs b/src/app.rs index a474d35..ebb6577 100644 --- a/src/app.rs +++ b/src/app.rs @@ -93,10 +93,14 @@ pub enum Action { TabPrev, TabViewGrid, TabViewList, + ToggleFoldersFirst, ToggleShowHidden, ToggleSort(HeadingOptions), WindowClose, WindowNew, + ZoomDefault, + ZoomIn, + ZoomOut, } impl Action { @@ -137,10 +141,20 @@ impl Action { Action::TabViewList => { Message::TabMessage(entity_opt, tab::Message::View(tab::View::List)) } - Action::ToggleShowHidden => Message::TabMessage(None, tab::Message::ToggleShowHidden), - Action::ToggleSort(sort) => Message::TabMessage(None, tab::Message::ToggleSort(*sort)), + Action::ToggleFoldersFirst => { + Message::TabMessage(entity_opt, tab::Message::ToggleFoldersFirst) + } + Action::ToggleShowHidden => { + Message::TabMessage(entity_opt, tab::Message::ToggleShowHidden) + } + Action::ToggleSort(sort) => { + Message::TabMessage(entity_opt, tab::Message::ToggleSort(*sort)) + } Action::WindowClose => Message::WindowClose, Action::WindowNew => Message::WindowNew, + Action::ZoomDefault => Message::TabMessage(entity_opt, tab::Message::ZoomDefault), + Action::ZoomIn => Message::TabMessage(entity_opt, tab::Message::ZoomIn), + Action::ZoomOut => Message::TabMessage(entity_opt, tab::Message::ZoomOut), } } } @@ -1951,7 +1965,7 @@ impl Application for App { } fn header_start(&self) -> Vec> { - vec![menu::menu_bar(&self.key_binds).into()] + vec![menu::menu_bar(self.tab_model.active_data::(), &self.key_binds).into()] } fn header_end(&self) -> Vec> { diff --git a/src/config.rs b/src/config.rs index 36c131c..aa67818 100644 --- a/src/config.rs +++ b/src/config.rs @@ -111,6 +111,8 @@ impl Default for Config { /// locally. Local changes aren't saved to the main config. #[derive(Clone, Copy, Debug, Eq, PartialEq, CosmicConfigEntry, Deserialize, Serialize)] pub struct TabConfig { + /// Show folders before files + pub folders_first: bool, /// Show hidden files and folders pub show_hidden: bool, /// Sorter @@ -123,6 +125,7 @@ pub struct TabConfig { impl Default for TabConfig { fn default() -> Self { Self { + folders_first: true, show_hidden: false, sort_name: HeadingOptions::Name, sort_direction: true, diff --git a/src/key_bind.rs b/src/key_bind.rs index 13aeb4f..72d5659 100644 --- a/src/key_bind.rs +++ b/src/key_bind.rs @@ -51,6 +51,9 @@ pub fn key_binds() -> HashMap { bind!([Ctrl], Key::Character("h".into()), ToggleShowHidden); bind!([Ctrl], Key::Character("q".into()), WindowClose); bind!([Ctrl], Key::Character("n".into()), WindowNew); + bind!([Ctrl], Key::Character("=".into()), ZoomIn); + bind!([Ctrl], Key::Character("0".into()), ZoomDefault); + bind!([Ctrl], Key::Character("-".into()), ZoomOut); key_binds } diff --git a/src/menu.rs b/src/menu.rs index e801bf3..9088f9a 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -169,7 +169,10 @@ pub fn context_menu<'a>( .into() } -pub fn menu_bar<'a>(key_binds: &HashMap) -> Element<'a, Message> { +pub fn menu_bar<'a>( + tab_opt: Option<&Tab>, + key_binds: &HashMap, +) -> Element<'a, Message> { MenuBar::new(vec![ menu::Tree::with_children( menu::root(fl!("file")), @@ -212,8 +215,31 @@ pub fn menu_bar<'a>(key_binds: &HashMap) -> Element<'a, Message menu::items( key_binds, vec![ - menu::Item::Button(fl!("grid-view"), Action::TabViewGrid), - menu::Item::Button(fl!("list-view"), Action::TabViewList), + menu::Item::Button(fl!("zoom-in"), Action::ZoomIn), + menu::Item::Button(fl!("default-size"), Action::ZoomDefault), + menu::Item::Button(fl!("zoom-out"), Action::ZoomOut), + menu::Item::Divider, + menu::Item::CheckBox( + fl!("grid-view"), + tab_opt.map_or(false, |tab| matches!(tab.view, tab::View::Grid)), + Action::TabViewGrid, + ), + menu::Item::CheckBox( + fl!("list-view"), + tab_opt.map_or(false, |tab| matches!(tab.view, tab::View::List)), + Action::TabViewList, + ), + menu::Item::Divider, + menu::Item::CheckBox( + fl!("show-hidden-files"), + tab_opt.map_or(false, |tab| tab.config.show_hidden), + Action::ToggleShowHidden, + ), + menu::Item::CheckBox( + fl!("list-directories-first"), + tab_opt.map_or(false, |tab| tab.config.folders_first), + Action::ToggleFoldersFirst, + ), menu::Item::Divider, menu::Item::Button(fl!("menu-settings"), Action::Settings), menu::Item::Divider, diff --git a/src/tab.rs b/src/tab.rs index 33e2134..31d9790 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -36,6 +36,7 @@ use std::{ collections::HashMap, fmt, fs::{self, Metadata}, + num::NonZeroU16, path::PathBuf, time::{Duration, Instant}, }; @@ -465,6 +466,7 @@ pub enum Message { Scroll(Viewport), SelectAll, Thumbnail(PathBuf, ItemThumbnail), + ToggleFoldersFirst, ToggleShowHidden, View(View), ToggleSort(HeadingOptions), @@ -472,6 +474,9 @@ pub enum Message { DndHover(Location), DndEnter(Location), DndLeave(Location), + ZoomDefault, + ZoomIn, + ZoomOut, } #[derive(Clone, Debug)] @@ -1394,6 +1399,7 @@ impl Tab { } } } + Message::ToggleFoldersFirst => self.config.folders_first = !self.config.folders_first, Message::ToggleShowHidden => self.config.show_hidden = !self.config.show_hidden, Message::View(view) => { @@ -1453,6 +1459,48 @@ impl Tab { self.dnd_hovered = None; } } + Message::ZoomDefault => match self.view { + View::List => self.config.icon_sizes.list = 100.try_into().unwrap(), + View::Grid => self.config.icon_sizes.grid = 100.try_into().unwrap(), + }, + Message::ZoomIn => { + let zoom_in = |size: &mut NonZeroU16, min: u16, max: u16| { + let mut step = min; + while step <= max { + if size.get() < step { + *size = step.try_into().unwrap(); + break; + } + step += 25; + } + if size.get() > step { + *size = step.try_into().unwrap(); + } + }; + match self.view { + View::List => zoom_in(&mut self.config.icon_sizes.list, 100, 500), + View::Grid => zoom_in(&mut self.config.icon_sizes.grid, 50, 500), + } + } + Message::ZoomOut => { + let zoom_out = |size: &mut NonZeroU16, min: u16, max: u16| { + let mut step = max; + while step >= min { + if size.get() > step { + *size = step.try_into().unwrap(); + break; + } + step -= 25; + } + if size.get() < step { + *size = step.try_into().unwrap(); + } + }; + match self.view { + View::List => zoom_out(&mut self.config.icon_sizes.list, 100, 500), + View::Grid => zoom_out(&mut self.config.icon_sizes.grid, 50, 500), + } + } } if let Some(location) = cd { if location != self.location { @@ -1509,10 +1557,14 @@ impl Tab { }) } HeadingOptions::Name => items.sort_by(|a, b| { - let ord = match (a.1.metadata.is_dir(), b.1.metadata.is_dir()) { - (true, false) => Ordering::Less, - (false, true) => Ordering::Greater, - _ => lexical_sort::natural_lexical_cmp(&a.1.name, &b.1.name), + let ord = if self.config.folders_first { + match (a.1.metadata.is_dir(), b.1.metadata.is_dir()) { + (true, false) => Ordering::Less, + (false, true) => Ordering::Greater, + _ => lexical_sort::natural_lexical_cmp(&a.1.name, &b.1.name), + } + } else { + lexical_sort::natural_lexical_cmp(&a.1.name, &b.1.name) }; check_reverse(ord, heading_sort) }), @@ -1993,6 +2045,7 @@ impl Tab { icon_sizes, sort_name, sort_direction, + .. } = self.config; let size = self.size_opt.unwrap_or_else(|| Size::new(0.0, 0.0));