Add operation to permanently delete trash items, fixes #841
This commit is contained in:
parent
3cce822ffc
commit
c8aa80fb2f
6 changed files with 101 additions and 18 deletions
|
|
@ -181,6 +181,14 @@ copied = Copied {$items} {$items ->
|
|||
[one] item
|
||||
*[other] items
|
||||
} from "{$from}" to "{$to}"
|
||||
deleting = Deleting {$items} {$items ->
|
||||
[one] item
|
||||
*[other] items
|
||||
} from {trash} ({$progress})...
|
||||
deleted = Deleted {$items} {$items ->
|
||||
[one] item
|
||||
*[other] items
|
||||
} from {trash}
|
||||
emptying-trash = Emptying {trash} ({$progress})...
|
||||
emptied-trash = Emptied {trash}
|
||||
extracting = Extracting {$items} {$items ->
|
||||
|
|
@ -240,6 +248,7 @@ light = Light
|
|||
# Context menu
|
||||
add-to-sidebar = Add to sidebar
|
||||
compress = Compress
|
||||
delete-permanently = Delete permanently
|
||||
extract-here = Extract
|
||||
new-file = New file...
|
||||
new-folder = New folder...
|
||||
|
|
|
|||
51
src/app.rs
51
src/app.rs
|
|
@ -100,6 +100,7 @@ pub enum Action {
|
|||
CosmicSettingsDisplays,
|
||||
CosmicSettingsWallpaper,
|
||||
DesktopViewOptions,
|
||||
Delete,
|
||||
EditHistory,
|
||||
EditLocation,
|
||||
EmptyTrash,
|
||||
|
|
@ -114,7 +115,6 @@ pub enum Action {
|
|||
ItemRight,
|
||||
ItemUp,
|
||||
LocationUp,
|
||||
MoveToTrash,
|
||||
NewFile,
|
||||
NewFolder,
|
||||
Open,
|
||||
|
|
@ -161,6 +161,7 @@ impl Action {
|
|||
Action::CosmicSettingsAppearance => Message::CosmicSettings("appearance"),
|
||||
Action::CosmicSettingsDisplays => Message::CosmicSettings("displays"),
|
||||
Action::CosmicSettingsWallpaper => Message::CosmicSettings("wallpaper"),
|
||||
Action::Delete => Message::Delete(entity_opt),
|
||||
Action::DesktopViewOptions => Message::DesktopViewOptions,
|
||||
Action::EditHistory => Message::ToggleContextPage(ContextPage::EditHistory),
|
||||
Action::EditLocation => {
|
||||
|
|
@ -180,7 +181,6 @@ impl Action {
|
|||
Action::ItemRight => Message::TabMessage(entity_opt, tab::Message::ItemRight),
|
||||
Action::ItemUp => Message::TabMessage(entity_opt, tab::Message::ItemUp),
|
||||
Action::LocationUp => Message::TabMessage(entity_opt, tab::Message::LocationUp),
|
||||
Action::MoveToTrash => Message::MoveToTrash(entity_opt),
|
||||
Action::NewFile => Message::NewItem(entity_opt, false),
|
||||
Action::NewFolder => Message::NewItem(entity_opt, true),
|
||||
Action::Open => Message::TabMessage(entity_opt, tab::Message::Open(None)),
|
||||
|
|
@ -281,6 +281,7 @@ pub enum Message {
|
|||
CosmicSettings(&'static str),
|
||||
CursorMoved(Point),
|
||||
Cut(Option<Entity>),
|
||||
Delete(Option<Entity>),
|
||||
DesktopConfig(DesktopConfig),
|
||||
DesktopViewOptions,
|
||||
DialogCancel,
|
||||
|
|
@ -295,7 +296,6 @@ pub enum Message {
|
|||
LaunchUrl(String),
|
||||
MaybeExit,
|
||||
Modifiers(Modifiers),
|
||||
MoveToTrash(Option<Entity>),
|
||||
MounterItems(MounterKey, MounterItems),
|
||||
MountResult(MounterKey, MounterItem, Result<bool, String>),
|
||||
NavBarClose(Entity),
|
||||
|
|
@ -1966,6 +1966,39 @@ impl Application for App {
|
|||
}
|
||||
}
|
||||
}
|
||||
Message::Delete(entity_opt) => {
|
||||
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
||||
if let Some(tab) = self.tab_model.data::<Tab>(entity) {
|
||||
match &tab.location {
|
||||
Location::Trash => {
|
||||
if let Some(items) = tab.items_opt() {
|
||||
let mut trash_items = Vec::new();
|
||||
for item in items.iter() {
|
||||
if item.selected {
|
||||
match &item.metadata {
|
||||
ItemMetadata::Trash { entry, .. } => {
|
||||
trash_items.push(entry.clone());
|
||||
}
|
||||
_ => {
|
||||
//TODO: error on trying to permanently delete non-trash file?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !trash_items.is_empty() {
|
||||
self.operation(Operation::DeleteTrash { items: trash_items });
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let paths = dbg!(self.selected_paths(entity_opt));
|
||||
if !paths.is_empty() {
|
||||
self.operation(Operation::Delete { paths });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::DesktopConfig(config) => {
|
||||
if config != self.config.desktop {
|
||||
config_set!(desktop, config);
|
||||
|
|
@ -2177,12 +2210,6 @@ impl Application for App {
|
|||
Message::Modifiers(modifiers) => {
|
||||
self.modifiers = modifiers;
|
||||
}
|
||||
Message::MoveToTrash(entity_opt) => {
|
||||
let paths = self.selected_paths(entity_opt);
|
||||
if !paths.is_empty() {
|
||||
self.operation(Operation::Delete { paths });
|
||||
}
|
||||
}
|
||||
Message::MounterItems(mounter_key, mounter_items) => {
|
||||
// Check for unmounted folders
|
||||
let mut unmounted = Vec::new();
|
||||
|
|
@ -2972,6 +2999,9 @@ impl Application for App {
|
|||
self.update_tab(entity, tab_path, selection_paths),
|
||||
]));
|
||||
}
|
||||
tab::Command::Delete(paths) => {
|
||||
self.operation(Operation::Delete { paths });
|
||||
}
|
||||
tab::Command::DropFiles(to, from) => {
|
||||
commands.push(self.update(Message::PasteContents(to, from)));
|
||||
}
|
||||
|
|
@ -2989,9 +3019,6 @@ impl Application for App {
|
|||
}),
|
||||
);
|
||||
}
|
||||
tab::Command::MoveToTrash(paths) => {
|
||||
self.operation(Operation::Delete { paths });
|
||||
}
|
||||
tab::Command::OpenFile(path) => self.open_file(&path),
|
||||
tab::Command::OpenInNewTab(path) => {
|
||||
commands.push(self.open_tab(Location::Path(path.clone()), false, None));
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ pub fn key_binds(mode: &tab::Mode) -> HashMap<KeyBind, Action> {
|
|||
if matches!(mode, tab::Mode::App | tab::Mode::Desktop) {
|
||||
bind!([Ctrl], Key::Character("c".into()), Copy);
|
||||
bind!([Ctrl], Key::Character("x".into()), Cut);
|
||||
bind!([], Key::Named(Named::Delete), MoveToTrash);
|
||||
bind!([], Key::Named(Named::Delete), Delete);
|
||||
bind!([Shift], Key::Named(Named::Enter), OpenInNewWindow);
|
||||
bind!([Ctrl], Key::Character("v".into()), Paste);
|
||||
bind!([], Key::Named(Named::F2), Rename);
|
||||
|
|
|
|||
16
src/menu.rs
16
src/menu.rs
|
|
@ -151,7 +151,7 @@ pub fn context_menu<'a>(
|
|||
children.push(menu_item(fl!("cut"), Action::Cut).into());
|
||||
children.push(menu_item(fl!("copy"), Action::Copy).into());
|
||||
// Should this simply bypass trash and remove the shortcut?
|
||||
children.push(menu_item(fl!("move-to-trash"), Action::MoveToTrash).into());
|
||||
children.push(menu_item(fl!("move-to-trash"), Action::Delete).into());
|
||||
} else if selected > 0 {
|
||||
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
||||
children.push(menu_item(fl!("open"), Action::Open).into());
|
||||
|
|
@ -211,7 +211,7 @@ pub fn context_menu<'a>(
|
|||
children.push(menu_item(fl!("add-to-sidebar"), Action::AddToSidebar).into());
|
||||
}
|
||||
children.push(divider::horizontal::light().into());
|
||||
children.push(menu_item(fl!("move-to-trash"), Action::MoveToTrash).into());
|
||||
children.push(menu_item(fl!("move-to-trash"), Action::Delete).into());
|
||||
} else {
|
||||
//TODO: need better designs for menu with no selection
|
||||
//TODO: have things like properties but they apply to the folder?
|
||||
|
|
@ -311,6 +311,8 @@ pub fn context_menu<'a>(
|
|||
children.push(divider::horizontal::light().into());
|
||||
children
|
||||
.push(menu_item(fl!("restore-from-trash"), Action::RestoreFromTrash).into());
|
||||
children.push(divider::horizontal::light().into());
|
||||
children.push(menu_item(fl!("delete-permanently"), Action::Delete).into());
|
||||
} else {
|
||||
// TODO: Nested menu
|
||||
children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name));
|
||||
|
|
@ -537,7 +539,15 @@ pub fn menu_bar<'a>(
|
|||
menu::Item::Divider,
|
||||
menu_button_optional(fl!("add-to-sidebar"), Action::AddToSidebar, selected > 0),
|
||||
menu::Item::Divider,
|
||||
menu_button_optional(fl!("move-to-trash"), Action::MoveToTrash, selected > 0),
|
||||
menu_button_optional(
|
||||
if in_trash {
|
||||
fl!("delete-permanently")
|
||||
} else {
|
||||
fl!("move-to-trash")
|
||||
},
|
||||
Action::Delete,
|
||||
selected > 0,
|
||||
),
|
||||
menu::Item::Divider,
|
||||
menu::Item::Button(fl!("close-tab"), None, Action::TabClose),
|
||||
menu::Item::Button(fl!("quit"), None, Action::WindowClose),
|
||||
|
|
|
|||
|
|
@ -460,6 +460,10 @@ pub enum Operation {
|
|||
Delete {
|
||||
paths: Vec<PathBuf>,
|
||||
},
|
||||
/// Delete a path from the trash
|
||||
DeleteTrash {
|
||||
items: Vec<trash::TrashItem>,
|
||||
},
|
||||
/// Empty the trash
|
||||
EmptyTrash,
|
||||
/// Uncompress files
|
||||
|
|
@ -550,6 +554,9 @@ impl Operation {
|
|||
to = fl!("trash"),
|
||||
progress = progress()
|
||||
),
|
||||
Self::DeleteTrash { items } => {
|
||||
fl!("deleting", items = items.len(), progress = progress())
|
||||
}
|
||||
Self::EmptyTrash => fl!("emptying-trash", progress = progress()),
|
||||
Self::Extract {
|
||||
paths,
|
||||
|
|
@ -609,6 +616,7 @@ impl Operation {
|
|||
from = paths_parent_name(paths),
|
||||
to = fl!("trash")
|
||||
),
|
||||
Self::DeleteTrash { items } => fl!("deleted", items = items.len()),
|
||||
Self::EmptyTrash => fl!("emptied-trash"),
|
||||
Self::Extract {
|
||||
paths,
|
||||
|
|
@ -650,6 +658,7 @@ impl Operation {
|
|||
Self::Compress { .. }
|
||||
| Self::Copy { .. }
|
||||
| Self::Delete { .. }
|
||||
| Self::DeleteTrash { .. }
|
||||
| Self::EmptyTrash
|
||||
| Self::Extract { .. }
|
||||
| Self::Move { .. }
|
||||
|
|
@ -846,6 +855,34 @@ impl Operation {
|
|||
}
|
||||
Ok(OperationSelection::default())
|
||||
}
|
||||
Self::DeleteTrash { items } => {
|
||||
#[cfg(any(
|
||||
target_os = "windows",
|
||||
all(
|
||||
unix,
|
||||
not(target_os = "macos"),
|
||||
not(target_os = "ios"),
|
||||
not(target_os = "android")
|
||||
)
|
||||
))]
|
||||
{
|
||||
tokio::task::spawn_blocking(move || -> Result<(), OperationError> {
|
||||
let count = items.len();
|
||||
for (i, item) in items.into_iter().enumerate() {
|
||||
controller.check().map_err(OperationError::from_str)?;
|
||||
|
||||
controller.set_progress(i as f32 / count as f32);
|
||||
|
||||
trash::os_limited::purge_all([item])
|
||||
.map_err(OperationError::from_str)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
.map_err(OperationError::from_str)??;
|
||||
}
|
||||
Ok(OperationSelection::default())
|
||||
}
|
||||
Self::EmptyTrash => {
|
||||
#[cfg(any(
|
||||
target_os = "windows",
|
||||
|
|
|
|||
|
|
@ -1127,12 +1127,12 @@ pub enum Command {
|
|||
AddToSidebar(PathBuf),
|
||||
AutoScroll(Option<f32>),
|
||||
ChangeLocation(String, Location, Option<Vec<PathBuf>>),
|
||||
Delete(Vec<PathBuf>),
|
||||
DropFiles(PathBuf, ClipboardPaste),
|
||||
EmptyTrash,
|
||||
#[cfg(feature = "desktop")]
|
||||
ExecEntryAction(cosmic::desktop::DesktopEntryData, usize),
|
||||
Iced(TaskWrapper),
|
||||
MoveToTrash(Vec<PathBuf>),
|
||||
OpenFile(PathBuf),
|
||||
OpenInNewTab(PathBuf),
|
||||
OpenInNewWindow(PathBuf),
|
||||
|
|
@ -3188,7 +3188,7 @@ impl Tab {
|
|||
commands.push(Command::DropFiles(to, from))
|
||||
}
|
||||
Location::Trash if matches!(from.kind, ClipboardKind::Cut) => {
|
||||
commands.push(Command::MoveToTrash(from.paths))
|
||||
commands.push(Command::Delete(from.paths))
|
||||
}
|
||||
_ => {
|
||||
log::warn!("{:?} to {:?} is not supported.", from.kind, to);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue