feat: persist sort options for each location
This commit is contained in:
parent
68c4a8bb6a
commit
01bf55865c
4 changed files with 111 additions and 16 deletions
55
src/app.rs
55
src/app.rs
|
|
@ -16,7 +16,8 @@ use cosmic::iced::{
|
|||
use cosmic::iced_winit::commands::overlap_notify::overlap_notify;
|
||||
use cosmic::{
|
||||
app::{self, context_drawer, Core, Task},
|
||||
cosmic_config, cosmic_theme, executor,
|
||||
cosmic_config::{self, ConfigSet},
|
||||
cosmic_theme, executor,
|
||||
iced::{
|
||||
self,
|
||||
clipboard::dnd::DndAction,
|
||||
|
|
@ -358,6 +359,7 @@ pub enum Message {
|
|||
Rename(Option<Entity>),
|
||||
ReplaceResult(ReplaceResult),
|
||||
RestoreFromTrash(Option<Entity>),
|
||||
SaveSortNames,
|
||||
ScrollTab(i16),
|
||||
SearchActivate,
|
||||
SearchClear,
|
||||
|
|
@ -574,6 +576,7 @@ pub struct App {
|
|||
mime_app_cache: MimeAppCache,
|
||||
modifiers: Modifiers,
|
||||
mounter_items: HashMap<MounterKey, MounterItems>,
|
||||
must_save_sort_names: bool,
|
||||
network_drive_connecting: Option<(MounterKey, String)>,
|
||||
network_drive_input: String,
|
||||
#[cfg(feature = "notify")]
|
||||
|
|
@ -873,7 +876,11 @@ impl App {
|
|||
activate: bool,
|
||||
selection_paths: Option<Vec<PathBuf>>,
|
||||
) -> (Entity, Task<Message>) {
|
||||
let mut tab = Tab::new(location.clone(), self.config.tab);
|
||||
let mut tab = Tab::new(
|
||||
location.clone(),
|
||||
self.config.tab,
|
||||
Some(&self.config.sort_names),
|
||||
);
|
||||
tab.mode = match self.mode {
|
||||
Mode::App => tab::Mode::App,
|
||||
Mode::Desktop => {
|
||||
|
|
@ -1949,6 +1956,7 @@ impl Application for App {
|
|||
mime_app_cache: MimeAppCache::new(),
|
||||
modifiers: Modifiers::empty(),
|
||||
mounter_items: HashMap::new(),
|
||||
must_save_sort_names: false,
|
||||
network_drive_connecting: None,
|
||||
network_drive_input: String::new(),
|
||||
#[cfg(feature = "notify")]
|
||||
|
|
@ -3587,6 +3595,19 @@ impl Application for App {
|
|||
commands.push(window::toggle_maximize(*window_id));
|
||||
}
|
||||
}
|
||||
tab::Command::SetSort(location, heading_options, direction) => {
|
||||
self.config
|
||||
.sort_names
|
||||
.insert(location, (heading_options, direction));
|
||||
self.must_save_sort_names = true;
|
||||
return cosmic::Task::perform(
|
||||
async move {
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
cosmic::action::app(Message::SaveSortNames)
|
||||
},
|
||||
|x| x,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Task::batch(commands);
|
||||
|
|
@ -3599,11 +3620,22 @@ impl Application for App {
|
|||
};
|
||||
return self.open_tab(location, true, None);
|
||||
}
|
||||
Message::TabRescan(entity, location, parent_item_opt, items, selection_paths) => {
|
||||
Message::TabRescan(entity, mut location, parent_item_opt, items, selection_paths) => {
|
||||
location = location.normalize();
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
tab.location = tab.location.normalize();
|
||||
if location == tab.location {
|
||||
tab.parent_item_opt = parent_item_opt;
|
||||
tab.set_items(items);
|
||||
let sort = self
|
||||
.config
|
||||
.sort_names
|
||||
.get(&location.to_string())
|
||||
.unwrap_or_else(|| &(HeadingOptions::Name, true));
|
||||
|
||||
tab.sort_name = sort.0;
|
||||
tab.sort_direction = sort.1;
|
||||
|
||||
if let Some(selection_paths) = selection_paths {
|
||||
tab.select_paths(selection_paths);
|
||||
}
|
||||
|
|
@ -4194,6 +4226,21 @@ impl Application for App {
|
|||
cosmic::app::Action::Surface(a),
|
||||
));
|
||||
}
|
||||
Message::SaveSortNames => {
|
||||
if self.must_save_sort_names {
|
||||
self.must_save_sort_names = false;
|
||||
if let Some(config_handler) = self.config_handler.as_ref() {
|
||||
if let Err(err) = config_handler
|
||||
.set::<HashMap<String, (HeadingOptions, bool)>>(
|
||||
"sort_names",
|
||||
self.config.sort_names.clone(),
|
||||
)
|
||||
{
|
||||
log::warn!("Failed to save sort names: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Task::none()
|
||||
|
|
@ -5764,7 +5811,7 @@ pub(crate) mod test_utils {
|
|||
// New tab with items
|
||||
let location = Location::Path(path.to_owned());
|
||||
let (parent_item_opt, items) = location.scan(IconSizes::default());
|
||||
let mut tab = Tab::new(location, TabConfig::default());
|
||||
let mut tab = Tab::new(location, TabConfig::default(), None);
|
||||
tab.parent_item_opt = parent_item_opt;
|
||||
tab.set_items(items);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{any::TypeId, num::NonZeroU16, path::PathBuf};
|
||||
use std::{any::TypeId, collections::HashMap, num::NonZeroU16, path::PathBuf};
|
||||
|
||||
use cosmic::{
|
||||
cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry},
|
||||
|
|
@ -9,7 +9,10 @@ use cosmic::{
|
|||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{app::App, tab::View};
|
||||
use crate::{
|
||||
app::App,
|
||||
tab::{HeadingOptions, Location, View},
|
||||
};
|
||||
|
||||
pub const CONFIG_VERSION: u64 = 1;
|
||||
|
||||
|
|
@ -110,6 +113,7 @@ pub struct Config {
|
|||
pub show_details: bool,
|
||||
pub tab: TabConfig,
|
||||
pub type_to_search: TypeToSearch,
|
||||
pub sort_names: HashMap<String, (HeadingOptions, bool)>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
|
@ -158,6 +162,12 @@ impl Default for Config {
|
|||
show_details: false,
|
||||
tab: TabConfig::default(),
|
||||
type_to_search: TypeToSearch::Recursive,
|
||||
sort_names: HashMap::from_iter(dirs::download_dir().into_iter().map(|dir| {
|
||||
(
|
||||
Location::Path(dir).normalize().to_string(),
|
||||
(HeadingOptions::Modified, false),
|
||||
)
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -916,7 +916,7 @@ impl Application for App {
|
|||
folders_first: false,
|
||||
..Default::default()
|
||||
};
|
||||
let mut tab = Tab::new(location, tab_config);
|
||||
let mut tab = Tab::new(location, tab_config, None);
|
||||
tab.mode = tab::Mode::Dialog(flags.kind.clone());
|
||||
tab.sort_name = tab::HeadingOptions::Modified;
|
||||
tab.sort_direction = false;
|
||||
|
|
|
|||
56
src/tab.rs
56
src/tab.rs
|
|
@ -54,10 +54,11 @@ use std::{
|
|||
error::Error,
|
||||
fmt::{self, Display},
|
||||
fs::{self, File, Metadata},
|
||||
hash::Hash,
|
||||
io::{BufRead, BufReader},
|
||||
os::unix::fs::MetadataExt,
|
||||
path::{Path, PathBuf},
|
||||
sync::{atomic, Arc, Mutex, RwLock},
|
||||
sync::{atomic, Arc, LazyLock, Mutex, RwLock},
|
||||
time::{Duration, Instant, SystemTime},
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
|
|
@ -90,6 +91,16 @@ const THUMBNAIL_SIZE: u32 = (ICON_SIZE_GRID as u32) * (ICON_SCALE_MAX as u32);
|
|||
|
||||
const DRAG_SCROLL_DISTANCE: f32 = 15.0;
|
||||
|
||||
static SORT_OPTION_FALLBACK: LazyLock<HashMap<String, (HeadingOptions, bool)>> =
|
||||
LazyLock::new(|| {
|
||||
HashMap::from_iter(dirs::download_dir().into_iter().map(|dir| {
|
||||
(
|
||||
Location::Path(dir).normalize().to_string(),
|
||||
(HeadingOptions::Modified, false),
|
||||
)
|
||||
}))
|
||||
});
|
||||
|
||||
static MODE_NAMES: Lazy<Vec<String>> = Lazy::new(|| {
|
||||
vec![
|
||||
// Mode 0
|
||||
|
|
@ -1506,6 +1517,7 @@ pub enum Command {
|
|||
Preview(PreviewKind),
|
||||
SetOpenWith(Mime, String),
|
||||
SetPermissions(PathBuf, u32),
|
||||
SetSort(String, HeadingOptions, bool),
|
||||
WindowDrag,
|
||||
WindowToggleMaximize,
|
||||
}
|
||||
|
|
@ -2351,7 +2363,17 @@ fn parse_hidden_file(path: &PathBuf) -> Vec<String> {
|
|||
}
|
||||
|
||||
impl Tab {
|
||||
pub fn new(location: Location, config: TabConfig) -> Self {
|
||||
pub fn new(
|
||||
location: Location,
|
||||
config: TabConfig,
|
||||
sorting_options: Option<&HashMap<String, (HeadingOptions, bool)>>,
|
||||
) -> Self {
|
||||
let location_str = location.to_string();
|
||||
let (sort_name, sort_direction) = sorting_options
|
||||
.and_then(|opts| opts.get(&location_str))
|
||||
.or_else(|| SORT_OPTION_FALLBACK.get(&location_str))
|
||||
.cloned()
|
||||
.unwrap_or_else(|| (HeadingOptions::Name, true));
|
||||
let location = location.normalize();
|
||||
let location_ancestors = location.ancestors();
|
||||
let location_title = location.title();
|
||||
|
|
@ -2372,8 +2394,8 @@ impl Tab {
|
|||
history_i: 0,
|
||||
history,
|
||||
config,
|
||||
sort_name: HeadingOptions::Name,
|
||||
sort_direction: true,
|
||||
sort_name,
|
||||
sort_direction,
|
||||
gallery: false,
|
||||
parent_item_opt: None,
|
||||
items_opt: None,
|
||||
|
|
@ -3669,6 +3691,13 @@ impl Tab {
|
|||
if !matches!(self.location, Location::Search(..)) {
|
||||
self.sort_name = heading_option;
|
||||
self.sort_direction = dir;
|
||||
if !matches!(self.location, Location::Desktop(..)) {
|
||||
commands.push(Command::SetSort(
|
||||
self.location.normalize().to_string(),
|
||||
heading_option,
|
||||
self.sort_direction,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::TabComplete(path, completions) => {
|
||||
|
|
@ -3721,6 +3750,15 @@ impl Tab {
|
|||
// Default modified to descending, and others to ascending.
|
||||
heading_option != HeadingOptions::Modified
|
||||
};
|
||||
|
||||
if !matches!(self.location, Location::Desktop(..)) {
|
||||
commands.push(Command::SetSort(
|
||||
self.location.normalize().to_string(),
|
||||
heading_option,
|
||||
heading_sort,
|
||||
));
|
||||
}
|
||||
|
||||
self.sort_direction = heading_sort;
|
||||
self.sort_name = heading_option;
|
||||
}
|
||||
|
|
@ -5941,7 +5979,7 @@ mod tests {
|
|||
fn tab_history() -> io::Result<(TempDir, Tab, Vec<PathBuf>)> {
|
||||
let fs = simple_fs(NUM_FILES, NUM_NESTED, NUM_DIRS, NUM_NESTED, NAME_LEN)?;
|
||||
let path = fs.path();
|
||||
let mut tab = Tab::new(Location::Path(path.into()), TabConfig::default());
|
||||
let mut tab = Tab::new(Location::Path(path.into()), TabConfig::default(), None);
|
||||
|
||||
// All directories (simple_fs only produces one nested layer)
|
||||
let dirs: Vec<PathBuf> = filter_dirs(path)?
|
||||
|
|
@ -6038,7 +6076,7 @@ mod tests {
|
|||
.next()
|
||||
.expect("temp directory should have at least one directory");
|
||||
|
||||
let mut tab = Tab::new(Location::Path(path.to_owned()), TabConfig::default());
|
||||
let mut tab = Tab::new(Location::Path(path.to_owned()), TabConfig::default(), None);
|
||||
debug!(
|
||||
"Emitting Message::Location(Location::Path(\"{}\"))",
|
||||
next_dir.display()
|
||||
|
|
@ -6170,7 +6208,7 @@ mod tests {
|
|||
fn tab_empty_history_does_nothing_on_prev_next() -> io::Result<()> {
|
||||
let fs = simple_fs(0, NUM_NESTED, NUM_DIRS, 0, NAME_LEN)?;
|
||||
let path = fs.path();
|
||||
let mut tab = Tab::new(Location::Path(path.into()), TabConfig::default());
|
||||
let mut tab = Tab::new(Location::Path(path.into()), TabConfig::default(), None);
|
||||
|
||||
// Tab's location shouldn't change if GoPrev or GoNext is triggered
|
||||
debug!("Emitting Message::GoPrevious",);
|
||||
|
|
@ -6192,7 +6230,7 @@ mod tests {
|
|||
.next()
|
||||
.expect("should be at least one directory");
|
||||
|
||||
let mut tab = Tab::new(Location::Path(next_dir.clone()), TabConfig::default());
|
||||
let mut tab = Tab::new(Location::Path(next_dir.clone()), TabConfig::default(), None);
|
||||
// This will eventually yield false once root is hit
|
||||
while next_dir.pop() {
|
||||
debug!("Emitting Message::LocationUp",);
|
||||
|
|
@ -6225,7 +6263,7 @@ mod tests {
|
|||
}
|
||||
|
||||
debug!("Creating tab for directory of long file names");
|
||||
Tab::new(Location::Path(path.into()), TabConfig::default());
|
||||
Tab::new(Location::Path(path.into()), TabConfig::default(), None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue