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...
This commit is contained in:
Ian Douglas Scott 2025-01-24 14:09:17 -08:00
parent f532205bf9
commit efdfaed6c8
2 changed files with 57 additions and 63 deletions

View file

@ -122,6 +122,30 @@ impl TryFrom<(Vec<u8>, 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::<u8>() };
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)] #[derive(Clone, Debug)]
enum Msg { enum Msg {
WaylandEvent(WaylandEvent), WaylandEvent(WaylandEvent),
@ -132,16 +156,9 @@ enum Msg {
CloseWorkspace(ZcosmicWorkspaceHandleV1), CloseWorkspace(ZcosmicWorkspaceHandleV1),
ActivateToplevel(ZcosmicToplevelHandleV1), ActivateToplevel(ZcosmicToplevelHandleV1),
CloseToplevel(ZcosmicToplevelHandleV1), CloseToplevel(ZcosmicToplevelHandleV1),
//StartDrag(Size, Vector, DragSurface),
StartDrag(DragSurface), StartDrag(DragSurface),
DndWorkspaceEnter( DndEnter(DropTarget, f64, f64, Vec<String>),
ZcosmicWorkspaceHandleV1, DndLeave(DropTarget),
wl_output::WlOutput,
f64,
f64,
Vec<String>,
),
DndWorkspaceLeave(ZcosmicWorkspaceHandleV1, wl_output::WlOutput),
DndWorkspaceDrop(DragToplevel), DndWorkspaceDrop(DragToplevel),
SourceFinished, SourceFinished,
#[allow(dead_code)] #[allow(dead_code)]
@ -217,7 +234,7 @@ struct App {
drag_surface: Option<(DragSurface, Size)>, drag_surface: Option<(DragSurface, Size)>,
conf: Conf, conf: Conf,
core: cosmic::app::Core, core: cosmic::app::Core,
drop_target: Option<(ZcosmicWorkspaceHandleV1, wl_output::WlOutput)>, drop_target: Option<DropTarget>,
} }
impl App { impl App {
@ -537,23 +554,25 @@ impl Application for App {
Msg::StartDrag(drag_surface) => { Msg::StartDrag(drag_surface) => {
self.drag_surface = Some((drag_surface, Default::default())); self.drag_surface = Some((drag_surface, Default::default()));
} }
Msg::DndWorkspaceEnter(handle, output, _x, _y, _mimes) => { Msg::DndEnter(drop_target, _x, _y, _mimes) => {
self.drop_target = Some((handle, output)); 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 // 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. // 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; self.drop_target = None;
} }
} }
Msg::DndWorkspaceDrop(_toplevel) => { Msg::DndWorkspaceDrop(_toplevel) => {
if let Some((DragSurface::Toplevel { handle, .. }, _)) = &self.drag_surface { 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( self.send_wayland_cmd(backend::Cmd::MoveToplevelToWorkspace(
handle.clone(), handle.clone(),
drop_target.0, workspace,
drop_target.1, output,
)); ));
} }
} }

View file

@ -21,28 +21,25 @@ use cosmic_comp_config::workspace::WorkspaceLayout;
use crate::{ use crate::{
backend::{self, CaptureImage}, 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 fn toplevel_dnd_destination<'a>(
#[repr(u8)] target: DropTarget,
enum DragId { child: cosmic::Element<'a, Msg>,
WorkspaceEntry(usize), ) -> cosmic::Element<'a, Msg> {
} let target2 = target.clone();
cosmic::widget::dnd_destination::dnd_destination_for_data(
// Encode as a u64 for iced/smithay_sctk child,
impl From<DragId> for u64 { |data: Option<DragToplevel>, _action| match data {
fn from(id: DragId) -> u64 { Some(toplevel) => Msg::DndWorkspaceDrop(toplevel),
// https://doc.rust-lang.org/std/mem/fn.discriminant.html#accessing-the-numeric-value-of-the-discriminant None => Msg::Ignore,
let discriminant = unsafe { *<*const _>::from(&id).cast::<u8>() }; },
match id { )
DragId::WorkspaceEntry(idx) => { .drag_id(target.drag_id())
// Index should not exceed 32 bits .on_enter(move |actions, mime, pos| Msg::DndEnter(target.clone(), actions, mime, pos))
let idx = u32::try_from(idx).unwrap(); .on_leave(move || Msg::DndLeave(target2.clone()))
((discriminant as u64) << 32) | (idx as u64) .into()
}
}
}
} }
pub(crate) fn layer_surface<'a>( pub(crate) fn layer_surface<'a>(
@ -50,7 +47,7 @@ pub(crate) fn layer_surface<'a>(
surface: &'a LayerSurface, surface: &'a LayerSurface,
) -> cosmic::Element<'a, Msg> { ) -> cosmic::Element<'a, Msg> {
let mut drop_target = None; 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 { if output == &surface.output {
drop_target = Some(workspace); drop_target = Some(workspace);
} }
@ -167,7 +164,6 @@ fn workspace_sidebar_entry<'a>(
workspace: &'a Workspace, workspace: &'a Workspace,
output: &'a wl_output::WlOutput, output: &'a wl_output::WlOutput,
is_drop_target: bool, is_drop_target: bool,
idx: usize,
) -> cosmic::Element<'a, Msg> { ) -> cosmic::Element<'a, Msg> {
/* XXX /* XXX
let mouse_interaction = if is_drop_target { let mouse_interaction = if is_drop_target {
@ -193,30 +189,10 @@ fn workspace_sidebar_entry<'a>(
*/ */
//crate::widgets::mouse_interaction_wrapper( //crate::widgets::mouse_interaction_wrapper(
// mouse_interaction, // mouse_interaction,
let workspace_handle = workspace.handle.clone(); toplevel_dnd_destination(
let workspace_handle2 = workspace_handle.clone(); DropTarget::WorkspaceSidebarEntry(workspace.handle.clone(), output.clone()),
let output_clone = output.clone();
let output_clone2 = output.clone();
cosmic::widget::dnd_destination::dnd_destination_for_data(
workspace_item(workspace, output, is_drop_target), workspace_item(workspace, output, is_drop_target),
|data: Option<DragToplevel>, _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>( fn workspaces_sidebar<'a>(
@ -226,8 +202,7 @@ fn workspaces_sidebar<'a>(
drop_target: Option<&backend::ZcosmicWorkspaceHandleV1>, drop_target: Option<&backend::ZcosmicWorkspaceHandleV1>,
) -> cosmic::Element<'a, Msg> { ) -> cosmic::Element<'a, Msg> {
let sidebar_entries = workspaces let sidebar_entries = workspaces
.enumerate() .map(|w| workspace_sidebar_entry(w, output, drop_target == Some(&w.handle)))
.map(|(i, w)| workspace_sidebar_entry(w, output, drop_target == Some(&w.handle), i))
.collect(); .collect();
let axis = match layout { let axis = match layout {
WorkspaceLayout::Vertical => Axis::Vertical, WorkspaceLayout::Vertical => Axis::Vertical,