From a489a6b7904cbab90b0f5f717b27fb54f62f20b1 Mon Sep 17 00:00:00 2001 From: Stephan Buys Date: Wed, 19 Nov 2025 15:44:21 +0200 Subject: [PATCH] iced: propagate pane drag destinations --- core/src/clipboard.rs | 10 ++++ widget/src/pane_grid.rs | 30 ++++++++++++ widget/src/pane_grid/content.rs | 47 ++++++++++++++++++- winit/Cargo.toml | 1 - winit/src/clipboard.rs | 25 ++++++++++ winit/src/lib.rs | 28 +++++++++-- .../wayland/event_loop/mod.rs | 2 +- .../wayland/event_loop/state.rs | 8 ++-- .../platform_specific/wayland/winit_window.rs | 6 +-- 9 files changed, 142 insertions(+), 15 deletions(-) diff --git a/core/src/clipboard.rs b/core/src/clipboard.rs index d5f3c50b..bef85f27 100644 --- a/core/src/clipboard.rs +++ b/core/src/clipboard.rs @@ -224,6 +224,16 @@ impl DndDestinationRectangles { /// Pushes a new rectangle to the list of DnD destination rectangles. pub fn push(&mut self, rectangle: DndDestinationRectangle) { + log::debug!( + "clipboard: register dnd rectangle id={} rect=({}, {}, {}, {}) mimes={:?} actions={:?}", + rectangle.id, + rectangle.rectangle.x, + rectangle.rectangle.y, + rectangle.rectangle.width, + rectangle.rectangle.height, + rectangle.mime_types, + rectangle.actions + ); self.rectangles.push(rectangle); } diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 00fc0855..2d4683eb 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -90,6 +90,7 @@ use crate::core::{ self, Background, Border, Clipboard, Color, Element, Event, Layout, Length, Pixels, Point, Rectangle, Shell, Size, Theme, Vector, Widget, }; +use log::trace; const DRAG_DEADBAND_DISTANCE: f32 = 10.0; const THICKNESS_RATIO: f32 = 25.0; @@ -1030,6 +1031,35 @@ where (!children.is_empty()).then(|| Group::with_children(children).overlay()) } + + fn drag_destinations( + &self, + state: &Tree, + layout: Layout<'_>, + renderer: &Renderer, + dnd_rectangles: &mut crate::core::clipboard::DndDestinationRectangles, + ) { + self.contents + .iter() + .zip(state.children.iter()) + .zip(layout.children()) + .for_each(|((content, tree), child_layout)| { + trace!( + target: "iced::widget::pane_grid", + "pane drag layout bounds=({:.2},{:.2},{:.2},{:.2})", + child_layout.bounds().x, + child_layout.bounds().y, + child_layout.bounds().width, + child_layout.bounds().height + ); + content.drag_destinations( + tree, + child_layout.with_virtual_offset(layout.virtual_offset()), + renderer, + dnd_rectangles, + ); + }); + } } impl<'a, Message, Theme, Renderer> From> diff --git a/widget/src/pane_grid/content.rs b/widget/src/pane_grid/content.rs index a06cbba7..a76010e7 100644 --- a/widget/src/pane_grid/content.rs +++ b/widget/src/pane_grid/content.rs @@ -6,7 +6,7 @@ use crate::core::renderer; use crate::core::widget::{self, Tree}; use crate::core::{ self, Clipboard, Element, Event, Layout, Point, Rectangle, Shell, Size, - Vector, + Vector, event, }; use crate::pane_grid::{Draggable, TitleBar}; @@ -308,6 +308,51 @@ where None } + pub(crate) fn drag_destinations( + &self, + tree: &Tree, + layout: Layout<'_>, + renderer: &Renderer, + dnd_rectangles: &mut core::clipboard::DndDestinationRectangles, + ) { + let mut body_layout = layout; + if self.title_bar.is_some() { + let mut children = layout.children(); + let title_bar_layout = children.next(); + body_layout = children.next().unwrap_or(layout); + if let Some(title_bar_layout) = title_bar_layout { + log::trace!( + target: "iced::widget::pane_grid::content", + "title_bar bounds=({:.2},{:.2},{:.2},{:.2}) body_bounds=({:.2},{:.2},{:.2},{:.2})", + title_bar_layout.bounds().x, + title_bar_layout.bounds().y, + title_bar_layout.bounds().width, + title_bar_layout.bounds().height, + body_layout.bounds().x, + body_layout.bounds().y, + body_layout.bounds().width, + body_layout.bounds().height, + ); + } + } else { + log::trace!( + target: "iced::widget::pane_grid::content", + "body_bounds=({:.2},{:.2},{:.2},{:.2})", + body_layout.bounds().x, + body_layout.bounds().y, + body_layout.bounds().width, + body_layout.bounds().height, + ); + } + + self.body.as_widget().drag_destinations( + &tree.children[0], + body_layout, + renderer, + dnd_rectangles, + ); + } + pub(crate) fn mouse_interaction( &self, tree: &Tree, diff --git a/winit/Cargo.toml b/winit/Cargo.toml index e72b2fca..db936217 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -50,7 +50,6 @@ iced_accessibility.features = ["accesskit_winit"] log.workspace = true rustc-hash.workspace = true thiserror.workspace = true -tracing.workspace = true window_clipboard.workspace = true dnd.workspace = true winit.workspace = true diff --git a/winit/src/clipboard.rs b/winit/src/clipboard.rs index 30403c70..d302ac36 100644 --- a/winit/src/clipboard.rs +++ b/winit/src/clipboard.rs @@ -402,7 +402,13 @@ impl crate::core::Clipboard for Clipboard { sender, .. } => { + log::debug!( + "clipboard::start_dnd queued internal={} source={:?}", + internal, + source_surface + ); _ = sender.sender.unbounded_send(Control::StartDnd); + queued_events.push(StartDnd { internal, source_surface, @@ -422,6 +428,25 @@ impl crate::core::Clipboard for Clipboard { ) { match &self.state { State::Connected { clipboard, .. } => { + trace!( + target: "iced::winit::clipboard", + "register destination surface={:?} count={}", + surface, + rectangles.len() + ); + for rect in &rectangles { + trace!( + target: "iced::winit::clipboard", + "rect id={:?} bounds=({:.2},{:.2},{:.2},{:.2}) actions={:?} preferred={:?}", + rect.id, + rect.rectangle.x, + rect.rectangle.y, + rect.rectangle.width, + rect.rectangle.height, + rect.actions, + rect.preferred + ); + } _ = clipboard.register_dnd_destination(surface, rectangles) } State::Unavailable => {} diff --git a/winit/src/lib.rs b/winit/src/lib.rs index ef4653e9..d6adc0b8 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -1491,6 +1491,9 @@ async fn run_instance

( Event::Exit => break, Event::Dnd(e) => { use winit::raw_window_handle::HasWindowHandle; + + log::trace!(target: "iced::winit::program", "Dnd event {:?}", e); + match &e { dnd::DndEvent::Offer(_, dnd::OfferEvent::Leave) => { events.push((cur_dnd_surface, core::Event::Dnd(e))); @@ -1577,7 +1580,7 @@ async fn run_instance

( let compositor = match compositor.as_mut() { Some(c) => c, None => { - tracing::error!("No compositor for DnD"); + log::error!("No compositor for DnD"); continue; } }; @@ -1592,10 +1595,20 @@ async fn run_instance

( { let Some(window_id) = source_surface.and_then(|source| { match source { - core::clipboard::DndSource::Surface(s) => Some(s), + core::clipboard::DndSource::Surface(s) => { + log::trace!( + "start_dnd: using explicit surface {:?}", + s + ); + Some(s) + } core::clipboard::DndSource::Widget(w) => { + log::debug!( + "start_dnd: searching for widget {:?}", + w + ); // search windows for widget with operation - user_interfaces.iter_mut().find_map( + let result = user_interfaces.iter_mut().find_map( |(ui_id, ui)| { let Some(ui_renderer) = window_manager .get_mut(ui_id.clone()) @@ -1642,7 +1655,14 @@ async fn run_instance

( } None }, - ) + ); + if result.is_none() { + log::warn!( + "start_dnd: widget {:?} not found; drag will fail", + w + ); + } + result } } }) else { diff --git a/winit/src/platform_specific/wayland/event_loop/mod.rs b/winit/src/platform_specific/wayland/event_loop/mod.rs index c49ca43e..896ca859 100644 --- a/winit/src/platform_specific/wayland/event_loop/mod.rs +++ b/winit/src/platform_specific/wayland/event_loop/mod.rs @@ -49,7 +49,7 @@ use std::{ collections::{HashMap, HashSet}, fmt::Debug, }; -use tracing::error; +use log::error; use wayland_backend::client::Backend; use wayland_client::globals::GlobalError; use wayland_protocols::wp::keyboard_shortcuts_inhibit::zv1::client::zwp_keyboard_shortcuts_inhibit_manager_v1; diff --git a/winit/src/platform_specific/wayland/event_loop/state.rs b/winit/src/platform_specific/wayland/event_loop/state.rs index b722c79c..1b1c9e6a 100644 --- a/winit/src/platform_specific/wayland/event_loop/state.rs +++ b/winit/src/platform_specific/wayland/event_loop/state.rs @@ -1444,7 +1444,7 @@ impl SctkState { platform_specific::wayland::session_lock::Action::LockSurface { id, output } => { // Should we panic if the id does not match? if self.lock_surfaces.iter().any(|s| s.output == output) { - tracing::warn!("Cannot create multiple lock surfaces for a single output."); + log::warn!("Cannot create multiple lock surfaces for a single output."); return Ok(()); } // TODO how to handle this when there's no lock? @@ -1488,13 +1488,13 @@ impl SctkState { Action::OverlapNotify(id, enabled) => { if let Some(layer_surface) = self.layer_surfaces.iter_mut().find(|l| l.id == id) { let Some(overlap_notify_state) = self.overlap_notify.as_ref() else { - tracing::error!("Overlap notify is not supported."); + log::error!("Overlap notify is not supported."); return Ok(()); }; let my_id = layer_surface.surface.wl_surface().id(); if enabled && !self.overlap_notifications.contains_key(&my_id) { let SurfaceKind::Wlr(wlr) = &layer_surface.surface.kind() else { - tracing::error!("Overlap notify is not supported for non wlr surface."); + log::error!("Overlap notify is not supported for non wlr surface."); return Ok(()); }; let notification = overlap_notify_state.notify.notify_on_overlap(wlr, &self.queue_handle, OverlapNotificationV1 { surface: layer_surface.surface.wl_surface().clone() }); @@ -1503,7 +1503,7 @@ impl SctkState { _ = self.overlap_notifications.remove(&my_id); } } else { - tracing::error!("Overlap notify subscription cannot be created for surface. No matching layer surface found."); + log::error!("Overlap notify subscription cannot be created for surface. No matching layer surface found."); } }, Action::Subsurface(action) => match action { diff --git a/winit/src/platform_specific/wayland/winit_window.rs b/winit/src/platform_specific/wayland/winit_window.rs index 2d5065a2..3061e709 100644 --- a/winit/src/platform_specific/wayland/winit_window.rs +++ b/winit/src/platform_specific/wayland/winit_window.rs @@ -242,9 +242,7 @@ impl winit::window::Window for SctkWinitWindow { } fn current_monitor(&self) -> Option { - tracing::warn!( - "current_monitor is not implemented for wayland windows." - ); + log::warn!("current_monitor is not implemented for wayland windows."); None } @@ -255,7 +253,7 @@ impl winit::window::Window for SctkWinitWindow { } fn has_focus(&self) -> bool { - tracing::warn!("has_focus is not implemented for wayland windows."); + log::warn!("has_focus is not implemented for wayland windows."); false }