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

commitba7b0cdd34e7096e6a187c75d5f0f36ec6324e20
parentf6dde96dac
authorLucas Galante <[email protected]>
date2026-06-29 12:14
Fix titlebar drag check to exclude clicks on active widgets

 src/backend/window_runner.rs |  8 +++++++-
 src/context.rs               | 26 ++++++++++++++++++++++++++
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index c2099f3..7fa7f15 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -1930,7 +1930,13 @@ impl<A: Application> PointerHandler for EngineState<A> {
 
                         // Titlebar drag check: y is in [8.0, 32.0], and x is not in the top-right button area
                         let mut should_move = false;
-                        if ly >= border && ly < 32.0 && lx < self.logical_width - 70.0 {
+                        let mut is_widget = false;
+                        if let Some(ctx) = self.inner.ui_context() {
+                            if ctx.is_widget_at(lx, ly) {
+                                is_widget = true;
+                            }
+                        }
+                        if !is_widget && ly >= border && ly < 32.0 && lx < self.logical_width - 70.0 {
                             should_move = true;
                         } else if self.inner.is_movable_backplate_at(lx, ly) {
                             should_move = true;
diff --git a/src/context.rs b/src/context.rs
index 50c2011..e17f520 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -845,6 +845,32 @@ impl UiContext {
         hit_backplate
     }
 
+    pub fn is_widget_at(&self, px: f32, py: f32) -> bool {
+        let scroll_y = crate::widget::hover_animation::get_scroll_offset();
+        let mut candidate_ids = self.spatial_grid.query(px, py).to_vec();
+        if scroll_y != 0.0 {
+            candidate_ids.extend_from_slice(self.spatial_grid.query(px, py + scroll_y));
+            candidate_ids.sort_unstable();
+            candidate_ids.dedup();
+        }
+        for &id in &candidate_ids {
+            if let Some(&ptr) = self.widget_registry.get(&id) {
+                unsafe {
+                    if !ptr.is_null() {
+                        let w = &*ptr;
+                        if !w.is_backplate() {
+                            let is_hit = w.hit_test(px, py, self) || (scroll_y != 0.0 && w.hit_test(px, py + scroll_y, self));
+                            if is_hit && w.blocks_backplate_drag() {
+                                return true;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        false
+    }
+
     fn find_hovered_scrollable(&self, root: *mut (dyn Element + 'static), cx: f32, cy: f32) -> Option<*mut (dyn Element + 'static)> {
         unsafe {
             if root.is_null() {