feat(dnd_destination): xdg file transfer portal support
Requires the `xdg-portal` feature to be enabled to use these features. - Adds `DndDestination::on_file_transfer` method to handle `application/vnd.portal.filetransfer` drop requests - Adds `command::file_transfer_receive` function to handle the file transfer request messages - Adds `command::file_transfer_send` to initiate a file transfer from the application
This commit is contained in:
parent
ae1f15f37e
commit
21c5a4f34a
2 changed files with 55 additions and 0 deletions
|
|
@ -1,6 +1,9 @@
|
|||
// Copyright 2023 System76 <info@system76.com>
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#[cfg(feature = "xdg-portal")]
|
||||
use std::os::fd::AsFd;
|
||||
|
||||
use iced::window;
|
||||
|
||||
/// Initiates a window drag.
|
||||
|
|
@ -43,3 +46,24 @@ pub fn set_windowed<M>(id: window::Id) -> iced::Task<crate::Action<M>> {
|
|||
pub fn toggle_maximize<M>(id: window::Id) -> iced::Task<crate::Action<M>> {
|
||||
iced_runtime::window::toggle_maximize(id)
|
||||
}
|
||||
|
||||
#[cfg(feature = "xdg-portal")]
|
||||
pub fn file_transfer_send(writeable: bool, auto_stop: bool, files: Vec<impl AsFd + Send + Sync + 'static>) -> iced::Task<ashpd::Result<String>> {
|
||||
iced::Task::future(async move {
|
||||
let file_transfer = ashpd::documents::FileTransfer::new().await?;
|
||||
let key = file_transfer.start_transfer(writeable, auto_stop).await?;
|
||||
file_transfer.add_files(&key, &files).await?;
|
||||
Ok(key)
|
||||
})
|
||||
}
|
||||
|
||||
/// Receive the files offered over the xdg share portal using the `key`.
|
||||
/// Returns a list of file paths.
|
||||
#[cfg(feature = "xdg-portal")]
|
||||
pub fn file_transfer_receive(key: String) -> iced::Task<ashpd::Result<Vec<String>>> {
|
||||
dbg!(&key);
|
||||
iced::Task::future(async move {
|
||||
let file_transfer = ashpd::documents::FileTransfer::new().await?;
|
||||
file_transfer.retrieve_files(&key).await
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ pub fn dnd_destination_for_data<'a, T: AllowedMimeTypes, Message: 'static>(
|
|||
|
||||
static DRAG_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
const DND_DEST_LOG_TARGET: &str = "libcosmic::widget::dnd_destination";
|
||||
#[cfg(feature = "xdg-portal")]
|
||||
pub const FILE_TRANSFER_MIME: &str = "application/vnd.portal.filetransfer";
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct DragId(pub u128);
|
||||
|
|
@ -73,6 +75,8 @@ pub struct DndDestination<'a, Message> {
|
|||
on_action_selected: Option<Box<dyn Fn(DndAction) -> Message>>,
|
||||
on_data_received: Option<Box<dyn Fn(String, Vec<u8>) -> Message>>,
|
||||
on_finish: Option<Box<dyn Fn(String, Vec<u8>, DndAction, f64, f64) -> Message>>,
|
||||
#[cfg(feature = "xdg-portal")]
|
||||
on_file_transfer: Option<Box<dyn Fn(String) -> Message>>,
|
||||
}
|
||||
|
||||
impl<'a, Message: 'static> DndDestination<'a, Message> {
|
||||
|
|
@ -99,6 +103,8 @@ impl<'a, Message: 'static> DndDestination<'a, Message> {
|
|||
on_action_selected: None,
|
||||
on_data_received: None,
|
||||
on_finish: None,
|
||||
#[cfg(feature = "xdg-portal")]
|
||||
on_file_transfer: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +130,8 @@ impl<'a, Message: 'static> DndDestination<'a, Message> {
|
|||
on_finish: Some(Box::new(move |mime, data, action, _, _| {
|
||||
on_finish(T::try_from((data, mime)).ok(), action)
|
||||
})),
|
||||
#[cfg(feature = "xdg-portal")]
|
||||
on_file_transfer: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -159,6 +167,8 @@ impl<'a, Message: 'static> DndDestination<'a, Message> {
|
|||
on_action_selected: None,
|
||||
on_data_received: None,
|
||||
on_finish: None,
|
||||
#[cfg(feature = "xdg-portal")]
|
||||
on_file_transfer: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -237,6 +247,20 @@ impl<'a, Message: 'static> DndDestination<'a, Message> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Add a message that will be emitted instead of [`on_data_received`](Self::on_data_received) if the dropped files
|
||||
/// are offered through the xdg share portal. You can then use [`crate::command::file_transfer_receive`]
|
||||
/// with the key to receive the files.
|
||||
#[cfg(feature = "xdg-portal")]
|
||||
#[must_use]
|
||||
pub fn on_file_transfer(mut self, f: impl Fn(String) -> Message + 'static) -> Self {
|
||||
match self.mime_types.iter().position(|v| v == "text/uri-list") {
|
||||
Some(i) => self.mime_types.insert(i, Cow::Borrowed(FILE_TRANSFER_MIME)),
|
||||
None => self.mime_types.push(Cow::Borrowed(FILE_TRANSFER_MIME)),
|
||||
}
|
||||
self.on_file_transfer = Some(Box::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the drag id of the destination.
|
||||
///
|
||||
/// # Panics
|
||||
|
|
@ -496,6 +520,13 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
|
|||
"offer data id={my_id:?} mime={mime_type:?} bytes={}",
|
||||
data.len()
|
||||
);
|
||||
|
||||
#[cfg(feature = "xdg-portal")]
|
||||
if mime_type == FILE_TRANSFER_MIME && let Some(f) = self.on_file_transfer.as_ref() && let Ok(s) = String::from_utf8(data[..data.len() - 1].to_vec()) {
|
||||
shell.publish(f(s));
|
||||
return event::Status::Captured;
|
||||
}
|
||||
|
||||
if let (Some(msg), ret) = state.on_data_received(
|
||||
mime_type,
|
||||
data,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue