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
|
[one] item
|
||||||
*[other] items
|
*[other] items
|
||||||
} from "{$from}" to "{$to}"
|
} 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})...
|
emptying-trash = Emptying {trash} ({$progress})...
|
||||||
emptied-trash = Emptied {trash}
|
emptied-trash = Emptied {trash}
|
||||||
extracting = Extracting {$items} {$items ->
|
extracting = Extracting {$items} {$items ->
|
||||||
|
|
@ -240,6 +248,7 @@ light = Light
|
||||||
# Context menu
|
# Context menu
|
||||||
add-to-sidebar = Add to sidebar
|
add-to-sidebar = Add to sidebar
|
||||||
compress = Compress
|
compress = Compress
|
||||||
|
delete-permanently = Delete permanently
|
||||||
extract-here = Extract
|
extract-here = Extract
|
||||||
new-file = New file...
|
new-file = New file...
|
||||||
new-folder = New folder...
|
new-folder = New folder...
|
||||||
|
|
|
||||||
51
src/app.rs
51
src/app.rs
|
|
@ -100,6 +100,7 @@ pub enum Action {
|
||||||
CosmicSettingsDisplays,
|
CosmicSettingsDisplays,
|
||||||
CosmicSettingsWallpaper,
|
CosmicSettingsWallpaper,
|
||||||
DesktopViewOptions,
|
DesktopViewOptions,
|
||||||
|
Delete,
|
||||||
EditHistory,
|
EditHistory,
|
||||||
EditLocation,
|
EditLocation,
|
||||||
EmptyTrash,
|
EmptyTrash,
|
||||||
|
|
@ -114,7 +115,6 @@ pub enum Action {
|
||||||
ItemRight,
|
ItemRight,
|
||||||
ItemUp,
|
ItemUp,
|
||||||
LocationUp,
|
LocationUp,
|
||||||
MoveToTrash,
|
|
||||||
NewFile,
|
NewFile,
|
||||||
NewFolder,
|
NewFolder,
|
||||||
Open,
|
Open,
|
||||||
|
|
@ -161,6 +161,7 @@ impl Action {
|
||||||
Action::CosmicSettingsAppearance => Message::CosmicSettings("appearance"),
|
Action::CosmicSettingsAppearance => Message::CosmicSettings("appearance"),
|
||||||
Action::CosmicSettingsDisplays => Message::CosmicSettings("displays"),
|
Action::CosmicSettingsDisplays => Message::CosmicSettings("displays"),
|
||||||
Action::CosmicSettingsWallpaper => Message::CosmicSettings("wallpaper"),
|
Action::CosmicSettingsWallpaper => Message::CosmicSettings("wallpaper"),
|
||||||
|
Action::Delete => Message::Delete(entity_opt),
|
||||||
Action::DesktopViewOptions => Message::DesktopViewOptions,
|
Action::DesktopViewOptions => Message::DesktopViewOptions,
|
||||||
Action::EditHistory => Message::ToggleContextPage(ContextPage::EditHistory),
|
Action::EditHistory => Message::ToggleContextPage(ContextPage::EditHistory),
|
||||||
Action::EditLocation => {
|
Action::EditLocation => {
|
||||||
|
|
@ -180,7 +181,6 @@ impl Action {
|
||||||
Action::ItemRight => Message::TabMessage(entity_opt, tab::Message::ItemRight),
|
Action::ItemRight => Message::TabMessage(entity_opt, tab::Message::ItemRight),
|
||||||
Action::ItemUp => Message::TabMessage(entity_opt, tab::Message::ItemUp),
|
Action::ItemUp => Message::TabMessage(entity_opt, tab::Message::ItemUp),
|
||||||
Action::LocationUp => Message::TabMessage(entity_opt, tab::Message::LocationUp),
|
Action::LocationUp => Message::TabMessage(entity_opt, tab::Message::LocationUp),
|
||||||
Action::MoveToTrash => Message::MoveToTrash(entity_opt),
|
|
||||||
Action::NewFile => Message::NewItem(entity_opt, false),
|
Action::NewFile => Message::NewItem(entity_opt, false),
|
||||||
Action::NewFolder => Message::NewItem(entity_opt, true),
|
Action::NewFolder => Message::NewItem(entity_opt, true),
|
||||||
Action::Open => Message::TabMessage(entity_opt, tab::Message::Open(None)),
|
Action::Open => Message::TabMessage(entity_opt, tab::Message::Open(None)),
|
||||||
|
|
@ -281,6 +281,7 @@ pub enum Message {
|
||||||
CosmicSettings(&'static str),
|
CosmicSettings(&'static str),
|
||||||
CursorMoved(Point),
|
CursorMoved(Point),
|
||||||
Cut(Option<Entity>),
|
Cut(Option<Entity>),
|
||||||
|
Delete(Option<Entity>),
|
||||||
DesktopConfig(DesktopConfig),
|
DesktopConfig(DesktopConfig),
|
||||||
DesktopViewOptions,
|
DesktopViewOptions,
|
||||||
DialogCancel,
|
DialogCancel,
|
||||||
|
|
@ -295,7 +296,6 @@ pub enum Message {
|
||||||
LaunchUrl(String),
|
LaunchUrl(String),
|
||||||
MaybeExit,
|
MaybeExit,
|
||||||
Modifiers(Modifiers),
|
Modifiers(Modifiers),
|
||||||
MoveToTrash(Option<Entity>),
|
|
||||||
MounterItems(MounterKey, MounterItems),
|
MounterItems(MounterKey, MounterItems),
|
||||||
MountResult(MounterKey, MounterItem, Result<bool, String>),
|
MountResult(MounterKey, MounterItem, Result<bool, String>),
|
||||||
NavBarClose(Entity),
|
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) => {
|
Message::DesktopConfig(config) => {
|
||||||
if config != self.config.desktop {
|
if config != self.config.desktop {
|
||||||
config_set!(desktop, config);
|
config_set!(desktop, config);
|
||||||
|
|
@ -2177,12 +2210,6 @@ impl Application for App {
|
||||||
Message::Modifiers(modifiers) => {
|
Message::Modifiers(modifiers) => {
|
||||||
self.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) => {
|
Message::MounterItems(mounter_key, mounter_items) => {
|
||||||
// Check for unmounted folders
|
// Check for unmounted folders
|
||||||
let mut unmounted = Vec::new();
|
let mut unmounted = Vec::new();
|
||||||
|
|
@ -2972,6 +2999,9 @@ impl Application for App {
|
||||||
self.update_tab(entity, tab_path, selection_paths),
|
self.update_tab(entity, tab_path, selection_paths),
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
tab::Command::Delete(paths) => {
|
||||||
|
self.operation(Operation::Delete { paths });
|
||||||
|
}
|
||||||
tab::Command::DropFiles(to, from) => {
|
tab::Command::DropFiles(to, from) => {
|
||||||
commands.push(self.update(Message::PasteContents(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::OpenFile(path) => self.open_file(&path),
|
||||||
tab::Command::OpenInNewTab(path) => {
|
tab::Command::OpenInNewTab(path) => {
|
||||||
commands.push(self.open_tab(Location::Path(path.clone()), false, None));
|
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) {
|
if matches!(mode, tab::Mode::App | tab::Mode::Desktop) {
|
||||||
bind!([Ctrl], Key::Character("c".into()), Copy);
|
bind!([Ctrl], Key::Character("c".into()), Copy);
|
||||||
bind!([Ctrl], Key::Character("x".into()), Cut);
|
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!([Shift], Key::Named(Named::Enter), OpenInNewWindow);
|
||||||
bind!([Ctrl], Key::Character("v".into()), Paste);
|
bind!([Ctrl], Key::Character("v".into()), Paste);
|
||||||
bind!([], Key::Named(Named::F2), Rename);
|
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!("cut"), Action::Cut).into());
|
||||||
children.push(menu_item(fl!("copy"), Action::Copy).into());
|
children.push(menu_item(fl!("copy"), Action::Copy).into());
|
||||||
// Should this simply bypass trash and remove the shortcut?
|
// 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 {
|
} else if selected > 0 {
|
||||||
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
||||||
children.push(menu_item(fl!("open"), Action::Open).into());
|
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(menu_item(fl!("add-to-sidebar"), Action::AddToSidebar).into());
|
||||||
}
|
}
|
||||||
children.push(divider::horizontal::light().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 {
|
} else {
|
||||||
//TODO: need better designs for menu with no selection
|
//TODO: need better designs for menu with no selection
|
||||||
//TODO: have things like properties but they apply to the folder?
|
//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(divider::horizontal::light().into());
|
||||||
children
|
children
|
||||||
.push(menu_item(fl!("restore-from-trash"), Action::RestoreFromTrash).into());
|
.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 {
|
} else {
|
||||||
// TODO: Nested menu
|
// TODO: Nested menu
|
||||||
children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name));
|
children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name));
|
||||||
|
|
@ -537,7 +539,15 @@ pub fn menu_bar<'a>(
|
||||||
menu::Item::Divider,
|
menu::Item::Divider,
|
||||||
menu_button_optional(fl!("add-to-sidebar"), Action::AddToSidebar, selected > 0),
|
menu_button_optional(fl!("add-to-sidebar"), Action::AddToSidebar, selected > 0),
|
||||||
menu::Item::Divider,
|
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::Divider,
|
||||||
menu::Item::Button(fl!("close-tab"), None, Action::TabClose),
|
menu::Item::Button(fl!("close-tab"), None, Action::TabClose),
|
||||||
menu::Item::Button(fl!("quit"), None, Action::WindowClose),
|
menu::Item::Button(fl!("quit"), None, Action::WindowClose),
|
||||||
|
|
|
||||||
|
|
@ -460,6 +460,10 @@ pub enum Operation {
|
||||||
Delete {
|
Delete {
|
||||||
paths: Vec<PathBuf>,
|
paths: Vec<PathBuf>,
|
||||||
},
|
},
|
||||||
|
/// Delete a path from the trash
|
||||||
|
DeleteTrash {
|
||||||
|
items: Vec<trash::TrashItem>,
|
||||||
|
},
|
||||||
/// Empty the trash
|
/// Empty the trash
|
||||||
EmptyTrash,
|
EmptyTrash,
|
||||||
/// Uncompress files
|
/// Uncompress files
|
||||||
|
|
@ -550,6 +554,9 @@ impl Operation {
|
||||||
to = fl!("trash"),
|
to = fl!("trash"),
|
||||||
progress = progress()
|
progress = progress()
|
||||||
),
|
),
|
||||||
|
Self::DeleteTrash { items } => {
|
||||||
|
fl!("deleting", items = items.len(), progress = progress())
|
||||||
|
}
|
||||||
Self::EmptyTrash => fl!("emptying-trash", progress = progress()),
|
Self::EmptyTrash => fl!("emptying-trash", progress = progress()),
|
||||||
Self::Extract {
|
Self::Extract {
|
||||||
paths,
|
paths,
|
||||||
|
|
@ -609,6 +616,7 @@ impl Operation {
|
||||||
from = paths_parent_name(paths),
|
from = paths_parent_name(paths),
|
||||||
to = fl!("trash")
|
to = fl!("trash")
|
||||||
),
|
),
|
||||||
|
Self::DeleteTrash { items } => fl!("deleted", items = items.len()),
|
||||||
Self::EmptyTrash => fl!("emptied-trash"),
|
Self::EmptyTrash => fl!("emptied-trash"),
|
||||||
Self::Extract {
|
Self::Extract {
|
||||||
paths,
|
paths,
|
||||||
|
|
@ -650,6 +658,7 @@ impl Operation {
|
||||||
Self::Compress { .. }
|
Self::Compress { .. }
|
||||||
| Self::Copy { .. }
|
| Self::Copy { .. }
|
||||||
| Self::Delete { .. }
|
| Self::Delete { .. }
|
||||||
|
| Self::DeleteTrash { .. }
|
||||||
| Self::EmptyTrash
|
| Self::EmptyTrash
|
||||||
| Self::Extract { .. }
|
| Self::Extract { .. }
|
||||||
| Self::Move { .. }
|
| Self::Move { .. }
|
||||||
|
|
@ -846,6 +855,34 @@ impl Operation {
|
||||||
}
|
}
|
||||||
Ok(OperationSelection::default())
|
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 => {
|
Self::EmptyTrash => {
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
target_os = "windows",
|
target_os = "windows",
|
||||||
|
|
|
||||||
|
|
@ -1127,12 +1127,12 @@ pub enum Command {
|
||||||
AddToSidebar(PathBuf),
|
AddToSidebar(PathBuf),
|
||||||
AutoScroll(Option<f32>),
|
AutoScroll(Option<f32>),
|
||||||
ChangeLocation(String, Location, Option<Vec<PathBuf>>),
|
ChangeLocation(String, Location, Option<Vec<PathBuf>>),
|
||||||
|
Delete(Vec<PathBuf>),
|
||||||
DropFiles(PathBuf, ClipboardPaste),
|
DropFiles(PathBuf, ClipboardPaste),
|
||||||
EmptyTrash,
|
EmptyTrash,
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
ExecEntryAction(cosmic::desktop::DesktopEntryData, usize),
|
ExecEntryAction(cosmic::desktop::DesktopEntryData, usize),
|
||||||
Iced(TaskWrapper),
|
Iced(TaskWrapper),
|
||||||
MoveToTrash(Vec<PathBuf>),
|
|
||||||
OpenFile(PathBuf),
|
OpenFile(PathBuf),
|
||||||
OpenInNewTab(PathBuf),
|
OpenInNewTab(PathBuf),
|
||||||
OpenInNewWindow(PathBuf),
|
OpenInNewWindow(PathBuf),
|
||||||
|
|
@ -3188,7 +3188,7 @@ impl Tab {
|
||||||
commands.push(Command::DropFiles(to, from))
|
commands.push(Command::DropFiles(to, from))
|
||||||
}
|
}
|
||||||
Location::Trash if matches!(from.kind, ClipboardKind::Cut) => {
|
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);
|
log::warn!("{:?} to {:?} is not supported.", from.kind, to);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue