fix: add eject button for context menus on mount point

This commit is contained in:
Ashley Wulber 2025-06-30 19:23:40 -04:00 committed by Jeremy Soller
parent aba90279e6
commit 6a2bd1faf1
6 changed files with 90 additions and 16 deletions

View file

@ -291,6 +291,7 @@ type-to-search-enter-path = Enters the path to the directory or file
add-to-sidebar = Add to sidebar
compress = Compress
delete-permanently = Delete permanently
eject = Eject
extract-here = Extract
new-file = New file...
new-folder = New folder...

View file

@ -117,6 +117,7 @@ pub enum Action {
Delete,
EditHistory,
EditLocation,
Eject,
EmptyTrash,
#[cfg(feature = "desktop")]
ExecEntryAction(usize),
@ -184,6 +185,7 @@ impl Action {
Action::EditLocation => {
Message::TabMessage(entity_opt, tab::Message::EditLocationEnable)
}
Action::Eject => Message::Eject,
Action::EmptyTrash => Message::TabMessage(None, tab::Message::EmptyTrash),
Action::ExtractHere => Message::ExtractHere(entity_opt),
Action::ExtractTo => Message::ExtractTo(entity_opt),
@ -304,6 +306,7 @@ pub enum Message {
DesktopViewOptions,
DialogCancel,
DialogComplete,
Eject,
FileDialogMessage(DialogMessage),
DialogPush(DialogPage),
DialogUpdate(DialogPage),
@ -1044,17 +1047,36 @@ impl App {
) -> Task<Message> {
log::info!("rescan_tab {entity:?} {location:?} {selection_paths:?}");
let icon_sizes = self.config.tab.icon_sizes;
let mounter_items = self.mounter_items.clone();
Task::perform(
async move {
let location2 = location.clone();
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
Ok((parent_item_opt, items)) => cosmic::action::app(Message::TabRescan(
entity,
location,
parent_item_opt,
items,
selection_paths,
)),
Ok((parent_item_opt, mut items)) => {
#[cfg(feature = "gvfs")]
{
let mounter_paths: Vec<_> = mounter_items
.iter()
.flat_map(|item| item.1.iter())
.filter_map(|item| item.path())
.collect();
if !mounter_paths.is_empty() {
for item in &mut items {
item.is_mount_point =
item.path_opt().is_some_and(|p| mounter_paths.contains(p));
}
}
}
cosmic::action::app(Message::TabRescan(
entity,
location,
parent_item_opt,
items,
selection_paths,
))
}
Err(err) => {
log::warn!("failed to rescan: {}", err);
cosmic::action::none()
@ -4136,6 +4158,28 @@ impl Application for App {
self.size = Some(size);
self.handle_overlap();
}
Message::Eject => {
#[cfg(feature = "gvfs")]
{
let paths = self.selected_paths(None);
if let Some(p) = paths.first() {
{
for (k, mounter_items) in &self.mounter_items {
if let Some(mounter) = MOUNTERS.get(&k) {
if let Some(item) = mounter_items
.iter()
.find(|item| item.path().is_some_and(|path| path == *p))
{
return mounter
.unmount(item.clone())
.map(|_| cosmic::action::none());
}
}
}
}
}
}
}
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
Message::Focused(id) => {
if let Some(w) = self.windows.get(&id) {

View file

@ -650,11 +650,26 @@ impl App {
fn rescan_tab(&self) -> Task<Message> {
let location = self.tab.location.clone();
let icon_sizes = self.tab.config.icon_sizes;
let mounter_items = self.mounter_items.clone();
Task::perform(
async move {
let location2 = location.clone();
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
Ok((parent_item_opt, items)) => {
Ok((parent_item_opt, mut items)) => {
#[cfg(feature = "gvfs")]
{
let mounter_paths: Vec<_> = mounter_items
.iter()
.flat_map(|item| item.1.iter())
.filter_map(|item| item.path())
.collect();
if !mounter_paths.is_empty() {
for item in &mut items {
item.is_mount_point =
item.path_opt().is_some_and(|p| mounter_paths.contains(p));
}
}
}
cosmic::action::app(Message::TabRescan(location, parent_item_opt, items))
}
Err(err) => {

View file

@ -108,11 +108,13 @@ pub fn context_menu<'a>(
let mut selected_trash_only = false;
let mut selected_desktop_entry = None;
let mut selected_types: Vec<Mime> = vec![];
let mut selected_mount_point = 0;
if let Some(items) = tab.items_opt() {
for item in items.iter() {
if item.selected {
selected += 1;
if item.metadata.is_dir() {
selected_mount_point += item.is_mount_point as i32;
selected_dir += 1;
}
match &item.location_opt {
@ -194,8 +196,10 @@ pub fn context_menu<'a>(
.push(menu_item(fl!("open-in-new-window"), Action::OpenInNewWindow).into());
}
children.push(divider::horizontal::light().into());
children.push(menu_item(fl!("rename"), Action::Rename).into());
children.push(menu_item(fl!("cut"), Action::Cut).into());
if selected_mount_point == 0 {
children.push(menu_item(fl!("rename"), Action::Rename).into());
children.push(menu_item(fl!("cut"), Action::Cut).into());
}
children.push(menu_item(fl!("copy"), Action::Copy).into());
children.push(divider::horizontal::light().into());
@ -235,12 +239,16 @@ pub fn context_menu<'a>(
children.push(menu_item(fl!("add-to-sidebar"), Action::AddToSidebar).into());
}
children.push(divider::horizontal::light().into());
if modifiers.shift() && !modifiers.control() {
children.push(
menu_item(fl!("delete-permanently"), Action::PermanentlyDelete).into(),
);
} else {
children.push(menu_item(fl!("move-to-trash"), Action::Delete).into());
if selected_mount_point == 0 {
if modifiers.shift() && !modifiers.control() {
children.push(
menu_item(fl!("delete-permanently"), Action::PermanentlyDelete).into(),
);
} else {
children.push(menu_item(fl!("move-to-trash"), Action::Delete).into());
}
} else if selected == 1 {
children.push(menu_item(fl!("eject"), Action::Eject).into());
}
} else {
//TODO: need better designs for menu with no selection

View file

@ -109,6 +109,7 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
items.push(tab::Item {
name,
is_mount_point: false,
display_name,
metadata,
hidden: false,

View file

@ -668,6 +668,7 @@ pub fn item_from_gvfs_info(path: PathBuf, file_info: gio::FileInfo, sizes: IconS
Item {
name: file_name.clone().to_string(),
display_name,
is_mount_point: false,
metadata: ItemMetadata::GvfsPath {
mtime,
size_opt,
@ -809,6 +810,7 @@ pub fn item_from_entry(
Item {
name,
display_name,
is_mount_point: false,
metadata: ItemMetadata::Path {
metadata,
children_opt,
@ -1090,6 +1092,7 @@ pub fn scan_trash(sizes: IconSizes) -> Vec<Item> {
items.push(Item {
name,
display_name,
is_mount_point: false,
metadata: ItemMetadata::Trash { metadata, entry },
hidden: false,
location_opt: None,
@ -1270,6 +1273,7 @@ pub fn scan_desktop(
items.push(Item {
name,
display_name,
is_mount_point: false,
metadata,
hidden: false,
location_opt: Some(Location::Trash),
@ -1821,6 +1825,7 @@ impl ItemThumbnail {
#[derive(Clone, Debug)]
pub struct Item {
pub name: String,
pub is_mount_point: bool,
pub display_name: String,
pub metadata: ItemMetadata,
pub hidden: bool,