git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit41415bee37697e899fbdbc8eac5cf578b6662e54
parent06c6190ae7
authorLucas Galante <[email protected]>
date2026-08-26 15:18
graph: drop_target_cell_rect — the cell an in-flight node drag lands on

commit_drag's exact resolution (round to nearest cell, find_empty_cell
walks off occupied ones) as a read-only accessor, so a host's drop-target
highlight never lies about where the node deposits. On GraphController;
ContentBg answers None.

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/widget/container/content_bg.rs |  1 +
 src/widget/display/graph.rs        | 25 +++++++++++++++++++++++++
 src/widget/mod.rs                  |  4 ++++
 3 files changed, 30 insertions(+)

diff --git a/src/widget/container/content_bg.rs b/src/widget/container/content_bg.rs
index 2701e1c..726fa20 100644
--- a/src/widget/container/content_bg.rs
+++ b/src/widget/container/content_bg.rs
@@ -272,4 +272,5 @@ impl GraphController for ContentBg {
     fn node_at(&self, _px: f32, _py: f32) -> Option<usize> { None }
     fn cell_corner_radius(&self) -> f32 { 0.0 }
     fn geometry_quads_tagged(&self, _rect: crate::scene::layout::Rect) -> Vec<crate::widget::TaggedQuad> { Vec::new() }
+    fn drop_target_cell_rect(&self) -> Option<(f32, f32, f32, f32)> { None }
 }
diff --git a/src/widget/display/graph.rs b/src/widget/display/graph.rs
index de2d8df..8fae672 100644
--- a/src/widget/display/graph.rs
+++ b/src/widget/display/graph.rs
@@ -188,6 +188,30 @@ impl Graph {
         Some((nx, ny, self.grid_size_x, self.grid_size_y))
     }
 
+    /// The grid cell an in-flight node drag will deposit on, as its pixel
+    /// rect — hosts highlight it as the drop target. Runs the SAME
+    /// resolution as `commit_drag` (round to the nearest cell, then
+    /// `find_empty_cell` walks off occupied ones), so the highlight never
+    /// lies about where the node actually lands. None outside a node drag.
+    pub fn drop_target_cell_rect(&self) -> Option<(f32, f32, f32, f32)> {
+        let idx = self.dragging_idx?;
+        let (nx, ny) = self.drag_node_pos?;
+        let step_x = self.grid_size_x + self.skipped_col_w;
+        let step_y = self.grid_size_y + self.skipped_row_h;
+        if step_x <= 0.0 || step_y <= 0.0 {
+            return None;
+        }
+        let c = ((nx - self.grid_origin_x) / step_x).round();
+        let r = ((ny - self.grid_origin_y) / step_y).round();
+        let (c, r) = self.find_empty_cell(c, r, Some(idx));
+        Some((
+            self.grid_origin_x + c * step_x,
+            self.grid_origin_y + r * step_y,
+            self.grid_size_x,
+            self.grid_size_y,
+        ))
+    }
+
     pub fn is_node_rect(&self, qx: f32, qy: f32, qw: f32, qh: f32) -> bool {
         for i in 0..self.nodes.len() {
             if let Some((nx, ny, nw, nh)) = self.node_rect(i) {
@@ -1094,6 +1118,7 @@ impl GraphController for Graph {
     fn get_nodes(&self) -> Vec<GraphNode> { self.nodes.clone() }
     fn cell_corner_radius(&self) -> f32 { Graph::cell_corner_radius(self) }
     fn geometry_quads_tagged(&self, rect: Rect) -> Vec<TaggedQuad> { Graph::geometry_quads_tagged(self, rect) }
+    fn drop_target_cell_rect(&self) -> Option<(f32, f32, f32, f32)> { Graph::drop_target_cell_rect(self) }
     fn selected_node(&self) -> Option<usize> { self.selected_idx }
     fn set_selected_node(&mut self, idx: Option<usize>) {
         self.selected_idx = idx;
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index 60b7337..9387562 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -612,6 +612,10 @@ pub trait GraphController {
     /// rounded corners — for hosts that draw the graph's quads themselves
     /// (the designer) and want cells as superellipse tiles.
     fn geometry_quads_tagged(&self, rect: crate::scene::layout::Rect) -> Vec<TaggedQuad>;
+    /// The pixel rect of the cell an in-flight node drag will deposit on
+    /// (`commit_drag`'s resolution), for hosts' drop-target highlight.
+    /// None outside a node drag.
+    fn drop_target_cell_rect(&self) -> Option<(f32, f32, f32, f32)>;
 }
 
 pub trait SpreadsheetController {