egui_graph_edit/
ui_state.rs

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