egui_node_editor/
ui_state.rs

1use super::*;
2use egui::{Rect, Style, Ui, Vec2};
3use std::marker::PhantomData;
4use std::sync::Arc;
5
6use crate::scale::Scale;
7#[cfg(feature = "persistence")]
8use serde::{Deserialize, Serialize};
9
10const MIN_ZOOM: f32 = 0.2;
11const MAX_ZOOM: f32 = 2.0;
12
13#[derive(Clone)]
14#[cfg_attr(feature = "persistence", derive(Serialize, Deserialize))]
15pub struct GraphEditorState<NodeData, DataType, ValueType, NodeTemplate, UserState> {
16    pub graph: Graph<NodeData, DataType, ValueType>,
17    /// Nodes are drawn in this order. Draw order is important because nodes
18    /// that are drawn last are on top.
19    pub node_order: Vec<NodeId>,
20    /// An ongoing connection interaction: The mouse has dragged away from a
21    /// port and the user is holding the click
22    pub connection_in_progress: Option<(NodeId, AnyParameterId)>,
23    /// The currently selected node. Some interface actions depend on the
24    /// currently selected node.
25    pub selected_nodes: Vec<NodeId>,
26    /// The mouse drag start position for an ongoing box selection.
27    pub ongoing_box_selection: Option<egui::Pos2>,
28    /// The position of each node.
29    pub node_positions: SecondaryMap<NodeId, egui::Pos2>,
30    /// The node finder is used to create new nodes.
31    pub node_finder: Option<NodeFinder<NodeTemplate>>,
32    /// The panning of the graph viewport.
33    pub pan_zoom: PanZoom,
34    pub _user_state: PhantomData<fn() -> UserState>,
35}
36
37impl<NodeData, DataType, ValueType, NodeKind, UserState>
38    GraphEditorState<NodeData, DataType, ValueType, NodeKind, UserState>
39{
40    pub fn new(default_zoom: f32) -> Self {
41        Self {
42            pan_zoom: PanZoom::new(default_zoom),
43            ..Default::default()
44        }
45    }
46}
47impl<NodeData, DataType, ValueType, NodeKind, UserState> Default
48    for GraphEditorState<NodeData, DataType, ValueType, NodeKind, UserState>
49{
50    fn default() -> Self {
51        Self {
52            graph: Default::default(),
53            node_order: Default::default(),
54            connection_in_progress: Default::default(),
55            selected_nodes: Default::default(),
56            ongoing_box_selection: Default::default(),
57            node_positions: Default::default(),
58            node_finder: Default::default(),
59            pan_zoom: Default::default(),
60            _user_state: Default::default(),
61        }
62    }
63}
64
65#[cfg(feature = "persistence")]
66fn _default_clip_rect() -> Rect {
67    Rect::NOTHING
68}
69
70#[derive(Clone)]
71#[cfg_attr(feature = "persistence", derive(Serialize, Deserialize))]
72pub struct PanZoom {
73    pub pan: Vec2,
74    pub zoom: f32,
75    #[cfg_attr(feature = "persistence", serde(skip, default = "_default_clip_rect"))]
76    pub clip_rect: Rect,
77    #[cfg_attr(feature = "persistence", serde(skip, default))]
78    pub zoomed_style: Arc<Style>,
79    #[cfg_attr(feature = "persistence", serde(skip, default))]
80    pub started: bool,
81}
82
83impl Default for PanZoom {
84    fn default() -> Self {
85        PanZoom {
86            pan: Vec2::ZERO,
87            zoom: 1.0,
88            clip_rect: Rect::NOTHING,
89            zoomed_style: Default::default(),
90            started: false,
91        }
92    }
93}
94
95impl PanZoom {
96    pub fn new(zoom: f32) -> PanZoom {
97        let style: Style = Default::default();
98        PanZoom {
99            pan: Vec2::ZERO,
100            zoom,
101            clip_rect: Rect::NOTHING,
102            zoomed_style: Arc::new(style.scaled(1.0)),
103            started: false,
104        }
105    }
106
107    pub fn zoom(&mut self, clip_rect: Rect, style: &Arc<Style>, zoom_delta: f32) {
108        self.clip_rect = clip_rect;
109        let new_zoom = (self.zoom * zoom_delta).clamp(MIN_ZOOM, MAX_ZOOM);
110        self.zoomed_style = Arc::new(style.scaled(new_zoom));
111        self.zoom = new_zoom;
112    }
113}
114
115pub fn show_zoomed<R, F>(
116    default_style: Arc<Style>,
117    zoomed_style: Arc<Style>,
118    ui: &mut Ui,
119    add_content: F,
120) -> R
121where
122    F: FnOnce(&mut Ui) -> R,
123{
124    *ui.style_mut() = (*zoomed_style).clone();
125    let response = add_content(ui);
126    *ui.style_mut() = (*default_style).clone();
127
128    response
129}