Implement automatic preview, part of #109

This commit is contained in:
Jeremy Soller 2024-09-20 14:13:04 -06:00
parent 55eb21911e
commit f4fd98cc23
No known key found for this signature in database
GPG key ID: D02FD439211AF56F
4 changed files with 167 additions and 149 deletions

View file

@ -112,7 +112,7 @@ pub enum Action {
OpenTerminal, OpenTerminal,
OpenWith, OpenWith,
Paste, Paste,
Properties, Preview,
Rename, Rename,
RestoreFromTrash, RestoreFromTrash,
SearchActivate, SearchActivate,
@ -164,7 +164,9 @@ impl Action {
Action::OpenTerminal => Message::OpenTerminal(entity_opt), Action::OpenTerminal => Message::OpenTerminal(entity_opt),
Action::OpenWith => Message::ToggleContextPage(ContextPage::OpenWith), Action::OpenWith => Message::ToggleContextPage(ContextPage::OpenWith),
Action::Paste => Message::Paste(entity_opt), Action::Paste => Message::Paste(entity_opt),
Action::Properties => Message::ToggleContextPage(ContextPage::Properties(None)), Action::Preview => {
Message::ToggleContextPage(ContextPage::Preview(entity_opt, PreviewKind::Selected))
}
Action::Rename => Message::Rename(entity_opt), Action::Rename => Message::Rename(entity_opt),
Action::RestoreFromTrash => Message::RestoreFromTrash(entity_opt), Action::RestoreFromTrash => Message::RestoreFromTrash(entity_opt),
Action::SearchActivate => Message::SearchActivate, Action::SearchActivate => Message::SearchActivate,
@ -210,18 +212,29 @@ impl MenuAction for Action {
} }
} }
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Debug)]
pub enum ContextItem { pub struct PreviewItem(pub tab::Item);
NavBar(segmented_button::Entity),
TabBar(segmented_button::Entity), impl PartialEq for PreviewItem {
BreadCrumbs(usize), fn eq(&self, other: &Self) -> bool {
self.0.location_opt == other.0.location_opt
}
}
impl Eq for PreviewItem {}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum PreviewKind {
Custom(PreviewItem),
Location(Location),
Selected,
} }
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum NavMenuAction { pub enum NavMenuAction {
OpenInNewTab(segmented_button::Entity), OpenInNewTab(segmented_button::Entity),
OpenInNewWindow(segmented_button::Entity), OpenInNewWindow(segmented_button::Entity),
Properties(segmented_button::Entity), Preview(segmented_button::Entity),
RemoveFromSidebar(segmented_button::Entity), RemoveFromSidebar(segmented_button::Entity),
EmptyTrash, EmptyTrash,
} }
@ -279,6 +292,7 @@ pub enum Message {
PendingComplete(u64), PendingComplete(u64),
PendingError(u64, String), PendingError(u64, String),
PendingProgress(u64, f32), PendingProgress(u64, f32),
Preview(Entity, PreviewKind, time::Duration),
RescanTrash, RescanTrash,
Rename(Option<Entity>), Rename(Option<Entity>),
ReplaceResult(ReplaceResult), ReplaceResult(ReplaceResult),
@ -317,13 +331,13 @@ pub enum Message {
None, None,
} }
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum ContextPage { pub enum ContextPage {
About, About,
EditHistory, EditHistory,
NetworkDrive, NetworkDrive,
OpenWith, OpenWith,
Properties(Option<ContextItem>), Preview(Option<Entity>, PreviewKind),
Settings, Settings,
} }
@ -334,7 +348,7 @@ impl ContextPage {
Self::EditHistory => fl!("edit-history"), Self::EditHistory => fl!("edit-history"),
Self::NetworkDrive => fl!("add-network-drive"), Self::NetworkDrive => fl!("add-network-drive"),
Self::OpenWith => fl!("open-with"), Self::OpenWith => fl!("open-with"),
Self::Properties(..) => String::default(), Self::Preview(..) => String::default(),
Self::Settings => fl!("settings"), Self::Settings => fl!("settings"),
} }
} }
@ -461,6 +475,7 @@ pub struct App {
pending_operations: BTreeMap<u64, (Operation, f32)>, pending_operations: BTreeMap<u64, (Operation, f32)>,
complete_operations: BTreeMap<u64, Operation>, complete_operations: BTreeMap<u64, Operation>,
failed_operations: BTreeMap<u64, (Operation, String)>, failed_operations: BTreeMap<u64, (Operation, String)>,
preview_opt: Option<(Entity, PreviewKind, time::Instant)>,
search_active: bool, search_active: bool,
search_id: widget::Id, search_id: widget::Id,
search_input: String, search_input: String,
@ -949,59 +964,42 @@ impl App {
widget::settings::view_column(children).into() widget::settings::view_column(children).into()
} }
fn properties(&self, entity: Option<ContextItem>) -> Element<Message> { fn preview(&self, entity_opt: &Option<Entity>, kind: &PreviewKind) -> Element<Message> {
match entity { let mut children = Vec::with_capacity(1);
None => self.tab_properties(self.tab_model.active()), let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
Some(ContextItem::TabBar(entity)) => self.tab_properties(entity), match kind {
Some(ContextItem::NavBar(item)) => { PreviewKind::Custom(PreviewItem(item)) => {
let mut children = Vec::with_capacity(1); children.push(item.property_view(IconSizes::default()));
if let Some(location) = self.nav_model.data::<Location>(item) { }
if let Location::Path(path) = location { PreviewKind::Location(location) => {
//TODO: this should be done once, not when generating the view! if let Some(tab) = self.tab_model.data::<Tab>(entity) {
if let Ok(item) = tab::item_from_path(path, self.config.tab.icon_sizes) { if let Some(items) = tab.items_opt() {
children.push(item.property_view(IconSizes::default())); for item in items.iter() {
if item.location_opt.as_ref() == Some(location) {
children.push(item.property_view(tab.config.icon_sizes));
// Only show one property view to avoid issues like hangs when generating
// preview images on thousands of files
break;
}
} }
} }
} }
widget::settings::view_column(children).into()
} }
PreviewKind::Selected => {
Some(ContextItem::BreadCrumbs(index)) => { if let Some(tab) = self.tab_model.data::<Tab>(entity) {
let mut children = Vec::with_capacity(1); if let Some(items) = tab.items_opt() {
if let Some(tab) = self.tab_model.active_data::<Tab>() { for item in items.iter() {
let path_opt = tab if item.selected {
.location children.push(item.property_view(tab.config.icon_sizes));
.path_opt() // Only show one property view to avoid issues like hangs when generating
.and_then(|path| path.ancestors().nth(index)) // preview images on thousands of files
.map(|path| path.to_path_buf()); break;
if let Some(ref path) = path_opt { }
//TODO: this should be done once, not when generating the view!
if let Ok(item) = tab::item_from_path(path, self.config.tab.icon_sizes) {
children.push(item.property_view(IconSizes::default()));
} }
};
}
widget::settings::view_column(children).into()
}
}
}
fn tab_properties(&self, entity: segmented_button::Entity) -> Element<Message> {
let mut children = Vec::new();
if let Some(tab) = self.tab_model.data::<Tab>(entity) {
if let Some(items) = tab.items_opt() {
for item in items.iter() {
if item.selected {
children.push(item.property_view(tab.config.icon_sizes));
// Only show one property view to avoid issues like hangs when generating
// preview images on thousands of files
break;
} }
} }
} }
} }
widget::settings::view_column(children).into() widget::settings::view_column(children).into()
} }
@ -1205,6 +1203,7 @@ impl Application for App {
pending_operations: BTreeMap::new(), pending_operations: BTreeMap::new(),
complete_operations: BTreeMap::new(), complete_operations: BTreeMap::new(),
failed_operations: BTreeMap::new(), failed_operations: BTreeMap::new(),
preview_opt: None,
search_active: false, search_active: false,
search_id: widget::Id::unique(), search_id: widget::Id::unique(),
search_input: String::new(), search_input: String::new(),
@ -1307,10 +1306,7 @@ impl Application for App {
NavMenuAction::OpenInNewWindow(id), NavMenuAction::OpenInNewWindow(id),
), ),
cosmic::widget::menu::Item::Divider, cosmic::widget::menu::Item::Divider,
cosmic::widget::menu::Item::Button( cosmic::widget::menu::Item::Button(fl!("show-details"), NavMenuAction::Preview(id)),
fl!("show-details"),
NavMenuAction::Properties(id),
),
cosmic::widget::menu::Item::Divider, cosmic::widget::menu::Item::Divider,
if is_context_trash { if is_context_trash {
cosmic::widget::menu::Item::Button( cosmic::widget::menu::Item::Button(
@ -2031,6 +2027,17 @@ impl Application for App {
} }
return self.update_notification(); return self.update_notification();
} }
Message::Preview(entity, kind, timeout) => {
if self
.preview_opt
.as_ref()
.is_some_and(|(e, k, i)| *e == entity && *k == kind && i.elapsed() > timeout)
{
self.context_page = ContextPage::Preview(Some(entity), kind);
self.set_show_context(true);
self.set_context_title(self.context_page.title());
}
}
Message::RescanTrash => { Message::RescanTrash => {
// Update trash icon if empty/full // Update trash icon if empty/full
let maybe_entity = self.nav_model.iter().find(|&entity| { let maybe_entity = self.nav_model.iter().find(|&entity| {
@ -2273,10 +2280,9 @@ impl Application for App {
commands.push(self.update(action.message(Some(entity)))); commands.push(self.update(action.message(Some(entity))));
} }
tab::Command::AddNetworkDrive => { tab::Command::AddNetworkDrive => {
let context_page = ContextPage::NetworkDrive; self.context_page = ContextPage::NetworkDrive;
self.context_page = context_page;
self.set_show_context(true); self.set_show_context(true);
self.set_context_title(context_page.title()); self.set_context_title(self.context_page.title());
} }
tab::Command::ChangeLocation(tab_title, tab_path, selection_path) => { tab::Command::ChangeLocation(tab_title, tab_path, selection_path) => {
self.activate_nav_model_location(&tab_path); self.activate_nav_model_location(&tab_path);
@ -2299,12 +2305,6 @@ impl Application for App {
message::app(Message::TabMessage(Some(entity), tab_message)) message::app(Message::TabMessage(Some(entity), tab_message))
})); }));
} }
tab::Command::LocationProperties(index) => {
self.context_page =
ContextPage::Properties(Some(ContextItem::BreadCrumbs(index)));
self.set_show_context(true);
self.set_context_title(self.context_page.title());
}
tab::Command::MoveToTrash(paths) => { tab::Command::MoveToTrash(paths) => {
self.operation(Operation::Delete { paths }); self.operation(Operation::Delete { paths });
} }
@ -2375,6 +2375,23 @@ impl Application for App {
log::error!("failed to get current executable path: {}", err); log::error!("failed to get current executable path: {}", err);
} }
}, },
tab::Command::Preview(kind, mut timeout) => {
self.preview_opt = Some((entity, kind.clone(), Instant::now()));
if self.core.window.show_context {
// If the context window is already open, immediately show the preview
timeout = time::Duration::new(0, 0)
};
commands.push(Command::perform(
async move {
tokio::time::sleep(timeout).await;
message::app(Message::Preview(entity, kind, timeout))
},
|x| x,
));
}
tab::Command::PreviewCancel => {
self.preview_opt = None;
}
} }
} }
return Command::batch(commands); return Command::batch(commands);
@ -2405,10 +2422,10 @@ impl Application for App {
if self.context_page == context_page { if self.context_page == context_page {
self.set_show_context(!self.core.window.show_context); self.set_show_context(!self.core.window.show_context);
} else { } else {
self.context_page = context_page;
self.set_show_context(true); self.set_show_context(true);
} }
self.set_context_title(context_page.title()); self.context_page = context_page;
self.set_context_title(self.context_page.title());
} }
Message::Undo(id) => { Message::Undo(id) => {
// TODO; // TODO;
@ -2624,10 +2641,22 @@ impl Application for App {
} }
} }
NavMenuAction::Properties(entity) => { NavMenuAction::Preview(entity) => {
self.context_page = ContextPage::Properties(Some(ContextItem::NavBar(entity))); if let Some(Location::Path(path)) = self.nav_model.data::<Location>(entity) {
self.set_show_context(true); match tab::item_from_path(path, IconSizes::default()) {
self.set_context_title(self.context_page.title()); Ok(item) => {
self.context_page = ContextPage::Preview(
None,
PreviewKind::Custom(PreviewItem(item)),
);
self.set_show_context(true);
self.set_context_title(self.context_page.title());
}
Err(err) => {
log::warn!("failed to get item from path {:?}: {}", path, err);
}
}
}
} }
NavMenuAction::RemoveFromSidebar(entity) => { NavMenuAction::RemoveFromSidebar(entity) => {
@ -2732,12 +2761,12 @@ impl Application for App {
return None; return None;
} }
Some(match self.context_page { Some(match &self.context_page {
ContextPage::About => self.about(), ContextPage::About => self.about(),
ContextPage::EditHistory => self.edit_history(), ContextPage::EditHistory => self.edit_history(),
ContextPage::NetworkDrive => self.network_drive(), ContextPage::NetworkDrive => self.network_drive(),
ContextPage::OpenWith => self.open_with(), ContextPage::OpenWith => self.open_with(),
ContextPage::Properties(entity) => self.properties(entity), ContextPage::Preview(entity_opt, kind) => self.preview(entity_opt, kind),
ContextPage::Settings => self.settings(), ContextPage::Settings => self.settings(),
}) })
} }

View file

@ -46,7 +46,7 @@ pub fn key_binds() -> HashMap<KeyBind, Action> {
bind!([Ctrl], Key::Named(Named::Enter), OpenInNewTab); bind!([Ctrl], Key::Named(Named::Enter), OpenInNewTab);
bind!([Shift], Key::Named(Named::Enter), OpenInNewWindow); bind!([Shift], Key::Named(Named::Enter), OpenInNewWindow);
bind!([Ctrl], Key::Character("v".into()), Paste); bind!([Ctrl], Key::Character("v".into()), Paste);
bind!([], Key::Named(Named::Space), Properties); bind!([], Key::Named(Named::Space), Preview);
bind!([], Key::Named(Named::F2), Rename); bind!([], Key::Named(Named::F2), Rename);
bind!([Ctrl], Key::Character("f".into()), SearchActivate); bind!([Ctrl], Key::Character("f".into()), SearchActivate);
bind!([Ctrl], Key::Character("a".into()), SelectAll); bind!([Ctrl], Key::Character("a".into()), SelectAll);

View file

@ -155,7 +155,7 @@ pub fn context_menu<'a>(
children.push(divider::horizontal::light().into()); children.push(divider::horizontal::light().into());
//TODO: Print? //TODO: Print?
children.push(menu_item(fl!("show-details"), Action::Properties).into()); children.push(menu_item(fl!("show-details"), Action::Preview).into());
children.push(divider::horizontal::light().into()); children.push(divider::horizontal::light().into());
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());
@ -231,7 +231,7 @@ pub fn context_menu<'a>(
children.push(divider::horizontal::light().into()); children.push(divider::horizontal::light().into());
} }
if selected > 0 { if selected > 0 {
children.push(menu_item(fl!("show-details"), Action::Properties).into()); children.push(menu_item(fl!("show-details"), Action::Preview).into());
children.push(divider::horizontal::light().into()); children.push(divider::horizontal::light().into());
children children
.push(menu_item(fl!("restore-from-trash"), Action::RestoreFromTrash).into()); .push(menu_item(fl!("restore-from-trash"), Action::RestoreFromTrash).into());
@ -371,7 +371,7 @@ pub fn menu_bar<'a>(
menu::Item::Divider, menu::Item::Divider,
menu::Item::Button(fl!("rename"), Action::Rename), menu::Item::Button(fl!("rename"), Action::Rename),
menu::Item::Divider, menu::Item::Divider,
menu::Item::Button(fl!("menu-show-details"), Action::Properties), menu::Item::Button(fl!("menu-show-details"), Action::Preview),
menu::Item::Divider, menu::Item::Divider,
menu::Item::Button(fl!("add-to-sidebar"), Action::AddToSidebar), menu::Item::Button(fl!("add-to-sidebar"), Action::AddToSidebar),
menu::Item::Divider, menu::Item::Divider,
@ -486,7 +486,7 @@ pub fn location_context_menu<'a>(ancestor_index: usize) -> Element<'a, tab::Mess
divider::horizontal::light().into(), divider::horizontal::light().into(),
menu_button!(text::body(fl!("show-details"))) menu_button!(text::body(fl!("show-details")))
.on_press(tab::Message::LocationMenuAction( .on_press(tab::Message::LocationMenuAction(
LocationMenuAction::Properties(ancestor_index), LocationMenuAction::Preview(ancestor_index),
)) ))
.into(), .into(),
]; ];

View file

@ -55,7 +55,7 @@ use std::{
}; };
use crate::{ use crate::{
app::{self, Action}, app::{self, Action, PreviewItem, PreviewKind},
clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste}, clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste},
config::{IconSizes, TabConfig, ICON_SCALE_MAX, ICON_SIZE_GRID}, config::{IconSizes, TabConfig, ICON_SCALE_MAX, ICON_SIZE_GRID},
dialog::DialogKind, dialog::DialogKind,
@ -687,11 +687,9 @@ fn uri_to_path(uri: String) -> Option<PathBuf> {
} }
pub fn scan_recents(sizes: IconSizes) -> Vec<Item> { pub fn scan_recents(sizes: IconSizes) -> Vec<Item> {
let mut recent_files = recently_used_xbel::parse_file();
let mut recents = Vec::new(); let mut recents = Vec::new();
match recent_files { match recently_used_xbel::parse_file() {
Ok(recent_files) => { Ok(recent_files) => {
for bookmark in recent_files.bookmarks { for bookmark in recent_files.bookmarks {
let uri = bookmark.href; let uri = bookmark.href;
@ -814,11 +812,12 @@ pub enum Command {
DropFiles(PathBuf, ClipboardPaste), DropFiles(PathBuf, ClipboardPaste),
EmptyTrash, EmptyTrash,
Iced(cosmic::Command<Message>), Iced(cosmic::Command<Message>),
LocationProperties(usize),
MoveToTrash(Vec<PathBuf>), MoveToTrash(Vec<PathBuf>),
OpenFile(PathBuf), OpenFile(PathBuf),
OpenInNewTab(PathBuf), OpenInNewTab(PathBuf),
OpenInNewWindow(PathBuf), OpenInNewWindow(PathBuf),
Preview(PreviewKind, Duration),
PreviewCancel,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -870,7 +869,7 @@ pub enum Message {
pub enum LocationMenuAction { pub enum LocationMenuAction {
OpenInNewTab(usize), OpenInNewTab(usize),
OpenInNewWindow(usize), OpenInNewWindow(usize),
Properties(usize), Preview(usize),
} }
impl MenuAction for LocationMenuAction { impl MenuAction for LocationMenuAction {
@ -1574,6 +1573,7 @@ impl Tab {
let mut history_i_opt = None; let mut history_i_opt = None;
let mod_ctrl = modifiers.contains(Modifiers::CTRL) && self.mode.multiple(); let mod_ctrl = modifiers.contains(Modifiers::CTRL) && self.mode.multiple();
let mod_shift = modifiers.contains(Modifiers::SHIFT) && self.mode.multiple(); let mod_shift = modifiers.contains(Modifiers::SHIFT) && self.mode.multiple();
let last_select_focus = self.select_focus;
match message { match message {
Message::AddNetworkDrive => { Message::AddNetworkDrive => {
commands.push(Command::AddNetworkDrive); commands.push(Command::AddNetworkDrive);
@ -1627,6 +1627,9 @@ impl Tab {
} else { } else {
log::warn!("no item for click index {:?}", click_i_opt); log::warn!("no item for click index {:?}", click_i_opt);
} }
// Cancel any preview timers
commands.push(Command::PreviewCancel);
} }
Message::Click(click_i_opt) => { Message::Click(click_i_opt) => {
self.selected_clicked = false; self.selected_clicked = false;
@ -1742,7 +1745,7 @@ impl Tab {
item.selected = true; item.selected = true;
self.select_range = Some((i, i)); self.select_range = Some((i, i));
} }
self.select_focus = click_i_opt;
self.selected_clicked = true; self.selected_clicked = true;
} else if !dont_unset && item.selected { } else if !dont_unset && item.selected {
self.clicked = click_i_opt; self.clicked = click_i_opt;
@ -1750,10 +1753,6 @@ impl Tab {
} }
} }
} }
if self.select_focus.take().is_some() {
// Unfocus currently focused button
commands.push(Command::Iced(widget::button::focus(widget::Id::unique())));
}
} }
} }
Message::Config(config) => { Message::Config(config) => {
@ -1806,8 +1805,22 @@ impl Tab {
commands.push(Command::OpenInNewWindow(path)); commands.push(Command::OpenInNewWindow(path));
} }
} }
LocationMenuAction::Properties(ancestor_index) => { LocationMenuAction::Preview(ancestor_index) => {
commands.push(Command::LocationProperties(ancestor_index)); if let Some(path) = path_for_index(ancestor_index) {
//TODO: blocking code, run in command
match item_from_path(&path, IconSizes::default()) {
Ok(item) => {
// Show preview instantly
commands.push(Command::Preview(
PreviewKind::Custom(PreviewItem(item)),
Duration::new(0, 0),
));
}
Err(err) => {
log::warn!("failed to get item from path {:?}: {}", path, err);
}
}
}
} }
} }
} }
@ -2160,9 +2173,11 @@ impl Tab {
self.dnd_hovered = None; self.dnd_hovered = None;
} }
Message::DndHover(loc) => { Message::DndHover(loc) => {
if self.dnd_hovered.as_ref().is_some_and(|(l, i)| { if self
*l == loc && Instant::now().duration_since(*i) > HOVER_DURATION .dnd_hovered
}) { .as_ref()
.is_some_and(|(l, i)| *l == loc && i.elapsed() > HOVER_DURATION)
{
cd = Some(loc); cd = Some(loc);
} }
} }
@ -2177,6 +2192,9 @@ impl Tab {
|x| x, |x| x,
))); )));
} }
// Clear preview timer
commands.push(Command::PreviewCancel);
} }
Message::DndLeave(loc) => { Message::DndLeave(loc) => {
if Some(&loc) == self.dnd_hovered.as_ref().map(|(l, _)| l) { if Some(&loc) == self.dnd_hovered.as_ref().map(|(l, _)| l) {
@ -2226,6 +2244,26 @@ impl Tab {
} }
} }
} }
// Update preview timer
//TODO: make this configurable
if last_select_focus != self.select_focus {
if let Some(index) = self.select_focus {
if let Some(ref items) = self.items_opt {
if let Some(item) = items.get(index) {
if let Some(location) = item.location_opt.clone() {
// Show preview after double click timeout
commands.push(Command::Preview(
PreviewKind::Location(location),
DOUBLE_CLICK_DURATION,
));
}
}
}
}
}
// Change directory if requested
if let Some(location) = cd { if let Some(location) = cd {
if matches!(self.mode, Mode::Desktop) { if matches!(self.mode, Mode::Desktop) {
match location { match location {
@ -2252,6 +2290,7 @@ impl Tab {
} }
} }
} }
commands commands
} }
@ -3238,54 +3277,7 @@ impl Tab {
let button_row = button(row.into()); let button_row = button(row.into());
let button_row: Element<_> = let button_row: Element<_> =
if item.metadata.is_dir() && item.location_opt.is_some() { if item.metadata.is_dir() && item.location_opt.is_some() {
let tab_location = item.location_opt.clone().unwrap(); self.dnd_dest(item.location_opt.as_ref().unwrap(), button_row)
let tab_location_enter = tab_location.clone();
let tab_location_leave = tab_location.clone();
let is_dnd_hovered =
self.dnd_hovered.as_ref().map(|(l, _)| l) == Some(&tab_location);
cosmic::widget::container(
DndDestination::for_data(button_row, move |data, action| {
if let Some(mut data) = data {
if action == DndAction::Copy {
Message::Drop(Some((tab_location.clone(), data)))
} else if action == DndAction::Move {
data.kind = ClipboardKind::Cut;
Message::Drop(Some((tab_location.clone(), data)))
} else {
log::warn!("unsupported action: {:?}", action);
Message::Drop(None)
}
} else {
log::warn!("No data for drop.");
Message::Drop(None)
}
})
.on_enter(move |_, _, _| Message::DndEnter(tab_location_enter.clone()))
.on_leave(move || Message::DndLeave(tab_location_leave.clone())),
)
// todo refactor into the dnd destination wrapper
.style(if is_dnd_hovered {
theme::Container::custom(|t| {
let mut a = cosmic::iced_style::container::StyleSheet::appearance(
t,
&theme::Container::default(),
);
let t = t.cosmic();
// todo use theme drop target color
let mut bg = t.accent_color();
bg.alpha = 0.2;
a.background = Some(Color::from(bg).into());
a.border = Border {
color: t.accent_color().into(),
width: 1.0,
radius: t.radius_s().into(),
};
a
})
} else {
theme::Container::default()
})
.into()
} else { } else {
button_row.into() button_row.into()
}; };
@ -3669,10 +3661,7 @@ impl Tab {
} }
} }
//TODO: how to properly kill this task? std::future::pending().await
loop {
tokio::time::sleep(std::time::Duration::new(1, 0)).await;
}
}, },
)); ));
} }