From ca5db9cc8eed7f2f50066cd8732ce2fef68a3a8e Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 10 Feb 2025 12:02:36 -0800 Subject: [PATCH] UI code for supporting workspace drags Not used yet. --- src/dnd.rs | 41 +++++++++++++++++++++++++++++++++++++- src/main.rs | 12 ++++++++--- src/view/mod.rs | 53 ++++++++++++++++++++++++++++++++++++------------- 3 files changed, 88 insertions(+), 18 deletions(-) diff --git a/src/dnd.rs b/src/dnd.rs index 09241e7..090ec59 100644 --- a/src/dnd.rs +++ b/src/dnd.rs @@ -10,7 +10,6 @@ use crate::backend::{ZcosmicToplevelHandleV1, ZcosmicWorkspaceHandleV1}; // Include `pid` in mime. Want to drag between our surfaces, but not another // process, if we use Wayland object ids. -#[allow(dead_code)] static WORKSPACE_MIME: LazyLock = LazyLock::new(|| format!("text/x.cosmic-workspace-id-{}", std::process::id())); @@ -59,11 +58,47 @@ impl TryFrom<(Vec, std::string::String)> for DragToplevel { } } +#[derive(Clone, Debug)] +pub struct DragWorkspace {} + +impl AsMimeTypes for DragWorkspace { + fn available(&self) -> Cow<'static, [String]> { + vec![WORKSPACE_MIME.clone()].into() + } + + fn as_bytes(&self, mime_type: &str) -> Option> { + if mime_type == *WORKSPACE_MIME { + Some(Vec::new().into()) + } else { + None + } + } +} + +impl cosmic::iced::clipboard::mime::AllowedMimeTypes for DragWorkspace { + fn allowed() -> Cow<'static, [String]> { + vec![WORKSPACE_MIME.clone()].into() + } +} + +impl TryFrom<(Vec, std::string::String)> for DragWorkspace { + type Error = (); + fn try_from((_bytes, mime_type): (Vec, String)) -> Result { + if mime_type == *WORKSPACE_MIME { + Ok(Self {}) + } else { + Err(()) + } + } +} + #[derive(Clone, Debug, PartialEq)] #[repr(u8)] pub enum DropTarget { WorkspaceSidebarEntry(ZcosmicWorkspaceHandleV1, wl_output::WlOutput), OutputToplevels(ZcosmicWorkspaceHandleV1, wl_output::WlOutput), + #[allow(dead_code)] + WorkspacesBar(wl_output::WlOutput), } impl DropTarget { @@ -81,6 +116,10 @@ impl DropTarget { let id = output.id().protocol_id(); (u64::from(discriminant) << 32) | u64::from(id) } + Self::WorkspacesBar(output) => { + let id = output.id().protocol_id(); + (u64::from(discriminant) << 32) | u64::from(id) + } } } } diff --git a/src/main.rs b/src/main.rs index a5c182f..5fdd1d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,7 +47,7 @@ use backend::{ToplevelInfo, ZcosmicToplevelHandleV1, ZcosmicWorkspaceHandleV1}; mod dnd; mod utils; mod widgets; -use dnd::{DragSurface, DragToplevel, DropTarget}; +use dnd::{DragSurface, DragToplevel, DragWorkspace, DropTarget}; #[derive(Clone, Debug, Default, PartialEq, CosmicConfigEntry)] struct CosmicWorkspacesConfig { @@ -98,6 +98,10 @@ enum Msg { DndEnter(DropTarget, f64, f64, Vec), DndLeave(DropTarget), DndToplevelDrop(DragToplevel), + #[allow(dead_code)] + DndWorkspaceDrag, + #[allow(dead_code)] + DndWorkspaceDrop(DragWorkspace), SourceFinished, #[allow(dead_code)] NewWorkspace, @@ -109,7 +113,7 @@ enum Msg { Ignore, } -#[derive(Debug)] +#[derive(Clone, Debug)] struct Workspace { name: String, // img_for_output: HashMap, @@ -515,7 +519,7 @@ impl Application for App { output, )); } - None => {} + Some(DropTarget::WorkspacesBar(_)) | None => {} } } } @@ -608,6 +612,8 @@ impl Application for App { } } } + Msg::DndWorkspaceDrag => {} + Msg::DndWorkspaceDrop(_workspace) => {} Msg::Ignore => {} } diff --git a/src/view/mod.rs b/src/view/mod.rs index 2f898d0..a1ac4f4 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -19,7 +19,7 @@ use std::collections::HashMap; use crate::{ backend::{self, CaptureImage}, - dnd::{DragSurface, DragToplevel, DropTarget}, + dnd::{DragSurface, DragToplevel, DragWorkspace, DropTarget}, App, LayerSurface, Msg, Toplevel, Workspace, }; @@ -41,6 +41,25 @@ fn toplevel_dnd_destination<'a>( .into() } +#[allow(dead_code)] +fn workspace_dnd_destination<'a>( + target: DropTarget, + child: cosmic::Element<'a, Msg>, +) -> cosmic::Element<'a, Msg> { + let target2 = target.clone(); + cosmic::widget::dnd_destination::dnd_destination_for_data( + child, + |data: Option, _action| match data { + Some(workspace) => Msg::DndWorkspaceDrop(workspace), + None => Msg::Ignore, + }, + ) + .drag_id(target.drag_id()) + .on_enter(move |actions, mime, pos| Msg::DndEnter(target.clone(), actions, mime, pos)) + .on_leave(move || Msg::DndLeave(target2.clone())) + .into() +} + pub(crate) fn layer_surface<'a>( app: &'a App, surface: &'a LayerSurface, @@ -142,7 +161,7 @@ fn workspace_item<'a>( workspace: &'a Workspace, _output: &wl_output::WlOutput, is_drop_target: bool, -) -> cosmic::Element<'a, Msg> { +) -> cosmic::Element<'static, Msg> { let image = capture_image(workspace.img.as_ref(), 1.0); let is_active = workspace.is_active; // TODO editable name? @@ -184,26 +203,32 @@ fn workspace_sidebar_entry<'a>( iced::mouse::Interaction::Idle }; */ + let item = workspace_item(workspace, output, is_drop_target); /* TODO allow moving workspaces (needs compositor support) - iced::widget::dnd_source(workspace_item(workspace, output)) - .on_drag(|size| { - Msg::StartDrag( - size, - DragSurface::Workspace { - handle: workspace.handle.clone(), - output: output.clone(), - }, + let workspace_clone = workspace.clone(); // TODO avoid clone + let output_clone = output.clone(); + let source = cosmic::widget::dnd_source(item) + .drag_threshold(5.) + .drag_content(|| DragWorkspace {}) + .drag_icon(move |offset| { + ( + workspace_item(&workspace_clone, &output_clone, false).map(|_| ()), + cosmic::iced_core::widget::tree::State::None, + -offset, ) }) - .on_finished(Msg::SourceFinished) - .on_cancelled(Msg::SourceFinished) - .into() + .on_start(Some(Msg::StartDrag(DragSurface::Workspace( + workspace.handle.clone(), + )))) + .on_finish(Some(Msg::SourceFinished)) + .on_cancel(Some(Msg::SourceFinished)) + .into(); */ //crate::widgets::mouse_interaction_wrapper( // mouse_interaction, toplevel_dnd_destination( DropTarget::WorkspaceSidebarEntry(workspace.handle.clone(), output.clone()), - workspace_item(workspace, output, is_drop_target), + item, ) }