Add empty trash, fixes #118

This commit is contained in:
Jeremy Soller 2024-05-09 13:24:06 -06:00
parent 99c378873a
commit 1116eaa05a
No known key found for this signature in database
GPG key ID: D02FD439211AF56F
5 changed files with 91 additions and 15 deletions

View file

@ -5,7 +5,18 @@ filesystem = Filesystem
home = Home
trash = Trash
# New File/Folder Dialog
# List view
name = Name
modified = Modified
size = Size
# Dialogs
## Empty Trash Dialog
empty-trash = Empty trash
empty-trash-warning = Are you sure you want to permanently delete all the items in Trash?
## New File/Folder Dialog
create-new-file = Create new file
create-new-folder = Create new folder
file-name = File name
@ -16,7 +27,7 @@ name-hidden = Names starting with "." will be hidden.
name-invalid = Name cannot be "{$filename}".
name-no-slashes = Name cannot contain slashes.
# Open/Save Dialog
## Open/Save Dialog
cancel = Cancel
open = Open
open-file = Open file
@ -28,20 +39,15 @@ open-multiple-folders = Open multiple folders
save = Save
save-file = Save file
# Rename Dialog
## Rename Dialog
rename-file = Rename file
rename-folder = Rename folder
# Replace Dialog
## Replace Dialog
replace = Replace
replace-title = {$filename} already exists in this location.
replace-warning = Do you want to replace it with the one you are saving? Replacing it will overwrite its content.
# List view
name = Name
modified = Modified
size = Size
# Context Pages
## About

View file

@ -247,6 +247,7 @@ impl ContextPage {
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DialogPage {
EmptyTrash,
FailedOperation(u64),
NewItem {
parent: PathBuf,
@ -1034,6 +1035,9 @@ impl Application for App {
Message::DialogComplete => {
if let Some(dialog_page) = self.dialog_pages.pop_front() {
match dialog_page {
DialogPage::EmptyTrash => {
self.operation(Operation::EmptyTrash);
}
DialogPage::FailedOperation(id) => {
log::warn!("TODO: retry operation {}", id);
}
@ -1488,6 +1492,9 @@ impl Application for App {
self.rescan_tab(entity, tab_path),
]));
}
tab::Command::EmptyTrash => {
self.dialog_pages.push_back(DialogPage::EmptyTrash);
}
tab::Command::FocusButton(id) => {
commands.push(widget::button::focus(id));
}
@ -1758,6 +1765,14 @@ impl Application for App {
let cosmic_theme::Spacing { space_xxs, .. } = theme::active().cosmic().spacing;
let dialog = match dialog_page {
DialogPage::EmptyTrash => widget::dialog(fl!("empty-trash"))
.body(fl!("empty-trash-warning"))
.primary_action(
widget::button::suggested(fl!("empty-trash")).on_press(Message::DialogComplete),
)
.secondary_action(
widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel),
),
DialogPage::FailedOperation(id) => {
//TODO: try next dialog page (making sure index is used by Dialog messages)?
let (operation, err) = self.failed_operations.get(id)?;

View file

@ -618,6 +618,9 @@ impl Application for App {
tab::Command::DropFiles(_, _) => {
log::warn!("DropFiles not supported in dialog");
}
tab::Command::EmptyTrash => {
log::warn!("EmptyTrash not supported in dialog");
}
tab::Command::FocusButton(id) => {
commands.push(widget::button::focus(id));
}

View file

@ -18,6 +18,8 @@ pub enum Operation {
Delete {
paths: Vec<PathBuf>,
},
/// Empty the trash
EmptyTrash,
/// Move items
Move {
paths: Vec<PathBuf>,
@ -100,6 +102,31 @@ impl Operation {
.await;
}
}
Self::EmptyTrash => {
#[cfg(any(
target_os = "windows",
all(
unix,
not(target_os = "macos"),
not(target_os = "ios"),
not(target_os = "android")
)
))]
{
tokio::task::spawn_blocking(|| {
let items = trash::os_limited::list()?;
trash::os_limited::purge_all(items)
})
.await
.map_err(err_str)?
.map_err(err_str)?;
}
let _ = msg_tx
.lock()
.await
.send(Message::PendingProgress(id, 100.0))
.await;
}
Self::Move { paths, to } => {
let msg_tx = msg_tx.clone();
tokio::task::spawn_blocking(move || {

View file

@ -429,6 +429,7 @@ impl Location {
pub enum Command {
Action(Action),
ChangeLocation(String, Location),
EmptyTrash,
FocusButton(widget::Id),
FocusTextInput(widget::Id),
OpenFile(PathBuf),
@ -449,6 +450,7 @@ pub enum Message {
ContextMenu(Option<Point>),
Drag(Option<Rectangle>),
EditLocation(Option<Location>),
EmptyTrash,
GoNext,
GoPrevious,
ItemDown,
@ -1081,6 +1083,9 @@ impl Tab {
}
self.edit_location = edit_location;
}
Message::EmptyTrash => {
commands.push(Command::EmptyTrash);
}
Message::GoNext => {
if let Some(history_i) = self.history_i.checked_add(1) {
if let Some(location) = self.history.get(history_i) {
@ -2270,12 +2275,32 @@ impl Tab {
.on_scroll(Message::Scroll)
.width(Length::Fill)
.height(Length::Fill);
let mut tab_view = widget::container(widget::column::with_children(vec![
location_view,
scrollable.into(),
]))
.height(Length::Fill)
.width(Length::Fill);
let mut tab_column = widget::column::with_children(vec![location_view, scrollable.into()]);
if let Location::Trash = self.location {
if let Some(items) = self.items_opt() {
if !items.is_empty() {
let cosmic_theme::Spacing {
space_xxs,
space_xs,
..
} = theme::active().cosmic().spacing;
tab_column = tab_column.push(
widget::layer_container(widget::row::with_children(vec![
widget::horizontal_space(Length::Fill).into(),
widget::button::standard(fl!("empty-trash"))
.on_press(Message::EmptyTrash)
.into(),
]))
.padding([space_xxs, space_xs])
.layer(cosmic_theme::Layer::Primary),
);
}
}
}
let mut tab_view = widget::container(tab_column)
.height(Length::Fill)
.width(Length::Fill);
if self.dnd_hovered.as_ref().map(|(l, _)| l) == Some(&tab_location) {
tab_view = tab_view.style(cosmic::theme::Container::custom(|t| {