From 3dc82789f5d28df4f01f7a72f15a0c839859feed Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Fri, 24 Jan 2025 14:28:42 -0800 Subject: [PATCH] Move drag-and-drop related types to a `dnd` module --- src/dnd.rs | 93 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 86 ++------------------------------------------- src/view/mod.rs | 15 ++++---- 3 files changed, 102 insertions(+), 92 deletions(-) create mode 100644 src/dnd.rs diff --git a/src/dnd.rs b/src/dnd.rs new file mode 100644 index 0000000..75f5a2b --- /dev/null +++ b/src/dnd.rs @@ -0,0 +1,93 @@ +//! Types related to drag-and-drop + +use cosmic::{ + cctk::{ + cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1, + wayland_client::{protocol::wl_output, Proxy}, + }, + iced::clipboard::mime::AsMimeTypes, +}; +use std::{borrow::Cow, sync::LazyLock}; + +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())); + +static TOPLEVEL_MIME: LazyLock = + LazyLock::new(|| format!("text/x.cosmic-toplevel-id-{}", std::process::id())); + +#[derive(Clone, Debug)] +pub enum DragSurface { + #[allow(dead_code)] + Workspace { + handle: ZcosmicWorkspaceHandleV1, + output: wl_output::WlOutput, + }, + Toplevel { + handle: ZcosmicToplevelHandleV1, + output: wl_output::WlOutput, + }, +} + +// TODO store protocol object id? +#[derive(Clone, Debug)] +pub struct DragToplevel {} + +impl AsMimeTypes for DragToplevel { + fn available(&self) -> Cow<'static, [String]> { + vec![TOPLEVEL_MIME.clone()].into() + } + + fn as_bytes(&self, mime_type: &str) -> Option> { + if mime_type == *TOPLEVEL_MIME { + Some(Vec::new().into()) + } else { + None + } + } +} + +impl cosmic::iced::clipboard::mime::AllowedMimeTypes for DragToplevel { + fn allowed() -> Cow<'static, [String]> { + vec![TOPLEVEL_MIME.clone()].into() + } +} + +impl TryFrom<(Vec, std::string::String)> for DragToplevel { + type Error = (); + fn try_from((_bytes, mime_type): (Vec, String)) -> Result { + if mime_type == *TOPLEVEL_MIME { + Ok(Self {}) + } else { + Err(()) + } + } +} + +#[derive(Clone, Debug, PartialEq)] +#[repr(u8)] +pub enum DropTarget { + WorkspaceSidebarEntry( + zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1, + wl_output::WlOutput, + ), +} + +impl DropTarget { + /// Encode as a u64 for iced/smithay_sctk to associate drag destination area with widget. + pub fn drag_id(&self) -> u64 { + // https://doc.rust-lang.org/std/mem/fn.discriminant.html#accessing-the-numeric-value-of-the-discriminant + let discriminant = unsafe { *<*const _>::from(self).cast::() }; + match self { + Self::WorkspaceSidebarEntry(workspace, _output) => { + // TODO consider workspace that span multiple outputs? + let id = workspace.id().protocol_id(); + (u64::from(discriminant) << 32) | u64::from(id) + } + } + } +} diff --git a/src/main.rs b/src/main.rs index 71f1f28..0694920 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,6 @@ use cosmic::{ cctk, iced::{ self, - clipboard::mime::AsMimeTypes, event::wayland::{Event as WaylandEvent, LayerEvent, OutputEvent}, keyboard::key::{Key, Named}, Size, Subscription, Task, @@ -31,12 +30,10 @@ use cosmic_comp_config::CosmicCompConfig; use cosmic_config::{cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry}; use i18n_embed::DesktopLanguageRequester; use std::{ - borrow::Cow, collections::{HashMap, HashSet}, mem, path::PathBuf, str, - sync::LazyLock, }; mod desktop_info; @@ -45,8 +42,10 @@ mod localize; mod backend; mod view; use backend::{ToplevelInfo, ZcosmicToplevelHandleV1, ZcosmicWorkspaceHandleV1}; +mod dnd; mod utils; mod widgets; +use dnd::{DragSurface, DragToplevel, DropTarget}; #[derive(Clone, Debug, Default, PartialEq, CosmicConfigEntry)] struct CosmicWorkspacesConfig { @@ -54,15 +53,6 @@ struct CosmicWorkspacesConfig { show_workspace_name: bool, } -// 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())); - -static TOPLEVEL_MIME: LazyLock = - LazyLock::new(|| format!("text/x.cosmic-toplevel-id-{}", std::process::id())); - #[derive(Parser, Debug, Clone)] #[command(author, version, about, long_about = None)] #[command(propagate_version = true)] @@ -87,65 +77,6 @@ impl CosmicFlags for Args { } } -// TODO store protocol object id? -#[derive(Clone, Debug)] -struct DragToplevel {} - -impl AsMimeTypes for DragToplevel { - fn available(&self) -> Cow<'static, [String]> { - vec![TOPLEVEL_MIME.clone()].into() - } - - fn as_bytes(&self, mime_type: &str) -> Option> { - if mime_type == *TOPLEVEL_MIME { - Some(Vec::new().into()) - } else { - None - } - } -} - -impl cosmic::iced::clipboard::mime::AllowedMimeTypes for DragToplevel { - fn allowed() -> Cow<'static, [String]> { - vec![crate::TOPLEVEL_MIME.clone()].into() - } -} - -impl TryFrom<(Vec, std::string::String)> for DragToplevel { - type Error = (); - fn try_from((_bytes, mime_type): (Vec, String)) -> Result { - if mime_type == *TOPLEVEL_MIME { - Ok(Self {}) - } else { - Err(()) - } - } -} - -#[derive(Clone, Debug, PartialEq)] -#[repr(u8)] -pub enum DropTarget { - WorkspaceSidebarEntry( - zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1, - wl_output::WlOutput, - ), -} - -impl DropTarget { - /// Encode as a u64 for iced/smithay_sctk to associate drag destination area with widget. - fn drag_id(&self) -> u64 { - // https://doc.rust-lang.org/std/mem/fn.discriminant.html#accessing-the-numeric-value-of-the-discriminant - let discriminant = unsafe { *<*const _>::from(self).cast::() }; - match self { - Self::WorkspaceSidebarEntry(workspace, _output) => { - // TODO consider workspace that span multiple outputs? - let id = workspace.id().protocol_id(); - (u64::from(discriminant) << 32) | u64::from(id) - } - } - } -} - #[derive(Clone, Debug)] enum Msg { WaylandEvent(WaylandEvent), @@ -202,19 +133,6 @@ struct LayerSurface { // them all the time every frame. } -#[derive(Clone, Debug)] -enum DragSurface { - #[allow(dead_code)] - Workspace { - handle: ZcosmicWorkspaceHandleV1, - output: wl_output::WlOutput, - }, - Toplevel { - handle: ZcosmicToplevelHandleV1, - output: wl_output::WlOutput, - }, -} - #[derive(Default)] struct Conf { workspace_config: cosmic_comp_config::workspace::WorkspaceConfig, diff --git a/src/view/mod.rs b/src/view/mod.rs index 2f1f090..ff2d27a 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -1,11 +1,8 @@ -use std::collections::HashMap; - -use cctk::{ - cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1, - wayland_client::protocol::wl_output, -}; use cosmic::{ - cctk, + cctk::{ + cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1, + wayland_client::protocol::wl_output, + }, iced::{ self, advanced::layout::flex::Axis, @@ -18,10 +15,12 @@ use cosmic::{ }; use cosmic_bg_config::Source; use cosmic_comp_config::workspace::WorkspaceLayout; +use std::collections::HashMap; use crate::{ backend::{self, CaptureImage}, - App, DragSurface, DragToplevel, DropTarget, LayerSurface, Msg, Toplevel, Workspace, + dnd::{DragSurface, DragToplevel, DropTarget}, + App, LayerSurface, Msg, Toplevel, Workspace, }; fn toplevel_dnd_destination<'a>(