From efdfaed6c83bc8e3af3e3235cf82923572bf39a6 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Fri, 24 Jan 2025 14:09:17 -0800 Subject: [PATCH] `DropTarget` enum instead of tuple; combine with `DragId` I guess some change is needed for handling workspaces that span outputs (can't encode both the workspace and output id in less than 64 bits, and need bits for discriminant as well). But that's an issue with the previous workspace index based approach. I wonder if `drag_id` is really needed; libcosmic could check if the drag is in bounds of the widget, if drag surfaces can't overlap... --- src/main.rs | 53 +++++++++++++++++++++++++------------- src/view/mod.rs | 67 ++++++++++++++++--------------------------------- 2 files changed, 57 insertions(+), 63 deletions(-) diff --git a/src/main.rs b/src/main.rs index d222f7e..71f1f28 100644 --- a/src/main.rs +++ b/src/main.rs @@ -122,6 +122,30 @@ impl TryFrom<(Vec, std::string::String)> for DragToplevel { } } +#[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), @@ -132,16 +156,9 @@ enum Msg { CloseWorkspace(ZcosmicWorkspaceHandleV1), ActivateToplevel(ZcosmicToplevelHandleV1), CloseToplevel(ZcosmicToplevelHandleV1), - //StartDrag(Size, Vector, DragSurface), StartDrag(DragSurface), - DndWorkspaceEnter( - ZcosmicWorkspaceHandleV1, - wl_output::WlOutput, - f64, - f64, - Vec, - ), - DndWorkspaceLeave(ZcosmicWorkspaceHandleV1, wl_output::WlOutput), + DndEnter(DropTarget, f64, f64, Vec), + DndLeave(DropTarget), DndWorkspaceDrop(DragToplevel), SourceFinished, #[allow(dead_code)] @@ -217,7 +234,7 @@ struct App { drag_surface: Option<(DragSurface, Size)>, conf: Conf, core: cosmic::app::Core, - drop_target: Option<(ZcosmicWorkspaceHandleV1, wl_output::WlOutput)>, + drop_target: Option, } impl App { @@ -537,23 +554,25 @@ impl Application for App { Msg::StartDrag(drag_surface) => { self.drag_surface = Some((drag_surface, Default::default())); } - Msg::DndWorkspaceEnter(handle, output, _x, _y, _mimes) => { - self.drop_target = Some((handle, output)); + Msg::DndEnter(drop_target, _x, _y, _mimes) => { + self.drop_target = Some(drop_target); } - Msg::DndWorkspaceLeave(handle, output) => { + Msg::DndLeave(drop_target) => { // Currently in iced-sctk, a `DndOfferEvent::Motion` may cause a leave event after // an enter event, based on which widget handles it first. So we need a test here. - if self.drop_target == Some((handle, output)) { + if self.drop_target == Some(drop_target) { self.drop_target = None; } } Msg::DndWorkspaceDrop(_toplevel) => { if let Some((DragSurface::Toplevel { handle, .. }, _)) = &self.drag_surface { - if let Some(drop_target) = self.drop_target.take() { + if let Some(DropTarget::WorkspaceSidebarEntry(workspace, output)) = + self.drop_target.take() + { self.send_wayland_cmd(backend::Cmd::MoveToplevelToWorkspace( handle.clone(), - drop_target.0, - drop_target.1, + workspace, + output, )); } } diff --git a/src/view/mod.rs b/src/view/mod.rs index f884e86..2f1f090 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -21,28 +21,25 @@ use cosmic_comp_config::workspace::WorkspaceLayout; use crate::{ backend::{self, CaptureImage}, - App, DragSurface, DragToplevel, LayerSurface, Msg, Toplevel, Workspace, + App, DragSurface, DragToplevel, DropTarget, LayerSurface, Msg, Toplevel, Workspace, }; -// Used in iced/smithay_sctk to associate drag destination area with widget -#[repr(u8)] -enum DragId { - WorkspaceEntry(usize), -} - -// Encode as a u64 for iced/smithay_sctk -impl From for u64 { - fn from(id: DragId) -> u64 { - // https://doc.rust-lang.org/std/mem/fn.discriminant.html#accessing-the-numeric-value-of-the-discriminant - let discriminant = unsafe { *<*const _>::from(&id).cast::() }; - match id { - DragId::WorkspaceEntry(idx) => { - // Index should not exceed 32 bits - let idx = u32::try_from(idx).unwrap(); - ((discriminant as u64) << 32) | (idx as u64) - } - } - } +fn toplevel_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(toplevel) => Msg::DndWorkspaceDrop(toplevel), + 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>( @@ -50,7 +47,7 @@ pub(crate) fn layer_surface<'a>( surface: &'a LayerSurface, ) -> cosmic::Element<'a, Msg> { let mut drop_target = None; - if let Some((workspace, output)) = &app.drop_target { + if let Some(DropTarget::WorkspaceSidebarEntry(workspace, output)) = &app.drop_target { if output == &surface.output { drop_target = Some(workspace); } @@ -167,7 +164,6 @@ fn workspace_sidebar_entry<'a>( workspace: &'a Workspace, output: &'a wl_output::WlOutput, is_drop_target: bool, - idx: usize, ) -> cosmic::Element<'a, Msg> { /* XXX let mouse_interaction = if is_drop_target { @@ -193,30 +189,10 @@ fn workspace_sidebar_entry<'a>( */ //crate::widgets::mouse_interaction_wrapper( // mouse_interaction, - let workspace_handle = workspace.handle.clone(); - let workspace_handle2 = workspace_handle.clone(); - let output_clone = output.clone(); - let output_clone2 = output.clone(); - cosmic::widget::dnd_destination::dnd_destination_for_data( + toplevel_dnd_destination( + DropTarget::WorkspaceSidebarEntry(workspace.handle.clone(), output.clone()), workspace_item(workspace, output, is_drop_target), - |data: Option, _action| match data { - Some(toplevel) => Msg::DndWorkspaceDrop(toplevel), - None => Msg::Ignore, - }, ) - .drag_id(DragId::WorkspaceEntry(idx).into()) - .on_enter(move |actions, mime, pos| { - Msg::DndWorkspaceEnter( - workspace_handle.clone(), - output_clone.clone(), - actions, - mime, - pos, - ) - }) - .on_leave(move || Msg::DndWorkspaceLeave(workspace_handle2.clone(), output_clone2.clone())) - //) - .into() } fn workspaces_sidebar<'a>( @@ -226,8 +202,7 @@ fn workspaces_sidebar<'a>( drop_target: Option<&backend::ZcosmicWorkspaceHandleV1>, ) -> cosmic::Element<'a, Msg> { let sidebar_entries = workspaces - .enumerate() - .map(|(i, w)| workspace_sidebar_entry(w, output, drop_target == Some(&w.handle), i)) + .map(|w| workspace_sidebar_entry(w, output, drop_target == Some(&w.handle))) .collect(); let axis = match layout { WorkspaceLayout::Vertical => Axis::Vertical,