Skip to main content

jellyflow_runtime/runtime/selection/
types.rs

1use serde::{Deserialize, Serialize};
2
3use jellyflow_core::core::{CanvasPoint, CanvasRect, CanvasSize, EdgeId, GroupId, NodeId};
4
5/// Modifier state that controls whether a selection gesture adds to existing selection.
6#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "snake_case")]
8pub enum SelectionModifier {
9    #[default]
10    Replace,
11    Additive,
12}
13
14impl SelectionModifier {
15    pub fn additive(self) -> bool {
16        matches!(self, Self::Additive)
17    }
18}
19
20/// Options for applying a marquee selection box.
21#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
22pub struct SelectionBoxOptions {
23    /// Whether the box result replaces or unions with the current selection.
24    #[serde(default)]
25    pub modifier: SelectionModifier,
26    /// Fallback size for nodes that do not have an explicit measured size.
27    #[serde(default, skip_serializing_if = "Option::is_none")]
28    pub fallback_size: Option<CanvasSize>,
29}
30
31/// Renderer-neutral input for a canvas-space marquee selection gesture.
32#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
33pub struct SelectionBoxInput {
34    pub rect: CanvasRect,
35    #[serde(default)]
36    pub options: SelectionBoxOptions,
37}
38
39impl SelectionBoxInput {
40    pub fn new(rect: CanvasRect, options: SelectionBoxOptions) -> Self {
41        Self { rect, options }
42    }
43
44    pub fn from_drag(
45        start: CanvasPoint,
46        current: CanvasPoint,
47        options: SelectionBoxOptions,
48    ) -> Self {
49        Self::new(normalized_drag_rect(start, current), options)
50    }
51
52    pub fn replace(rect: CanvasRect) -> Self {
53        Self::new(rect, SelectionBoxOptions::default())
54    }
55
56    pub fn replace_from_drag(start: CanvasPoint, current: CanvasPoint) -> Self {
57        Self::from_drag(start, current, SelectionBoxOptions::default())
58    }
59
60    pub fn additive(rect: CanvasRect) -> Self {
61        Self::new(
62            rect,
63            SelectionBoxOptions {
64                modifier: SelectionModifier::Additive,
65                ..SelectionBoxOptions::default()
66            },
67        )
68    }
69
70    pub fn additive_from_drag(start: CanvasPoint, current: CanvasPoint) -> Self {
71        Self::from_drag(
72            start,
73            current,
74            SelectionBoxOptions {
75                modifier: SelectionModifier::Additive,
76                ..SelectionBoxOptions::default()
77            },
78        )
79    }
80}
81
82fn normalized_drag_rect(start: CanvasPoint, current: CanvasPoint) -> CanvasRect {
83    let min_x = start.x.min(current.x);
84    let min_y = start.y.min(current.y);
85    CanvasRect {
86        origin: CanvasPoint { x: min_x, y: min_y },
87        size: CanvasSize {
88            width: (start.x - current.x).abs(),
89            height: (start.y - current.y).abs(),
90        },
91    }
92}
93
94/// Ordered selection result produced by a marquee selection box.
95#[derive(Debug, Clone, Default, PartialEq, Eq)]
96pub struct SelectionBoxResult {
97    pub nodes: Vec<NodeId>,
98    pub edges: Vec<EdgeId>,
99    pub groups: Vec<GroupId>,
100}
101
102impl SelectionBoxResult {
103    pub fn is_empty(&self) -> bool {
104        self.nodes.is_empty() && self.edges.is_empty() && self.groups.is_empty()
105    }
106}
107
108/// Resolved selection-box outcome ready to be applied by a store or inspected by tests.
109#[derive(Debug, Clone, Default, PartialEq, Eq)]
110pub struct SelectionBoxDecision {
111    result: SelectionBoxResult,
112}
113
114impl SelectionBoxDecision {
115    pub fn new(result: SelectionBoxResult) -> Self {
116        Self { result }
117    }
118
119    pub fn result(&self) -> &SelectionBoxResult {
120        &self.result
121    }
122
123    pub fn into_result(self) -> SelectionBoxResult {
124        self.result
125    }
126}