gardesk/gardisplay / eb8314e

Browse files

prevent monitor overlaps during snap

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
eb8314e0011cfdf9beb24d16f38014df3548eada
Parents
896a89e
Tree
faa4058

1 changed file

StatusFile+-
M gardisplay/src/ui/monitor_view.rs 67 34
gardisplay/src/ui/monitor_view.rsmodified
@@ -381,8 +381,32 @@ impl MonitorView {
381
         self.snap_to_nearest(dragged_idx, dragged);
381
         self.snap_to_nearest(dragged_idx, dragged);
382
     }
382
     }
383
 
383
 
384
+    /// Check if a rect overlaps with any monitor except the specified one.
385
+    fn would_overlap(&self, rect: Rect, exclude_idx: usize) -> bool {
386
+        for (i, other) in self.monitors.iter().enumerate() {
387
+            if i == exclude_idx {
388
+                continue;
389
+            }
390
+            if Self::rects_overlap(rect, other.scaled_rect) {
391
+                return true;
392
+            }
393
+        }
394
+        false
395
+    }
396
+
397
+    /// Check if two rects overlap (share any interior area).
398
+    fn rects_overlap(a: Rect, b: Rect) -> bool {
399
+        let a_right = a.x + a.width as i32;
400
+        let a_bottom = a.y + a.height as i32;
401
+        let b_right = b.x + b.width as i32;
402
+        let b_bottom = b.y + b.height as i32;
403
+
404
+        a.x < b_right && a_right > b.x && a.y < b_bottom && a_bottom > b.y
405
+    }
406
+
384
     /// Snap a monitor to be adjacent to the nearest other monitor.
407
     /// Snap a monitor to be adjacent to the nearest other monitor.
385
     /// Uses directional awareness - snaps to the side the monitor was dropped on.
408
     /// Uses directional awareness - snaps to the side the monitor was dropped on.
409
+    /// Ensures no overlaps with other monitors.
386
     fn snap_to_nearest(&mut self, dragged_idx: usize, dragged: Rect) {
410
     fn snap_to_nearest(&mut self, dragged_idx: usize, dragged: Rect) {
387
         let dragged_center_x = dragged.x + dragged.width as i32 / 2;
411
         let dragged_center_x = dragged.x + dragged.width as i32 / 2;
388
         let dragged_center_y = dragged.y + dragged.height as i32 / 2;
412
         let dragged_center_y = dragged.y + dragged.height as i32 / 2;
@@ -402,44 +426,53 @@ impl MonitorView {
402
             let dx = dragged_center_x - other_center_x;
426
             let dx = dragged_center_x - other_center_x;
403
             let dy = dragged_center_y - other_center_y;
427
             let dy = dragged_center_y - other_center_y;
404
 
428
 
405
-            // Determine snap position based on which side we're dropping on
429
+            // Try all 4 sides and pick the best non-overlapping position
406
-            // Also align on the perpendicular axis to ensure actual adjacency
430
+            let candidates = [
407
-            let (new_x, new_y) = if dx.abs() > dy.abs() {
431
+                // Right of other
408
-                // More horizontal - snap left or right
432
+                (other_rect.x + other_rect.width as i32, other_rect.y),
409
-                // Clamp y to ensure vertical overlap with the target
433
+                // Left of other
410
-                let clamped_y = dragged.y
434
+                (other_rect.x - dragged.width as i32, other_rect.y),
411
-                    .max(other_rect.y - dragged.height as i32 + 1)
435
+                // Below other
412
-                    .min(other_rect.y + other_rect.height as i32 - 1);
436
+                (other_rect.x, other_rect.y + other_rect.height as i32),
413
-                if dx > 0 {
437
+                // Above other
414
-                    // Dropping to the right of other - snap to right side
438
+                (other_rect.x, other_rect.y - dragged.height as i32),
415
-                    (other_rect.x + other_rect.width as i32, clamped_y)
439
+            ];
416
-                } else {
440
+
417
-                    // Dropping to the left of other - snap to left side
441
+            // Score each candidate based on direction preference
418
-                    (other_rect.x - dragged.width as i32, clamped_y)
442
+            for (new_x, new_y) in candidates {
443
+                let candidate_rect = Rect::new(new_x, new_y, dragged.width, dragged.height);
444
+
445
+                // Skip if this position would overlap with another monitor
446
+                if self.would_overlap(candidate_rect, dragged_idx) {
447
+                    continue;
419
                 }
448
                 }
420
-            } else {
449
+
421
-                // More vertical - snap above or below
450
+                // Calculate distance with direction weighting
422
-                // Clamp x to ensure horizontal overlap with the target
451
+                let new_center_x = new_x + dragged.width as i32 / 2;
423
-                let clamped_x = dragged.x
452
+                let new_center_y = new_y + dragged.height as i32 / 2;
424
-                    .max(other_rect.x - dragged.width as i32 + 1)
453
+
425
-                    .min(other_rect.x + other_rect.width as i32 - 1);
454
+                // Base distance
426
-                if dy > 0 {
455
+                let mut dist = (dragged_center_x - new_center_x).abs()
427
-                    // Dropping below other - snap to bottom
456
+                    + (dragged_center_y - new_center_y).abs();
428
-                    (clamped_x, other_rect.y + other_rect.height as i32)
457
+
458
+                // Penalize positions that don't match the drag direction
459
+                let snap_dx = new_center_x - other_center_x;
460
+                let snap_dy = new_center_y - other_center_y;
461
+
462
+                // If we're dragging more horizontally, prefer horizontal snaps
463
+                if dx.abs() > dy.abs() {
464
+                    if (dx > 0) != (snap_dx > 0) {
465
+                        dist += 1000; // Penalize wrong horizontal direction
466
+                    }
429
                 } else {
467
                 } else {
430
-                    // Dropping above other - snap to top
468
+                    if (dy > 0) != (snap_dy > 0) {
431
-                    (clamped_x, other_rect.y - dragged.height as i32)
469
+                        dist += 1000; // Penalize wrong vertical direction
470
+                    }
432
                 }
471
                 }
433
-            };
434
 
472
 
435
-            // Distance from current position to this snap position
473
+                if best_snap.map_or(true, |(_, _, best_dist)| dist < best_dist) {
436
-            let new_center_x = new_x + dragged.width as i32 / 2;
474
+                    best_snap = Some((new_x, new_y, dist));
437
-            let new_center_y = new_y + dragged.height as i32 / 2;
475
+                }
438
-            let dist = (dragged_center_x - new_center_x).abs()
439
-                + (dragged_center_y - new_center_y).abs();
440
-
441
-            if best_snap.map_or(true, |(_, _, best_dist)| dist < best_dist) {
442
-                best_snap = Some((new_x, new_y, dist));
443
             }
476
             }
444
         }
477
         }
445
 
478