Skip to main content

agg_gui/widgets/window/
snap_glue.rs

1//! Snap-layout glue for [`super::Window`]: the drag/resize snap passes, the
2//! internal→engine direction mapping, and the [`Snappable`](crate::snap::Snappable)
3//! trait impl. Extracted from `window.rs` so that file stays under the 800-line
4//! guardrail; the logic is unchanged, it just lives in its own cohesive module.
5
6use super::{snap, ResizeDir, Window};
7use crate::geometry::Rect;
8
9impl Window {
10    /// Snap pass for a title-bar drag.  Skipped entirely when the
11    /// global toggle is off — cheap when not in use.  Replaces
12    /// `self.bounds` with the engine's snapped result and writes the
13    /// guide list for `SnapOverlay` to paint.
14    pub(crate) fn apply_move_snap(&mut self) {
15        if !crate::snap::is_enabled() {
16            crate::snap::clear_guides();
17            return;
18        }
19        let targets = crate::snap::targets_snapshot();
20        let result = crate::snap::compute_snap(
21            self.bounds,
22            self.snap_id,
23            &targets,
24            crate::snap::DEFAULT_THRESHOLD,
25            crate::snap::SnapMode::Move,
26        );
27        self.bounds = snap(result.rect);
28        crate::snap::set_guides(result.guides);
29    }
30
31    /// Snap pass for an edge / corner resize drag.  Only edges that
32    /// the active handle is allowed to move can snap — the engine
33    /// enforces that internally via `SnapMode::Resize`.
34    pub(crate) fn apply_resize_snap(&mut self, dir: ResizeDir) {
35        if !crate::snap::is_enabled() {
36            crate::snap::clear_guides();
37            return;
38        }
39        let targets = crate::snap::targets_snapshot();
40        let edge = resize_dir_to_snap_edge(dir);
41        let result = crate::snap::compute_snap(
42            self.bounds,
43            self.snap_id,
44            &targets,
45            crate::snap::DEFAULT_THRESHOLD,
46            crate::snap::SnapMode::Resize(edge),
47        );
48        self.bounds = snap(result.rect);
49        crate::snap::set_guides(result.guides);
50    }
51}
52
53/// Map an internal `ResizeDir` to the snap engine's compass-direction
54/// enum.  Kept private — the snap engine owns its own enum so the
55/// engine isn't coupled to the Window widget.
56fn resize_dir_to_snap_edge(dir: ResizeDir) -> crate::snap::ResizeEdge {
57    use crate::snap::ResizeEdge as E;
58    match dir {
59        ResizeDir::N => E::North,
60        ResizeDir::NE => E::NorthEast,
61        ResizeDir::E => E::East,
62        ResizeDir::SE => E::SouthEast,
63        ResizeDir::S => E::South,
64        ResizeDir::SW => E::SouthWest,
65        ResizeDir::W => E::West,
66        ResizeDir::NW => E::NorthWest,
67    }
68}
69
70impl crate::snap::Snappable for Window {
71    fn snap_id(&self) -> crate::snap::SnapId {
72        self.snap_id
73    }
74    fn snap_rect(&self) -> Rect {
75        self.bounds
76    }
77    fn set_snap_rect(&mut self, r: Rect) {
78        self.bounds = snap(r);
79    }
80    fn is_snap_source(&self) -> bool {
81        self.requested_visible() && !self.maximized
82    }
83    fn is_snap_target(&self) -> bool {
84        // Maximized windows fill the canvas — pulling siblings to
85        // their edges would just glue everything to the canvas
86        // perimeter, which isn't useful as a layout aid.  Hidden
87        // windows aren't valid targets either.
88        self.requested_visible() && !self.maximized
89    }
90}