fix: add eject button for context menus on mount point
This commit is contained in:
parent
aba90279e6
commit
6a2bd1faf1
6 changed files with 90 additions and 16 deletions
|
|
@ -291,6 +291,7 @@ type-to-search-enter-path = Enters the path to the directory or file
|
||||||
add-to-sidebar = Add to sidebar
|
add-to-sidebar = Add to sidebar
|
||||||
compress = Compress
|
compress = Compress
|
||||||
delete-permanently = Delete permanently
|
delete-permanently = Delete permanently
|
||||||
|
eject = Eject
|
||||||
extract-here = Extract
|
extract-here = Extract
|
||||||
new-file = New file...
|
new-file = New file...
|
||||||
new-folder = New folder...
|
new-folder = New folder...
|
||||||
|
|
|
||||||
58
src/app.rs
58
src/app.rs
|
|
@ -117,6 +117,7 @@ pub enum Action {
|
||||||
Delete,
|
Delete,
|
||||||
EditHistory,
|
EditHistory,
|
||||||
EditLocation,
|
EditLocation,
|
||||||
|
Eject,
|
||||||
EmptyTrash,
|
EmptyTrash,
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
ExecEntryAction(usize),
|
ExecEntryAction(usize),
|
||||||
|
|
@ -184,6 +185,7 @@ impl Action {
|
||||||
Action::EditLocation => {
|
Action::EditLocation => {
|
||||||
Message::TabMessage(entity_opt, tab::Message::EditLocationEnable)
|
Message::TabMessage(entity_opt, tab::Message::EditLocationEnable)
|
||||||
}
|
}
|
||||||
|
Action::Eject => Message::Eject,
|
||||||
Action::EmptyTrash => Message::TabMessage(None, tab::Message::EmptyTrash),
|
Action::EmptyTrash => Message::TabMessage(None, tab::Message::EmptyTrash),
|
||||||
Action::ExtractHere => Message::ExtractHere(entity_opt),
|
Action::ExtractHere => Message::ExtractHere(entity_opt),
|
||||||
Action::ExtractTo => Message::ExtractTo(entity_opt),
|
Action::ExtractTo => Message::ExtractTo(entity_opt),
|
||||||
|
|
@ -304,6 +306,7 @@ pub enum Message {
|
||||||
DesktopViewOptions,
|
DesktopViewOptions,
|
||||||
DialogCancel,
|
DialogCancel,
|
||||||
DialogComplete,
|
DialogComplete,
|
||||||
|
Eject,
|
||||||
FileDialogMessage(DialogMessage),
|
FileDialogMessage(DialogMessage),
|
||||||
DialogPush(DialogPage),
|
DialogPush(DialogPage),
|
||||||
DialogUpdate(DialogPage),
|
DialogUpdate(DialogPage),
|
||||||
|
|
@ -1044,17 +1047,36 @@ impl App {
|
||||||
) -> Task<Message> {
|
) -> Task<Message> {
|
||||||
log::info!("rescan_tab {entity:?} {location:?} {selection_paths:?}");
|
log::info!("rescan_tab {entity:?} {location:?} {selection_paths:?}");
|
||||||
let icon_sizes = self.config.tab.icon_sizes;
|
let icon_sizes = self.config.tab.icon_sizes;
|
||||||
|
let mounter_items = self.mounter_items.clone();
|
||||||
|
|
||||||
Task::perform(
|
Task::perform(
|
||||||
async move {
|
async move {
|
||||||
let location2 = location.clone();
|
let location2 = location.clone();
|
||||||
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
|
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
|
||||||
Ok((parent_item_opt, items)) => cosmic::action::app(Message::TabRescan(
|
Ok((parent_item_opt, mut items)) => {
|
||||||
entity,
|
#[cfg(feature = "gvfs")]
|
||||||
location,
|
{
|
||||||
parent_item_opt,
|
let mounter_paths: Vec<_> = mounter_items
|
||||||
items,
|
.iter()
|
||||||
selection_paths,
|
.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) => {
|
Err(err) => {
|
||||||
log::warn!("failed to rescan: {}", err);
|
log::warn!("failed to rescan: {}", err);
|
||||||
cosmic::action::none()
|
cosmic::action::none()
|
||||||
|
|
@ -4136,6 +4158,28 @@ impl Application for App {
|
||||||
self.size = Some(size);
|
self.size = Some(size);
|
||||||
self.handle_overlap();
|
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"))]
|
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
|
||||||
Message::Focused(id) => {
|
Message::Focused(id) => {
|
||||||
if let Some(w) = self.windows.get(&id) {
|
if let Some(w) = self.windows.get(&id) {
|
||||||
|
|
|
||||||
|
|
@ -650,11 +650,26 @@ impl App {
|
||||||
fn rescan_tab(&self) -> Task<Message> {
|
fn rescan_tab(&self) -> Task<Message> {
|
||||||
let location = self.tab.location.clone();
|
let location = self.tab.location.clone();
|
||||||
let icon_sizes = self.tab.config.icon_sizes;
|
let icon_sizes = self.tab.config.icon_sizes;
|
||||||
|
let mounter_items = self.mounter_items.clone();
|
||||||
Task::perform(
|
Task::perform(
|
||||||
async move {
|
async move {
|
||||||
let location2 = location.clone();
|
let location2 = location.clone();
|
||||||
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
|
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))
|
cosmic::action::app(Message::TabRescan(location, parent_item_opt, items))
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|
|
||||||
24
src/menu.rs
24
src/menu.rs
|
|
@ -108,11 +108,13 @@ pub fn context_menu<'a>(
|
||||||
let mut selected_trash_only = false;
|
let mut selected_trash_only = false;
|
||||||
let mut selected_desktop_entry = None;
|
let mut selected_desktop_entry = None;
|
||||||
let mut selected_types: Vec<Mime> = vec![];
|
let mut selected_types: Vec<Mime> = vec![];
|
||||||
|
let mut selected_mount_point = 0;
|
||||||
if let Some(items) = tab.items_opt() {
|
if let Some(items) = tab.items_opt() {
|
||||||
for item in items.iter() {
|
for item in items.iter() {
|
||||||
if item.selected {
|
if item.selected {
|
||||||
selected += 1;
|
selected += 1;
|
||||||
if item.metadata.is_dir() {
|
if item.metadata.is_dir() {
|
||||||
|
selected_mount_point += item.is_mount_point as i32;
|
||||||
selected_dir += 1;
|
selected_dir += 1;
|
||||||
}
|
}
|
||||||
match &item.location_opt {
|
match &item.location_opt {
|
||||||
|
|
@ -194,8 +196,10 @@ pub fn context_menu<'a>(
|
||||||
.push(menu_item(fl!("open-in-new-window"), Action::OpenInNewWindow).into());
|
.push(menu_item(fl!("open-in-new-window"), Action::OpenInNewWindow).into());
|
||||||
}
|
}
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(divider::horizontal::light().into());
|
||||||
children.push(menu_item(fl!("rename"), Action::Rename).into());
|
if selected_mount_point == 0 {
|
||||||
children.push(menu_item(fl!("cut"), Action::Cut).into());
|
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(menu_item(fl!("copy"), Action::Copy).into());
|
||||||
|
|
||||||
children.push(divider::horizontal::light().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(menu_item(fl!("add-to-sidebar"), Action::AddToSidebar).into());
|
||||||
}
|
}
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(divider::horizontal::light().into());
|
||||||
if modifiers.shift() && !modifiers.control() {
|
if selected_mount_point == 0 {
|
||||||
children.push(
|
if modifiers.shift() && !modifiers.control() {
|
||||||
menu_item(fl!("delete-permanently"), Action::PermanentlyDelete).into(),
|
children.push(
|
||||||
);
|
menu_item(fl!("delete-permanently"), Action::PermanentlyDelete).into(),
|
||||||
} else {
|
);
|
||||||
children.push(menu_item(fl!("move-to-trash"), Action::Delete).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 {
|
} else {
|
||||||
//TODO: need better designs for menu with no selection
|
//TODO: need better designs for menu with no selection
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
|
||||||
|
|
||||||
items.push(tab::Item {
|
items.push(tab::Item {
|
||||||
name,
|
name,
|
||||||
|
is_mount_point: false,
|
||||||
display_name,
|
display_name,
|
||||||
metadata,
|
metadata,
|
||||||
hidden: false,
|
hidden: false,
|
||||||
|
|
|
||||||
|
|
@ -668,6 +668,7 @@ pub fn item_from_gvfs_info(path: PathBuf, file_info: gio::FileInfo, sizes: IconS
|
||||||
Item {
|
Item {
|
||||||
name: file_name.clone().to_string(),
|
name: file_name.clone().to_string(),
|
||||||
display_name,
|
display_name,
|
||||||
|
is_mount_point: false,
|
||||||
metadata: ItemMetadata::GvfsPath {
|
metadata: ItemMetadata::GvfsPath {
|
||||||
mtime,
|
mtime,
|
||||||
size_opt,
|
size_opt,
|
||||||
|
|
@ -809,6 +810,7 @@ pub fn item_from_entry(
|
||||||
Item {
|
Item {
|
||||||
name,
|
name,
|
||||||
display_name,
|
display_name,
|
||||||
|
is_mount_point: false,
|
||||||
metadata: ItemMetadata::Path {
|
metadata: ItemMetadata::Path {
|
||||||
metadata,
|
metadata,
|
||||||
children_opt,
|
children_opt,
|
||||||
|
|
@ -1090,6 +1092,7 @@ pub fn scan_trash(sizes: IconSizes) -> Vec<Item> {
|
||||||
items.push(Item {
|
items.push(Item {
|
||||||
name,
|
name,
|
||||||
display_name,
|
display_name,
|
||||||
|
is_mount_point: false,
|
||||||
metadata: ItemMetadata::Trash { metadata, entry },
|
metadata: ItemMetadata::Trash { metadata, entry },
|
||||||
hidden: false,
|
hidden: false,
|
||||||
location_opt: None,
|
location_opt: None,
|
||||||
|
|
@ -1270,6 +1273,7 @@ pub fn scan_desktop(
|
||||||
items.push(Item {
|
items.push(Item {
|
||||||
name,
|
name,
|
||||||
display_name,
|
display_name,
|
||||||
|
is_mount_point: false,
|
||||||
metadata,
|
metadata,
|
||||||
hidden: false,
|
hidden: false,
|
||||||
location_opt: Some(Location::Trash),
|
location_opt: Some(Location::Trash),
|
||||||
|
|
@ -1821,6 +1825,7 @@ impl ItemThumbnail {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub is_mount_point: bool,
|
||||||
pub display_name: String,
|
pub display_name: String,
|
||||||
pub metadata: ItemMetadata,
|
pub metadata: ItemMetadata,
|
||||||
pub hidden: bool,
|
pub hidden: bool,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue