dnd now works for files in service items
Some checks are pending
/ test (push) Waiting to run

This commit is contained in:
Chris Cochrun 2025-10-02 13:10:55 -05:00
parent f9f3f7f95f
commit 20843f1b2c
3 changed files with 86 additions and 21 deletions

View file

@ -1,4 +1,4 @@
use std::{borrow::Cow, mem::replace}; use std::{borrow::Cow, mem::replace, path::{Path, PathBuf}};
use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes}; use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes};
use miette::{IntoDiagnostic, Result, miette}; use miette::{IntoDiagnostic, Result, miette};
@ -23,18 +23,29 @@ pub enum LibraryKind {
} }
#[derive( #[derive(
Debug, Clone, PartialEq, Eq, Copy, Hash, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize,
)] )]
pub struct KindWrapper(pub (LibraryKind, i32)); pub struct KindWrapper(pub (LibraryKind, i32));
impl From<PathBuf> for LibraryKind {
fn from(value: PathBuf) -> Self {
todo!()
}
}
impl TryFrom<(Vec<u8>, String)> for KindWrapper { impl TryFrom<(Vec<u8>, String)> for KindWrapper {
type Error = miette::Error; type Error = miette::Error;
fn try_from( fn try_from(
value: (Vec<u8>, String), value: (Vec<u8>, String),
) -> std::result::Result<Self, Self::Error> { ) -> std::result::Result<Self, Self::Error> {
debug!(?value); let (data, mime) = value;
ron::de::from_bytes(&value.0).into_diagnostic() match mime.as_str() {
"application/service-item" => {
ron::de::from_bytes(&data).into_diagnostic()
}
_ => Err(miette!("Wrong mime type: {mime}"))
}
} }
} }

View file

@ -1,10 +1,11 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::ops::Deref; use std::ops::Deref;
use std::path::PathBuf;
use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes}; use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes};
use crisp::types::{Keyword, Symbol, Value}; use crisp::types::{Keyword, Symbol, Value};
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result, miette};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tracing::{debug, error}; use tracing::{debug, error};
@ -48,13 +49,8 @@ impl TryFrom<(Vec<u8>, String)> for ServiceItem {
value: (Vec<u8>, String), value: (Vec<u8>, String),
) -> std::result::Result<Self, Self::Error> { ) -> std::result::Result<Self, Self::Error> {
let (data, mime) = value; let (data, mime) = value;
match mime.as_str() { debug!(?mime);
"application/service_item" => {} ron::de::from_bytes(&data).into_diagnostic()
"text/uri-list" => {}
"x-special/gnome-copied-files" => {}
}
debug!(?value);
ron::de::from_bytes(&value.0).into_diagnostic()
} }
} }
@ -86,6 +82,29 @@ impl AsMimeTypes for ServiceItem {
} }
} }
impl TryFrom<PathBuf> for ServiceItem {
type Error = miette::Error;
fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
let ext = path
.extension()
.map(|ext| ext.to_str())
.flatten()
.ok_or(miette::miette!(
"There isn't an extension on this file"
))?;
match ext {
"png" | "jpg" | "jpeg" => {
Ok(Self::from(&Image::from(path)))
}
"mp4" | "mkv" | "webm" => {
Ok(Self::from(&Video::from(path)))
}
_ => Err(miette!("Unkown service item")),
}
}
}
impl From<&ServiceItem> for Value { impl From<&ServiceItem> for Value {
fn from(value: &ServiceItem) -> Self { fn from(value: &ServiceItem) -> Self {
match &value.kind { match &value.kind {

View file

@ -167,6 +167,7 @@ enum Message {
AddSelectServiceItem(usize), AddSelectServiceItem(usize),
HoveredServiceItem(Option<usize>), HoveredServiceItem(Option<usize>),
AddServiceItem(usize, KindWrapper), AddServiceItem(usize, KindWrapper),
AddServiceItemsFiles(usize, Vec<ServiceItem>),
RemoveServiceItem(usize), RemoveServiceItem(usize),
AddServiceItemDrop(usize), AddServiceItemDrop(usize),
AppendServiceItem(ServiceItem), AppendServiceItem(ServiceItem),
@ -1231,6 +1232,13 @@ impl cosmic::Application for App {
self.presenter.update_items(self.service.clone()); self.presenter.update_items(self.service.clone());
Task::none() Task::none()
} }
Message::AddServiceItemsFiles(index, items) => {
for item in items {
self.service.insert(index, item);
}
self.presenter.update_items(self.service.clone());
Task::none()
}
Message::RemoveServiceItem(index) => { Message::RemoveServiceItem(index) => {
self.service.remove(index); self.service.remove(index);
self.presenter.update_items(self.service.clone()); self.presenter.update_items(self.service.clone());
@ -1786,9 +1794,11 @@ where
.gap(cosmic::theme::spacing().space_xs); .gap(cosmic::theme::spacing().space_xs);
dnd_destination( dnd_destination(
tooltip, tooltip,
vec!["application/service-item".into()], vec!["application/service-item".into(), "text/uri-list".into(), "x-special/gnome-copied-files".into()],
) )
.on_finish(move |mime, data, _, _, _| { .on_finish(move |mime, data, _, _, _| {
match mime.as_str() {
"application/service-item" => {
let Ok(item) = let Ok(item) =
KindWrapper::try_from((data, mime)) KindWrapper::try_from((data, mime))
else { else {
@ -1797,6 +1807,31 @@ where
}; };
debug!(?item, index, "adding Service item"); debug!(?item, index, "adding Service item");
Message::AddServiceItem(index, item) Message::AddServiceItem(index, item)
}
"text/uri-list" => {
let Ok(text) = str::from_utf8(&data) else {
return Message::None;
};
let mut items = Vec::new();
for line in text.lines() {
let Ok(url) = url::Url::parse(line) else {
error!(?line, "problem parsing this file url");
continue;
};
let Ok(path) = url.to_file_path() else {
error!(?url, "invalid file URL");
continue;
};
let item = ServiceItem::try_from(path);
match item {
Ok(item) => items.push(item),
Err(e) => error!(?e),
}
}
Message::AddServiceItemsFiles(index, items)
}
_ => Message::None
}
}) })
.into() .into()
}); });