Support drag-and-drop for files
Insert file paths when files are dropped in terminal from a file manager.
This commit is contained in:
parent
fbb69fd399
commit
059dd5fa97
4 changed files with 86 additions and 3 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1237,6 +1237,7 @@ dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
"smol_str",
|
"smol_str",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"url",
|
||||||
"vergen",
|
"vergen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ i18n-embed-fl = "0.7"
|
||||||
icu_collator = "1.5"
|
icu_collator = "1.5"
|
||||||
icu_provider = { version = "1.5", features = ["sync"] }
|
icu_provider = { version = "1.5", features = ["sync"] }
|
||||||
rust-embed = "8"
|
rust-embed = "8"
|
||||||
|
url = "2.5"
|
||||||
|
|
||||||
[dependencies.cosmic-files]
|
[dependencies.cosmic-files]
|
||||||
git = "https://github.com/pop-os/cosmic-files.git"
|
git = "https://github.com/pop-os/cosmic-files.git"
|
||||||
|
|
|
||||||
51
src/dnd.rs
Normal file
51
src/dnd.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
use cosmic::iced::clipboard::mime::AllowedMimeTypes;
|
||||||
|
use std::{borrow::Cow, error::Error, path::PathBuf, str};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct DndDrop {
|
||||||
|
pub paths: Vec<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AllowedMimeTypes for DndDrop {
|
||||||
|
fn allowed() -> Cow<'static, [String]> {
|
||||||
|
Cow::from(vec![
|
||||||
|
"x-special/gnome-copied-files".to_string(),
|
||||||
|
"text/uri-list".to_string(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<(Vec<u8>, String)> for DndDrop {
|
||||||
|
type Error = Box<dyn Error>;
|
||||||
|
fn try_from(value: (Vec<u8>, String)) -> Result<Self, Self::Error> {
|
||||||
|
let (data, mime) = value;
|
||||||
|
let mut paths = Vec::new();
|
||||||
|
match mime.as_str() {
|
||||||
|
"text/uri-list" => {
|
||||||
|
let text = str::from_utf8(&data)?;
|
||||||
|
for line in text.lines() {
|
||||||
|
let url = Url::parse(line)?;
|
||||||
|
match url.to_file_path() {
|
||||||
|
Ok(path) => paths.push(path),
|
||||||
|
Err(()) => Err(format!("invalid file URL {:?}", url))?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"x-special/gnome-copied-files" => {
|
||||||
|
let text = str::from_utf8(&data)?;
|
||||||
|
for (i, line) in text.lines().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
let url = Url::parse(line)?;
|
||||||
|
match url.to_file_path() {
|
||||||
|
Ok(path) => paths.push(path),
|
||||||
|
Err(()) => Err(format!("invalid file URL {:?}", url))?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(format!("unsupported mime type {:?}", mime))?,
|
||||||
|
}
|
||||||
|
Ok(Self { paths })
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/main.rs
34
src/main.rs
|
|
@ -2,10 +2,13 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use alacritty_terminal::{event::Event as TermEvent, term, term::color::Colors as TermColors, tty};
|
use alacritty_terminal::{event::Event as TermEvent, term, term::color::Colors as TermColors, tty};
|
||||||
|
use cosmic::iced::clipboard::dnd::DndAction;
|
||||||
use cosmic::widget::menu::action::MenuAction;
|
use cosmic::widget::menu::action::MenuAction;
|
||||||
use cosmic::widget::menu::key_bind::KeyBind;
|
use cosmic::widget::menu::key_bind::KeyBind;
|
||||||
|
use cosmic::widget::DndDestination;
|
||||||
|
use cosmic::Apply;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
app::{message, Command, Core, Settings},
|
app::{command, message, Command, Core, Settings},
|
||||||
cosmic_config::{self, ConfigSet, CosmicConfigEntry},
|
cosmic_config::{self, ConfigSet, CosmicConfigEntry},
|
||||||
cosmic_theme, executor,
|
cosmic_theme, executor,
|
||||||
iced::{
|
iced::{
|
||||||
|
|
@ -56,10 +59,14 @@ use terminal::{Terminal, TerminalPaneGrid, TerminalScroll};
|
||||||
mod terminal;
|
mod terminal;
|
||||||
|
|
||||||
use terminal_box::terminal_box;
|
use terminal_box::terminal_box;
|
||||||
|
|
||||||
|
use crate::dnd::DndDrop;
|
||||||
mod terminal_box;
|
mod terminal_box;
|
||||||
|
|
||||||
mod terminal_theme;
|
mod terminal_theme;
|
||||||
|
|
||||||
|
mod dnd;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
static ref ICON_CACHE: Mutex<IconCache> = Mutex::new(IconCache::new());
|
static ref ICON_CACHE: Mutex<IconCache> = Mutex::new(IconCache::new());
|
||||||
}
|
}
|
||||||
|
|
@ -295,6 +302,7 @@ pub enum Message {
|
||||||
DefaultFontWeight(usize),
|
DefaultFontWeight(usize),
|
||||||
DefaultZoomStep(usize),
|
DefaultZoomStep(usize),
|
||||||
DialogMessage(DialogMessage),
|
DialogMessage(DialogMessage),
|
||||||
|
Drop(Option<(pane_grid::Pane, segmented_button::Entity, DndDrop)>),
|
||||||
Find(bool),
|
Find(bool),
|
||||||
FindNext,
|
FindNext,
|
||||||
FindPrevious,
|
FindPrevious,
|
||||||
|
|
@ -1894,6 +1902,16 @@ impl Application for App {
|
||||||
return dialog.update(dialog_message);
|
return dialog.update(dialog_message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Message::Drop(Some((pane, entity, data))) => {
|
||||||
|
self.pane_model.focus = pane;
|
||||||
|
if let Ok(value) = shlex::try_join(data.paths.iter().filter_map(|p| p.to_str())) {
|
||||||
|
return Command::batch([
|
||||||
|
self.update_focus(),
|
||||||
|
command::message::app(Message::PasteValue(Some(entity), value)),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::Drop(None) => {}
|
||||||
Message::Find(find) => {
|
Message::Find(find) => {
|
||||||
self.find = find;
|
self.find = find;
|
||||||
if find {
|
if find {
|
||||||
|
|
@ -2673,7 +2691,19 @@ impl Application for App {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
pane_grid::Content::new(tab_column)
|
DndDestination::for_data::<DndDrop>(tab_column, move |data, action| {
|
||||||
|
if let Some(data) = data {
|
||||||
|
if action == DndAction::Move {
|
||||||
|
Message::Drop(Some((pane, entity, data)))
|
||||||
|
} else {
|
||||||
|
log::warn!("unsuppported action: {:?}", action);
|
||||||
|
Message::Drop(None)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Message::Drop(None)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.apply(pane_grid::Content::new)
|
||||||
})
|
})
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue