feat: gray out paste menu when clipboard is empty or location unsupported
This commit is contained in:
parent
f9d4ca4867
commit
591ba0f9b9
5 changed files with 173 additions and 20 deletions
126
src/app.rs
126
src/app.rs
|
|
@ -73,8 +73,8 @@ use wayland_client::{Proxy, protocol::wl_output::WlOutput};
|
|||
use crate::{
|
||||
FxOrderMap,
|
||||
clipboard::{
|
||||
ClipboardCopy, ClipboardKind, ClipboardPaste, ClipboardPasteImage, ClipboardPasteText,
|
||||
ClipboardPasteVideo,
|
||||
ClipboardCache, ClipboardCopy, ClipboardKind, ClipboardPaste, ClipboardPasteImage,
|
||||
ClipboardPasteText, ClipboardPasteVideo,
|
||||
},
|
||||
config::{
|
||||
AppTheme, Config, DesktopConfig, Favorite, IconSizes, State, TIME_CONFIG_ID, TabConfig,
|
||||
|
|
@ -407,6 +407,11 @@ pub enum Message {
|
|||
PasteTextContents(PathBuf, ClipboardPasteText),
|
||||
PasteVideo(PathBuf),
|
||||
PasteVideoContents(PathBuf, ClipboardPasteVideo),
|
||||
CheckClipboard,
|
||||
CheckClipboardImage,
|
||||
CheckClipboardVideo,
|
||||
CheckClipboardText,
|
||||
ClipboardCached(ClipboardCache),
|
||||
PendingCancel(u64),
|
||||
PendingCancelAll,
|
||||
PendingComplete(u64, OperationSelection),
|
||||
|
|
@ -761,9 +766,15 @@ pub struct App {
|
|||
tab_drag_id: DragId,
|
||||
auto_scroll_speed: Option<i16>,
|
||||
file_dialog_opt: Option<Dialog<Message>>,
|
||||
clipboard_cache: ClipboardCache,
|
||||
}
|
||||
|
||||
impl App {
|
||||
/// Returns true if the clipboard cache contains pasteable content
|
||||
fn clipboard_has_content(&self) -> bool {
|
||||
!matches!(self.clipboard_cache, ClipboardCache::Empty)
|
||||
}
|
||||
|
||||
fn push_dialog(&mut self, page: DialogPage, focus_id: Option<widget::Id>) -> Task<Message> {
|
||||
let t = self.dialog_pages.push_back(page);
|
||||
if let Some(focus_id) = focus_id {
|
||||
|
|
@ -2311,11 +2322,12 @@ impl Application for App {
|
|||
tab_drag_id: DragId::new(),
|
||||
auto_scroll_speed: None,
|
||||
file_dialog_opt: None,
|
||||
clipboard_cache: ClipboardCache::Empty,
|
||||
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
|
||||
layer_sizes: FxHashMap::default(),
|
||||
};
|
||||
|
||||
let mut commands = vec![app.update_config()];
|
||||
let mut commands = vec![app.update_config(), app.update(Message::CheckClipboard)];
|
||||
|
||||
for location in flags.locations {
|
||||
if let Some(path) = location.path_opt()
|
||||
|
|
@ -3658,15 +3670,39 @@ impl Application for App {
|
|||
&& let Some(path) = tab.location.path_opt()
|
||||
{
|
||||
let to = path.clone();
|
||||
return clipboard::read_data::<ClipboardPaste>().map(move |contents_opt| {
|
||||
match contents_opt {
|
||||
Some(contents) => {
|
||||
cosmic::action::app(Message::PasteContents(to.clone(), contents))
|
||||
}
|
||||
// No file data in clipboard, try image data
|
||||
None => cosmic::action::app(Message::PasteImage(to.clone())),
|
||||
|
||||
// Use cached clipboard data if available (needed for Wayland popups)
|
||||
match &self.clipboard_cache {
|
||||
ClipboardCache::Files(contents) => {
|
||||
return self
|
||||
.update(Message::PasteContents(to.clone(), contents.clone()));
|
||||
}
|
||||
});
|
||||
ClipboardCache::Image(contents) => {
|
||||
return self
|
||||
.update(Message::PasteImageContents(to.clone(), contents.clone()));
|
||||
}
|
||||
ClipboardCache::Video(contents) => {
|
||||
return self
|
||||
.update(Message::PasteVideoContents(to.clone(), contents.clone()));
|
||||
}
|
||||
ClipboardCache::Text(contents) => {
|
||||
return self
|
||||
.update(Message::PasteTextContents(to.clone(), contents.clone()));
|
||||
}
|
||||
ClipboardCache::Empty => {
|
||||
// Cache is empty, try reading from clipboard directly
|
||||
// (works when triggered from main window, e.g., Ctrl+V)
|
||||
return clipboard::read_data::<ClipboardPaste>().map(
|
||||
move |contents_opt| match contents_opt {
|
||||
Some(contents) => cosmic::action::app(Message::PasteContents(
|
||||
to.clone(),
|
||||
contents,
|
||||
)),
|
||||
None => cosmic::action::app(Message::PasteImage(to.clone())),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::PasteContents(to, mut contents) => {
|
||||
|
|
@ -3781,6 +3817,48 @@ impl Application for App {
|
|||
}
|
||||
}
|
||||
}
|
||||
Message::CheckClipboard => {
|
||||
// Check if clipboard has any paste-able content and cache it
|
||||
return clipboard::read_data::<ClipboardPaste>().map(|contents_opt| {
|
||||
match contents_opt {
|
||||
Some(contents) => cosmic::action::app(Message::ClipboardCached(
|
||||
ClipboardCache::Files(contents),
|
||||
)),
|
||||
None => cosmic::action::app(Message::CheckClipboardImage),
|
||||
}
|
||||
});
|
||||
}
|
||||
Message::CheckClipboardImage => {
|
||||
return clipboard::read_data::<ClipboardPasteImage>().map(|contents_opt| {
|
||||
match contents_opt {
|
||||
Some(contents) => cosmic::action::app(Message::ClipboardCached(
|
||||
ClipboardCache::Image(contents),
|
||||
)),
|
||||
None => cosmic::action::app(Message::CheckClipboardVideo),
|
||||
}
|
||||
});
|
||||
}
|
||||
Message::CheckClipboardVideo => {
|
||||
return clipboard::read_data::<ClipboardPasteVideo>().map(|contents_opt| {
|
||||
match contents_opt {
|
||||
Some(contents) => cosmic::action::app(Message::ClipboardCached(
|
||||
ClipboardCache::Video(contents),
|
||||
)),
|
||||
None => cosmic::action::app(Message::CheckClipboardText),
|
||||
}
|
||||
});
|
||||
}
|
||||
Message::CheckClipboardText => {
|
||||
return clipboard::read_data::<ClipboardPasteText>().map(|contents_opt| {
|
||||
cosmic::action::app(Message::ClipboardCached(match contents_opt {
|
||||
Some(contents) => ClipboardCache::Text(contents),
|
||||
None => ClipboardCache::Empty,
|
||||
}))
|
||||
});
|
||||
}
|
||||
Message::ClipboardCached(cache) => {
|
||||
self.clipboard_cache = cache;
|
||||
}
|
||||
Message::PendingCancel(id) => {
|
||||
if let Some((_, controller)) = self.pending_operations.get(&id) {
|
||||
controller.cancel();
|
||||
|
|
@ -5042,6 +5120,8 @@ impl Application for App {
|
|||
_ => {}
|
||||
};
|
||||
}
|
||||
// Check clipboard when window gains focus
|
||||
return self.update(Message::CheckClipboard);
|
||||
}
|
||||
Message::Surface(action) => {
|
||||
return cosmic::task::message(cosmic::Action::Cosmic(
|
||||
|
|
@ -5996,6 +6076,7 @@ impl Application for App {
|
|||
&self.config,
|
||||
&self.modifiers,
|
||||
&self.key_binds,
|
||||
self.clipboard_has_content(),
|
||||
)]
|
||||
}
|
||||
|
||||
|
|
@ -6084,7 +6165,11 @@ impl Application for App {
|
|||
let entity = self.tab_model.active();
|
||||
if let Some(tab) = self.tab_model.data::<Tab>(entity) {
|
||||
let tab_view = tab
|
||||
.view(&self.key_binds, &self.modifiers)
|
||||
.view(
|
||||
&self.key_binds,
|
||||
&self.modifiers,
|
||||
self.clipboard_has_content(),
|
||||
)
|
||||
.map(move |message| Message::TabMessage(Some(entity), message));
|
||||
tab_column = tab_column.push(tab_view);
|
||||
} else {
|
||||
|
|
@ -6107,8 +6192,13 @@ impl Application for App {
|
|||
WindowKind::ContextMenu(entity, id) => match self.tab_model.data::<Tab>(*entity) {
|
||||
Some(tab) => {
|
||||
return widget::autosize::autosize(
|
||||
menu::context_menu(tab, &self.key_binds, &window.modifiers)
|
||||
.map(|x| Message::TabMessage(Some(*entity), x)),
|
||||
menu::context_menu(
|
||||
tab,
|
||||
&self.key_binds,
|
||||
&window.modifiers,
|
||||
self.clipboard_has_content(),
|
||||
)
|
||||
.map(|x| Message::TabMessage(Some(*entity), x)),
|
||||
id.clone(),
|
||||
)
|
||||
.into();
|
||||
|
|
@ -6120,7 +6210,11 @@ impl Application for App {
|
|||
|
||||
let tab_view = match self.tab_model.data::<Tab>(*entity) {
|
||||
Some(tab) => tab
|
||||
.view(&self.key_binds, &window.modifiers)
|
||||
.view(
|
||||
&self.key_binds,
|
||||
&window.modifiers,
|
||||
self.clipboard_has_content(),
|
||||
)
|
||||
.map(move |message| Message::TabMessage(Some(*entity), message)),
|
||||
None => widget::vertical_space().into(),
|
||||
};
|
||||
|
|
@ -6221,6 +6315,8 @@ impl Application for App {
|
|||
}
|
||||
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
|
||||
Event::Window(WindowEvent::Focused) => Some(Message::Focused(window_id)),
|
||||
#[cfg(not(all(feature = "wayland", feature = "desktop-applet")))]
|
||||
Event::Window(WindowEvent::Focused) => Some(Message::CheckClipboard),
|
||||
Event::Window(WindowEvent::CloseRequested) => Some(Message::WindowClose),
|
||||
Event::Window(WindowEvent::Opened { position: _, size }) => {
|
||||
Some(Message::Size(window_id, size))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue