Skip to main content

jellyflow_runtime/runtime/store/view/
state.rs

1use crate::io::NodeGraphViewState;
2use crate::runtime::events::ViewChange;
3use crate::runtime::viewport::{
4    ViewportConstraints, ViewportPanRequest, ViewportTransform, ViewportZoomRequest,
5    constrain_viewport, pan_viewport, zoom_viewport,
6};
7use jellyflow_core::core::{CanvasPoint, CanvasSize, EdgeId, GroupId, NodeId};
8
9use super::super::NodeGraphStore;
10use super::changes::ViewStateMutationKind;
11
12impl NodeGraphStore {
13    pub fn view_state(&self) -> &NodeGraphViewState {
14        &self.view_state
15    }
16
17    /// Replaces the full view-state payload.
18    ///
19    /// This is the controlled-mode counterpart of `set_viewport`/`set_selection`.
20    pub fn replace_view_state(&mut self, view_state: NodeGraphViewState) {
21        self.update_view_state_if_changed(
22            |current| *current = view_state,
23            ViewStateMutationKind::FullState,
24        );
25    }
26
27    /// Mutates view-state in place and emits derived `ViewChange` events.
28    pub fn update_view_state(&mut self, f: impl FnOnce(&mut NodeGraphViewState)) {
29        self.update_view_state_if_changed(f, ViewStateMutationKind::FullState);
30    }
31
32    /// Sets the viewport (pan/zoom) and notifies subscribers.
33    pub fn set_viewport(&mut self, pan: CanvasPoint, zoom: f32) {
34        self.update_view_state_if_changed(
35            |view_state| view_state.set_viewport(pan, zoom),
36            ViewStateMutationKind::Viewport,
37        );
38    }
39
40    /// Applies a normalized drag-pan request through normal view-state publication.
41    pub fn apply_viewport_pan(&mut self, request: ViewportPanRequest) -> Option<ViewportTransform> {
42        let current = ViewportTransform::from_view_state(&self.view_state)?;
43        let next = constrain_viewport(
44            pan_viewport(current, request)?,
45            ViewportConstraints::unconstrained(),
46        )?;
47        self.set_viewport(next.pan, next.zoom);
48        Some(next)
49    }
50
51    /// Applies a drag-pan request while honoring configured translate extents.
52    pub fn apply_viewport_pan_constrained(
53        &mut self,
54        request: ViewportPanRequest,
55        viewport_size: CanvasSize,
56    ) -> Option<ViewportTransform> {
57        let current = ViewportTransform::from_view_state(&self.view_state)?;
58        let next = constrain_viewport(
59            pan_viewport(current, request)?,
60            self.viewport_constraints(viewport_size),
61        )?;
62        self.set_viewport(next.pan, next.zoom);
63        Some(next)
64    }
65
66    /// Applies a normalized anchored zoom request through normal view-state publication.
67    pub fn apply_viewport_zoom(
68        &mut self,
69        request: ViewportZoomRequest,
70    ) -> Option<ViewportTransform> {
71        let current = ViewportTransform::from_view_state(&self.view_state)?;
72        let next = constrain_viewport(
73            zoom_viewport(current, request)?,
74            ViewportConstraints::unconstrained(),
75        )?;
76        self.set_viewport(next.pan, next.zoom);
77        Some(next)
78    }
79
80    /// Applies an anchored zoom request while honoring configured translate extents.
81    pub fn apply_viewport_zoom_constrained(
82        &mut self,
83        request: ViewportZoomRequest,
84        viewport_size: CanvasSize,
85    ) -> Option<ViewportTransform> {
86        let current = ViewportTransform::from_view_state(&self.view_state)?;
87        let next = constrain_viewport(
88            zoom_viewport(current, request)?,
89            self.viewport_constraints(viewport_size),
90        )?;
91        self.set_viewport(next.pan, next.zoom);
92        Some(next)
93    }
94
95    /// Sets selection state and notifies subscribers.
96    pub fn set_selection(&mut self, nodes: Vec<NodeId>, edges: Vec<EdgeId>, groups: Vec<GroupId>) {
97        self.update_view_state_if_changed(
98            |view_state| view_state.set_selection(nodes, edges, groups),
99            ViewStateMutationKind::Selection,
100        );
101    }
102
103    fn update_view_state_if_changed(
104        &mut self,
105        mutate: impl FnOnce(&mut NodeGraphViewState),
106        kind: ViewStateMutationKind,
107    ) {
108        let before = self.view_state.clone();
109        mutate(&mut self.view_state);
110        kind.sanitize(&self.graph, &mut self.view_state);
111        let after = self.view_state.clone();
112
113        if !kind.changed(&before, &after) {
114            return;
115        }
116
117        let changes = kind.collect_changes(&before, &after);
118        self.publish_view_state_change(before, after, changes);
119    }
120
121    fn viewport_constraints(&self, viewport_size: CanvasSize) -> ViewportConstraints {
122        match self
123            .resolved_interaction_state()
124            .pan_interaction()
125            .translate_extent
126        {
127            Some(translate_extent) => {
128                ViewportConstraints::with_translate_extent(viewport_size, translate_extent)
129            }
130            None => ViewportConstraints::unconstrained(),
131        }
132    }
133
134    fn publish_view_state_change(
135        &mut self,
136        before: NodeGraphViewState,
137        after: NodeGraphViewState,
138        changes: Vec<ViewChange>,
139    ) {
140        self.publish_view_changed(&before, &after, &changes);
141    }
142}