1use gpui::{Bounds, Pixels, Point, Size, Window, px};
2
3use crate::Node;
4
5#[derive(Debug, Clone)]
6pub struct Viewport {
7 pub zoom: f32,
8 pub offset: Point<Pixels>,
9 pub window_bounds: Option<Bounds<Pixels>>,
10}
11
12impl Viewport {
13 pub fn new() -> Self {
14 Self {
15 zoom: 1.0,
16 offset: Point::new(px(0.0), px(0.0)),
17 window_bounds: None,
18 }
19 }
20
21 pub fn sync_drawable_bounds(&mut self, window: &Window) {
27 let vs = window.viewport_size();
28 let unchanged = self.window_bounds.is_some_and(|b| {
29 b.size.width == vs.width && b.size.height == vs.height
30 });
31 if !unchanged {
32 self.window_bounds = Some(Bounds::new(
33 Point::new(px(0.0), px(0.0)),
34 Size::new(vs.width, vs.height),
35 ));
36 }
37 }
38
39 pub fn world_to_screen(&self, p: Point<Pixels>) -> Point<Pixels> {
40 Point::new(
41 p.x * self.zoom + self.offset.x,
42 p.y * self.zoom + self.offset.y,
43 )
44 }
45
46 pub fn screen_to_world(&self, p: Point<Pixels>) -> Point<Pixels> {
47 Point::new(
48 (p.x - self.offset.x) / self.zoom,
49 (p.y - self.offset.y) / self.zoom,
50 )
51 }
52
53 pub fn is_node_visible(&self, node: &Node) -> bool {
54 let Some(window_bounds) = self.window_bounds else {
55 return false;
56 };
57
58 let screen = self.world_to_screen(node.point());
59
60 screen.x + node.size.width * self.zoom > px(0.0)
61 && screen.x < window_bounds.size.width
62 && screen.y + node.size.height * self.zoom > px(0.0)
63 && screen.y < window_bounds.size.height
64 }
65}