Skip to main content

ferrum_flow/plugins/
fit_all.rs

1use gpui::{Bounds, Point, px};
2
3use crate::{
4    Graph,
5    plugin::{FlowEvent, InitPluginContext, Plugin, PluginContext, primary_platform_modifier},
6    plugins::viewport_frame::{apply_frame_world_rect_direct, frame_world_rect},
7};
8
9/// Zoom and pan so **all** nodes fit in the window (⌘0 / Ctrl+0). Undo restores the previous view.
10///
11/// On [`Plugin::setup`], fits once using [`InitPluginContext::drawable_size`] (does not push an undo
12/// entry; the initial view is not recorded as a command).
13pub struct FitAllGraphPlugin;
14
15impl FitAllGraphPlugin {
16    pub fn new() -> Self {
17        Self
18    }
19}
20
21fn graph_world_bounds_graph(graph: &Graph) -> Option<(f32, f32, f32, f32)> {
22    let mut min_x = f32::MAX;
23    let mut min_y = f32::MAX;
24    let mut max_x = f32::MIN;
25    let mut max_y = f32::MIN;
26    let mut any = false;
27
28    for n in graph.nodes().values() {
29        let x: f32 = n.x.into();
30        let y: f32 = n.y.into();
31        let w: f32 = n.size.width.into();
32        let h: f32 = n.size.height.into();
33        min_x = min_x.min(x);
34        min_y = min_y.min(y);
35        max_x = max_x.max(x + w);
36        max_y = max_y.max(y + h);
37        any = true;
38    }
39
40    if !any {
41        return None;
42    }
43
44    Some((
45        min_x,
46        min_y,
47        (max_x - min_x).max(1.0),
48        (max_y - min_y).max(1.0),
49    ))
50}
51
52fn graph_world_bounds(ctx: &PluginContext) -> Option<(f32, f32, f32, f32)> {
53    graph_world_bounds_graph(ctx.graph)
54}
55
56fn fit_all(ctx: &mut PluginContext) {
57    let Some((bx, by, bw, bh)) = graph_world_bounds(ctx) else {
58        return;
59    };
60    frame_world_rect(ctx, bx, by, bw, bh);
61}
62
63pub(crate) fn fit_entire_graph(ctx: &mut PluginContext) {
64    fit_all(ctx);
65}
66
67impl Plugin for FitAllGraphPlugin {
68    fn name(&self) -> &'static str {
69        "fit_all_graph"
70    }
71
72    fn setup(&mut self, ctx: &mut InitPluginContext) {
73        let Some((bx, by, bw, bh)) = graph_world_bounds_graph(ctx.graph) else {
74            return;
75        };
76        let win_w: f32 = ctx.drawable_size.width.into();
77        let win_h: f32 = ctx.drawable_size.height.into();
78        apply_frame_world_rect_direct(ctx.viewport, win_w, win_h, bx, by, bw, bh);
79        ctx.viewport.window_bounds = Some(Bounds::new(
80            Point::new(px(0.0), px(0.0)),
81            ctx.drawable_size,
82        ));
83    }
84
85    fn priority(&self) -> i32 {
86        88
87    }
88
89    fn on_event(
90        &mut self,
91        event: &FlowEvent,
92        ctx: &mut PluginContext,
93    ) -> crate::plugin::EventResult {
94        if let FlowEvent::Input(crate::plugin::InputEvent::KeyDown(ev)) = event {
95            if primary_platform_modifier(ev)
96                && !ev.keystroke.modifiers.shift
97                && ev.keystroke.key == "0"
98            {
99                fit_all(ctx);
100                return crate::plugin::EventResult::Stop;
101            }
102        }
103        crate::plugin::EventResult::Continue
104    }
105}